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 calendar 31import codecs 32import datetime 33import re 34import time 35 36import dns.name, dns.rdatatype 37 38DNSKEY_FLAGS = {'ZONE': 0x0100, 'SEP': 0x0001, 'revoke': 0x0080} 39DNSKEY_PROTOCOLS = { 3: 'DNSSEC' } 40DNSKEY_ALGORITHMS = { 1: 'RSA/MD5', 2: 'Diffie-Hellman', 3: 'DSA/SHA1', 5: 'RSA/SHA-1', 6: 'DSA-NSEC3-SHA1', 7: 'RSASHA1-NSEC3-SHA1', \ 41 8: 'RSA/SHA-256', 10: 'RSA/SHA-512', 12: 'GOST R 34.10-2001', 13: 'ECDSA Curve P-256 with SHA-256', 14: 'ECDSA Curve P-384 with SHA-384', 42 15: 'Ed25519', 16: 'Ed448' } 43DS_DIGEST_TYPES = { 1: 'SHA-1', 2: 'SHA-256', 3: 'GOST 34.11-94', 4: 'SHA-384' } 44 45NSEC3_FLAGS = {'OPTOUT': 0x01} 46 47DNS_FLAG_DESCRIPTIONS = { 48 32768: 'Query Response', 1024: 'Authoritative Answer', 512: 'Truncated Response', 49 256: 'Recursion Desired', 128: 'Recursion Available', 32: 'Authentic Data', 16: 'Checking Disabled' 50} 51 52EDNS_FLAG_DESCRIPTIONS = { 32768: 'DNSSEC answer OK' } 53 54EDNS_OPT_DESCRIPTIONS = { 3: 'NSID', 8: 'edns-client-subnet', 10: 'COOKIE' } 55 56FMT_MS = '%Y-%m-%d %H:%M:%S.%f %Z' 57FMT_NO_MS = '%Y-%m-%d %H:%M:%S %Z' 58 59ZERO = datetime.timedelta(0) 60class UTC(datetime.tzinfo): 61 '''UTC''' 62 63 def utcoffset(self, dt): 64 return ZERO 65 66 def tzname(self, dt): 67 # python3/python2 dual compatibility 68 if type(b'') is str: 69 return b'UTC' 70 else: 71 return 'UTC' 72 73 def dst(self, dt): 74 return ZERO 75 76utc = UTC() 77 78################# 79# Timestamp conversions 80def timestamp_to_datetime(timestamp, tz=utc): 81 return datetime.datetime.fromtimestamp(timestamp, tz) 82 83def datetime_to_timestamp(dt): 84 return calendar.timegm(dt.timetuple()) + dt.microsecond/1.0e6 85 86def str_to_datetime(s, tz=utc): 87 return timestamp_to_datetime(str_to_timestamp(s), tz) 88 89def str_to_timestamp(s): 90 try: 91 return calendar.timegm(time.strptime(s, FMT_NO_MS)) 92 except ValueError: 93 return calendar.timegm(time.strptime(s, FMT_MS)) 94 95def datetime_to_str(dt): 96 if dt.microsecond: 97 return dt.strftime(FMT_MS) 98 else: 99 return dt.strftime(FMT_NO_MS) 100 101def timestamp_to_str(timestamp): 102 return datetime_to_str(timestamp_to_datetime(timestamp)) 103 104################# 105# Human representation of time 106def humanize_time(seconds, days=None): 107 if days is None: 108 days, remainder = divmod(seconds, 86400) 109 else: 110 remainder = seconds 111 hours, remainder = divmod(remainder, 3600) 112 minutes, seconds = divmod(remainder, 60) 113 114 output = '' 115 if days > 0: 116 if days != 1: 117 plural = 's' 118 else: 119 plural = '' 120 output += '%d day%s' % (days, plural) 121 else: 122 if hours > 0: 123 if hours != 1: 124 plural = 's' 125 else: 126 plural = '' 127 output += '%d hour%s' % (hours, plural) 128 if minutes > 0: 129 if output: 130 output += ', ' 131 if minutes != 1: 132 plural = 's' 133 else: 134 plural = '' 135 output += '%d minute%s' % (minutes, plural) 136 if not output: 137 if seconds != 1: 138 plural = 's' 139 else: 140 plural = '' 141 output += '%d second%s' % (seconds, plural) 142 return output 143 144def format_diff(date_now, date_relative): 145 if date_now > date_relative: 146 diff = date_now - date_relative 147 suffix = 'in the past' 148 else: 149 diff = date_relative - date_now 150 suffix = 'in the future' 151 return '%s %s' % (humanize_time(diff.seconds, diff.days), suffix) 152 153################# 154# Human representation of DNS names 155def format_nsec3_name(name): 156 return lb2s(dns.name.from_text(name.labels[0].upper(), name.parent().canonicalize()).to_text()) 157 158def format_nsec3_rrset_text(nsec3_rrset_text): 159 return re.sub(r'^(\d+\s+\d+\s+\d+\s+\S+\s+)([0-9a-zA-Z]+)', lambda x: '%s%s' % (x.group(1), x.group(2).upper()), nsec3_rrset_text).rstrip('.') 160 161def humanize_name(name, idn=False, canonicalize=True): 162 if canonicalize: 163 name = name.canonicalize() 164 if idn: 165 try: 166 name = name.to_unicode() 167 except UnicodeError: 168 name = lb2s(name.to_text()) 169 else: 170 name = lb2s(name.to_text()) 171 if name == '.': 172 return name 173 return name.rstrip('.') 174 175def latin1_binary_to_string(s): 176 # python3/python2 dual compatibility 177 #XXX In places where this method wraps calls to dns.name.Name.to_text(), 178 # this is no longer needed with dnspython 1.15.0 179 if isinstance(s, bytes): 180 return codecs.decode(s, 'latin1') 181 return s 182lb2s = latin1_binary_to_string 183