dnstraverse 0.1.4 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|