mirror of
https://github.com/Ground-Zerro/DomainMapper.git
synced 2025-12-10 01:47:18 +07:00
update
This commit is contained in:
@@ -1,92 +0,0 @@
|
||||
import dns.resolver
|
||||
import os
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
import glob
|
||||
|
||||
routerconf = "work.conf"
|
||||
|
||||
# Сброс счетчиков, объявление переменных
|
||||
successful_resolutions = 0
|
||||
failed_resolutions = 0
|
||||
unresolved_domains_file_name = "unresolved_domains.txt" # Имя файла с необработанными доменами
|
||||
result_file_name = "result.txt" # Имя файла результатов
|
||||
domain_files_pattern = "domain/*.txt" # Имя папки с txt файлами DNS
|
||||
pub_dns = "8.8.8.8" # Публичный DNS № 1
|
||||
|
||||
|
||||
# Функция записи разрешенных IP-адресов в файл .conf
|
||||
def write_allowed_ips(resolved_ips):
|
||||
# Открываем файл routerconf для чтения и чтения содержимого
|
||||
with open(routerconf, 'r') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
# Находим строку с "AllowedIPs = " и удаляем имеющиеся IP-адреса после нее
|
||||
for i, line in enumerate(lines):
|
||||
if line.startswith("AllowedIPs = "):
|
||||
ips = ", ".join(resolved_ips) + "/32\n"
|
||||
lines[i] = "AllowedIPs = " + ips
|
||||
break
|
||||
|
||||
# Записываем обновленное содержимое в файл
|
||||
with open(routerconf, 'w') as f:
|
||||
f.writelines(lines)
|
||||
|
||||
|
||||
# Функция разрешения DNS-имени с использованием заданного DNS-сервера
|
||||
def resolve_dns(domain):
|
||||
global successful_resolutions, failed_resolutions
|
||||
ip_addresses = [] # Изменение на список для хранения нескольких IP-адресов
|
||||
try:
|
||||
answers = dns.resolver.resolve(domain, 'A')
|
||||
for rdata in answers:
|
||||
ip_address = rdata.address
|
||||
successful_resolutions += 1
|
||||
print(f"{domain} IP адрес: {ip_address}")
|
||||
ip_addresses.append(ip_address) # Добавление IP-адреса в список
|
||||
return domain, ip_addresses # Возвращаем домен и список IP-адресов
|
||||
except dns.resolver.NXDOMAIN:
|
||||
failed_resolutions += 1
|
||||
print(f"Не удалось обработать домен: {domain}, Не существует")
|
||||
return None, None
|
||||
except dns.resolver.NoAnswer:
|
||||
failed_resolutions += 1
|
||||
print(f"Не удалось обработать домен: {domain}, Нет ответа")
|
||||
return None, None
|
||||
except dns.resolver.Timeout:
|
||||
failed_resolutions += 1
|
||||
print(f"Не удалось обработать домен: {domain}, Тайм-аут")
|
||||
return None, None
|
||||
|
||||
|
||||
# Основная
|
||||
def resolve_dns_in_threads(domain_files, num_threads=20):
|
||||
global successful_resolutions, failed_resolutions
|
||||
resolved_ips = set() # Создание множества для хранения IP обработанных доменов
|
||||
|
||||
# Выполнение резолва в нескольких потоках
|
||||
with ThreadPoolExecutor(max_workers=num_threads) as executor:
|
||||
futures = []
|
||||
for domain_file in domain_files:
|
||||
if os.path.isfile(domain_file):
|
||||
with open(domain_file, 'r', encoding='utf-8-sig') as f:
|
||||
domains = [line.strip() for line in f]
|
||||
for domain in domains:
|
||||
future = executor.submit(resolve_dns, domain)
|
||||
futures.append(future)
|
||||
|
||||
for future in futures:
|
||||
domain, ip_addresses = future.result()
|
||||
if ip_addresses:
|
||||
resolved_ips.update(ip_addresses) # Добавление всех IP-адресов в множество
|
||||
|
||||
# Запись разрешенных IP-адресов в файл routerconf
|
||||
write_allowed_ips(resolved_ips) # Запись разрешенных IP-адресов в файл routerconf
|
||||
|
||||
print(f"\nСопоставлено IP адресов доменам:", successful_resolutions)
|
||||
print(f"Не удалось обработать доменных имен:", failed_resolutions)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
script_directory = os.path.dirname(os.path.abspath(__file__)) # Получение пути к директории с исполняемым файлом
|
||||
domain_files = glob.glob(os.path.join(script_directory, domain_files_pattern)) # Создание списка txt файлов в директории "domain"
|
||||
resolve_dns_in_threads(domain_files) # Вызов основной функции
|
||||
9
dm-light/config.ini
Normal file
9
dm-light/config.ini
Normal file
@@ -0,0 +1,9 @@
|
||||
[Router]
|
||||
router_ip = 192.168.1.1
|
||||
router_port = 22
|
||||
login = user
|
||||
password = secret
|
||||
eth_id = Wireguard0
|
||||
domain_folder = domain
|
||||
public_dns_1 = 8.8.8.8
|
||||
public_dns_2 = 8.8.4.4
|
||||
201
dm-light/dm-light-router.py
Normal file
201
dm-light/dm-light-router.py
Normal file
@@ -0,0 +1,201 @@
|
||||
import asyncio
|
||||
import configparser
|
||||
import logging
|
||||
import os
|
||||
import socket
|
||||
from typing import List, Optional, Dict
|
||||
|
||||
import asyncssh
|
||||
from dnslib import DNSRecord
|
||||
|
||||
|
||||
# Настройка логгирования
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
|
||||
|
||||
# Множество для хранения уникальных IP-адресов
|
||||
unique_ip_addresses = set()
|
||||
|
||||
|
||||
# Чтение конфигурационного файла
|
||||
def read_config(filename: str) -> Optional[configparser.SectionProxy]:
|
||||
config = configparser.ConfigParser()
|
||||
try:
|
||||
config.read(filename)
|
||||
logging.info(f"Файл конфигурации {filename} загружен.")
|
||||
return config['Router']
|
||||
except KeyError:
|
||||
logging.error(f"Секция 'Router' отсутствует в файле конфигурации {filename}.")
|
||||
return None
|
||||
except Exception as e:
|
||||
logging.error(f"Ошибка загрузки файла конфигурации {filename}: {e}")
|
||||
return None
|
||||
|
||||
|
||||
# Загрузка доменных имен из файлов в папке
|
||||
def load_domain_names_from_folder(folder: str) -> List[str]:
|
||||
domains = []
|
||||
try:
|
||||
for filename in os.listdir(folder):
|
||||
if filename.endswith('.txt'):
|
||||
with open(os.path.join(folder, filename), 'r', encoding='utf-8-sig') as file:
|
||||
domains.extend([line.strip() for line in file if line.strip()])
|
||||
logging.info(f"Доменные имена загружены из папки {folder}.")
|
||||
except Exception as e:
|
||||
logging.error(f"Ошибка загрузки доменных имен из папки {folder}: {e}")
|
||||
return domains
|
||||
|
||||
|
||||
# Отправка DNS запроса к публичному DNS серверу
|
||||
async def send_dns_query(domain: str, dns_servers: List[str]) -> List[str]:
|
||||
loop = asyncio.get_event_loop()
|
||||
resolved_addresses = []
|
||||
for current_dns in dns_servers:
|
||||
try:
|
||||
query = DNSRecord.question(domain)
|
||||
data = query.pack()
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as client_socket:
|
||||
client_socket.settimeout(2)
|
||||
await loop.run_in_executor(None, client_socket.sendto, data, (current_dns, 53))
|
||||
response, _ = await loop.run_in_executor(None, client_socket.recvfrom, 1024)
|
||||
dns_record = DNSRecord.parse(response)
|
||||
for r in dns_record.rr:
|
||||
if r.rtype == 1: # A record
|
||||
resolved_addresses.append(str(r.rdata))
|
||||
except socket.timeout:
|
||||
logging.warning(f"Тайм-аут при отправке DNS запроса к {current_dns}")
|
||||
except Exception as e:
|
||||
logging.error(f"Ошибка отправки DNS запроса: {e}")
|
||||
return resolved_addresses
|
||||
|
||||
|
||||
# Поиск DNS имени в фильтре
|
||||
def compare_dns(f_domain: str, domain_list: List[str]) -> bool:
|
||||
name_parts = f_domain.rstrip('.').split('.')
|
||||
for filter_domain in domain_list:
|
||||
filter_domain_parts = filter_domain.split('.')
|
||||
if len(name_parts) < len(filter_domain_parts):
|
||||
continue
|
||||
match = all(name_parts[i] == filter_domain_parts[i] for i in range(-1, -len(filter_domain_parts) - 1, -1))
|
||||
if match:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# Класс для пула SSH соединений
|
||||
class SSHConnectionPool:
|
||||
def __init__(self, max_size: int):
|
||||
self.pool = asyncio.Queue(max_size)
|
||||
self.max_size = max_size
|
||||
self.size = 0
|
||||
|
||||
async def get_connection(self, router_ip: str, ssh_port: int, login: str,
|
||||
password: str) -> asyncssh.SSHClientConnection:
|
||||
if self.pool.empty() and self.size < self.max_size:
|
||||
connection = await asyncssh.connect(
|
||||
router_ip, port=ssh_port, username=login, password=password, known_hosts=None
|
||||
)
|
||||
self.size += 1
|
||||
return connection
|
||||
else:
|
||||
return await self.pool.get()
|
||||
|
||||
async def release_connection(self, connection: asyncssh.SSHClientConnection):
|
||||
await self.pool.put(connection)
|
||||
|
||||
async def close_all(self):
|
||||
while not self.pool.empty():
|
||||
connection = await self.pool.get()
|
||||
connection.close()
|
||||
self.size -= 1
|
||||
|
||||
|
||||
# Инициализация пула SSH соединений
|
||||
ssh_pool = SSHConnectionPool(max_size=5)
|
||||
|
||||
|
||||
# Отправка команд через SSH с повторными попытками
|
||||
async def send_commands_via_ssh(router_ip: str, ssh_port: int, login: str, password: str, commands: List[str]) -> None:
|
||||
max_retries = 3
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
connection = await ssh_pool.get_connection(router_ip, ssh_port, login, password)
|
||||
for command in commands:
|
||||
logging.info(f"Executing command: {command}")
|
||||
result = await connection.run(command)
|
||||
logging.info(f"Command result: {result.stdout}")
|
||||
if result.stderr:
|
||||
logging.error(f"Command error: {result.stderr}")
|
||||
await ssh_pool.release_connection(connection)
|
||||
break # Выход из цикла после успешного выполнения команд
|
||||
except (asyncssh.Error, asyncio.TimeoutError, OSError) as e:
|
||||
logging.error(f"Ошибка при выполнении команд через SSH: {e}. Попытка {attempt + 1} из {max_retries}.")
|
||||
if attempt + 1 == max_retries:
|
||||
raise
|
||||
await asyncio.sleep(5) # Подождать 5 секунд перед повторной попыткой
|
||||
|
||||
|
||||
# Основная функция
|
||||
async def execute_tasks() -> None:
|
||||
config_data = read_config('config.ini')
|
||||
if not config_data:
|
||||
return
|
||||
|
||||
try:
|
||||
router_ip = config_data['router_ip']
|
||||
router_port = int(config_data['router_port'])
|
||||
login = config_data['login']
|
||||
password = config_data['password']
|
||||
eth_id = config_data['eth_id']
|
||||
domain_folder = config_data['domain_folder']
|
||||
public_dns_1 = config_data['public_dns_1']
|
||||
public_dns_2 = config_data['public_dns_2']
|
||||
|
||||
# Загрузка доменных имен из папки
|
||||
domain_list = load_domain_names_from_folder(domain_folder)
|
||||
|
||||
# Инициализация списка DNS серверов
|
||||
dns_servers = [public_dns_1, public_dns_2]
|
||||
|
||||
except KeyError as e:
|
||||
logging.error(f"Ошибка чтения параметров конфигурации: отсутствует ключ {e}")
|
||||
return
|
||||
|
||||
# Этап 1: Разрешение всех DNS имен
|
||||
domain_to_addresses: Dict[str, List[str]] = {}
|
||||
for domain in domain_list:
|
||||
resolved_addresses = await send_dns_query(domain, dns_servers)
|
||||
if resolved_addresses:
|
||||
logging.info(f"Resolved {domain} to {resolved_addresses}")
|
||||
domain_to_addresses[domain] = resolved_addresses
|
||||
|
||||
# Этап 2: Отправка команд SSH для добавления маршрутов
|
||||
commands = []
|
||||
for domain, addresses in domain_to_addresses.items():
|
||||
for address in addresses:
|
||||
if address.rstrip('.') not in unique_ip_addresses:
|
||||
commands.append(f"ip route {address.rstrip('.')}/32 {eth_id}")
|
||||
unique_ip_addresses.add(address.rstrip('.'))
|
||||
|
||||
if commands:
|
||||
# Разделение команд на блоки по 5 штук
|
||||
command_chunks = [commands[i:i + 5] for i in range(0, len(commands), 5)]
|
||||
for chunk in command_chunks:
|
||||
try:
|
||||
await asyncio.wait_for(
|
||||
send_commands_via_ssh(router_ip, router_port, login, password, chunk), timeout=5
|
||||
)
|
||||
except (asyncssh.Error, asyncio.TimeoutError, OSError) as e:
|
||||
logging.error(f"Ошибка при выполнении команд через SSH: {e}")
|
||||
|
||||
await ssh_pool.close_all()
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
while True:
|
||||
await execute_tasks()
|
||||
logging.info("Ожидание 30 минут до следующего выполнения...")
|
||||
await asyncio.sleep(1800) # Ожидание 30 минут (1800 секунд)
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -1,6 +1,8 @@
|
||||
import glob
|
||||
import os
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
import threading
|
||||
import time
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
|
||||
import dns.resolver
|
||||
|
||||
@@ -11,7 +13,9 @@ unresolved_domains = set()
|
||||
unresolved_domains_file_name = "unresolved_domains.txt" # Имя файла с необработанными доменами
|
||||
result_file_name = "result.txt" # Имя файла результатов
|
||||
domain_files_pattern = "domain/*.txt" # Имя папки с txt файлами DNS
|
||||
pub_dns = "8.8.8.8" # Публичный DNS № 1
|
||||
pub_dns_primary = "8.8.8.8" # Публичный DNS № 1
|
||||
pub_dns_secondary = "8.8.4.4" # Публичный DNS № 2
|
||||
max_requests = 20 # Максимальное число запросов к DNS
|
||||
|
||||
|
||||
# Постобработка файла вывода
|
||||
@@ -24,7 +28,8 @@ def post_process_output(input_file, output_file):
|
||||
ip_address = line.strip() # удаление повторов IP адресов
|
||||
if ip_address not in unique_addresses:
|
||||
unique_addresses.add(ip_address)
|
||||
f.write(f"route add {ip_address} mask 255.255.255.255 0.0.0.0\n") # Запись результатов в заданном формате
|
||||
f.write(
|
||||
f"route add {ip_address} mask 255.255.255.255 0.0.0.0\n") # Запись результатов в заданном формате
|
||||
|
||||
|
||||
# Функция записи необработанных доменов в файл
|
||||
@@ -35,78 +40,103 @@ def write_unresolved_domains(unresolved_domains):
|
||||
|
||||
|
||||
# Функция записи IP обработанных доменов в файл
|
||||
def write_resolved_ip(resolved_ip):
|
||||
def write_resolved_ip(resolved_ips):
|
||||
with open(result_file, 'w', encoding='utf-8-sig') as f:
|
||||
for ip_address in resolved_ip:
|
||||
for ip_address in resolved_ips:
|
||||
f.write(ip_address + '\n')
|
||||
|
||||
|
||||
# Функция для контроля числа запросов
|
||||
class RateLimitedResolver:
|
||||
def __init__(self, primary_dns, secondary_dns, max_requests):
|
||||
self.resolver = dns.resolver.Resolver()
|
||||
self.resolver.nameservers = [primary_dns, secondary_dns]
|
||||
self.max_requests = max_requests
|
||||
self.request_count = 0
|
||||
self.lock = threading.Lock()
|
||||
|
||||
def resolve(self, domain):
|
||||
with self.lock:
|
||||
if self.request_count >= self.max_requests:
|
||||
print("Достигнуто максимальное число запросов, приостановка выполнения.")
|
||||
time.sleep(1) # Пауза перед следующим запросом
|
||||
self.request_count = 0 # Сброс счетчика после паузы
|
||||
self.request_count += 1
|
||||
|
||||
try:
|
||||
answers = self.resolver.resolve(domain, 'A')
|
||||
return [rdata.address for rdata in answers]
|
||||
except Exception as e:
|
||||
print(f"Ошибка при разрешении домена {domain}: {e}")
|
||||
return None
|
||||
|
||||
|
||||
# Функция разрешения DNS-имени с использованием заданного DNS-сервера
|
||||
def resolve_dns(domain):
|
||||
def resolve_dns(domain, rate_limited_resolver):
|
||||
global successful_resolutions, failed_resolutions, unresolved_domains
|
||||
ip_addresses = [] # Изменение на список для хранения нескольких IP-адресов
|
||||
try:
|
||||
answers = dns.resolver.resolve(domain, 'A')
|
||||
for rdata in answers:
|
||||
ip_address = rdata.address
|
||||
ip_addresses = rate_limited_resolver.resolve(domain)
|
||||
if ip_addresses:
|
||||
successful_resolutions += 1
|
||||
print(f"{domain} IP адрес: {ip_address}")
|
||||
ip_addresses.append(ip_address) # Добавление IP-адреса в список
|
||||
return ip_addresses # Возвращаем список IP-адресов
|
||||
except dns.resolver.NXDOMAIN:
|
||||
print(f"{domain} IP адрес(а): {', '.join(ip_addresses)}")
|
||||
else:
|
||||
failed_resolutions += 1
|
||||
unresolved_domains.add(domain)
|
||||
print(f"Не удалось обработать домен: {domain}")
|
||||
except Exception as e:
|
||||
failed_resolutions += 1
|
||||
unresolved_domains.add(domain)
|
||||
print(f"Не удалось обработать домен: {domain}, Не существует")
|
||||
return None
|
||||
except dns.resolver.NoAnswer:
|
||||
failed_resolutions += 1
|
||||
unresolved_domains.add(domain)
|
||||
print(f"Не удалось обработать домен: {domain}, Нет ответа")
|
||||
return None
|
||||
except dns.resolver.Timeout:
|
||||
failed_resolutions += 1
|
||||
unresolved_domains.add(domain)
|
||||
print(f"Не удалось обработать домен: {domain}, Тайм-аут")
|
||||
return None
|
||||
print(f"Не удалось обработать домен: {domain}, Ошибка: {e}")
|
||||
return (domain, ip_addresses) # Возвращаем кортеж
|
||||
|
||||
|
||||
# Основная
|
||||
def resolve_dns_in_threads(domain_files, result_file, num_threads=20):
|
||||
global successful_resolutions, failed_resolutions
|
||||
unresolved_domains = set() # Создание множества для хранения необработанных доменов
|
||||
global successful_resolutions, failed_resolutions, unresolved_domains
|
||||
resolved_ips = set() # Создание множества для хранения IP обработанных доменов
|
||||
|
||||
rate_limited_resolver = RateLimitedResolver(pub_dns_primary, pub_dns_secondary, max_requests)
|
||||
|
||||
# Выполнение резолва в нескольких потоках
|
||||
with ThreadPoolExecutor(max_workers=num_threads) as executor:
|
||||
futures = []
|
||||
for domain_file in domain_files:
|
||||
if os.path.isfile(domain_file):
|
||||
with open(domain_file, 'r', encoding='utf-8-sig') as f:
|
||||
domains = [line.strip() for line in f]
|
||||
results = executor.map(resolve_dns, domains)
|
||||
|
||||
# Открыть файл для записи результатов
|
||||
with open(result_file, 'a', encoding='utf-8-sig') as result_f:
|
||||
for domain, ip_addresses in zip(domains, results):
|
||||
if ip_addresses:
|
||||
resolved_ips.update(ip_addresses) # Добавление всех IP-адресов в множество
|
||||
for ip_address in ip_addresses:
|
||||
result_f.write(f"{domain} IP адрес: {ip_address}\n") # Запись каждого IP-адреса
|
||||
else:
|
||||
unresolved_domains.add(domain) # Добавление необработанных доменов в множество
|
||||
for domain in domains:
|
||||
futures.append(executor.submit(resolve_dns, domain, rate_limited_resolver))
|
||||
|
||||
# Обработка результатов
|
||||
with open(result_file, 'a', encoding='utf-8-sig') as result_f:
|
||||
for future in as_completed(futures):
|
||||
result = future.result()
|
||||
if result is not None:
|
||||
domain, ip_addresses = result
|
||||
if ip_addresses:
|
||||
resolved_ips.update(ip_addresses) # Добавление всех IP-адресов в множество
|
||||
for ip_address in ip_addresses:
|
||||
result_f.write(f"{domain} IP адрес: {ip_address}\n") # Запись каждого IP-адреса
|
||||
else:
|
||||
unresolved_domains.add(domain) # Добавление необработанных доменов в множество
|
||||
|
||||
# Запись множеств в соответствующие файлы
|
||||
write_resolved_ip(resolved_ips) # Запись IP обработанных доменов в файл
|
||||
write_unresolved_domains(unresolved_domains) # Запись необработанных доменов в файл
|
||||
post_process_output(result_file, result_file) # Вызов функции постобработки файла результатов
|
||||
|
||||
print(f"\nСопоставлено IP адресов доменам:", successful_resolutions)
|
||||
print(f"Не удалось обработать доменных имен:", failed_resolutions)
|
||||
input("Нажмите Enter для продолжения...") # Для пользователей Windows при запуске из проводника
|
||||
|
||||
print(f"\nСопоставлено IP адресов доменам: {successful_resolutions}")
|
||||
print(f"Не удалось обработать доменных имен: {failed_resolutions}")
|
||||
if os.name == 'nt': # Для пользователей Windows при запуске из проводника
|
||||
input("Нажмите Enter для выхода...")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
script_directory = os.path.dirname(os.path.abspath(__file__)) # Получение пути к директории с исполняемым файлом
|
||||
result_file = os.path.join(script_directory, result_file_name) # Формирование пути к файлу результатов
|
||||
unresolved_file = os.path.join(script_directory, unresolved_domains_file_name) # Формирование пути к файлу с необработанными доменами
|
||||
domain_files = glob.glob(os.path.join(script_directory, domain_files_pattern)) # Создание списка txt файлов в директории "domain"
|
||||
unresolved_file = os.path.join(script_directory,
|
||||
unresolved_domains_file_name) # Формирование пути к файлу с необработанными доменами
|
||||
domain_files = glob.glob(
|
||||
os.path.join(script_directory, domain_files_pattern)) # Создание списка txt файлов в директории "domain"
|
||||
resolve_dns_in_threads(domain_files, result_file) # Вызов основной функции
|
||||
|
||||
@@ -1 +1,7 @@
|
||||
dnspython==2.6.1
|
||||
requests~=2.31.0
|
||||
dnspython~=2.6.1
|
||||
ipaddress~=1.0.23
|
||||
configparser~=6.0.1
|
||||
|
||||
asyncssh~=2.15.0
|
||||
dnslib~=0.9.25
|
||||
@@ -2,3 +2,6 @@ requests~=2.31.0
|
||||
dnspython~=2.6.1
|
||||
ipaddress~=1.0.23
|
||||
configparser~=6.0.1
|
||||
|
||||
asyncssh~=2.15.0
|
||||
dnslib~=0.9.25
|
||||
Reference in New Issue
Block a user