Compare commits
29 Commits
Author | SHA1 | Date | |
---|---|---|---|
c025a0894c
|
|||
a820d11e0e
|
|||
d8b9de5fcc
|
|||
d79f5821c5
|
|||
f74b311170
|
|||
acd8602891
|
|||
403a583942
|
|||
8725ce0aeb
|
|||
c45d408be1 | |||
8a1b74fe0f
|
|||
22ef5331af
|
|||
efeccef2aa
|
|||
4092e4e2cb | |||
db90716837
|
|||
24adbab4aa
|
|||
76a24b10f2
|
|||
2609fdd758
|
|||
440a9f4287
|
|||
4c2d9d51e1
|
|||
920b0a8952
|
|||
575113ed69
|
|||
0164eae199
|
|||
c7a602ae4e
|
|||
d0db0c62aa
|
|||
1d373df954
|
|||
895c80b5bf
|
|||
13751fed51
|
|||
a1ee92d8f2
|
|||
c745b96e08
|
7
.gitmodules
vendored
7
.gitmodules
vendored
@ -1,7 +1,6 @@
|
||||
[submodule "dnspython"]
|
||||
path = dnspython
|
||||
url = https://github.com/farrokhi/dnspython.git
|
||||
branch = python3
|
||||
[submodule "cymruwhois"]
|
||||
path = cymruwhois
|
||||
url = https://github.com/JustinAzoff/python-cymruwhois.git
|
||||
[submodule "dnspython-orig"]
|
||||
path = dnspython
|
||||
url = https://github.com/rthalley/dnspython/
|
||||
|
@ -1,5 +1,10 @@
|
||||
language: python
|
||||
python: "3.4"
|
||||
python:
|
||||
- "3.3"
|
||||
- "3.4"
|
||||
- "3.5"
|
||||
- "3.5-dev" # 3.5 development branch
|
||||
- "nightly" # currently points to 3.6-dev
|
||||
install: pip install --process-dependency-links dnsdiag
|
||||
script: nosetests dnstraceroute.py
|
||||
|
||||
|
30
README.md
30
README.md
@ -35,6 +35,10 @@ submodule update --init` and project directory to pull the required code.
|
||||
|
||||
# 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
|
||||
|
||||
```
|
||||
@ -49,6 +53,10 @@ git submodule update --init
|
||||
pip3 install --process-dependency-links 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:
|
||||
@ -99,17 +107,17 @@ 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 wikipedia.org
|
||||
server avg(ms) min(ms) max(ms) stddev(ms) lost(%)
|
||||
--------------------------------------------------------------------------
|
||||
4.2.2.1 151.067 131.270 221.742 28.643 %10
|
||||
4.2.2.2 142.175 132.921 178.133 13.348 %0
|
||||
64.6.64.6 133.047 109.145 162.938 20.609 %0
|
||||
64.6.65.6 377.270 97.669 661.471 172.717 %0
|
||||
8.8.4.4 389.048 294.581 511.134 67.953 %0
|
||||
8.8.8.8 0.000 0.000 0.000 0.000 %100
|
||||
208.67.222.222 179.068 135.975 258.582 50.681 %0
|
||||
208.67.220.220 137.817 135.822 140.113 1.504 %0
|
||||
% ./dnseval.py -f public-v4.txt -c3 ripe.net
|
||||
server avg(ms) min(ms) max(ms) stddev(ms) lost(%) flags
|
||||
----------------------------------------------------------------------------------
|
||||
8.8.8.8 210.225 109.864 407.420 170.785 %0 QR RD RA
|
||||
8.8.4.4 107.850 93.134 120.578 13.830 %0 QR RD RA
|
||||
ns.ripe.net 118.911 114.874 123.389 4.275 %0 QR AA RD
|
||||
4.2.2.1 104.380 102.449 106.588 2.083 %0 QR RD RA
|
||||
4.2.2.2 131.056 99.143 193.711 54.264 %0 QR RD RA
|
||||
4.2.2.3 98.956 97.463 100.310 1.429 %0 QR RD RA
|
||||
4.2.2.4 223.173 97.418 463.728 208.398 %0 QR RD RA
|
||||
4.2.2.5 104.290 97.264 117.878 11.770 %0 QR RD RA
|
||||
```
|
||||
|
||||
### Author
|
||||
|
96
dnseval.py
96
dnseval.py
@ -37,7 +37,7 @@ from statistics import stdev
|
||||
import dns.rdatatype
|
||||
import dns.resolver
|
||||
|
||||
__VERSION__ = 1.0
|
||||
__VERSION__ = 1.4
|
||||
__PROGNAME__ = os.path.basename(sys.argv[0])
|
||||
shutdown = False
|
||||
|
||||
@ -69,18 +69,66 @@ def maxlen(names):
|
||||
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 dnsping(host, server, dnsrecord, timeout, count):
|
||||
resolver = dns.resolver.Resolver()
|
||||
resolver.nameservers = [server]
|
||||
resolver.timeout = timeout
|
||||
resolver.lifetime = timeout
|
||||
resolver.retry_servfail = 0
|
||||
flags = 0
|
||||
answers = None
|
||||
resolver.use_edns(edns=True, payload=0, ednsflags=8)
|
||||
|
||||
response_time = []
|
||||
response_times = []
|
||||
i = 0
|
||||
|
||||
for i in range(count):
|
||||
if shutdown:
|
||||
if shutdown: # user pressed CTRL+C
|
||||
break
|
||||
try:
|
||||
stime = time.time()
|
||||
@ -92,18 +140,18 @@ def dnsping(host, server, dnsrecord, timeout, count):
|
||||
pass
|
||||
else:
|
||||
elapsed = (etime - stime) * 1000 # convert to milliseconds
|
||||
response_time.append(elapsed)
|
||||
response_times.append(elapsed)
|
||||
|
||||
r_sent = i + 1
|
||||
r_received = len(response_time)
|
||||
r_received = len(response_times)
|
||||
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)
|
||||
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:
|
||||
@ -112,19 +160,23 @@ def dnsping(host, server, dnsrecord, timeout, count):
|
||||
r_avg = 0
|
||||
r_stddev = 0
|
||||
|
||||
return (server, r_avg, r_min, r_max, r_stddev, r_lost_percent)
|
||||
if answers:
|
||||
flags = answers.response.flags
|
||||
|
||||
return server, r_avg, r_min, r_max, r_stddev, r_lost_percent, flags
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
signal.signal(signal.SIGTSTP, signal.SIG_IGN) # ignore CTRL+Z
|
||||
signal.signal(signal.SIGINT, signal_handler) # ignore CTRL+C
|
||||
except AttributeError: # Some systems may not support all signals
|
||||
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 = 5
|
||||
@ -172,10 +224,13 @@ def main():
|
||||
f = [name.strip() for name in f]
|
||||
width = maxlen(f)
|
||||
blanks = (width - 5) * ' '
|
||||
print('server ', blanks, ' avg(ms) min(ms) max(ms) stddev(ms) lost(%)')
|
||||
print((60 + width) * '-')
|
||||
print('server ', blanks, ' avg(ms) min(ms) max(ms) stddev(ms) lost(%) flags')
|
||||
print((84 + 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
|
||||
@ -184,17 +239,20 @@ def main():
|
||||
except OSError:
|
||||
print('Error: cannot resolve hostname:', server)
|
||||
s = None
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
s = server
|
||||
|
||||
if not s:
|
||||
continue
|
||||
(s, r_avg, r_min, r_max, r_stddev, r_lost_percent) = dnsping(hostname, s, dnsrecord, waittime,
|
||||
(s, r_avg, r_min, r_max, r_stddev, r_lost_percent, flags) = dnsping(hostname, s, dnsrecord, waittime,
|
||||
count)
|
||||
|
||||
s = server.ljust(width + 1)
|
||||
print("%s %-8.3f %-8.3f %-8.3f %-8.3f %%%d" % (
|
||||
s, r_avg, r_min, r_max, r_stddev, r_lost_percent), flush=True)
|
||||
text_flags = flags_to_text(flags)
|
||||
print("%s %-8.3f %-8.3f %-8.3f %-8.3f %%%-3d %25s" % (
|
||||
s, r_avg, r_min, r_max, r_stddev, r_lost_percent, text_flags), flush=True)
|
||||
|
||||
except Exception as e:
|
||||
print('error: %s' % e)
|
||||
|
11
dnsping.py
11
dnsping.py
@ -34,10 +34,11 @@ import sys
|
||||
import time
|
||||
from statistics import stdev
|
||||
|
||||
import dns.flags
|
||||
import dns.rdatatype
|
||||
import dns.resolver
|
||||
|
||||
__VERSION__ = 1.1
|
||||
__VERSION__ = 1.4
|
||||
__PROGNAME__ = os.path.basename(sys.argv[0])
|
||||
shutdown = False
|
||||
|
||||
@ -95,8 +96,8 @@ def main():
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], "qhc:s:t:w:vp:P:S:T46",
|
||||
["help", "output=", "count=", "server=", "quiet", "type=", "wait=", "verbose",
|
||||
"port", "dstport=", "srcip=", "tcp", "ipv4", "ipv6"])
|
||||
["help", "count=", "server=", "quiet", "type=", "wait=", "verbose",
|
||||
"port=", "srcip=", "tcp", "ipv4", "ipv6", "srcport="])
|
||||
except getopt.GetoptError as err:
|
||||
# print help information and exit:
|
||||
print(err) # will print something like "option -a not recognized"
|
||||
@ -167,7 +168,8 @@ def main():
|
||||
break
|
||||
try:
|
||||
stime = time.time()
|
||||
answers = resolver.query(hostname, dnsrecord, source_port=src_port, source=src_ip, tcp=use_tcp, af=af)
|
||||
answers = resolver.query(hostname, dnsrecord, source_port=src_port, source=src_ip, tcp=use_tcp,
|
||||
raise_on_no_answer=False)
|
||||
etime = time.time()
|
||||
except dns.resolver.NoNameservers as e:
|
||||
if not quiet:
|
||||
@ -198,6 +200,7 @@ def main():
|
||||
len(str(answers.rrset)), dnsserver, i, elapsed))
|
||||
if verbose:
|
||||
print(answers.rrset)
|
||||
print("flags:", dns.flags.to_text(answers.response.flags))
|
||||
|
||||
r_sent = i + 1
|
||||
r_received = len(response_time)
|
||||
|
Submodule dnspython updated: bc7445dcd5...a55e23fbc4
@ -35,13 +35,28 @@ import socket
|
||||
import sys
|
||||
import time
|
||||
|
||||
import dns.query
|
||||
import dns.rdatatype
|
||||
import dns.resolver
|
||||
|
||||
from cymruwhois import cymruwhois
|
||||
|
||||
__author__ = 'Babak Farrokhi (babak@farrokhi.net)'
|
||||
__license__ = 'BSD'
|
||||
__version__ = 1.3
|
||||
__version__ = 1.4
|
||||
_ttl = None
|
||||
quiet = False
|
||||
|
||||
|
||||
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():
|
||||
@ -79,14 +94,14 @@ def whoisrecord(ip):
|
||||
currenttime = time.time()
|
||||
ts = currenttime
|
||||
if ip in whois:
|
||||
ASN, ts = whois[ip]
|
||||
asn, ts = whois[ip]
|
||||
else:
|
||||
ts = 0
|
||||
if ((currenttime - ts) > 36000):
|
||||
if (currenttime - ts) > 36000:
|
||||
c = cymruwhois.Client()
|
||||
ASN = c.lookup(ip)
|
||||
whois[ip] = (ASN, currenttime)
|
||||
return ASN
|
||||
asn = c.lookup(ip)
|
||||
whois[ip] = (asn, currenttime)
|
||||
return asn
|
||||
except Exception as e:
|
||||
return e
|
||||
|
||||
@ -153,15 +168,20 @@ def expert_report(trace_path, color_mode):
|
||||
print(" %s[*]%s public DNS server is next to a reserved IP address (possible hijacking)" % (color.R, color.N))
|
||||
return
|
||||
|
||||
## no expert info available
|
||||
# no expert info available
|
||||
print(" %s[*]%s No expert hint available for this trace" % (color.G, color.N))
|
||||
|
||||
|
||||
def ping(resolver, hostname, dnsrecord, ttl):
|
||||
global _ttl
|
||||
|
||||
reached = False
|
||||
|
||||
dns.query.socket_factory = CustomSocket
|
||||
_ttl = ttl
|
||||
|
||||
try:
|
||||
resolver.query(hostname, dnsrecord, ipttl=ttl)
|
||||
resolver.query(hostname, dnsrecord)
|
||||
|
||||
except dns.resolver.NoNameservers as e:
|
||||
if not quiet:
|
||||
@ -203,7 +223,6 @@ def main():
|
||||
dnsrecord = 'A'
|
||||
count = 30
|
||||
timeout = 1
|
||||
quiet = False
|
||||
dnsserver = dns.resolver.get_default_resolver().nameservers[0]
|
||||
dest_port = 53
|
||||
hops = 0
|
||||
@ -330,11 +349,11 @@ def main():
|
||||
if curr_addr:
|
||||
as_name = ""
|
||||
if as_lookup:
|
||||
ASN = whoisrecord(curr_addr)
|
||||
asn = whoisrecord(curr_addr)
|
||||
as_name = ''
|
||||
try:
|
||||
if ASN and ASN.asn != "NA":
|
||||
as_name = "[%s %s] " % (ASN.asn, ASN.owner)
|
||||
if asn and asn.asn != "NA":
|
||||
as_name = "[%s %s] " % (asn.asn, asn.owner)
|
||||
except AttributeError:
|
||||
if shutdown:
|
||||
sys.exit(0)
|
||||
|
0
requirements.txt
Normal file
0
requirements.txt
Normal file
32
setup.py
32
setup.py
@ -1,29 +1,37 @@
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name = "dnsdiag",
|
||||
version = "1.3.3",
|
||||
packages = find_packages(),
|
||||
scripts = ['dnsping.py', 'dnstraceroute.py', 'dnseval.py'],
|
||||
name="dnsdiag",
|
||||
version="1.3.5",
|
||||
packages=find_packages(),
|
||||
classifiers=[
|
||||
"Topic :: System :: Networking",
|
||||
"Environment :: Console",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: BSD License",
|
||||
'Programming Language :: Python :: 3.4',
|
||||
"Programming Language :: Python :: 3.4",
|
||||
"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 = """
|
||||
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",
|
||||
url = "https://dnsdiag.org/",
|
||||
license="BSD",
|
||||
keywords="dns traceroute ping",
|
||||
url="https://dnsdiag.org/",
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'dnsping = dnsping:main',
|
||||
'dnstraceroute = dnstraceroute:main',
|
||||
'dnseval = dnseval:main',
|
||||
]
|
||||
}
|
||||
)
|
||||
|
Reference in New Issue
Block a user