summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2024-04-30 18:38:47 +0100
committerColin Watson <cjwatson@debian.org>2024-04-30 18:40:57 +0100
commited1f0077b20669e0dac7b88c90b04d203773c705 (patch)
tree27df39bb2c3a75d8df7881f11ac56de68d02d312
parentecd96126fca6205b955593642adb65c9d9a602b4 (diff)
parent3b4aff8e0a40db5429ac298ee37a19652b18c492 (diff)
Update upstream source from tag 'upstream/2.1.0'
Update to upstream version '2.1.0' with Debian dir 53c97fd6e5f4fc850e8f41dc985981bf2d43f458
-rw-r--r--Dockerfile4
-rw-r--r--README.md31
-rw-r--r--debian/changelog7
-rw-r--r--debian/copyright2
-rwxr-xr-xdnseval.py21
-rwxr-xr-xdnsping.py125
-rwxr-xr-xdnstraceroute.py23
-rw-r--r--requirements.txt6
-rw-r--r--setup.py8
-rw-r--r--tox.ini4
-rw-r--r--util/dns.py2
-rw-r--r--util/shared.py44
-rw-r--r--util/whois.py2
13 files changed, 166 insertions, 113 deletions
diff --git a/Dockerfile b/Dockerfile
index 72fbf76..f8c6ac5 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,9 @@
-FROM python:3.8.2
+FROM python:3.8-alpine
WORKDIR /dnsdiag
+ENV PATH "$PATH:/dnsdiag"
+
COPY . .
RUN pip install --no-cache-dir -r requirements.txt
diff --git a/README.md b/README.md
index 447c262..0ae62cf 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-[![Build Status](https://travis-ci.org/farrokhi/dnsdiag.svg)](https://travis-ci.org/farrokhi/dnsdiag) [![PyPI](https://img.shields.io/pypi/v/dnsdiag.svg?maxAge=8600)](https://pypi.python.org/pypi/dnsdiag/) [![PyPI](https://img.shields.io/pypi/l/dnsdiag.svg?maxAge=8600)]() [![PyPI](https://img.shields.io/pypi/pyversions/dnsdiag.svg?maxAge=8600)]() [![Docker Pulls](https://img.shields.io/docker/pulls/farrokhi/dnsdiag)](https://hub.docker.com/r/farrokhi/dnsdiag) [![GitHub stars](https://img.shields.io/github/stars/farrokhi/dnsdiag.svg?style=social&label=Star&maxAge=8600)](https://github.com/farrokhi/dnsdiag/stargazers)
+[![Build Status](https://travis-ci.org/farrokhi/dnsdiag.svg)](https://travis-ci.org/farrokhi/dnsdiag) [![PyPI](https://img.shields.io/pypi/v/dnsdiag.svg?maxAge=8600)](https://pypi.python.org/pypi/dnsdiag/) [![PyPI](https://img.shields.io/pypi/l/dnsdiag.svg?maxAge=8600)]() [![Downloads](https://static.pepy.tech/personalized-badge/dnsdiag?period=total&units=international_system&left_color=grey&right_color=blue&left_text=PyPi%20Downloads)](https://pepy.tech/project/dnsdiag) [![PyPI](https://img.shields.io/pypi/pyversions/dnsdiag.svg?maxAge=8600)]() [![Docker Pulls](https://img.shields.io/docker/pulls/farrokhi/dnsdiag)](https://hub.docker.com/r/farrokhi/dnsdiag) [![GitHub stars](https://img.shields.io/github/stars/farrokhi/dnsdiag.svg?style=social&label=Star&maxAge=8600)](https://github.com/farrokhi/dnsdiag/stargazers)
DNS Measurement, Troubleshooting and Security Auditing Toolset
===============================================================
@@ -54,7 +54,7 @@ From time to time, binary packages will be released for Windows, Mac OS X and Li
If you don't want to install dnsdiags on your local machine, you may use the docker image and run programs in a container. For example:
```
-docker run -it --rm farrokhi/dnsdiag ./dnsping.py
+docker run --network host -it --rm farrokhi/dnsdiag dnsping.py
```
# dnsping
@@ -67,18 +67,21 @@ A complete explanation of supported command line flags is shown by using `--help
In addition to UDP, you can ping using TCP, DoT (DNS over TLS) and DoH (DNS over HTTPS) using `--tcp`, `--tls` and `--doh` respectively.
+```shell
+./dnsping.py -c 5 --dnssec --flags --tls -t AAAA -s 9.9.9.9 ripe.net
+```
+
```
-% ./dnsping.py -c 5 --dnssec --flags --tls -t AAAA -s 9.9.9.9 ripe.net
dnsping.py DNS: 9.9.9.9:853, hostname: ripe.net, proto: TLS, rdatatype: AAAA, flags: RD
-233 bytes from 9.9.9.9: seq=1 time=186.202 ms [QR RD RA AD]
-233 bytes from 9.9.9.9: seq=2 time=191.233 ms [QR RD RA AD]
-233 bytes from 9.9.9.9: seq=3 time=105.455 ms [QR RD RA AD]
-233 bytes from 9.9.9.9: seq=4 time=111.053 ms [QR RD RA AD]
-233 bytes from 9.9.9.9: seq=5 time=110.329 ms [QR RD RA AD]
+169 bytes from 9.9.9.9: seq=1 time=279.805 ms [QR RD RA AD] NOERROR
+169 bytes from 9.9.9.9: seq=2 time=107.237 ms [QR RD RA AD] NOERROR
+169 bytes from 9.9.9.9: seq=3 time=96.747 ms [QR RD RA AD] NOERROR
+169 bytes from 9.9.9.9: seq=4 time=107.782 ms [QR RD RA AD] NOERROR
+169 bytes from 9.9.9.9: seq=5 time=94.713 ms [QR RD RA AD] NOERROR
--- 9.9.9.9 dnsping statistics ---
5 requests transmitted, 5 responses received, 0% lost
-min=105.455 ms, avg=140.854 ms, max=191.233 ms, stddev=43.782 ms
+min=94.713 ms, avg=137.257 ms, max=279.805 ms, stddev=79.908 ms
```
It also displays statistics such as minimum, maximum and average response time as well as
@@ -98,8 +101,11 @@ routed to any unwanted path.
In addition to UDP, it also supports TCP as transport protocol, using `--tcp` flag.
+```shell
+./dnstraceroute.py --expert --asn -C -t A -s 8.8.4.4 facebook.com
+```
+
```
-% ./dnstraceroute.py --expert --asn -C -t A -s 8.8.4.4 facebook.com
dnstraceroute.py DNS: 8.8.4.4:53, hostname: facebook.com, rdatatype: A
1 192.168.0.1 (192.168.0.1) 1 ms
2 192.168.28.177 (192.168.28.177) 4 ms
@@ -123,8 +129,11 @@ You can use `dnseval` to compare response times using different transport
protocols such as UDP (default), TCP, DoT and DoH using `--tcp`, `--tls` and
`--doh` respectively.
+```shell
+./dnseval.py --dnssec -t AAAA -f public-servers.txt -c10 ripe.net
+```
+
```
-% ./dnseval.py --dnssec -t AAAA -f public-servers.txt -c10 ripe.net
server avg(ms) min(ms) max(ms) stddev(ms) lost(%) ttl flags response
----------------------------------------------------------------------------------------------------------------------------
1.0.0.1 36.906 7.612 152.866 50.672 %0 300 QR -- -- RD RA AD -- NOERROR
diff --git a/debian/changelog b/debian/changelog
index 7d7abad..e0a4f49 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+dnsdiag (2.1.0-1) UNRELEASED; urgency=medium
+
+ * Team upload.
+ * New upstream release.
+
+ -- Colin Watson <cjwatson@debian.org> Tue, 30 Apr 2024 18:40:37 +0100
+
dnsdiag (2.0.2-2) unstable; urgency=medium
[ Ondřej Nový ]
diff --git a/debian/copyright b/debian/copyright
index 58ad428..4ecdb29 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -3,7 +3,7 @@ Upstream-Name: dnsdiag
Source: https://dnsdiag.org/
Files: *
-Copyright: 2016 Babak Farrokhi <babak@farrokhi.net>
+Copyright: 2016-2023 Babak Farrokhi <babak@farrokhi.net>
License: BSD-2-clause
Files: debian/*
diff --git a/dnseval.py b/dnseval.py
index aeea86d..7f8ee6b 100755
--- a/dnseval.py
+++ b/dnseval.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (c) 2016-2021, Babak Farrokhi
+# Copyright (c) 2016-2023, Babak Farrokhi
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -41,25 +41,10 @@ import util.dns
__author__ = 'Babak Farrokhi (babak@farrokhi.net)'
__license__ = 'BSD'
-__version__ = '2.0.2'
__progname__ = os.path.basename(sys.argv[0])
from util.dns import PROTO_UDP, PROTO_TCP, PROTO_TLS, PROTO_HTTPS, setup_signal_handler, flags_to_text
-
-
-class Colors(object):
- N = '\033[m' # native
- R = '\033[31m' # red
- G = '\033[32m' # green
- O = '\033[33m' # orange
- B = '\033[34m' # blue
-
- def __init__(self, mode):
- if not mode:
- self.N = ''
- self.R = ''
- self.G = ''
- self.B = ''
+from util.shared import __version__, Colors
def usage():
@@ -117,7 +102,7 @@ def main():
try:
opts, args = getopt.getopt(sys.argv[1:], "hf:c:t:w:S:TevCmXHDj:",
["help", "file=", "count=", "type=", "wait=", "json=", "tcp", "edns", "verbose",
- "color", "force-miss", "srcip=", "tls", "doh", "dnssec"])
+ "color", "cache-miss", "srcip=", "tls", "doh", "dnssec"])
except getopt.GetoptError as err:
print(err)
usage()
diff --git a/dnsping.py b/dnsping.py
index 8a26cb2..cabe8f1 100755
--- a/dnsping.py
+++ b/dnsping.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (c) 2016-2021, Babak Farrokhi
+# Copyright (c) 2016-2023, Babak Farrokhi
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -29,7 +29,6 @@ import datetime
import getopt
import ipaddress
import os
-import requests
import signal
import socket
import sys
@@ -38,12 +37,13 @@ from statistics import stdev
import dns.flags
import dns.resolver
+import requests
-from util.dns import PROTO_UDP, PROTO_TCP, PROTO_TLS, PROTO_HTTPS, proto_to_text, unsupported_feature
+from util.dns import PROTO_UDP, PROTO_TCP, PROTO_TLS, PROTO_HTTPS, proto_to_text, unsupported_feature, random_string
+from util.shared import __version__
__author__ = 'Babak Farrokhi (babak@farrokhi.net)'
__license__ = 'BSD'
-__version__ = '2.0.2'
__progname__ = os.path.basename(sys.argv[0])
shutdown = False
@@ -52,29 +52,39 @@ def usage():
print("""%s version %s
usage: %s [-46DeFhqTvX] [-i interval] [-s server] [-p port] [-P port] [-S address] [-c count] [-t type] [-w wait] hostname
- -h --help Show this help
- -q --quiet Quiet
- -v --verbose Print actual dns response
- -s --server DNS server to use (default: first entry from /etc/resolv.conf)
- -p --port DNS server port number (default: 53 for TCP/UDP and 853 for TLS)
- -T --tcp Use TCP as transport protocol
- -X --tls Use TLS as transport protocol
- -H --doh Use HTTPS as transport protols (DoH)
- -4 --ipv4 Use IPv4 as default network protocol
- -6 --ipv6 Use IPv6 as default network protocol
- -P --srcport Query source port number (default: 0)
- -S --srcip Query source IP address (default: default interface address)
- -c --count Number of requests to send (default: 10, 0 for infinity)
- -w --wait Maximum wait time for a reply (default: 2 seconds)
- -i --interval Time between each request (default: 1 seconds)
- -t --type DNS request record type (default: A)
- -e --edns Disable EDNS0 (default: Enabled)
- -D --dnssec Enable 'DNSSEC desired' flag in requests. Implies EDNS.
- -F --flags Display response flags
+ -h --help Show this help
+ -q --quiet Quiet
+ -v --verbose Print actual dns response
+ -s --server DNS server to use (default: first entry from /etc/resolv.conf)
+ -p --port DNS server port number (default: 53 for TCP/UDP and 853 for TLS)
+ -T --tcp Use TCP as transport protocol
+ -X --tls Use TLS as transport protocol
+ -H --doh Use HTTPS as transport protols (DoH)
+ -4 --ipv4 Use IPv4 as default network protocol
+ -6 --ipv6 Use IPv6 as default network protocol
+ -P --srcport Query source port number (default: 0)
+ -S --srcip Query source IP address (default: default interface address)
+ -c --count Number of requests to send (default: 10, 0 for infinity)
+ -r --norecurse Enforce non-recursive query by clearing the RD (recursion desired) bit in the query
+ -m --cache-miss Force cache miss measurement by prepending a random hostname
+ -w --wait Maximum wait time for a reply (default: 2 seconds)
+ -i --interval Time between each request (default: 1 seconds)
+ -t --type DNS request record type (default: A)
+ -e --edns Disable EDNS0 (default: Enabled)
+ -D --dnssec Enable 'DNSSEC desired' flag in requests. Implies EDNS.
+ -F --flags Display response flags
""" % (__progname__, __version__, __progname__))
sys.exit(0)
+def setup_signal_handler():
+ try:
+ signal.signal(signal.SIGTSTP, signal.SIG_IGN) # ignore CTRL+Z
+ signal.signal(signal.SIGINT, signal_handler) # custom CTRL+C handler
+ except AttributeError: # not all signals are supported on all platforms
+ pass
+
+
def signal_handler(sig, frame):
global shutdown
if shutdown: # pressed twice, so exit immediately
@@ -82,12 +92,22 @@ def signal_handler(sig, frame):
shutdown = True # pressed once, exit gracefully
-def main():
+def validate_server_address(dnsserver, address_family):
+ """checks if we have a valid dns server address and resolve if it is a hostname"""
+
try:
- signal.signal(signal.SIGTSTP, signal.SIG_IGN) # ignore CTRL+Z
- signal.signal(signal.SIGINT, signal_handler) # custom CTRL+C handler
- except AttributeError: # OS Does not support some signals, probably windows
- pass
+ ipaddress.ip_address(dnsserver)
+ except ValueError: # so it is not a valid IPv4 or IPv6 address, so try to resolve host name
+ try:
+ dnsserver = socket.getaddrinfo(dnsserver, port=None, family=address_family)[1][4][0]
+ except OSError:
+ print('Error: cannot resolve hostname:', dnsserver, file=sys.stderr, flush=True)
+ sys.exit(1)
+ return dnsserver
+
+
+def main():
+ setup_signal_handler()
if len(sys.argv) == 1:
usage()
@@ -107,14 +127,16 @@ def main():
proto = PROTO_UDP
use_edns = True
want_dnssec = False
+ force_miss = False
+ request_flags = dns.flags.from_text('RD')
af = socket.AF_INET
qname = 'wikipedia.org'
try:
- opts, args = getopt.getopt(sys.argv[1:], "qhc:s:t:w:i:vp:P:S:T46eDFXH",
+ opts, args = getopt.getopt(sys.argv[1:], "qhc:s:t:w:i:vp:P:S:T46meDFXHr",
["help", "count=", "server=", "quiet", "type=", "wait=", "interval=", "verbose",
- "port=", "srcip=", "tcp", "ipv4", "ipv6", "srcport=", "edns", "dnssec", "flags",
- "tls", "doh"])
+ "port=", "srcip=", "tcp", "ipv4", "ipv6", "cache-miss", "srcport=", "edns",
+ "dnssec", "flags", "norecurse", "tls", "doh"])
except getopt.GetoptError as err:
# print help information and exit:
print(err, file=sys.stderr) # will print something like "option -a not recognized"
@@ -139,8 +161,10 @@ def main():
verbose = False
elif o in ("-w", "--wait"):
timeout = int(a)
+ elif o in ("-m", "--cache-miss"):
+ force_miss = True
elif o in ("-i", "--interval"):
- interval = int(a)
+ interval = float(a)
elif o in ("-t", "--type"):
rdatatype = a
elif o in ("-T", "--tcp"):
@@ -157,6 +181,8 @@ def main():
af = socket.AF_INET6
elif o in ("-e", "--edns"):
use_edns = False
+ elif o in ("-r", "--norecurse"):
+ request_flags = dns.flags.from_text('')
elif o in ("-D", "--dnssec"):
want_dnssec = True
elif o in ("-F", "--flags"):
@@ -177,30 +203,14 @@ def main():
if dnsserver is None:
dnsserver = dns.resolver.get_default_resolver().nameservers[0]
- # check if we have a valid dns server address
- try:
- ipaddress.ip_address(dnsserver)
- except ValueError: # so it is not a valid IPv4 or IPv6 address, so try to resolve host name
- try:
- dnsserver = socket.getaddrinfo(dnsserver, port=None, family=af)[1][4][0]
- except OSError:
- print('Error: cannot resolve hostname:', dnsserver, file=sys.stderr, flush=True)
- sys.exit(1)
-
- if use_edns:
- query = dns.message.make_query(qname, rdatatype, dns.rdataclass.IN,
- use_edns=True, want_dnssec=want_dnssec,
- ednsflags=dns.flags.edns_from_text('DO'), payload=8192)
- else:
- query = dns.message.make_query(qname, rdatatype, dns.rdataclass.IN,
- use_edns=False, want_dnssec=want_dnssec)
+ dnsserver = validate_server_address(dnsserver, af)
response_time = []
i = 0
print("%s DNS: %s:%d, hostname: %s, proto: %s, rdatatype: %s, flags: %s" %
(__progname__, dnsserver, dst_port, qname, proto_to_text(proto), rdatatype,
- dns.flags.to_text(query.flags)), flush=True)
+ dns.flags.to_text(request_flags)), flush=True)
while not shutdown:
@@ -209,6 +219,19 @@ def main():
else:
i += 1
+ if force_miss:
+ fqdn = "_dnsdiag_%s_.%s" % (random_string(8, 8), qname)
+ else:
+ fqdn = qname
+
+ if use_edns:
+ query = dns.message.make_query(fqdn, rdatatype, dns.rdataclass.IN, flags=request_flags,
+ use_edns=True, want_dnssec=want_dnssec,
+ ednsflags=dns.flags.EDNSFlag.DO, payload=8192)
+ else:
+ query = dns.message.make_query(fqdn, rdatatype, dns.rdataclass.IN, flags=request_flags,
+ use_edns=False, want_dnssec=want_dnssec)
+
try:
stime = time.perf_counter()
if proto is PROTO_UDP:
@@ -261,7 +284,7 @@ def main():
flags = " [%s] %s" % (dns.flags.to_text(answers.flags), dns.rcode.to_text(answers.rcode()))
else:
flags = ""
- print("%d bytes from %s: seq=%-3d time=%.3f ms%s" % (
+ print("%d bytes from %s: seq=%-3d time=%-7.3f ms%s" % (
len(answers.to_wire()), dnsserver, i, elapsed, flags), flush=True)
if verbose:
print(answers.to_text(), flush=True)
diff --git a/dnstraceroute.py b/dnstraceroute.py
index 2838cc9..2e1c3a8 100755
--- a/dnstraceroute.py
+++ b/dnstraceroute.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (c) 2016-2021, Babak Farrokhi
+# Copyright (c) 2016-2023, Babak Farrokhi
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -39,6 +39,7 @@ import dns.resolver
import util.whois
from util.dns import PROTO_UDP, PROTO_TCP, setup_signal_handler
+from util.shared import __version__, Colors
# Global Variables
quiet = False
@@ -47,7 +48,6 @@ whois_cache = {}
# Constants
__author__ = 'Babak Farrokhi (babak@farrokhi.net)'
__license__ = 'BSD'
-__version__ = '2.0.2'
__progname__ = os.path.basename(sys.argv[0])
@@ -56,26 +56,12 @@ def test_import():
pass
-class Colors(object):
- N = '\033[m' # native
- R = '\033[31m' # red
- G = '\033[32m' # green
- B = '\033[34m' # blue
-
- def __init__(self, mode):
- if not mode:
- self.N = ''
- self.R = ''
- self.G = ''
- self.B = ''
-
-
def usage():
print("""%s version %s
usage: %s [-aeqhCx] [-s server] [-p port] [-c count] [-t type] [-w wait] hostname
-h --help Show this help
- -q --quiet Quiet
+ -q --quiet Quiet mode: No extra information, only traceroute output.
-T --tcp Use TCP as transport protocol
-x --expert Print expert hints if available
-a --asn Turn on AS# lookups for each hop encountered
@@ -291,6 +277,7 @@ def main():
else:
elapsed = abs(etime - stime) * 1000 # convert to milliseconds
+ curr_name = curr_addr
if should_resolve:
try:
if curr_addr:
@@ -301,8 +288,6 @@ def main():
pass
except Exception:
print("unxpected error: ", sys.exc_info()[0])
- else:
- curr_name = curr_addr
global whois_cache
if curr_addr:
diff --git a/requirements.txt b/requirements.txt
index b5a2f2a..78a643e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-dnspython>=1.16.0
+dnspython>=2.3.0
cymruwhois>=1.6
-requests>=2.21.0
-requests-toolbelt>=0.9.1
+requests==2.28.2
+requests-toolbelt==0.10.1
diff --git a/setup.py b/setup.py
index 4c46b4c..ba1c10a 100644
--- a/setup.py
+++ b/setup.py
@@ -1,20 +1,18 @@
from setuptools import setup, find_packages
+from util.shared import __version__
setup(
name="dnsdiag",
- version="2.0.2",
+ version=__version__,
packages=find_packages(),
scripts=["dnseval.py", "dnsping.py", "dnstraceroute.py"],
- install_requires=['dnspython>=1.16.0', 'cymruwhois>=1.6', 'requests>=2.21.0', 'requests-toolbelt>=0.9.1'],
+ install_requires=['dnspython>=2.3.0', 'cymruwhois>=1.6', 'requests>=2.28.2', 'requests-toolbelt>=0.9.1'],
classifiers=[
"Topic :: System :: Networking",
"Environment :: Console",
"Intended Audience :: Developers",
"License :: OSI Approved :: BSD License",
- "Programming Language :: Python :: 3.4",
- "Programming Language :: Python :: 3.5",
- "Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
diff --git a/tox.ini b/tox.ini
index 4583bf3..be2beda 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[pycodestyle]
-ignore = E501
+ignore = E501, E741
[flake8]
-ignore = E501
+ignore = E501, E741
diff --git a/util/dns.py b/util/dns.py
index 0dea46f..f5cef38 100644
--- a/util/dns.py
+++ b/util/dns.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (c) 2016-2021, Babak Farrokhi
+# Copyright (c) 2016-2023, Babak Farrokhi
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
diff --git a/util/shared.py b/util/shared.py
new file mode 100644
index 0000000..ebb66be
--- /dev/null
+++ b/util/shared.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2016-2023, Babak Farrokhi
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+__version__ = '2.1.0'
+
+
+class Colors(object):
+ N = '\033[m' # native
+ R = '\033[31m' # red
+ G = '\033[32m' # green
+ O = '\033[33m' # orange
+ B = '\033[34m' # blue
+
+ def __init__(self, mode):
+ if not mode:
+ self.N = ''
+ self.R = ''
+ self.G = ''
+ self.O = ''
+ self.B = ''
diff --git a/util/whois.py b/util/whois.py
index f037335..fd6ad6c 100644
--- a/util/whois.py
+++ b/util/whois.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (c) 2016-2021, Babak Farrokhi
+# Copyright (c) 2016-2023, Babak Farrokhi
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without