Commit 7d1c0de2 authored by Robert Edmonds's avatar Robert Edmonds

Merge tag 'upstream/0.6.0' into debian/sid

Upstream version 0.6.0
parents 27631bfd 1eef66bb
This diff is collapsed.
......@@ -14,7 +14,8 @@ powers the Web-based analysis available at http://dnsviz.net/
* python (2.7.x) - http://www.python.org/
python 2.7.x is required.
python 2.7.x is required. python 3.4.x has also been successfully tested and
should work, as long as the other third-party dependencies support python 3.
* dnspython (1.11.0 or later) - http://www.dnspython.org/
......@@ -48,6 +49,14 @@ powers the Web-based analysis available at http://dnsviz.net/
$ patch -p1 < /path/to/dnsviz-source/contrib/m2crypto-pre0.23.patch
```
* (optional) ISC BIND - https://www.isc.org/downloads/bind/
When calling `dnsviz probe` if the `-N` option is used or if a zone file is
used in conjunction with the `-x` option, `named(8)` is looked for in PATH
and invoked to serve the zone file. ISC BIND is only needed in this specific
case, and `named(8)` does not need to be running.
### Build and Install
A typical build and install is performed with the following commands:
......@@ -82,7 +91,7 @@ of which are serialized into JSON format.
#### Examples
Analyze the domain name example.com using your configured DNS resolvers (i.e.,
in /etc/resolv.conf) and store the queries and responses in the file named
in `/etc/resolv.conf`) and store the queries and responses in the file named
"example.com.json":
```
$ dnsviz probe example.com > example.com.json
......@@ -152,48 +161,43 @@ Same thing:
$ dnsviz grok -r example.com.json -o example.com-chk.json example.com
```
Same thing, but with "pretty", formatted JSON:
```
$ dnsviz grok -p -r example.com.json -o example.com-chk.json
```
Show only info-level information: descriptions, statuses, warnings, and errors:
```
$ dnsviz grok -p -l info -r example.com.json -o example.com-chk.json
$ dnsviz grok -l info -r example.com.json -o example.com-chk.json
```
Show descriptions only if there are related warnings or errors:
```
$ dnsviz grok -p -l warning -r example.com.json -o example.com-chk.json
$ dnsviz grok -l warning -r example.com.json -o example.com-chk.json
```
Show descriptions only if there are related errors:
```
$ dnsviz grok -p -l error -r example.com.json -o example.com-chk.json
$ dnsviz grok -l error -r example.com.json -o example.com-chk.json
```
Use root key as DNSSEC trust anchor, to additionally indicate
authentication status of responses:
```
$ dig +noall +answer . dnskey | awk '$5 % 2 { print $0 }' > tk.txt
$ dnsviz grok -p -l info -t tk.txt -r example.com.json -o example.com-chk.json
$ dnsviz grok -l info -t tk.txt -r example.com.json -o example.com-chk.json
```
Pipe `dnsviz probe` output directly to `dnsviz grok`:
```
$ dnsviz probe example.com | \
dnsviz grok -p -l info -o example.com-chk.json
dnsviz grok -l info -o example.com-chk.json
```
Same thing, but save the raw output (for re-use) along the way:
```
$ dnsviz probe example.com | tee example.com.json | \
dnsviz grok -p -l info -o example.com-chk.json
dnsviz grok -l info -o example.com-chk.json
```
Assess multiple names at once with error level:
```
$ dnsviz grok -p -l error -r multiple.json -o example.com-chk.json
$ dnsviz grok -l error -r multiple.json -o example.com-chk.json
```
......@@ -306,7 +310,7 @@ one go.
#### Examples
Analyze the domain name example.com using the first of your configured DNS
resolvers (i.e., in /etc/resolv.conf):
resolvers (i.e., in `/etc/resolv.conf`):
```
$ dnsviz query example.com
```
......@@ -316,7 +320,107 @@ Same, but specify an alternate trust anchor:
$ dnsviz query +trusted-key=tk.txt example.com
```
Analyze example.com through the recurisve resolver at 192.0.2.1:
Analyze example.com through the recursive resolver at 192.0.2.1:
```
$ dnsviz query @192.0.2.1 +trusted-key=tk.txt example.com
```
## Pre-Deployment DNS Testing
The examples in this section demonstrate usage of DNSViz for pre-deployment
testing.
### Pre-Delegation Testing
The following examples involve issuing diagnostic queries for a zone before it
is ever delegated.
Issue queries against a zone file on the local system (`example.com.zone`).
`named(8)` is invoked to serve the file locally:
```
$ dnsviz probe -A -x example.com+:example.com.zone example.com
```
(Note the use of "+", which designates that the parent servers should not be
queried for DS records.)
Issue queries to a server that is serving the zone:
```
$ dnsviz probe -A -x example.com+:192.0.2.1 example.com
```
(Note that this server doesn't need to be a server in the NS RRset for
example.com.)
Issue queries to the servers in the authoritative NS RRset, specified by name
and/or address:
```
$ dnsviz probe -A \
-x example.com+:ns1.example.com=192.0.2.1 \
-x example.com+:ns2.example.com=192.0.2.1,ns2.example.com=[2001:db8::1] \
example.com
```
Specify the names and addresses corresponding to the future delegation NS
records and (as appropriate) A/AAAA glue records in the parent zone (com):
```
$ dnsviz probe -A \
-N example.com:ns1.example.com=192.0.2.1 \
-N example.com:ns2.example.com=192.0.2.1,ns2.example.com=[2001:db8::1] \
example.com
```
Also supply future DS records:
```
$ dnsviz probe -A \
-N example.com:ns1.example.com=192.0.2.1 \
-N example.com:ns2.example.com=192.0.2.1,ns2.example.com=[2001:db8::1] \
-D example.com:dsset-example.com. \
example.com
```
### Pre-Deployment Testing of Authoritative Zone Changes
The following examples involve issuing diagnostic queries for a delegated zone
before changes are deployed.
Issue diagnostic queries for a new zone file that has been created but not yet
been deployed (i.e., with changes to DNSKEY or other records):
```
$ dnsviz probe -A -x example.com:example.com.zone example.com
```
(Note the absence of "+", which designates that the parent servers will be
queried for DS records.)
Issue queries to a server that is serving the new version of the zone:
```
$ dnsviz probe -A -x example.com:192.0.2.1 example.com
```
(Note that this server doesn't need to be a server in the NS RRset for
example.com.)
### Pre-Deployment Testing of Delegation Changes
The following examples involve issuing diagnostic queries for a delegated zone
before changes are deployed to the delegation, glue, or DS records for that
zone.
Specify the names and addresses corresponding to the new delegation NS records
and (as appropriate) A/AAAA glue records in the parent zone (com):
```
$ dnsviz probe -A \
-N example.com:ns1.example.com=192.0.2.1 \
-N example.com:ns2.example.com=192.0.2.1,ns2.example.com=[2001:db8::1] \
example.com
```
Also supply the replacement DS records:
```
$ dnsviz probe -A \
-N example.com:ns1.example.com=192.0.2.1 \
-N example.com:ns2.example.com=192.0.2.1,ns2.example.com=[2001:db8::1] \
-D example.com:dsset-example.com. \
example.com
```
......@@ -20,6 +20,8 @@
# with DNSViz. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import unicode_literals
import importlib
import sys
......
......@@ -20,6 +20,8 @@
# with DNSViz. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import unicode_literals
import datetime
import errno
import socket
......@@ -264,7 +266,7 @@ class DigCommandLineQuery:
else:
try:
tk_str = open(arg).read()
except IOError, e:
except IOError as e:
raise CommandLineException('%s: "%s"' % (e.strerror, arg))
try:
self.trusted_keys = get_trusted_keys(tk_str)
......@@ -293,9 +295,9 @@ class DigCommandLineQuery:
processed_nameservers.extend(_get_nameservers_for_name(addr))
if not use_ipv4:
processed_nameservers = filter(lambda x: x.version != 4, processed_nameservers)
processed_nameservers = [x for x in processed_nameservers if x.version != 4]
if not use_ipv6:
processed_nameservers = filter(lambda x: x.version != 6, processed_nameservers)
processed_nameservers = [x for x in processed_nameservers if x.version != 6]
self.nameservers = nameservers + processed_nameservers
......@@ -430,7 +432,7 @@ class DigCommandLineQuery:
def query_and_display(self, options, filehandle):
try:
server, response = self.query(options)
except transport.RemoteQueryTransportError, e:
except transport.RemoteQueryTransportError as e:
sys.stderr.write('%s\n' % e)
else:
output = self.display(response, server, options)
......@@ -582,7 +584,7 @@ class DigCommandLine:
try:
s = socket.socket(family)
s.bind((addr, 0))
except socket.error, e:
except socket.error as e:
if e.errno == errno.EADDRNOTAVAIL:
raise SemanticException('Cannot bind to specified IP address: "%s"' % addr)
else:
......@@ -684,9 +686,9 @@ class DigCommandLine:
processed_nameservers.extend(_get_nameservers_for_name(addr))
if not self.options['use_ipv4']:
processed_nameservers = filter(lambda x: x.version != 4, processed_nameservers)
processed_nameservers = [x for x in processed_nameservers if x.version != 4]
if not self.options['use_ipv6']:
processed_nameservers = filter(lambda x: x.version != 6, processed_nameservers)
processed_nameservers = [x for x in processed_nameservers if x.version != 6]
self.nameservers = processed_nameservers
......@@ -694,7 +696,7 @@ def main():
try:
q = DigCommandLine(sys.argv[1:])
q.query_and_display()
except (CommandLineException, SemanticException), e:
except (CommandLineException, SemanticException) as e:
sys.stderr.write('%s\n' % e)
sys.exit(1)
except KeyboardInterrupt:
......
......@@ -20,14 +20,21 @@
# with DNSViz. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import unicode_literals
import cgi
import json
import os
import Queue
import re
import struct
import sys
# python3/python2 dual compatibility
try:
import queue
except ImportError:
import Queue as queue
from dnsviz.ipaddr import *
from dnsviz import transport
......@@ -69,7 +76,12 @@ def get_qname(msg):
# no label
if index >= len(msg):
raise InvalidName()
l = struct.unpack('!B', msg[index])[0]
# python3/python2 dual compatibility
if isinstance(msg, str):
l = struct.unpack('!B', msg[index])[0]
else:
l = msg[index]
# no compression allowed in question
if l & 0xc0:
......@@ -149,11 +161,13 @@ def check_qname(msg):
def main():
try:
if os.environ.get('REQUEST_METHOD', '') != 'POST':
raise RemoteQueryError('Request method %s not supported' % os.environ.get('REQUEST_METHOD'))
if not os.environ.get('REQUEST_METHOD', None):
os.environ['REQUEST_METHOD'] = 'POST'
if os.environ['REQUEST_METHOD'] != 'POST':
raise RemoteQueryError('Request method %s not supported' % os.environ['REQUEST_METHOD'])
form = cgi.FieldStorage()
response_queue = Queue.Queue()
response_queue = queue.Queue()
queries_in_waiting = set()
th_factory = transport.DNSQueryTransportHandlerDNSFactory()
tm = transport.DNSQueryTransportManager()
......@@ -171,13 +185,13 @@ def main():
if 'version' not in content:
raise RemoteQueryError('No version information in HTTP request.')
try:
major_vers, minor_vers = map(int, str(content['version']).split('.', 1))
major_vers, minor_vers = [int(x) for x in str(content['version']).split('.', 1)]
except ValueError:
raise RemoteQueryError('Version of JSON input in HTTP request is invalid: %s' % content['version'])
# ensure major version is a match and minor version is no greater
# than the current minor version
curr_major_vers, curr_minor_vers = map(int, str(transport.DNS_TRANSPORT_VERSION).split('.', 1))
curr_major_vers, curr_minor_vers = [int(x) for x in str(transport.DNS_TRANSPORT_VERSION).split('.', 1)]
if major_vers != curr_major_vers or minor_vers > curr_minor_vers:
raise RemoteQueryError('Version %d.%d of JSON input in HTTP request is incompatible with this software.' % (major_vers, minor_vers))
......@@ -190,7 +204,7 @@ def main():
try:
qtm = transport.DNSQueryTransportMeta.deserialize_request(qtm_serialized)
except transport.TransportMetaDeserializationError, e:
except transport.TransportMetaDeserializationError as e:
raise RemoteQueryError('Error deserializing request information: %s' % e)
check_dst(qtm.dst)
......@@ -215,7 +229,7 @@ def main():
'version': transport.DNS_TRANSPORT_VERSION,
'responses': [qtm.serialize_response() for qtm in qtms],
}
except RemoteQueryError, e:
except RemoteQueryError as e:
ret = {
'version': transport.DNS_TRANSPORT_VERSION,
'error': str(e),
......
from online import WILDCARD_EXPLICIT_DELEGATION, Analyst, OnlineDomainNameAnalysis, PrivateAnalyst, RecursiveAnalyst, PrivateRecursiveAnalyst, NetworkConnectivityException, DNS_RAW_VERSION
from offline import OfflineDomainNameAnalysis, TTLAgnosticOfflineDomainNameAnalysis, DNS_PROCESSED_VERSION
from .online import WILDCARD_EXPLICIT_DELEGATION, Analyst, OnlineDomainNameAnalysis, PrivateAnalyst, RecursiveAnalyst, PrivateRecursiveAnalyst, NetworkConnectivityException, DNS_RAW_VERSION
from .offline import OfflineDomainNameAnalysis, TTLAgnosticOfflineDomainNameAnalysis, DNS_PROCESSED_VERSION
......@@ -19,6 +19,8 @@
# with DNSViz. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import unicode_literals
import cgi
import collections
import datetime
......@@ -52,14 +54,11 @@ class DomainNameAnalysisError(object):
def __str__(self):
return self.code
def __unicode__(self):
return self.code
def __eq__(self, other):
return self.__class__ == other.__class__ and self.args == other.args
def copy(self):
return self.__class__(**dict(zip(self.required_params, self.args)))
return self.__class__(**dict(list(zip(self.required_params, self.args))))
@property
def args(self):
......@@ -80,10 +79,10 @@ class DomainNameAnalysisError(object):
description_template_escaped = cgi.escape(self.description_template, True)
template_kwargs_escaped = {}
for n, v in self.template_kwargs.items():
if isinstance(v, (int, long)):
if isinstance(v, int):
template_kwargs_escaped[n] = v
else:
if isinstance(v, (str, unicode)):
if isinstance(v, str):
template_kwargs_escaped[n] = cgi.escape(v)
else:
template_kwargs_escaped[n] = cgi.escape(str(v))
......@@ -291,6 +290,42 @@ class ExpirationInPast(RRSIGError):
diff = self.template_kwargs['reference_time'] - self.template_kwargs['expiration']
self.template_kwargs['expired_time'] = fmt.humanize_time(diff.seconds, diff.days)
class InceptionWithinClockSkew(RRSIGError):
'''
>>> e = InceptionWithinClockSkew(inception=datetime.datetime(2015,1,10,0,0,0), reference_time=datetime.datetime(2015,1,10,0,0,1))
>>> e.description
'The value of the Signature Inception field of the RRSIG RR (2015-01-10 00:00:00) is within possible clock skew range of the current time (2015-01-10 00:00:01)'.
'''
_abstract = False
code = 'INCEPTION_WITHIN_CLOCK_SKEW'
description_template = "The value of the Signature Inception field of the RRSIG RR (%(inception)s) is within possible clock skew range (%(difference)s) of the current time (%(reference_time)s)."
references = ['RFC 4035, Sec. 5.3.1']
required_params = ['inception', 'reference_time']
def __init__(self, **kwargs):
super(InceptionWithinClockSkew, self).__init__(**kwargs)
diff = self.template_kwargs['reference_time'] - self.template_kwargs['inception']
self.template_kwargs['difference'] = fmt.humanize_time(diff.seconds, diff.days)
class ExpirationWithinClockSkew(RRSIGError):
'''
>>> e = ExpirationWithinClockSkew(expiration=datetime.datetime(2015,1,10,0,0,1), reference_time=datetime.datetime(2015,1,10,0,0,0))
>>> e.description
'The value of the Signature Expiration field of the RRSIG RR (2015-01-10 00:00:01) is within possible clock skew range of the current time (2015-01-10 00:00:00)'.
'''
_abstract = False
code = 'EXPIRATION_WITHIN_CLOCK_SKEW'
description_template = "The value of the Signature Expiration field of the RRSIG RR (%(expiration)s) is within possible clock skew range (%(difference)s) of the current time (%(reference_time)s)."
references = ['RFC 4035, Sec. 5.3.1']
required_params = ['expiration', 'reference_time']
def __init__(self, **kwargs):
super(ExpirationWithinClockSkew, self).__init__(**kwargs)
diff = self.template_kwargs['expiration'] - self.template_kwargs['reference_time']
self.template_kwargs['difference'] = fmt.humanize_time(diff.seconds, diff.days)
class SignatureInvalid(RRSIGError):
'''
>>> e = SignatureInvalid()
......@@ -1326,6 +1361,19 @@ class InconsistentNXDOMAIN(ResponseError):
required_params = ['qname', 'rdtype_nxdomain', 'rdtype_noerror']
references = ['RFC 1034, Sec. 4.3.2']
class InconsistentNXDOMAINAncestry(ResponseError):
'''
>>> e = InconsistentNXDOMAINAncestry(qname='foo.baz.', ancestor_qname='baz.')
>>> e.description
"A query for foo.baz. results in a NOERROR response, while a query for its ancestor, baz., returns a name error (NXDOMAIN), which indicates that subdomains of baz., including foo.baz., don't exist."
'''
_abstract = False
code = 'INCONSISTENT_NXDOMAIN_ANCESTOR'
description_template = "A query for %(qname)s results in a NOERROR response, while a query for its ancestor, %(ancestor_qname)s, returns a name error (NXDOMAIN), which indicates that subdomains of %(ancestor_qname)s, including %(qname)s, don't exist."
required_params = ['qname', 'ancestor_qname']
references = []
class PMTUExceeded(ResponseError):
'''
>>> e = PMTUExceeded(pmtu_lower_bound=None, pmtu_upper_bound=None)
......@@ -1508,12 +1556,12 @@ class GlueMismatchError(DelegationError):
'''
>>> e = GlueMismatchError(name='ns1.foo.baz.', glue_addresses=('192.0.2.1',), auth_addresses=('192.0.2.2',))
>>> e.description
'The glue address(es) for ns1.foo.baz. (192.0.2.1) differed from their respective authoritative address(es) (192.0.2.2).'
'The glue address(es) for ns1.foo.baz. (192.0.2.1) differed from its authoritative address(es) (192.0.2.2).'
'''
_abstract = False
code = 'GLUE_MISMATCH'
description_template = 'The glue address(es) for %(name)s (%(glue_addresses_text)s) differed from their respective authoritative address(es) (%(auth_addresses_text)s).'
description_template = 'The glue address(es) for %(name)s (%(glue_addresses_text)s) differed from its authoritative address(es) (%(auth_addresses_text)s).'
required_params = ['name', 'glue_addresses', 'auth_addresses']
def __init__(self, **kwargs):
......@@ -1521,6 +1569,54 @@ class GlueMismatchError(DelegationError):
self.template_kwargs['glue_addresses_text'] = ', '.join(self.template_kwargs['glue_addresses'])
self.template_kwargs['auth_addresses_text'] = ', '.join(self.template_kwargs['auth_addresses'])
class MissingGlueIPv4(DelegationError):
'''
>>> e = MissingGlueIPv4(name='ns1.foo.baz.')
>>> e.description
'Authoritative A records exist for ns1.foo.baz., but there are no corresponding A glue records.'
'''
_abstract = False
code = 'MISSING_GLUE_IPV4'
description_template = "Authoritative A records exist for %(name)s, but there are no corresponding A glue records."
required_params = ['name']
class MissingGlueIPv6(DelegationError):
'''
>>> e = MissingGlueIPv6(name='ns1.foo.baz.')
>>> e.description
'Authoritative AAAA records exist for ns1.foo.baz., but there are no corresponding AAAA glue records.'
'''
_abstract = False
code = 'MISSING_GLUE_IPV6'
description_template = "Authoritative AAAA records exist for %(name)s, but there are no corresponding AAAA glue records."
required_params = ['name']
class ExtraGlueIPv4(DelegationError):
'''
>>> e = ExtraGlueIPv4(name='ns1.foo.baz.')
>>> e.description
'A glue records exist for ns1.foo.baz., but there are no corresponding authoritative A records.'
'''
_abstract = False
code = 'EXTRA_GLUE_IPV4'
description_template = "A glue records exist for %(name)s, but there are no corresponding authoritative A records."
required_params = ['name']
class ExtraGlueIPv6(DelegationError):
'''
>>> e = ExtraGlueIPv6(name='ns1.foo.baz.')
>>> e.description
'AAAA glue records exist for ns1.foo.baz., but there are no corresponding authoritative AAAA records.'
'''
_abstract = False
code = 'EXTRA_GLUE_IPV6'
description_template = "AAAA glue records exist for %(name)s, but there are no corresponding authoritative AAAA records."
required_params = ['name']
class ServerUnresponsive(DelegationError):
description_template = "The server(s) were not responsive to queries over %(proto)s."
proto = None
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -56,16 +56,18 @@
#
# The contents of this module are derived the base64 module of python 2.7, with
# the value of _b32tab modified to use the Base 32 Encoding with Extended Hex
# Alphabet, as specified in RFC 4648.
#
# Alphabet, as specified in RFC 4648. Also, bytes literals are prefixed with
# 'b'.
from __future__ import unicode_literals
import struct
_b32tab = { 0: '0', 1: '1', 2: '2', 3: '3', 4: '4', 5: '5', 6: '6', 7: '7', 8: '8', 9: '9',
10: 'A', 11: 'B', 12: 'C', 13: 'D', 14: 'E', 15: 'F', 16: 'G', 17: 'H', 18: 'I', 19: 'J',
20: 'K', 21: 'L', 22: 'M', 23: 'N', 24: 'O', 25: 'P', 26: 'Q', 27: 'R', 28: 'S', 29: 'T',
30: 'U', 31: 'V' }
EMPTYSTRING = ''
_b32tab = { 0: b'0', 1: b'1', 2: b'2', 3: b'3', 4: b'4', 5: b'5', 6: b'6', 7: b'7', 8: b'8', 9: b'9',
10: b'A', 11: b'B', 12: b'C', 13: b'D', 14: b'E', 15: b'F', 16: b'G', 17: b'H', 18: b'I', 19: b'J',
20: b'K', 21: b'L', 22: b'M', 23: b'N', 24: b'O', 25: b'P', 26: b'Q', 27: b'R', 28: b'S', 29: b'T',
30: b'U', 31: b'V' }
EMPTYSTRING = b''
b32alphabet = set(_b32tab.values())
......@@ -78,7 +80,7 @@ def b32encode(s):
quanta, leftover = divmod(len(s), 5)
# Pad the last quantum with zero bits if necessary
if leftover:
s += ('\0' * (5 - leftover))
s += (b'\0' * (5 - leftover))
quanta += 1
for i in range(quanta):
# c1 and c2 are 16 bits wide, c3 is 8 bits wide. The intent of this
......@@ -86,7 +88,7 @@ def b32encode(s):
# leftover bit of c1 and tack it onto c2. Then we take the 2 leftover
# bits of c2 and tack them onto c3. The shifts and masks are intended
# to give us values of exactly 5 bits in width.
c1, c2, c3 = struct.unpack('!HHB', s[i*5:(i+1)*5])
c1, c2, c3 = struct.unpack(b'!HHB', s[i*5:(i+1)*5])
c2 += (c1 & 1) << 16 # 17 bits wide
c3 += (c2 & 3) << 8 # 10 bits wide
parts.extend([_b32tab[c1 >> 11], # bits 1 - 5
......@@ -101,11 +103,11 @@ def b32encode(s):
encoded = EMPTYSTRING.join(parts)
# Adjust for any leftover partial quanta
if leftover == 1:
return encoded[:-6] + '======'
return encoded[:-6] + b'======'
elif leftover == 2:
return encoded[:-4] + '===='
return encoded[:-4] + b'===='
elif leftover == 3:
return encoded[:-3] + '==='
return encoded[:-3] + b'==='
elif leftover == 4:
return encoded[:-1] + '='
return encoded[:-1] + b'='
return encoded
......@@ -20,8 +20,11 @@
# with DNSViz. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import unicode_literals
import codecs
import getopt
import io
import json
import logging
import os
......@@ -32,6 +35,7 @@ import dns.exception, dns.name
from dnsviz.analysis import OfflineDomainNameAnalysis, DNS_RAW_VERSION
from dnsviz.config import DNSVIZ_SHARE_PATH, JQUERY_PATH, JQUERY_UI_PATH, JQUERY_UI_CSS_PATH, RAPHAEL_PATH
from dnsviz.format import latin1_binary_to_string as lb2s
from dnsviz.util import TRUSTED_KEYS_ROOT, get_trusted_keys
# If the import of DNSAuthGraph fails because of the lack of pygraphviz, it
......@@ -69,22 +73,20 @@ Options:
-h - display the usage and exit
''' % (err))
def finish_graph(G, name_objs, rdtypes, trusted_keys, fmt, filename, fh=None):
assert filename is not None or fh is not None, 'Either filename or fh must be passed'
def finish_graph(G, name_objs, rdtypes, trusted_keys, fmt, filename):
G.add_trust(trusted_keys)
G.remove_extra_edges()
if fmt == 'html':
try:
js_img = G.draw('js')
except IOError, e:
js_img = codecs.decode(G.draw('js'), 'utf-8')
except IOError as e:
logger.error(str(e))
sys.exit(3)
try:
template_str = codecs.open(DNSSEC_TEMPLATE_FILE, 'r', 'utf-8').read()
except IOError, e:
template_str = io.open(DNSSEC_TEMPLATE_FILE, 'r', encoding='utf-8').read()
except IOError as e:
logger.error('Error reading template file "%s": %s' % (DNSSEC_TEMPLATE_FILE, e.strerror))
sys.exit(3)
......@@ -94,21 +96,21 @@ def finish_graph(G, name_objs, rdtypes, trusted_keys, fmt, filename, fh=None):
template_str = template_str.replace('JQUERY_UI_CSS_PATH', JQUERY_UI_CSS_PATH)
template_str = template_str.replace('RAPHAEL_PATH', RAPHAEL_PATH)
template_str = template_str.replace('JS_CODE', js_img)
if filename is None:
fh.write(template_str)
else:
try:
codecs.open(filename, 'w', 'utf-8').write(template_str)
except IOError, e:
logger.error('%s: "%s"' % (e.strerror, filename))
sys.exit(3)
filename = sys.stdout.fileno()
try:
io.open(filename, 'wt', encoding='utf-8').write(template_str)
except IOError as e:
logger.error('%s: "%s"' % (e.strerror, filename))
sys.exit(3)
else:
if filename is None:
fh.write(G.draw(fmt))
io.open(sys.stdout.fileno(), 'wb').write(G.draw(fmt))
else:
try:
G.draw(fmt, path=filename)
except IOError, e:
except IOError as e:
if e.strerror:
logger.error('%s: "%s"' % (e.strerror, filename))
else:
......@@ -145,7 +147,7 @@ def main(argv):
try:
opts, args = getopt.getopt(argv[1:], 'f:r:R:t:Oo:T:h')
except getopt.GetoptError, e:
except getopt.GetoptError as e:
usage(str(e))
sys.exit(1)
......@@ -154,8 +156,8 @@ def main(argv):
for opt, arg in opts:
if opt == '-t':
try:
tk_str = open(arg).read()
except IOError, e:
tk_str = io.open(arg, 'r', encoding='utf-8').read()
except IOError as e:
sys.stderr.write('%s: "%s"\n' % (e.strerror, arg))
sys.exit(3)
try:
......@@ -180,7 +182,7 @@ def main(argv):
usage('The list of types was invalid: "%s"' % opts['-R'])
sys.exit(1)
try:
rdtypes = map(dns.rdatatype.from_text, rdtypes)
rdtypes = [dns.rdatatype.from_text(x) for x in rdtypes]
except dns.rdatatype.UnknownRdatatype:
usage('The list of types was invalid: "%s"' % opts['-R'])
sys.exit(1)
......@@ -207,13 +209,14 @@ def main(argv):
logger.setLevel(logging.WARNING)
if '-r' not in opts or opts['-r'] == '-':
analysis_str = codecs.getreader('utf-8')(sys.stdin).read()
opt_r = sys.stdin.fileno()