dnstraverse 0.1.0 → 0.1.1
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 +11 -2
- data/lib/dnstraverse/decoded_query.rb +3 -2
- data/lib/dnstraverse/referral.rb +45 -25
- data/lib/dnstraverse/response.rb +3 -1
- data/lib/dnstraverse/response_loop.rb +54 -0
- data/lib/dnstraverse/traverser.rb +31 -34
- data/lib/dnstraverse/version.rb +1 -1
- metadata +5 -4
data/bin/dnstraverse
CHANGED
@@ -113,7 +113,7 @@ options[:domainname] = nil
|
|
113
113
|
options[:follow_aaaa] = false
|
114
114
|
options[:root_aaaa] = false
|
115
115
|
options[:always_tcp] = false
|
116
|
-
options[:allow_tcp] =
|
116
|
+
options[:allow_tcp] = true
|
117
117
|
options[:allstats] = false
|
118
118
|
options[:saveobjects] = false
|
119
119
|
options[:fast] = true
|
@@ -121,6 +121,7 @@ options[:udpsize] = 2048
|
|
121
121
|
options[:maxdepth] = 24
|
122
122
|
options[:retries] = 2
|
123
123
|
options[:results] = true
|
124
|
+
options[:quiet] = false
|
124
125
|
|
125
126
|
opts = OptionParser.new
|
126
127
|
opts.banner = "Usage: #{File.basename($0)} [options] DOMAIN"
|
@@ -146,6 +147,7 @@ opts.on("--[no-]show-results", "Display the results (default true)") { |o| optio
|
|
146
147
|
opts.on("--save-objects") { |o| options[:saveobjects] = o }
|
147
148
|
opts.on("--[no-]fast", "Fast mode (default true) turn off to be more accurate" ) { |o| options[:fast] = o }
|
148
149
|
opts.on_tail("-h", "--help", "Show full help") { puts opts; exit }
|
150
|
+
opts.on_tail("-q", "--quiet", "Supress supplementary information") { options[:quiet] = true }
|
149
151
|
opts.on_tail("-V", "--version", "Show version") { puts DNSTraverse::Version::STRING; exit }
|
150
152
|
begin
|
151
153
|
opts.parse!
|
@@ -169,8 +171,15 @@ args[:loglevel] = options[:debug] >= 1 ? Logger::DEBUG : Logger::UNKNOWN
|
|
169
171
|
args[:libloglevel] = options[:debug] >= 2 ? Logger::DEBUG : Logger::UNKNOWN
|
170
172
|
args[:always_tcp] = true if options[:always_tcp]
|
171
173
|
args[:allow_tcp] = true if options[:allow_tcp]
|
172
|
-
args[:fast] =
|
174
|
+
args[:fast] = options[:fast]
|
173
175
|
begin
|
176
|
+
unless options[:quiet] then
|
177
|
+
puts "# Using fast mode" if options[:fast]
|
178
|
+
puts "# Limiting traverse to one root" unless options[:allroots]
|
179
|
+
puts "# UDP size #{options[:udpsize]} (EDNS0 is #{options[:udpsize == 512] ? "off" : "on"})"
|
180
|
+
puts "# Retries #{options[:retries]}, max depth #{options[:maxdepth]}"
|
181
|
+
puts "# Allow TCP is #{options[:allow_tcp]}, always TCP is #{options[:always_tcp]}"
|
182
|
+
end
|
174
183
|
traverser = DNSTraverse::Traverser.new(args)
|
175
184
|
if options[:root] then
|
176
185
|
root = options[:root]
|
@@ -51,6 +51,7 @@ module DNSTraverse
|
|
51
51
|
@warnings = nil # Warnings if there are any (array)
|
52
52
|
query unless @message
|
53
53
|
process
|
54
|
+
Log.debug { "Query to #{@ip} for #{@qname}/#{@qclass}/#{@qtype} decoded status=#{@status}" }
|
54
55
|
return self
|
55
56
|
end
|
56
57
|
|
@@ -155,9 +156,9 @@ module DNSTraverse
|
|
155
156
|
@error_message = "Server failure (SERVFAIL)"
|
156
157
|
when Dnsruby::RCode::NXDOMAIN
|
157
158
|
@error_message = "No such domain (NXDOMAIN)"
|
158
|
-
when NOTIMP
|
159
|
+
when Dnsruby::RCode.NOTIMP
|
159
160
|
@error_message = "Not implemented (NOTIMP)"
|
160
|
-
when REFUSED
|
161
|
+
when Dnsruby::RCode.REFUSED
|
161
162
|
@error_message = "Refused"
|
162
163
|
else
|
163
164
|
@error_message = @message.rcode.to_s
|
data/lib/dnstraverse/referral.rb
CHANGED
@@ -15,6 +15,7 @@
|
|
15
15
|
|
16
16
|
require 'dnstraverse/response'
|
17
17
|
require 'dnstraverse/response_noglue'
|
18
|
+
require 'dnstraverse/response_loop'
|
18
19
|
require 'dnstraverse/info_cache'
|
19
20
|
require 'dnstraverse/decoded_query_cache'
|
20
21
|
require 'dnstraverse/summary_stats'
|
@@ -164,6 +165,26 @@ module DNSTraverse
|
|
164
165
|
return true
|
165
166
|
end
|
166
167
|
|
168
|
+
# look out for endless loops
|
169
|
+
# e.g. while looking for a.b we get b NS c.d
|
170
|
+
# and while looking for c.d we get d NS a.b
|
171
|
+
# which would take us back to b NS c.d
|
172
|
+
def loop?
|
173
|
+
return false if @serverips
|
174
|
+
parent = @parent
|
175
|
+
until parent.nil? do
|
176
|
+
if parent.qname.to_s == @qname.to_s and
|
177
|
+
parent.qclass.to_s == @qclass.to_s and
|
178
|
+
parent.qtype.to_s == @qtype.to_s and
|
179
|
+
parent.server == @server and
|
180
|
+
parent.serverips.nil?
|
181
|
+
return true
|
182
|
+
end
|
183
|
+
parent = parent.parent
|
184
|
+
end
|
185
|
+
return false
|
186
|
+
end
|
187
|
+
|
167
188
|
# resolve server to serverips, return list of Referral objects to process
|
168
189
|
def resolve(*args)
|
169
190
|
raise "This Referral object has already been resolved" if resolved?
|
@@ -173,6 +194,11 @@ module DNSTraverse
|
|
173
194
|
" of #{bailiwick} - no glue record provided" }
|
174
195
|
return Array.new
|
175
196
|
end
|
197
|
+
if loop? then
|
198
|
+
# b IN NS c.d, d IN NS a.b
|
199
|
+
Log.debug { "Loop reached at server #{server}" }
|
200
|
+
return Array.new
|
201
|
+
end
|
176
202
|
child_refid = 1
|
177
203
|
starters, newbailiwick = @infocache.get_startservers(@server)
|
178
204
|
Log.debug { "Resolving #{@server} type #{@nsatype} " }
|
@@ -205,6 +231,15 @@ module DNSTraverse
|
|
205
231
|
:bailiwick => @bailiwick)
|
206
232
|
@stats_resolve[r.stats_key] = { :prob => 1.0, :response => r,
|
207
233
|
:referral => self }
|
234
|
+
elsif loop? then # endless loop, e.g. b. NS c.d, d NS a.b
|
235
|
+
r = DNSTraverse::Response::Loop.new(:qname => @qname,
|
236
|
+
:qclass => @qclass,
|
237
|
+
:qtype => @qtype,
|
238
|
+
:server => @server,
|
239
|
+
:ip => @parent_ip,
|
240
|
+
:bailiwick => @bailiwick)
|
241
|
+
@stats_resolve[r.stats_key] = { :prob => 1.0, :response => r,
|
242
|
+
:referral => self }
|
208
243
|
else
|
209
244
|
# normal resolve - combine children's statistics in to @stats_resolve
|
210
245
|
stats_calculate_children(@stats_resolve, @resolves, 1.0)
|
@@ -339,31 +374,8 @@ module DNSTraverse
|
|
339
374
|
end
|
340
375
|
end
|
341
376
|
|
342
|
-
def check_loop?(args) # :ip, :qtype, :qname, :qclass
|
343
|
-
parent = @parent
|
344
|
-
until parent.nil? do
|
345
|
-
if parent.qname.to_s == args[:qname].to_s and
|
346
|
-
parent.qclass.to_s == args[:qclass].to_s and
|
347
|
-
parent.qtype.to_s == args[:qtype].to_s and
|
348
|
-
parent.serverips and parent.serverips.include?(args[:ip]) then
|
349
|
-
exit 1 # XXX fix me
|
350
|
-
return RuntimeError.new("Loop detected")
|
351
|
-
end
|
352
|
-
parent = parent.parent
|
353
|
-
end
|
354
|
-
return nil
|
355
|
-
end
|
356
|
-
|
357
377
|
def process_normal(args)
|
358
378
|
Log.debug { "process " + self.to_s }
|
359
|
-
# if l = check_loop?(:ip => ip, :qname => @qname,
|
360
|
-
# :qtype => @qtype, :qclass => @qclass) then
|
361
|
-
# for ip in @serverips do
|
362
|
-
# @responses[ip] = l
|
363
|
-
# done
|
364
|
-
# return
|
365
|
-
# end
|
366
|
-
# end
|
367
379
|
for ip in @serverips do
|
368
380
|
Log.debug { "Process normal #{ip}" }
|
369
381
|
next if ip =~ /^key:/ # resolve failed on something
|
@@ -376,6 +388,7 @@ module DNSTraverse
|
|
376
388
|
:qclass => @qclass, :qtype => @qtype,
|
377
389
|
:bailiwick => @bailiwick,
|
378
390
|
:infocache => @infocache, :ip => ip,
|
391
|
+
:server => @server,
|
379
392
|
:decoded_query_cache => @decoded_query_cache)
|
380
393
|
Log.debug { "Process normal #{ip} - done making response" }
|
381
394
|
@responses[ip] = r
|
@@ -404,7 +417,7 @@ module DNSTraverse
|
|
404
417
|
:server => starter[:name],
|
405
418
|
:serverips => starter[:ips],
|
406
419
|
:refid => "#{@refid}.#{child_refid}",
|
407
|
-
|
420
|
+
:refkey => "#{@refkey}.#{starters.count}"
|
408
421
|
}.merge(args)
|
409
422
|
children.push make_referral(refargs)
|
410
423
|
child_refid+= 1
|
@@ -427,6 +440,9 @@ module DNSTraverse
|
|
427
440
|
@children.each_key do | ip |
|
428
441
|
@children[ip].map! { |c| c.equal?(before) ? after : c }
|
429
442
|
end
|
443
|
+
if @resolves then
|
444
|
+
@resolves.map! { |c| c.equal?(before) ? after : c }
|
445
|
+
end
|
430
446
|
end
|
431
447
|
|
432
448
|
def stats_display(args)
|
@@ -448,7 +464,11 @@ module DNSTraverse
|
|
448
464
|
puts "#{response.exception_message} at #{where}"
|
449
465
|
when :noglue
|
450
466
|
puts "No glue at #{referral.parent.server} " +
|
451
|
-
"(#{response.ip}) for #{
|
467
|
+
"(#{response.ip}) for #{referral.server}"
|
468
|
+
when :referral_lame
|
469
|
+
puts "Lame referral from #{where}"
|
470
|
+
when :loop
|
471
|
+
puts "Loop encountered at #{response.server} "
|
452
472
|
when :error
|
453
473
|
puts "#{response.error_message} at #{where}"
|
454
474
|
when :nodata
|
data/lib/dnstraverse/response.rb
CHANGED
@@ -26,6 +26,7 @@ module DNSTraverse
|
|
26
26
|
attr_reader :status # our status, expanding on DecodedQuery status
|
27
27
|
attr_reader :starters, :starters_bailiwick # :referral/:restart only
|
28
28
|
attr_reader :stats_key
|
29
|
+
attr_reader :server
|
29
30
|
|
30
31
|
# :qname, :qclass, :qtype, :ip, :bailiwick, optional :message
|
31
32
|
def initialize(args)
|
@@ -33,6 +34,7 @@ module DNSTraverse
|
|
33
34
|
:qtype => args[:qtype], :ip => args[:ip],
|
34
35
|
:bailiwick => args[:bailiwick], :message => args[:message] }
|
35
36
|
@decoded_query = args[:decoded_query_cache].query(dqc_args)
|
37
|
+
@server = args[:server]
|
36
38
|
@infocache = InfoCache.new(args[:infocache]) # our infocache
|
37
39
|
@starters = nil # initial servers for :referral/:restart
|
38
40
|
@starters_bailiwick = nil # for initial servers for :referral/:restart
|
@@ -50,7 +52,7 @@ module DNSTraverse
|
|
50
52
|
|
51
53
|
def update_stats_key
|
52
54
|
r = @decoded_query
|
53
|
-
@stats_key = "key:#{r.ip}:#{@status}:#{r.qname}:#{r.qclass}:#{r.qtype}"
|
55
|
+
@stats_key = "key:#{r.ip}:#{@server}:#{@status}:#{r.qname}:#{r.qclass}:#{r.qtype}"
|
54
56
|
end
|
55
57
|
|
56
58
|
# clean up the workings
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# DNSTraverse traverses the DNS to show statistics and information
|
2
|
+
# Copyright (C) 2008 James Ponder
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, version 3 of the License.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
module DNSTraverse
|
17
|
+
|
18
|
+
class Response::Loop < Response
|
19
|
+
|
20
|
+
attr_reader :qname, :qclass, :qtype, :ip, :bailiwick, :server
|
21
|
+
|
22
|
+
def initialize(args)
|
23
|
+
# we queried @ip about @qname/@qclass/@qtype and received @server as a
|
24
|
+
# referral in bailiwick @bailiwick but without any glue
|
25
|
+
@qname = args[:qname]
|
26
|
+
@qclass = args[:qclass]
|
27
|
+
@qtype = args[:qtype]
|
28
|
+
@bailiwick = args[:bailiwick]
|
29
|
+
@ip = args[:ip]
|
30
|
+
@server = args[:server]
|
31
|
+
@decoded_query = nil
|
32
|
+
@infocache = nil
|
33
|
+
@starters = nil
|
34
|
+
@starters_bailiwick = nil
|
35
|
+
@status = :loop
|
36
|
+
update_stats_key
|
37
|
+
return self
|
38
|
+
end
|
39
|
+
|
40
|
+
def method_missing(key, *args, &block)
|
41
|
+
super # there are no missing methods, we answer directly
|
42
|
+
end
|
43
|
+
|
44
|
+
def update_stats_key
|
45
|
+
@stats_key = "key:#{@ip}:#{@status}:#{@qname}:#{@qclass}:#{@qtype}:#{@server}:#{@bailiwick}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
return "Loop encountered resolving #{@server}"
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -158,8 +158,7 @@ module DNSTraverse
|
|
158
158
|
r.cleanup(cleanup)
|
159
159
|
if @fast then
|
160
160
|
# store away in @answered hash so we can lookup later
|
161
|
-
key = "#{r.qname}:#{r.qclass}:#{r.qtype}:#{r.server}:#{r.txt_ips_verbose}"
|
162
|
-
key.downcase!
|
161
|
+
key = "#{r.qname}:#{r.qclass}:#{r.qtype}:#{r.server}:#{r.txt_ips_verbose}".downcase!
|
163
162
|
Log.debug { "Fast mode cache store: #{key}" }
|
164
163
|
@answered[key] = r
|
165
164
|
end
|
@@ -169,48 +168,46 @@ module DNSTraverse
|
|
169
168
|
@seen[r.server.downcase].uniq!
|
170
169
|
end
|
171
170
|
next
|
172
|
-
else
|
173
|
-
report_progress r, :stage => :start
|
174
171
|
end
|
172
|
+
# ok time to process a new item
|
173
|
+
if @fast then
|
174
|
+
Log.debug { "Checking #{r} for already completed earlier" }
|
175
|
+
key = "#{r.qname}:#{r.qclass}:#{r.qtype}:#{r.server}:#{r.txt_ips_verbose}".downcase!
|
176
|
+
Log.debug { "Fast mode cache lookup: #{key}" }
|
177
|
+
# check for previously stored answer
|
178
|
+
# special case noglue situation, don't use previous answer
|
179
|
+
# because attributes are complicated for stats collection and
|
180
|
+
# we don't want to merge them together - creating the noglue
|
181
|
+
# response object is fast anyway
|
182
|
+
if @answered.key?(key) and (not r.noglue?) then
|
183
|
+
Log.debug { "Fast method - completed #{r}" }
|
184
|
+
r.parent.replace_child(r, @answered[key])
|
185
|
+
report_progress r, :stage => :answer_fast
|
186
|
+
next
|
187
|
+
end
|
188
|
+
end
|
189
|
+
report_progress r, :stage => :start
|
175
190
|
unless r.resolved? then
|
176
191
|
# get resolve Referral objects, place on stack with placeholder
|
177
192
|
stack << r << :calc_resolve
|
178
193
|
referrals = r.resolve({})
|
179
194
|
referrals.each { |c| report_progress c, :stage => :new }
|
180
|
-
stack.push(*referrals.reverse)
|
195
|
+
stack.push(*referrals.reverse)
|
181
196
|
next
|
182
197
|
end
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
if @fast
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
key.downcase!
|
193
|
-
Log.debug { "Fast mode cache lookup: #{key}" }
|
194
|
-
# check for previously stored answer
|
195
|
-
# special case noglue situation, don't use previous answer
|
196
|
-
# because attributes are complicated for stats collection and
|
197
|
-
# we don't want to merge them together - creating the noglue
|
198
|
-
# response object is fast anyway
|
199
|
-
if @answered.key?(key) and (not c.noglue?) then
|
200
|
-
Log.debug { "Fast method - completed #{c}" }
|
201
|
-
r.replace_child(c, @answered[key])
|
202
|
-
report_progress c, :stage => :answer_fast
|
203
|
-
else
|
204
|
-
newchildren << c
|
205
|
-
end
|
206
|
-
end
|
207
|
-
children = newchildren
|
198
|
+
# get Referral objects, place on stack with placeholder
|
199
|
+
stack << r << :calc_answer
|
200
|
+
children = r.process({})
|
201
|
+
children.each do |c|
|
202
|
+
if @fast
|
203
|
+
key = "#{c.qname}:#{c.qclass}:#{c.qtype}:#{c.server}:#{c.txt_ips_verbose}".downcase!
|
204
|
+
stage = @answered.key?(key) ? :new_fast : :new
|
205
|
+
else
|
206
|
+
stage = :new
|
208
207
|
end
|
209
|
-
|
210
|
-
stack.push(*children.reverse)
|
211
|
-
next
|
208
|
+
report_progress c, :stage => stage
|
212
209
|
end
|
213
|
-
|
210
|
+
stack.push(*children.reverse)
|
214
211
|
end
|
215
212
|
end
|
216
213
|
|
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: 25
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 1
|
10
|
+
version: 0.1.1
|
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-06-
|
18
|
+
date: 2011-06-10 00:00:00 +01:00
|
19
19
|
default_executable: dnstraverse
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -106,6 +106,7 @@ files:
|
|
106
106
|
- lib/dnstraverse/message_utility.rb
|
107
107
|
- lib/dnstraverse/referral.rb
|
108
108
|
- lib/dnstraverse/response.rb
|
109
|
+
- lib/dnstraverse/response_loop.rb
|
109
110
|
- lib/dnstraverse/response_noglue.rb
|
110
111
|
- lib/dnstraverse/summary_stats.rb
|
111
112
|
- lib/dnstraverse/traverser.rb
|