40 Commits

Author SHA1 Message Date
21944596fb Bump version 2017-04-24 15:39:23 +04:30
896484244c Fix string formatting 2017-04-24 15:37:24 +04:30
62f66a9b3c Add verbose mode to print actual response(s) (FIX #28) 2017-04-24 15:17:22 +04:30
6c1d7313fe Display correct TTL in case of multiple responses like CNAME (FIX #29) 2017-04-24 14:42:34 +04:30
0c01d0ebb2 Improve help message 2017-04-24 14:41:53 +04:30
8267ce55b2 Update list of public resolvers 2017-04-23 14:06:21 +04:30
87cea8c04c Improve query time measurement accuracy and display 2017-04-23 13:23:17 +04:30
6a0b0c2ff5 Fix build with Travis CI 2016-11-14 16:26:36 +03:30
c9f3fe6f0d We don't need a _modified_ dnspython anymore. Fix docs. 2016-11-14 15:44:49 +03:30
77aec5958f Improve expert hints (fix #30)
Now we assume a radius for local network (persumably 4 hops). Now if a DNS server appears next to a private or reserved IP address, and it happens within 4 hops, then it is your local network and this is not necessarily a bad thing. But if it happens beyond 4 hops, we assume it is public network, and you are not supposed to see a probe response from a private IP address on internet (fishy).
2016-11-14 15:40:07 +03:30
d78c67dc79 Update README 2016-10-20 14:12:30 +03:30
66e5d5acef Remove local dnspython submodule since the latest dnspython (as of 1.15.0) supports all the requirements 2016-10-14 14:19:43 +03:30
2715f42722 Change default timeout value to 2 (was 5) (fix #24) 2016-08-22 16:02:25 +04:30
50934cbe91 Fix handling invalid TTL and some output string justifications (fix #26, #27) 2016-08-22 15:58:58 +04:30
93b0d6ec51 Show TTL in dnseval output and update README (fix #23) 2016-08-16 15:08:19 +04:30
cc5dffc5d5 Do not exit if a resolver cannot resolve a name (Fix #22) 2016-08-16 14:36:08 +04:30
3220c1b65d Remove -e from examples 2016-08-06 16:41:56 +04:30
af6e32aa3f Update README to reflect new changes in flags (-e -> -x) 2016-08-06 16:41:01 +04:30
c110b19266 Reverse behavior of -e flag. EDNS0 is now enabled by default (fix #21) 2016-08-06 16:40:05 +04:30
6c4a88b819 Fix conflicting -e switch (--expert and --edns).
- Also reverse behavior of -e flag. EDNS0 is now enabled by default.
2016-08-06 16:38:47 +04:30
a576293bc0 Enable EDNS0 by default. -e disables EDNS0 (fix #21) 2016-08-06 16:36:56 +04:30
b0e73e1b9d Merge pull request #20 from hamishcoleman/master
Add option to pause between each dnsping request
2016-08-04 13:15:28 +04:30
df366d5934 Add option to pause between each dnsping request 2016-08-04 14:13:33 +10:00
ec6e93e2b2 Update readme with fixed example output 2016-06-26 17:49:05 +04:30
52b89212f0 Use ASCII character for separator 2016-06-26 17:48:03 +04:30
94fa0508eb Bump version 2016-06-26 17:17:24 +04:30
ff52245007 Add EDNS0 support and update docs 2016-06-26 17:15:54 +04:30
5b8b94a2c0 Update example for dnstraceroute 2016-06-26 16:59:05 +04:30
744d492c3d Update example for dnsping 2016-06-26 16:57:11 +04:30
26fdd00647 Add support for EDNS0 flag 2016-06-26 16:54:22 +04:30
40052f008d Using EDNS0 is now optional and disabled by default
Also update documentation with new example
2016-06-26 16:42:19 +04:30
8535bb2aba Bump version 2016-06-15 17:15:29 +04:30
b23a3ce081 Fix looking up NS record from root server
- also resolve hostname to IP if name is given as dns server
- raise timeout to 2 seconds
2016-06-15 17:14:10 +04:30
364312cbac Add list of root servers as example for dnseval 2016-06-15 16:29:53 +04:30
adb4f26f8a Bump version 2016-06-15 16:27:27 +04:30
eeb647b99f Fix bug in dealing with root servers 2016-06-15 16:26:58 +04:30
758b7b8ccd Change separator line 2016-06-14 18:46:45 +04:30
2fb6cdb300 Add --tcp/-T option (fix #19) 2016-06-14 18:31:58 +04:30
1fa14ded48 Correctly show AD flag (fix #13) 2016-06-14 18:27:20 +04:30
597b801b7b Bump version 2016-06-14 17:44:46 +04:30
13 changed files with 182 additions and 110 deletions

3
.gitmodules vendored
View File

@ -1,6 +1,3 @@
[submodule "cymruwhois"] [submodule "cymruwhois"]
path = cymruwhois path = cymruwhois
url = https://github.com/JustinAzoff/python-cymruwhois.git url = https://github.com/JustinAzoff/python-cymruwhois.git
[submodule "dnspython-orig"]
path = dnspython
url = https://github.com/rthalley/dnspython/

View File

@ -5,6 +5,6 @@ python:
- "3.5" - "3.5"
- "3.5-dev" # 3.5 development branch - "3.5-dev" # 3.5 development branch
- "nightly" # currently points to 3.6-dev - "nightly" # currently points to 3.6-dev
install: pip install --process-dependency-links dnsdiag install: "pip install -r requirements.txt"
script: nosetests dnstraceroute.py script: nosetests dnstraceroute.py

View File

@ -28,10 +28,9 @@ of view.
# prerequisites # prerequisites
This script requires python3 as well as latest This script requires python3 as well as latest
[dnspython](http://www.dnspython.org/) and [dnspython](http://www.dnspython.org/) and
[cymruwhois](https://pythonhosted.org/cymruwhois/). Please note that [cymruwhois](https://pythonhosted.org/cymruwhois/).
"dnstraceroute" requires a modified version of dnspython module. All required All required third-party modules are included as GIT submodules. You just need
third-party modules are included as GIT submodules. You just need to run `git to run `git submodule update --init` and project directory to pull the required code.
submodule update --init` and project directory to pull the required code.
# installation # installation
@ -61,15 +60,15 @@ From time to time, binary version will be released for Windows, Mac OS X and Lin
dnsping pings a DNS resolver by sending an arbitrary DNS query for given number dnsping pings a DNS resolver by sending an arbitrary DNS query for given number
of times: of times:
``` ```
% ./dnsping.py -c 3 -s 8.8.8.8 -t MX wikipedia.org % ./dnsping.py -c 3 -t AAAA -s 8.8.8.8 dnsdiag.org
dnsping.py DNS: 8.8.8.8:53, hostname: wikipedia.org, rdatatype: MX dnsping.py DNS: 8.8.8.8:53, hostname: dnsdiag.org, rdatatype: AAAA
101 bytes from 8.8.8.8: seq=0 time=262.896 ms 4 bytes from 8.8.8.8: seq=0 time=123.509 ms
101 bytes from 8.8.8.8: seq=1 time=305.608 ms 4 bytes from 8.8.8.8: seq=1 time=115.726 ms
101 bytes from 8.8.8.8: seq=2 time=307.221 ms 4 bytes from 8.8.8.8: seq=2 time=117.351 ms
--- 8.8.8.8 dnsping statistics --- --- 8.8.8.8 dnsping statistics ---
3 requests transmitted, 3 responses received, 0% lost 3 requests transmitted, 3 responses received, 0% lost
min=262.896 ms, avg=291.908 ms, max=307.221 ms, stddev=25.138 ms 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 This script calculates minimum, maximum and average response time as well as
jitter (stddev) jitter (stddev)
@ -81,23 +80,16 @@ it to your actual network traceroute and make sure your DNS traffic is not
routed to any unwanted path. routed to any unwanted path.
``` ```
% ./dnstraceroute.py --expert -s 8.8.4.4 yahoo.com % ./dnstraceroute.py --expert -C -t A -s 8.8.4.4 facebook.com
dnstraceroute.py DNS: 8.8.4.4:53, hostname: yahoo.com, rdatatype: A dnstraceroute.py DNS: 8.8.4.4:53, hostname: facebook.com, rdatatype: A
1 204.109.58.53 (204.109.58.53) 1 ms 1 192.168.0.1 (192.168.0.1) 1 ms
2 208.79.80.5 (208.79.80.5) 1 ms 2 192.168.28.177 (192.168.28.177) 4 ms
3 162.223.13.177 (162.223.13.177) 1 ms 3 192.168.0.1 (192.168.0.1) 693 ms
4 208.79.80.254 (208.79.80.254) 7 ms 4 172.19.4.17 (172.19.4.17) 3 ms
5 eqixva-google-gige.google.com (206.126.236.21) 7 ms 5 google-public-dns-b.google.com (8.8.4.4) 8 ms
6 209.85.242.142 (209.85.242.142) 7 ms
7 72.14.236.148 (72.14.236.148) 8 ms
8 209.85.250.70 (209.85.250.70) 16 ms
9 74.125.37.222 (74.125.37.222) 16 ms
10 *
11 google-public-dns-b.google.com (8.8.4.4) 15 ms
=== Expert Hints ===
[*] public DNS server is next to an invisible hop (probably a firewall)
=== 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). Using `--expert` will instruct dnstraceroute to print expert hints (such as warnings of possible DNS traffic hijacking).
@ -107,17 +99,21 @@ 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 of DNS servers. This script is meant for comparing response time of multiple
DNS servers at once: DNS servers at once:
``` ```
% ./dnseval.py -f public-v4.txt -c3 ripe.net % ./dnseval.py -t AAAA -f public-v4.txt -c10 yahoo.com
server avg(ms) min(ms) max(ms) stddev(ms) lost(%) flags server avg(ms) min(ms) max(ms) stddev(ms) lost(%) ttl flags
---------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------
8.8.8.8 210.225 109.864 407.420 170.785 %0 QR RD RA 8.8.8.8 270.791 215.599 307.498 40.630 %0 298 QR -- -- RD RA -- --
8.8.4.4 107.850 93.134 120.578 13.830 %0 QR RD RA 8.8.4.4 222.955 171.753 307.251 60.481 %10 291 QR -- -- RD RA -- --
ns.ripe.net 118.911 114.874 123.389 4.275 %0 QR AA RD ns.ripe.net 174.855 160.949 187.458 10.099 %0 289 QR -- -- RD RA -- --
4.2.2.1 104.380 102.449 106.588 2.083 %0 QR RD RA 4.2.2.1 172.798 163.892 189.918 7.823 %0 287 QR -- -- RD RA -- --
4.2.2.2 131.056 99.143 193.711 54.264 %0 QR RD RA 4.2.2.2 178.594 169.158 184.696 5.067 %0 285 QR -- -- RD RA -- --
4.2.2.3 98.956 97.463 100.310 1.429 %0 QR RD RA 4.2.2.3 153.574 138.509 173.439 12.015 %0 284 QR -- -- RD RA -- --
4.2.2.4 223.173 97.418 463.728 208.398 %0 QR RD RA 4.2.2.4 153.182 141.023 162.323 6.700 %0 282 QR -- -- RD RA -- --
4.2.2.5 104.290 97.264 117.878 11.770 %0 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 ### Author
@ -125,7 +121,7 @@ ns.ripe.net 118.911 114.874 123.389 4.275 %0 QR AA
Babak Farrokhi Babak Farrokhi
- twitter: [@farrokhi](https://twitter.com/farrokhi) - twitter: [@farrokhi](https://twitter.com/farrokhi)
- github: [/farrokhi](https://github.com/farrokhi/) - github: [github.com/farrokhi](https://github.com/farrokhi/)
- website: [farrokhi.net](https://farrokhi.net/) - website: [farrokhi.net](https://farrokhi.net/)
@ -133,8 +129,3 @@ Babak Farrokhi
dnsdiag is released under a 2 clause BSD license. dnsdiag is released under a 2 clause BSD license.
### Credits
- [@rthalley](https://github.com/rthalley) for invaluable [dnspython](https://github.com/rthalley/dnspython) library
- [@JustinAzoff](https://github.com/JustinAzoff) for [python-cymruwhois](https://github.com/JustinAzoff/python-cymruwhois) library
- [@bortzmeyer](https://github.com/bortzmeyer) for his feedback and patches

View File

@ -1,6 +1,3 @@
# TODO # TODO
- implement basic DNS client functionality into dnstraceroute to eliminate need
of modified dnspython module
- add support for python 2.x for the conservatives and faint-hearted ;-)
- dnsfingerprint.py tool to fingerprint DNS servers - dnsfingerprint.py tool to fingerprint DNS servers

1
dns
View File

@ -1 +0,0 @@
dnspython/dns

View File

@ -25,9 +25,10 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import os
import getopt import getopt
import ipaddress import ipaddress
import os
import signal import signal
import socket import socket
import sys import sys
@ -37,7 +38,7 @@ from statistics import stdev
import dns.rdatatype import dns.rdatatype
import dns.resolver import dns.resolver
__VERSION__ = 1.4 __VERSION__ = 1.6
__PROGNAME__ = os.path.basename(sys.argv[0]) __PROGNAME__ = os.path.basename(sys.argv[0])
shutdown = False shutdown = False
@ -51,8 +52,11 @@ usage: %s [-h] [-f server-list] [-c count] [-t type] [-w wait] hostname
-h --help show this help -h --help show this help
-f --file dns server list to use (default: system resolvers) -f --file dns server list to use (default: system resolvers)
-c --count number of requests to send (default: 10) -c --count number of requests to send (default: 10)
-w --wait maximum wait time for a reply (default: 5) -w --wait maximum wait time for a reply (default: 2)
-t --type DNS request record type (default: A) -t --type DNS request record type (default: A)
-T --tcp Use TCP instead of UDP
-e --edns Disable EDNS0 (Default: Enabled)
-v --verbose Print actual dns response
""" % (__PROGNAME__, __VERSION__, __PROGNAME__)) """ % (__PROGNAME__, __VERSION__, __PROGNAME__))
sys.exit() sys.exit()
@ -114,15 +118,17 @@ def flags_to_text(flags):
return ' '.join(text_flags) return ' '.join(text_flags)
def dnsping(host, server, dnsrecord, timeout, count): def dnsping(host, server, dnsrecord, timeout, count, use_tcp=False, use_edns=False):
resolver = dns.resolver.Resolver() resolver = dns.resolver.Resolver()
resolver.nameservers = [server] resolver.nameservers = [server]
resolver.timeout = timeout resolver.timeout = timeout
resolver.lifetime = timeout resolver.lifetime = timeout
resolver.retry_servfail = 0 resolver.retry_servfail = 0
flags = 0 flags = 0
ttl = None
answers = None answers = None
resolver.use_edns(edns=True, payload=0, ednsflags=8) if use_edns:
resolver.use_edns(edns=0, payload=8192, ednsflags=dns.flags.edns_from_text('DO'))
response_times = [] response_times = []
i = 0 i = 0
@ -131,9 +137,10 @@ def dnsping(host, server, dnsrecord, timeout, count):
if shutdown: # user pressed CTRL+C if shutdown: # user pressed CTRL+C
break break
try: try:
stime = time.time() stime = time.perf_counter()
answers = resolver.query(host, dnsrecord) # todo: response validation in future answers = resolver.query(host, dnsrecord, tcp=use_tcp,
etime = time.time() raise_on_no_answer=False) # todo: response validation in future
etime = time.perf_counter()
except (dns.resolver.NoNameservers, dns.resolver.NoAnswer): except (dns.resolver.NoNameservers, dns.resolver.NoAnswer):
break break
except dns.resolver.Timeout: except dns.resolver.Timeout:
@ -160,10 +167,12 @@ def dnsping(host, server, dnsrecord, timeout, count):
r_avg = 0 r_avg = 0
r_stddev = 0 r_stddev = 0
if answers: if answers is not None:
flags = answers.response.flags 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 return server, r_avg, r_min, r_max, r_stddev, r_lost_percent, flags, ttl, answers
def main(): def main():
@ -179,14 +188,17 @@ def main():
# defaults # defaults
dnsrecord = 'A' dnsrecord = 'A'
count = 10 count = 10
waittime = 5 waittime = 2
inputfilename = None inputfilename = None
fromfile = False fromfile = False
use_tcp = False
use_edns = True
verbose = False
hostname = 'wikipedia.org' hostname = 'wikipedia.org'
try: try:
opts, args = getopt.getopt(sys.argv[1:], "hf:c:t:w:", opts, args = getopt.getopt(sys.argv[1:], "hf:c:t:w:Tev",
["help", "file=", "count=", "type=", "wait="]) ["help", "file=", "count=", "type=", "wait=", "tcp", "edns", "verbose"])
except getopt.GetoptError as err: except getopt.GetoptError as err:
print(err) print(err)
usage() usage()
@ -208,6 +220,12 @@ def main():
waittime = int(a) waittime = int(a)
elif o in ("-t", "--type"): elif o in ("-t", "--type"):
dnsrecord = a dnsrecord = a
elif o in ("-T", "--tcp"):
use_tcp = True
elif o in ("-e", "--edns"):
use_edns = False
elif o in ("-v", "--verbose"):
verbose = True
else: else:
print("Invalid option: %s" % o) print("Invalid option: %s" % o)
usage() usage()
@ -224,8 +242,8 @@ def main():
f = [name.strip() for name in f] f = [name.strip() for name in f]
width = maxlen(f) width = maxlen(f)
blanks = (width - 5) * ' ' blanks = (width - 5) * ' '
print('server ', blanks, ' avg(ms) min(ms) max(ms) stddev(ms) lost(%) flags') print('server ', blanks, ' avg(ms) min(ms) max(ms) stddev(ms) lost(%) ttl flags')
print((84 + width) * '-') print((93 + width) * '-')
for server in f: for server in f:
# check if we have a valid dns server address # check if we have a valid dns server address
if server.lstrip() == '': # deal with empty lines if server.lstrip() == '': # deal with empty lines
@ -246,16 +264,39 @@ def main():
if not s: if not s:
continue continue
(s, r_avg, r_min, r_max, r_stddev, r_lost_percent, flags) = dnsping(hostname, s, dnsrecord, waittime,
count) try:
(s, r_avg, r_min, r_max, r_stddev, r_lost_percent, flags, ttl, answers) = dnsping(hostname, s,
dnsrecord,
waittime,
count,
use_tcp=use_tcp,
use_edns=use_edns)
except dns.resolver.NXDOMAIN:
print('%-15s NXDOMAIN' % server)
continue
except Exception as e:
print('%s: %s' % (server, e))
continue
s = server.ljust(width + 1) s = server.ljust(width + 1)
text_flags = flags_to_text(flags) 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) s_ttl = str(ttl)
if s_ttl == "None":
s_ttl = "N/A"
print("%s %-8.3f %-8.3f %-8.3f %-8.3f %%%-3d %-8s %21s" % (
s, r_avg, r_min, r_max, r_stddev, r_lost_percent, s_ttl, text_flags), flush=True)
if verbose:
ans_index = 1
for answer in answers.response.answer:
print("Answer %d [ %s ]" % (ans_index, answer))
ans_index += 1
print("")
except Exception as e: except Exception as e:
print('error: %s' % e) print('%s: %s' % (server, e))
sys.exit(1) sys.exit(1)

View File

@ -38,14 +38,14 @@ import dns.flags
import dns.rdatatype import dns.rdatatype
import dns.resolver import dns.resolver
__VERSION__ = 1.4 __VERSION__ = 1.6
__PROGNAME__ = os.path.basename(sys.argv[0]) __PROGNAME__ = os.path.basename(sys.argv[0])
shutdown = False shutdown = False
def usage(): def usage():
print("""%s version %1.1f print("""%s version %1.1f
usage: %s [-h] [-q] [-v] [-s server] [-p port] [-P port] [-S address] [-c count] [-t type] [-w wait] hostname usage: %s [-ehqv] [-s server] [-p port] [-P port] [-S address] [-c count] [-t type] [-w wait] hostname
-h --help Show this help -h --help Show this help
-q --quiet Quiet -q --quiet Quiet
-v --verbose Print actual dns response -v --verbose Print actual dns response
@ -57,8 +57,10 @@ usage: %s [-h] [-q] [-v] [-s server] [-p port] [-P port] [-S address] [-c count]
-P --srcport Query source port number (default: 0) -P --srcport Query source port number (default: 0)
-S --srcip Query source IP address (default: default interface address) -S --srcip Query source IP address (default: default interface address)
-c --count Number of requests to send (default: 10) -c --count Number of requests to send (default: 10)
-w --wait Maximum wait time for a reply (default: 5) -w --wait Maximum wait time for a reply (default: 2 seconds)
-i --interval Time between each request (default: 0 seconds)
-t --type DNS request record type (default: A) -t --type DNS request record type (default: A)
-e --edns Disable EDNS0 (default: Enabled)
""" % (__PROGNAME__, __VERSION__, __PROGNAME__)) """ % (__PROGNAME__, __VERSION__, __PROGNAME__))
sys.exit(0) sys.exit(0)
@ -83,7 +85,8 @@ def main():
# defaults # defaults
dnsrecord = 'A' dnsrecord = 'A'
count = 10 count = 10
timeout = 5 timeout = 2
interval = 0
quiet = False quiet = False
verbose = False verbose = False
dnsserver = dns.resolver.get_default_resolver().nameservers[0] dnsserver = dns.resolver.get_default_resolver().nameservers[0]
@ -91,13 +94,14 @@ def main():
src_port = 0 src_port = 0
src_ip = None src_ip = None
use_tcp = False use_tcp = False
use_edns = True
af = socket.AF_INET af = socket.AF_INET
hostname = 'wikipedia.org' hostname = 'wikipedia.org'
try: try:
opts, args = getopt.getopt(sys.argv[1:], "qhc:s:t:w:vp:P:S:T46", opts, args = getopt.getopt(sys.argv[1:], "qhc:s:t:w:i:vp:P:S:T46e",
["help", "count=", "server=", "quiet", "type=", "wait=", "verbose", ["help", "count=", "server=", "quiet", "type=", "wait=", "interval=", "verbose",
"port=", "srcip=", "tcp", "ipv4", "ipv6", "srcport="]) "port=", "srcip=", "tcp", "ipv4", "ipv6", "srcport=", "edns"])
except getopt.GetoptError as err: except getopt.GetoptError as err:
# print help information and exit: # print help information and exit:
print(err) # will print something like "option -a not recognized" print(err) # will print something like "option -a not recognized"
@ -124,6 +128,8 @@ def main():
verbose = False verbose = False
elif o in ("-w", "--wait"): elif o in ("-w", "--wait"):
timeout = int(a) timeout = int(a)
elif o in ("-i", "--interval"):
interval = int(a)
elif o in ("-t", "--type"): elif o in ("-t", "--type"):
dnsrecord = a dnsrecord = a
elif o in ("-T", "--tcp"): elif o in ("-T", "--tcp"):
@ -132,6 +138,8 @@ def main():
af = socket.AF_INET af = socket.AF_INET
elif o in ("-6", "--ipv6"): elif o in ("-6", "--ipv6"):
af = socket.AF_INET6 af = socket.AF_INET6
elif o in ("-e", "--edns"):
use_edns = False
elif o in ("-P", "--srcport"): elif o in ("-P", "--srcport"):
src_port = int(a) src_port = int(a)
if src_port < 1024: if src_port < 1024:
@ -158,6 +166,9 @@ def main():
resolver.port = dst_port resolver.port = dst_port
resolver.retry_servfail = 0 resolver.retry_servfail = 0
if use_edns:
resolver.use_edns(edns=0, payload=8192, ednsflags=dns.flags.edns_from_text('DO'))
response_time = [] response_time = []
i = 0 i = 0
@ -167,10 +178,10 @@ def main():
if shutdown: if shutdown:
break break
try: try:
stime = time.time() stime = time.perf_counter()
answers = resolver.query(hostname, dnsrecord, source_port=src_port, source=src_ip, tcp=use_tcp, answers = resolver.query(hostname, dnsrecord, source_port=src_port, source=src_ip, tcp=use_tcp,
raise_on_no_answer=False) raise_on_no_answer=False)
etime = time.time() etime = time.perf_counter()
except dns.resolver.NoNameservers as e: except dns.resolver.NoNameservers as e:
if not quiet: if not quiet:
print("No response to dns request") print("No response to dns request")
@ -196,12 +207,16 @@ def main():
response_time.append(elapsed) response_time.append(elapsed)
if not quiet: if not quiet:
print( print(
"%d bytes from %s: seq=%-3d time=%3.3f ms" % ( "%d bytes from %s: seq=%-3d time=%.3f ms" % (
len(str(answers.rrset)), dnsserver, i, elapsed)) len(str(answers.rrset)), dnsserver, i, elapsed))
if verbose: if verbose:
print(answers.rrset) print(answers.rrset)
print("flags:", dns.flags.to_text(answers.response.flags)) print("flags:", dns.flags.to_text(answers.response.flags))
time_to_next = (stime + interval) - etime
if time_to_next > 0:
time.sleep(time_to_next)
r_sent = i + 1 r_sent = i + 1
r_received = len(response_time) r_received = len(response_time)
r_lost = r_sent - r_received r_lost = r_sent - r_received
@ -221,8 +236,8 @@ def main():
r_stddev = 0 r_stddev = 0
print('\n--- %s dnsping statistics ---' % dnsserver) print('\n--- %s dnsping statistics ---' % dnsserver)
print('%d requests transmitted, %d responses received, %3.0f%% lost' % (r_sent, r_received, r_lost_percent)) print('%d requests transmitted, %d responses received, %.0f%% lost' % (r_sent, r_received, r_lost_percent))
print('min=%3.3f ms, avg=%3.3f ms, max=%3.3f ms, stddev=%3.3f ms' % (r_min, r_avg, r_max, r_stddev)) print('min=%.3f ms, avg=%.3f ms, max=%.3f ms, stddev=%.3f ms' % (r_min, r_avg, r_max, r_stddev))
if __name__ == '__main__': if __name__ == '__main__':

Submodule dnspython deleted from a55e23fbc4

View File

@ -43,7 +43,7 @@ from cymruwhois import cymruwhois
__author__ = 'Babak Farrokhi (babak@farrokhi.net)' __author__ = 'Babak Farrokhi (babak@farrokhi.net)'
__license__ = 'BSD' __license__ = 'BSD'
__version__ = 1.4 __version__ = 1.6
_ttl = None _ttl = None
quiet = False quiet = False
@ -91,7 +91,7 @@ shutdown = False
def whoisrecord(ip): def whoisrecord(ip):
try: try:
currenttime = time.time() currenttime = time.perf_counter()
ts = currenttime ts = currenttime
if ip in whois: if ip in whois:
asn, ts = whois[ip] asn, ts = whois[ip]
@ -118,17 +118,18 @@ except IOError:
def usage(): def usage():
print('%s version %1.1f\n' % (__PROGNAME__, __version__)) print('%s version %1.1f\n' % (__PROGNAME__, __version__))
print('usage: %s [-h] [-q] [-a] [-s server] [-p port] [-c count] [-t type] [-w wait] hostname' % __PROGNAME__) print('usage: %s [-aeqhCx] [-s server] [-p port] [-c count] [-t type] [-w wait] hostname' % __PROGNAME__)
print(' -h --help Show this help') print(' -h --help Show this help')
print(' -q --quiet Quiet') print(' -q --quiet Quiet')
print(' -e --expert Print expert hints if available') print(' -x --expert Print expert hints if available')
print(' -a --asn Turn on AS# lookups for each hop encountered') print(' -a --asn Turn on AS# lookups for each hop encountered')
print(' -s --server DNS server to use (default: first system resolver)') print(' -s --server DNS server to use (default: first system resolver)')
print(' -p --port DNS server port number (default: 53)') print(' -p --port DNS server port number (default: 53)')
print(' -c --count Maximum number of hops (default: 30)') print(' -c --count Maximum number of hops (default: 30)')
print(' -w --wait Maximum wait time for a reply (default: 5)') print(' -w --wait Maximum wait time for a reply (default: 2)')
print(' -t --type DNS request record type (default: A)') print(' -t --type DNS request record type (default: A)')
print(' -C --color Print colorful output') print(' -C --color Print colorful output')
print(' -e --edns Disable EDNS0 (Default: Enabled)')
print(' ') print(' ')
sys.exit() sys.exit()
@ -147,6 +148,7 @@ def expert_report(trace_path, color_mode):
print(" [*] empty trace - should not happen") print(" [*] empty trace - should not happen")
return return
private_network_radius = 4 # number of hops we assume we are still inside our local network
prev_hop = None prev_hop = None
if len(trace_path) > 1: if len(trace_path) > 1:
prev_hop = trace_path[-2] prev_hop = trace_path[-2]
@ -156,15 +158,15 @@ def expert_report(trace_path, color_mode):
" %s[*]%s path too short (possible DNS hijacking, unless it is a local DNS resolver)" % (color.R, color.N)) " %s[*]%s path too short (possible DNS hijacking, unless it is a local DNS resolver)" % (color.R, color.N))
return return
if prev_hop == '*': 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)) print(" %s[*]%s public DNS server is next to an invisible hop (probably a firewall)" % (color.R, color.N))
return return
if prev_hop and ipaddress.ip_address(prev_hop).is_private: 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)) print(" %s[*]%s public DNS server is next to a private IP address (possible hijacking)" % (color.R, color.N))
return return
if prev_hop and ipaddress.ip_address(prev_hop).is_reserved: 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)) print(" %s[*]%s public DNS server is next to a reserved IP address (possible hijacking)" % (color.R, color.N))
return return
@ -172,16 +174,18 @@ def expert_report(trace_path, color_mode):
print(" %s[*]%s No expert hint available for this trace" % (color.G, color.N)) print(" %s[*]%s No expert hint available for this trace" % (color.G, color.N))
def ping(resolver, hostname, dnsrecord, ttl): def ping(resolver, hostname, dnsrecord, ttl, use_edns=False):
global _ttl global _ttl
reached = False reached = False
dns.query.socket_factory = CustomSocket dns.query.socket_factory = CustomSocket
_ttl = ttl _ttl = ttl
if use_edns:
resolver.use_edns(edns=0, payload=8192, ednsflags=dns.flags.edns_from_text('DO'))
try: try:
resolver.query(hostname, dnsrecord) resolver.query(hostname, dnsrecord, raise_on_no_answer=False)
except dns.resolver.NoNameservers as e: except dns.resolver.NoNameservers as e:
if not quiet: if not quiet:
@ -199,8 +203,8 @@ def ping(resolver, hostname, dnsrecord, ttl):
pass pass
except SystemExit: except SystemExit:
pass pass
except: except Exception as e:
print("unxpected error: ", sys.exc_info()[0]) print("unxpected error: ", e)
sys.exit(1) sys.exit(1)
else: else:
reached = True reached = True
@ -222,17 +226,18 @@ def main():
dnsrecord = 'A' dnsrecord = 'A'
count = 30 count = 30
timeout = 1 timeout = 2
dnsserver = dns.resolver.get_default_resolver().nameservers[0] dnsserver = dns.resolver.get_default_resolver().nameservers[0]
dest_port = 53 dest_port = 53
hops = 0 hops = 0
as_lookup = False as_lookup = False
expert_mode = False expert_mode = False
should_resolve = True should_resolve = True
use_edns = True
color_mode = False color_mode = False
try: try:
opts, args = getopt.getopt(sys.argv[1:], "aqhc:s:t:w:p:neC", opts, args = getopt.getopt(sys.argv[1:], "aqhc:s:t:w:p:nexC",
["help", "count=", "server=", "quiet", "type=", "wait=", "asn", "port", "expert", ["help", "count=", "server=", "quiet", "type=", "wait=", "asn", "port", "expert",
"color"]) "color"])
except getopt.GetoptError as err: except getopt.GetoptError as err:
@ -250,7 +255,7 @@ def main():
usage() usage()
elif o in ("-c", "--count"): elif o in ("-c", "--count"):
count = int(a) count = int(a)
elif o in ("-e", "--expert"): elif o in ("-x", "--expert"):
expert_mode = True expert_mode = True
elif o in ("-s", "--server"): elif o in ("-s", "--server"):
dnsserver = a dnsserver = a
@ -268,11 +273,23 @@ def main():
should_resolve = False should_resolve = False
elif o in ("-a", "--asn"): elif o in ("-a", "--asn"):
as_lookup = True as_lookup = True
elif o in ("-e", "--edns"):
use_edns = False
else: else:
usage() usage()
color = Colors(color_mode) 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 = dns.resolver.Resolver()
resolver.nameservers = [dnsserver] resolver.nameservers = [dnsserver]
resolver.timeout = timeout resolver.timeout = timeout
@ -286,7 +303,8 @@ def main():
trace_path = [] trace_path = []
if not quiet: if not quiet:
print("%s DNS: %s:%d, hostname: %s, rdatatype: %s" % (__PROGNAME__, dnsserver, dest_port, hostname, dnsrecord)) print("%s DNS: %s:%d, hostname: %s, rdatatype: %s" % (__PROGNAME__, dnsserver, dest_port, hostname, dnsrecord),
flush=True)
while True: while True:
if shutdown: if shutdown:
@ -310,26 +328,26 @@ def main():
curr_host = None curr_host = None
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool: # dispatch dns lookup to another thread with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool: # dispatch dns lookup to another thread
stime = time.time() stime = time.perf_counter()
thr = pool.submit(ping, resolver, hostname, dnsrecord, ttl) thr = pool.submit(ping, resolver, hostname, dnsrecord, ttl, use_edns=use_edns)
try: # expect ICMP response try: # expect ICMP response
_, curr_addr = icmp_socket.recvfrom(512) _, curr_addr = icmp_socket.recvfrom(512)
curr_addr = curr_addr[0] curr_addr = curr_addr[0]
except socket.error: except socket.error:
etime = time.time() etime = time.perf_counter()
pass pass
finally: finally:
etime = time.time() etime = time.perf_counter()
icmp_socket.close() icmp_socket.close()
reached = thr.result() reached = thr.result()
if reached: if reached:
curr_addr = dnsserver curr_addr = dnsserver
stime = time.time() # need to recalculate elapsed time for last hop without waiting for an icmp error reply stime = time.perf_counter() # need to recalculate elapsed time for last hop without waiting for an icmp error reply
ping(resolver, hostname, dnsrecord, ttl) ping(resolver, hostname, dnsrecord, ttl, use_edns=use_edns)
etime = time.time() etime = time.perf_counter()
elapsed = abs(etime - stime) * 1000 # convert to milliseconds elapsed = abs(etime - stime) * 1000 # convert to milliseconds
@ -369,7 +387,7 @@ def main():
if curr_addr == dnsserver: if curr_addr == dnsserver:
c = color.G c = color.G
print("%d\t%s (%s%s%s) %s%d ms" % (ttl, curr_name, c, curr_addr, color.N, as_name, elapsed), flush=True) 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) trace_path.append(curr_addr)
else: else:
print("%d\t *" % ttl, flush=True) print("%d\t *" % ttl, flush=True)

View File

@ -11,4 +11,5 @@
209.244.0.4 209.244.0.4
195.46.39.39 195.46.39.39
195.46.39.40 195.46.39.40
216.146.35.35
216.146.36.36

View File

@ -0,0 +1 @@
dnspython==1.15.0

13
rootservers.txt Normal file
View File

@ -0,0 +1,13 @@
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

View File

@ -2,7 +2,7 @@ from setuptools import setup, find_packages
setup( setup(
name="dnsdiag", name="dnsdiag",
version="1.3.5", version="1.6.0",
packages=find_packages(), packages=find_packages(),
classifiers=[ classifiers=[
"Topic :: System :: Networking", "Topic :: System :: Networking",