add option to disable dnssec

This commit is contained in:
ThomasV 2015-05-31 16:43:27 +02:00
parent 9be94c74d2
commit a3cd33fe03
3 changed files with 50 additions and 31 deletions

View File

@ -65,7 +65,7 @@ register_command('decodetx', 0, 0, 0, ['tx'], [], 'Decode serialized t
register_command('getprivatekeys', 0, 1, 1, ['address'], [], 'Get the private keys of an address', 'Address must be in wallet.') register_command('getprivatekeys', 0, 1, 1, ['address'], [], 'Get the private keys of an address', 'Address must be in wallet.')
register_command('dumpprivkeys', 0, 1, 1, [], [], 'Dump private keys from your wallet', '') register_command('dumpprivkeys', 0, 1, 1, [], [], 'Dump private keys from your wallet', '')
register_command('freeze', 0, 1, 0, ['address'], [], 'Freeze address', 'Freeze the funds at one of your wallet\'s addresses') register_command('freeze', 0, 1, 0, ['address'], [], 'Freeze address', 'Freeze the funds at one of your wallet\'s addresses')
register_command('getalias', 0, 0, 0, ['key'], [], 'Retrieve alias', 'Lookup in your list of contacts, and for an OpenAlias DNS record') register_command('getalias', 0, 0, 0, ['key'], ['nocheck'], 'Retrieve alias', 'Lookup in your list of contacts, and for an OpenAlias DNS record')
register_command('getbalance', 1, 1, 0, [], [], 'Return the balance of your wallet', '') register_command('getbalance', 1, 1, 0, [], [], 'Return the balance of your wallet', '')
register_command('getservers', 1, 0, 0, [], [], 'Return the list of available servers', '') register_command('getservers', 1, 0, 0, [], [], 'Return the list of available servers', '')
register_command('getaddressbalance', 1, 0, 0, ['address'], [], 'Return the balance of an address', '') register_command('getaddressbalance', 1, 0, 0, ['address'], [], 'Return the balance of an address', '')
@ -83,10 +83,10 @@ register_command('listaddresses', 0, 1, 0, [], ['show_all', 'show_labels',
'List wallet addresses', 'Returns your list of addresses.') 'List wallet addresses', 'Returns your list of addresses.')
register_command('listunspent', 1, 1, 0, [], [], 'List unspent outputs', 'Returns the list of unspent transaction outputs in your wallet.') register_command('listunspent', 1, 1, 0, [], [], 'List unspent outputs', 'Returns the list of unspent transaction outputs in your wallet.')
register_command('getaddressunspent', 1, 0, 0, ['address'], [], 'Returns the list of unspent inputs for an address', '') register_command('getaddressunspent', 1, 0, 0, ['address'], [], 'Returns the list of unspent inputs for an address', '')
register_command('mktx', 0, 1, 1, ['address', 'amount'], ['tx_fee', 'from_addr', 'change_addr'], 'Create signed transaction', '') register_command('mktx', 0, 1, 1, ['address', 'amount'], ['tx_fee', 'from_addr', 'change_addr', 'nocheck'], 'Create signed transaction', '')
register_command('payto', 1, 1, 1, ['address', 'amount'], ['tx_fee', 'from_addr', 'change_addr'], 'Create and broadcast a transaction.', '') register_command('payto', 1, 1, 1, ['address', 'amount'], ['tx_fee', 'from_addr', 'change_addr', 'nocheck'], 'Create and broadcast a transaction.', '')
register_command('mktx_csv', 0, 1, 1, ['csv_file'], ['tx_fee', 'from_addr', 'change_addr'], 'Create a signed transaction', '') register_command('mktx_csv', 0, 1, 1, ['csv_file'], ['tx_fee', 'from_addr', 'change_addr', 'nocheck'], 'Create a signed transaction', '')
register_command('payto_csv', 1, 1, 1, ['csv_file'], ['tx_fee', 'from_addr', 'change_addr'], 'Create and broadcast a transaction.', '') register_command('payto_csv', 1, 1, 1, ['csv_file'], ['tx_fee', 'from_addr', 'change_addr', 'nocheck'], 'Create and broadcast a transaction.', '')
register_command('password', 0, 1, 1, [], [], 'Change your password', '') register_command('password', 0, 1, 1, [], [], 'Change your password', '')
register_command('restore', 1, 1, 0, [], ['gap_limit', 'mpk', 'concealed'], 'Restore a wallet from seed', '') register_command('restore', 1, 1, 0, [], ['gap_limit', 'mpk', 'concealed'], 'Restore a wallet from seed', '')
register_command('searchcontacts', 0, 1, 0, ['query'], [], 'Search through contacts, return matching entries', '') register_command('searchcontacts', 0, 1, 0, ['query'], [], 'Search through contacts, return matching entries', '')
@ -137,6 +137,7 @@ command_options = {
'funded': (None, "--funded", False, "Show only funded addresses"), 'funded': (None, "--funded", False, "Show only funded addresses"),
'show_balance':("-b", "--balance", False, "Show the balances of listed addresses"), 'show_balance':("-b", "--balance", False, "Show the balances of listed addresses"),
'show_labels': ("-l", "--labels", False, "Show the labels of listed addresses"), 'show_labels': ("-l", "--labels", False, "Show the labels of listed addresses"),
'nocheck': (None, "--nocheck", False, "Do not verify aliases"),
'tx_fee': ("-f", "--fee", None, "Transaction fee (in BTC)"), 'tx_fee': ("-f", "--fee", None, "Transaction fee (in BTC)"),
'from_addr': ("-F", "--from", None, "Source address. If it isn't in the wallet, it will ask for the private key unless supplied in the format public_key:private_key. It's not saved in the wallet."), 'from_addr': ("-F", "--from", None, "Source address. If it isn't in the wallet, it will ask for the private key unless supplied in the format public_key:private_key. It's not saved in the wallet."),
'change_addr': ("-c", "--change", None, "Change address. Default is a spare address, or the source address if it's not in the wallet"), 'change_addr': ("-c", "--change", None, "Change address. Default is a spare address, or the source address if it's not in the wallet"),
@ -427,13 +428,14 @@ class Commands:
def verifymessage(self, address, signature, message): def verifymessage(self, address, signature, message):
return bitcoin.verify_message(address, signature, message) return bitcoin.verify_message(address, signature, message)
def _mktx(self, outputs, fee=None, change_addr=None, domain=None): def _mktx(self, outputs, fee=None, change_addr=None, domain=None, nocheck=False):
change_addr = None if change_addr is None else self.contacts.resolve(change_addr) resolver = lambda x: None if x is None else self.contacts.resolve(x, nocheck)['address']
domain = None if domain is None else map(self.contacts.resolve, domain) change_addr = resolver(change_addr)
domain = None if domain is None else map(resolver, domain)
fee = None if fee is None else int(100000000*Decimal(fee)) fee = None if fee is None else int(100000000*Decimal(fee))
final_outputs = [] final_outputs = []
for address, amount in outputs: for address, amount in outputs:
address = self.contacts.resolve(address) address = resolver(address)
#assert self.wallet.is_mine(address) #assert self.wallet.is_mine(address)
if amount == '!': if amount == '!':
assert len(outputs) == 1 assert len(outputs) == 1
@ -464,28 +466,28 @@ class Commands:
outputs.append((address, amount)) outputs.append((address, amount))
return outputs return outputs
def mktx(self, to_address, amount, fee=None, from_addr=None, change_addr=None): def mktx(self, to_address, amount, fee=None, from_addr=None, change_addr=None, nocheck=False):
domain = [from_addr] if from_addr else None domain = [from_addr] if from_addr else None
tx = self._mktx([(to_address, amount)], fee, change_addr, domain) tx = self._mktx([(to_address, amount)], fee, change_addr, domain, nocheck)
return tx return tx
def mktx_csv(self, path, fee=None, from_addr=None, change_addr=None): def mktx_csv(self, path, fee=None, from_addr=None, change_addr=None, nocheck=False):
domain = [from_addr] if from_addr else None domain = [from_addr] if from_addr else None
outputs = self._read_csv(path) outputs = self._read_csv(path)
tx = self._mktx(outputs, fee, change_addr, domain) tx = self._mktx(outputs, fee, change_addr, domain, nocheck)
return tx return tx
def payto(self, to_address, amount, fee=None, from_addr=None, change_addr=None): def payto(self, to_address, amount, fee=None, from_addr=None, change_addr=None, nocheck=False):
domain = [from_addr] if from_addr else None domain = [from_addr] if from_addr else None
tx = self._mktx([(to_address, amount)], fee, change_addr, domain) tx = self._mktx([(to_address, amount)], fee, change_addr, domain, nocheck)
r, h = self.wallet.sendtx( tx ) r, h = self.wallet.sendtx(tx)
return h return h
def payto_csv(self, path, fee = None, from_addr=None, change_addr=None): def payto_csv(self, path, fee = None, from_addr=None, change_addr=None, nocheck=False):
domain = [from_addr] if from_addr else None domain = [from_addr] if from_addr else None
outputs = self._read_csv(path) outputs = self._read_csv(path)
tx = self._mktx(outputs, fee, change_addr, domain) tx = self._mktx(outputs, fee, change_addr, domain, nocheck)
r, h = self.wallet.sendtx( tx ) r, h = self.wallet.sendtx(tx)
return h return h
def history(self): def history(self):
@ -509,8 +511,8 @@ class Commands:
def listcontacts(self): def listcontacts(self):
return self.contacts return self.contacts
def getalias(self, key): def getalias(self, key, nocheck=False):
return self.contacts.resolve(key) return self.contacts.resolve(key, nocheck)
def searchcontacts(self, query): def searchcontacts(self, query):
results = {} results = {}

View File

@ -467,18 +467,20 @@ import bitcoin
from plugins import run_hook from plugins import run_hook
class Contacts(StoreDict): class Contacts(StoreDict):
def __init__(self, config): def __init__(self, config):
StoreDict.__init__(self, config, 'contacts') StoreDict.__init__(self, config, 'contacts')
def resolve(self, k): def resolve(self, k, nocheck=False):
if bitcoin.is_address(k): if bitcoin.is_address(k):
return k return {'address':k, 'type':'address'}
if k in self.keys(): if k in self.keys():
_type, addr = self[k] _type, addr = self[k]
if _type == 'address': if _type == 'address':
return addr return {'address':addr, 'type':'contact'}
out = run_hook('resolve_address', k) out = run_hook('resolve_address', k)
if out: if out:
if not nocheck and out.get('validated') is False:
raise Exception("cannot validate alias")
return out return out
raise Exception("invalid Bitcoin address", k) raise Exception("invalid Bitcoin address", k)

View File

@ -181,7 +181,22 @@ class Plugin(BasePlugin):
@hook @hook
def resolve_address(self, url): def resolve_address(self, url):
return self.resolve(url)[0] data = self.resolve(url)
if not data:
return
address, name = data
try:
validated = self.validate_dnssec(url)
except:
validated = False
traceback.print_exc(file=sys.stderr)
return {
'address': address,
'name': name,
'type': 'openalias',
'validated': validated
}
def resolve(self, url): def resolve(self, url):
'''Resolve OpenAlias address using url.''' '''Resolve OpenAlias address using url.'''
@ -244,7 +259,7 @@ class Plugin(BasePlugin):
response = dns.query.udp(query, ns, 3) response = dns.query.udp(query, ns, 3)
if response.rcode() != dns.rcode.NOERROR: if response.rcode() != dns.rcode.NOERROR:
self.print_error("query error") self.print_error("query error")
return 0 return False
if len(response.authority) > 0: if len(response.authority) > 0:
rrset = response.authority[0] rrset = response.authority[0]
@ -262,14 +277,14 @@ class Plugin(BasePlugin):
response = dns.query.udp(query, ns, 3) response = dns.query.udp(query, ns, 3)
if response.rcode() != 0: if response.rcode() != 0:
self.print_error("query error") self.print_error("query error")
return 0 return False
# HANDLE QUERY FAILED (SERVER ERROR OR NO DNSKEY RECORD) # HANDLE QUERY FAILED (SERVER ERROR OR NO DNSKEY RECORD)
# answer should contain two RRSET: DNSKEY and RRSIG(DNSKEY) # answer should contain two RRSET: DNSKEY and RRSIG(DNSKEY)
answer = response.answer answer = response.answer
if len(answer) != 2: if len(answer) != 2:
self.print_error("answer error", answer) self.print_error("answer error", answer)
return 0 return False
# the DNSKEY should be self signed, validate it # the DNSKEY should be self signed, validate it
name = dns.name.from_text(sub) name = dns.name.from_text(sub)
@ -277,6 +292,6 @@ class Plugin(BasePlugin):
dns.dnssec.validate(answer[0], answer[1], {name: answer[0]}) dns.dnssec.validate(answer[0], answer[1], {name: answer[0]})
except dns.dnssec.ValidationFailure: except dns.dnssec.ValidationFailure:
self.print_error("validation error") self.print_error("validation error")
return 0 return False
return 1 return True