Compare commits
No commits in common. "master" and "gh-pages" have entirely different histories.
67
.gitignore
vendored
67
.gitignore
vendored
@ -1,67 +0,0 @@
|
||||
# virtualenv
|
||||
.venv/
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
.idea/
|
||||
whois.cache
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*,cover
|
||||
.hypothesis/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
#Ipython Notebook
|
||||
.ipynb_checkpoints
|
0
.gitmodules
vendored
0
.gitmodules
vendored
18
.travis.yml
18
.travis.yml
@ -1,18 +0,0 @@
|
||||
language: python
|
||||
sudo: false
|
||||
install: "pip install -r requirements.txt"
|
||||
script: nosetests dnstraceroute.py
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- python: "3.3"
|
||||
- python: "3.4"
|
||||
- python: "3.5"
|
||||
- python: "3.6"
|
||||
- python: "3.7"
|
||||
dist: xenial
|
||||
sudo: true
|
||||
- python: "3.8-dev"
|
||||
dist: xenial
|
||||
sudo: true
|
||||
- python: "pypy3"
|
23
LICENSE
23
LICENSE
@ -1,23 +0,0 @@
|
||||
Copyright (c) 2016, 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.
|
@ -1 +0,0 @@
|
||||
include LICENSE README.md TODO.md public-servers.txt public-v4.txt rootservers.txt
|
130
README.md
130
README.md
@ -1,130 +0,0 @@
|
||||
[](https://travis-ci.org/farrokhi/dnsdiag) [](https://pypi.python.org/pypi/dnsdiag/) []() [](https://app.fossa.io/projects/git%2Bgithub.com%2Ffarrokhi%2Fdnsdiag?ref=badge_shield) []() [](https://github.com/farrokhi/dnsdiag/stargazers)
|
||||
|
||||
DNS Diagnostics and Performance Measurement Tools
|
||||
==================================================
|
||||
|
||||
Ever been wondering if your ISP is [hijacking your DNS traffic](https://decentralize.today/is-your-isp-hijacking-your-dns-traffic-f3eb7ccb0ee7#.fevks5wyc)? Ever observed any
|
||||
misbehavior with your DNS responses? Ever been redirected to wrong address and
|
||||
suspected something is wrong with your DNS? Here we have a [set of tools](http://github.com/farrokhi/dnsdiag) to
|
||||
perform basic audits on your DNS requests and responses to make sure your DNS is
|
||||
working as you expect.
|
||||
|
||||
You can measure the response time of any given DNS server for arbitrary requests
|
||||
using `dnsping`. Just like traditional ping utility, it gives you similar
|
||||
functionality for DNS requests.
|
||||
|
||||
You can also trace the path your DNS request takes to destination to make sure
|
||||
it is not being redirected or hijacked. This can be done by comparing different
|
||||
DNS queries being sent to the same DNS server using `dnstraceroute` and observe
|
||||
if there is any difference between the path.
|
||||
|
||||
`dnseval` evaluates multiple DNS resolvers and helps you choose the best DNS
|
||||
server for your network. While it is highly recommended to use your own DNS
|
||||
resolver and never trust any third-party DNS server, but in case you need to
|
||||
choose the best DNS forwarder for your network, `dnseval` lets you compare
|
||||
different DNS servers from performance (latency) and reliability (loss) point
|
||||
of view.
|
||||
|
||||
# prerequisites
|
||||
This script requires python3 as well as latest
|
||||
[dnspython](http://www.dnspython.org/) and
|
||||
[cymruwhois](https://pythonhosted.org/cymruwhois/).
|
||||
|
||||
# installation
|
||||
|
||||
There are several ways that you can use this toolset. However using the sourcecode is always recommended.
|
||||
|
||||
## From Source Code
|
||||
|
||||
1. You can checkout this git repo and its submodules
|
||||
|
||||
```
|
||||
git clone https://github.com/farrokhi/dnsdiag.git
|
||||
cd dnsdiag
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
|
||||
2. You can alternatively install the package using pip:
|
||||
|
||||
```
|
||||
pip3 install dnsdiag
|
||||
```
|
||||
|
||||
## From Binary
|
||||
|
||||
From time to time, binary version will be released for Windows, Mac OS X and Linux platforms. You can grab the latest release from [releases page](https://github.com/farrokhi/dnsdiag/releases).
|
||||
|
||||
# dnsping
|
||||
dnsping pings a DNS resolver by sending an arbitrary DNS query for given number
|
||||
of times:
|
||||
```
|
||||
% ./dnsping.py -c 3 -t AAAA -s 8.8.8.8 dnsdiag.org
|
||||
dnsping.py DNS: 8.8.8.8:53, hostname: dnsdiag.org, rdatatype: AAAA
|
||||
4 bytes from 8.8.8.8: seq=0 time=123.509 ms
|
||||
4 bytes from 8.8.8.8: seq=1 time=115.726 ms
|
||||
4 bytes from 8.8.8.8: seq=2 time=117.351 ms
|
||||
|
||||
--- 8.8.8.8 dnsping statistics ---
|
||||
3 requests transmitted, 3 responses received, 0% lost
|
||||
min=115.726 ms, avg=118.862 ms, max=123.509 ms, stddev=4.105 ms
|
||||
```
|
||||
This script calculates minimum, maximum and average response time as well as
|
||||
jitter (stddev)
|
||||
|
||||
# dnstraceroute
|
||||
dnstraceroute is a traceroute utility to figure out the path that your DNS
|
||||
request is passing through to get to its destination. You may want to compare
|
||||
it to your actual network traceroute and make sure your DNS traffic is not
|
||||
routed to any unwanted path.
|
||||
|
||||
```
|
||||
% ./dnstraceroute.py --expert -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
|
||||
3 192.168.0.1 (192.168.0.1) 693 ms
|
||||
4 172.19.4.17 (172.19.4.17) 3 ms
|
||||
5 google-public-dns-b.google.com (8.8.4.4) 8 ms
|
||||
|
||||
=== Expert Hints ===
|
||||
[*] public DNS server is next to a private IP address (possible hijacking)
|
||||
```
|
||||
|
||||
Using `--expert` will instruct dnstraceroute to print expert hints (such as warnings of possible DNS traffic hijacking).
|
||||
|
||||
# dnseval
|
||||
dnseval is a bulk ping utility that sends an arbitrary DNS query to a give list
|
||||
of DNS servers. This script is meant for comparing response time of multiple
|
||||
DNS servers at once:
|
||||
```
|
||||
% ./dnseval.py -t AAAA -f public-servers.txt -c10 yahoo.com
|
||||
server avg(ms) min(ms) max(ms) stddev(ms) lost(%) ttl flags
|
||||
------------------------------------------------------------------------------------------------------
|
||||
8.8.8.8 270.791 215.599 307.498 40.630 %0 298 QR -- -- RD RA -- --
|
||||
8.8.4.4 222.955 171.753 307.251 60.481 %10 291 QR -- -- RD RA -- --
|
||||
ns.ripe.net 174.855 160.949 187.458 10.099 %0 289 QR -- -- RD RA -- --
|
||||
4.2.2.1 172.798 163.892 189.918 7.823 %0 287 QR -- -- RD RA -- --
|
||||
4.2.2.2 178.594 169.158 184.696 5.067 %0 285 QR -- -- RD RA -- --
|
||||
4.2.2.3 153.574 138.509 173.439 12.015 %0 284 QR -- -- RD RA -- --
|
||||
4.2.2.4 153.182 141.023 162.323 6.700 %0 282 QR -- -- RD RA -- --
|
||||
4.2.2.5 154.840 141.557 163.889 7.195 %0 281 QR -- -- RD RA -- --
|
||||
209.244.0.3 156.270 147.320 161.365 3.958 %0 279 QR -- -- RD RA -- --
|
||||
209.244.0.4 159.329 151.283 163.726 3.958 %0 278 QR -- -- RD RA -- --
|
||||
195.46.39.39 171.098 163.612 181.147 5.067 %0 276 QR -- -- RD RA -- --
|
||||
195.46.39.40 175.335 160.920 185.618 8.726 %0 274 QR -- -- RD RA -- --
|
||||
```
|
||||
|
||||
### Author
|
||||
|
||||
Babak Farrokhi
|
||||
|
||||
- twitter: [@farrokhi](https://twitter.com/farrokhi)
|
||||
- github: [github.com/farrokhi](https://github.com/farrokhi/)
|
||||
- website: [farrokhi.net](https://farrokhi.net/)
|
||||
|
||||
|
||||
### License
|
||||
|
||||
dnsdiag is released under a 2 clause BSD license.
|
||||
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Ffarrokhi%2Fdnsdiag?ref=badge_large)
|
371
dnseval.py
371
dnseval.py
@ -1,371 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2016, 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.
|
||||
|
||||
|
||||
import os
|
||||
|
||||
import getopt
|
||||
import ipaddress
|
||||
import signal
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
import random
|
||||
import string
|
||||
from statistics import stdev
|
||||
|
||||
import dns.rdatatype
|
||||
import dns.resolver
|
||||
|
||||
__author__ = 'Babak Farrokhi (babak@farrokhi.net)'
|
||||
__license__ = 'BSD'
|
||||
__version__ = "1.6.4"
|
||||
__progname__ = os.path.basename(sys.argv[0])
|
||||
shutdown = False
|
||||
|
||||
resolvers = dns.resolver.get_default_resolver().nameservers
|
||||
|
||||
|
||||
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 = ''
|
||||
|
||||
|
||||
def usage():
|
||||
print("""%s version %s
|
||||
|
||||
usage: %s [-h] [-f server-list] [-c count] [-t type] [-w wait] hostname
|
||||
-h --help Show this help
|
||||
-f --file DNS server list to use (default: system resolvers)
|
||||
-c --count Number of requests to send (default: 10)
|
||||
-m --cache-miss Force cache miss measurement by prepending a random hostname
|
||||
-w --wait Maximum wait time for a reply (default: 2)
|
||||
-t --type DNS request record type (default: A)
|
||||
-T --tcp Use TCP instead of UDP
|
||||
-e --edns Disable EDNS0 (Default: Enabled)
|
||||
-C --color Print colorful output
|
||||
-v --verbose Print actual dns response
|
||||
""" % (__progname__, __version__, __progname__))
|
||||
sys.exit()
|
||||
|
||||
|
||||
def signal_handler(sig, frame):
|
||||
global shutdown
|
||||
if shutdown: # pressed twice, so exit immediately
|
||||
sys.exit(0)
|
||||
shutdown = True # pressed once, exit gracefully
|
||||
|
||||
|
||||
def maxlen(names):
|
||||
sn = sorted(names, key=len)
|
||||
return len(sn[-1])
|
||||
|
||||
|
||||
def _order_flags(table):
|
||||
return sorted(table.items(), reverse=True)
|
||||
|
||||
|
||||
def flags_to_text(flags):
|
||||
# Standard DNS flags
|
||||
|
||||
QR = 0x8000
|
||||
AA = 0x0400
|
||||
TC = 0x0200
|
||||
RD = 0x0100
|
||||
RA = 0x0080
|
||||
AD = 0x0020
|
||||
CD = 0x0010
|
||||
|
||||
# EDNS flags
|
||||
|
||||
DO = 0x8000
|
||||
|
||||
_by_text = {
|
||||
'QR': QR,
|
||||
'AA': AA,
|
||||
'TC': TC,
|
||||
'RD': RD,
|
||||
'RA': RA,
|
||||
'AD': AD,
|
||||
'CD': CD
|
||||
}
|
||||
|
||||
_by_value = dict([(y, x) for x, y in _by_text.items()])
|
||||
_flags_order = _order_flags(_by_value)
|
||||
|
||||
_by_value = dict([(y, x) for x, y in _by_text.items()])
|
||||
|
||||
order = sorted(_by_value.items(), reverse=True)
|
||||
text_flags = []
|
||||
for k, v in order:
|
||||
if flags & k != 0:
|
||||
text_flags.append(v)
|
||||
else:
|
||||
text_flags.append('--')
|
||||
|
||||
return ' '.join(text_flags)
|
||||
|
||||
|
||||
def random_string(min_length=5, max_length=10):
|
||||
char_set = string.ascii_letters + string.digits
|
||||
length = random.randint(min_length, max_length)
|
||||
return ''.join(map(lambda unused: random.choice(char_set), range(length)))
|
||||
|
||||
|
||||
def dnsping(host, server, dnsrecord, timeout, count, use_tcp=False, use_edns=False, force_miss=False):
|
||||
resolver = dns.resolver.Resolver()
|
||||
resolver.nameservers = [server]
|
||||
resolver.timeout = timeout
|
||||
resolver.lifetime = timeout
|
||||
resolver.retry_servfail = 0
|
||||
flags = 0
|
||||
ttl = None
|
||||
answers = None
|
||||
if use_edns:
|
||||
resolver.use_edns(edns=0, payload=8192, ednsflags=dns.flags.edns_from_text('DO'))
|
||||
|
||||
response_times = []
|
||||
i = 0
|
||||
|
||||
for i in range(count):
|
||||
if shutdown: # user pressed CTRL+C
|
||||
break
|
||||
try:
|
||||
if force_miss:
|
||||
fqdn = "_dnsdiag_%s_.%s" % (random_string(), host)
|
||||
else:
|
||||
fqdn = host
|
||||
|
||||
stime = time.perf_counter()
|
||||
answers = resolver.query(fqdn, dnsrecord, tcp=use_tcp,
|
||||
raise_on_no_answer=False) # todo: response validation in future
|
||||
|
||||
except (dns.resolver.NoNameservers, dns.resolver.NoAnswer):
|
||||
break
|
||||
except dns.resolver.Timeout:
|
||||
pass
|
||||
except dns.resolver.NXDOMAIN:
|
||||
etime = time.perf_counter()
|
||||
if force_miss:
|
||||
elapsed = (etime - stime) * 1000 # convert to milliseconds
|
||||
response_times.append(elapsed)
|
||||
else:
|
||||
elapsed = answers.response.time * 1000 # convert to milliseconds
|
||||
response_times.append(elapsed)
|
||||
|
||||
r_sent = i + 1
|
||||
r_received = len(response_times)
|
||||
r_lost = r_sent - r_received
|
||||
r_lost_percent = (100 * r_lost) / r_sent
|
||||
if response_times:
|
||||
r_min = min(response_times)
|
||||
r_max = max(response_times)
|
||||
r_avg = sum(response_times) / r_received
|
||||
if len(response_times) > 1:
|
||||
r_stddev = stdev(response_times)
|
||||
else:
|
||||
r_stddev = 0
|
||||
else:
|
||||
r_min = 0
|
||||
r_max = 0
|
||||
r_avg = 0
|
||||
r_stddev = 0
|
||||
|
||||
if answers is not None:
|
||||
flags = answers.response.flags
|
||||
if len(answers.response.answer) > 0:
|
||||
ttl = answers.response.answer[0].ttl
|
||||
|
||||
return server, r_avg, r_min, r_max, r_stddev, r_lost_percent, flags, ttl, answers
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
signal.signal(signal.SIGTSTP, signal.SIG_IGN) # ignore CTRL+Z
|
||||
signal.signal(signal.SIGINT, signal_handler) # catch CTRL+C
|
||||
except AttributeError: # Some systems (e.g. Windows) may not support all signals
|
||||
pass
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
usage()
|
||||
|
||||
# defaults
|
||||
dnsrecord = 'A'
|
||||
count = 10
|
||||
waittime = 2
|
||||
inputfilename = None
|
||||
fromfile = False
|
||||
use_tcp = False
|
||||
use_edns = True
|
||||
force_miss = False
|
||||
verbose = False
|
||||
color_mode = False
|
||||
hostname = 'wikipedia.org'
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], "hf:c:t:w:TevCm",
|
||||
["help", "file=", "count=", "type=", "wait=", "tcp", "edns", "verbose", "color",
|
||||
"force-miss"])
|
||||
except getopt.GetoptError as err:
|
||||
print(err)
|
||||
usage()
|
||||
|
||||
if args and len(args) == 1:
|
||||
hostname = args[0]
|
||||
else:
|
||||
usage()
|
||||
|
||||
for o, a in opts:
|
||||
if o in ("-h", "--help"):
|
||||
usage()
|
||||
elif o in ("-c", "--count"):
|
||||
count = int(a)
|
||||
elif o in ("-f", "--file"):
|
||||
inputfilename = a
|
||||
fromfile = True
|
||||
elif o in ("-w", "--wait"):
|
||||
waittime = int(a)
|
||||
elif o in ("-m", "--cache-miss"):
|
||||
force_miss = True
|
||||
elif o in ("-t", "--type"):
|
||||
dnsrecord = a
|
||||
elif o in ("-T", "--tcp"):
|
||||
use_tcp = True
|
||||
elif o in ("-e", "--edns"):
|
||||
use_edns = False
|
||||
elif o in ("-C", "--color"):
|
||||
color_mode = True
|
||||
elif o in ("-v", "--verbose"):
|
||||
verbose = True
|
||||
else:
|
||||
print("Invalid option: %s" % o)
|
||||
usage()
|
||||
|
||||
color = Colors(color_mode)
|
||||
|
||||
try:
|
||||
if fromfile:
|
||||
if inputfilename == '-':
|
||||
# read from stdin
|
||||
with sys.stdin as flist:
|
||||
f = flist.read().splitlines()
|
||||
else:
|
||||
try:
|
||||
with open(inputfilename, 'rt') as flist:
|
||||
f = flist.read().splitlines()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
sys.exit(1)
|
||||
else:
|
||||
f = resolvers
|
||||
if len(f) == 0:
|
||||
print("No nameserver specified")
|
||||
|
||||
f = [name.strip() for name in f] # remove annoying blanks
|
||||
f = [x for x in f if not x.startswith('#') and len(x)] # remove comments and empty entries
|
||||
|
||||
width = maxlen(f)
|
||||
blanks = (width - 5) * ' '
|
||||
print('server ', blanks, ' avg(ms) min(ms) max(ms) stddev(ms) lost(%) ttl flags')
|
||||
print((93 + width) * '-')
|
||||
for server in f:
|
||||
# check if we have a valid dns server address
|
||||
if server.lstrip() == '': # deal with empty lines
|
||||
continue
|
||||
server = server.replace(' ', '')
|
||||
try:
|
||||
ipaddress.ip_address(server)
|
||||
except ValueError: # so it is not a valid IPv4 or IPv6 address, so try to resolve host name
|
||||
try:
|
||||
resolver = socket.getaddrinfo(server, port=None)[1][4][0]
|
||||
except OSError:
|
||||
print('Error: cannot resolve hostname:', server)
|
||||
resolver = None
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
resolver = server
|
||||
|
||||
if not resolver:
|
||||
continue
|
||||
|
||||
try:
|
||||
(resolver, r_avg, r_min, r_max, r_stddev, r_lost_percent, flags, ttl, answers) = dnsping(
|
||||
hostname,
|
||||
resolver,
|
||||
dnsrecord,
|
||||
waittime,
|
||||
count,
|
||||
use_tcp=use_tcp,
|
||||
use_edns=use_edns,
|
||||
force_miss=force_miss
|
||||
)
|
||||
except dns.resolver.NXDOMAIN:
|
||||
print('%-15s NXDOMAIN' % server)
|
||||
continue
|
||||
except Exception as e:
|
||||
print('%s: %s' % (server, e))
|
||||
continue
|
||||
|
||||
resolver = server.ljust(width + 1)
|
||||
text_flags = flags_to_text(flags)
|
||||
|
||||
s_ttl = str(ttl)
|
||||
if s_ttl == "None":
|
||||
s_ttl = "N/A"
|
||||
|
||||
if r_lost_percent > 0:
|
||||
l_color = color.O
|
||||
else:
|
||||
l_color = color.N
|
||||
print("%s %-8.3f %-8.3f %-8.3f %-8.3f %s%%%-3d%s %-8s %21s" % (
|
||||
resolver, r_avg, r_min, r_max, r_stddev, l_color, r_lost_percent, color.N, s_ttl, text_flags),
|
||||
flush=True)
|
||||
if verbose and hasattr(answers, 'response'):
|
||||
ans_index = 1
|
||||
for answer in answers.response.answer:
|
||||
print("Answer %d [ %s%s%s ]" % (ans_index, color.G, answer, color.N))
|
||||
ans_index += 1
|
||||
print("")
|
||||
|
||||
except Exception as e:
|
||||
print('%s: %s' % (server, e))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
252
dnsping.py
252
dnsping.py
@ -1,252 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2016, 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.
|
||||
|
||||
|
||||
import getopt
|
||||
import ipaddress
|
||||
import os
|
||||
import signal
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
from statistics import stdev
|
||||
|
||||
import dns.flags
|
||||
import dns.rdatatype
|
||||
import dns.resolver
|
||||
|
||||
__author__ = 'Babak Farrokhi (babak@farrokhi.net)'
|
||||
__license__ = 'BSD'
|
||||
__version__ = "1.6.4"
|
||||
__progname__ = os.path.basename(sys.argv[0])
|
||||
shutdown = False
|
||||
|
||||
|
||||
def usage():
|
||||
print("""%s version %s
|
||||
usage: %s [-ehqv] [-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)
|
||||
-T --tcp Use TCP instead of UDP
|
||||
-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)
|
||||
""" % (__progname__, __version__, __progname__))
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def signal_handler(sig, frame):
|
||||
global shutdown
|
||||
if shutdown: # pressed twice, so exit immediately
|
||||
sys.exit(0)
|
||||
shutdown = True # pressed once, exit gracefully
|
||||
|
||||
|
||||
def main():
|
||||
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
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
usage()
|
||||
|
||||
# defaults
|
||||
dnsrecord = 'A'
|
||||
count = 10
|
||||
timeout = 2
|
||||
interval = 1
|
||||
quiet = False
|
||||
verbose = False
|
||||
dnsserver = dns.resolver.get_default_resolver().nameservers[0]
|
||||
dst_port = 53
|
||||
src_port = 0
|
||||
src_ip = None
|
||||
use_tcp = False
|
||||
use_edns = True
|
||||
af = socket.AF_INET
|
||||
hostname = 'wikipedia.org'
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], "qhc:s:t:w:i:vp:P:S:T46e",
|
||||
["help", "count=", "server=", "quiet", "type=", "wait=", "interval=", "verbose",
|
||||
"port=", "srcip=", "tcp", "ipv4", "ipv6", "srcport=", "edns"])
|
||||
except getopt.GetoptError as err:
|
||||
# print help information and exit:
|
||||
print(err, file=sys.stderr) # will print something like "option -a not recognized"
|
||||
usage()
|
||||
|
||||
if args and len(args) == 1:
|
||||
hostname = args[0]
|
||||
else:
|
||||
usage()
|
||||
|
||||
for o, a in opts:
|
||||
if o in ("-h", "--help"):
|
||||
usage()
|
||||
elif o in ("-c", "--count"):
|
||||
count = abs(int(a))
|
||||
elif o in ("-v", "--verbose"):
|
||||
verbose = True
|
||||
elif o in ("-s", "--server"):
|
||||
dnsserver = a
|
||||
elif o in ("-p", "--port"):
|
||||
dst_port = int(a)
|
||||
elif o in ("-q", "--quiet"):
|
||||
quiet = True
|
||||
verbose = False
|
||||
elif o in ("-w", "--wait"):
|
||||
timeout = int(a)
|
||||
elif o in ("-i", "--interval"):
|
||||
interval = int(a)
|
||||
elif o in ("-t", "--type"):
|
||||
dnsrecord = a
|
||||
elif o in ("-T", "--tcp"):
|
||||
use_tcp = True
|
||||
elif o in ("-4", "--ipv4"):
|
||||
af = socket.AF_INET
|
||||
elif o in ("-6", "--ipv6"):
|
||||
af = socket.AF_INET6
|
||||
elif o in ("-e", "--edns"):
|
||||
use_edns = False
|
||||
elif o in ("-P", "--srcport"):
|
||||
src_port = int(a)
|
||||
if src_port < 1024:
|
||||
print("WARNING: Source ports below 1024 are only available to superuser", flush=True)
|
||||
elif o in ("-S", "--srcip"):
|
||||
src_ip = a
|
||||
else:
|
||||
usage()
|
||||
|
||||
# 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)
|
||||
|
||||
resolver = dns.resolver.Resolver()
|
||||
resolver.nameservers = [dnsserver]
|
||||
resolver.timeout = timeout
|
||||
resolver.lifetime = timeout
|
||||
resolver.port = dst_port
|
||||
resolver.retry_servfail = 0
|
||||
|
||||
if use_edns:
|
||||
resolver.use_edns(edns=0, payload=8192, ednsflags=dns.flags.edns_from_text('DO'))
|
||||
|
||||
response_time = []
|
||||
i = 0
|
||||
|
||||
print("%s DNS: %s:%d, hostname: %s, rdatatype: %s" % (__progname__, dnsserver, dst_port, hostname, dnsrecord),
|
||||
flush=True)
|
||||
|
||||
while not shutdown:
|
||||
|
||||
if 0 < count <= i:
|
||||
break
|
||||
else:
|
||||
i += 1
|
||||
|
||||
try:
|
||||
stime = time.perf_counter()
|
||||
answers = resolver.query(hostname, dnsrecord, source_port=src_port, source=src_ip, tcp=use_tcp,
|
||||
raise_on_no_answer=False)
|
||||
etime = time.perf_counter()
|
||||
except dns.resolver.NoNameservers as e:
|
||||
if not quiet:
|
||||
print("No response to dns request", file=sys.stderr, flush=True)
|
||||
if verbose:
|
||||
print("error:", e, file=sys.stderr, flush=True)
|
||||
sys.exit(1)
|
||||
except dns.resolver.NXDOMAIN as e:
|
||||
if not quiet:
|
||||
print("Hostname does not exist", file=sys.stderr, flush=True)
|
||||
if verbose:
|
||||
print("Error:", e, file=sys.stderr, flush=True)
|
||||
sys.exit(1)
|
||||
except dns.resolver.Timeout:
|
||||
if not quiet:
|
||||
print("Request timeout", flush=True)
|
||||
pass
|
||||
except dns.resolver.NoAnswer:
|
||||
if not quiet:
|
||||
print("No answer", flush=True)
|
||||
pass
|
||||
else:
|
||||
elapsed = answers.response.time * 1000 # convert to milliseconds
|
||||
response_time.append(elapsed)
|
||||
if not quiet:
|
||||
print(
|
||||
"%d bytes from %s: seq=%-3d time=%.3f ms" % (
|
||||
len(str(answers.rrset)), dnsserver, i, elapsed), flush=True)
|
||||
if verbose:
|
||||
print(answers.rrset, flush=True)
|
||||
print("flags:", dns.flags.to_text(answers.response.flags), flush=True)
|
||||
|
||||
time_to_next = (stime + interval) - etime
|
||||
if time_to_next > 0:
|
||||
time.sleep(time_to_next)
|
||||
|
||||
r_sent = i + 1
|
||||
r_received = len(response_time)
|
||||
r_lost = r_sent - r_received
|
||||
r_lost_percent = (100 * r_lost) / r_sent
|
||||
if response_time:
|
||||
r_min = min(response_time)
|
||||
r_max = max(response_time)
|
||||
r_avg = sum(response_time) / r_received
|
||||
if len(response_time) > 1:
|
||||
r_stddev = stdev(response_time)
|
||||
else:
|
||||
r_stddev = 0
|
||||
else:
|
||||
r_min = 0
|
||||
r_max = 0
|
||||
r_avg = 0
|
||||
r_stddev = 0
|
||||
|
||||
print('\n--- %s dnsping statistics ---' % dnsserver, flush=True)
|
||||
print('%d requests transmitted, %d responses received, %.0f%% lost' % (r_sent, r_received, r_lost_percent),
|
||||
flush=True)
|
||||
print('min=%.3f ms, avg=%.3f ms, max=%.3f ms, stddev=%.3f ms' % (r_min, r_avg, r_max, r_stddev), flush=True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
426
dnstraceroute.py
426
dnstraceroute.py
@ -1,426 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2016, 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.
|
||||
|
||||
|
||||
import concurrent.futures
|
||||
import getopt
|
||||
import ipaddress
|
||||
import os
|
||||
import pickle
|
||||
import signal
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
|
||||
import dns.query
|
||||
import dns.rdatatype
|
||||
import dns.resolver
|
||||
|
||||
import cymruwhois
|
||||
|
||||
# Global Variables
|
||||
__author__ = 'Babak Farrokhi (babak@farrokhi.net)'
|
||||
__license__ = 'BSD'
|
||||
__version__ = "1.6.4"
|
||||
_ttl = None
|
||||
quiet = False
|
||||
whois_cache = {}
|
||||
shutdown = False
|
||||
|
||||
# Constants
|
||||
__progname__ = os.path.basename(sys.argv[0])
|
||||
WHOIS_CACHE = 'whois.cache'
|
||||
|
||||
|
||||
class CustomSocket(socket.socket):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CustomSocket, self).__init__(*args, **kwargs)
|
||||
|
||||
def sendto(self, *args, **kwargs):
|
||||
global _ttl
|
||||
if _ttl:
|
||||
self.setsockopt(socket.SOL_IP, socket.IP_TTL, _ttl)
|
||||
super(CustomSocket, self).sendto(*args, **kwargs)
|
||||
|
||||
|
||||
def test_import():
|
||||
# passing this test means imports were successful
|
||||
pass
|
||||
|
||||
|
||||
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 = ''
|
||||
|
||||
|
||||
def whois_lookup(ip):
|
||||
try:
|
||||
global whois_cache
|
||||
currenttime = time.time()
|
||||
ts = currenttime
|
||||
if ip in whois_cache:
|
||||
asn, ts = whois_cache[ip]
|
||||
else:
|
||||
ts = 0
|
||||
if (currenttime - ts) > 36000:
|
||||
c = cymruwhois.Client()
|
||||
asn = c.lookup(ip)
|
||||
whois_cache[ip] = (asn, currenttime)
|
||||
return asn
|
||||
except Exception as e:
|
||||
return e
|
||||
|
||||
|
||||
def load_whois_cache(cachefile):
|
||||
try:
|
||||
pkl_file = open(cachefile, 'rb')
|
||||
try:
|
||||
whois = pickle.load(pkl_file)
|
||||
pkl_file.close()
|
||||
except Exception:
|
||||
whois = {}
|
||||
except IOError:
|
||||
whois = {}
|
||||
return whois
|
||||
|
||||
|
||||
def save_whois_cache(cachefile, whois_data):
|
||||
pkl_file = open(cachefile, 'wb')
|
||||
pickle.dump(whois_data, pkl_file)
|
||||
pkl_file.close()
|
||||
|
||||
|
||||
def usage():
|
||||
print('%s version %s\n' % (__progname__, __version__))
|
||||
print('usage: %s [-aeqhCx] [-s server] [-p port] [-c count] [-t type] [-w wait] hostname' % __progname__)
|
||||
print(' -h --help Show this help')
|
||||
print(' -q --quiet Quiet')
|
||||
print(' -x --expert Print expert hints if available')
|
||||
print(' -a --asn Turn on AS# lookups for each hop encountered')
|
||||
print(' -s --server DNS server to use (default: first system resolver)')
|
||||
print(' -p --port DNS server port number (default: 53)')
|
||||
print(' -S --srcip Query source IP address (default: default interface address)')
|
||||
print(' -c --count Maximum number of hops (default: 30)')
|
||||
print(' -w --wait Maximum wait time for a reply (default: 2)')
|
||||
print(' -t --type DNS request record type (default: A)')
|
||||
print(' -C --color Print colorful output')
|
||||
print(' -e --edns Disable EDNS0 (Default: Enabled)')
|
||||
print(' ')
|
||||
sys.exit()
|
||||
|
||||
|
||||
def signal_handler(sig, frame):
|
||||
global shutdown
|
||||
if shutdown: # pressed twice, so exit immediately
|
||||
sys.exit(0)
|
||||
shutdown = True # pressed once, exit gracefully
|
||||
|
||||
|
||||
def expert_report(trace_path, color_mode):
|
||||
color = Colors(color_mode)
|
||||
print("\n%s=== Expert Hints ===%s" % (color.B, color.N))
|
||||
if len(trace_path) == 0:
|
||||
print(" [*] empty trace - should not happen")
|
||||
return
|
||||
|
||||
private_network_radius = 4 # number of hops we assume we are still inside our local network
|
||||
prev_hop = None
|
||||
if len(trace_path) > 1:
|
||||
prev_hop = trace_path[-2]
|
||||
|
||||
if len(trace_path) < 2:
|
||||
print(
|
||||
" %s[*]%s path too short (possible DNS hijacking, unless it is a local DNS resolver)" % (color.R, color.N))
|
||||
return
|
||||
|
||||
if prev_hop == '*' and len(trace_path) > private_network_radius:
|
||||
print(" %s[*]%s public DNS server is next to an invisible hop (probably a firewall)" % (color.R, color.N))
|
||||
return
|
||||
|
||||
if prev_hop and len(trace_path) > private_network_radius and ipaddress.ip_address(prev_hop).is_private:
|
||||
print(" %s[*]%s public DNS server is next to a private IP address (possible hijacking)" % (color.R, color.N))
|
||||
return
|
||||
|
||||
if prev_hop and len(trace_path) > private_network_radius and ipaddress.ip_address(prev_hop).is_reserved:
|
||||
print(" %s[*]%s public DNS server is next to a reserved IP address (possible hijacking)" % (color.R, color.N))
|
||||
return
|
||||
|
||||
# no expert info available
|
||||
print(" %s[*]%s No expert hint available for this trace" % (color.G, color.N))
|
||||
|
||||
|
||||
def ping(resolver, hostname, dnsrecord, ttl, src_ip, use_edns=False):
|
||||
global _ttl
|
||||
|
||||
reached = False
|
||||
|
||||
dns.query.socket_factory = CustomSocket
|
||||
_ttl = ttl
|
||||
if use_edns:
|
||||
resolver.use_edns(edns=0, payload=8192, ednsflags=dns.flags.edns_from_text('DO'))
|
||||
|
||||
try:
|
||||
resolver.query(hostname, dnsrecord, source=src_ip, raise_on_no_answer=False)
|
||||
|
||||
except dns.resolver.NoNameservers as e:
|
||||
if not quiet:
|
||||
print("no or bad response:", e)
|
||||
sys.exit(1)
|
||||
except dns.resolver.NXDOMAIN as e:
|
||||
if not quiet:
|
||||
print("Invalid hostname:", e)
|
||||
sys.exit(1)
|
||||
except dns.resolver.Timeout:
|
||||
pass
|
||||
except dns.resolver.NoAnswer:
|
||||
if not quiet:
|
||||
print("invalid answer")
|
||||
pass
|
||||
except SystemExit:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("unxpected error: ", e)
|
||||
sys.exit(1)
|
||||
else:
|
||||
reached = True
|
||||
|
||||
return reached
|
||||
|
||||
|
||||
def main():
|
||||
global quiet
|
||||
|
||||
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
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
usage()
|
||||
|
||||
dnsrecord = 'A'
|
||||
count = 30
|
||||
timeout = 2
|
||||
dnsserver = dns.resolver.get_default_resolver().nameservers[0]
|
||||
dest_port = 53
|
||||
src_ip = None
|
||||
hops = 0
|
||||
as_lookup = False
|
||||
expert_mode = False
|
||||
should_resolve = True
|
||||
use_edns = True
|
||||
color_mode = False
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], "aqhc:s:S:t:w:p:nexC",
|
||||
["help", "count=", "server=", "quiet", "type=", "wait=", "asn", "port", "expert",
|
||||
"color", "srcip="])
|
||||
except getopt.GetoptError as err:
|
||||
# print help information and exit:
|
||||
print(err) # will print something like "option -a not recognized"
|
||||
usage()
|
||||
|
||||
if args and len(args) == 1:
|
||||
hostname = args[0]
|
||||
else:
|
||||
usage()
|
||||
|
||||
for o, a in opts:
|
||||
if o in ("-h", "--help"):
|
||||
usage()
|
||||
elif o in ("-c", "--count"):
|
||||
count = int(a)
|
||||
elif o in ("-x", "--expert"):
|
||||
expert_mode = True
|
||||
elif o in ("-s", "--server"):
|
||||
dnsserver = a
|
||||
elif o in ("-q", "--quiet"):
|
||||
quiet = True
|
||||
elif o in ("-S", "--srcip"):
|
||||
src_ip = a
|
||||
elif o in ("-w", "--wait"):
|
||||
timeout = int(a)
|
||||
elif o in ("-t", "--type"):
|
||||
dnsrecord = a
|
||||
elif o in ("-p", "--port"):
|
||||
dest_port = int(a)
|
||||
elif o in ("-C", "--color"):
|
||||
color_mode = True
|
||||
elif o in ("-n"):
|
||||
should_resolve = False
|
||||
elif o in ("-a", "--asn"):
|
||||
as_lookup = True
|
||||
elif o in ("-e", "--edns"):
|
||||
use_edns = False
|
||||
else:
|
||||
usage()
|
||||
|
||||
color = Colors(color_mode)
|
||||
|
||||
# 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=socket.AF_INET)[1][4][0]
|
||||
except OSError:
|
||||
print('Error: cannot resolve hostname:', dnsserver)
|
||||
sys.exit(1)
|
||||
|
||||
resolver = dns.resolver.Resolver()
|
||||
resolver.nameservers = [dnsserver]
|
||||
resolver.timeout = timeout
|
||||
resolver.port = dest_port
|
||||
resolver.lifetime = timeout
|
||||
resolver.retry_servfail = 0
|
||||
|
||||
icmp = socket.getprotobyname('icmp')
|
||||
|
||||
ttl = 1
|
||||
reached = False
|
||||
trace_path = []
|
||||
|
||||
if not quiet:
|
||||
print("%s DNS: %s:%d, hostname: %s, rdatatype: %s" % (__progname__, dnsserver, dest_port, hostname, dnsrecord),
|
||||
flush=True)
|
||||
|
||||
while True:
|
||||
if shutdown:
|
||||
break
|
||||
|
||||
# some platforms permit opening a DGRAM socket for ICMP without root permission
|
||||
# if not availble, we will fall back to RAW which explicitly requires root permission
|
||||
try:
|
||||
icmp_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp)
|
||||
except OSError:
|
||||
try:
|
||||
icmp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, icmp)
|
||||
except OSError:
|
||||
print("Error: Unable to create ICMP socket with unprivileged user. Please run as root.")
|
||||
sys.exit(1)
|
||||
|
||||
icmp_socket.bind(("", dest_port))
|
||||
icmp_socket.settimeout(timeout)
|
||||
|
||||
curr_addr = None
|
||||
curr_host = None
|
||||
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool: # dispatch dns lookup to another thread
|
||||
stime = time.perf_counter()
|
||||
thr = pool.submit(ping, resolver, hostname, dnsrecord, ttl, src_ip=src_ip, use_edns=use_edns)
|
||||
|
||||
try: # expect ICMP response
|
||||
_, curr_addr = icmp_socket.recvfrom(512)
|
||||
curr_addr = curr_addr[0]
|
||||
except socket.error:
|
||||
etime = time.perf_counter()
|
||||
pass
|
||||
finally:
|
||||
etime = time.perf_counter()
|
||||
icmp_socket.close()
|
||||
|
||||
reached = thr.result()
|
||||
|
||||
if reached:
|
||||
curr_addr = dnsserver
|
||||
stime = time.perf_counter() # need to recalculate elapsed time for last hop asynchronously
|
||||
ping(resolver, hostname, dnsrecord, ttl, src_ip=src_ip, use_edns=use_edns)
|
||||
etime = time.perf_counter()
|
||||
|
||||
elapsed = abs(etime - stime) * 1000 # convert to milliseconds
|
||||
|
||||
if should_resolve:
|
||||
try:
|
||||
if curr_addr:
|
||||
curr_name = socket.gethostbyaddr(curr_addr)[0]
|
||||
except socket.error:
|
||||
curr_name = curr_addr
|
||||
except SystemExit:
|
||||
pass
|
||||
except:
|
||||
print("unxpected error: ", sys.exc_info()[0])
|
||||
else:
|
||||
curr_name = curr_addr
|
||||
|
||||
if curr_addr:
|
||||
as_name = ""
|
||||
if as_lookup:
|
||||
asn = whois_lookup(curr_addr)
|
||||
as_name = ''
|
||||
try:
|
||||
if asn and asn.asn != "NA":
|
||||
as_name = "[AS%s %s] " % (asn.asn, asn.owner)
|
||||
except AttributeError:
|
||||
if shutdown:
|
||||
sys.exit(0)
|
||||
pass
|
||||
|
||||
c = color.N # default
|
||||
if curr_addr != '*':
|
||||
try:
|
||||
IP = ipaddress.ip_address(curr_addr)
|
||||
if IP.is_private:
|
||||
c = color.R
|
||||
if IP.is_reserved:
|
||||
c = color.B
|
||||
if curr_addr == dnsserver:
|
||||
c = color.G
|
||||
except:
|
||||
pass
|
||||
|
||||
print("%d\t%s (%s%s%s) %s%.3f ms" % (ttl, curr_name, c, curr_addr, color.N, as_name, elapsed), flush=True)
|
||||
trace_path.append(curr_addr)
|
||||
else:
|
||||
print("%d\t *" % ttl, flush=True)
|
||||
trace_path.append("*")
|
||||
|
||||
ttl += 1
|
||||
hops += 1
|
||||
if (hops >= count) or (curr_addr == dnsserver) or reached:
|
||||
break
|
||||
|
||||
if expert_mode and not shutdown:
|
||||
expert_report(trace_path, color_mode)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
whois_cache = load_whois_cache(WHOIS_CACHE)
|
||||
main()
|
||||
finally:
|
||||
save_whois_cache(WHOIS_CACHE, whois_cache)
|
BIN
images/arrow-down.png
Normal file
BIN
images/arrow-down.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 216 B |
BIN
images/octocat-small.png
Normal file
BIN
images/octocat-small.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 357 B |
203
index.html
Normal file
203
index.html
Normal file
@ -0,0 +1,203 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
||||
<title>Dnsdiag by farrokhi</title>
|
||||
|
||||
<link rel="stylesheet" href="stylesheets/styles.css">
|
||||
<link rel="stylesheet" href="stylesheets/github-light.css">
|
||||
<script src="javascripts/scale.fix.js"></script>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<header>
|
||||
<h1 class="header">Dnsdiag</h1>
|
||||
<p class="header">DNS Diagnostics and Performance Measurement Tools</p>
|
||||
|
||||
<ul>
|
||||
<li class="download"><a class="buttons" href="https://github.com/farrokhi/dnsdiag/zipball/master">Download ZIP</a></li>
|
||||
<li class="download"><a class="buttons" href="https://github.com/farrokhi/dnsdiag/tarball/master">Download TAR</a></li>
|
||||
<li><a class="buttons github" href="https://github.com/farrokhi/dnsdiag">View On GitHub</a></li>
|
||||
</ul>
|
||||
|
||||
<p class="header">This project is maintained by <a class="header name" href="https://github.com/farrokhi">farrokhi</a></p>
|
||||
|
||||
|
||||
</header>
|
||||
<section>
|
||||
<p><a href="https://travis-ci.org/farrokhi/dnsdiag"><img src="https://travis-ci.org/farrokhi/dnsdiag.svg" alt="Build Status"></a> <a href="https://pypi.python.org/pypi/dnsdiag/"><img src="https://img.shields.io/pypi/v/dnsdiag.svg?maxAge=8600" alt="PyPI"></a> <a href=""><img src="https://img.shields.io/pypi/l/dnsdiag.svg?maxAge=8600" alt="PyPI"></a> <a href=""><img src="https://img.shields.io/pypi/pyversions/dnsdiag.svg?maxAge=8600" alt="PyPI"></a> <a href="https://github.com/farrokhi/dnsdiag/stargazers"><img src="https://img.shields.io/github/stars/farrokhi/dnsdiag.svg?style=social&label=Star&maxAge=8600" alt="GitHub stars"></a></p>
|
||||
|
||||
<h1>
|
||||
<a id="dns-diagnostics-and-performance-measurement-tools" class="anchor" href="#dns-diagnostics-and-performance-measurement-tools" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>DNS Diagnostics and Performance Measurement Tools</h1>
|
||||
|
||||
<p>Ever been wondering if your ISP is <a href="https://decentralize.today/is-your-isp-hijacking-your-dns-traffic-f3eb7ccb0ee7#.fevks5wyc">hijacking your DNS traffic</a>? Ever observed any
|
||||
misbehavior with your DNS responses? Ever been redirected to wrong address and
|
||||
suspected something is wrong with your DNS? Here we have a <a href="http://github.com/farrokhi/dnsdiag">set of tools</a> to
|
||||
perform basic audits on your DNS requests and responses to make sure your DNS is
|
||||
working as you expect.</p>
|
||||
|
||||
<p>You can measure the response time of any given DNS server for arbitrary requests
|
||||
using <code>dnsping</code>. Just like traditional ping utility, it gives you similar
|
||||
functionality for DNS requests.</p>
|
||||
|
||||
<p>You can also trace the path your DNS request takes to destination to make sure
|
||||
it is not being redirected or hijacked. This can be done by comparing different
|
||||
DNS queries being sent to the same DNS server using <code>dnstraceroute</code> and observe
|
||||
if there is any difference between the path.</p>
|
||||
|
||||
<p><code>dnseval</code> evaluates multiple DNS resolvers and helps you choose the best DNS
|
||||
server for your network. While it is highly recommended to use your own DNS
|
||||
resolver and never trust any third-party DNS server, but in case you need to
|
||||
choose the best DNS forwarder for your network, <code>dnseval</code> lets you compare
|
||||
different DNS servers from performance (latency) and reliability (loss) point
|
||||
of view.</p>
|
||||
|
||||
<h1>
|
||||
<a id="prerequisites" class="anchor" href="#prerequisites" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>prerequisites</h1>
|
||||
|
||||
<p>This script requires python3 as well as latest
|
||||
<a href="http://www.dnspython.org/">dnspython</a> and
|
||||
<a href="https://pythonhosted.org/cymruwhois/">cymruwhois</a>. Please note that
|
||||
"dnstraceroute" requires a modified version of dnspython module. All required
|
||||
third-party modules are included as GIT submodules. You just need to run <code>git
|
||||
submodule update --init</code> and project directory to pull the required code.</p>
|
||||
|
||||
<h1>
|
||||
<a id="installation" class="anchor" href="#installation" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>installation</h1>
|
||||
|
||||
<p>There are several ways that you can use this toolset. However using the sourcecode is always recommended.</p>
|
||||
|
||||
<h2>
|
||||
<a id="from-source-code" class="anchor" href="#from-source-code" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>From Source Code</h2>
|
||||
|
||||
<ol>
|
||||
<li>You can checkout this git repo and its submodules</li>
|
||||
</ol>
|
||||
|
||||
<pre><code>git clone https://github.com/farrokhi/dnsdiag.git
|
||||
cd dnsdiag
|
||||
git submodule update --init
|
||||
</code></pre>
|
||||
|
||||
<ol>
|
||||
<li>You can alternatively install the package using pip:</li>
|
||||
</ol>
|
||||
|
||||
<pre><code>pip3 install --process-dependency-links dnsdiag
|
||||
</code></pre>
|
||||
|
||||
<h2>
|
||||
<a id="from-binary" class="anchor" href="#from-binary" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>From Binary</h2>
|
||||
|
||||
<p>From time to time, binary version will be released for Windows, Mac OS X and Linux platforms. You can grab the latest release from <a href="https://github.com/farrokhi/dnsdiag/releases">releases page</a>.</p>
|
||||
|
||||
<h1>
|
||||
<a id="dnsping" class="anchor" href="#dnsping" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>dnsping</h1>
|
||||
|
||||
<p>dnsping pings a DNS resolver by sending an arbitrary DNS query for given number
|
||||
of times:</p>
|
||||
|
||||
<pre><code>% ./dnsping.py -c 3 -t AAAA -s 8.8.8.8 dnsdiag.org
|
||||
dnsping.py DNS: 8.8.8.8:53, hostname: dnsdiag.org, rdatatype: AAAA
|
||||
4 bytes from 8.8.8.8: seq=0 time=123.509 ms
|
||||
4 bytes from 8.8.8.8: seq=1 time=115.726 ms
|
||||
4 bytes from 8.8.8.8: seq=2 time=117.351 ms
|
||||
|
||||
--- 8.8.8.8 dnsping statistics ---
|
||||
3 requests transmitted, 3 responses received, 0% lost
|
||||
min=115.726 ms, avg=118.862 ms, max=123.509 ms, stddev=4.105 ms
|
||||
</code></pre>
|
||||
|
||||
<p>This script calculates minimum, maximum and average response time as well as
|
||||
jitter (stddev)</p>
|
||||
|
||||
<h1>
|
||||
<a id="dnstraceroute" class="anchor" href="#dnstraceroute" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>dnstraceroute</h1>
|
||||
|
||||
<p>dnstraceroute is a traceroute utility to figure out the path that your DNS
|
||||
request is passing through to get to its destination. You may want to compare
|
||||
it to your actual network traceroute and make sure your DNS traffic is not
|
||||
routed to any unwanted path.</p>
|
||||
|
||||
<pre><code>% ./dnstraceroute.py --expert -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
|
||||
3 192.168.0.1 (192.168.0.1) 693 ms
|
||||
4 172.19.4.17 (172.19.4.17) 3 ms
|
||||
5 google-public-dns-b.google.com (8.8.4.4) 8 ms
|
||||
|
||||
=== Expert Hints ===
|
||||
[*] public DNS server is next to a private IP address (possible hijacking)
|
||||
</code></pre>
|
||||
|
||||
<p>Using <code>--expert</code> will instruct dnstraceroute to print expert hints (such as warnings of possible DNS traffic hijacking).</p>
|
||||
|
||||
<h1>
|
||||
<a id="dnseval" class="anchor" href="#dnseval" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>dnseval</h1>
|
||||
|
||||
<p>dnseval is a bulk ping utility that sends an arbitrary DNS query to a give list
|
||||
of DNS servers. This script is meant for comparing response time of multiple
|
||||
DNS servers at once:</p>
|
||||
|
||||
<pre><code>% ./dnseval.py -t AAAA -f public-v4.txt -c10 fg.weberdns.de
|
||||
server avg(ms) min(ms) max(ms) stddev(ms) lost(%) flags
|
||||
------------------------------------------------------------------------------------------------
|
||||
8.8.8.8 94.556 90.488 112.209 6.322 %0 QR -- -- RD RA AD --
|
||||
8.8.4.4 92.599 90.265 94.338 1.086 %0 QR -- -- RD RA AD --
|
||||
ns.ripe.net 92.754 91.632 93.980 0.900 %0 QR -- -- RD RA AD --
|
||||
4.2.2.1 92.703 91.869 93.298 0.482 %0 QR -- -- RD RA AD --
|
||||
4.2.2.2 93.195 91.667 94.919 1.065 %0 QR -- -- RD RA AD --
|
||||
4.2.2.3 93.118 92.076 94.835 0.835 %0 QR -- -- RD RA AD --
|
||||
4.2.2.4 94.308 92.175 103.318 3.261 %0 QR -- -- RD RA AD --
|
||||
4.2.2.5 92.650 91.643 94.460 1.002 %0 QR -- -- RD RA AD --
|
||||
209.244.0.3 92.810 89.961 94.807 1.266 %0 QR -- -- RD RA AD --
|
||||
209.244.0.4 93.127 91.962 95.970 1.227 %0 QR -- -- RD RA AD --
|
||||
195.46.39.39 92.770 90.777 93.656 0.914 %0 QR -- -- RD RA AD --
|
||||
195.46.39.40 92.903 91.280 94.914 1.147 %0 QR -- -- RD RA AD --
|
||||
</code></pre>
|
||||
|
||||
<h3>
|
||||
<a id="author" class="anchor" href="#author" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Author</h3>
|
||||
|
||||
<p>Babak Farrokhi </p>
|
||||
|
||||
<ul>
|
||||
<li>twitter: <a href="https://twitter.com/farrokhi">@farrokhi</a>
|
||||
</li>
|
||||
<li>github: <a href="https://github.com/farrokhi/">/farrokhi</a>
|
||||
</li>
|
||||
<li>website: <a href="https://farrokhi.net/">farrokhi.net</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>
|
||||
<a id="license" class="anchor" href="#license" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>License</h3>
|
||||
|
||||
<p>dnsdiag is released under a 2 clause BSD license.</p>
|
||||
|
||||
<h3>
|
||||
<a id="credits" class="anchor" href="#credits" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Credits</h3>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://github.com/rthalley">@rthalley</a> for invaluable <a href="https://github.com/rthalley/dnspython">dnspython</a> library</li>
|
||||
<li>
|
||||
<a href="https://github.com/JustinAzoff">@JustinAzoff</a> for <a href="https://github.com/JustinAzoff/python-cymruwhois">python-cymruwhois</a> library</li>
|
||||
<li>
|
||||
<a href="https://github.com/bortzmeyer">@bortzmeyer</a> for his feedback and patches</li>
|
||||
</ul>
|
||||
</section>
|
||||
<footer>
|
||||
<p><small>Hosted on <a href="https://pages.github.com">GitHub Pages</a> using the Dinky theme</small></p>
|
||||
</footer>
|
||||
</div>
|
||||
<!--[if !IE]><script>fixScale(document);</script><![endif]-->
|
||||
|
||||
</body>
|
||||
</html>
|
20
javascripts/scale.fix.js
Normal file
20
javascripts/scale.fix.js
Normal file
@ -0,0 +1,20 @@
|
||||
fixScale = function(doc) {
|
||||
|
||||
var addEvent = 'addEventListener',
|
||||
type = 'gesturestart',
|
||||
qsa = 'querySelectorAll',
|
||||
scales = [1, 1],
|
||||
meta = qsa in doc ? doc[qsa]('meta[name=viewport]') : [];
|
||||
|
||||
function fix() {
|
||||
meta.content = 'width=device-width,minimum-scale=' + scales[0] + ',maximum-scale=' + scales[1];
|
||||
doc.removeEventListener(type, fix, true);
|
||||
}
|
||||
|
||||
if ((meta = meta[meta.length - 1]) && addEvent in doc) {
|
||||
fix();
|
||||
scales = [.25, 1.6];
|
||||
doc[addEvent](type, fix, true);
|
||||
}
|
||||
|
||||
};
|
6
params.json
Normal file
6
params.json
Normal file
File diff suppressed because one or more lines are too long
@ -1,42 +0,0 @@
|
||||
#Cloudflare
|
||||
1.0.0.1
|
||||
1.1.1.1
|
||||
2606:4700:4700::1001
|
||||
2606:4700:4700::1111
|
||||
|
||||
#SafeDNS
|
||||
195.46.39.39
|
||||
195.46.39.40
|
||||
|
||||
#OpenDNS
|
||||
208.67.220.220
|
||||
208.67.222.222
|
||||
2620:0:ccc::2
|
||||
2620:0:ccd::2
|
||||
|
||||
#DYN DNS
|
||||
216.146.35.35
|
||||
216.146.36.36
|
||||
|
||||
#Level3
|
||||
209.244.0.3
|
||||
209.244.0.4
|
||||
4.2.2.1
|
||||
4.2.2.2
|
||||
4.2.2.3
|
||||
4.2.2.4
|
||||
4.2.2.5
|
||||
|
||||
#freenom world
|
||||
80.80.80.80
|
||||
80.80.81.81
|
||||
#Google
|
||||
8.8.4.4
|
||||
8.8.8.8
|
||||
2001:4860:4860::8844
|
||||
2001:4860:4860::8888
|
||||
|
||||
#PCH's Quad9
|
||||
9.9.9.9
|
||||
2620:fe::fe
|
||||
149.112.112.112
|
@ -1,36 +0,0 @@
|
||||
#Cloudflare
|
||||
1.0.0.1
|
||||
1.1.1.1
|
||||
|
||||
#SafeDNS
|
||||
195.46.39.39
|
||||
195.46.39.40
|
||||
|
||||
#OpenDNS
|
||||
208.67.220.220
|
||||
208.67.222.222
|
||||
|
||||
#DYN DNS
|
||||
216.146.35.35
|
||||
216.146.36.36
|
||||
|
||||
#Level3
|
||||
209.244.0.3
|
||||
209.244.0.4
|
||||
4.2.2.1
|
||||
4.2.2.2
|
||||
4.2.2.3
|
||||
4.2.2.4
|
||||
4.2.2.5
|
||||
|
||||
#freenom world
|
||||
80.80.80.80
|
||||
80.80.81.81
|
||||
|
||||
#Google
|
||||
8.8.4.4
|
||||
8.8.8.8
|
||||
|
||||
#PCH's Quad9
|
||||
9.9.9.9
|
||||
149.112.112.112
|
@ -1,2 +0,0 @@
|
||||
dnspython>=1.15.0
|
||||
cymruwhois>=1.6
|
@ -1,13 +0,0 @@
|
||||
a.root-servers.net
|
||||
b.root-servers.net
|
||||
c.root-servers.net
|
||||
d.root-servers.net
|
||||
e.root-servers.net
|
||||
f.root-servers.net
|
||||
g.root-servers.net
|
||||
h.root-servers.net
|
||||
i.root-servers.net
|
||||
j.root-servers.net
|
||||
k.root-servers.net
|
||||
l.root-servers.net
|
||||
m.root-servers.net
|
45
setup.py
45
setup.py
@ -1,45 +0,0 @@
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name="dnsdiag",
|
||||
version="1.6.4",
|
||||
packages=find_packages(),
|
||||
scripts=["dnseval.py", "dnsping.py", "dnstraceroute.py"],
|
||||
install_requires=['dnspython>=1.15.0', 'cymruwhois>=1.6'],
|
||||
|
||||
classifiers=[
|
||||
"Topic :: System :: Networking",
|
||||
"Environment :: Console",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: BSD License",
|
||||
"Programming Language :: Python :: 3.3",
|
||||
"Programming Language :: Python :: 3.4",
|
||||
"Programming Language :: Python :: 3.5",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: Implementation :: PyPy",
|
||||
"Topic :: Internet :: Name Service (DNS)",
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Operating System :: OS Independent",
|
||||
],
|
||||
|
||||
author="Babak Farrokhi",
|
||||
author_email="babak@farrokhi.net",
|
||||
description="DNS Diagnostics and measurement tools (ping, traceroute)",
|
||||
long_description="""
|
||||
DNSDiag provides a handful of tools to measure and diagnose your DNS
|
||||
performance and integrity. Using dnsping, dnstraceroute and dnseval tools
|
||||
you can measure your DNS response quality from delay and loss perspective
|
||||
as well as tracing the path your DNS query takes to get to DNS server.
|
||||
""",
|
||||
license="BSD",
|
||||
keywords="dns traceroute ping performance",
|
||||
url="https://dnsdiag.org/",
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'dnsping = dnsping:main',
|
||||
'dnstraceroute = dnstraceroute:main',
|
||||
'dnseval = dnseval:main',
|
||||
]
|
||||
}
|
||||
)
|
124
stylesheets/github-light.css
Normal file
124
stylesheets/github-light.css
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 GitHub, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
.pl-c /* comment */ {
|
||||
color: #969896;
|
||||
}
|
||||
|
||||
.pl-c1 /* constant, variable.other.constant, support, meta.property-name, support.constant, support.variable, meta.module-reference, markup.raw, meta.diff.header */,
|
||||
.pl-s .pl-v /* string variable */ {
|
||||
color: #0086b3;
|
||||
}
|
||||
|
||||
.pl-e /* entity */,
|
||||
.pl-en /* entity.name */ {
|
||||
color: #795da3;
|
||||
}
|
||||
|
||||
.pl-smi /* variable.parameter.function, storage.modifier.package, storage.modifier.import, storage.type.java, variable.other */,
|
||||
.pl-s .pl-s1 /* string source */ {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.pl-ent /* entity.name.tag */ {
|
||||
color: #63a35c;
|
||||
}
|
||||
|
||||
.pl-k /* keyword, storage, storage.type */ {
|
||||
color: #a71d5d;
|
||||
}
|
||||
|
||||
.pl-s /* string */,
|
||||
.pl-pds /* punctuation.definition.string, string.regexp.character-class */,
|
||||
.pl-s .pl-pse .pl-s1 /* string punctuation.section.embedded source */,
|
||||
.pl-sr /* string.regexp */,
|
||||
.pl-sr .pl-cce /* string.regexp constant.character.escape */,
|
||||
.pl-sr .pl-sre /* string.regexp source.ruby.embedded */,
|
||||
.pl-sr .pl-sra /* string.regexp string.regexp.arbitrary-repitition */ {
|
||||
color: #183691;
|
||||
}
|
||||
|
||||
.pl-v /* variable */ {
|
||||
color: #ed6a43;
|
||||
}
|
||||
|
||||
.pl-id /* invalid.deprecated */ {
|
||||
color: #b52a1d;
|
||||
}
|
||||
|
||||
.pl-ii /* invalid.illegal */ {
|
||||
color: #f8f8f8;
|
||||
background-color: #b52a1d;
|
||||
}
|
||||
|
||||
.pl-sr .pl-cce /* string.regexp constant.character.escape */ {
|
||||
font-weight: bold;
|
||||
color: #63a35c;
|
||||
}
|
||||
|
||||
.pl-ml /* markup.list */ {
|
||||
color: #693a17;
|
||||
}
|
||||
|
||||
.pl-mh /* markup.heading */,
|
||||
.pl-mh .pl-en /* markup.heading entity.name */,
|
||||
.pl-ms /* meta.separator */ {
|
||||
font-weight: bold;
|
||||
color: #1d3e81;
|
||||
}
|
||||
|
||||
.pl-mq /* markup.quote */ {
|
||||
color: #008080;
|
||||
}
|
||||
|
||||
.pl-mi /* markup.italic */ {
|
||||
font-style: italic;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.pl-mb /* markup.bold */ {
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.pl-md /* markup.deleted, meta.diff.header.from-file */ {
|
||||
color: #bd2c00;
|
||||
background-color: #ffecec;
|
||||
}
|
||||
|
||||
.pl-mi1 /* markup.inserted, meta.diff.header.to-file */ {
|
||||
color: #55a532;
|
||||
background-color: #eaffea;
|
||||
}
|
||||
|
||||
.pl-mdr /* meta.diff.range */ {
|
||||
font-weight: bold;
|
||||
color: #795da3;
|
||||
}
|
||||
|
||||
.pl-mo /* meta.output */ {
|
||||
color: #1d3e81;
|
||||
}
|
||||
|
423
stylesheets/styles.css
Normal file
423
stylesheets/styles.css
Normal file
@ -0,0 +1,423 @@
|
||||
@import url(https://fonts.googleapis.com/css?family=Arvo:400,700,400italic);
|
||||
|
||||
/* MeyerWeb Reset */
|
||||
|
||||
html, body, div, span, applet, object, iframe,
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||
a, abbr, acronym, address, big, cite, code,
|
||||
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||
small, strike, strong, sub, sup, tt, var,
|
||||
b, u, i, center,
|
||||
dl, dt, dd, ol, ul, li,
|
||||
fieldset, form, label, legend,
|
||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||
article, aside, canvas, details, embed,
|
||||
figure, figcaption, footer, header, hgroup,
|
||||
menu, nav, output, ruby, section, summary,
|
||||
time, mark, audio, video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
|
||||
/* Base text styles */
|
||||
|
||||
body {
|
||||
padding:10px 50px 0 0;
|
||||
font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
color: #232323;
|
||||
background-color: #FBFAF7;
|
||||
margin: 0;
|
||||
line-height: 1.8em;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color:#232323;
|
||||
margin:36px 0 10px;
|
||||
}
|
||||
|
||||
p, ul, ol, table, dl {
|
||||
margin:0 0 22px;
|
||||
}
|
||||
|
||||
h1, h2, h3 {
|
||||
font-family: Arvo, Monaco, serif;
|
||||
line-height:1.3;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
h1,h2, h3 {
|
||||
display: block;
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
h4, h5, h6 {
|
||||
font-family: Arvo, Monaco, serif;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
a {
|
||||
color:#C30000;
|
||||
font-weight:200;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a small {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
em {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight:700;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-position: inside;
|
||||
list-style: disc;
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
ol {
|
||||
list-style-position: inside;
|
||||
list-style: decimal;
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0;
|
||||
padding: 0 0 0 20px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
dl, dt, dd, dl p {
|
||||
font-color: #444;
|
||||
}
|
||||
|
||||
dl dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
dl dd {
|
||||
padding-left: 20px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
dl p {
|
||||
padding-left: 20px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
hr {
|
||||
border:0;
|
||||
background:#ccc;
|
||||
height:1px;
|
||||
margin:0 0 24px;
|
||||
}
|
||||
|
||||
/* Images */
|
||||
|
||||
img {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
max-width: 650px;
|
||||
padding: 5px;
|
||||
margin: 10px 0 32px 0;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
p img {
|
||||
display: inline;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
|
||||
code, pre {
|
||||
font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace;
|
||||
color:#000;
|
||||
font-size:14px;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 4px 12px;
|
||||
background: #FDFEFB;
|
||||
border-radius:4px;
|
||||
border:1px solid #D7D8C8;
|
||||
overflow: auto;
|
||||
overflow-y: hidden;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
|
||||
/* Tables */
|
||||
|
||||
table {
|
||||
width:100%;
|
||||
}
|
||||
|
||||
table {
|
||||
border: 1px solid #ccc;
|
||||
margin-bottom: 32px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
th {
|
||||
font-family: 'Arvo', Helvetica, Arial, sans-serif;
|
||||
font-size: 18px;
|
||||
font-weight: normal;
|
||||
padding: 10px;
|
||||
background: #232323;
|
||||
color: #FDFEFB;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 10px;
|
||||
background: #ccc;
|
||||
}
|
||||
|
||||
|
||||
/* Wrapper */
|
||||
.wrapper {
|
||||
width:960px;
|
||||
}
|
||||
|
||||
|
||||
/* Header */
|
||||
|
||||
header {
|
||||
background-color: #171717;
|
||||
color: #FDFDFB;
|
||||
width:170px;
|
||||
float:left;
|
||||
position:fixed;
|
||||
border: 1px solid #000;
|
||||
-webkit-border-top-right-radius: 4px;
|
||||
-webkit-border-bottom-right-radius: 4px;
|
||||
-moz-border-radius-topright: 4px;
|
||||
-moz-border-radius-bottomright: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
padding: 34px 25px 22px 50px;
|
||||
margin: 30px 25px 0 0;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
p.header {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
h1.header {
|
||||
font-family: Arvo, sans-serif;
|
||||
font-size: 30px;
|
||||
font-weight: 300;
|
||||
line-height: 1.3em;
|
||||
border-bottom: none;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
|
||||
h1.header, a.header, a.name, header a{
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
a.header {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a.name {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
header ul {
|
||||
list-style:none;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
header li {
|
||||
list-style-type: none;
|
||||
width:132px;
|
||||
height:15px;
|
||||
margin-bottom: 12px;
|
||||
line-height: 1em;
|
||||
padding: 6px 6px 6px 7px;
|
||||
|
||||
background: #AF0011;
|
||||
background: -moz-linear-gradient(top, #AF0011 0%, #820011 100%);
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f8f8f8), color-stop(100%,#dddddd));
|
||||
background: -webkit-linear-gradient(top, #AF0011 0%,#820011 100%);
|
||||
background: -o-linear-gradient(top, #AF0011 0%,#820011 100%);
|
||||
background: -ms-linear-gradient(top, #AF0011 0%,#820011 100%);
|
||||
background: linear-gradient(top, #AF0011 0%,#820011 100%);
|
||||
|
||||
border-radius:4px;
|
||||
border:1px solid #0D0D0D;
|
||||
|
||||
-webkit-box-shadow: inset 0px 1px 1px 0 rgba(233,2,38, 1);
|
||||
box-shadow: inset 0px 1px 1px 0 rgba(233,2,38, 1);
|
||||
|
||||
}
|
||||
|
||||
header li:hover {
|
||||
background: #C3001D;
|
||||
background: -moz-linear-gradient(top, #C3001D 0%, #950119 100%);
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f8f8f8), color-stop(100%,#dddddd));
|
||||
background: -webkit-linear-gradient(top, #C3001D 0%,#950119 100%);
|
||||
background: -o-linear-gradient(top, #C3001D 0%,#950119 100%);
|
||||
background: -ms-linear-gradient(top, #C3001D 0%,#950119 100%);
|
||||
background: linear-gradient(top, #C3001D 0%,#950119 100%);
|
||||
}
|
||||
|
||||
a.buttons {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
background: url(../images/arrow-down.png) no-repeat;
|
||||
font-weight: normal;
|
||||
text-shadow: rgba(0, 0, 0, 0.4) 0 -1px 0;
|
||||
padding: 2px 2px 2px 22px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
a.github {
|
||||
background: url(../images/octocat-small.png) no-repeat 1px;
|
||||
}
|
||||
|
||||
a.buttons:hover {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
||||
/* Section - for main page content */
|
||||
|
||||
section {
|
||||
width:650px;
|
||||
float:right;
|
||||
padding-bottom:50px;
|
||||
}
|
||||
|
||||
|
||||
/* Footer */
|
||||
|
||||
footer {
|
||||
width:170px;
|
||||
float:left;
|
||||
position:fixed;
|
||||
bottom:10px;
|
||||
padding-left: 50px;
|
||||
}
|
||||
|
||||
@media print, screen and (max-width: 960px) {
|
||||
|
||||
div.wrapper {
|
||||
width:auto;
|
||||
margin:0;
|
||||
}
|
||||
|
||||
header, section, footer {
|
||||
float:none;
|
||||
position:static;
|
||||
width:auto;
|
||||
}
|
||||
|
||||
footer {
|
||||
border-top: 1px solid #ccc;
|
||||
margin:0 84px 0 50px;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
header {
|
||||
padding-right:320px;
|
||||
}
|
||||
|
||||
section {
|
||||
padding:20px 84px 20px 50px;
|
||||
margin:0 0 20px;
|
||||
}
|
||||
|
||||
header a small {
|
||||
display:inline;
|
||||
}
|
||||
|
||||
header ul {
|
||||
position:absolute;
|
||||
right:130px;
|
||||
top:84px;
|
||||
}
|
||||
}
|
||||
|
||||
@media print, screen and (max-width: 720px) {
|
||||
body {
|
||||
word-wrap:break-word;
|
||||
}
|
||||
|
||||
header {
|
||||
padding:10px 20px 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
section {
|
||||
padding:10px 0 10px 20px;
|
||||
margin:0 0 30px;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin: 0 0 0 30px;
|
||||
}
|
||||
|
||||
header ul, header p.view {
|
||||
position:static;
|
||||
}
|
||||
}
|
||||
|
||||
@media print, screen and (max-width: 480px) {
|
||||
|
||||
header ul li.download {
|
||||
display:none;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin: 0 0 0 20px;
|
||||
}
|
||||
|
||||
footer a{
|
||||
display:block;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
padding:0.4in;
|
||||
font-size:12pt;
|
||||
color:#444;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user