TonyD365 commited on
Commit
9d59302
·
verified ·
1 Parent(s): 5a4c5de
Files changed (1) hide show
  1. dns_resolver.py +41 -35
dns_resolver.py CHANGED
@@ -3,91 +3,97 @@ import urllib.request
3
  import json
4
  import time
5
  import logging
 
6
 
7
  class DnsManager:
8
  _instance = None
 
9
 
10
  def __new__(cls, *args, **kwargs):
 
11
  if not cls._instance:
12
- # 使用 super() 调用 object 的 __new__ 来创建单例内存
13
- cls._instance = super(DnsManager, cls).__new__(cls)
 
14
  return cls._instance
15
 
16
  def __init__(self, cache_ttl=600):
17
  if hasattr(self, '_initialized'):
18
  return
19
  self.cache_ttl = cache_ttl
20
- self.cache = {} # 缓存: {"hostname": {"ip": "xxx", "expiry": 0}}
 
21
  self.logger = logging.getLogger("DnsManager")
22
  self._initialized = True
23
 
24
  def _fetch_ip_from_http(self, hostname):
25
- """核心:通过 HTTPS (TCP 443) 绕过系统 UDP DNS 限制获取 IP"""
26
  try:
27
- # 注意:这里直接使用 Google DNS 的 API 地址
28
  url = f"https://dns.google/resolve?name={hostname}&type=A"
29
  req = urllib.request.Request(url, headers={"Accept": "application/dns-json"})
30
  with urllib.request.urlopen(req, timeout=5) as response:
31
  data = json.loads(response.read().decode())
32
  if "Answer" in data:
33
  for answer in data['Answer']:
34
- if answer['type'] == 1: # 只要 A 记录 (IPv4)
35
  return answer['data']
36
  except Exception as e:
37
  self.logger.warning(f"DoH 解析失败 {hostname}: {e}")
38
  return None
39
 
40
  def get_ip(self, hostname):
41
- """获取 IP 逻辑:缓存 -> DoH -> 保底"""
42
- # 1. 如果是纯 IP 地址,直接返回
43
  if hostname.replace('.', '').isdigit():
44
  return hostname
45
 
46
- current_time = time.time()
47
- # 2. 检查缓存
48
- if hostname in self.cache:
49
- if current_time < self.cache[hostname]['expiry']:
50
- return self.cache[hostname]['ip']
 
 
 
51
 
52
- # 3. HTTP 解析
53
- new_ip = self._fetch_ip_from_http(hostname)
54
- if new_ip:
55
- self.cache[hostname] = {
56
- "ip": new_ip,
57
- "expiry": current_time + self.cache_ttl
58
- }
59
- print(f"🔄 [DNS库] 动态解析成功: {hostname} -> {new_ip}")
60
- return new_ip
61
-
62
- # 4. 保底 IP (Discord)
63
- if hostname in self.cache:
64
- return self.cache[hostname]['ip']
65
- return "162.159.137.232"
 
 
 
66
 
67
  def patch_socket(self):
68
- """注入补丁:除了 dns.google,全部强行接管"""
69
  original_getaddrinfo = socket.getaddrinfo
70
 
71
  def patched_getaddrinfo(*args, **kwargs):
72
  host = args[0]
73
  port = args[1]
74
 
75
- # 关键排除逻辑:
76
- # 如果请求的是 dns.google 本身,必须走原始解析,否则会陷入死循环(无限递归)
77
  if host == "dns.google":
78
  return original_getaddrinfo(*args, **kwargs)
79
 
80
- # 其他所有请求(Discord, MongoDB 等)全部走补丁
81
  try:
 
82
  latest_ip = self.get_ip(host)
83
- # 强制返回 IPv4 结构体给系统
84
  return [(socket.AF_INET, socket.SOCK_STREAM, 6, '', (latest_ip, port))]
85
  except Exception:
86
- # 最后的最后,如果补丁崩了,尝试退回原始解析
87
  return original_getaddrinfo(*args, **kwargs)
88
 
89
  socket.getaddrinfo = patched_getaddrinfo
90
- print("💉 [DNS库] 全局覆盖模式已加载:除 dns.google 外,所有域名解析已托管至 DoH")
91
 
92
- # 导出单例实例
93
  dns_manager = DnsManager()
 
3
  import json
4
  import time
5
  import logging
6
+ import threading # 导入线程库
7
 
8
  class DnsManager:
9
  _instance = None
10
+ _lock = threading.Lock() # 类级别的锁,用于保护单例创建
11
 
12
  def __new__(cls, *args, **kwargs):
13
+ # 双重检查锁定,确保单例创建时的线程安全
14
  if not cls._instance:
15
+ with cls._lock:
16
+ if not cls._instance:
17
+ cls._instance = super(DnsManager, cls).__new__(cls)
18
  return cls._instance
19
 
20
  def __init__(self, cache_ttl=600):
21
  if hasattr(self, '_initialized'):
22
  return
23
  self.cache_ttl = cache_ttl
24
+ self.cache = {}
25
+ self.instance_lock = threading.Lock() # 实例锁:保护 cache 字典的读写
26
  self.logger = logging.getLogger("DnsManager")
27
  self._initialized = True
28
 
29
  def _fetch_ip_from_http(self, hostname):
30
+ """通过 HTTPS 请求获取 IP (DoH)"""
31
  try:
 
32
  url = f"https://dns.google/resolve?name={hostname}&type=A"
33
  req = urllib.request.Request(url, headers={"Accept": "application/dns-json"})
34
  with urllib.request.urlopen(req, timeout=5) as response:
35
  data = json.loads(response.read().decode())
36
  if "Answer" in data:
37
  for answer in data['Answer']:
38
+ if answer['type'] == 1:
39
  return answer['data']
40
  except Exception as e:
41
  self.logger.warning(f"DoH 解析失败 {hostname}: {e}")
42
  return None
43
 
44
  def get_ip(self, hostname):
45
+ """线程安全的 IP 获取逻辑"""
 
46
  if hostname.replace('.', '').isdigit():
47
  return hostname
48
 
49
+ # 使用锁保护缓存读取和更新过程
50
+ with self.instance_lock:
51
+ current_time = time.time()
52
+
53
+ # 1. 检查缓存
54
+ if hostname in self.cache:
55
+ if current_time < self.cache[hostname]['expiry']:
56
+ return self.cache[hostname]['ip']
57
 
58
+ # 2. 缓存失效或不存在,进行解析
59
+ # 注意:在锁内部执行网络请求会阻塞其他线程,但在 0.1vCPU
60
+ # 下这能防止多个线程同时发起重复的 DoH 请求,保护算力。
61
+ new_ip = self._fetch_ip_from_http(hostname)
62
+
63
+ if new_ip:
64
+ self.cache[hostname] = {
65
+ "ip": new_ip,
66
+ "expiry": current_time + self.cache_ttl
67
+ }
68
+ print(f"🔄 [DNS库] 线程 {threading.current_thread().name} 解析成功: {hostname} -> {new_ip}")
69
+ return new_ip
70
+
71
+ # 3. 失败保底
72
+ if hostname in self.cache:
73
+ return self.cache[hostname]['ip']
74
+ return "162.159.137.232"
75
 
76
  def patch_socket(self):
77
+ """注入全局补丁"""
78
  original_getaddrinfo = socket.getaddrinfo
79
 
80
  def patched_getaddrinfo(*args, **kwargs):
81
  host = args[0]
82
  port = args[1]
83
 
84
+ # 排除 google 以防死循环
 
85
  if host == "dns.google":
86
  return original_getaddrinfo(*args, **kwargs)
87
 
 
88
  try:
89
+ # 这里调用 get_ip,内部已经有 instance_lock 保护
90
  latest_ip = self.get_ip(host)
 
91
  return [(socket.AF_INET, socket.SOCK_STREAM, 6, '', (latest_ip, port))]
92
  except Exception:
 
93
  return original_getaddrinfo(*args, **kwargs)
94
 
95
  socket.getaddrinfo = patched_getaddrinfo
96
+ print("💉 [DNS库] 补丁已加载(多线程就绪)")
97
 
98
+ # 导出实例
99
  dns_manager = DnsManager()