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