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 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] = 24
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 24)") { |o| options[:maxdepth] = o }
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
@@ -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] = @stats_resolve[ip].dup
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
- if (server) then
352
- process_normal(args)
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
- # XXX flatten really necessary?
359
- @processed = true
360
- return @children.values.flatten.select {|x| x.is_a? Referral}
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
- if @refid.scan(/\./).length >= @maxdepth.to_i then
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 => "#{@refid}.#{child_refid}",
421
- :refkey => "#{@refkey}.#{starters.count}"
422
- }.merge(args)
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
@@ -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
- p.call(:state => @state, :referral => refobj, :stage => stage)
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 the children
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
@@ -2,7 +2,7 @@ module DNSTraverse
2
2
  module Version
3
3
  MAJOR = 0
4
4
  MINOR = 1
5
- PATCH = 4
5
+ PATCH = 6
6
6
  STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
7
7
  end
8
8
  end
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: 19
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 4
10
- version: 0.1.4
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-08 00:00:00 +01:00
18
+ date: 2011-08-17 00:00:00 +01:00
19
19
  default_executable: dnstraverse
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency