Updated build files
This commit is contained in:
parent
fcf9d6e826
commit
b6f2ffd6ad
3
src/step 5 ooni list/.idea/.gitignore
generated
vendored
Normal file
3
src/step 5 ooni list/.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
1
src/step 5 ooni list/.idea/.name
generated
Normal file
1
src/step 5 ooni list/.idea/.name
generated
Normal file
@ -0,0 +1 @@
|
||||
domain_sum.py
|
||||
6
src/step 5 ooni list/.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
src/step 5 ooni list/.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
7
src/step 5 ooni list/.idea/misc.xml
generated
Normal file
7
src/step 5 ooni list/.idea/misc.xml
generated
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.9 (Resolver)" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9 (Resolver)" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
8
src/step 5 ooni list/.idea/modules.xml
generated
Normal file
8
src/step 5 ooni list/.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/step 5 ooni list.iml" filepath="$PROJECT_DIR$/.idea/step 5 ooni list.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
8
src/step 5 ooni list/.idea/step 5 ooni list.iml
generated
Normal file
8
src/step 5 ooni list/.idea/step 5 ooni list.iml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.9 (Resolver)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
6
src/step 5 ooni list/.idea/vcs.xml
generated
Normal file
6
src/step 5 ooni list/.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@ -25,7 +25,7 @@ def main():
|
||||
# Read domains from the three files
|
||||
domains1 = read_domains_from_file("sum/input/domains.lst")
|
||||
domains2 = read_domains_from_file("sum/input/ooni_domains.lst")
|
||||
domains3 = read_domains_from_file("sum/input/community.lst")
|
||||
domains3 = read_domains_from_file("community.lst")
|
||||
|
||||
# Combine all domains
|
||||
all_domains = set(domains1 + domains2 + domains3)
|
||||
|
||||
@ -13,7 +13,7 @@ def main():
|
||||
ips1 = read_ips_from_file("sum/input/ip.lst")
|
||||
ips2 = read_ips_from_file("sum/input/ooni_ips.lst")
|
||||
ips3 = read_ips_from_file("sum/input/ip_community.lst")
|
||||
ips4 = read_ips_from_file("sum/input/discord_ips.lst")
|
||||
ips4 = read_ips_from_file("discord_ips.lst")
|
||||
|
||||
# Combine all IPs and remove duplicates
|
||||
unique_ips = set(ips1 + ips2 + ips3 + ips4)
|
||||
|
||||
@ -1,198 +0,0 @@
|
||||
import socket
|
||||
import geoip2.database
|
||||
import logging
|
||||
import requests
|
||||
import ipaddress
|
||||
import time
|
||||
import os
|
||||
import subprocess
|
||||
import json
|
||||
from collections import defaultdict
|
||||
from idna import encode as idna_encode
|
||||
|
||||
# Paths to input files
|
||||
IP_LST_PATH = 'sum/input/ips_all.lst'
|
||||
DOMAINS_LST_PATH = 'sum/output/domains_all.lst'
|
||||
OUTPUT_FILE = 'sum/output/ipsum.lst'
|
||||
|
||||
# Path to the GeoLite2 ASN database
|
||||
GEOIP_DB_PATH = 'sum/GeoLite2-ASN.mmdb'
|
||||
GEOIP_DB_URL = 'https://git.io/GeoLite2-ASN.mmdb'
|
||||
|
||||
# Function to download the GeoLite2 ASN database
|
||||
def download_geolite2_asn_db():
|
||||
if not os.path.exists(GEOIP_DB_PATH):
|
||||
try:
|
||||
response = requests.get(GEOIP_DB_URL)
|
||||
response.raise_for_status()
|
||||
with open(GEOIP_DB_PATH, 'wb') as f:
|
||||
f.write(response.content)
|
||||
logging.info(f'Downloaded GeoLite2 ASN database to {GEOIP_DB_PATH}')
|
||||
except requests.RequestException as e:
|
||||
logging.error(f'Failed to download GeoLite2 ASN database: {e}')
|
||||
raise
|
||||
|
||||
# Initialize the GeoIP2 reader
|
||||
def initialize_geoip_reader():
|
||||
download_geolite2_asn_db()
|
||||
return geoip2.database.Reader(GEOIP_DB_PATH)
|
||||
|
||||
# Set up logging
|
||||
logging.basicConfig(level=logging.DEBUG,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler('summary.log', mode='a'),
|
||||
logging.StreamHandler()
|
||||
])
|
||||
|
||||
# Trusted ASNs for company domains
|
||||
COMPANY_DOMAINS = {
|
||||
'google.com': [15169],
|
||||
'youtube.com': [15169],
|
||||
'ggpht.com': [15169],
|
||||
'facebook.com': [32934],
|
||||
'instagram.com': [32934],
|
||||
'whatsapp.com': [32934],
|
||||
'microsoft.com': [8075],
|
||||
'linkedin.com': [14492],
|
||||
'netflix.com': [2906],
|
||||
'akamai.com': [20940],
|
||||
'twitter.com': [13414],
|
||||
'x.com': [13414],
|
||||
'dropbox.com': [19679],
|
||||
'tesla.com': [394161]
|
||||
}
|
||||
|
||||
# Local IP CIDRs to exclude
|
||||
LOCAL_IP_CIDRS = [
|
||||
ipaddress.ip_network('127.0.0.0/8'),
|
||||
ipaddress.ip_network('10.0.0.0/8'),
|
||||
ipaddress.ip_network('172.16.0.0/12'),
|
||||
ipaddress.ip_network('192.168.0.0/16'),
|
||||
ipaddress.ip_network('169.254.0.0/16'),
|
||||
ipaddress.ip_network('::1/128'),
|
||||
ipaddress.ip_network('fc00::/7'),
|
||||
ipaddress.ip_network('fe80::/10')
|
||||
]
|
||||
|
||||
# Function to summarize IPs into /28 subnets at most
|
||||
def summarize_ips(ips):
|
||||
try:
|
||||
# Remove duplicates and sort IPs, treating them as networks (e.g., x.x.x.x/32)
|
||||
networks = [ipaddress.ip_network(ip, strict=False) for ip in set(ips)]
|
||||
collapsed_networks = ipaddress.collapse_addresses(networks)
|
||||
summarized_networks = []
|
||||
|
||||
for network in collapsed_networks:
|
||||
if network.prefixlen < 28: # If network is bigger than /28, split into /28
|
||||
for subnet in network.subnets(new_prefix=28):
|
||||
summarized_networks.append(subnet)
|
||||
else:
|
||||
summarized_networks.append(network)
|
||||
|
||||
logging.info(f'Summarized networks: {summarized_networks}')
|
||||
return summarized_networks
|
||||
except ValueError as e:
|
||||
logging.error(f'Error summarizing IPs: {e}')
|
||||
return []
|
||||
|
||||
# Function to handle rate-limiting errors (429) and retry after waiting
|
||||
def handle_rate_limit():
|
||||
wait_time = 60 # Wait time of 60 seconds
|
||||
logging.warning(f'Rate limit hit. Waiting for {wait_time} seconds.')
|
||||
time.sleep(wait_time)
|
||||
|
||||
# Function to get CIDRs for a domain from ASN using ip.guide
|
||||
def get_cidr_for_asn(asn):
|
||||
try:
|
||||
command = f'curl -sL https://ip.guide/as{asn}'
|
||||
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
||||
if result.returncode == 0:
|
||||
data = json.loads(result.stdout)
|
||||
return data.get('routes', {}).get('v4', [])
|
||||
else:
|
||||
logging.error(f'Error executing curl command: {result.stderr}')
|
||||
return []
|
||||
except Exception as e:
|
||||
logging.error(f'Error retrieving CIDRs for ASN {asn}: {e}')
|
||||
return []
|
||||
|
||||
# Function to resolve a domain with retries and punycode support
|
||||
def resolve_domain(domain):
|
||||
try:
|
||||
domain_punycode = idna_encode(domain).decode('utf-8')
|
||||
return socket.gethostbyname_ex(domain_punycode)[2]
|
||||
except Exception as e:
|
||||
logging.error(f'Could not resolve domain {domain}: {e}')
|
||||
return []
|
||||
|
||||
# Function to check if a domain matches COMPANY_DOMAINS and fetch CIDRs
|
||||
def process_domain_for_asn(domain, processed_asns):
|
||||
asns = COMPANY_DOMAINS.get(domain, [])
|
||||
cidrs = set()
|
||||
for asn in asns:
|
||||
if asn not in processed_asns:
|
||||
processed_asns.add(asn)
|
||||
cidrs.update(get_cidr_for_asn(asn))
|
||||
return cidrs
|
||||
|
||||
# Function to read IPs from ip.lst
|
||||
def read_ips_from_file(file_path):
|
||||
try:
|
||||
with open(file_path, 'r') as f:
|
||||
return [line.strip() for line in f.readlines() if line.strip()]
|
||||
except FileNotFoundError:
|
||||
logging.error(f'File not found: {file_path}')
|
||||
return []
|
||||
|
||||
# Function to check if an IP is local
|
||||
def is_local_ip(ip):
|
||||
try:
|
||||
ip_obj = ipaddress.ip_network(ip, strict=False)
|
||||
for cidr in LOCAL_IP_CIDRS:
|
||||
if ip_obj.version == cidr.version and ip_obj.subnet_of(cidr):
|
||||
return True
|
||||
except ValueError as e:
|
||||
logging.error(f'Invalid IP or CIDR: {ip}: {e}')
|
||||
return False
|
||||
|
||||
# Function to write summarized CIDRs to ipsum.lst
|
||||
def write_summarized_ips(ips, filename):
|
||||
try:
|
||||
with open(filename, 'w') as f:
|
||||
for cidr in ips:
|
||||
f.write(f'{cidr}\n')
|
||||
logging.info(f'Written summarized IPs to {filename}')
|
||||
except Exception as e:
|
||||
logging.error(f'Error writing summarized IPs to file: {e}')
|
||||
|
||||
# Main function to process ip.lst, summarize, and add CIDRs for company domains
|
||||
def main():
|
||||
# Initialize the GeoIP2 reader
|
||||
reader = initialize_geoip_reader()
|
||||
|
||||
# Read IPs from ip.lst
|
||||
ips = read_ips_from_file(IP_LST_PATH)
|
||||
|
||||
# Filter out local IPs
|
||||
ips = [ip for ip in ips if not is_local_ip(ip)]
|
||||
|
||||
# Summarize the IPs into /28 networks
|
||||
summarized_ips = summarize_ips(ips)
|
||||
|
||||
# Check domains.lst for COMPANY_DOMAINS matches and get corresponding CIDRs
|
||||
domains = read_ips_from_file(DOMAINS_LST_PATH)
|
||||
company_cidrs = set()
|
||||
processed_asns = set()
|
||||
|
||||
for domain in domains:
|
||||
company_cidrs.update(process_domain_for_asn(domain, processed_asns))
|
||||
|
||||
# Combine summarized IPs and company CIDRs
|
||||
final_cidrs = set(summarized_ips) | company_cidrs
|
||||
|
||||
# Write the final output to ipsum.lst
|
||||
write_summarized_ips(final_cidrs, OUTPUT_FILE)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@ -2,8 +2,8 @@ import socket
|
||||
import concurrent.futures
|
||||
import threading
|
||||
import gc
|
||||
import time # For introducing delay
|
||||
import requests # For making API calls to get ASN details
|
||||
#import time # For introducing delay
|
||||
#import requests # For making API calls to get ASN details
|
||||
import ipaddress
|
||||
from idna import encode as idna_encode
|
||||
from queue import Queue
|
||||
@ -55,7 +55,7 @@ def process_domain(domain, existing_cidrs):
|
||||
return set()
|
||||
|
||||
# Function to read domains from domains.lst file
|
||||
def read_domains_from_file(file_path="sum/input/community.lst"):
|
||||
def read_domains_from_file(file_path="community.lst"):
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
domains = [line.strip() for line in f.readlines() if line.strip()]
|
||||
@ -81,7 +81,7 @@ def main():
|
||||
gc.enable()
|
||||
|
||||
# Read the domains from domains.lst file
|
||||
domains = read_domains_from_file("sum/input/community.lst")
|
||||
domains = read_domains_from_file("community.lst")
|
||||
if not domains:
|
||||
return
|
||||
|
||||
@ -92,7 +92,7 @@ def main():
|
||||
writer_thread.start()
|
||||
|
||||
# Use ThreadPoolExecutor to use more threads (set to 16 threads for better utilization)
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor:
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=16) as executor:
|
||||
future_to_domain = {executor.submit(process_domain, domain, existing_cidrs): domain for domain in domains}
|
||||
|
||||
for future in concurrent.futures.as_completed(future_to_domain):
|
||||
|
||||
@ -2,8 +2,8 @@ import socket
|
||||
import concurrent.futures
|
||||
import threading
|
||||
import gc
|
||||
import time # For introducing delay
|
||||
import requests # For making API calls to get ASN details
|
||||
#import time
|
||||
#import requests
|
||||
import ipaddress
|
||||
from idna import encode as idna_encode
|
||||
from queue import Queue
|
||||
@ -92,7 +92,7 @@ def main():
|
||||
writer_thread.start()
|
||||
|
||||
# Use ThreadPoolExecutor to use more threads (set to 16 threads for better utilization)
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor:
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=16) as executor:
|
||||
future_to_domain = {executor.submit(process_domain, domain, existing_cidrs): domain for domain in domains}
|
||||
|
||||
for future in concurrent.futures.as_completed(future_to_domain):
|
||||
|
||||
@ -4,25 +4,44 @@ import logging
|
||||
import requests
|
||||
import ipaddress
|
||||
import time
|
||||
import os
|
||||
import subprocess
|
||||
import json
|
||||
from collections import defaultdict
|
||||
from idna import encode as idna_encode
|
||||
|
||||
# Paths to input files
|
||||
IP_LST_PATH = "ip.lst"
|
||||
DOMAINS_LST_PATH = "domains.lst"
|
||||
OUTPUT_FILE = "ipsum.lst"
|
||||
IP_LST_PATH = 'sum/input/ips_all.lst'
|
||||
DOMAINS_LST_PATH = 'sum/output/domains_all.lst'
|
||||
OUTPUT_FILE = 'sum/output/ipsum.lst'
|
||||
|
||||
# Path to the GeoLite2 ASN database
|
||||
GEOIP_DB_PATH = "GeoLite2-ASN.mmdb"
|
||||
GEOIP_DB_PATH = 'sum/GeoLite2-ASN.mmdb'
|
||||
GEOIP_DB_URL = 'https://git.io/GeoLite2-ASN.mmdb'
|
||||
|
||||
# Function to download the GeoLite2 ASN database
|
||||
def download_geolite2_asn_db():
|
||||
if not os.path.exists(GEOIP_DB_PATH):
|
||||
try:
|
||||
response = requests.get(GEOIP_DB_URL)
|
||||
response.raise_for_status()
|
||||
with open(GEOIP_DB_PATH, 'wb') as f:
|
||||
f.write(response.content)
|
||||
logging.info(f'Downloaded GeoLite2 ASN database to {GEOIP_DB_PATH}')
|
||||
except requests.RequestException as e:
|
||||
logging.error(f'Failed to download GeoLite2 ASN database: {e}')
|
||||
raise
|
||||
|
||||
# Initialize the GeoIP2 reader
|
||||
reader = geoip2.database.Reader(GEOIP_DB_PATH)
|
||||
def initialize_geoip_reader():
|
||||
download_geolite2_asn_db()
|
||||
return geoip2.database.Reader(GEOIP_DB_PATH)
|
||||
|
||||
# Set up logging
|
||||
logging.basicConfig(level=logging.DEBUG,
|
||||
format="%(asctime)s - %(levelname)s - %(message)s",
|
||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler("summary.log", mode='a'),
|
||||
logging.FileHandler('summary.log', mode='a'),
|
||||
logging.StreamHandler()
|
||||
])
|
||||
|
||||
@ -34,7 +53,6 @@ COMPANY_DOMAINS = {
|
||||
'facebook.com': [32934],
|
||||
'instagram.com': [32934],
|
||||
'whatsapp.com': [32934],
|
||||
'fbcdn.net': [32934],
|
||||
'microsoft.com': [8075],
|
||||
'linkedin.com': [14492],
|
||||
'netflix.com': [2906],
|
||||
@ -45,6 +63,17 @@ COMPANY_DOMAINS = {
|
||||
'tesla.com': [394161]
|
||||
}
|
||||
|
||||
# Local IP CIDRs to exclude
|
||||
LOCAL_IP_CIDRS = [
|
||||
ipaddress.ip_network('127.0.0.0/8'),
|
||||
ipaddress.ip_network('10.0.0.0/8'),
|
||||
ipaddress.ip_network('172.16.0.0/12'),
|
||||
ipaddress.ip_network('192.168.0.0/16'),
|
||||
ipaddress.ip_network('169.254.0.0/16'),
|
||||
ipaddress.ip_network('::1/128'),
|
||||
ipaddress.ip_network('fc00::/7'),
|
||||
ipaddress.ip_network('fe80::/10')
|
||||
]
|
||||
|
||||
# Function to summarize IPs into /28 subnets at most
|
||||
def summarize_ips(ips):
|
||||
@ -61,99 +90,103 @@ def summarize_ips(ips):
|
||||
else:
|
||||
summarized_networks.append(network)
|
||||
|
||||
logging.info(f"Summarized networks: {summarized_networks}")
|
||||
logging.info(f'Summarized networks: {summarized_networks}')
|
||||
return summarized_networks
|
||||
except ValueError as e:
|
||||
logging.error(f"Error summarizing IPs: {e}")
|
||||
logging.error(f'Error summarizing IPs: {e}')
|
||||
return []
|
||||
|
||||
|
||||
# Function to handle rate-limiting errors (429) and retry after waiting
|
||||
def handle_rate_limit():
|
||||
wait_time = 60 # Wait time of 60 seconds
|
||||
logging.warning(f"Rate limit hit. Waiting for {wait_time} seconds.")
|
||||
logging.warning(f'Rate limit hit. Waiting for {wait_time} seconds.')
|
||||
time.sleep(wait_time)
|
||||
|
||||
|
||||
# Function to get CIDRs for a domain from ASN using GeoLite2
|
||||
# Function to get CIDRs for a domain from ASN using ip.guide
|
||||
def get_cidr_for_asn(asn):
|
||||
try:
|
||||
url = f"https://api.bgpview.io/asn/{asn}/prefixes"
|
||||
response = requests.get(url)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
return [prefix['prefix'] for prefix in data['data']['ipv4_prefixes']]
|
||||
|
||||
elif response.status_code == 429:
|
||||
handle_rate_limit()
|
||||
return get_cidr_for_asn(asn) # Retry after waiting
|
||||
|
||||
elif response.status_code == 403:
|
||||
logging.error(f"Access forbidden for ASN {asn}, skipping.")
|
||||
command = f'curl -sL https://ip.guide/as{asn}'
|
||||
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
||||
if result.returncode == 0:
|
||||
data = json.loads(result.stdout)
|
||||
return data.get('routes', {}).get('v4', [])
|
||||
else:
|
||||
logging.error(f'Error executing curl command: {result.stderr}')
|
||||
return []
|
||||
|
||||
return []
|
||||
except Exception as e:
|
||||
logging.error(f"Error retrieving CIDRs for ASN {asn}: {e}")
|
||||
logging.error(f'Error retrieving CIDRs for ASN {asn}: {e}')
|
||||
return []
|
||||
|
||||
|
||||
# Function to resolve a domain with retries and punycode support
|
||||
def resolve_domain(domain):
|
||||
try:
|
||||
domain_punycode = idna_encode(domain).decode('utf-8')
|
||||
return socket.gethostbyname_ex(domain_punycode)[2]
|
||||
except Exception as e:
|
||||
logging.error(f"Could not resolve domain {domain}: {e}")
|
||||
logging.error(f'Could not resolve domain {domain}: {e}')
|
||||
return []
|
||||
|
||||
|
||||
# Function to check if a domain matches COMPANY_DOMAINS and fetch CIDRs
|
||||
def process_domain_for_asn(domain):
|
||||
def process_domain_for_asn(domain, processed_asns):
|
||||
asns = COMPANY_DOMAINS.get(domain, [])
|
||||
cidrs = set()
|
||||
if asns:
|
||||
for asn in asns:
|
||||
for asn in asns:
|
||||
if asn not in processed_asns:
|
||||
processed_asns.add(asn)
|
||||
cidrs.update(get_cidr_for_asn(asn))
|
||||
return cidrs
|
||||
|
||||
|
||||
# Function to read IPs from ip.lst
|
||||
def read_ips_from_file(file_path):
|
||||
try:
|
||||
with open(file_path, 'r') as f:
|
||||
return [line.strip() for line in f.readlines() if line.strip()]
|
||||
except FileNotFoundError:
|
||||
logging.error(f"File not found: {file_path}")
|
||||
logging.error(f'File not found: {file_path}')
|
||||
return []
|
||||
|
||||
# Function to check if an IP is local
|
||||
def is_local_ip(ip):
|
||||
try:
|
||||
ip_obj = ipaddress.ip_network(ip, strict=False)
|
||||
for cidr in LOCAL_IP_CIDRS:
|
||||
if ip_obj.version == cidr.version and ip_obj.subnet_of(cidr):
|
||||
return True
|
||||
except ValueError as e:
|
||||
logging.error(f'Invalid IP or CIDR: {ip}: {e}')
|
||||
return False
|
||||
|
||||
# Function to write summarized CIDRs to ipsum.lst
|
||||
def write_summarized_ips(ips, filename):
|
||||
try:
|
||||
with open(filename, 'w') as f:
|
||||
for cidr in ips:
|
||||
f.write(f"{cidr}\n")
|
||||
logging.info(f"Written summarized IPs to {filename}")
|
||||
f.write(f'{cidr}\n')
|
||||
logging.info(f'Written summarized IPs to {filename}')
|
||||
except Exception as e:
|
||||
logging.error(f"Error writing summarized IPs to file: {e}")
|
||||
|
||||
logging.error(f'Error writing summarized IPs to file: {e}')
|
||||
|
||||
# Main function to process ip.lst, summarize, and add CIDRs for company domains
|
||||
def main():
|
||||
# Initialize the GeoIP2 reader
|
||||
reader = initialize_geoip_reader()
|
||||
|
||||
# Read IPs from ip.lst
|
||||
ips = read_ips_from_file(IP_LST_PATH)
|
||||
|
||||
# Filter out local IPs
|
||||
ips = [ip for ip in ips if not is_local_ip(ip)]
|
||||
|
||||
# Summarize the IPs into /28 networks
|
||||
summarized_ips = summarize_ips(ips)
|
||||
|
||||
# Check domains.lst for COMPANY_DOMAINS matches and get corresponding CIDRs
|
||||
domains = read_ips_from_file(DOMAINS_LST_PATH)
|
||||
company_cidrs = set()
|
||||
processed_asns = set()
|
||||
|
||||
for domain in domains:
|
||||
company_cidrs.update(process_domain_for_asn(domain))
|
||||
company_cidrs.update(process_domain_for_asn(domain, processed_asns))
|
||||
|
||||
# Combine summarized IPs and company CIDRs
|
||||
final_cidrs = set(summarized_ips) | company_cidrs
|
||||
@ -161,6 +194,5 @@ def main():
|
||||
# Write the final output to ipsum.lst
|
||||
write_summarized_ips(final_cidrs, OUTPUT_FILE)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
27
src/xray-geoip/config.json
Normal file
27
src/xray-geoip/config.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"input": [
|
||||
{
|
||||
"type": "text",
|
||||
"action": "add",
|
||||
"args": {
|
||||
"name": "refilter",
|
||||
"uri": "./ipsum.lst",
|
||||
"onlyIPType": "ipv4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "private",
|
||||
"action": "add"
|
||||
}
|
||||
],
|
||||
"output": [
|
||||
{
|
||||
"type": "v2rayGeoIPDat",
|
||||
"action": "output",
|
||||
"args": {
|
||||
"outputName": "geoip.dat",
|
||||
"wantedList": ["refilter", "private"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
34
src/xray-geoip/go.mod
Normal file
34
src/xray-geoip/go.mod
Normal file
@ -0,0 +1,34 @@
|
||||
module github.com/v2fly/geoip
|
||||
|
||||
go 1.22.0
|
||||
|
||||
toolchain go1.23.1
|
||||
|
||||
require (
|
||||
github.com/oschwald/maxminddb-golang v1.13.1
|
||||
github.com/v2fly/v2ray-core/v5 v5.19.0
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||
google.golang.org/protobuf v1.34.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/adrg/xdg v0.5.0 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/pprof v0.0.0-20240927233043-f3f46ee36851 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.20.2 // indirect
|
||||
github.com/onsi/gomega v1.34.1 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pires/go-proxyproto v0.7.0 // indirect
|
||||
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
|
||||
github.com/quic-go/qtls-go1-19 v0.3.3 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
|
||||
github.com/quic-go/quic-go v0.47.0 // indirect
|
||||
github.com/stretchr/testify v1.9.0 // indirect
|
||||
go.starlark.net v0.0.0-20240925182052-1207426daebd // indirect
|
||||
golang.org/x/crypto v0.27.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
|
||||
golang.org/x/net v0.29.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/text v0.18.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
248
src/xray-geoip/go.sum
Normal file
248
src/xray-geoip/go.sum
Normal file
@ -0,0 +1,248 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
|
||||
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
|
||||
github.com/adrg/xdg v0.5.0 h1:dDaZvhMXatArP1NPHhnfaQUqWBLBsmx1h1HXQdMoFCY=
|
||||
github.com/adrg/xdg v0.5.0/go.mod h1:dDdY4M4DF9Rjy4kHPeNL+ilVF+p2lK8IdM9/rTSGcI4=
|
||||
github.com/aead/cmac v0.0.0-20160719120800-7af84192f0b1 h1:+JkXLHME8vLJafGhOH4aoV2Iu8bR55nU6iKMVfYVLjY=
|
||||
github.com/aead/cmac v0.0.0-20160719120800-7af84192f0b1/go.mod h1:nuudZmJhzWtx2212z+pkuy7B6nkBqa+xwNXZHL1j8cg=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d h1:zsO4lp+bjv5XvPTF58Vq+qgmZEYZttJK+CWtSZhKenI=
|
||||
github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d/go.mod h1:f1iKL6ZhUWvbk7PdWVmOaak10o86cqMUYEmn1CZNGEI=
|
||||
github.com/bufbuild/protocompile v0.2.1-0.20230123224550-da57cd758c2f h1:IXSA5gow10s7zIOJfPOpXDtNBWCTA0715BDAhoJBXEs=
|
||||
github.com/bufbuild/protocompile v0.2.1-0.20230123224550-da57cd758c2f/go.mod h1:tleDrpPTlLUVmgnEoN6qBliKWqJaZFJXqZdFjTd+ocU=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165 h1:BS21ZUJ/B5X2UVUbczfmdWH7GapPWAhxcMsDnjJTU1E=
|
||||
github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
|
||||
github.com/ebfe/bcrypt_pbkdf v0.0.0-20140212075826-3c8d2dcb253a h1:YtdtTUN1iH97s+6PUjLnaiKSQj4oG1/EZ3N9bx6g4kU=
|
||||
github.com/ebfe/bcrypt_pbkdf v0.0.0-20140212075826-3c8d2dcb253a/go.mod h1:/CZpbhAusDOobpcb9yubw46kdYjq0zRC0Wpg9a9zFQM=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
|
||||
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
|
||||
github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg=
|
||||
github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU=
|
||||
github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s=
|
||||
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/pprof v0.0.0-20240927233043-f3f46ee36851 h1:vVVnLmuArD0iybzYyh+YQ98ZQNx6yeqBqYouyJ36hcE=
|
||||
github.com/google/pprof v0.0.0-20240927233043-f3f46ee36851/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/jhump/protoreflect v1.15.0 h1:U5T5/2LF0AZQFP9T4W5GfBjBaTruomrKobiR4E+oA/Q=
|
||||
github.com/jhump/protoreflect v1.15.0/go.mod h1:qww51KYjD2hoCl/ohxw5cK2LSssFczrbO1t8Ld2TENs=
|
||||
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
|
||||
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
|
||||
github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs=
|
||||
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/reedsolomon v1.9.3 h1:N/VzgeMfHmLc+KHMD1UL/tNkfXAt8FnUqlgXGIduwAY=
|
||||
github.com/klauspost/reedsolomon v1.9.3/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc=
|
||||
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
|
||||
github.com/mustafaturan/bus v1.0.2 h1:2x3ErwZ0uUPwwZ5ZZoknEQprdaxr68Yl3mY8jDye1Ws=
|
||||
github.com/mustafaturan/bus v1.0.2/go.mod h1:h7gfehm8TThv4Dcaa+wDQG7r7j6p74v+7ftr0Rq9i1Q=
|
||||
github.com/mustafaturan/monoton v1.0.0 h1:8SCej+JiNn0lyps7V+Jzc1CRAkDR4EZPWrTupQ61YCQ=
|
||||
github.com/mustafaturan/monoton v1.0.0/go.mod h1:FOnE7NV3s3EWPXb8/7+/OSdiMBbdlkV0Lz8p1dc+vy8=
|
||||
github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
|
||||
github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
|
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
|
||||
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
|
||||
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
|
||||
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pion/dtls/v2 v2.2.4 h1:YSfYwDQgrxMYXLBc/m7PFY5BVtWlNm/DN4qoU2CbcWg=
|
||||
github.com/pion/dtls/v2 v2.2.4/go.mod h1:WGKfxqhrddne4Kg3p11FUMJrynkOY4lb25zHNO49wuw=
|
||||
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
||||
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||
github.com/pion/sctp v1.7.6 h1:8qZTdJtbKfAns/Hv5L0PAj8FyXcsKhMH1pKUCGisQg4=
|
||||
github.com/pion/sctp v1.7.6/go.mod h1:ichkYQ5tlgCQwEwvgfdcAolqx1nHbYCxo4D7zK/K0X8=
|
||||
github.com/pion/transport/v2 v2.0.0 h1:bsMYyqHCbkvHwj+eNCFBuxtlKndKfyGI2vaQmM3fIE4=
|
||||
github.com/pion/transport/v2 v2.0.0/go.mod h1:HS2MEBJTwD+1ZI2eSXSvHJx/HnzQqRy2/LXxt6eVMHc=
|
||||
github.com/pion/udp v0.1.4 h1:OowsTmu1Od3sD6i3fQUJxJn2fEvJO6L1TidgadtbTI8=
|
||||
github.com/pion/udp v0.1.4/go.mod h1:G8LDo56HsFwC24LIcnT4YIDU5qcB6NepqqjP0keL2us=
|
||||
github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8=
|
||||
github.com/pires/go-proxyproto v0.6.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
||||
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
|
||||
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/quic-go/qtls-go1-18 v0.2.0 h1:5ViXqBZ90wpUcZS0ge79rf029yx0dYB0McyPJwqqj7U=
|
||||
github.com/quic-go/qtls-go1-18 v0.2.0/go.mod h1:moGulGHK7o6O8lSPSZNoOwcLvJKJ85vVNc7oJFD65bc=
|
||||
github.com/quic-go/qtls-go1-19 v0.2.1 h1:aJcKNMkH5ASEJB9FXNeZCyTEIHU1J7MmHyz1Q1TSG1A=
|
||||
github.com/quic-go/qtls-go1-19 v0.2.1/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
||||
github.com/quic-go/qtls-go1-19 v0.3.3 h1:wznEHvJwd+2X3PqftRha0SUKmGsnb6dfArMhy9PeJVE=
|
||||
github.com/quic-go/qtls-go1-19 v0.3.3/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
||||
github.com/quic-go/qtls-go1-20 v0.1.1 h1:KbChDlg82d3IHqaj2bn6GfKRj84Per2VGf5XV3wSwQk=
|
||||
github.com/quic-go/qtls-go1-20 v0.1.1/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||
github.com/quic-go/quic-go v0.47.0 h1:yXs3v7r2bm1wmPTYNLKAAJTHMYkPEsfYJmTazXrCZ7Y=
|
||||
github.com/quic-go/quic-go v0.47.0/go.mod h1:3bCapYsJvXGZcipOHuu7plYtaV6tnF+z7wIFsU0WK9E=
|
||||
github.com/refraction-networking/utls v1.3.1 h1:3zVomUqx7nCmyGuU/6kYA/jp5NcqX8KQSGko8pY5Ch4=
|
||||
github.com/refraction-networking/utls v1.3.1/go.mod h1:kHXvVB66a4BzVRYC4Em7e1HAfp7uwOCCw0+2CZ3sMY8=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
|
||||
github.com/secure-io/siv-go v0.0.0-20180922214919-5ff40651e2c4 h1:zOjq+1/uLzn/Xo40stbvjIY/yehG0+mfmlsiEmc0xmQ=
|
||||
github.com/secure-io/siv-go v0.0.0-20180922214919-5ff40651e2c4/go.mod h1:aI+8yClBW+1uovkHw6HM01YXnYB8vohtB9C83wzx34E=
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U=
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/v2fly/BrowserBridge v0.0.0-20210430233438-0570fc1d7d08 h1:4Yh46CVE3k/lPq6hUbEdbB1u1anRBXLewm3k+L0iOMc=
|
||||
github.com/v2fly/BrowserBridge v0.0.0-20210430233438-0570fc1d7d08/go.mod h1:KAuQNm+LWQCOFqdBcUgihPzRpVXRKzGbTNhfEfRZ4wY=
|
||||
github.com/v2fly/VSign v0.0.0-20201108000810-e2adc24bf848 h1:p1UzXK6VAutXFFQMnre66h7g1BjRKUnLv0HfmmRoz7w=
|
||||
github.com/v2fly/VSign v0.0.0-20201108000810-e2adc24bf848/go.mod h1:p80Bv154ZtrGpXMN15slDCqc9UGmfBuUzheDFBYaW/M=
|
||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI=
|
||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU=
|
||||
github.com/v2fly/v2ray-core/v5 v5.4.1 h1:1l3KIFKoOlZkUp6D9MUlN/gz6aOEx09YfRVmnwnoGIQ=
|
||||
github.com/v2fly/v2ray-core/v5 v5.4.1/go.mod h1:8JUFMS/1biOF9rWV7V5IU3NKU8GhCd442MqW3DgdFKw=
|
||||
github.com/v2fly/v2ray-core/v5 v5.19.0 h1:TF2noX1c1npgSg98TASHLdYDWDRhi97gBLpid1cwMUY=
|
||||
github.com/v2fly/v2ray-core/v5 v5.19.0/go.mod h1:iRydCoQWwE8mhaf/VOWe5jKB8r7LkZfHsbOvwRfJWUo=
|
||||
github.com/xiaokangwang/VLite v0.0.0-20220418190619-cff95160a432 h1:I/ATawgO2RerCq9ACwL0wBB8xNXZdE3J+93MCEHReRs=
|
||||
github.com/xiaokangwang/VLite v0.0.0-20220418190619-cff95160a432/go.mod h1:QN7Go2ftTVfx0aCTh9RXHV8pkpi0FtmbwQw40dy61wQ=
|
||||
github.com/xtaci/smux v1.5.15 h1:6hMiXswcleXj5oNfcJc+DXS8Vj36XX2LaX98udog6Kc=
|
||||
github.com/xtaci/smux v1.5.15/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
|
||||
go.starlark.net v0.0.0-20220817180228-f738f5508c12 h1:xOBJXWGEDwU5xSDxH6macxO11Us0AH2fTa9rmsbbF7g=
|
||||
go.starlark.net v0.0.0-20220817180228-f738f5508c12/go.mod h1:VZcBMdr3cT3PnBoWunTabuSEXwVAH+ZJ5zxfs3AdASk=
|
||||
go.starlark.net v0.0.0-20240925182052-1207426daebd h1:S+EMisJOHklQxnS3kqsY8jl2y5aF0FDEdcLnOw3q22E=
|
||||
go.starlark.net v0.0.0-20240925182052-1207426daebd/go.mod h1:YKMCv9b1WrfWmeqdV5MAuEHWsu5iC+fe6kYl2sQjdI8=
|
||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35 h1:nJAwRlGWZZDOD+6wni9KVUNHMpHko/OnRwsrCYeAzPo=
|
||||
go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35/go.mod h1:TQvodOM+hJTioNQJilmLXu08JNb8i+ccq418+KWu1/Y=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
|
||||
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w=
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
|
||||
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
8
src/xray-geoip/init.go
Normal file
8
src/xray-geoip/init.go
Normal file
@ -0,0 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "github.com/v2fly/geoip/plugin/maxmind"
|
||||
_ "github.com/v2fly/geoip/plugin/plaintext"
|
||||
_ "github.com/v2fly/geoip/plugin/special"
|
||||
_ "github.com/v2fly/geoip/plugin/v2ray"
|
||||
)
|
||||
128
src/xray-geoip/lib/config.go
Normal file
128
src/xray-geoip/lib/config.go
Normal file
@ -0,0 +1,128 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
inputConfigCreatorCache = make(map[string]inputConfigCreator)
|
||||
outputConfigCreatorCache = make(map[string]outputConfigCreator)
|
||||
)
|
||||
|
||||
type inputConfigCreator func(Action, json.RawMessage) (InputConverter, error)
|
||||
|
||||
type outputConfigCreator func(Action, json.RawMessage) (OutputConverter, error)
|
||||
|
||||
func RegisterInputConfigCreator(id string, fn inputConfigCreator) error {
|
||||
id = strings.ToLower(id)
|
||||
if _, found := inputConfigCreatorCache[id]; found {
|
||||
return errors.New("config creator has already been registered")
|
||||
}
|
||||
inputConfigCreatorCache[id] = fn
|
||||
return nil
|
||||
}
|
||||
|
||||
func createInputConfig(id string, action Action, data json.RawMessage) (InputConverter, error) {
|
||||
id = strings.ToLower(id)
|
||||
fn, found := inputConfigCreatorCache[id]
|
||||
if !found {
|
||||
return nil, errors.New("unknown config type")
|
||||
}
|
||||
return fn(action, data)
|
||||
}
|
||||
|
||||
func RegisterOutputConfigCreator(id string, fn outputConfigCreator) error {
|
||||
id = strings.ToLower(id)
|
||||
if _, found := outputConfigCreatorCache[id]; found {
|
||||
return errors.New("config creator has already been registered")
|
||||
}
|
||||
outputConfigCreatorCache[id] = fn
|
||||
return nil
|
||||
}
|
||||
|
||||
func createOutputConfig(id string, action Action, data json.RawMessage) (OutputConverter, error) {
|
||||
id = strings.ToLower(id)
|
||||
fn, found := outputConfigCreatorCache[id]
|
||||
if !found {
|
||||
return nil, errors.New("unknown config type")
|
||||
}
|
||||
return fn(action, data)
|
||||
}
|
||||
|
||||
type config struct {
|
||||
Input []*inputConvConfig `json:"input"`
|
||||
Output []*outputConvConfig `json:"output"`
|
||||
}
|
||||
|
||||
type inputConvConfig struct {
|
||||
iType string
|
||||
action Action
|
||||
converter InputConverter
|
||||
}
|
||||
|
||||
func (i *inputConvConfig) UnmarshalJSON(data []byte) error {
|
||||
var temp struct {
|
||||
Type string `json:"type"`
|
||||
Action Action `json:"action"`
|
||||
Args json.RawMessage `json:"args"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &temp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ActionsRegistry[temp.Action] {
|
||||
return fmt.Errorf("invalid action %s in type %s", temp.Action, temp.Type)
|
||||
}
|
||||
|
||||
config, err := createInputConfig(temp.Type, temp.Action, temp.Args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i.iType = config.GetType()
|
||||
i.action = config.GetAction()
|
||||
i.converter = config
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type outputConvConfig struct {
|
||||
iType string
|
||||
action Action
|
||||
converter OutputConverter
|
||||
}
|
||||
|
||||
func (i *outputConvConfig) UnmarshalJSON(data []byte) error {
|
||||
var temp struct {
|
||||
Type string `json:"type"`
|
||||
Action Action `json:"action"`
|
||||
Args json.RawMessage `json:"args"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &temp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if temp.Action == "" {
|
||||
temp.Action = ActionOutput
|
||||
}
|
||||
|
||||
if !ActionsRegistry[temp.Action] {
|
||||
return fmt.Errorf("invalid action %s in type %s", temp.Action, temp.Type)
|
||||
}
|
||||
|
||||
config, err := createOutputConfig(temp.Type, temp.Action, temp.Args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i.iType = config.GetType()
|
||||
i.action = config.GetAction()
|
||||
i.converter = config
|
||||
|
||||
return nil
|
||||
}
|
||||
13
src/xray-geoip/lib/error.go
Normal file
13
src/xray-geoip/lib/error.go
Normal file
@ -0,0 +1,13 @@
|
||||
package lib
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrDuplicatedConverter = errors.New("duplicated converter")
|
||||
ErrUnknownAction = errors.New("unknown action")
|
||||
ErrInvalidIPType = errors.New("invalid IP type")
|
||||
ErrInvalidIP = errors.New("invalid IP address")
|
||||
ErrInvalidIPLength = errors.New("invalid IP address length")
|
||||
ErrInvalidIPNet = errors.New("invalid IPNet address")
|
||||
ErrInvalidPrefixType = errors.New("invalid prefix type")
|
||||
)
|
||||
43
src/xray-geoip/lib/func.go
Normal file
43
src/xray-geoip/lib/func.go
Normal file
@ -0,0 +1,43 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
inputConverterMap = make(map[string]InputConverter)
|
||||
outputConverterMap = make(map[string]OutputConverter)
|
||||
)
|
||||
|
||||
func ListInputConverter() {
|
||||
fmt.Println("All available input formats:")
|
||||
for name, ic := range inputConverterMap {
|
||||
fmt.Printf(" - %s (%s)\n", name, ic.GetDescription())
|
||||
}
|
||||
}
|
||||
|
||||
func RegisterInputConverter(name string, c InputConverter) error {
|
||||
name = strings.TrimSpace(name)
|
||||
if _, ok := inputConverterMap[name]; ok {
|
||||
return ErrDuplicatedConverter
|
||||
}
|
||||
inputConverterMap[name] = c
|
||||
return nil
|
||||
}
|
||||
|
||||
func ListOutputConverter() {
|
||||
fmt.Println("All available output formats:")
|
||||
for name, oc := range outputConverterMap {
|
||||
fmt.Printf(" - %s (%s)\n", name, oc.GetDescription())
|
||||
}
|
||||
}
|
||||
|
||||
func RegisterOutputConverter(name string, c OutputConverter) error {
|
||||
name = strings.TrimSpace(name)
|
||||
if _, ok := outputConverterMap[name]; ok {
|
||||
return ErrDuplicatedConverter
|
||||
}
|
||||
outputConverterMap[name] = c
|
||||
return nil
|
||||
}
|
||||
65
src/xray-geoip/lib/instance.go
Normal file
65
src/xray-geoip/lib/instance.go
Normal file
@ -0,0 +1,65 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Instance struct {
|
||||
config *config
|
||||
input []InputConverter
|
||||
output []OutputConverter
|
||||
}
|
||||
|
||||
func NewInstance() (*Instance, error) {
|
||||
return &Instance{
|
||||
config: new(config),
|
||||
input: make([]InputConverter, 0),
|
||||
output: make([]OutputConverter, 0),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (i *Instance) Init(configFile string) error {
|
||||
content, err := os.ReadFile(configFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(content, &i.config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, input := range i.config.Input {
|
||||
i.input = append(i.input, input.converter)
|
||||
}
|
||||
|
||||
for _, output := range i.config.Output {
|
||||
i.output = append(i.output, output.converter)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Instance) Run() error {
|
||||
if len(i.input) == 0 || len(i.output) == 0 {
|
||||
return errors.New("input type and output type must be specified")
|
||||
}
|
||||
|
||||
var err error
|
||||
container := NewContainer()
|
||||
for _, ic := range i.input {
|
||||
container, err = ic.Input(container)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, oc := range i.output {
|
||||
if err := oc.Output(container); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
458
src/xray-geoip/lib/lib.go
Normal file
458
src/xray-geoip/lib/lib.go
Normal file
@ -0,0 +1,458 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"go4.org/netipx"
|
||||
)
|
||||
|
||||
const (
|
||||
ActionAdd Action = "add"
|
||||
ActionRemove Action = "remove"
|
||||
ActionOutput Action = "output"
|
||||
|
||||
IPv4 IPType = "ipv4"
|
||||
IPv6 IPType = "ipv6"
|
||||
)
|
||||
|
||||
var ActionsRegistry = map[Action]bool{
|
||||
ActionAdd: true,
|
||||
ActionRemove: true,
|
||||
ActionOutput: true,
|
||||
}
|
||||
|
||||
type Action string
|
||||
|
||||
type IPType string
|
||||
|
||||
type Typer interface {
|
||||
GetType() string
|
||||
}
|
||||
|
||||
type Actioner interface {
|
||||
GetAction() Action
|
||||
}
|
||||
|
||||
type Descriptioner interface {
|
||||
GetDescription() string
|
||||
}
|
||||
|
||||
type InputConverter interface {
|
||||
Typer
|
||||
Actioner
|
||||
Descriptioner
|
||||
Input(Container) (Container, error)
|
||||
}
|
||||
|
||||
type OutputConverter interface {
|
||||
Typer
|
||||
Actioner
|
||||
Descriptioner
|
||||
Output(Container) error
|
||||
}
|
||||
|
||||
type Entry struct {
|
||||
name string
|
||||
mu *sync.Mutex
|
||||
ipv4Builder *netipx.IPSetBuilder
|
||||
ipv6Builder *netipx.IPSetBuilder
|
||||
}
|
||||
|
||||
func NewEntry(name string) *Entry {
|
||||
return &Entry{
|
||||
name: strings.ToUpper(strings.TrimSpace(name)),
|
||||
mu: new(sync.Mutex),
|
||||
ipv4Builder: new(netipx.IPSetBuilder),
|
||||
ipv6Builder: new(netipx.IPSetBuilder),
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Entry) GetName() string {
|
||||
return e.name
|
||||
}
|
||||
|
||||
func (e *Entry) hasIPv4Builder() bool {
|
||||
return e.ipv4Builder != nil
|
||||
}
|
||||
|
||||
func (e *Entry) hasIPv6Builder() bool {
|
||||
return e.ipv6Builder != nil
|
||||
}
|
||||
|
||||
func (e *Entry) processPrefix(src any) (*netip.Prefix, IPType, error) {
|
||||
switch src := src.(type) {
|
||||
case net.IP:
|
||||
ip, ok := netipx.FromStdIP(src)
|
||||
if !ok {
|
||||
return nil, "", ErrInvalidIP
|
||||
}
|
||||
switch {
|
||||
case ip.Is4():
|
||||
prefix := netip.PrefixFrom(ip, 32)
|
||||
return &prefix, IPv4, nil
|
||||
case ip.Is6():
|
||||
prefix := netip.PrefixFrom(ip, 128)
|
||||
return &prefix, IPv6, nil
|
||||
default:
|
||||
return nil, "", ErrInvalidIPLength
|
||||
}
|
||||
|
||||
case *net.IPNet:
|
||||
prefix, ok := netipx.FromStdIPNet(src)
|
||||
if !ok {
|
||||
return nil, "", ErrInvalidIPNet
|
||||
}
|
||||
ip := prefix.Addr()
|
||||
switch {
|
||||
case ip.Is4():
|
||||
return &prefix, IPv4, nil
|
||||
case ip.Is6():
|
||||
return &prefix, IPv6, nil
|
||||
default:
|
||||
return nil, "", ErrInvalidIPLength
|
||||
}
|
||||
|
||||
case netip.Addr:
|
||||
switch {
|
||||
case src.Is4():
|
||||
prefix := netip.PrefixFrom(src, 32)
|
||||
return &prefix, IPv4, nil
|
||||
case src.Is6():
|
||||
prefix := netip.PrefixFrom(src, 128)
|
||||
return &prefix, IPv6, nil
|
||||
default:
|
||||
return nil, "", ErrInvalidIPLength
|
||||
}
|
||||
|
||||
case *netip.Addr:
|
||||
switch {
|
||||
case src.Is4():
|
||||
prefix := netip.PrefixFrom(*src, 32)
|
||||
return &prefix, IPv4, nil
|
||||
case src.Is6():
|
||||
prefix := netip.PrefixFrom(*src, 128)
|
||||
return &prefix, IPv6, nil
|
||||
default:
|
||||
return nil, "", ErrInvalidIPLength
|
||||
}
|
||||
|
||||
case netip.Prefix:
|
||||
ip := src.Addr()
|
||||
switch {
|
||||
case ip.Is4():
|
||||
return &src, IPv4, nil
|
||||
case ip.Is6():
|
||||
return &src, IPv6, nil
|
||||
default:
|
||||
return nil, "", ErrInvalidIPLength
|
||||
}
|
||||
|
||||
case *netip.Prefix:
|
||||
ip := src.Addr()
|
||||
switch {
|
||||
case ip.Is4():
|
||||
return src, IPv4, nil
|
||||
case ip.Is6():
|
||||
return src, IPv6, nil
|
||||
default:
|
||||
return nil, "", ErrInvalidIPLength
|
||||
}
|
||||
|
||||
case string:
|
||||
_, network, err := net.ParseCIDR(src)
|
||||
switch err {
|
||||
case nil:
|
||||
prefix, ok := netipx.FromStdIPNet(network)
|
||||
if !ok {
|
||||
return nil, "", ErrInvalidIPNet
|
||||
}
|
||||
ip := prefix.Addr()
|
||||
switch {
|
||||
case ip.Is4():
|
||||
return &prefix, IPv4, nil
|
||||
case ip.Is6():
|
||||
return &prefix, IPv6, nil
|
||||
default:
|
||||
return nil, "", ErrInvalidIPLength
|
||||
}
|
||||
|
||||
default:
|
||||
ip, err := netip.ParseAddr(src)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
switch {
|
||||
case ip.Is4():
|
||||
prefix := netip.PrefixFrom(ip, 32)
|
||||
return &prefix, IPv4, nil
|
||||
case ip.Is6():
|
||||
prefix := netip.PrefixFrom(ip, 128)
|
||||
return &prefix, IPv6, nil
|
||||
default:
|
||||
return nil, "", ErrInvalidIPLength
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, "", ErrInvalidPrefixType
|
||||
}
|
||||
|
||||
func (e *Entry) add(prefix *netip.Prefix, ipType IPType) error {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
switch ipType {
|
||||
case IPv4:
|
||||
if !e.hasIPv4Builder() {
|
||||
e.ipv4Builder = new(netipx.IPSetBuilder)
|
||||
}
|
||||
e.ipv4Builder.AddPrefix(*prefix)
|
||||
case IPv6:
|
||||
if !e.hasIPv6Builder() {
|
||||
e.ipv6Builder = new(netipx.IPSetBuilder)
|
||||
}
|
||||
e.ipv6Builder.AddPrefix(*prefix)
|
||||
default:
|
||||
return ErrInvalidIPType
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Entry) remove(prefix *netip.Prefix, ipType IPType) error {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
switch ipType {
|
||||
case IPv4:
|
||||
if e.hasIPv4Builder() {
|
||||
e.ipv4Builder.RemovePrefix(*prefix)
|
||||
}
|
||||
case IPv6:
|
||||
if e.hasIPv6Builder() {
|
||||
e.ipv6Builder.RemovePrefix(*prefix)
|
||||
}
|
||||
default:
|
||||
return ErrInvalidIPType
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Entry) AddPrefix(cidr any) error {
|
||||
prefix, ipType, err := e.processPrefix(cidr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := e.add(prefix, ipType); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Entry) RemovePrefix(cidr string) error {
|
||||
prefix, ipType, err := e.processPrefix(cidr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := e.remove(prefix, ipType); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Entry) MarshalText(opts ...IgnoreIPOption) ([]string, error) {
|
||||
var ignoreIPType IPType
|
||||
for _, opt := range opts {
|
||||
if opt != nil {
|
||||
ignoreIPType = opt()
|
||||
}
|
||||
}
|
||||
disableIPv4, disableIPv6 := false, false
|
||||
switch ignoreIPType {
|
||||
case IPv4:
|
||||
disableIPv4 = true
|
||||
case IPv6:
|
||||
disableIPv6 = true
|
||||
}
|
||||
|
||||
prefixSet := make([]string, 0, 1024)
|
||||
|
||||
if !disableIPv4 && e.hasIPv4Builder() {
|
||||
ipv4set, err := e.ipv4Builder.IPSet()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prefixes := ipv4set.Prefixes()
|
||||
for _, prefix := range prefixes {
|
||||
prefixSet = append(prefixSet, prefix.String())
|
||||
}
|
||||
}
|
||||
|
||||
if !disableIPv6 && e.hasIPv6Builder() {
|
||||
ipv6set, err := e.ipv6Builder.IPSet()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prefixes := ipv6set.Prefixes()
|
||||
for _, prefix := range prefixes {
|
||||
prefixSet = append(prefixSet, prefix.String())
|
||||
}
|
||||
}
|
||||
|
||||
if len(prefixSet) > 0 {
|
||||
return prefixSet, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("entry %s has no prefix", e.GetName())
|
||||
}
|
||||
|
||||
type IgnoreIPOption func() IPType
|
||||
|
||||
func IgnoreIPv4() IPType {
|
||||
return IPv4
|
||||
}
|
||||
|
||||
func IgnoreIPv6() IPType {
|
||||
return IPv6
|
||||
}
|
||||
|
||||
type Container interface {
|
||||
GetEntry(name string) (*Entry, bool)
|
||||
Add(entry *Entry, opts ...IgnoreIPOption) error
|
||||
Remove(name string, opts ...IgnoreIPOption)
|
||||
Loop() <-chan *Entry
|
||||
}
|
||||
|
||||
type container struct {
|
||||
entries *sync.Map // map[name]*Entry
|
||||
}
|
||||
|
||||
func NewContainer() Container {
|
||||
return &container{
|
||||
entries: new(sync.Map),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *container) isValid() bool {
|
||||
if c == nil || c.entries == nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *container) GetEntry(name string) (*Entry, bool) {
|
||||
if !c.isValid() {
|
||||
return nil, false
|
||||
}
|
||||
val, ok := c.entries.Load(strings.ToUpper(strings.TrimSpace(name)))
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
return val.(*Entry), true
|
||||
}
|
||||
|
||||
func (c *container) Loop() <-chan *Entry {
|
||||
ch := make(chan *Entry, 300)
|
||||
go func() {
|
||||
c.entries.Range(func(key, value any) bool {
|
||||
ch <- value.(*Entry)
|
||||
return true
|
||||
})
|
||||
close(ch)
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
func (c *container) Add(entry *Entry, opts ...IgnoreIPOption) error {
|
||||
var ignoreIPType IPType
|
||||
for _, opt := range opts {
|
||||
if opt != nil {
|
||||
ignoreIPType = opt()
|
||||
}
|
||||
}
|
||||
|
||||
name := entry.GetName()
|
||||
val, found := c.GetEntry(name)
|
||||
switch found {
|
||||
case true:
|
||||
var ipv4set, ipv6set *netipx.IPSet
|
||||
var err4, err6 error
|
||||
if entry.hasIPv4Builder() {
|
||||
ipv4set, err4 = entry.ipv4Builder.IPSet()
|
||||
if err4 != nil {
|
||||
return err4
|
||||
}
|
||||
}
|
||||
if entry.hasIPv6Builder() {
|
||||
ipv6set, err6 = entry.ipv6Builder.IPSet()
|
||||
if err6 != nil {
|
||||
return err6
|
||||
}
|
||||
}
|
||||
switch ignoreIPType {
|
||||
case IPv4:
|
||||
if !val.hasIPv6Builder() {
|
||||
val.ipv6Builder = new(netipx.IPSetBuilder)
|
||||
}
|
||||
val.ipv6Builder.AddSet(ipv6set)
|
||||
case IPv6:
|
||||
if !val.hasIPv4Builder() {
|
||||
val.ipv4Builder = new(netipx.IPSetBuilder)
|
||||
}
|
||||
val.ipv4Builder.AddSet(ipv4set)
|
||||
default:
|
||||
if !val.hasIPv4Builder() {
|
||||
val.ipv4Builder = new(netipx.IPSetBuilder)
|
||||
}
|
||||
if !val.hasIPv6Builder() {
|
||||
val.ipv6Builder = new(netipx.IPSetBuilder)
|
||||
}
|
||||
val.ipv4Builder.AddSet(ipv4set)
|
||||
val.ipv6Builder.AddSet(ipv6set)
|
||||
}
|
||||
c.entries.Store(name, val)
|
||||
|
||||
case false:
|
||||
switch ignoreIPType {
|
||||
case IPv4:
|
||||
entry.ipv4Builder = nil
|
||||
case IPv6:
|
||||
entry.ipv6Builder = nil
|
||||
}
|
||||
c.entries.Store(name, entry)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *container) Remove(name string, opts ...IgnoreIPOption) {
|
||||
val, found := c.GetEntry(name)
|
||||
if !found {
|
||||
log.Printf("failed to remove non-existent entry %s", name)
|
||||
return
|
||||
}
|
||||
|
||||
var ignoreIPType IPType
|
||||
for _, opt := range opts {
|
||||
if opt != nil {
|
||||
ignoreIPType = opt()
|
||||
}
|
||||
}
|
||||
|
||||
switch ignoreIPType {
|
||||
case IPv4:
|
||||
val.ipv6Builder = nil
|
||||
c.entries.Store(name, val)
|
||||
case IPv6:
|
||||
val.ipv4Builder = nil
|
||||
c.entries.Store(name, val)
|
||||
default:
|
||||
c.entries.Delete(name)
|
||||
}
|
||||
}
|
||||
41
src/xray-geoip/main.go
Normal file
41
src/xray-geoip/main.go
Normal file
@ -0,0 +1,41 @@
|
||||
// GeoIP generator
|
||||
//
|
||||
// Before running this file, the GeoIP database must be downloaded and present.
|
||||
// To download GeoIP database: https://dev.maxmind.com/geoip/geoip2/geolite2/
|
||||
// Inside you will find block files for IPv4 and IPv6 and country code mapping.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
|
||||
"github.com/v2fly/geoip/lib"
|
||||
)
|
||||
|
||||
var (
|
||||
list = flag.Bool("l", false, "List all available input and output formats")
|
||||
configFile = flag.String("c", "config.json", "Path to the config file")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if *list {
|
||||
lib.ListInputConverter()
|
||||
lib.ListOutputConverter()
|
||||
return
|
||||
}
|
||||
|
||||
instance, err := lib.NewInstance()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := instance.Init(*configFile); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := instance.Run(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
BIN
src/xray-geoip/output/dat/geoip.dat
Normal file
BIN
src/xray-geoip/output/dat/geoip.dat
Normal file
Binary file not shown.
10417
src/xray-geoip/output/dat/nord.dat
Normal file
10417
src/xray-geoip/output/dat/nord.dat
Normal file
File diff suppressed because it is too large
Load Diff
214
src/xray-geoip/plugin/maxmind/country_csv.go
Normal file
214
src/xray-geoip/plugin/maxmind/country_csv.go
Normal file
@ -0,0 +1,214 @@
|
||||
package maxmind
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/v2fly/geoip/lib"
|
||||
)
|
||||
|
||||
const (
|
||||
typeCountryCSV = "maxmindGeoLite2CountryCSV"
|
||||
descCountryCSV = "Convert MaxMind GeoLite2 country CSV data to other formats"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultCCFile = filepath.Join("./", "geolite2", "GeoLite2-Country-Locations-en.csv")
|
||||
defaultIPv4File = filepath.Join("./", "geolite2", "GeoLite2-Country-Blocks-IPv4.csv")
|
||||
defaultIPv6File = filepath.Join("./", "geolite2", "GeoLite2-Country-Blocks-IPv6.csv")
|
||||
)
|
||||
|
||||
func init() {
|
||||
lib.RegisterInputConfigCreator(typeCountryCSV, func(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
|
||||
return newGeoLite2CountryCSV(action, data)
|
||||
})
|
||||
lib.RegisterInputConverter(typeCountryCSV, &geoLite2CountryCSV{
|
||||
Description: descCountryCSV,
|
||||
})
|
||||
}
|
||||
|
||||
func newGeoLite2CountryCSV(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
|
||||
var tmp struct {
|
||||
CountryCodeFile string `json:"country"`
|
||||
IPv4File string `json:"ipv4"`
|
||||
IPv6File string `json:"ipv6"`
|
||||
Want []string `json:"wantedList"`
|
||||
OnlyIPType lib.IPType `json:"onlyIPType"`
|
||||
}
|
||||
|
||||
if len(data) > 0 {
|
||||
if err := json.Unmarshal(data, &tmp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if tmp.CountryCodeFile == "" {
|
||||
tmp.CountryCodeFile = defaultCCFile
|
||||
}
|
||||
|
||||
if tmp.IPv4File == "" {
|
||||
tmp.IPv4File = defaultIPv4File
|
||||
}
|
||||
|
||||
if tmp.IPv6File == "" {
|
||||
tmp.IPv6File = defaultIPv6File
|
||||
}
|
||||
|
||||
return &geoLite2CountryCSV{
|
||||
Type: typeCountryCSV,
|
||||
Action: action,
|
||||
Description: descCountryCSV,
|
||||
CountryCodeFile: tmp.CountryCodeFile,
|
||||
IPv4File: tmp.IPv4File,
|
||||
IPv6File: tmp.IPv6File,
|
||||
Want: tmp.Want,
|
||||
OnlyIPType: tmp.OnlyIPType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type geoLite2CountryCSV struct {
|
||||
Type string
|
||||
Action lib.Action
|
||||
Description string
|
||||
CountryCodeFile string
|
||||
IPv4File string
|
||||
IPv6File string
|
||||
Want []string
|
||||
OnlyIPType lib.IPType
|
||||
}
|
||||
|
||||
func (g *geoLite2CountryCSV) GetType() string {
|
||||
return g.Type
|
||||
}
|
||||
|
||||
func (g *geoLite2CountryCSV) GetAction() lib.Action {
|
||||
return g.Action
|
||||
}
|
||||
|
||||
func (g *geoLite2CountryCSV) GetDescription() string {
|
||||
return g.Description
|
||||
}
|
||||
|
||||
func (g *geoLite2CountryCSV) Input(container lib.Container) (lib.Container, error) {
|
||||
ccMap, err := g.getCountryCode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
entries := make(map[string]*lib.Entry)
|
||||
|
||||
if g.IPv4File != "" {
|
||||
if err := g.process(g.IPv4File, ccMap, entries); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if g.IPv6File != "" {
|
||||
if err := g.process(g.IPv6File, ccMap, entries); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var ignoreIPType lib.IgnoreIPOption
|
||||
switch g.OnlyIPType {
|
||||
case lib.IPv4:
|
||||
ignoreIPType = lib.IgnoreIPv6
|
||||
case lib.IPv6:
|
||||
ignoreIPType = lib.IgnoreIPv4
|
||||
}
|
||||
|
||||
for name, entry := range entries {
|
||||
switch g.Action {
|
||||
case lib.ActionAdd:
|
||||
if err := container.Add(entry, ignoreIPType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case lib.ActionRemove:
|
||||
container.Remove(name, ignoreIPType)
|
||||
default:
|
||||
return nil, lib.ErrUnknownAction
|
||||
}
|
||||
}
|
||||
|
||||
return container, nil
|
||||
}
|
||||
|
||||
func (g *geoLite2CountryCSV) getCountryCode() (map[string]string, error) {
|
||||
ccReader, err := os.Open(g.CountryCodeFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer ccReader.Close()
|
||||
|
||||
reader := csv.NewReader(ccReader)
|
||||
lines, err := reader.ReadAll()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ccMap := make(map[string]string)
|
||||
for _, line := range lines[1:] {
|
||||
id := strings.TrimSpace(line[0])
|
||||
countryCode := strings.TrimSpace(line[4])
|
||||
if id == "" || countryCode == "" {
|
||||
continue
|
||||
}
|
||||
ccMap[id] = strings.ToUpper(countryCode)
|
||||
}
|
||||
return ccMap, nil
|
||||
}
|
||||
|
||||
func (g *geoLite2CountryCSV) process(file string, ccMap map[string]string, entries map[string]*lib.Entry) error {
|
||||
if len(ccMap) == 0 {
|
||||
return errors.New("country code list must be specified")
|
||||
}
|
||||
if entries == nil {
|
||||
entries = make(map[string]*lib.Entry)
|
||||
}
|
||||
|
||||
fReader, err := os.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fReader.Close()
|
||||
|
||||
reader := csv.NewReader(fReader)
|
||||
lines, err := reader.ReadAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Filter want list
|
||||
wantList := make(map[string]bool)
|
||||
for _, want := range g.Want {
|
||||
if want = strings.ToUpper(strings.TrimSpace(want)); want != "" {
|
||||
wantList[want] = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, line := range lines[1:] {
|
||||
ccID := strings.TrimSpace(line[1])
|
||||
if countryCode, found := ccMap[ccID]; found {
|
||||
if len(wantList) > 0 {
|
||||
if _, found := wantList[countryCode]; !found {
|
||||
continue
|
||||
}
|
||||
}
|
||||
cidrStr := strings.ToLower(strings.TrimSpace(line[0]))
|
||||
entry, found := entries[countryCode]
|
||||
if !found {
|
||||
entry = lib.NewEntry(countryCode)
|
||||
}
|
||||
if err := entry.AddPrefix(cidrStr); err != nil {
|
||||
return err
|
||||
}
|
||||
entries[countryCode] = entry
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
227
src/xray-geoip/plugin/maxmind/mmdb_in.go
Normal file
227
src/xray-geoip/plugin/maxmind/mmdb_in.go
Normal file
@ -0,0 +1,227 @@
|
||||
package maxmind
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/oschwald/maxminddb-golang"
|
||||
"github.com/v2fly/geoip/lib"
|
||||
)
|
||||
|
||||
const (
|
||||
typeMaxmindMMDBIn = "maxmindMMDB"
|
||||
descMaxmindMMDBIn = "Convert MaxMind mmdb database to other formats"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultMMDBFile = filepath.Join("./", "geolite2", "GeoLite2-Country.mmdb")
|
||||
tempMMDBPath = filepath.Join("./", "tmp")
|
||||
tempMMDBFile = filepath.Join(tempMMDBPath, "input.mmdb")
|
||||
)
|
||||
|
||||
func init() {
|
||||
lib.RegisterInputConfigCreator(typeMaxmindMMDBIn, func(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
|
||||
return newMaxmindMMDBIn(action, data)
|
||||
})
|
||||
lib.RegisterInputConverter(typeMaxmindMMDBIn, &maxmindMMDBIn{
|
||||
Description: descMaxmindMMDBIn,
|
||||
})
|
||||
}
|
||||
|
||||
func newMaxmindMMDBIn(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
|
||||
var tmp struct {
|
||||
URI string `json:"uri"`
|
||||
Want []string `json:"wantedList"`
|
||||
OnlyIPType lib.IPType `json:"onlyIPType"`
|
||||
}
|
||||
|
||||
if len(data) > 0 {
|
||||
if err := json.Unmarshal(data, &tmp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if tmp.URI == "" {
|
||||
tmp.URI = defaultMMDBFile
|
||||
}
|
||||
|
||||
return &maxmindMMDBIn{
|
||||
Type: typeMaxmindMMDBIn,
|
||||
Action: action,
|
||||
Description: descMaxmindMMDBIn,
|
||||
URI: tmp.URI,
|
||||
Want: tmp.Want,
|
||||
OnlyIPType: tmp.OnlyIPType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type maxmindMMDBIn struct {
|
||||
Type string
|
||||
Action lib.Action
|
||||
Description string
|
||||
URI string
|
||||
Want []string
|
||||
OnlyIPType lib.IPType
|
||||
}
|
||||
|
||||
func (g *maxmindMMDBIn) GetType() string {
|
||||
return g.Type
|
||||
}
|
||||
|
||||
func (g *maxmindMMDBIn) GetAction() lib.Action {
|
||||
return g.Action
|
||||
}
|
||||
|
||||
func (g *maxmindMMDBIn) GetDescription() string {
|
||||
return g.Description
|
||||
}
|
||||
|
||||
func (g *maxmindMMDBIn) Input(container lib.Container) (lib.Container, error) {
|
||||
var fd io.ReadCloser
|
||||
var err error
|
||||
switch {
|
||||
case strings.HasPrefix(g.URI, "http://"), strings.HasPrefix(g.URI, "https://"):
|
||||
fd, err = g.downloadFile(g.URI)
|
||||
default:
|
||||
fd, err = os.Open(g.URI)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = g.moveFile(fd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
entries := make(map[string]*lib.Entry)
|
||||
err = g.generateEntries(entries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(entries) == 0 {
|
||||
return nil, fmt.Errorf("❌ [type %s | action %s] no entry is newly generated", typeMaxmindMMDBIn, g.Action)
|
||||
}
|
||||
|
||||
var ignoreIPType lib.IgnoreIPOption
|
||||
switch g.OnlyIPType {
|
||||
case lib.IPv4:
|
||||
ignoreIPType = lib.IgnoreIPv6
|
||||
case lib.IPv6:
|
||||
ignoreIPType = lib.IgnoreIPv4
|
||||
}
|
||||
|
||||
// Filter want list
|
||||
wantList := make(map[string]bool)
|
||||
for _, want := range g.Want {
|
||||
if want = strings.ToUpper(strings.TrimSpace(want)); want != "" {
|
||||
wantList[want] = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
name := entry.GetName()
|
||||
if len(wantList) > 0 && !wantList[name] {
|
||||
continue
|
||||
}
|
||||
|
||||
switch g.Action {
|
||||
case lib.ActionAdd:
|
||||
if err := container.Add(entry, ignoreIPType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case lib.ActionRemove:
|
||||
container.Remove(name, ignoreIPType)
|
||||
}
|
||||
}
|
||||
|
||||
return container, nil
|
||||
}
|
||||
|
||||
func (g *maxmindMMDBIn) downloadFile(url string) (io.ReadCloser, error) {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("failed to get remote file %s, http status code %d", url, resp.StatusCode)
|
||||
}
|
||||
|
||||
return resp.Body, nil
|
||||
}
|
||||
|
||||
func (g *maxmindMMDBIn) moveFile(src io.ReadCloser) error {
|
||||
defer src.Close()
|
||||
|
||||
err := os.MkdirAll(tempMMDBPath, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out, err := os.Create(tempMMDBFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
_, err = io.Copy(out, src)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (g *maxmindMMDBIn) generateEntries(entries map[string]*lib.Entry) error {
|
||||
db, err := maxminddb.Open(tempMMDBFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
record := struct {
|
||||
Country struct {
|
||||
IsoCode string `maxminddb:"iso_code"`
|
||||
} `maxminddb:"country"`
|
||||
}{}
|
||||
|
||||
networks := db.Networks(maxminddb.SkipAliasedNetworks)
|
||||
for networks.Next() {
|
||||
subnet, err := networks.Network(&record)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var entry *lib.Entry
|
||||
name := strings.ToUpper(record.Country.IsoCode)
|
||||
if theEntry, found := entries[name]; found {
|
||||
entry = theEntry
|
||||
} else {
|
||||
entry = lib.NewEntry(name)
|
||||
}
|
||||
|
||||
switch g.Action {
|
||||
case lib.ActionAdd:
|
||||
if err := entry.AddPrefix(subnet); err != nil {
|
||||
return err
|
||||
}
|
||||
case lib.ActionRemove:
|
||||
if err := entry.RemovePrefix(subnet.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
entries[name] = entry
|
||||
}
|
||||
|
||||
if networks.Err() != nil {
|
||||
return networks.Err()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
227
src/xray-geoip/plugin/plaintext/text_in.go
Normal file
227
src/xray-geoip/plugin/plaintext/text_in.go
Normal file
@ -0,0 +1,227 @@
|
||||
package plaintext
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/v2fly/geoip/lib"
|
||||
)
|
||||
|
||||
const (
|
||||
typeTextIn = "text"
|
||||
descTextIn = "Convert plaintext IP and CIDR to other formats"
|
||||
)
|
||||
|
||||
func init() {
|
||||
lib.RegisterInputConfigCreator(typeTextIn, func(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
|
||||
return newTextIn(action, data)
|
||||
})
|
||||
lib.RegisterInputConverter(typeTextIn, &textIn{
|
||||
Description: descTextIn,
|
||||
})
|
||||
}
|
||||
|
||||
func newTextIn(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
|
||||
var tmp struct {
|
||||
Name string `json:"name"`
|
||||
URI string `json:"uri"`
|
||||
InputDir string `json:"inputDir"`
|
||||
OnlyIPType lib.IPType `json:"onlyIPType"`
|
||||
}
|
||||
|
||||
if len(data) > 0 {
|
||||
if err := json.Unmarshal(data, &tmp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if tmp.Name == "" && tmp.URI == "" && tmp.InputDir == "" {
|
||||
return nil, fmt.Errorf("type %s | action %s missing inputdir or name or uri", typeTextIn, action)
|
||||
}
|
||||
|
||||
if (tmp.Name != "" && tmp.URI == "") || (tmp.Name == "" && tmp.URI != "") {
|
||||
return nil, fmt.Errorf("type %s | action %s name & uri must be specified together", typeTextIn, action)
|
||||
}
|
||||
|
||||
return &textIn{
|
||||
Type: typeTextIn,
|
||||
Action: action,
|
||||
Description: descTextIn,
|
||||
Name: tmp.Name,
|
||||
URI: tmp.URI,
|
||||
InputDir: tmp.InputDir,
|
||||
OnlyIPType: tmp.OnlyIPType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type textIn struct {
|
||||
Type string
|
||||
Action lib.Action
|
||||
Description string
|
||||
Name string
|
||||
URI string
|
||||
InputDir string
|
||||
OnlyIPType lib.IPType
|
||||
}
|
||||
|
||||
func (t *textIn) GetType() string {
|
||||
return t.Type
|
||||
}
|
||||
|
||||
func (t *textIn) GetAction() lib.Action {
|
||||
return t.Action
|
||||
}
|
||||
|
||||
func (t *textIn) GetDescription() string {
|
||||
return t.Description
|
||||
}
|
||||
|
||||
func (t *textIn) Input(container lib.Container) (lib.Container, error) {
|
||||
entries := make(map[string]*lib.Entry)
|
||||
var err error
|
||||
|
||||
switch {
|
||||
case t.InputDir != "":
|
||||
err = t.walkDir(t.InputDir, entries)
|
||||
case t.Name != "" && t.URI != "":
|
||||
switch {
|
||||
case strings.HasPrefix(t.URI, "http://"), strings.HasPrefix(t.URI, "https://"):
|
||||
err = t.walkRemoteFile(t.URI, t.Name, entries)
|
||||
default:
|
||||
err = t.walkLocalFile(t.URI, t.Name, entries)
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("config missing argument inputDir or name or uri")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ignoreIPType lib.IgnoreIPOption
|
||||
switch t.OnlyIPType {
|
||||
case lib.IPv4:
|
||||
ignoreIPType = lib.IgnoreIPv6
|
||||
case lib.IPv6:
|
||||
ignoreIPType = lib.IgnoreIPv4
|
||||
}
|
||||
|
||||
if len(entries) == 0 {
|
||||
return nil, fmt.Errorf("type %s | action %s no entry are generated", t.Type, t.Action)
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
switch t.Action {
|
||||
case lib.ActionAdd:
|
||||
if err := container.Add(entry, ignoreIPType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case lib.ActionRemove:
|
||||
container.Remove(entry.GetName(), ignoreIPType)
|
||||
}
|
||||
}
|
||||
|
||||
return container, nil
|
||||
}
|
||||
|
||||
func (t *textIn) walkDir(dir string, entries map[string]*lib.Entry) error {
|
||||
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := t.walkLocalFile(path, "", entries); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *textIn) walkLocalFile(path, name string, entries map[string]*lib.Entry) error {
|
||||
name = strings.TrimSpace(name)
|
||||
var filename string
|
||||
if name != "" {
|
||||
filename = name
|
||||
} else {
|
||||
filename = filepath.Base(path)
|
||||
}
|
||||
|
||||
// check filename
|
||||
if !regexp.MustCompile(`^[a-zA-Z0-9_.\-]+$`).MatchString(filename) {
|
||||
return fmt.Errorf("filename %s cannot be entry name, please remove special characters in it", filename)
|
||||
}
|
||||
dotIndex := strings.LastIndex(filename, ".")
|
||||
if dotIndex > 0 {
|
||||
filename = filename[:dotIndex]
|
||||
}
|
||||
|
||||
if _, found := entries[filename]; found {
|
||||
return fmt.Errorf("found duplicated file %s", filename)
|
||||
}
|
||||
|
||||
entry := lib.NewEntry(filename)
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
if err := t.scanFile(file, entry); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
entries[filename] = entry
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *textIn) walkRemoteFile(url, name string, entries map[string]*lib.Entry) error {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return fmt.Errorf("failed to get remote file %s, http status code %d", url, resp.StatusCode)
|
||||
}
|
||||
|
||||
entry := lib.NewEntry(name)
|
||||
if err := t.scanFile(resp.Body, entry); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
entries[name] = entry
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *textIn) scanFile(reader io.Reader, entry *lib.Entry) error {
|
||||
scanner := bufio.NewScanner(reader)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
if err := entry.AddPrefix(line); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
166
src/xray-geoip/plugin/plaintext/text_out.go
Normal file
166
src/xray-geoip/plugin/plaintext/text_out.go
Normal file
@ -0,0 +1,166 @@
|
||||
package plaintext
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/v2fly/geoip/lib"
|
||||
)
|
||||
|
||||
const (
|
||||
typeTextOut = "text"
|
||||
descTextOut = "Convert data to plaintext CIDR format"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultOutputDir = filepath.Join("./", "output", "text")
|
||||
)
|
||||
|
||||
func init() {
|
||||
lib.RegisterOutputConfigCreator(typeTextOut, func(action lib.Action, data json.RawMessage) (lib.OutputConverter, error) {
|
||||
return newTextOut(action, data)
|
||||
})
|
||||
lib.RegisterOutputConverter(typeTextOut, &textOut{
|
||||
Description: descTextOut,
|
||||
})
|
||||
}
|
||||
|
||||
func newTextOut(action lib.Action, data json.RawMessage) (lib.OutputConverter, error) {
|
||||
var tmp struct {
|
||||
OutputDir string `json:"outputDir"`
|
||||
Want []string `json:"wantedList"`
|
||||
OnlyIPType lib.IPType `json:"onlyIPType"`
|
||||
}
|
||||
|
||||
if len(data) > 0 {
|
||||
if err := json.Unmarshal(data, &tmp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if tmp.OutputDir == "" {
|
||||
tmp.OutputDir = defaultOutputDir
|
||||
}
|
||||
|
||||
return &textOut{
|
||||
Type: typeTextOut,
|
||||
Action: action,
|
||||
Description: descTextOut,
|
||||
OutputDir: tmp.OutputDir,
|
||||
Want: tmp.Want,
|
||||
OnlyIPType: tmp.OnlyIPType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type textOut struct {
|
||||
Type string
|
||||
Action lib.Action
|
||||
Description string
|
||||
OutputDir string
|
||||
Want []string
|
||||
OnlyIPType lib.IPType
|
||||
}
|
||||
|
||||
func (t *textOut) GetType() string {
|
||||
return t.Type
|
||||
}
|
||||
|
||||
func (t *textOut) GetAction() lib.Action {
|
||||
return t.Action
|
||||
}
|
||||
|
||||
func (t *textOut) GetDescription() string {
|
||||
return t.Description
|
||||
}
|
||||
|
||||
func (t *textOut) Output(container lib.Container) error {
|
||||
// Filter want list
|
||||
wantList := make(map[string]bool)
|
||||
for _, want := range t.Want {
|
||||
if want = strings.ToUpper(strings.TrimSpace(want)); want != "" {
|
||||
wantList[want] = true
|
||||
}
|
||||
}
|
||||
|
||||
switch len(wantList) {
|
||||
case 0:
|
||||
for entry := range container.Loop() {
|
||||
cidrList, err := t.marshalText(entry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filename := strings.ToLower(entry.GetName()) + ".txt"
|
||||
if err := t.writeFile(filename, cidrList); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
for name := range wantList {
|
||||
entry, found := container.GetEntry(name)
|
||||
if !found {
|
||||
log.Printf("❌ entry %s not found", name)
|
||||
continue
|
||||
}
|
||||
cidrList, err := t.marshalText(entry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filename := strings.ToLower(entry.GetName()) + ".txt"
|
||||
if err := t.writeFile(filename, cidrList); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *textOut) marshalText(entry *lib.Entry) ([]string, error) {
|
||||
var entryCidr []string
|
||||
var err error
|
||||
switch t.OnlyIPType {
|
||||
case lib.IPv4:
|
||||
entryCidr, err = entry.MarshalText(lib.IgnoreIPv6)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case lib.IPv6:
|
||||
entryCidr, err = entry.MarshalText(lib.IgnoreIPv4)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
entryCidr, err = entry.MarshalText()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return entryCidr, nil
|
||||
}
|
||||
|
||||
func (t *textOut) writeFile(filename string, cidrList []string) error {
|
||||
var buf bytes.Buffer
|
||||
for _, cidr := range cidrList {
|
||||
buf.WriteString(cidr)
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
cidrBytes := buf.Bytes()
|
||||
|
||||
if err := os.MkdirAll(t.OutputDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.WriteFile(filepath.Join(t.OutputDir, filename), cidrBytes, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("✅ [%s] %s --> %s", t.Type, filename, t.OutputDir)
|
||||
|
||||
return nil
|
||||
}
|
||||
96
src/xray-geoip/plugin/special/cutter.go
Normal file
96
src/xray-geoip/plugin/special/cutter.go
Normal file
@ -0,0 +1,96 @@
|
||||
package special
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/v2fly/geoip/lib"
|
||||
)
|
||||
|
||||
const (
|
||||
typeCutter = "cutter"
|
||||
descCutter = "Remove data from previous steps"
|
||||
)
|
||||
|
||||
func init() {
|
||||
lib.RegisterInputConfigCreator(typeCutter, func(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
|
||||
return newCutter(action, data)
|
||||
})
|
||||
lib.RegisterInputConverter(typeCutter, &cutter{
|
||||
Description: descCutter,
|
||||
})
|
||||
}
|
||||
|
||||
func newCutter(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
|
||||
var tmp struct {
|
||||
Want []string `json:"wantedList"`
|
||||
OnlyIPType lib.IPType `json:"onlyIPType"`
|
||||
}
|
||||
|
||||
if len(data) > 0 {
|
||||
if err := json.Unmarshal(data, &tmp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if action != lib.ActionRemove {
|
||||
return nil, fmt.Errorf("type %s only supports `remove` action", typeCutter)
|
||||
}
|
||||
|
||||
return &cutter{
|
||||
Type: typeCutter,
|
||||
Action: action,
|
||||
Description: descCutter,
|
||||
Want: tmp.Want,
|
||||
OnlyIPType: tmp.OnlyIPType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type cutter struct {
|
||||
Type string
|
||||
Action lib.Action
|
||||
Description string
|
||||
Want []string
|
||||
OnlyIPType lib.IPType
|
||||
}
|
||||
|
||||
func (c *cutter) GetType() string {
|
||||
return c.Type
|
||||
}
|
||||
|
||||
func (c *cutter) GetAction() lib.Action {
|
||||
return c.Action
|
||||
}
|
||||
|
||||
func (c *cutter) GetDescription() string {
|
||||
return c.Description
|
||||
}
|
||||
|
||||
func (c *cutter) Input(container lib.Container) (lib.Container, error) {
|
||||
var ignoreIPType lib.IgnoreIPOption
|
||||
switch c.OnlyIPType {
|
||||
case lib.IPv4:
|
||||
ignoreIPType = lib.IgnoreIPv6
|
||||
case lib.IPv6:
|
||||
ignoreIPType = lib.IgnoreIPv4
|
||||
}
|
||||
|
||||
// Filter want list
|
||||
wantList := make(map[string]bool)
|
||||
for _, want := range c.Want {
|
||||
if want = strings.ToUpper(strings.TrimSpace(want)); want != "" {
|
||||
wantList[want] = true
|
||||
}
|
||||
}
|
||||
|
||||
for entry := range container.Loop() {
|
||||
name := entry.GetName()
|
||||
if len(wantList) > 0 && !wantList[name] {
|
||||
continue
|
||||
}
|
||||
container.Remove(name, ignoreIPType)
|
||||
}
|
||||
|
||||
return container, nil
|
||||
}
|
||||
94
src/xray-geoip/plugin/special/private.go
Normal file
94
src/xray-geoip/plugin/special/private.go
Normal file
@ -0,0 +1,94 @@
|
||||
package special
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/v2fly/geoip/lib"
|
||||
)
|
||||
|
||||
const (
|
||||
entryNamePrivate = "private"
|
||||
typePrivate = "private"
|
||||
descPrivate = "Convert LAN and private network CIDR to other formats"
|
||||
)
|
||||
|
||||
var privateCIDRs = []string{
|
||||
"0.0.0.0/8",
|
||||
"10.0.0.0/8",
|
||||
"100.64.0.0/10",
|
||||
"127.0.0.0/8",
|
||||
"169.254.0.0/16",
|
||||
"172.16.0.0/12",
|
||||
"192.0.0.0/24",
|
||||
"192.0.2.0/24",
|
||||
"192.88.99.0/24",
|
||||
"192.168.0.0/16",
|
||||
"198.18.0.0/15",
|
||||
"198.51.100.0/24",
|
||||
"203.0.113.0/24",
|
||||
"224.0.0.0/4",
|
||||
"240.0.0.0/4",
|
||||
"255.255.255.255/32",
|
||||
"::/128",
|
||||
"::1/128",
|
||||
"fc00::/7",
|
||||
"fe80::/10",
|
||||
"ff00::/8",
|
||||
}
|
||||
|
||||
func init() {
|
||||
lib.RegisterInputConfigCreator(typePrivate, func(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
|
||||
return newPrivate(action, data)
|
||||
})
|
||||
lib.RegisterInputConverter(typePrivate, &private{
|
||||
Description: descPrivate,
|
||||
})
|
||||
}
|
||||
|
||||
func newPrivate(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
|
||||
return &private{
|
||||
Type: typePrivate,
|
||||
Action: action,
|
||||
Description: descPrivate,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type private struct {
|
||||
Type string
|
||||
Action lib.Action
|
||||
Description string
|
||||
}
|
||||
|
||||
func (p *private) GetType() string {
|
||||
return p.Type
|
||||
}
|
||||
|
||||
func (p *private) GetAction() lib.Action {
|
||||
return p.Action
|
||||
}
|
||||
|
||||
func (p *private) GetDescription() string {
|
||||
return p.Description
|
||||
}
|
||||
|
||||
func (p *private) Input(container lib.Container) (lib.Container, error) {
|
||||
entry := lib.NewEntry(entryNamePrivate)
|
||||
for _, cidr := range privateCIDRs {
|
||||
if err := entry.AddPrefix(cidr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
switch p.Action {
|
||||
case lib.ActionAdd:
|
||||
if err := container.Add(entry); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case lib.ActionRemove:
|
||||
container.Remove(entryNamePrivate)
|
||||
default:
|
||||
return nil, lib.ErrUnknownAction
|
||||
}
|
||||
|
||||
return container, nil
|
||||
}
|
||||
74
src/xray-geoip/plugin/special/test.go
Normal file
74
src/xray-geoip/plugin/special/test.go
Normal file
@ -0,0 +1,74 @@
|
||||
package special
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/v2fly/geoip/lib"
|
||||
)
|
||||
|
||||
const (
|
||||
entryNameTest = "test"
|
||||
typeTest = "test"
|
||||
descTest = "Convert specific CIDR to other formats (for test only)"
|
||||
)
|
||||
|
||||
var testCIDRs = []string{
|
||||
"127.0.0.0/8",
|
||||
}
|
||||
|
||||
func init() {
|
||||
lib.RegisterInputConfigCreator(typeTest, func(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
|
||||
return newTest(action, data)
|
||||
})
|
||||
lib.RegisterInputConverter(typeTest, &test{
|
||||
Description: descTest,
|
||||
})
|
||||
}
|
||||
|
||||
func newTest(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
|
||||
return &test{
|
||||
Type: typeTest,
|
||||
Action: action,
|
||||
Description: descTest,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type test struct {
|
||||
Type string
|
||||
Action lib.Action
|
||||
Description string
|
||||
}
|
||||
|
||||
func (t *test) GetType() string {
|
||||
return t.Type
|
||||
}
|
||||
|
||||
func (t *test) GetAction() lib.Action {
|
||||
return t.Action
|
||||
}
|
||||
|
||||
func (t *test) GetDescription() string {
|
||||
return t.Description
|
||||
}
|
||||
|
||||
func (t *test) Input(container lib.Container) (lib.Container, error) {
|
||||
entry := lib.NewEntry(entryNameTest)
|
||||
for _, cidr := range testCIDRs {
|
||||
if err := entry.AddPrefix(cidr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
switch t.Action {
|
||||
case lib.ActionAdd:
|
||||
if err := container.Add(entry); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case lib.ActionRemove:
|
||||
container.Remove(entryNameTest)
|
||||
default:
|
||||
return nil, lib.ErrUnknownAction
|
||||
}
|
||||
|
||||
return container, nil
|
||||
}
|
||||
204
src/xray-geoip/plugin/v2ray/dat_in.go
Normal file
204
src/xray-geoip/plugin/v2ray/dat_in.go
Normal file
@ -0,0 +1,204 @@
|
||||
package v2ray
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/v2fly/geoip/lib"
|
||||
router "github.com/v2fly/v2ray-core/v5/app/router/routercommon"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
typeGeoIPdatIn = "v2rayGeoIPDat"
|
||||
descGeoIPdatIn = "Convert V2Ray GeoIP dat to other formats"
|
||||
)
|
||||
|
||||
func init() {
|
||||
lib.RegisterInputConfigCreator(typeGeoIPdatIn, func(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
|
||||
return newGeoIPDatIn(action, data)
|
||||
})
|
||||
lib.RegisterInputConverter(typeGeoIPdatIn, &geoIPDatIn{
|
||||
Description: descGeoIPdatIn,
|
||||
})
|
||||
}
|
||||
|
||||
func newGeoIPDatIn(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
|
||||
var tmp struct {
|
||||
URI string `json:"uri"`
|
||||
Want []string `json:"wantedList"`
|
||||
OnlyIPType lib.IPType `json:"onlyIPType"`
|
||||
}
|
||||
|
||||
if len(data) > 0 {
|
||||
if err := json.Unmarshal(data, &tmp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if tmp.URI == "" {
|
||||
return nil, fmt.Errorf("[type %s | action %s] uri must be specified in config", typeGeoIPdatIn, action)
|
||||
}
|
||||
|
||||
return &geoIPDatIn{
|
||||
Type: typeGeoIPdatIn,
|
||||
Action: action,
|
||||
Description: descGeoIPdatIn,
|
||||
URI: tmp.URI,
|
||||
Want: tmp.Want,
|
||||
OnlyIPType: tmp.OnlyIPType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type geoIPDatIn struct {
|
||||
Type string
|
||||
Action lib.Action
|
||||
Description string
|
||||
URI string
|
||||
Want []string
|
||||
OnlyIPType lib.IPType
|
||||
}
|
||||
|
||||
func (g *geoIPDatIn) GetType() string {
|
||||
return g.Type
|
||||
}
|
||||
|
||||
func (g *geoIPDatIn) GetAction() lib.Action {
|
||||
return g.Action
|
||||
}
|
||||
|
||||
func (g *geoIPDatIn) GetDescription() string {
|
||||
return g.Description
|
||||
}
|
||||
|
||||
func (g *geoIPDatIn) Input(container lib.Container) (lib.Container, error) {
|
||||
entries := make(map[string]*lib.Entry)
|
||||
var err error
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(g.URI, "http://"), strings.HasPrefix(g.URI, "https://"):
|
||||
err = g.walkRemoteFile(g.URI, entries)
|
||||
default:
|
||||
err = g.walkLocalFile(g.URI, entries)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(entries) == 0 {
|
||||
return nil, fmt.Errorf("❌ [type %s | action %s] no entry is newly generated", typeGeoIPdatIn, g.Action)
|
||||
}
|
||||
|
||||
var ignoreIPType lib.IgnoreIPOption
|
||||
switch g.OnlyIPType {
|
||||
case lib.IPv4:
|
||||
ignoreIPType = lib.IgnoreIPv6
|
||||
case lib.IPv6:
|
||||
ignoreIPType = lib.IgnoreIPv4
|
||||
}
|
||||
|
||||
// Filter want list
|
||||
wantList := make(map[string]bool)
|
||||
for _, want := range g.Want {
|
||||
if want = strings.ToUpper(strings.TrimSpace(want)); want != "" {
|
||||
wantList[want] = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
name := entry.GetName()
|
||||
if len(wantList) > 0 && !wantList[name] {
|
||||
continue
|
||||
}
|
||||
|
||||
switch g.Action {
|
||||
case lib.ActionAdd:
|
||||
if err := container.Add(entry, ignoreIPType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case lib.ActionRemove:
|
||||
container.Remove(name, ignoreIPType)
|
||||
}
|
||||
}
|
||||
|
||||
return container, nil
|
||||
}
|
||||
|
||||
func (g *geoIPDatIn) walkLocalFile(path string, entries map[string]*lib.Entry) error {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if err := g.generateEntries(file, entries); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *geoIPDatIn) walkRemoteFile(url string, entries map[string]*lib.Entry) error {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return fmt.Errorf("failed to get remote file %s, http status code %d", url, resp.StatusCode)
|
||||
}
|
||||
|
||||
if err := g.generateEntries(resp.Body, entries); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *geoIPDatIn) generateEntries(reader io.Reader, entries map[string]*lib.Entry) error {
|
||||
geoipBytes, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var geoipList router.GeoIPList
|
||||
if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, geoip := range geoipList.Entry {
|
||||
var entry *lib.Entry
|
||||
name := geoip.CountryCode
|
||||
if theEntry, found := entries[name]; found {
|
||||
fmt.Printf("⚠️ [type %s | action %s] found duplicated entry: %s. Process anyway\n", typeGeoIPdatIn, g.Action, name)
|
||||
entry = theEntry
|
||||
} else {
|
||||
entry = lib.NewEntry(name)
|
||||
}
|
||||
|
||||
for _, v2rayCIDR := range geoip.Cidr {
|
||||
ipStr := net.IP(v2rayCIDR.GetIp()).String() + "/" + fmt.Sprint(v2rayCIDR.GetPrefix())
|
||||
switch g.Action {
|
||||
case lib.ActionAdd:
|
||||
if err := entry.AddPrefix(ipStr); err != nil {
|
||||
return err
|
||||
}
|
||||
case lib.ActionRemove:
|
||||
if err := entry.RemovePrefix(ipStr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entries[name] = entry
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
227
src/xray-geoip/plugin/v2ray/dat_out.go
Normal file
227
src/xray-geoip/plugin/v2ray/dat_out.go
Normal file
@ -0,0 +1,227 @@
|
||||
package v2ray
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/v2fly/geoip/lib"
|
||||
router "github.com/v2fly/v2ray-core/v5/app/router/routercommon"
|
||||
"github.com/v2fly/v2ray-core/v5/infra/conf/rule"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
typeGeoIPdatOut = "v2rayGeoIPDat"
|
||||
descGeoIPdatOut = "Convert data to V2Ray GeoIP dat format"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultOutputName = "geoip.dat"
|
||||
defaultOutputDir = filepath.Join("./", "output", "dat")
|
||||
)
|
||||
|
||||
func init() {
|
||||
lib.RegisterOutputConfigCreator(typeGeoIPdatOut, func(action lib.Action, data json.RawMessage) (lib.OutputConverter, error) {
|
||||
return newGeoIPDat(action, data)
|
||||
})
|
||||
lib.RegisterOutputConverter(typeGeoIPdatOut, &geoIPDatOut{
|
||||
Description: descGeoIPdatOut,
|
||||
})
|
||||
}
|
||||
|
||||
func newGeoIPDat(action lib.Action, data json.RawMessage) (lib.OutputConverter, error) {
|
||||
var tmp struct {
|
||||
OutputName string `json:"outputName"`
|
||||
OutputDir string `json:"outputDir"`
|
||||
Want []string `json:"wantedList"`
|
||||
OneFilePerList bool `json:"oneFilePerList"`
|
||||
OnlyIPType lib.IPType `json:"onlyIPType"`
|
||||
}
|
||||
|
||||
if len(data) > 0 {
|
||||
if err := json.Unmarshal(data, &tmp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if tmp.OutputName == "" {
|
||||
tmp.OutputName = defaultOutputName
|
||||
}
|
||||
|
||||
if tmp.OutputDir == "" {
|
||||
tmp.OutputDir = defaultOutputDir
|
||||
}
|
||||
|
||||
return &geoIPDatOut{
|
||||
Type: typeGeoIPdatOut,
|
||||
Action: action,
|
||||
Description: descGeoIPdatOut,
|
||||
OutputName: tmp.OutputName,
|
||||
OutputDir: tmp.OutputDir,
|
||||
Want: tmp.Want,
|
||||
OneFilePerList: tmp.OneFilePerList,
|
||||
OnlyIPType: tmp.OnlyIPType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type geoIPDatOut struct {
|
||||
Type string
|
||||
Action lib.Action
|
||||
Description string
|
||||
OutputName string
|
||||
OutputDir string
|
||||
Want []string
|
||||
OneFilePerList bool
|
||||
OnlyIPType lib.IPType
|
||||
}
|
||||
|
||||
func (g *geoIPDatOut) GetType() string {
|
||||
return g.Type
|
||||
}
|
||||
|
||||
func (g *geoIPDatOut) GetAction() lib.Action {
|
||||
return g.Action
|
||||
}
|
||||
|
||||
func (g *geoIPDatOut) GetDescription() string {
|
||||
return g.Description
|
||||
}
|
||||
|
||||
func (g *geoIPDatOut) Output(container lib.Container) error {
|
||||
// Filter want list
|
||||
wantList := make(map[string]bool)
|
||||
for _, want := range g.Want {
|
||||
if want = strings.ToUpper(strings.TrimSpace(want)); want != "" {
|
||||
wantList[want] = true
|
||||
}
|
||||
}
|
||||
|
||||
geoIPList := new(router.GeoIPList)
|
||||
geoIPList.Entry = make([]*router.GeoIP, 0, 300)
|
||||
updated := false
|
||||
switch len(wantList) {
|
||||
case 0:
|
||||
for entry := range container.Loop() {
|
||||
geoIP, err := g.generateGeoIP(entry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
geoIPList.Entry = append(geoIPList.Entry, geoIP)
|
||||
updated = true
|
||||
|
||||
if g.OneFilePerList {
|
||||
geoIPBytes, err := proto.Marshal(geoIPList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filename := strings.ToLower(entry.GetName()) + ".dat"
|
||||
if err := g.writeFile(filename, geoIPBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
geoIPList.Entry = nil
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
for name := range wantList {
|
||||
entry, found := container.GetEntry(name)
|
||||
if !found {
|
||||
log.Printf("❌ entry %s not found", name)
|
||||
continue
|
||||
}
|
||||
geoIP, err := g.generateGeoIP(entry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
geoIPList.Entry = append(geoIPList.Entry, geoIP)
|
||||
updated = true
|
||||
|
||||
if g.OneFilePerList {
|
||||
geoIPBytes, err := proto.Marshal(geoIPList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filename := strings.ToLower(entry.GetName()) + ".dat"
|
||||
if err := g.writeFile(filename, geoIPBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
geoIPList.Entry = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort to make reproducible builds
|
||||
g.sort(geoIPList)
|
||||
|
||||
if !g.OneFilePerList && updated {
|
||||
geoIPBytes, err := proto.Marshal(geoIPList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := g.writeFile(g.OutputName, geoIPBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *geoIPDatOut) generateGeoIP(entry *lib.Entry) (*router.GeoIP, error) {
|
||||
var entryCidr []string
|
||||
var err error
|
||||
switch g.OnlyIPType {
|
||||
case lib.IPv4:
|
||||
entryCidr, err = entry.MarshalText(lib.IgnoreIPv6)
|
||||
case lib.IPv6:
|
||||
entryCidr, err = entry.MarshalText(lib.IgnoreIPv4)
|
||||
default:
|
||||
entryCidr, err = entry.MarshalText()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v2rayCIDR := make([]*router.CIDR, 0, 1024)
|
||||
for _, cidrStr := range entryCidr {
|
||||
cidr, err := rule.ParseIP(cidrStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v2rayCIDR = append(v2rayCIDR, cidr)
|
||||
}
|
||||
|
||||
if len(v2rayCIDR) > 0 {
|
||||
return &router.GeoIP{
|
||||
CountryCode: entry.GetName(),
|
||||
Cidr: v2rayCIDR,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("entry %s has no CIDR", entry.GetName())
|
||||
}
|
||||
|
||||
// Sort by country code to make reproducible builds
|
||||
func (g *geoIPDatOut) sort(list *router.GeoIPList) {
|
||||
sort.SliceStable(list.Entry, func(i, j int) bool {
|
||||
return list.Entry[i].CountryCode < list.Entry[j].CountryCode
|
||||
})
|
||||
}
|
||||
|
||||
func (g *geoIPDatOut) writeFile(filename string, geoIPBytes []byte) error {
|
||||
if err := os.MkdirAll(g.OutputDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.WriteFile(filepath.Join(g.OutputDir, filename), geoIPBytes, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("✅ [%s] %s --> %s", g.Type, filename, g.OutputDir)
|
||||
|
||||
return nil
|
||||
}
|
||||
42752
src/xray-geosite/data/refilter
Normal file
42752
src/xray-geosite/data/refilter
Normal file
File diff suppressed because it is too large
Load Diff
16
src/xray-geosite/go.mod
Normal file
16
src/xray-geosite/go.mod
Normal file
@ -0,0 +1,16 @@
|
||||
module github.com/v2fly/domain-list-community
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.23.1
|
||||
|
||||
require (
|
||||
github.com/v2fly/v2ray-core/v5 v5.19.0
|
||||
google.golang.org/protobuf v1.34.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/adrg/xdg v0.5.0 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
)
|
||||
20
src/xray-geosite/go.sum
Normal file
20
src/xray-geosite/go.sum
Normal file
@ -0,0 +1,20 @@
|
||||
github.com/adrg/xdg v0.5.0 h1:dDaZvhMXatArP1NPHhnfaQUqWBLBsmx1h1HXQdMoFCY=
|
||||
github.com/adrg/xdg v0.5.0/go.mod h1:dDdY4M4DF9Rjy4kHPeNL+ilVF+p2lK8IdM9/rTSGcI4=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/v2fly/v2ray-core/v5 v5.19.0 h1:TF2noX1c1npgSg98TASHLdYDWDRhi97gBLpid1cwMUY=
|
||||
github.com/v2fly/v2ray-core/v5 v5.19.0/go.mod h1:iRydCoQWwE8mhaf/VOWe5jKB8r7LkZfHsbOvwRfJWUo=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
391
src/xray-geosite/main.go
Normal file
391
src/xray-geosite/main.go
Normal file
@ -0,0 +1,391 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
router "github.com/v2fly/v2ray-core/v5/app/router/routercommon"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
var (
|
||||
dataPath = flag.String("datapath", "./data", "Path to your custom 'data' directory")
|
||||
outputName = flag.String("outputname", "dlc.dat", "Name of the generated dat file")
|
||||
outputDir = flag.String("outputdir", "./", "Directory to place all generated files")
|
||||
exportLists = flag.String("exportlists", "", "Lists to be flattened and exported in plaintext format, separated by ',' comma")
|
||||
)
|
||||
|
||||
type Entry struct {
|
||||
Type string
|
||||
Value string
|
||||
Attrs []*router.Domain_Attribute
|
||||
}
|
||||
|
||||
type List struct {
|
||||
Name string
|
||||
Entry []Entry
|
||||
}
|
||||
|
||||
type ParsedList struct {
|
||||
Name string
|
||||
Inclusion map[string]bool
|
||||
Entry []Entry
|
||||
}
|
||||
|
||||
func (l *ParsedList) toPlainText(listName string) 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 fmt.Errorf(err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *ParsedList) toProto() (*router.GeoSite, error) {
|
||||
site := &router.GeoSite{
|
||||
CountryCode: l.Name,
|
||||
}
|
||||
for _, entry := range l.Entry {
|
||||
switch entry.Type {
|
||||
case "domain":
|
||||
site.Domain = append(site.Domain, &router.Domain{
|
||||
Type: router.Domain_RootDomain,
|
||||
Value: entry.Value,
|
||||
Attribute: entry.Attrs,
|
||||
})
|
||||
case "regexp":
|
||||
site.Domain = append(site.Domain, &router.Domain{
|
||||
Type: router.Domain_Regex,
|
||||
Value: entry.Value,
|
||||
Attribute: entry.Attrs,
|
||||
})
|
||||
case "keyword":
|
||||
site.Domain = append(site.Domain, &router.Domain{
|
||||
Type: router.Domain_Plain,
|
||||
Value: entry.Value,
|
||||
Attribute: entry.Attrs,
|
||||
})
|
||||
case "full":
|
||||
site.Domain = append(site.Domain, &router.Domain{
|
||||
Type: router.Domain_Full,
|
||||
Value: entry.Value,
|
||||
Attribute: entry.Attrs,
|
||||
})
|
||||
default:
|
||||
return nil, errors.New("unknown domain type: " + entry.Type)
|
||||
}
|
||||
}
|
||||
return site, nil
|
||||
}
|
||||
|
||||
func exportPlainTextList(list []string, refName string, pl *ParsedList) {
|
||||
for _, listName := range list {
|
||||
if strings.EqualFold(refName, listName) {
|
||||
if err := pl.toPlainText(strings.ToLower(refName)); err != nil {
|
||||
fmt.Println("Failed: ", err)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("'%s' has been generated successfully.\n", listName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func removeComment(line string) string {
|
||||
idx := strings.Index(line, "#")
|
||||
if idx == -1 {
|
||||
return line
|
||||
}
|
||||
return strings.TrimSpace(line[:idx])
|
||||
}
|
||||
|
||||
func parseDomain(domain string, entry *Entry) error {
|
||||
kv := strings.Split(domain, ":")
|
||||
if len(kv) == 1 {
|
||||
entry.Type = "domain"
|
||||
entry.Value = strings.ToLower(kv[0])
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(kv) == 2 {
|
||||
entry.Type = strings.ToLower(kv[0])
|
||||
entry.Value = strings.ToLower(kv[1])
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("Invalid format: " + domain)
|
||||
}
|
||||
|
||||
func parseAttribute(attr string) (*router.Domain_Attribute, error) {
|
||||
var attribute router.Domain_Attribute
|
||||
if len(attr) == 0 || attr[0] != '@' {
|
||||
return &attribute, errors.New("invalid attribute: " + attr)
|
||||
}
|
||||
|
||||
// Trim attribute prefix `@` character
|
||||
attr = attr[1:]
|
||||
parts := strings.Split(attr, "=")
|
||||
if len(parts) == 1 {
|
||||
attribute.Key = strings.ToLower(parts[0])
|
||||
attribute.TypedValue = &router.Domain_Attribute_BoolValue{BoolValue: true}
|
||||
} else {
|
||||
attribute.Key = strings.ToLower(parts[0])
|
||||
intv, err := strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
return &attribute, errors.New("invalid attribute: " + attr + ": " + err.Error())
|
||||
}
|
||||
attribute.TypedValue = &router.Domain_Attribute_IntValue{IntValue: int64(intv)}
|
||||
}
|
||||
return &attribute, nil
|
||||
}
|
||||
|
||||
func parseEntry(line string) (Entry, error) {
|
||||
line = strings.TrimSpace(line)
|
||||
parts := strings.Split(line, " ")
|
||||
|
||||
var entry Entry
|
||||
if len(parts) == 0 {
|
||||
return entry, errors.New("empty entry")
|
||||
}
|
||||
|
||||
if err := parseDomain(parts[0], &entry); err != nil {
|
||||
return entry, err
|
||||
}
|
||||
|
||||
for i := 1; i < len(parts); i++ {
|
||||
attr, err := parseAttribute(parts[i])
|
||||
if err != nil {
|
||||
return entry, err
|
||||
}
|
||||
entry.Attrs = append(entry.Attrs, attr)
|
||||
}
|
||||
|
||||
return entry, nil
|
||||
}
|
||||
|
||||
func Load(path string) (*List, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
list := &List{
|
||||
Name: strings.ToUpper(filepath.Base(path)),
|
||||
}
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
line = removeComment(line)
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
entry, err := parseEntry(line)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list.Entry = append(list.Entry, entry)
|
||||
}
|
||||
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func isMatchAttr(Attrs []*router.Domain_Attribute, includeKey string) bool {
|
||||
isMatch := false
|
||||
mustMatch := true
|
||||
matchName := includeKey
|
||||
if strings.HasPrefix(includeKey, "!") {
|
||||
isMatch = true
|
||||
mustMatch = false
|
||||
matchName = strings.TrimLeft(includeKey, "!")
|
||||
}
|
||||
|
||||
for _, Attr := range Attrs {
|
||||
attrName := Attr.Key
|
||||
if mustMatch {
|
||||
if matchName == attrName {
|
||||
isMatch = true
|
||||
break
|
||||
}
|
||||
} else {
|
||||
if matchName == attrName {
|
||||
isMatch = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
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 == "include" {
|
||||
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, errors.New(entry.Value + " not found.")
|
||||
}
|
||||
attrEntrys := createIncludeAttrEntrys(refList, attr)
|
||||
if len(attrEntrys) != 0 {
|
||||
newEntryList = append(newEntryList, attrEntrys...)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
InclusionName := refName
|
||||
if pl.Inclusion[InclusionName] {
|
||||
continue
|
||||
}
|
||||
pl.Inclusion[InclusionName] = true
|
||||
refList := ref[refName]
|
||||
if refList == nil {
|
||||
return nil, errors.New(entry.Value + " not found.")
|
||||
}
|
||||
newEntryList = append(newEntryList, refList.Entry...)
|
||||
}
|
||||
hasInclude = true
|
||||
} else {
|
||||
newEntryList = append(newEntryList, entry)
|
||||
}
|
||||
}
|
||||
entryList = newEntryList
|
||||
if !hasInclude {
|
||||
break
|
||||
}
|
||||
}
|
||||
pl.Entry = entryList
|
||||
|
||||
return pl, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
dir := *dataPath
|
||||
fmt.Println("Use domain lists in", dir)
|
||||
|
||||
ref := make(map[string]*List)
|
||||
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
list, err := Load(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ref[list.Name] = list
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println("Failed: ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 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: ", mkErr)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
protoList := new(router.GeoSiteList)
|
||||
var existList []string
|
||||
for refName, list := range ref {
|
||||
pl, err := ParseList(list, ref)
|
||||
if err != nil {
|
||||
fmt.Println("Failed: ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
site, err := pl.toProto()
|
||||
if err != nil {
|
||||
fmt.Println("Failed: ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
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.SliceStable(protoList.Entry, func(i, j int) bool {
|
||||
return protoList.Entry[i].CountryCode < protoList.Entry[j].CountryCode
|
||||
})
|
||||
|
||||
protoBytes, err := proto.Marshal(protoList)
|
||||
if err != nil {
|
||||
fmt.Println("Failed:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(*outputDir, *outputName), protoBytes, 0644); err != nil {
|
||||
fmt.Println("Failed: ", err)
|
||||
os.Exit(1)
|
||||
} else {
|
||||
fmt.Println(*outputName, "has been generated successfully.")
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user