diff --git a/README.md b/README.md index 8e80280..5bf4c9d 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,11 @@ **Функции:** - Скрипт загружает списки доменных имен Antifilter - community edition, а также популярных сервисов и разрешает их в IP-адреса используя публичные DNS-сервера. - Итоговый список содержит только уникальные IP-адреса исключая дубликаты, также фильтруются IP-адреса самих DNS-серверов, заглушки в виде редиректа на localhost и (по желанию) IP-адреса Cloudflare. +- Возможен выбор DNS сервера из установленного в системе, а также Google Public DNS, Quad9, Cloudflare DNS, OpenDNS, Cisco Umbrella, DNS.Watch, Dyn, CleanBrowsing, Alternate DNS, AdGuard DNS, Control D или все сразу. +- Разрешение DNS имени происходит используя каждый из указанных пользователем DNS серверов и не останавливается при первом же успешном получении его IP-адреса. - С помощью конфигурационного файла можно настроить все параметры работы в т.ч. задать список сервисов, формат сохранения, количество потоков, имя выводного файла и другие. +*Обратите внимание, что для эффекта от точечной маршрутизации близкого к 100% резолвить DNS имена необходимо из сети, в которой предполагается их использование при помощи DNS серверов, настроенных в роутере/хосте...* **Автоматизация:** @@ -34,10 +37,7 @@ **Зависимости:** Для работы Domain Mapper необходимо наличие следующих библиотек Python: -- requests -- dnspython -- ipaddress -- configparser +- requests, dnspython, ipaddress, configparser, httpx *Не забудьте установить их перед запуском:* ``` @@ -51,22 +51,3 @@ pip3 install -r requirements.txt ###### Протестировано в Ubuntu 20.04 и Windows 10/11 - - - -## Domain Mapper SDS (main-SDS.py) -###### Небольшой форк основного кода - - -**Отличия:** -- Иной подход к работе меню. -- Возможность выбора DNS серверов, которые будут использованы для проверки, в т.ч.: Системный DNS, Google, Quad9, OpenDNS, Cloudflare, CleanBrowsing, Alternate DNS, AdGuard DNS (пишите если нужно что-то еще добавить). - -*В отличии от основной программы проверка DNS имени будет производиться не до первого успешного разрешения его IP-адреса, а последовательно используя каждый из указанных пользователем DNS серверов. По другому говоря - скрипт будет пытаться получить IP адрес DNS имени отдельно у каждого DNS сервера, что повышает шансы разрешить его IP в случае, например "заглушек" провайдера.* -- Список сервисов и DNS серверов исключен из кода, теперь они загружаются с Github. - -*Таким образом пользователь получит актуальные данные запустив даже старую версию скрипта.* -- Автоматизация при помощи config.ini пока частичная. -- Более долгая работа т.к. DNS имя запрашивается у каждого из указанных DNS серверов. - -*Можно частично компенсировать увеличением числа используемых потоков, однако будьте осторожны - не превысьте количество установленных DNS сервером запросов в секунду, чтобы не получать от него таймаут вместо разрешенного IP-адреса.* diff --git a/config.ini b/config.ini index 4783c2f..1d2166c 100644 --- a/config.ini +++ b/config.ini @@ -1,29 +1,67 @@ [DomainMapper] -# Имена сервисов, разделенные запятыми, для разрешения доменных имен в IP-адреса без запроса у пользователя -# доступные опции: 'Antifilter community edition', 'Youtube', 'Facebook', 'Openai', 'Tik-Tok', 'Instagram', 'Twitter', 'Netflix', 'Bing', 'Adobe', 'Apple', 'Google', 'Tor-Truckers', 'Search-engines' -# 'all' - проверить все сервисы, если не указано (по умолчанию) - пользователю будет выведено меню выбора +# Имена сервисов, разделенные запятыми, для разрешения доменных имен в IP-адреса без запроса у пользователя, если не указано (по умолчанию) - пользователю будет выведено меню выбора +# опции: +# all - проверить все сервисы +# Antifilter community edition - список заблокированных DNS имен формируемый сообществом +# Youtube +# Facebook +# Openai +# Tik-Tok +# Instagram +# Twitter +# Netflix +# Bing +# Adobe +# Apple +# Google +# Tor-Truckers - торрент трекеры +# Search-engines - поисковые системы service = -# Включить фильтрацию IP-адресов cloudflare и не записывать их в файл результатов -# доступные опции: 'yes', 'no', если значение пусте (по умолчанию) - пользователю будет выведен запрос с подсказкой +# DNS сервера (номер), разделенные пробелом, которые будут использоваться для разрешения доменных имен, если не указано (по умолчанию) - пользователю будет выведено меню выбора +# опции: +# 0 - использовать все доступные DNS серверы +# 1 - Системный DNS +# 2 - Google Public DNS +# 3 - Quad9 +# 4 - Cloudflare DNS +# 5 - OpenDNS +# 6 - Cisco Umbrella +# 7 - DNS.Watch +# 8 - Dyn +# 0 - CleanBrowsing +# 10 - Alternate DNS +# 11 - AdGuard DNS +# 12 - Control D +dnsserver = + +# Включить фильтрацию IP-адресов cloudflare и не записывать их в файл результатов, если не указано (по умолчанию) - пользователю будет выведен запрос с подсказкой +# опции: +# yes - исключить IP адреса cloudflare из итогового списка +# no - оставить IP адреса cloudflare в итоговом списке cloudflare = -# Имя выходного файла -# доступные опции: 'имя_файла', 'полный_путь/имя_файла', если не указано - будет использоваться имя фала "domain-ip-resolve.txt" в папке со скриптом +# Имя конечного файла, если не указано (по умолчанию) - будет использоваться имя фала "domain-ip-resolve.txt" в каталоге со скриптом +# опции: +# имя_файла - файл с указанным именем будет сохранени в каталоге со скриптом +# полный_путь/имя_файла - файл будет сохранен с указанным именем в указанной каталоге filename = -# Количество потоков сканирования -# если не указано (по умолчанию) - будет использоваться 20 потоков +# Количество потоков сканирования, если не указано (по умолчанию) - будет использоваться 20 потоков threads = -# Тип выходного файла -# доступные опции: 'ip' - только "IP" адрес, 'unix' - "ip rote %IP% mask/%mask% %gateway%", cidr' - "IP/маска", 'win' - "rote add %IP% mask %mask% %gateway%" -# если не указано (по умолчанию) - пользователю будет выведен запрос с подсказкой +# Формат сохранения файла результатов, если не указано (по умолчанию) - пользователю будет выведен запрос с подсказкой +# опции: +# ip - только IP адрес +# unix - ip rote %IP%/32 %gateway% +# cidr - IP/32 +# win - rote add %IP% mask 255.255.255.255 %gateway% filetype = # адрес шлюза - используется при сохранении IP-адресов в 'win' или 'unix' формате, если не указан (по умолчанию) - пользователю будет выведен запрос с подсказкой gateway = # Команда для консоли после завершения скриптом всех операций, может быть полезно для автоматизации и комбинирования с другим скриптом, кодом или программой -# доступные опции: 'исполняемая_команда_для_консоли' +# опции: +# исполняемая_команда_для_консоли run = diff --git a/dnsdb b/dnsdb new file mode 100644 index 0000000..b7f2ac1 --- /dev/null +++ b/dnsdb @@ -0,0 +1,11 @@ +Google Public DNS: 8.8.8.8 8.8.4.4 +Quad9: 9.9.9.9 149.112.112.112 +Cloudflare DNS: 1.1.1.1 1.0.0.1 +OpenDNS: 208.67.222.222 208.67.220.220 +Cisco Umbrella: 208.67.222.222 208.67.220.220 +DNS.Watch: 84.200.69.80 84.200.70.40 +Dyn: 216.146.35.35 216.146.36.36 +CleanBrowsing: 185.228.168.9 185.228.169.9 +Alternate DNS: 76.76.19.19 76.223.122.150 +AdGuard DNS: 94.140.14.14 94.140.15.15 +Control D: 76.76.2.0 76.76.10.0 \ No newline at end of file diff --git a/dnsdb.txt b/dnsdb.txt deleted file mode 100644 index a7e1001..0000000 --- a/dnsdb.txt +++ /dev/null @@ -1,7 +0,0 @@ -Google: 8.8.8.8 8.8.4.4 -Quad9: 9.9.9.9 149.112.112.112 -OpenDNS: 208.67.222.222 208.67.220.220 -Cloudflare: 1.1.1.1 1.0.0.1 -CleanBrowsing: 185.228.168.9 185.228.169.9 -Alternate DNS: 76.76.19.19 76.223.122.150 -AdGuard DNS: 94.140.14.14 94.140.15.15 \ No newline at end of file diff --git a/main-SDS.py b/main-SDS.py deleted file mode 100644 index 2f91afd..0000000 --- a/main-SDS.py +++ /dev/null @@ -1,315 +0,0 @@ -import configparser -import ipaddress -import os -import re -from concurrent.futures import ThreadPoolExecutor, as_completed - -import dns.resolver -import requests - - -# Function to load URLs from external file -def load_urls(url): - try: - response = requests.get(url) - response.raise_for_status() - lines = response.text.split('\n') - urls = {} - for line in lines: - if line.strip(): - service, url = line.split(': ', 1) - urls[service.strip()] = url.strip() - return urls - except Exception as e: - print(f"Ошибка при загрузке списка платформ: {e}") - return {} - - -# Function to load DNS servers from external file -def load_dns_servers(url): - try: - response = requests.get(url) - response.raise_for_status() - lines = response.text.split('\n') - dns_servers = {} - for line in lines: - if line.strip(): - service, servers = line.split(': ', 1) - dns_servers[service.strip()] = servers.strip().split() - return dns_servers - except Exception as e: - print(f"Ошибка при загрузке списка DNS серверов: {e}") - return {} - - -# Load URLs and DNS servers from external files -platform_db_url = "https://raw.githubusercontent.com/Ground-Zerro/DomainMapper/main/platformdb.txt" -dns_db_url = "https://raw.githubusercontent.com/Ground-Zerro/DomainMapper/main/dnsdb.txt" -urls = load_urls(platform_db_url) -dns_servers = load_dns_servers(dns_db_url) - - -# Function to resolve DNS -def resolve_dns_and_write(service, url, unique_ips_all_services, include_cloudflare, threads, cloudflare_ips_count, - null_ips_count, resolver_nameserver_pairs): - try: - print(f"\033[33mЗагрузка данных - {service}\033[0m") - response = requests.get(url) - response.raise_for_status() - dns_names = response.text.split('\n') - - if include_cloudflare: - cloudflare_ips = get_cloudflare_ips() - else: - cloudflare_ips = set() - - unique_ips_current_service = set() - - print(f"\033[33mАнализ DNS имен платформы: {service}\033[0m") - - with ThreadPoolExecutor(max_workers=threads) as executor: - futures = [] - for nameserver_pair in resolver_nameserver_pairs: - future = executor.submit(resolve_domains_with_nameservers, dns_names, unique_ips_current_service, - unique_ips_all_services, cloudflare_ips, cloudflare_ips_count, null_ips_count, - nameserver_pair) - futures.append(future) - - for future in as_completed(futures): - future.result() - - print(f"\033[33mСписок IP-адресов для платформы {service} создан.\033[0m") - return '\n'.join(unique_ips_current_service) + '\n' - except Exception as e: - print(f"Не удалось сопоставить IP адреса {service} его доменным именам.", e) - return "" - - -# Function to get Cloudflare IP addresses -def get_cloudflare_ips(): - try: - response = requests.get("https://www.cloudflare.com/ips-v4/") - response.raise_for_status() - cloudflare_ips = set() - - cidr_blocks = re.findall(r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/\d{1,2})', response.text) - - for cidr in cidr_blocks: - ip_network = ipaddress.ip_network(cidr) - for ip in ip_network: - cloudflare_ips.add(str(ip)) - - return cloudflare_ips - except Exception as e: - print("Ошибка при получении IP адресов Cloudflare:", e) - return set() - - -# Function resolve domain using a pair of DNS servers -def resolve_domains_with_nameservers(domains, unique_ips_current_service, unique_ips_all_services, cloudflare_ips, - cloudflare_ips_count, null_ips_count, nameserver_pair): - dns_server_name, nameservers = nameserver_pair - for domain in domains: - domain = domain.strip() - if domain: - for nameserver in nameservers: - resolver = dns.resolver.Resolver() - resolver.nameservers = [nameserver] - resolver.rotate = False - resolver.timeout = 1 - resolver.lifetime = 1 - try: - ips = resolver.resolve(domain) - for ip in ips: - ip_address = ip.address - if ip_address in ('127.0.0.1', '0.0.0.0') or ip_address in resolver.nameservers: - null_ips_count[0] += 1 - elif ip_address in cloudflare_ips: - cloudflare_ips_count[0] += 1 - elif ip_address not in unique_ips_all_services: - unique_ips_current_service.add(ip_address) - unique_ips_all_services.add(ip_address) - print(f"\033[36m{domain} IP адрес: {ip_address} получен от {dns_server_name}\033[0m") - except Exception as e: - print(f"\033[31mНе удалось разрешить {domain} через {dns_server_name}\033[0m") - - -# Function to read configuration file -def read_config(filename): - try: - config = configparser.ConfigParser() - with open(filename, 'r', encoding='utf-8-sig') as file: - config.read_file(file) - if 'DomainMapper' in config: - config = config['DomainMapper'] - service = config.get('service') or '' - threads = int(config.get('threads') or 20) - filename = config.get('filename') or 'domain-ip-resolve.txt' - cloudflare = config.get('cloudflare') or '' - filetype = config.get('filetype') or '' - gateway = config.get('gateway') or '' - run_command = config.get('run') or '' - - print("Загружена конфигурация из config.ini.") - return service, threads, filename, cloudflare, filetype, gateway, run_command - - except Exception as e: - print(f"Ошибка загрузки конфигурации: {e}") - return '', 20, 'domain-ip-resolve.txt', '', '', '', '' - - -def gateway_input(gateway): - if not gateway: - input_gateway = input(f"Укажите \033[32mшлюз\033[0m или \033[32mимя интерфейса\033[0m: ") - if input_gateway: - return input_gateway.strip() - else: - return gateway - - -# Function to check if 'service' is specified in the configuration file -def check_service_config(service): - if service: - if service.strip().lower() == "all": - return list(urls.keys()) - else: - return [s.strip() for s in service.split(',')] - else: - selected_services = [] - while True: - if os.name == 'nt': - os.system('cls') - else: - os.system('clear') - print("\nВыберите сервисы:") - for idx, (service, url) in enumerate(urls.items(), 1): - print(f"{idx}. {service.capitalize()}") - - selection = input("\nУкажите номера сервисов через пробел и нажмите \033[32mEnter\033[0m: ") - if selection.strip(): - selections = selection.split() - selected_services = [list(urls.keys())[int(sel) - 1] for sel in selections if sel.isdigit() - and 1 <= int(sel) <= len(urls)] - break - return selected_services - - -def check_include_cloudflare(cloudflare): - if cloudflare.lower() == 'yes': - return True - elif cloudflare.lower() == 'no': - return False - else: - return input("\nИсключить IP адреса Cloudflare из итогового списка? (\033[32myes\033[0m " - "- исключить, \033[32mEnter\033[0m - оставить): ").strip().lower() == "yes" - - -# Function to select DNS servers -def check_dns_servers(): - system_dns_servers = dns.resolver.Resolver().nameservers - selected_dns_servers = [] - - dns_server_options = [('Системный DNS', system_dns_servers)] + list(dns_servers.items()) - - while True: - if os.name == 'nt': - os.system('cls') - else: - os.system('clear') - print("\nКакие DNS сервера использовать?") - for idx, (name, servers) in enumerate(dns_server_options, 1): - print(f"{idx}. {name}: {', '.join(servers)}") - - selection = input("\nУкажите номера DNS серверов через пробел и нажмите \033[32mEnter\033[0m: ") - if selection.strip(): - selections = selection.split() - for sel in selections: - if sel.isdigit(): - sel = int(sel) - if 1 <= sel <= len(dns_server_options): - selected_dns_servers.append((dns_server_options[sel-1][0], dns_server_options[sel-1][1])) - break - - return selected_dns_servers - - -def process_file_format(filename, filetype, gateway): - if not filetype: - filetype = input("\nВыберите в каком формате сохранить файл: \n\033[32mwin\033[0m" - " - 'route add %IP% mask %mask% %gateway%', \033[32munix\033[0m" - " - 'ip route %IP%/%mask% %gateway%', \033[32mcidr\033[0m" - " - 'IP/mask', \033[32mEnter\033[0m - только IP: ") - - if filetype.lower() in ['win', 'unix']: - gateway = gateway_input(gateway) - - try: - with open(filename, 'r', encoding='utf-8-sig') as file: - ips = file.readlines() - except Exception as e: - print(f"Ошибка чтения файла: {e}") - return - - if ips: - with open(filename, 'w', encoding='utf-8-sig') as file: - for ip in ips: - if filetype.lower() == 'win': - file.write(f"route add {ip.strip()} mask 255.255.255.255 {gateway}\n") - elif filetype.lower() == 'unix': - file.write(f"ip route {ip.strip()}/32 {gateway}\n") - elif filetype.lower() == 'cidr': - try: - with open(filename, 'r', encoding='utf-8-sig') as file: - ips = file.readlines() - except Exception as e: - print(f"Ошибка чтения файла: {e}") - return - - if ips: - with open(filename, 'w', encoding='utf-8-sig') as file: - for ip in ips: - file.write(f"{ip.strip()}/32\n") - else: - pass - - -def main(): - service, threads, filename, cloudflare, filetype, gateway, run_command = read_config('config.ini') - - total_resolved_domains = 0 - selected_services = check_service_config(service) - resolver_nameserver_pairs = check_dns_servers() # Get selected DNS server pairs - include_cloudflare = check_include_cloudflare(cloudflare) - - unique_ips_all_services = set() - cloudflare_ips_count = [0] # To count the number of Cloudflare IPs excluded - null_ips_count = [0] # To count the number of null IPs excluded - - with open(filename, 'w', encoding='utf-8-sig') as file: - for service in selected_services: - result = resolve_dns_and_write(service, urls[service], unique_ips_all_services, include_cloudflare, - threads, cloudflare_ips_count, null_ips_count, resolver_nameserver_pairs) - file.write(result) - total_resolved_domains += len(result.split('\n')) - 1 - - print("\nПроверка завершена.") - print(f"Использовались DNS сервера: {', '.join([f'{pair[0]} ({", ".join(pair[1])})' for pair - in resolver_nameserver_pairs])}") - if include_cloudflare: - print(f"Исключено IP-адресов Cloudflare: {cloudflare_ips_count[0]}") - print(f"Исключено IP-адресов 'заглушек' провайдера: {null_ips_count[0]}") - print(f"Разрешено IP-адресов из DNS имен сервисов: {total_resolved_domains}") - - process_file_format(filename, filetype, gateway) - - if run_command: - print("\nВыполнение команды после завершения скрипта...") - os.system(run_command) - else: - print("Результаты сохранены в файл:", filename) - if os.name == 'nt': - input("Нажмите \033[32mEnter\033[0m для выхода...") - - -if __name__ == "__main__": - main() diff --git a/main.py b/main.py index 90d9316..a6f7657 100644 --- a/main.py +++ b/main.py @@ -1,114 +1,16 @@ -import configparser +import asyncio +import configparser import ipaddress import os import re -from concurrent.futures import ThreadPoolExecutor +from asyncio import Semaphore +from collections import defaultdict -import dns.resolver -import requests - -# URLs -urls = { - 'Antifilter community edition': "https://community.antifilter.download/list/domains.lst", - 'Youtube': "https://raw.githubusercontent.com/Ground-Zerro/DomainMapper/main/platforms/dns-youtube.txt", - 'Facebook': "https://raw.githubusercontent.com/Ground-Zerro/DomainMapper/main/platforms/dns-facebook.txt", - 'Openai': "https://raw.githubusercontent.com/Ground-Zerro/DomainMapper/main/platforms/dns-openai.txt", - 'Tik-Tok': "https://raw.githubusercontent.com/Ground-Zerro/DomainMapper/main/platforms/dns-tiktok.txt", - 'Instagram': "https://raw.githubusercontent.com/Ground-Zerro/DomainMapper/main/platforms/dns-instagram.txt", - 'Twitter': "https://raw.githubusercontent.com/Ground-Zerro/DomainMapper/main/platforms/dns-twitter.txt", - 'Netflix': "https://raw.githubusercontent.com/Ground-Zerro/DomainMapper/main/platforms/dns-netflix.txt", - 'Bing': "https://raw.githubusercontent.com/Ground-Zerro/DomainMapper/main/platforms/dns-bing.txt", - 'Adobe': "https://raw.githubusercontent.com/Ground-Zerro/DomainMapper/main/platforms/dns-adobe.txt", - 'Apple': "https://raw.githubusercontent.com/Ground-Zerro/DomainMapper/main/platforms/dns-apple.txt", - 'Google': "https://raw.githubusercontent.com/Ground-Zerro/DomainMapper/main/platforms/dns-google.txt", - 'Tor-Truckers': "https://raw.githubusercontent.com/Ground-Zerro/DomainMapper/main/platforms/dns-ttruckers.txt", - 'Search-engines': "https://raw.githubusercontent.com/Ground-Zerro/DomainMapper/main/platforms/dns-search-engines" - ".txt", -} +import dns.asyncresolver +import httpx -# Function to resolve DNS -def resolve_dns_and_write(service, url, unique_ips_all_services, include_cloudflare, threads): - try: - print(f"Загрузка данных - {service}") - response = requests.get(url) - response.raise_for_status() - dns_names = response.text.split('\n') - - resolver = dns.resolver.Resolver(configure=False) - resolver.nameservers = ['8.8.8.8', '8.8.4.4', '208.67.222.222', '208.67.220.220', '4.2.2.1', '4.2.2.2', - '149.112.112.112'] # Public DNS servers - resolver.rotate = True - resolver.timeout = 1 - resolver.lifetime = 1 - - if include_cloudflare: - cloudflare_ips = get_cloudflare_ips() - else: - cloudflare_ips = set() - - unique_ips_current_service = set() # Set to store unique IP addresses for the current service - - print(f"Анализ DNS имен платформы: {service}") - - with ThreadPoolExecutor(max_workers=threads) as executor: - futures = [] - for domain in dns_names: - if domain.strip(): - future = executor.submit(resolve_domain, resolver, domain, unique_ips_current_service, - unique_ips_all_services, cloudflare_ips) - futures.append(future) - - # Дождаться завершения всех задач - for future in futures: - future.result() - - print(f"Список IP-адресов для платформы {service} создан.") - return '\n'.join(unique_ips_current_service) + '\n' - except Exception as e: - print(f"Не удалось сопоставить IP адреса {service} его доменным именам.", e) - return "" - - -# Function to get Cloudflare IP addresses -def get_cloudflare_ips(): - try: - response = requests.get("https://www.cloudflare.com/ips-v4/") - response.raise_for_status() - cloudflare_ips = set() - - # Extract CIDR blocks from the response text using regular expressions - cidr_blocks = re.findall(r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/\d{1,2})', response.text) - - for cidr in cidr_blocks: - ip_network = ipaddress.ip_network(cidr) - for ip in ip_network: - cloudflare_ips.add(str(ip)) - - return cloudflare_ips - except Exception as e: - print("Ошибка при получении IP адресов Cloudflare:", e) - return set() - - -# Function resolve domain -def resolve_domain(resolver, domain, unique_ips_current_service, unique_ips_all_services, cloudflare_ips): - try: - ips = resolver.resolve(domain) - for ip in ips: - ip_address = ip.address - if (ip_address not in ('127.0.0.1', '0.0.0.1') and - ip_address not in resolver.nameservers and - ip_address not in cloudflare_ips and - ip_address not in unique_ips_all_services): # Check for uniqueness - unique_ips_current_service.add(ip_address) - unique_ips_all_services.add(ip_address) - print(f"\033[36m{domain} IP адрес: {ip_address}\033[0m") - except Exception as e: - print(f"\033[31mНе удалось обработать: {domain}\033[0m - {e}") - - -# Function to read configuration file +# Read configuration file def read_config(filename): try: config = configparser.ConfigParser() @@ -117,27 +19,28 @@ def read_config(filename): if 'DomainMapper' in config: config = config['DomainMapper'] service = config.get('service') or '' - threads = int(config.get('threads') or 20) + request_limit = int(config.get('threads') or 20) filename = config.get('filename') or 'domain-ip-resolve.txt' cloudflare = config.get('cloudflare') or '' filetype = config.get('filetype') or '' gateway = config.get('gateway') or '' run_command = config.get('run') or '' + dns_server_indices = list(map(int, config.get('dnsserver', '').split())) if config.get('dnsserver') else [] - print("Загружена конфигурация из config.ini.") - return service, threads, filename, cloudflare, filetype, gateway, run_command + print("\033[33mЗагружена конфигурация из config.ini:\033[0m") + print(f"Сервисы для проверки: {service if service else 'не указаны'}") + print(f"Использовать DNS сервер: {dns_server_indices if dns_server_indices else 'не указано'}") + print(f"Количество потоков: {request_limit}") + print(f"Фильтр Cloudflare: {'включен' if cloudflare == 'yes' else 'вЫключен' if cloudflare == 'no' else 'не указано'}") + print(f"Файл результатов: {filename}") + print(f"Формат сохранения: {'только IP' if filetype == 'ip' else 'Linux route' if filetype == 'unix' else 'CIDR-нотация' if filetype == 'cidr' else 'Windows route' if filetype == 'win' else 'не указан'}") + print(f"Шлюз для маршрутов: {gateway if gateway else 'не указан'}") + print(f"Выполнить при заврешении: {run_command if run_command else 'не указано'}") + return service, request_limit, filename, cloudflare, filetype, gateway, run_command, dns_server_indices except Exception as e: - print(f"Ошибка загрузки конфигурации: {e}") - service = '' - threads = int(20) - filename = 'domain-ip-resolve.txt' - cloudflare = '' - filetype = '' - gateway = '' - run_command = '' - - return service, threads, filename, cloudflare, filetype, gateway, run_command + print(f"\033[33mОшибка загрузки config.ini:\033[0m {e}\nИспользуются настройки 'по умолчанию'.") + return '', 20, 'domain-ip-resolve.txt', '', '', '', '', [] def gateway_input(gateway): @@ -149,64 +52,207 @@ def gateway_input(gateway): return gateway -# Function to check if 'service' is specified in the configuration file -def check_service_config(service): +# Function to limit requests +def get_semaphore(request_limit): + return defaultdict(lambda: Semaphore(request_limit)) + + +# Initialize semaphore for limiting requests +def init_semaphores(request_limit): + return get_semaphore(request_limit) + + +async def load_urls(url): + try: + async with httpx.AsyncClient() as client: + response = await client.get(url) + response.raise_for_status() + text = response.text + lines = text.split('\n') + urls = {} + for line in lines: + if line.strip(): + service, url = line.split(': ', 1) + urls[service.strip()] = url.strip() + return urls + except Exception as e: + print(f"Ошибка при загрузке списка платформ: {e}") + return {} + + +async def load_dns_servers(url): + try: + async with httpx.AsyncClient() as client: + response = await client.get(url) + response.raise_for_status() + text = response.text + lines = text.split('\n') + dns_servers = {} + for line in lines: + if line.strip(): + service, servers = line.split(': ', 1) + dns_servers[service.strip()] = servers.strip().split() + return dns_servers + except Exception as e: + print(f"Ошибка при загрузке списка DNS серверов: {e}") + return {} + + +async def get_cloudflare_ips(): + try: + async with httpx.AsyncClient() as client: + response = await client.get("https://www.cloudflare.com/ips-v4/") + response.raise_for_status() + text = response.text + cloudflare_ips = set() + cidr_blocks = re.findall(r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/\d{1,2})', text) + for cidr in cidr_blocks: + ip_network = ipaddress.ip_network(cidr) + for ip in ip_network: + cloudflare_ips.add(str(ip)) + return cloudflare_ips + except Exception as e: + print("Ошибка при получении IP адресов Cloudflare:", e) + return set() + + +async def resolve_domain(domain, resolver, semaphore, dns_server_name, null_ips_count, cloudflare_ips, + cloudflare_ips_count): + async with semaphore: + try: + response = await resolver.resolve(domain) + ips = [ip.address for ip in response] + for ip_address in ips: + if ip_address in ('127.0.0.1', '0.0.0.0') or ip_address in resolver.nameservers: + null_ips_count[0] += 1 + elif ip_address in cloudflare_ips: + cloudflare_ips_count[0] += 1 + else: + print(f"\033[36m{domain} IP адрес: {ip_address} получен от {dns_server_name}\033[0m") + return ips + except Exception as e: + print(f"\033[31mНе удалось разрешить {domain} через {dns_server_name}\033[0m") + return [] + + +async def resolve_dns(service, url, dns_servers, cloudflare_ips, unique_ips_all_services, semaphore, null_ips_count, + cloudflare_ips_count): + try: + async with httpx.AsyncClient() as client: + response = await client.get(url) + response.raise_for_status() + dns_names = response.text.split('\n') + + print(f"\033[33mАнализ DNS имен платформы {service}...\033[0m") + + tasks = [] + for server_name, servers in dns_servers: + resolver = dns.asyncresolver.Resolver() + resolver.nameservers = servers + for domain in dns_names: + domain = domain.strip() + if domain: + tasks.append(resolve_domain(domain, resolver, semaphore[server_name], server_name, null_ips_count, + cloudflare_ips, cloudflare_ips_count)) + + results = await asyncio.gather(*tasks) + + unique_ips_current_service = set() + for result in results: + for ip_address in result: + if ip_address not in unique_ips_all_services: + unique_ips_current_service.add(ip_address) + unique_ips_all_services.add(ip_address) + + return '\n'.join(unique_ips_current_service) + '\n' + except Exception as e: + print(f"Не удалось сопоставить IP адреса {service} его доменным именам.", e) + return "" + + +def check_service_config(service, urls): if service: if service.strip().lower() == "all": - return list(urls.keys()) # Select all available services + return list(urls.keys()) else: return [s.strip() for s in service.split(',')] else: selected_services = [] while True: - if os.name == 'nt': # Для пользователей Windows - os.system('cls') # Очистить экран - else: - os.system('clear') - print("\nВыберите сервисы:\n") - print("0 - Отметить все") + print("\n\033[33mВыберите сервисы:\033[0m") + print("0. Выбрать все") for idx, (service, url) in enumerate(urls.items(), 1): - checkbox = "[*]" if service in selected_services else "[ ]" - print(f"{idx}. {service.capitalize()} {checkbox}") + print(f"{idx}. {service.capitalize()}") - selection = input("\n\033[32mВведите номер сервиса\033[0m и нажмите Enter (Пустая строка " - "и \033[32mEnter\033[0m для старта): ") - if selection == "0": - selected_services = list(urls.keys()) - elif selection.isdigit(): - idx = int(selection) - 1 - if 0 <= idx < len(urls): - service = list(urls.keys())[idx] - if service in selected_services: - selected_services.remove(service) - else: - selected_services.append(service) - elif selection == "": - break + selection = input("\nУкажите номера сервисов через пробел и нажмите \033[32mEnter\033[0m: ") + if selection.strip(): + selections = selection.split() + if '0' in selections: # User selected all services + selected_services = list(urls.keys()) + break + else: + selected_services = [list(urls.keys())[int(sel) - 1] for sel in selections if sel.isdigit() + and 1 <= int(sel) <= len(urls)] + break return selected_services -# Function to check if to include Cloudflare IPs based on configuration or user input def check_include_cloudflare(cloudflare): if cloudflare.lower() == 'yes': return True elif cloudflare.lower() == 'no': return False else: - return input("Исключить IP адреса Cloudflare из итогового списка? (\033[32myes\033[0m " + return input("\nИсключить IP адреса Cloudflare из итогового списка? (\033[32myes\033[0m " "- исключить, \033[32mEnter\033[0m - оставить): ").strip().lower() == "yes" -# Function to process file format +def check_dns_servers(dns_servers, dns_server_indices): + system_dns_servers = dns.asyncresolver.Resolver().nameservers + selected_dns_servers = [] + + dns_server_options = [('Системный DNS', system_dns_servers)] + list(dns_servers.items()) + + if dns_server_indices: + for idx in dns_server_indices: + if 0 <= idx <= len(dns_server_options): + selected_dns_servers.append((dns_server_options[idx][0], dns_server_options[idx][1])) + return selected_dns_servers + + while True: + print("\n\033[33mКакие DNS сервера использовать?\033[0m") + print("0. Выбрать все") + for idx, (name, servers) in enumerate(dns_server_options, 1): + print(f"{idx}. {name}: {', '.join(servers)}") + + selection = input("\nУкажите номера DNS серверов через пробел и нажмите \033[32mEnter\033[0m: ") + if selection.strip(): + selections = selection.split() + if '0' in selections: # User selected all DNS servers + selected_dns_servers = dns_server_options + break + else: + for sel in selections: + if sel.isdigit(): + sel = int(sel) + if 1 <= sel <= len(dns_server_options): + selected_dns_servers.append( + (dns_server_options[sel - 1][0], dns_server_options[sel - 1][1])) + break + + return selected_dns_servers + + def process_file_format(filename, filetype, gateway): if not filetype: - filetype = input("\nВыберите в каком формате сохранить файл: \n\033[32mwin\033[0m" - " - 'route add %IP% mask %mask% %gateway%', \033[32munix\033[0m" - " - 'ip route %IP%/%mask% %gateway%', \033[32mcidr\033[0m" - " - 'IP/mask', \033[32mEnter\033[0m - только IP: ") + filetype = input("\n\033[33mВ каком формате сохранить файл?\033[0m" + "\n\033[32mwin\033[0m - route add IP mask MASK GATEWAY" + "\n\033[32munix\033[0m - ip route IP/MASK GATEWAY" + "\n\033[32mcidr\033[0m - IP/MASK" + "\n\033[32mПустое значение\033[0m - только IP" + "\nВаш выбор: ") if filetype.lower() in ['win', 'unix']: - # Обработка файлов разных форматов gateway = gateway_input(gateway) try: @@ -224,7 +270,6 @@ def process_file_format(filename, filetype, gateway): elif filetype.lower() == 'unix': file.write(f"ip route {ip.strip()}/32 {gateway}\n") elif filetype.lower() == 'cidr': - # Обработка CIDR формата try: with open(filename, 'r', encoding='utf-8-sig') as file: ips = file.readlines() @@ -235,47 +280,69 @@ def process_file_format(filename, filetype, gateway): if ips: with open(filename, 'w', encoding='utf-8-sig') as file: for ip in ips: - file.write(f"{ip.strip()}/32\n") # Assuming /32 subnet mask for all IPs + file.write(f"{ip.strip()}/32\n") else: - # Сохранить только IP адреса pass -def main(): - # Read parameters from the configuration file - service, threads, filename, cloudflare, filetype, gateway, run_command = read_config('config.ini') +async def main(): + # Load configuration + service, request_limit, filename, cloudflare, filetype, gateway, run_command, dns_server_indices = read_config('config.ini') - total_resolved_domains = 0 - selected_services = check_service_config(service) + # Load URLs + platform_db_url = "https://raw.githubusercontent.com/Ground-Zerro/DomainMapper/main/platformdb" + urls = await load_urls(platform_db_url) - # Check if to include Cloudflare IPs based on configuration or user input + # Get selected services from user + selected_services = check_service_config(service, urls) + + # Load DNS servers + dns_db_url = "https://raw.githubusercontent.com/Ground-Zerro/DomainMapper/main/dnsdb" + dns_servers = await load_dns_servers(dns_db_url) + + # Get selected DNS servers from config or user + selected_dns_servers = check_dns_servers(dns_servers, dns_server_indices) + + # Get Cloudflare IP addresses + cloudflare_ips = await get_cloudflare_ips() + + # Check if Cloudflare IPs should be included or excluded include_cloudflare = check_include_cloudflare(cloudflare) - # Set to store unique IP addresses across all services unique_ips_all_services = set() + semaphore = init_semaphores(request_limit) + null_ips_count = [0] + cloudflare_ips_count = [0] + tasks = [] - # DNS resolution for selected services - with open(filename, 'w', encoding='utf-8-sig') as file: # Open file for writing - for service in selected_services: - result = resolve_dns_and_write(service, urls[service], unique_ips_all_services, include_cloudflare, threads) - file.write(result) # Write unique IPs directly to the file - total_resolved_domains += len(result.split('\n')) - 1 + for service in selected_services: + tasks.append(resolve_dns(service, urls[service], selected_dns_servers, cloudflare_ips, unique_ips_all_services, + semaphore, null_ips_count, cloudflare_ips_count)) - print("\nПроверка завершена.") - print(f"Сопоставлено IP адресов доменам: {total_resolved_domains}") + results = await asyncio.gather(*tasks) + + with open(filename, 'w', encoding='utf-8-sig') as file: + for result in results: + file.write(result) + + print("\n\033[33mПроверка завершена.\033[0m") + print( + f"Использовались DNS сервера: {', '.join([f'{pair[0]} ({', '.join(pair[1])})' for pair in selected_dns_servers])}") + if include_cloudflare: + print(f"Исключено IP-адресов Cloudflare: {cloudflare_ips_count[0]}") + print(f"Исключено IP-адресов 'заглушек': {null_ips_count[0]}") + print(f"Разрешено IP-адресов из DNS имен: {len(unique_ips_all_services)}") - # Asking for file format if filetype is not specified in the configuration file process_file_format(filename, filetype, gateway) - # Executing the command after the program is completed, if it is specified in the configuration file - if run_command is not None and run_command.strip(): + if run_command: print("\nВыполнение команды после завершения скрипта...") os.system(run_command) else: - print("Результаты сохранены в файл:", filename) - if os.name == 'nt': # Для пользователей Windows при запуске из проводника + print("\nРезультаты сохранены в файл:", filename) + if os.name == 'nt': input("Нажмите \033[32mEnter\033[0m для выхода...") if __name__ == "__main__": - main() + asyncio.run(main()) diff --git a/platformdb.txt b/platformdb similarity index 100% rename from platformdb.txt rename to platformdb diff --git a/requirements.txt b/requirements.txt index 79bb976..57f506f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,5 @@ requests~=2.31.0 dnspython~=2.6.1 ipaddress~=1.0.23 configparser~=7.0.0 + +httpx~=0.27.0 \ No newline at end of file diff --git a/todo.txt b/todo.txt deleted file mode 100644 index 6f6e96e..0000000 --- a/todo.txt +++ /dev/null @@ -1,4 +0,0 @@ - resolvedns DNS , DNS. - . - . -... \ No newline at end of file