User can specify source address (Fixes #46)

- also respect resolver port number (if specified by user)
This commit is contained in:
Babak Farrokhi 2018-01-23 12:13:50 +03:30
parent 1b9849c224
commit e3ddfff88e
Signed by: farrokhi
GPG Key ID: 6B267AD85D632E9A

View File

@ -133,6 +133,7 @@ def usage():
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(' -S --srcip Query source IP address (default: default interface address)')
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: 2)') 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)')
@ -182,7 +183,7 @@ 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, use_edns=False): def ping(resolver, hostname, dnsrecord, ttl, src_ip, use_edns=False):
global _ttl global _ttl
reached = False reached = False
@ -193,7 +194,7 @@ def ping(resolver, hostname, dnsrecord, ttl, use_edns=False):
resolver.use_edns(edns=0, payload=8192, ednsflags=dns.flags.edns_from_text('DO')) resolver.use_edns(edns=0, payload=8192, ednsflags=dns.flags.edns_from_text('DO'))
try: try:
resolver.query(hostname, dnsrecord, raise_on_no_answer=False) resolver.query(hostname, dnsrecord, source=src_ip, raise_on_no_answer=False)
except dns.resolver.NoNameservers as e: except dns.resolver.NoNameservers as e:
if not quiet: if not quiet:
@ -237,6 +238,7 @@ def main():
timeout = 2 timeout = 2
dnsserver = dns.resolver.get_default_resolver().nameservers[0] dnsserver = dns.resolver.get_default_resolver().nameservers[0]
dest_port = 53 dest_port = 53
src_ip = None
hops = 0 hops = 0
as_lookup = False as_lookup = False
expert_mode = False expert_mode = False
@ -245,9 +247,9 @@ def main():
color_mode = False color_mode = False
try: try:
opts, args = getopt.getopt(sys.argv[1:], "aqhc:s:t:w:p:nexC", opts, args = getopt.getopt(sys.argv[1:], "aqhc:s: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", "srcip="])
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"
@ -269,6 +271,8 @@ def main():
dnsserver = a dnsserver = a
elif o in ("-q", "--quiet"): elif o in ("-q", "--quiet"):
quiet = True quiet = True
elif o in ("-S", "--srcip"):
src_ip = a
elif o in ("-w", "--wait"): elif o in ("-w", "--wait"):
timeout = int(a) timeout = int(a)
elif o in ("-t", "--type"): elif o in ("-t", "--type"):
@ -301,6 +305,7 @@ def main():
resolver = dns.resolver.Resolver() resolver = dns.resolver.Resolver()
resolver.nameservers = [dnsserver] resolver.nameservers = [dnsserver]
resolver.timeout = timeout resolver.timeout = timeout
resolver.port = dest_port
resolver.lifetime = timeout resolver.lifetime = timeout
resolver.retry_servfail = 0 resolver.retry_servfail = 0
@ -337,7 +342,7 @@ def main():
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.perf_counter() stime = time.perf_counter()
thr = pool.submit(ping, resolver, hostname, dnsrecord, ttl, use_edns=use_edns) thr = pool.submit(ping, resolver, hostname, dnsrecord, ttl, src_ip=src_ip, use_edns=use_edns)
try: # expect ICMP response try: # expect ICMP response
_, curr_addr = icmp_socket.recvfrom(512) _, curr_addr = icmp_socket.recvfrom(512)
@ -353,8 +358,8 @@ def main():
if reached: if reached:
curr_addr = dnsserver curr_addr = dnsserver
stime = time.perf_counter() # 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 asynchronously
ping(resolver, hostname, dnsrecord, ttl, use_edns=use_edns) ping(resolver, hostname, dnsrecord, ttl, src_ip=src_ip, use_edns=use_edns)
etime = time.perf_counter() etime = time.perf_counter()
elapsed = abs(etime - stime) * 1000 # convert to milliseconds elapsed = abs(etime - stime) * 1000 # convert to milliseconds
@ -387,13 +392,16 @@ def main():
c = color.N # default c = color.N # default
if curr_addr != '*': if curr_addr != '*':
IP = ipaddress.ip_address(curr_addr) try:
if IP.is_private: IP = ipaddress.ip_address(curr_addr)
c = color.R if IP.is_private:
if IP.is_reserved: c = color.R
c = color.B if IP.is_reserved:
if curr_addr == dnsserver: c = color.B
c = color.G 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) 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)