|
|
import logging |
|
|
import socket |
|
|
import json |
|
|
import requests |
|
|
from requests.adapters import HTTPAdapter |
|
|
from requests.packages.urllib3.util.retry import Retry |
|
|
|
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
|
|
|
_DNS_CACHE = {} |
|
|
_ORIGINAL_GETADDRINFO = socket.getaddrinfo |
|
|
|
|
|
def resolve_via_doh(hostname): |
|
|
""" |
|
|
Resolve hostname using Google DNS-over-HTTPS. |
|
|
Bypasses local system resolver (/etc/resolv.conf). |
|
|
""" |
|
|
if hostname in _DNS_CACHE: |
|
|
return _DNS_CACHE[hostname] |
|
|
|
|
|
try: |
|
|
|
|
|
|
|
|
doh_url = f"https://8.8.8.8/resolve?name={hostname}&type=A" |
|
|
|
|
|
|
|
|
|
|
|
resp = requests.get(doh_url, timeout=5, verify=False) |
|
|
data = resp.json() |
|
|
|
|
|
if 'Answer' in data: |
|
|
for answer in data['Answer']: |
|
|
if answer['type'] == 1: |
|
|
ip = answer['data'] |
|
|
logger.debug(f"🔍 DoH Resolved {hostname} -> {ip}") |
|
|
_DNS_CACHE[hostname] = ip |
|
|
return ip |
|
|
except Exception as e: |
|
|
logger.error(f"⚠️ DoH Resolution failed for {hostname}: {e}") |
|
|
|
|
|
return None |
|
|
|
|
|
def patched_getaddrinfo(host, port, family=0, type=0, proto=0, flags=0): |
|
|
""" |
|
|
Custom getaddrinfo that attempts DoH if system resolution fails or for specific hosts. |
|
|
""" |
|
|
|
|
|
if host in ('graph.facebook.com', 'www.facebook.com', 'api.instagram.com', 'graph.instagram.com'): |
|
|
ip = resolve_via_doh(host) |
|
|
if ip: |
|
|
|
|
|
|
|
|
|
|
|
return _ORIGINAL_GETADDRINFO(ip, port, family, type, proto, flags) |
|
|
|
|
|
|
|
|
return _ORIGINAL_GETADDRINFO(host, port, family, type, proto, flags) |
|
|
|
|
|
def force_ipv4_requests(): |
|
|
""" |
|
|
Installs the DNS patch. |
|
|
Named 'force_ipv4_requests' for backward compatibility with existing calls. |
|
|
""" |
|
|
if socket.getaddrinfo != patched_getaddrinfo: |
|
|
socket.getaddrinfo = patched_getaddrinfo |
|
|
logger.debug("🛡️ Installed DoH-based DNS patch for Facebook API") |
|
|
else: |
|
|
logger.debug("🛡️ DNS patch already active") |
|
|
|
|
|
def get_resilient_session(): |
|
|
""" |
|
|
Returns a session with retry logic. |
|
|
""" |
|
|
session = requests.Session() |
|
|
retry = Retry(total=3, backoff_factor=1, status_forcelist=[500, 502, 503, 504]) |
|
|
adapter = HTTPAdapter(max_retries=retry) |
|
|
session.mount('https://', adapter) |
|
|
session.mount('http://', adapter) |
|
|
|
|
|
|
|
|
force_ipv4_requests() |
|
|
|
|
|
return session |
|
|
|
|
|
|