dnstraverse 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +674 -0
- data/README +4 -0
- data/Rakefile +19 -0
- data/a +44 -0
- data/bin/dnstraverse +300 -0
- data/doc.tar +0 -0
- data/doc/classes/DNSTraverse.html +198 -0
- data/doc/classes/DNSTraverse/CachingResolver.html +172 -0
- data/doc/classes/DNSTraverse/CachingResolver.src/M000055.html +21 -0
- data/doc/classes/DNSTraverse/CachingResolver.src/M000056.html +42 -0
- data/doc/classes/DNSTraverse/DecodedQuery.html +475 -0
- data/doc/classes/DNSTraverse/DecodedQuery.src/M000020.html +34 -0
- data/doc/classes/DNSTraverse/DecodedQuery.src/M000021.html +23 -0
- data/doc/classes/DNSTraverse/DecodedQuery.src/M000022.html +19 -0
- data/doc/classes/DNSTraverse/DecodedQuery.src/M000023.html +33 -0
- data/doc/classes/DNSTraverse/DecodedQuery.src/M000024.html +26 -0
- data/doc/classes/DNSTraverse/DecodedQuery.src/M000025.html +24 -0
- data/doc/classes/DNSTraverse/DecodedQuery.src/M000026.html +29 -0
- data/doc/classes/DNSTraverse/DecodedQuery.src/M000027.html +19 -0
- data/doc/classes/DNSTraverse/DecodedQuery.src/M000028.html +18 -0
- data/doc/classes/DNSTraverse/DecodedQuery.src/M000029.html +32 -0
- data/doc/classes/DNSTraverse/DecodedQuery.src/M000030.html +19 -0
- data/doc/classes/DNSTraverse/DecodedQuery.src/M000031.html +19 -0
- data/doc/classes/DNSTraverse/DecodedQuery.src/M000032.html +20 -0
- data/doc/classes/DNSTraverse/DecodedQuery.src/M000033.html +31 -0
- data/doc/classes/DNSTraverse/DecodedQueryCache.html +182 -0
- data/doc/classes/DNSTraverse/DecodedQueryCache.src/M000063.html +21 -0
- data/doc/classes/DNSTraverse/DecodedQueryCache.src/M000064.html +33 -0
- data/doc/classes/DNSTraverse/Fingerprint.html +277 -0
- data/doc/classes/DNSTraverse/Fingerprint.src/M000047.html +29 -0
- data/doc/classes/DNSTraverse/Fingerprint.src/M000048.html +18 -0
- data/doc/classes/DNSTraverse/Fingerprint.src/M000049.html +57 -0
- data/doc/classes/DNSTraverse/Fingerprint.src/M000050.html +28 -0
- data/doc/classes/DNSTraverse/Fingerprint.src/M000051.html +27 -0
- data/doc/classes/DNSTraverse/Fingerprint.src/M000052.html +35 -0
- data/doc/classes/DNSTraverse/Fingerprint.src/M000053.html +24 -0
- data/doc/classes/DNSTraverse/Fingerprint.src/M000054.html +29 -0
- data/doc/classes/DNSTraverse/InfoCache.html +235 -0
- data/doc/classes/DNSTraverse/InfoCache.src/M000034.html +20 -0
- data/doc/classes/DNSTraverse/InfoCache.src/M000035.html +23 -0
- data/doc/classes/DNSTraverse/InfoCache.src/M000036.html +28 -0
- data/doc/classes/DNSTraverse/InfoCache.src/M000037.html +25 -0
- data/doc/classes/DNSTraverse/InfoCache.src/M000038.html +27 -0
- data/doc/classes/DNSTraverse/InfoCache.src/M000039.html +33 -0
- data/doc/classes/DNSTraverse/MessageUtility.html +275 -0
- data/doc/classes/DNSTraverse/MessageUtility.src/M000011.html +35 -0
- data/doc/classes/DNSTraverse/MessageUtility.src/M000012.html +37 -0
- data/doc/classes/DNSTraverse/MessageUtility.src/M000013.html +28 -0
- data/doc/classes/DNSTraverse/MessageUtility.src/M000014.html +27 -0
- data/doc/classes/DNSTraverse/MessageUtility.src/M000015.html +30 -0
- data/doc/classes/DNSTraverse/MessageUtility.src/M000016.html +32 -0
- data/doc/classes/DNSTraverse/MessageUtility.src/M000017.html +34 -0
- data/doc/classes/DNSTraverse/MessageUtility.src/M000018.html +23 -0
- data/doc/classes/DNSTraverse/MessageUtility.src/M000019.html +36 -0
- data/doc/classes/DNSTraverse/Referral.html +641 -0
- data/doc/classes/DNSTraverse/Referral.src/M000065.html +22 -0
- data/doc/classes/DNSTraverse/Referral.src/M000066.html +21 -0
- data/doc/classes/DNSTraverse/Referral.src/M000067.html +23 -0
- data/doc/classes/DNSTraverse/Referral.src/M000068.html +19 -0
- data/doc/classes/DNSTraverse/Referral.src/M000069.html +18 -0
- data/doc/classes/DNSTraverse/Referral.src/M000070.html +54 -0
- data/doc/classes/DNSTraverse/Referral.src/M000071.html +25 -0
- data/doc/classes/DNSTraverse/Referral.src/M000072.html +27 -0
- data/doc/classes/DNSTraverse/Referral.src/M000073.html +23 -0
- data/doc/classes/DNSTraverse/Referral.src/M000074.html +20 -0
- data/doc/classes/DNSTraverse/Referral.src/M000075.html +40 -0
- data/doc/classes/DNSTraverse/Referral.src/M000076.html +52 -0
- data/doc/classes/DNSTraverse/Referral.src/M000077.html +29 -0
- data/doc/classes/DNSTraverse/Referral.src/M000078.html +54 -0
- data/doc/classes/DNSTraverse/Referral.src/M000079.html +18 -0
- data/doc/classes/DNSTraverse/Referral.src/M000080.html +22 -0
- data/doc/classes/DNSTraverse/Referral.src/M000081.html +20 -0
- data/doc/classes/DNSTraverse/Referral.src/M000082.html +29 -0
- data/doc/classes/DNSTraverse/Referral.src/M000083.html +28 -0
- data/doc/classes/DNSTraverse/Referral.src/M000084.html +29 -0
- data/doc/classes/DNSTraverse/Referral.src/M000085.html +55 -0
- data/doc/classes/DNSTraverse/Referral.src/M000086.html +30 -0
- data/doc/classes/DNSTraverse/Referral.src/M000087.html +24 -0
- data/doc/classes/DNSTraverse/Referral.src/M000088.html +20 -0
- data/doc/classes/DNSTraverse/Referral.src/M000089.html +58 -0
- data/doc/classes/DNSTraverse/ResolveError.html +111 -0
- data/doc/classes/DNSTraverse/Response.html +271 -0
- data/doc/classes/DNSTraverse/Response.src/M000057.html +27 -0
- data/doc/classes/DNSTraverse/Response.src/M000058.html +21 -0
- data/doc/classes/DNSTraverse/Response.src/M000059.html +19 -0
- data/doc/classes/DNSTraverse/Response.src/M000060.html +21 -0
- data/doc/classes/DNSTraverse/Response.src/M000061.html +31 -0
- data/doc/classes/DNSTraverse/Response.src/M000062.html +23 -0
- data/doc/classes/DNSTraverse/Response/NoGlue.html +222 -0
- data/doc/classes/DNSTraverse/Response/NoGlue.src/M000007.html +32 -0
- data/doc/classes/DNSTraverse/Response/NoGlue.src/M000008.html +18 -0
- data/doc/classes/DNSTraverse/Response/NoGlue.src/M000009.html +18 -0
- data/doc/classes/DNSTraverse/Response/NoGlue.src/M000010.html +18 -0
- data/doc/classes/DNSTraverse/Traverser.html +247 -0
- data/doc/classes/DNSTraverse/Traverser.src/M000040.html +17 -0
- data/doc/classes/DNSTraverse/Traverser.src/M000041.html +52 -0
- data/doc/classes/DNSTraverse/Traverser.src/M000042.html +61 -0
- data/doc/classes/DNSTraverse/Traverser.src/M000043.html +110 -0
- data/doc/classes/DNSTraverse/Traverser.src/M000044.html +63 -0
- data/doc/classes/DNSTraverse/Traverser.src/M000045.html +28 -0
- data/doc/classes/DNSTraverse/Traverser.src/M000046.html +18 -0
- data/doc/classes/FingerprintRules.html +175 -0
- data/doc/classes/Log.html +174 -0
- data/doc/classes/Log.src/M000002.html +18 -0
- data/doc/classes/Log.src/M000003.html +18 -0
- data/doc/classes/Log.src/M000004.html +18 -0
- data/doc/classes/Log/Formatter.html +165 -0
- data/doc/classes/Log/Formatter.src/M000005.html +22 -0
- data/doc/classes/Log/Formatter.src/M000006.html +24 -0
- data/doc/classes/TestFingerprint.html +137 -0
- data/doc/classes/TestFingerprint.src/M000001.html +22 -0
- data/doc/created.rid +1 -0
- data/doc/files/lib/dnstraverse/caching_resolver_rb.html +132 -0
- data/doc/files/lib/dnstraverse/decoded_query_cache_rb.html +132 -0
- data/doc/files/lib/dnstraverse/decoded_query_rb.html +132 -0
- data/doc/files/lib/dnstraverse/fingerprint_rb.html +134 -0
- data/doc/files/lib/dnstraverse/fingerprint_rules_rb.html +143 -0
- data/doc/files/lib/dnstraverse/info_cache_rb.html +131 -0
- data/doc/files/lib/dnstraverse/log_rb.html +131 -0
- data/doc/files/lib/dnstraverse/message_utility_rb.html +124 -0
- data/doc/files/lib/dnstraverse/referral_rb.html +134 -0
- data/doc/files/lib/dnstraverse/response_noglue_rb.html +124 -0
- data/doc/files/lib/dnstraverse/response_rb.html +132 -0
- data/doc/files/lib/dnstraverse/traverser_rb.html +137 -0
- data/doc/files/test/test_fingerprint_rb.html +133 -0
- data/doc/fr_class_index.html +43 -0
- data/doc/fr_file_index.html +39 -0
- data/doc/fr_method_index.html +115 -0
- data/doc/index.html +24 -0
- data/doc/rdoc-style.css +208 -0
- data/lib/dnstraverse/caching_resolver.rb +63 -0
- data/lib/dnstraverse/decoded_query.rb +199 -0
- data/lib/dnstraverse/decoded_query_cache.rb +53 -0
- data/lib/dnstraverse/fingerprint.rb +166 -0
- data/lib/dnstraverse/fingerprint_rules.rb +389 -0
- data/lib/dnstraverse/info_cache.rb +108 -0
- data/lib/dnstraverse/log.rb +55 -0
- data/lib/dnstraverse/message_utility.rb +199 -0
- data/lib/dnstraverse/referral.rb +463 -0
- data/lib/dnstraverse/response.rb +92 -0
- data/lib/dnstraverse/response_noglue.rb +54 -0
- data/lib/dnstraverse/traverser.rb +291 -0
- data/test/test_fingerprint.rb +29 -0
- metadata +231 -0
@@ -0,0 +1,108 @@
|
|
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
|
+
require 'dnstraverse/log'
|
17
|
+
|
18
|
+
module DNSTraverse
|
19
|
+
class InfoCache
|
20
|
+
|
21
|
+
attr_reader :parent
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def key(rr)
|
26
|
+
return "#{rr.name}:#{rr.klass}:#{rr.type}".downcase
|
27
|
+
end
|
28
|
+
|
29
|
+
public
|
30
|
+
|
31
|
+
def initialize(parent = nil)
|
32
|
+
@parent = parent
|
33
|
+
@data = Hash.new
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
# adds the resource records, clearing out any existing entries with the
|
38
|
+
# same details
|
39
|
+
def add(rrs)
|
40
|
+
rrs.each {|rr| @data[key(rr)] = Array.new } # clear out
|
41
|
+
for rr in rrs do
|
42
|
+
@data[key(rr)].push rr
|
43
|
+
Log.debug { "Adding to infocache: #{rr}" }
|
44
|
+
end
|
45
|
+
return nil
|
46
|
+
end
|
47
|
+
|
48
|
+
# array of hashes containing :name (server name) and :ips (array of strings)
|
49
|
+
# set domain to '' for setting root hints
|
50
|
+
def add_hints(domain, ns)
|
51
|
+
rrs = Array.new
|
52
|
+
for server in ns do
|
53
|
+
rrs.push Dnsruby::RR.create(:name => domain, :ttl => 0,
|
54
|
+
:type => 'NS', :domainname => server[:name])
|
55
|
+
for ip in server[:ips] do
|
56
|
+
type = (ip.to_s =~ /\A(\d+)\.(\d+)\.(\d+)\.(\d+)\z/) ? 'A' : 'AAAA'
|
57
|
+
rrs.push Dnsruby::RR.create(:type => type, :ttl => 0,
|
58
|
+
:name => server[:name], :address => ip)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
return add(rrs)
|
62
|
+
end
|
63
|
+
|
64
|
+
def get?(args)
|
65
|
+
qclass = args[:qclass] || 'IN'
|
66
|
+
gkey = "#{args[:qname]}:#{qclass}:#{args[:qtype]}".downcase
|
67
|
+
if @data.has_key?(gkey) then
|
68
|
+
Log.debug { "Infocache recall: " + @data[gkey].join(', ')}
|
69
|
+
return @data[gkey] # returns Array
|
70
|
+
end
|
71
|
+
return nil unless parent
|
72
|
+
return parent.get?(args)
|
73
|
+
end
|
74
|
+
|
75
|
+
def get_ns?(domain) # get an appropriate ns based on domain
|
76
|
+
domain = domain.to_s
|
77
|
+
while true do
|
78
|
+
Log.debug { "Infocache get_ns? checking NS records for '#{domain}'" }
|
79
|
+
rrs = get?(:qname => domain, :qtype => 'NS')
|
80
|
+
return rrs if rrs
|
81
|
+
if domain == '' then
|
82
|
+
raise "No nameservers available for #{domain} -- no root hints set??"
|
83
|
+
end
|
84
|
+
domain = (i = domain.index('.')) ? domain[i+1..-1] : ''
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def get_startservers(domain, nsatype = 'A')
|
89
|
+
Log.debug { "Getting startservers for #{domain}/#{nsatype}" }
|
90
|
+
newbailiwick = nil
|
91
|
+
# search for best NS records in authority cache based on this domain name
|
92
|
+
ns = get_ns?(domain)
|
93
|
+
starters = Array.new
|
94
|
+
# look up in additional cache corresponding IP addresses if we know them
|
95
|
+
for rr in ns do
|
96
|
+
nameserver = rr.domainname.to_s
|
97
|
+
iprrs = get?(:qname => nameserver, :qtype => nsatype)
|
98
|
+
ips = iprrs ? iprrs.map {|iprr| iprr.address.to_s } : nil
|
99
|
+
starters.push({ :name => nameserver, :ips => ips })
|
100
|
+
end
|
101
|
+
newbailiwick = ns[0].name.to_s
|
102
|
+
Log.debug { "For domain #{domain} using start servers: " +
|
103
|
+
starters.map { |x| x[:name] }.join(', ') }
|
104
|
+
return starters, newbailiwick
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,55 @@
|
|
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
|
+
require 'logger'
|
17
|
+
|
18
|
+
module Log
|
19
|
+
include Logger::Severity
|
20
|
+
|
21
|
+
class Formatter
|
22
|
+
Format = "%s, [%s #%d] %5s -- %s: %s\n"
|
23
|
+
|
24
|
+
def call(severity, time, progname, msg)
|
25
|
+
t = time.strftime("%Y-%m-%d %H:%M:%S.") << "%06d" % time.usec
|
26
|
+
#t = ""
|
27
|
+
msg2str(msg).split(/\n/).map do |m|
|
28
|
+
Format % [severity[0..0], t, $$, severity, progname, m]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def msg2str(msg)
|
33
|
+
case msg
|
34
|
+
when ::Exception
|
35
|
+
"#{ msg.message } (#{ msg.class })\n" <<
|
36
|
+
(msg.backtrace || []).join("\n")
|
37
|
+
else
|
38
|
+
msg.to_s
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.level=(l)
|
44
|
+
@@logger.level = l
|
45
|
+
end
|
46
|
+
def self.logger=(logger)
|
47
|
+
@@logger = logger
|
48
|
+
end
|
49
|
+
def self.method_missing(key, *args, &b)
|
50
|
+
@@logger.send(key, *args, &b)
|
51
|
+
end
|
52
|
+
@@logger = Logger.new(STDERR)
|
53
|
+
@@logger.formatter = Formatter.new
|
54
|
+
@@logger.level = Logger::FATAL
|
55
|
+
end
|
@@ -0,0 +1,199 @@
|
|
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
|
+
class ResolveError < RuntimeError
|
18
|
+
end
|
19
|
+
|
20
|
+
module MessageUtility
|
21
|
+
module_function
|
22
|
+
|
23
|
+
def msg_comment(msg, args)
|
24
|
+
warnings = Array.new
|
25
|
+
if args[:want_recursion] then
|
26
|
+
if not msg.header.ra then
|
27
|
+
warnings.push "#{msg.answerfrom} doesn't allow recursion"
|
28
|
+
end
|
29
|
+
else
|
30
|
+
if msg.header.ra then
|
31
|
+
warnings.push "#{msg.answerfrom} allows recursion"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
if msg.header.tc then
|
35
|
+
warnings.push "#{msg.answerfrom} sent truncated packet"
|
36
|
+
end
|
37
|
+
for warn in warnings do
|
38
|
+
Log.warn { warn }
|
39
|
+
end
|
40
|
+
Log.debug { "#{msg.answerfrom} code #{msg.rcode}" }
|
41
|
+
return warnings
|
42
|
+
end
|
43
|
+
|
44
|
+
def msg_validate(msg, args)
|
45
|
+
a = args.dup
|
46
|
+
a[:qclass]||= 'IN'
|
47
|
+
return true if msg.rcode != Dnsruby::RCode.NOERROR
|
48
|
+
begin
|
49
|
+
if msg.question.size != 1 then
|
50
|
+
raise ResolveError, "#{msg.answerfrom} returned unexpected " +
|
51
|
+
"question size #{msg.question.size}"
|
52
|
+
end
|
53
|
+
for c in [:qname, :qclass, :qtype] do
|
54
|
+
if a[c] and
|
55
|
+
a[c].to_s.downcase != msg.question[0].send(c).to_s.downcase then
|
56
|
+
raise ResolveError, "#{msg.answerfrom} returned mismatched #{c} " +
|
57
|
+
"#{msg.question[0].send(c)} instead of expected #{a[c]}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
rescue => e
|
61
|
+
Log.debug { "Failed message was: " + msg.to_s }
|
62
|
+
raise e
|
63
|
+
end
|
64
|
+
return true
|
65
|
+
end
|
66
|
+
|
67
|
+
def msg_answers?(msg, args)
|
68
|
+
qname = args[:qname].to_s
|
69
|
+
qclass = (args[:qclass] || 'IN').to_s
|
70
|
+
qtype = args[:qtype].to_s
|
71
|
+
any = qtype.casecmp('ANY') == 0 ? true : false
|
72
|
+
ans = msg.answer.select { |x|
|
73
|
+
x.name.to_s.casecmp(qname) == 0 &&
|
74
|
+
x.klass.to_s.casecmp(qclass) == 0 &&
|
75
|
+
(any || x.type.to_s.casecmp(qtype) == 0)
|
76
|
+
}
|
77
|
+
Log.debug { "Answers:" + ans.size.to_s}
|
78
|
+
return ans.size > 0 ? ans : nil
|
79
|
+
end
|
80
|
+
|
81
|
+
def msg_additional?(msg, args)
|
82
|
+
qclass = args[:qclass] || 'IN'
|
83
|
+
Log.debug { "Looking for #{args[:qname]}/#{args[:qtype]} in additional" }
|
84
|
+
add = msg.additional.select { |x|
|
85
|
+
x.name.to_s.casecmp(args[:qname].to_s) == 0 &&
|
86
|
+
x.klass.to_s.casecmp(qclass.to_s) == 0 &&
|
87
|
+
x.type.to_s.casecmp(args[:qtype].to_s) == 0
|
88
|
+
}
|
89
|
+
Log.debug { add.size > 0 ? "Found #{add.size} additional records" \
|
90
|
+
: "No additional records for #{args[:qname]}/#{args[:qtype]}"}
|
91
|
+
return add.size > 0 ? add : nil
|
92
|
+
end
|
93
|
+
|
94
|
+
def msg_additional_ips?(msg, args)
|
95
|
+
qclass = args[:qclass] || 'IN'
|
96
|
+
Log.debug { "Looking for #{args[:qname]}/#{args[:qtype]} in additional" }
|
97
|
+
if add = msg.additional.select { |x|
|
98
|
+
x.name.to_s.casecmp(args[:qname].to_s) == 0 &&
|
99
|
+
x.klass.to_s.casecmp(qclass.to_s) == 0 &&
|
100
|
+
x.type.to_s.casecmp(args[:qtype].to_s) == 0
|
101
|
+
} then
|
102
|
+
ips = add.map {|x| x.address.to_s }
|
103
|
+
Log.debug { "Found in additional #{args[:qname]} = " + ips.join(", ") }
|
104
|
+
return ips
|
105
|
+
end
|
106
|
+
Log.debug { "No additional records for #{args[:qname]}/#{args[:qtype]}" }
|
107
|
+
return nil
|
108
|
+
end
|
109
|
+
|
110
|
+
# def msg_referrals(msg, args)
|
111
|
+
# r = msg.authority.select { |x|
|
112
|
+
# x.type.to_s.casecmp('NS') == 0 && x.klass.to_s.casecmp('IN') == 0
|
113
|
+
# }
|
114
|
+
# if args[:bailiwick] then
|
115
|
+
# b = args[:bailiwick]
|
116
|
+
# r = r.select { |x|
|
117
|
+
# zonename = x.name.to_s
|
118
|
+
# if cond = zonename !~ /#{@b}$/i then
|
119
|
+
# Log.debug { "Excluding lame referral #{b} to #{zonename}" }
|
120
|
+
# raise "lame"
|
121
|
+
# end
|
122
|
+
# cond
|
123
|
+
# }
|
124
|
+
# end
|
125
|
+
# Log.debug { "Referrals: " + r.map {|x| x.domainname.to_s }.join(", ") }
|
126
|
+
# return r
|
127
|
+
# end
|
128
|
+
|
129
|
+
def msg_authority(msg)
|
130
|
+
ns = []
|
131
|
+
soa = []
|
132
|
+
other = []
|
133
|
+
for rr in msg.authority do
|
134
|
+
type = rr.type.to_s
|
135
|
+
klass = rr.klass.to_s
|
136
|
+
if type.casecmp('NS') == 0 && klass.casecmp('IN') == 0
|
137
|
+
ns.push rr
|
138
|
+
elsif type.casecmp('SOA') == 0 && klass.casecmp('IN') == 0
|
139
|
+
soa.push rr
|
140
|
+
else
|
141
|
+
other.push rr
|
142
|
+
end
|
143
|
+
end
|
144
|
+
return ns, soa, other
|
145
|
+
end
|
146
|
+
|
147
|
+
def msg_follow_cnames(msg, args)
|
148
|
+
name = args[:qname]
|
149
|
+
type = args[:qtype]
|
150
|
+
bw = args[:bailiwick].to_s
|
151
|
+
bwend = ".#{args[:bailiwick]}"
|
152
|
+
while true do
|
153
|
+
return name if msg_answers?(msg, :qname => name, :qtype => type)
|
154
|
+
if not ans = msg_answers?(msg, :qname => name, :qtype => 'CNAME') then
|
155
|
+
return name
|
156
|
+
end
|
157
|
+
target = ans[0].domainname.to_s
|
158
|
+
Log.debug { "CNAME encountered from #{name} to #{target}"}
|
159
|
+
if bw and (target.casecmp(bw) != 0 and name !~ /#{bwend}$/i) then
|
160
|
+
# target outside of bailiwick, don't follow any more CNAMEs.
|
161
|
+
return target
|
162
|
+
end
|
163
|
+
name = target
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def msg_nodata?(msg)
|
168
|
+
ns, soa, other = msg_authority(msg)
|
169
|
+
if soa.size > 0 or ns.size == 0 then
|
170
|
+
Log.debug { "NODATA: soa=#{soa.size} ns=#{ns.size}" }
|
171
|
+
return true
|
172
|
+
end
|
173
|
+
return false
|
174
|
+
end
|
175
|
+
|
176
|
+
def msg_cacheable(msg, bailiwick, type = :both)
|
177
|
+
good, bad = Array.new, Array.new
|
178
|
+
bw = bailiwick.to_s
|
179
|
+
bwend = "." + bw
|
180
|
+
for section in [:additional, :authority] do
|
181
|
+
for rr in msg.send(section) do
|
182
|
+
name = rr.name.to_s
|
183
|
+
if bailiwick.nil? or name.casecmp(bw) == 0 or
|
184
|
+
name =~ /#{bwend}$/i then
|
185
|
+
good.push rr
|
186
|
+
else
|
187
|
+
bad.push rr
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
good.map {|x| Log.debug { "Records within bailiwick: " + x.to_s } }
|
192
|
+
bad.map {|x| Log.debug { "Records outside bailiwick: " + x.to_s } }
|
193
|
+
return good if type == :good
|
194
|
+
return bad if type == :bad
|
195
|
+
return good, bad
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|
199
|
+
end
|
@@ -0,0 +1,463 @@
|
|
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
|
+
require 'dnstraverse/response'
|
17
|
+
require 'dnstraverse/response_noglue'
|
18
|
+
require 'dnstraverse/info_cache'
|
19
|
+
require 'dnstraverse/decoded_query_cache'
|
20
|
+
|
21
|
+
module DNSTraverse
|
22
|
+
|
23
|
+
class Referral
|
24
|
+
include MessageUtility
|
25
|
+
|
26
|
+
attr_reader :server, :serverips, :qname, :qclass, :qtype, :nsatype
|
27
|
+
attr_reader :refid, :message, :infocache, :parent, :bailiwick, :stats
|
28
|
+
attr_reader :warnings, :children, :parent_ip
|
29
|
+
attr_reader :decoded_query_cache
|
30
|
+
|
31
|
+
EMPTY_ARRAY = [].freeze
|
32
|
+
|
33
|
+
def txt_ips_verbose
|
34
|
+
return '' unless @serverips
|
35
|
+
a = @serverips.map do |ip|
|
36
|
+
sprintf("%.1f%%=", 100 * @serverweights[ip]).concat(ip =~ /^key:([^:]+(:[^:]*)?)/ ? $1 : ip)
|
37
|
+
end
|
38
|
+
a.sort.join(',')
|
39
|
+
end
|
40
|
+
|
41
|
+
def txt_ips
|
42
|
+
return '' unless @serverips
|
43
|
+
@serverips.map { |ip|
|
44
|
+
ip =~ /^key:/ ? @stats_resolve[ip][:response].to_s : ip
|
45
|
+
}.sort.join(',')
|
46
|
+
end
|
47
|
+
|
48
|
+
# ips_as_array will return any IP addresses we know for this referral server
|
49
|
+
def ips_as_array
|
50
|
+
return EMPTY_ARRAY unless @serverips
|
51
|
+
my_ips = []
|
52
|
+
for ip in @serverips do
|
53
|
+
my_ips << ip unless ip =~ /^key:/
|
54
|
+
end
|
55
|
+
return my_ips
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_s
|
59
|
+
return "#{@refid} [#{@qname}/#{@qclass}/#{@qtype}] server=#{@server} " +
|
60
|
+
"server_ips=#{txt_ips()} bailiwick=#{@bailiwick}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def referral_resolution?
|
64
|
+
return @referral_resolution ? true : false
|
65
|
+
end
|
66
|
+
|
67
|
+
# Referral object represents a particular referral to a specified server
|
68
|
+
# with given qname, qclass and qtype.
|
69
|
+
#
|
70
|
+
# roots can be passed in, which will be used to populate root hints in to
|
71
|
+
# the infocache, which if not passed in will be automatically created
|
72
|
+
#
|
73
|
+
# server can be nil which is a special case and causes all the roots
|
74
|
+
# to be added as child referrals (uses infocache to lookup roots)
|
75
|
+
#
|
76
|
+
# if the server's IP address(es) are known, they are passed in as serverips
|
77
|
+
# otherwise, we will resolve the serverips
|
78
|
+
#
|
79
|
+
# referral_resolution should be set to false. children that are a result
|
80
|
+
# of a resolution of a referral that didn't have glue records will have
|
81
|
+
# this set to true so that you can distringuish this detail
|
82
|
+
def initialize(args)
|
83
|
+
@resolver = args[:resolver] # Dnsruby::Resolver object
|
84
|
+
@qname = args[:qname]
|
85
|
+
@qclass = args[:qclass] || :IN
|
86
|
+
@qtype = args[:qtype] || :A
|
87
|
+
@nsatype = args[:nsatype] || :A
|
88
|
+
@infocache = args[:infocache] || DNSTraverse::InfoCache.new
|
89
|
+
@roots = args[:roots]
|
90
|
+
@resolves = nil # Array of referral objects for resolving phase
|
91
|
+
@refid = args[:refid] || ''
|
92
|
+
@server = args[:server] || nil # nil for the root-root server
|
93
|
+
@serverips = args[:serverips] || nil
|
94
|
+
@responses = Hash.new # responses/exception for each IP in @serverips
|
95
|
+
@children = Hash.new # Array of child Referrer objects keyed by IP
|
96
|
+
@bailiwick = args[:bailiwick] || nil
|
97
|
+
@secure = args[:secure] || true # ensure bailiwick checks
|
98
|
+
@parent = args[:parent] || nil # Parent Referral
|
99
|
+
@parent_ip = args[:parent_ip] || nil # Parent Referral IP if applicable
|
100
|
+
@maxdepth = args[:maxdepth] || 10 # maximum depth before error
|
101
|
+
@decoded_query_cache = args[:decoded_query_cache]
|
102
|
+
@referral_resolution = args[:referral_resolution] || false # flag
|
103
|
+
@stats = nil # will contain statistics for answers
|
104
|
+
@stats_resolve = nil # will contain statistics for our resolve (if applic)
|
105
|
+
@serverweights = Hash.new # key is IP
|
106
|
+
@warnings = Array.new # warnings will be placed here
|
107
|
+
@processed = false # flag for processed? method
|
108
|
+
raise "Must pass Resolver" unless @resolver
|
109
|
+
@infocache.add_hints('', args[:roots]) if args[:roots] # add root hints
|
110
|
+
unless @decoded_query_cache then
|
111
|
+
dcq_args = { :resolver => @resolver}
|
112
|
+
@decoded_query_cache = DNSTraverse::DecodedQueryCache.new(dcq_args)
|
113
|
+
end
|
114
|
+
if serverips then # we know the server weights - we're not resolving
|
115
|
+
for ip in serverips do
|
116
|
+
@serverweights[ip] = 1.0 / @serverips.length
|
117
|
+
end
|
118
|
+
end
|
119
|
+
Log.debug { "New resolver object created: " + self.to_s }
|
120
|
+
end
|
121
|
+
|
122
|
+
def showstats
|
123
|
+
s = Hash.new
|
124
|
+
ObjectSpace.each_object do |o|
|
125
|
+
s[o.class]||= 0
|
126
|
+
s[o.class]= s[o.class] + 1
|
127
|
+
end
|
128
|
+
s.sort {|a,b| a[1] <=> b[1]}.each do | c |
|
129
|
+
puts "#{c[1]} #{c[0]}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# clean up the workings
|
134
|
+
def cleanup(args = nil)
|
135
|
+
Log.debug { "cleaning: #{self}" }
|
136
|
+
@infocache = nil unless args and args[:infocache]
|
137
|
+
@cacheable_good = @cacheable_bad = nil unless args and args[:cacheable]
|
138
|
+
@starters = @starters_bailiwick = nil unless args and args[:starters]
|
139
|
+
@auth_ns = @auth_soa = @auth_other = nil unless args and args[:auth]
|
140
|
+
@children = nil unless args and args[:children]
|
141
|
+
@resolves = nil unless args and args[:resolves]
|
142
|
+
@responses = nil unless args and args[:responses]
|
143
|
+
@decoded_query_cache = nil unless args and args[:decoded_query_cache]
|
144
|
+
@resolver = nil unless args and args[:resolver]
|
145
|
+
end
|
146
|
+
|
147
|
+
def inside_bailiwick?(name)
|
148
|
+
return true if @bailiwick.nil?
|
149
|
+
bwend = ".#{@bailiwick}"
|
150
|
+
namestr = name.to_s
|
151
|
+
return true if namestr.casecmp(@bailiwick) == 0
|
152
|
+
return true if namestr =~ /#{bwend}$/i
|
153
|
+
return false
|
154
|
+
end
|
155
|
+
|
156
|
+
def noglue?
|
157
|
+
return false if @serverips
|
158
|
+
return false unless inside_bailiwick?(@server)
|
159
|
+
return true
|
160
|
+
end
|
161
|
+
|
162
|
+
# resolve server to serverips, return list of Referral objects to process
|
163
|
+
def resolve(*args)
|
164
|
+
raise "This Referral object has already been resolved" if resolved?
|
165
|
+
if noglue? then
|
166
|
+
# foo.net IN NS ns.foo.net - no IP cached & no glue = failure
|
167
|
+
Log.debug { "Attempt to resolve #{@server} with a bailiwick referral " +
|
168
|
+
" of #{bailiwick} - no glue record provided" }
|
169
|
+
return Array.new
|
170
|
+
end
|
171
|
+
refid = "#{@refid}.0"
|
172
|
+
child_refid = 1
|
173
|
+
starters, newbailiwick = @infocache.get_startservers(@server)
|
174
|
+
Log.debug { "Resolving #{@server} type #{@nsatype} " }
|
175
|
+
for starter in starters do
|
176
|
+
r = make_referral(:server => starter[:name],
|
177
|
+
:serverips => starter[:ips],
|
178
|
+
:referral_resolution => true,
|
179
|
+
:qname => @server, :qclass => 'IN',
|
180
|
+
:qtype => @nsatype, :bailiwick => newbailiwick,
|
181
|
+
:refid => "#{refid}.#{child_refid}")
|
182
|
+
(@resolves||= []) << r
|
183
|
+
child_refid+= 1
|
184
|
+
end
|
185
|
+
# return a set of Referral objects that need to be processed
|
186
|
+
return @resolves
|
187
|
+
end
|
188
|
+
|
189
|
+
def resolve_calculate
|
190
|
+
Log.debug { "Calculating resolution: #{self}" }
|
191
|
+
# create stats_resolve containing all the statistics of the resolution
|
192
|
+
@stats_resolve = Hash.new
|
193
|
+
if noglue? then # in-bailiwick referral without glue
|
194
|
+
r = DNSTraverse::Response::NoGlue.new(:qname => @qname,
|
195
|
+
:qclass => @qclass,
|
196
|
+
:qtype => @qtype,
|
197
|
+
:server => @server,
|
198
|
+
:ip => @parent_ip,
|
199
|
+
:bailiwick => @bailiwick)
|
200
|
+
@stats_resolve[r.stats_key] = { :prob => 1.0, :response => r,
|
201
|
+
:referral => self }
|
202
|
+
else
|
203
|
+
# normal resolve - combine children's statistics in to @stats_resolve
|
204
|
+
stats_calculate_children(@stats_resolve, @resolves, 1.0)
|
205
|
+
end
|
206
|
+
# now use this data to work out %age of each IP address returned
|
207
|
+
@serverweights = Hash.new
|
208
|
+
@stats_resolve.each_pair do |key, data|
|
209
|
+
# key = IP or key:blah, data is hash containing :prob, etc.
|
210
|
+
if data[:response].status == :answered then # RR records
|
211
|
+
# there were some answers - so add the probabilities in
|
212
|
+
answers = data[:response].answers # weight RRs evenly
|
213
|
+
for rr in answers do
|
214
|
+
@serverweights[rr.address.to_s]||= 0
|
215
|
+
@serverweights[rr.address.to_s]+= data[:prob] / answers.length
|
216
|
+
end
|
217
|
+
else
|
218
|
+
# there were no answers - use the special key and record probabilities
|
219
|
+
@serverweights[key]||= 0
|
220
|
+
@serverweights[key]+= data[:prob]
|
221
|
+
end
|
222
|
+
end
|
223
|
+
@serverips = @serverweights.keys
|
224
|
+
Log.debug { "Calculating resolution (answer): #{@serverips.join(',')}" }
|
225
|
+
end
|
226
|
+
|
227
|
+
def stats_calculate_children(stats, children, weight)
|
228
|
+
percent = (1.0 / children.length) * weight
|
229
|
+
for child in children do
|
230
|
+
child.stats.each_pair do |key, data|
|
231
|
+
if not stats[key] then
|
232
|
+
# just copy the child's statistics for this key
|
233
|
+
stats[key] = data.dup
|
234
|
+
stats[key][:prob]*= percent
|
235
|
+
else
|
236
|
+
stats[key][:prob]+= data[:prob] * percent
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def answer_calculate
|
243
|
+
Log.debug { "Calculating answer: #{self}" }
|
244
|
+
@stats = Hash.new
|
245
|
+
|
246
|
+
if not @server then
|
247
|
+
# special case - rootroot, no actual IPs, just root referrals
|
248
|
+
stats_calculate_children(@stats, @children[:rootroot], 1.0)
|
249
|
+
@stats.each_pair do |key, data|
|
250
|
+
Log.debug { sprintf "Answer: %.2f%% %s\n", data[:prob] * 100, key }
|
251
|
+
end
|
252
|
+
return
|
253
|
+
end
|
254
|
+
for ip in @serverips do
|
255
|
+
serverweight = @serverweights[ip] # set at initialize or at resolve
|
256
|
+
if ip =~ /^key:/ then # resolve failed for some reason
|
257
|
+
# pull out the statistics on the resolution and copy over
|
258
|
+
raise "duplicate key found" if @stats[ip] # assertion
|
259
|
+
if @stats_resolve[ip][:prob] != serverweight then # assertion
|
260
|
+
$stderr.puts "#{@stats_resolve[ip][:prob]} vs #{serverweight}"
|
261
|
+
@stats_resolve[ip].each_pair do |a,b|
|
262
|
+
$stderr.puts a
|
263
|
+
end
|
264
|
+
raise "unexpected probability"
|
265
|
+
end
|
266
|
+
@stats[ip] = @stats_resolve[ip].dup
|
267
|
+
next
|
268
|
+
end
|
269
|
+
if @children[ip] then
|
270
|
+
stats_calculate_children(@stats, @children[ip], serverweight)
|
271
|
+
else
|
272
|
+
response = @responses[ip]
|
273
|
+
@stats[response.stats_key] = { :prob => serverweight,
|
274
|
+
:response => response, :referral => self }
|
275
|
+
end
|
276
|
+
end
|
277
|
+
@stats.each_pair do |key, data|
|
278
|
+
Log.debug { sprintf "Answer: %.2f%% %s\n", data[:prob] * 100, key }
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def processed?
|
283
|
+
return @processed
|
284
|
+
end
|
285
|
+
|
286
|
+
def resolved?
|
287
|
+
# root-root is always resolved, otherwise check we have IP addresses
|
288
|
+
return true if is_rootroot?
|
289
|
+
return true if @noglue
|
290
|
+
return false if @serverips.nil?
|
291
|
+
return true
|
292
|
+
end
|
293
|
+
|
294
|
+
def is_rootroot?
|
295
|
+
# rootroot is the topmost object representing an automatic referral
|
296
|
+
# to all the root servers
|
297
|
+
@server.nil? ? true : false
|
298
|
+
end
|
299
|
+
|
300
|
+
def process(args)
|
301
|
+
raise "This Referral object has already been processed" if processed?
|
302
|
+
raise "You need to resolve this Referral object" unless resolved?
|
303
|
+
if (server) then
|
304
|
+
process_normal(args)
|
305
|
+
else
|
306
|
+
# special case - no server means start from the top with the roots
|
307
|
+
process_add_roots(args)
|
308
|
+
end
|
309
|
+
# return a set of Referral objects that need to be processed
|
310
|
+
# XXX flatten really necessary?
|
311
|
+
@processed = true
|
312
|
+
return @children.values.flatten.select {|x| x.is_a? Referral}
|
313
|
+
end
|
314
|
+
|
315
|
+
def process_add_roots(args)
|
316
|
+
Log.debug { "Special case processing, addding roots as referrals" }
|
317
|
+
refid_prefix = @refid == '' ? '' : "#{@refid}."
|
318
|
+
refid = 1
|
319
|
+
starters = (@infocache.get_startservers('', @nsatype))[0]
|
320
|
+
@children[:rootroot] = Array.new # use 'rootroot' instead of IP address
|
321
|
+
for root in starters do
|
322
|
+
r = make_referral(:server => root[:name], :serverips => root[:ips],
|
323
|
+
:refid => "#{refid_prefix}#{refid}")
|
324
|
+
@children[:rootroot].push r
|
325
|
+
refid+= 1
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
def check_loop?(args) # :ip, :qtype, :qname, :qclass
|
330
|
+
parent = @parent
|
331
|
+
until parent.nil? do
|
332
|
+
if parent.qname.to_s == args[:qname].to_s and
|
333
|
+
parent.qclass.to_s == args[:qclass].to_s and
|
334
|
+
parent.qtype.to_s == args[:qtype].to_s and
|
335
|
+
parent.serverips and parent.serverips.include?(args[:ip]) then
|
336
|
+
exit 1 # XXX fix me
|
337
|
+
return RuntimeError.new("Loop detected")
|
338
|
+
end
|
339
|
+
parent = parent.parent
|
340
|
+
end
|
341
|
+
return nil
|
342
|
+
end
|
343
|
+
|
344
|
+
def process_normal(args)
|
345
|
+
Log.debug { "process " + self.to_s }
|
346
|
+
# if l = check_loop?(:ip => ip, :qname => @qname,
|
347
|
+
# :qtype => @qtype, :qclass => @qclass) then
|
348
|
+
# for ip in @serverips do
|
349
|
+
# @responses[ip] = l
|
350
|
+
# done
|
351
|
+
# return
|
352
|
+
# end
|
353
|
+
# end
|
354
|
+
for ip in @serverips do
|
355
|
+
Log.debug { "Process normal #{ip}" }
|
356
|
+
next if ip =~ /^key:/ # resolve failed on something
|
357
|
+
m = nil
|
358
|
+
if @refid.scan(/\./).length >= @maxdepth.to_i then
|
359
|
+
m = RuntimeError.new "Maxdepth #{@maxdepth} exceeded"
|
360
|
+
end
|
361
|
+
Log.debug { "Process normal #{ip} - making response" }
|
362
|
+
r = DNSTraverse::Response.new(:message => m, :qname => @qname,
|
363
|
+
:qclass => @qclass, :qtype => @qtype,
|
364
|
+
:bailiwick => @bailiwick,
|
365
|
+
:infocache => @infocache, :ip => ip,
|
366
|
+
:decoded_query_cache => @decoded_query_cache)
|
367
|
+
Log.debug { "Process normal #{ip} - done making response" }
|
368
|
+
@responses[ip] = r
|
369
|
+
case r.status
|
370
|
+
when :restart, :referral then
|
371
|
+
Log.debug { "Process normal #{ip} - making referrals" }
|
372
|
+
@children[ip] = make_referrals(:qname => r.endname,
|
373
|
+
:starters => r.starters,
|
374
|
+
:bailiwick => r.starters_bailiwick,
|
375
|
+
:infocache => r.infocache,
|
376
|
+
:parent_ip => ip)
|
377
|
+
Log.debug { "Process normal #{ip} - done making referrals" }
|
378
|
+
# XXX shouldn't be any children unless referrals
|
379
|
+
#when :referral_lame then
|
380
|
+
#@children[ip] = RuntimeError.new "Improper or lame delegation"
|
381
|
+
end
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
def make_referrals(args) # :starters can be @root or our own list
|
386
|
+
starters = args[:starters]
|
387
|
+
children = Array.new
|
388
|
+
child_refid = 1
|
389
|
+
for starter in starters do
|
390
|
+
refargs = {
|
391
|
+
:server => starter[:name],
|
392
|
+
:serverips => starter[:ips],
|
393
|
+
:refid => "#{refid}.#{child_refid}"
|
394
|
+
}.merge(args)
|
395
|
+
children.push make_referral(refargs)
|
396
|
+
child_refid+= 1
|
397
|
+
end
|
398
|
+
return children
|
399
|
+
end
|
400
|
+
|
401
|
+
def make_referral(args)
|
402
|
+
raise "Must pass new refid" unless args[:refid]
|
403
|
+
refargs = { :qname => @qname, :qclass => @qclass,
|
404
|
+
:qtype => @qtype, :nsatype => @nsatype, :infocache => @infocache,
|
405
|
+
:referral_resolution => @referral_resolution,
|
406
|
+
:resolver => @resolver, :maxdepth => @maxdepth, :parent => self,
|
407
|
+
:decoded_query_cache => @decoded_query_cache }.merge(args)
|
408
|
+
return Referral.new(refargs)
|
409
|
+
end
|
410
|
+
|
411
|
+
def replace_child(before, after)
|
412
|
+
@children.each_key do | ip |
|
413
|
+
@children[ip].map! { |c| c.equal?(before) ? after : c }
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
def stats_display(args)
|
418
|
+
spacing = args[:spacing] || false
|
419
|
+
results = args[:results] || true
|
420
|
+
prefix = args[:prefix] || ''
|
421
|
+
indent = args[:indent] || "#{prefix} "
|
422
|
+
first = true
|
423
|
+
@stats.keys.sort!.each do | key |
|
424
|
+
data = @stats[key]
|
425
|
+
puts if spacing and not first
|
426
|
+
first = false
|
427
|
+
printf "#{prefix}%5.1f%%: ", data[:prob] * 100
|
428
|
+
response = data[:response]
|
429
|
+
referral = data[:referral]
|
430
|
+
where = "#{referral.server} (#{response.ip})"
|
431
|
+
case response.status
|
432
|
+
when :exception
|
433
|
+
puts "#{response.exception_message} at #{where}"
|
434
|
+
when :noglue
|
435
|
+
puts "No glue at #{referral.parent.server} " +
|
436
|
+
"(#{response.ip}) for #{response.server}"
|
437
|
+
when :error
|
438
|
+
puts "#{response.error_message} at #{where}"
|
439
|
+
when :nodata
|
440
|
+
puts "NODATA (for this type) at #{where})"
|
441
|
+
when :answered
|
442
|
+
puts "Answer from #{where}"
|
443
|
+
if results then
|
444
|
+
for rr in data[:response].answers do
|
445
|
+
puts "#{indent}#{rr}"
|
446
|
+
end
|
447
|
+
end
|
448
|
+
else
|
449
|
+
puts "Stopped at #{where})"
|
450
|
+
puts "#{indent}#{key}"
|
451
|
+
end
|
452
|
+
if response.status != :answered and
|
453
|
+
((response.qname != @qname) or (response.qclass != @qclass) or
|
454
|
+
(response.qtype != @qtype)) then
|
455
|
+
puts "#{indent}While querying #{response.qname}/" +
|
456
|
+
"#{response.qclass}/#{response.qtype}"
|
457
|
+
end
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
end
|
462
|
+
|
463
|
+
end
|