dnstraverse 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/dnstraverse +10 -2
- data/lib/dnstraverse/decoded_query.rb +0 -1
- data/lib/dnstraverse/referral.rb +38 -16
- data/lib/dnstraverse/response.rb +6 -0
- data/lib/dnstraverse/summary_stats.rb +44 -0
- data/lib/dnstraverse/traverser.rb +13 -2
- data/lib/dnstraverse/version.rb +1 -1
- metadata +4 -4
data/bin/dnstraverse
CHANGED
@@ -118,9 +118,10 @@ options[:allstats] = false
|
|
118
118
|
options[:saveobjects] = false
|
119
119
|
options[:fast] = true
|
120
120
|
options[:udpsize] = 2048
|
121
|
-
options[:maxdepth] =
|
121
|
+
options[:maxdepth] = 10
|
122
122
|
options[:retries] = 2
|
123
123
|
options[:results] = true
|
124
|
+
options[:summary_results] = true
|
124
125
|
options[:quiet] = false
|
125
126
|
|
126
127
|
opts = OptionParser.new
|
@@ -134,7 +135,7 @@ opts.on("-t", "--type TYPE", Dnsruby::Types.constants,
|
|
134
135
|
opts.on("--udp-size SIZE", "UDP packet size (default 2048). Set to 512 to turn off EDNS0.") { |o| options[:udpsize] = o }
|
135
136
|
opts.on("--allow-tcp", "Try using tcp if udp truncated (default true)") { |o| options[:allow_tcp] = o }
|
136
137
|
opts.on("--always-tcp", "Always use tcp (default false)") { |o| options[:always_tcp] = o }
|
137
|
-
opts.on("--max-depth DEPTH", "Maximum traversal depth (default
|
138
|
+
opts.on("--max-depth DEPTH", "Maximum traversal depth (default 10)") { |o| options[:maxdepth] = o }
|
138
139
|
opts.on("--retries TIMES", "Number of 2s retries before timing out (default 2)") { |o| options[:retries] = o }
|
139
140
|
opts.on("--[no-]follow-aaaa", "Only follow AAAA records for referrals (default false)") { |o| options[:follow_aaaa] = o }
|
140
141
|
opts.on("--[no-]root-aaaa", "Look for IPv6 addresses for root servers (default false)") { |o| options[:root_aaaa] = o }
|
@@ -144,6 +145,7 @@ opts.on("--[no-]show-servers", "Display list of servers seen (default false)") {
|
|
144
145
|
opts.on("--[no-]show-versions", "Display versions of dns servers (default true)") { |o| options[:versions] = o }
|
145
146
|
opts.on("--[no-]show-all-stats", "Display statistics as we go (default false)") { |o| options[:allstats] = o }
|
146
147
|
opts.on("--[no-]show-results", "Display the results (default true)") { |o| options[:results] = o }
|
148
|
+
opts.on("--[no-]show-summary-results", "Display the summary results (default true)") { |o| options[:summary_results] = o }
|
147
149
|
opts.on("--save-objects") { |o| options[:saveobjects] = o }
|
148
150
|
opts.on("--[no-]fast", "Fast mode (default true) turn off to be more accurate" ) { |o| options[:fast] = o }
|
149
151
|
opts.on_tail("-h", "--help", "Show full help") { puts opts; exit }
|
@@ -240,6 +242,12 @@ begin
|
|
240
242
|
if options[:results] then
|
241
243
|
puts "Results:"
|
242
244
|
result.stats_display(:results => true, :spacing => true)
|
245
|
+
puts
|
246
|
+
end
|
247
|
+
if options[:summary_results] then
|
248
|
+
puts "Summary Results:"
|
249
|
+
print result.summary_stats.text
|
250
|
+
puts
|
243
251
|
end
|
244
252
|
rescue Interrupt => e
|
245
253
|
$stderr.puts "Interrupted by user"
|
@@ -32,7 +32,6 @@ module DNSTraverse
|
|
32
32
|
attr_reader :answers # :answered only
|
33
33
|
attr_reader :warnings # warnings about this query
|
34
34
|
attr_reader :authoritynames # auth_ns converted to array of strings
|
35
|
-
# attr_reader :stats_key
|
36
35
|
|
37
36
|
def initialize(args)
|
38
37
|
@message = args[:message] || nil # skip query - use this message/exception
|
data/lib/dnstraverse/referral.rb
CHANGED
@@ -298,7 +298,6 @@ module DNSTraverse
|
|
298
298
|
serverweight = @serverweights[ip] # set at initialize or at resolve
|
299
299
|
if ip =~ /^key:/ then # resolve failed for some reason
|
300
300
|
# pull out the statistics on the resolution and copy over
|
301
|
-
raise "duplicate key found" if @stats[ip] # assertion
|
302
301
|
if @stats_resolve[ip][:prob] != serverweight then # assertion
|
303
302
|
$stderr.puts "#{@stats_resolve[ip][:prob]} vs #{serverweight}"
|
304
303
|
@stats_resolve[ip].each_pair do |a,b|
|
@@ -306,13 +305,27 @@ module DNSTraverse
|
|
306
305
|
end
|
307
306
|
raise "unexpected probability"
|
308
307
|
end
|
309
|
-
@stats[ip]
|
308
|
+
if @stats[ip] then
|
309
|
+
# the same condition was found on another IP of this referral
|
310
|
+
# and we've already added this key before
|
311
|
+
# most likely this is an exception
|
312
|
+
@stats[ip][:prob]+= @stats_resolve[ip][:prob]
|
313
|
+
else
|
314
|
+
# copy over the resolve statistics to the final stats
|
315
|
+
@stats[ip] = @stats_resolve[ip].dup
|
316
|
+
end
|
310
317
|
next
|
311
318
|
end
|
312
319
|
if @children[ip] then
|
313
320
|
stats_calculate_children(@stats, @children[ip], serverweight)
|
314
321
|
else
|
315
322
|
response = @responses[ip]
|
323
|
+
prob = serverweight
|
324
|
+
if @stats[response.stats_key] then
|
325
|
+
# the same condition was found as a result of resolve stage and
|
326
|
+
# when we asked the server. most likely this is an exception.
|
327
|
+
prob+= @stats[response.stats_key][:prob]
|
328
|
+
end
|
316
329
|
@stats[response.stats_key] = { :prob => serverweight,
|
317
330
|
:response => response, :referral => self }
|
318
331
|
end
|
@@ -345,19 +358,23 @@ module DNSTraverse
|
|
345
358
|
@server.nil? ? true : false
|
346
359
|
end
|
347
360
|
|
361
|
+
# process this Referral object:
|
362
|
+
# query each IP in @serverips and create a Response object
|
363
|
+
# return an array of children
|
348
364
|
def process(args)
|
349
365
|
raise "This Referral object has already been processed" if processed?
|
350
366
|
raise "You need to resolve this Referral object" unless resolved?
|
351
|
-
|
352
|
-
|
353
|
-
else
|
367
|
+
@processed = true
|
368
|
+
unless (server) then
|
354
369
|
# special case - no server means start from the top with the roots
|
355
370
|
process_add_roots(args)
|
371
|
+
return @children.values.flatten
|
356
372
|
end
|
373
|
+
process_normal(args)
|
357
374
|
# return a set of Referral objects that need to be processed
|
358
|
-
#
|
359
|
-
|
360
|
-
return @children.
|
375
|
+
# this is just using @serverips for ordering the children properly
|
376
|
+
# because we numbered them all already
|
377
|
+
return @serverips.map {|ip| @children[ip] }.flatten.select {|x| x.is_a? Referral }
|
361
378
|
end
|
362
379
|
|
363
380
|
def process_add_roots(args)
|
@@ -377,11 +394,16 @@ module DNSTraverse
|
|
377
394
|
|
378
395
|
def process_normal(args)
|
379
396
|
Log.debug { "process " + self.to_s }
|
397
|
+
childsets = @serverips.reject {|ip| ip =~ /^key:/ }.count
|
398
|
+
childset = 0
|
380
399
|
for ip in @serverips do
|
400
|
+
childset+= 1
|
381
401
|
Log.debug { "Process normal #{ip}" }
|
382
402
|
next if ip =~ /^key:/ # resolve failed on something
|
383
403
|
m = nil
|
384
|
-
|
404
|
+
# resolves get an extra .0. so strip those out before counting
|
405
|
+
current_depth = @refid.split('.').select {|x| x != '0' }.length
|
406
|
+
if current_depth >= @maxdepth.to_i then
|
385
407
|
m = RuntimeError.new "Maxdepth #{@maxdepth} exceeded"
|
386
408
|
end
|
387
409
|
Log.debug { "Process normal #{ip} - making response" }
|
@@ -396,15 +418,15 @@ module DNSTraverse
|
|
396
418
|
case r.status
|
397
419
|
when :restart, :referral then
|
398
420
|
Log.debug { "Process normal #{ip} - making referrals" }
|
421
|
+
refid = childsets == 1 ? @refid : "#{@refid}.#{childset}"
|
422
|
+
refkey = childsets == 1 ? @refkey : "#{@refkey}.#{childsets}"
|
399
423
|
@children[ip] = make_referrals(:qname => r.endname,
|
400
424
|
:starters => r.starters,
|
401
425
|
:bailiwick => r.starters_bailiwick,
|
402
426
|
:infocache => r.infocache,
|
427
|
+
:refid => refid, :refkey => refkey,
|
403
428
|
:parent_ip => ip)
|
404
429
|
Log.debug { "Process normal #{ip} - done making referrals" }
|
405
|
-
# XXX shouldn't be any children unless referrals
|
406
|
-
#when :referral_lame then
|
407
|
-
#@children[ip] = RuntimeError.new "Improper or lame delegation"
|
408
430
|
end
|
409
431
|
end
|
410
432
|
end
|
@@ -414,12 +436,12 @@ module DNSTraverse
|
|
414
436
|
children = Array.new
|
415
437
|
child_refid = 1
|
416
438
|
for starter in starters do
|
417
|
-
refargs = {
|
439
|
+
refargs = args.merge({
|
418
440
|
:server => starter[:name],
|
419
441
|
:serverips => starter[:ips],
|
420
|
-
:refid => "#{
|
421
|
-
:refkey => "#{
|
422
|
-
}
|
442
|
+
:refid => "#{args[:refid]}.#{child_refid}",
|
443
|
+
:refkey => "#{args[:refkey]}.#{starters.count}"
|
444
|
+
})
|
423
445
|
children.push make_referral(refargs)
|
424
446
|
child_refid+= 1
|
425
447
|
end
|
data/lib/dnstraverse/response.rb
CHANGED
@@ -50,9 +50,15 @@ module DNSTraverse
|
|
50
50
|
super
|
51
51
|
end
|
52
52
|
|
53
|
+
# set the statistics key - this is used to decide when to merge statistics
|
54
|
+
# together. the same key = merge, different key = keep separate
|
55
|
+
# this is why exception name/message is added for exception types
|
53
56
|
def update_stats_key
|
54
57
|
r = @decoded_query
|
55
58
|
@stats_key = "key:#{r.ip}:#{@server}:#{@status}:#{r.qname}:#{r.qclass}:#{r.qtype}"
|
59
|
+
if @stats == :exception and r.message.is_a? Exception then
|
60
|
+
@stats_key+= ":#{r.message}"
|
61
|
+
end
|
56
62
|
end
|
57
63
|
|
58
64
|
# clean up the workings
|
@@ -66,6 +66,44 @@ module DNSTraverse
|
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
+
def text(args = {})
|
70
|
+
prefix = args[:prefix] || ' '
|
71
|
+
indent = args[:indent] || "#{prefix} "
|
72
|
+
o = ''
|
73
|
+
each_summary do |type, sinfo|
|
74
|
+
if type == :answered then
|
75
|
+
each_answer do |prob, records|
|
76
|
+
initial = "#{prefix}#{txt_prob prob} answered with "
|
77
|
+
rr_prefix = "\n" + (' ' * initial.length)
|
78
|
+
o << initial
|
79
|
+
o << records.map {|x| x.to_s.gsub(/\s+/, ' ') }.join(rr_prefix) + "\n"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
each_summary do |type, sinfo|
|
84
|
+
if type != :answered then
|
85
|
+
case type
|
86
|
+
when :nodata
|
87
|
+
o << "#{prefix}#{txt_prob sinfo[:prob]} found no such record"
|
88
|
+
when :lame_referral
|
89
|
+
o << "#{prefix}#{txt_prob sinfo[:prob]} resulted in a lame referral"
|
90
|
+
when :exception
|
91
|
+
o << "#{prefix}#{txt_prob sinfo[:prob]} resulted in an exception"
|
92
|
+
when :error
|
93
|
+
o << "#{prefix}#{txt_prob sinfo[:prob]} resulted in an error"
|
94
|
+
when :noglue
|
95
|
+
o << "#{prefix}#{txt_prob sinfo[:prob]} found no glue"
|
96
|
+
when :loop
|
97
|
+
o << "#{prefix}#{txt_prob sinfo[:prob]} resulted in a loop"
|
98
|
+
else
|
99
|
+
o << "#{prefix}#{txt_prob sinfo[:prob]} #{type}"
|
100
|
+
end
|
101
|
+
o << "\n"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
return o
|
105
|
+
end
|
106
|
+
|
69
107
|
private
|
70
108
|
def get_summary_stats(referral)
|
71
109
|
s = {}
|
@@ -90,5 +128,11 @@ module DNSTraverse
|
|
90
128
|
end
|
91
129
|
return a
|
92
130
|
end
|
131
|
+
|
132
|
+
def txt_prob(prob)
|
133
|
+
t = sprintf("%.1f%%", prob * 100).sub(/\.0%/, '%')
|
134
|
+
return sprintf("%5s", t) # 99.9%
|
135
|
+
end
|
136
|
+
|
93
137
|
end
|
94
138
|
end
|
@@ -74,7 +74,8 @@ module DNSTraverse
|
|
74
74
|
stage = opts[:stage] or raise "must pass option :stage"
|
75
75
|
refres = refobj.referral_resolution?
|
76
76
|
p = (refres == true ? @progress_resolve : @progress_main)
|
77
|
-
|
77
|
+
newopts = opts.merge({:state => @state, :referral => refobj})
|
78
|
+
p.call(newopts)
|
78
79
|
end
|
79
80
|
|
80
81
|
### change to get_all or something?
|
@@ -197,11 +198,21 @@ module DNSTraverse
|
|
197
198
|
end
|
198
199
|
# put a placeholder on the stack
|
199
200
|
stack << r << :calc_answer
|
200
|
-
# get
|
201
|
+
# get children, one set per IP address of this name server in array
|
201
202
|
children = r.process({})
|
202
203
|
# now report progress. we already can tell whether this will be
|
203
204
|
# completed in fast mode or not, so we report this information
|
205
|
+
total_sets = children.map { |c| c.parent_ip }.uniq.count
|
206
|
+
# if there is more than one set (i.e. a DNS server has more than one
|
207
|
+
# IP address and we had to do multiple queries), the children will
|
208
|
+
# been numbered with an extra set digit, and we want to report this to
|
209
|
+
# the user interface
|
210
|
+
seen_parent_ip = Hash.new
|
204
211
|
children.each do |c|
|
212
|
+
if total_sets > 1 and not seen_parent_ip.include?(c.parent_ip) then
|
213
|
+
report_progress c, :stage => :new_referral_set
|
214
|
+
seen_parent_ip[c.parent_ip] = true
|
215
|
+
end
|
205
216
|
if @fast
|
206
217
|
key = "#{c.qname}:#{c.qclass}:#{c.qtype}:#{c.server}:#{c.txt_ips_verbose}".downcase!
|
207
218
|
stage = @answered.key?(key) ? :new_fast : :new
|
data/lib/dnstraverse/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dnstraverse
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 6
|
10
|
+
version: 0.1.6
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- James Ponder
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-08-
|
18
|
+
date: 2011-08-17 00:00:00 +01:00
|
19
19
|
default_executable: dnstraverse
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|