Files
archived-MoviePilot-Plugins/plugins.v2/tobypasstrackers/dns_helper.py
2025-06-09 14:15:19 +08:00

87 lines
3.2 KiB
Python

import re
from typing import Optional, List, Callable
import dns.asyncresolver
import dns.resolver
from app.log import logger
class DnsHelper:
def __init__(self, dns_server: str):
self.method_name = "Local"
self.doh_url = "https://dns.alidns.com/dns-query"
self.__resolver = dns.asyncresolver.Resolver()
self.__dns_query_method = self.__query_method(dns_server)
def __query_method(self, dns_input: str) -> Callable:
if not dns_input:
return self.query_dns_local
if dns_input.startswith('https://'):
self.doh_url = dns_input
self.method_name = dns_input
return self.query_dns_doh
udp_match = re.match(r"^(?:udp://)?(\[?.+?]?)(?::(\d+))?$", dns_input)
if udp_match:
try:
self.__resolver.nameservers = [udp_match.group(1).strip('[]')]
if udp_match.group(2):
self.__resolver.port = int(udp_match.group(2))
self.method_name = f"udp://{self.__resolver.nameservers[0]}:{self.__resolver.port}"
except Exception as e:
logger.warn(f'{e}, using default resolver')
return self.query_dns_local
return self.query_dns_udp
logger.warn(f'Unknown method {dns_input}, using default resolver')
return self.query_dns_local
async def query_dns(self, domain: str, dns_type: str = "A") -> Optional[List[str]]:
answers = await self.__dns_query_method(domain, dns_type)
return answers
async def query_dns_local(self, domain: str, dns_type: str = "A") -> Optional[List[str]]:
try:
answer = await self.__resolver.resolve(domain, dns_type)
return [record.address for record in answer if hasattr(record, "address")]
except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN):
return []
except Exception as e:
# logger.error(f"本地DNS查询错误: {e} {domain}")
return None
async def query_dns_doh(self, domain: str, dns_type: str = 'A') -> Optional[List[str]]:
"""
使用 DNS-over-HTTPS (DoH) 异步解析域名。
:param domain: 要解析的域名
:param dns_type: DNS 记录类型,例如 'A', 'AAAA'
:return: IP 地址列表,或 None
"""
try:
query = dns.message.make_query(domain, dns_type)
response = await dns.asyncquery.https(query, self.doh_url)
return [
item.address for rrset in response.answer for item in rrset.items
if hasattr(item, "address")
]
except Exception as e:
return None
async def query_dns_udp(self, domain: str, dns_type: str = 'A') -> Optional[List[str]]:
"""
使用 UDP 异步方式解析域名
:param domain: 域名
:param dns_type: 记录类型,如 A、AAAA
:return: IP地址列表 或 None
"""
try:
answer = await self.__resolver.resolve(domain, dns_type)
return [record.address for record in answer]
except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN):
return []
except Exception:
return None