Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 22 additions & 10 deletions electrum/dns_hacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,23 @@
from .util import get_asyncio_loop
from . import util

_LOCALHOSTS = {"localhost", "localhost."}

_logger = get_logger(__name__)


def configure_dns_resolver() -> None:
# Store this somewhere so we can un-monkey-patch:
if not hasattr(socket, "_getaddrinfo"):
socket._getaddrinfo = socket.getaddrinfo
if sys.platform == 'win32':
if sys.platform == "win32":
# On Windows, socket.getaddrinfo takes a mutex, and might hold it for up to 10 seconds
# when dns-resolving. To speed it up drastically, we resolve dns ourselves, outside that lock.
# See https://github.com/spesmilo/electrum/issues/4421
try:
_prepare_windows_dns_hack()
except Exception as e:
_logger.exception('failed to apply windows dns hack.')
_logger.exception("failed to apply windows dns hack.")
else:
socket.getaddrinfo = _fast_getaddrinfo

Expand All @@ -45,7 +47,7 @@ def _prepare_windows_dns_hack():


def _is_force_system_dns_for_host(host: str) -> bool:
return str(host) in ('localhost', 'localhost.',)
return host in _LOCALHOSTS


def _fast_getaddrinfo(host, *args, **kwargs):
Expand All @@ -61,10 +63,14 @@ def needs_dns_resolving(host):

def resolve_with_dnspython(host):
addrs = []
expected_errors = (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer,
concurrent.futures.CancelledError, concurrent.futures.TimeoutError)
expected_errors = (
dns.resolver.NXDOMAIN,
dns.resolver.NoAnswer,
concurrent.futures.CancelledError,
concurrent.futures.TimeoutError,
)
loop = get_asyncio_loop()
assert util.get_running_loop() != loop, 'must not be called from asyncio thread'
assert util.get_running_loop() != loop, "must not be called from asyncio thread"
ipv6_fut = asyncio.run_coroutine_threadsafe(
dns.asyncresolver.resolve(host, dns.rdatatype.AAAA),
loop,
Expand All @@ -80,7 +86,9 @@ def resolve_with_dnspython(host):
except expected_errors as e:
pass
except BaseException as e:
_logger.info(f'dnspython failed to resolve dns (AAAA) for {repr(host)} with error: {repr(e)}')
_logger.info(
f"dnspython failed to resolve dns (AAAA) for {repr(host)} with error: {repr(e)}"
)
# try IPv4
try:
answers = ipv4_fut.result()
Expand All @@ -89,10 +97,12 @@ def resolve_with_dnspython(host):
# dns failed for some reason, e.g. dns.resolver.NXDOMAIN this is normal.
# Simply report back failure; except if we already have some results.
if not addrs:
raise socket.gaierror(11001, 'getaddrinfo failed') from e
raise socket.gaierror(11001, "getaddrinfo failed") from e
except BaseException as e:
# Possibly internal error in dnspython :( see #4483 and #5638
_logger.info(f'dnspython failed to resolve dns (A) for {repr(host)} with error: {repr(e)}')
_logger.info(
f"dnspython failed to resolve dns (A) for {repr(host)} with error: {repr(e)}"
)
if addrs:
return addrs
# Fall back to original socket.getaddrinfo to resolve dns.
Expand All @@ -101,6 +111,8 @@ def resolve_with_dnspython(host):
addrs = [host]
if needs_dns_resolving(host):
addrs = resolve_with_dnspython(host)
list_of_list_of_socketinfos = [socket._getaddrinfo(addr, *args, **kwargs) for addr in addrs]
list_of_list_of_socketinfos = [
socket._getaddrinfo(addr, *args, **kwargs) for addr in addrs
]
list_of_socketinfos = [item for lst in list_of_list_of_socketinfos for item in lst]
return list_of_socketinfos