commit 2693d345f9c63b9f14000e80b0be13b09a809f5d
parent 402b49e76b9360edf6dc0af0bd30e2126f815f8e
Author: ThomasV <thomasv@electrum.org>
Date: Mon, 2 Jul 2018 08:14:31 +0200
Merge pull request #4486 from SomberNight/getaddrinfo_take2
socket.getaddrinfo on win32: do our own dns resolution
Diffstat:
1 file changed, 41 insertions(+), 1 deletion(-)
diff --git a/lib/network.py b/lib/network.py
@@ -31,9 +31,15 @@ from collections import defaultdict
import threading
import socket
import json
+import sys
+import ipaddress
+import dns
+import dns.resolver
import socks
+
from . import util
+from .util import print_error
from . import bitcoin
from .bitcoin import COIN
from . import constants
@@ -444,7 +450,41 @@ class Network(util.DaemonThread):
socket.getaddrinfo = lambda *args: [(socket.AF_INET, socket.SOCK_STREAM, 6, '', (args[0], args[1]))]
else:
socket.socket = socket._socketobject
- socket.getaddrinfo = socket._getaddrinfo
+ 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 #4421
+ socket.getaddrinfo = self._fast_getaddrinfo
+ else:
+ socket.getaddrinfo = socket._getaddrinfo
+
+ @staticmethod
+ def _fast_getaddrinfo(host, *args, **kwargs):
+ def needs_dns_resolving(host2):
+ try:
+ ipaddress.ip_address(host2)
+ return False # already valid IP
+ except ValueError:
+ pass # not an IP
+ if str(host) in ('localhost', 'localhost.',):
+ return False
+ return True
+ try:
+ if needs_dns_resolving(host):
+ answers = dns.resolver.query(host)
+ addr = str(answers[0])
+ else:
+ addr = host
+ except dns.exception.DNSException:
+ # dns failed for some reason, e.g. dns.resolver.NXDOMAIN
+ # this is normal. Simply report back failure:
+ raise socket.gaierror(11001, 'getaddrinfo failed')
+ except BaseException as e:
+ # Possibly internal error in dnspython :( see #4483
+ # Fall back to original socket.getaddrinfo to resolve dns.
+ print_error('dnspython failed to resolve dns with error:', e)
+ addr = host
+ return socket._getaddrinfo(addr, *args, **kwargs)
@with_interface_lock
def start_network(self, protocol, proxy):