tweaked timeouts, handle DNSSEC check errors

This commit is contained in:
Riccardo Spagni 2015-01-23 20:11:34 +02:00
parent fec7579043
commit 714db0f5a1
No known key found for this signature in database
GPG Key ID: 55432DF31CCD4FCD
1 changed files with 65 additions and 55 deletions

View File

@ -1,5 +1,6 @@
from electrum_gui.qt.util import EnterButton from electrum_gui.qt.util import EnterButton
from electrum.plugins import BasePlugin, hook from electrum.plugins import BasePlugin, hook
from electrum.util import print_msg
from electrum.i18n import _ from electrum.i18n import _
from PyQt4.QtGui import * from PyQt4.QtGui import *
from PyQt4.QtCore import * from PyQt4.QtCore import *
@ -43,6 +44,7 @@ class Plugin(BasePlugin):
return OA_READY return OA_READY
def __init__(self, gui, name): def __init__(self, gui, name):
print_msg('[OA] Initialiasing OpenAlias plugin, OA_READY is ' + str(OA_READY))
BasePlugin.__init__(self, gui, name) BasePlugin.__init__(self, gui, name)
self._is_available = OA_READY self._is_available = OA_READY
@ -82,9 +84,12 @@ class Plugin(BasePlugin):
if not data: if not data:
return True return True
(address, name) = data
self.win.payto_e.setText(address)
if not self.validate_dnssec(url): if not self.validate_dnssec(url):
msgBox = QMessageBox() msgBox = QMessageBox()
msgBox.setText(_('No valid DNSSEC trust chain!')) msgBox.setText(_('WARNING: the address ' + address + ' could not be validated via an additional security check, DNSSEC, and thus may not be correct.'))
msgBox.setInformativeText(_('Do you wish to continue?')) msgBox.setInformativeText(_('Do you wish to continue?'))
msgBox.setStandardButtons(QMessageBox.Cancel | QMessageBox.Ok) msgBox.setStandardButtons(QMessageBox.Cancel | QMessageBox.Ok)
msgBox.setDefaultButton(QMessageBox.Cancel) msgBox.setDefaultButton(QMessageBox.Cancel)
@ -92,8 +97,6 @@ class Plugin(BasePlugin):
if reply != QMessageBox.Ok: if reply != QMessageBox.Ok:
return True return True
(address, name) = data
self.win.payto_e.setText(address)
if self.config.get('openalias_autoadd') == 'checked': if self.config.get('openalias_autoadd') == 'checked':
self.win.wallet.add_contact(address, name) self.win.wallet.add_contact(address, name)
return False return False
@ -150,9 +153,11 @@ class Plugin(BasePlugin):
if not data: if not data:
return return
(address, name) = data
if not self.validate_dnssec(url): if not self.validate_dnssec(url):
msgBox = QMessageBox() msgBox = QMessageBox()
msgBox.setText("No valid DNSSEC trust chain!") msgBox.setText(_('WARNING: the address ' + address + ' could not be validated via an additional security check, DNSSEC, and thus may not be correct.'))
msgBox.setInformativeText("Do you wish to continue?") msgBox.setInformativeText("Do you wish to continue?")
msgBox.setStandardButtons(QMessageBox.Cancel | QMessageBox.Ok) msgBox.setStandardButtons(QMessageBox.Cancel | QMessageBox.Ok)
msgBox.setDefaultButton(QMessageBox.Cancel) msgBox.setDefaultButton(QMessageBox.Cancel)
@ -160,8 +165,6 @@ class Plugin(BasePlugin):
if reply != QMessageBox.Ok: if reply != QMessageBox.Ok:
return return
(address, name) = data
d2 = QDialog(self.win) d2 = QDialog(self.win)
vbox2 = QVBoxLayout(d2) vbox2 = QVBoxLayout(d2)
grid2 = QGridLayout() grid2 = QGridLayout()
@ -196,15 +199,17 @@ class Plugin(BasePlugin):
def resolve(self, url): def resolve(self, url):
'''Resolve OpenAlias address using url.''' '''Resolve OpenAlias address using url.'''
print_msg('[OA] Attempting to resolve OpenAlias data for ' + url)
prefix = 'btc' prefix = 'btc'
retries = 3 retries = 3
err = None err = None
for i in range(0, retries): for i in range(0, retries):
try: try:
resolver = dns.resolver.Resolver() resolver = dns.resolver.Resolver()
resolver.timeout = 15.0 resolver.timeout = 2.0
resolver.lifetime = 15.0 resolver.lifetime = 2.0
records = resolver.query(url, 'TXT') records = resolver.query(url, dns.rdatatype.TXT)
for record in records: for record in records:
string = record.strings[0] string = record.strings[0]
if string.startswith('oa1:' + prefix): if string.startswith('oa1:' + prefix):
@ -215,10 +220,10 @@ class Plugin(BasePlugin):
return (address, name) return (address, name)
QMessageBox.warning(self.win, _('Error'), _('No OpenAlias record found.'), _('OK')) QMessageBox.warning(self.win, _('Error'), _('No OpenAlias record found.'), _('OK'))
return 0 return 0
except resolver.NXDOMAIN: except dns.resolver.NXDOMAIN:
err = _('No such domain.') err = _('No such domain.')
continue continue
except resolver.Timeout: except dns.resolver.Timeout:
err = _('Timed out while resolving.') err = _('Timed out while resolving.')
continue continue
except DNSException: except DNSException:
@ -240,48 +245,53 @@ class Plugin(BasePlugin):
return None return None
def validate_dnssec(self, url): def validate_dnssec(self, url):
default = dns.resolver.get_default_resolver() print_msg('[OA] Checking DNSSEC trust chain for ' + url)
ns = default.nameservers[0]
parts = url.split('.') try:
default = dns.resolver.get_default_resolver()
ns = default.nameservers[0]
for i in xrange(len(parts), 0, -1): parts = url.split('.')
sub = '.'.join(parts[i - 1:])
query = dns.message.make_query(sub, dns.rdatatype.NS) for i in xrange(len(parts), 0, -1):
response = dns.query.udp(query, ns, 5) sub = '.'.join(parts[i - 1:])
if response.rcode() != dns.rcode.NOERROR: query = dns.message.make_query(sub, dns.rdatatype.NS)
return 0 response = dns.query.udp(query, ns, 1)
if len(response.authority) > 0: if response.rcode() != dns.rcode.NOERROR:
rrset = response.authority[0] return 0
else:
rrset = response.answer[0]
rr = rrset[0] if len(response.authority) > 0:
if rr.rdtype == dns.rdatatype.SOA: rrset = response.authority[0]
#Same server is authoritative, don't check again else:
continue rrset = response.answer[0]
query = dns.message.make_query(sub, rr = rrset[0]
dns.rdatatype.DNSKEY, if rr.rdtype == dns.rdatatype.SOA:
want_dnssec=True) #Same server is authoritative, don't check again
response = dns.query.udp(query, ns, 5) continue
if response.rcode() != 0: query = dns.message.make_query(sub,
return 0 dns.rdatatype.DNSKEY,
# HANDLE QUERY FAILED (SERVER ERROR OR NO DNSKEY RECORD) want_dnssec=True)
response = dns.query.udp(query, ns, 1)
# answer should contain two RRSET: DNSKEY and RRSIG(DNSKEY) if response.rcode() != 0:
answer = response.answer return 0
if len(answer) != 2: # HANDLE QUERY FAILED (SERVER ERROR OR NO DNSKEY RECORD)
return 0
# the DNSKEY should be self signed, validate it # answer should contain two RRSET: DNSKEY and RRSIG(DNSKEY)
name = dns.name.from_text(sub) answer = response.answer
try: if len(answer) != 2:
dns.dnssec.validate(answer[0], answer[1], {name: answer[0]}) return 0
except dns.dnssec.ValidationFailure:
return 0 # the DNSKEY should be self signed, validate it
name = dns.name.from_text(sub)
try:
dns.dnssec.validate(answer[0], answer[1], {name: answer[0]})
except dns.dnssec.ValidationFailure:
return 0
except Exception,e:
return 0
return 1 return 1