1#
2# This file is a part of DNSViz, a tool suite for DNS/DNSSEC monitoring,
3# analysis, and visualization.
4# Created by Casey Deccio (casey@deccio.net)
5#
6# Copyright 2012-2014 Sandia Corporation. Under the terms of Contract
7# DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
8# certain rights in this software.
9#
10# Copyright 2014-2016 VeriSign, Inc.
11#
12# Copyright 2016-2021 Casey Deccio
13#
14# DNSViz is free software; you can redistribute it and/or modify
15# it under the terms of the GNU General Public License as published by
16# the Free Software Foundation; either version 2 of the License, or
17# (at your option) any later version.
18#
19# DNSViz is distributed in the hope that it will be useful,
20# but WITHOUT ANY WARRANTY; without even the implied warranty of
21# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22# GNU General Public License for more details.
23#
24# You should have received a copy of the GNU General Public License along
25# with DNSViz.  If not, see <http://www.gnu.org/licenses/>.
26#
27
28from __future__ import unicode_literals
29
30import datetime
31import io
32import os
33import re
34import socket
35
36import dns.message, dns.name, dns.rdataclass, dns.rdatatype, dns.rrset
37
38from .config import DNSVIZ_SHARE_PATH
39from . import format as fmt
40from .ipaddr import IPAddr
41
42ROOT_HINTS = os.path.join(DNSVIZ_SHARE_PATH, 'hints', 'named.root')
43
44CR_RE = re.compile(r'\r\n', re.MULTILINE)
45ZONE_COMMENTS_RE = re.compile(r'\s*;.*', re.MULTILINE)
46BLANK_LINES_RE = re.compile(r'\n\s*\n')
47
48ROOT_HINTS_STR_DEFAULT = '''
49.                        3600000      NS    A.ROOT-SERVERS.NET.
50A.ROOT-SERVERS.NET.      3600000      A     198.41.0.4
51A.ROOT-SERVERS.NET.      3600000      AAAA  2001:503:ba3e::2:30
52.                        3600000      NS    B.ROOT-SERVERS.NET.
53B.ROOT-SERVERS.NET.      3600000      A     192.228.79.201
54B.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:200::b
55.                        3600000      NS    C.ROOT-SERVERS.NET.
56C.ROOT-SERVERS.NET.      3600000      A     192.33.4.12
57C.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2::c
58.                        3600000      NS    D.ROOT-SERVERS.NET.
59D.ROOT-SERVERS.NET.      3600000      A     199.7.91.13
60D.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2d::d
61.                        3600000      NS    E.ROOT-SERVERS.NET.
62E.ROOT-SERVERS.NET.      3600000      A     192.203.230.10
63E.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:a8::e
64.                        3600000      NS    F.ROOT-SERVERS.NET.
65F.ROOT-SERVERS.NET.      3600000      A     192.5.5.241
66F.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2f::f
67.                        3600000      NS    G.ROOT-SERVERS.NET.
68G.ROOT-SERVERS.NET.      3600000      A     192.112.36.4
69G.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:12::d0d
70.                        3600000      NS    H.ROOT-SERVERS.NET.
71H.ROOT-SERVERS.NET.      3600000      A     198.97.190.53
72H.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:1::53
73.                        3600000      NS    I.ROOT-SERVERS.NET.
74I.ROOT-SERVERS.NET.      3600000      A     192.36.148.17
75I.ROOT-SERVERS.NET.      3600000      AAAA  2001:7fe::53
76.                        3600000      NS    J.ROOT-SERVERS.NET.
77J.ROOT-SERVERS.NET.      3600000      A     192.58.128.30
78J.ROOT-SERVERS.NET.      3600000      AAAA  2001:503:c27::2:30
79.                        3600000      NS    K.ROOT-SERVERS.NET.
80K.ROOT-SERVERS.NET.      3600000      A     193.0.14.129
81K.ROOT-SERVERS.NET.      3600000      AAAA  2001:7fd::1
82.                        3600000      NS    L.ROOT-SERVERS.NET.
83L.ROOT-SERVERS.NET.      3600000      A     199.7.83.42
84L.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:9f::42
85.                        3600000      NS    M.ROOT-SERVERS.NET.
86M.ROOT-SERVERS.NET.      3600000      A     202.12.27.33
87M.ROOT-SERVERS.NET.      3600000      AAAA  2001:dc3::35
88'''
89
90HISTORICAL_ROOT_IPS = (
91        (dns.name.from_text('h.root-servers.net.'), IPAddr('2001:500:1::803f:235')), # 2015-12-01
92        (dns.name.from_text('l.root-servers.net.'), IPAddr('2001:500:3::42')), # 2016-03-24
93        (dns.name.from_text('b.root-servers.net.'), IPAddr('2001:500:84::b')), # 2017-06-01
94)
95
96# The following list should include all current and historical trust anchors
97# for the root zone.  This makes it possible to perform analysis of current DNS
98# responses, but also archived DNS responses.
99#
100# For each root zone trust anchor, the start value is the day it is first
101# published in the root zone.  Even though the key is not yet signing, this
102# draws attention to the fact that it will be signing and likely replacing its
103# predecessor.  The end value is (at least) the minimum of the TTL and the
104# expiration of its last published RRSIG.  This allows us to query caches with
105# contents referring to the old key, even after its replacement has taken over.
106TRUSTED_KEYS_ROOT = (
107        ('. IN DNSKEY 257 3 8 AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQbSEW0O8gcCjF FVQUTf6v58fLjwBd0YI0EzrAcQqBGCzh/RStIoO8g0NfnfL2MTJRkxoX bfDaUeVPQuYEhg37NZWAJQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaD X6RS6CXpoY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3LQpz W5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGOYl7OyQdXfZ57relS Qageu+ipAdTTJ25AsRTAoub8ONGcLmqrAmRLKBP1dfwhYB4N7knNnulq QxA+Uk1ihz0=',
108            datetime.datetime(2010, 7, 16, 0, 0, 0, 0, fmt.utc),
109            datetime.datetime(2018, 10, 15, 16, 0, 0, 0, fmt.utc)), # 2018-10-11 16:00:00 UTC + 4 days (2*TTL)
110        ('. IN DNSKEY 257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3 +/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kv ArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF 0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+e oZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfd RUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwN R1AkUTV74bU=',
111            datetime.datetime(2017, 8, 11, 0, 0, 0, 0, fmt.utc), # 2017-07-12 00:00:00 UTC + 30 days (RFC 5011 add hold-down time)
112            None),
113)
114
115def tuple_to_dict(t):
116    d = {}
117    for n, v in t:
118        if n not in d:
119            d[n] = []
120        d[n].append(v)
121    return d
122
123def get_trusted_keys(s):
124    trusted_keys = []
125
126    s = CR_RE.sub('\n', s)
127    s = ZONE_COMMENTS_RE.sub('', s)
128    s = BLANK_LINES_RE.sub(r'\n', s)
129    s = s.strip()
130    m = dns.message.from_text(str(';ANSWER\n'+s))
131    for rrset in m.answer:
132        if rrset.rdtype != dns.rdatatype.DNSKEY:
133            continue
134        for dnskey in rrset:
135            if dnskey.flags & fmt.DNSKEY_FLAGS['revoke']:
136                continue
137            trusted_keys.append((rrset.name,dnskey))
138
139    return trusted_keys
140
141def get_default_trusted_keys(date):
142    tk_str = ''
143    for tk, start, end in TRUSTED_KEYS_ROOT:
144        if start is not None and date < start:
145            continue
146        if end is not None and date > end:
147            continue
148        tk_str += tk + '\n'
149    return get_trusted_keys(tk_str)
150
151def get_hints(s):
152    hints = {}
153
154    s = CR_RE.sub('\n', s)
155    s = ZONE_COMMENTS_RE.sub('', s)
156    s = BLANK_LINES_RE.sub(r'\n', s)
157    s = s.strip()
158    m = dns.message.from_text(str(';ANSWER\n'+s))
159    for rrset in m.answer:
160        if rrset.rdtype not in (dns.rdatatype.NS, dns.rdatatype.A, dns.rdatatype.AAAA):
161            continue
162        hints[(rrset.name, rrset.rdtype)] = rrset
163
164    return hints
165
166def get_root_hints():
167    try:
168        with io.open(ROOT_HINTS, 'r', encoding='utf-8') as fh:
169            return get_hints(fh.read())
170    except IOError:
171        return get_hints(ROOT_HINTS_STR_DEFAULT)
172
173def get_client_address(server):
174    if server.version == 6:
175        af = socket.AF_INET6
176    else:
177        af = socket.AF_INET
178    s = socket.socket(af, socket.SOCK_DGRAM)
179    try:
180        s.connect((server, 53))
181    except socket.error:
182        ip = None
183    else:
184        ip = IPAddr(s.getsockname()[0])
185    s.close()
186    return ip
187