dnsruby 1.47 → 1.48
Sign up to get free protection for your applications and to get access to all the features.
- data/EXAMPLES +0 -19
- data/demo/{digitar.rb → digroot.rb} +6 -25
- data/lib/Dnsruby/Recursor.rb +5 -3
- data/lib/Dnsruby/Resolver.rb +6 -2
- data/lib/Dnsruby/dnssec.rb +6 -32
- data/lib/Dnsruby/message.rb +33 -25
- data/lib/Dnsruby/resource/DS.rb +1 -1
- data/lib/Dnsruby/resource/HINFO.rb +18 -2
- data/lib/Dnsruby/resource/LOC.rb +14 -11
- data/lib/Dnsruby/select_thread.rb +69 -36
- data/lib/Dnsruby/single_verifier.rb +4 -2
- data/lib/Dnsruby/zone_reader.rb +1 -1
- data/lib/dnsruby.rb +2 -1
- data/test/tc_rr.rb +17 -8
- data/test/tc_tcp.rb +113 -3
- data/test/tc_validator.rb +1 -0
- data/test/ts_online.rb +0 -1
- metadata +3 -4
- data/test/tc_itar.rb +0 -82
data/EXAMPLES
CHANGED
@@ -92,25 +92,6 @@ Dnsruby::Dnssec.clear_trust_anchors
|
|
92
92
|
Dnsruby::PacketSender.clear_caches
|
93
93
|
Dnsruby::Recursor.clear_caches
|
94
94
|
|
95
|
-
# Load the IANA TAR and query some signed zones
|
96
|
-
Dnssec.load_itar
|
97
|
-
res = Recursor.new
|
98
|
-
ret = res.query("frobbit.se", "NS")
|
99
|
-
print "Security level for signed zone from ITAR : #{ret.security_level}\n"
|
100
|
-
|
101
|
-
# Query a name with no validation to be performed
|
102
|
-
res = Dnsruby::Resolver.new
|
103
|
-
m = Message.new("frobbit.se", "MX")
|
104
|
-
m.do_validation = false
|
105
|
-
ret = res.send_message(m)
|
106
|
-
print "Security level of secure domain with no validation : #{ret.security_level}\n"
|
107
|
-
|
108
|
-
# Clear the keys and caches
|
109
|
-
Dnsruby::Dnssec.clear_trusted_keys
|
110
|
-
Dnsruby::Dnssec.clear_trust_anchors
|
111
|
-
Dnsruby::PacketSender.clear_caches
|
112
|
-
Dnsruby::Recursor.clear_caches
|
113
|
-
|
114
95
|
# Load a specific trust anchor and query some signed zones
|
115
96
|
trusted_key = Dnsruby::RR.create({:name => "uk-dnssec.nic.uk.",
|
116
97
|
:type => Dnsruby::Types.DNSKEY,
|
@@ -21,18 +21,14 @@
|
|
21
21
|
#
|
22
22
|
#= SYNOPSIS
|
23
23
|
#
|
24
|
-
#
|
24
|
+
#digroot name [ type [ class ] ]
|
25
25
|
#
|
26
26
|
#= DESCRIPTION
|
27
27
|
#
|
28
28
|
#Performs a DNS query on the given name. The record type
|
29
29
|
#and class can also be specified; if left blank they default
|
30
30
|
#to A and IN. The program firstly performs the requested DNS
|
31
|
-
# query. The response is then validated
|
32
|
-
#- the ITAR is searched for the keys of the closest ancestor
|
33
|
-
#of the query name, and the chain of trust is followed to prove
|
34
|
-
#that the DNSSEC records are correct, or that we do not expect the
|
35
|
-
#response to be signed.
|
31
|
+
# query. The response is then validated from the signed root.
|
36
32
|
#
|
37
33
|
#= AUTHOR
|
38
34
|
#
|
@@ -48,30 +44,16 @@ include Dnsruby
|
|
48
44
|
|
49
45
|
raise RuntimeError, "Usage: #{$0} name [ type [ class ] ]\n" unless (ARGV.length >= 1) && (ARGV.length <= 3)
|
50
46
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
47
|
+
resolver = Dnsruby::Resolver.new()
|
48
|
+
resolver.do_validation = true
|
49
|
+
res = Dnsruby::Recursor.new(resolver)
|
50
|
+
|
56
51
|
# Dnsruby::TheLog.level=Logger::DEBUG
|
57
52
|
|
58
53
|
name, type, klass = ARGV
|
59
54
|
type ||= "A"
|
60
55
|
klass ||= "IN"
|
61
56
|
|
62
|
-
if (type.upcase == "AXFR")
|
63
|
-
rrs = zt.transfer(name) # , klass)
|
64
|
-
|
65
|
-
if (rrs)
|
66
|
-
rrs.each do |rr|
|
67
|
-
print rr.to_s + "\n"
|
68
|
-
end
|
69
|
-
else
|
70
|
-
raise RuntimeError, "zone transfer failed: ", res.errorstring, "\n"
|
71
|
-
end
|
72
|
-
|
73
|
-
else
|
74
|
-
|
75
57
|
begin
|
76
58
|
answer = nil
|
77
59
|
answer = res.query(name, type, klass)
|
@@ -79,4 +61,3 @@ else
|
|
79
61
|
rescue Exception => e
|
80
62
|
print "query failed: #{e}\n"
|
81
63
|
end
|
82
|
-
end
|
data/lib/Dnsruby/Recursor.rb
CHANGED
@@ -303,8 +303,8 @@ module Dnsruby
|
|
303
303
|
#This method is much like the normal query() method except it disables
|
304
304
|
#the recurse flag in the packet and explicitly performs the recursion.
|
305
305
|
#
|
306
|
-
# packet = res.
|
307
|
-
# packet = res.
|
306
|
+
# packet = res.query( "www.netscape.com.", "A")
|
307
|
+
# packet = res.query( "www.netscape.com.", "A", "IN", true) # no validation
|
308
308
|
#
|
309
309
|
#The Recursor maintains a cache of known nameservers.
|
310
310
|
#DNSSEC validation is performed unless true is passed as the fourth parameter.
|
@@ -525,7 +525,9 @@ module Dnsruby
|
|
525
525
|
|
526
526
|
nameservers = []
|
527
527
|
ns.each do |nss|
|
528
|
-
nss.each {|n|
|
528
|
+
nss.each {|n|
|
529
|
+
nameservers.push(n.to_s)
|
530
|
+
}
|
529
531
|
end
|
530
532
|
resolver = Resolver.new({:nameserver=>nameservers})
|
531
533
|
servers = []
|
data/lib/Dnsruby/Resolver.rb
CHANGED
@@ -426,7 +426,6 @@ module Dnsruby
|
|
426
426
|
:use_tcp=>@use_tcp, :no_tcp=>@no_tcp, :packet_timeout=>@packet_timeout,
|
427
427
|
:tsig => @tsig, :ignore_truncation=>@ignore_truncation,
|
428
428
|
:src_address=>@src_address, :src_port=>@src_port,
|
429
|
-
:do_caching=>@do_caching,
|
430
429
|
:recurse=>@recurse, :udp_size=>@udp_size})
|
431
430
|
@single_resolvers.push(res) if res
|
432
431
|
end
|
@@ -453,7 +452,12 @@ module Dnsruby
|
|
453
452
|
end
|
454
453
|
|
455
454
|
# Attributes
|
456
|
-
|
455
|
+
|
456
|
+
# do_validation tells the Resolver whether to try to validate the response
|
457
|
+
# with DNSSEC. This should work for NSEC-signed domains, but NSEC3
|
458
|
+
# validation is not currently supported. This attribute now defaults to
|
459
|
+
# false. Please let me know if you require NSEC3 validation.
|
460
|
+
@do_validation = false
|
457
461
|
@query_timeout = DefaultQueryTimeout
|
458
462
|
@retry_delay = DefaultRetryDelay
|
459
463
|
@retry_times = DefaultRetryTimes
|
data/lib/Dnsruby/dnssec.rb
CHANGED
@@ -77,11 +77,15 @@ module Dnsruby
|
|
77
77
|
|
78
78
|
@@root_verifier = SingleVerifier.new(SingleVerifier::VerifierType::ROOT)
|
79
79
|
|
80
|
+
# #NOTE# You may wish to import these via a secure channel yourself, if
|
81
|
+
# using Dnsruby for validation.
|
82
|
+
@@root_key = RR.create(". IN DS 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5")
|
83
|
+
@@root_verifier.add_root_ds(@@root_key)
|
84
|
+
|
80
85
|
@@dlv_verifier = SingleVerifier.new(SingleVerifier::VerifierType::DLV)
|
81
86
|
|
82
87
|
# @TODO@ Could add a new one of these for each anchor.
|
83
88
|
@@anchor_verifier = SingleVerifier.new(SingleVerifier::VerifierType::ANCHOR)
|
84
|
-
# Should we be loading IANA Trust Anchor Repository? - no need - imported by ISC DLV
|
85
89
|
|
86
90
|
|
87
91
|
# Add a trusted Key Signing Key for the ISC DLV registry.
|
@@ -120,6 +124,7 @@ module Dnsruby
|
|
120
124
|
def self.reset
|
121
125
|
@@validation_policy = ValidationPolicy::LOCAL_ANCHORS_THEN_ROOT
|
122
126
|
@@root_verifier = SingleVerifier.new(SingleVerifier::VerifierType::ROOT)
|
127
|
+
@@root_verifier.add_root_ds(@@root_key)
|
123
128
|
|
124
129
|
@@dlv_verifier = SingleVerifier.new(SingleVerifier::VerifierType::DLV)
|
125
130
|
|
@@ -139,37 +144,6 @@ module Dnsruby
|
|
139
144
|
}
|
140
145
|
return no_keys
|
141
146
|
end
|
142
|
-
# Load the IANA TAR.
|
143
|
-
# THIS METHOD IS NOT SECURE!!!
|
144
|
-
def self.load_itar
|
145
|
-
# @TODO@ THIS IS VERY INSECURE!! WRITE THIS PROPERLY!!
|
146
|
-
# Should really check the signatures here to make sure the keys are good!
|
147
|
-
Net::FTP::open("ftp.iana.org") { |ftp|
|
148
|
-
ftp.login("anonymous")
|
149
|
-
ftp.passive = true
|
150
|
-
ftp.chdir("/itar")
|
151
|
-
lastname=nil
|
152
|
-
ftp.gettextfile("anchors.mf") {|line|
|
153
|
-
next if (line.strip.length == 0)
|
154
|
-
first = line[0]
|
155
|
-
if (first.class == String)
|
156
|
-
first = first.getbyte(0) # Ruby 1.9
|
157
|
-
end
|
158
|
-
# print "Reading ITAR : #{line}, first : #{first}\n"
|
159
|
-
next if (first==59) # ";")
|
160
|
-
if (line.strip=~(/^DS /) || line.strip=~(/^DNSKEY /))
|
161
|
-
line = lastname.to_s + ((lastname.absolute?)?".":"") + " " + line
|
162
|
-
end
|
163
|
-
ds = RR.create(line)
|
164
|
-
if ((ds.type == Types::DS) || (ds.type == Types::DNSKEY))
|
165
|
-
# assert(ds.name.absolute?)
|
166
|
-
Dnssec.add_trust_anchor(ds)
|
167
|
-
end
|
168
|
-
lastname = ds.name
|
169
|
-
}
|
170
|
-
}
|
171
|
-
end
|
172
|
-
|
173
147
|
|
174
148
|
@@do_validation_with_recursor = true # Many nameservers don't handle DNSSEC correctly yet
|
175
149
|
@@default_resolver = Resolver.new
|
data/lib/Dnsruby/message.rb
CHANGED
@@ -591,33 +591,41 @@ module Dnsruby
|
|
591
591
|
#Decode the encoded message
|
592
592
|
def Message.decode(m)
|
593
593
|
o = Message.new()
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
if (
|
613
|
-
|
614
|
-
|
594
|
+
begin
|
595
|
+
MessageDecoder.new(m) {|msg|
|
596
|
+
o.header = Header.new(msg)
|
597
|
+
o.header.qdcount.times {
|
598
|
+
question = msg.get_question
|
599
|
+
o.question << question
|
600
|
+
}
|
601
|
+
o.header.ancount.times {
|
602
|
+
rr = msg.get_rr
|
603
|
+
o.answer << rr
|
604
|
+
}
|
605
|
+
o.header.nscount.times {
|
606
|
+
rr = msg.get_rr
|
607
|
+
o.authority << rr
|
608
|
+
}
|
609
|
+
o.header.arcount.times { |count|
|
610
|
+
start = msg.index
|
611
|
+
rr = msg.get_rr
|
612
|
+
if (rr.type == Types::TSIG)
|
613
|
+
if (count!=o.header.arcount-1)
|
614
|
+
Dnsruby.log.Error("Incoming message has TSIG record before last record")
|
615
|
+
raise DecodeError.new("TSIG record present before last record")
|
616
|
+
end
|
617
|
+
o.tsigstart = start # needed for TSIG verification
|
615
618
|
end
|
616
|
-
o.
|
617
|
-
|
618
|
-
o.additional << rr
|
619
|
+
o.additional << rr
|
620
|
+
}
|
619
621
|
}
|
620
|
-
|
622
|
+
rescue DecodeError => e
|
623
|
+
# So we got a decode error
|
624
|
+
# However, we might have been able to fill in many parts of the message
|
625
|
+
# So let's raise the DecodeError, but add the partially completed message
|
626
|
+
e.partial_message = o
|
627
|
+
raise e
|
628
|
+
end
|
621
629
|
return o
|
622
630
|
end
|
623
631
|
|
data/lib/Dnsruby/resource/DS.rb
CHANGED
@@ -30,7 +30,15 @@ module Dnsruby
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def from_string(input) #:nodoc: all
|
33
|
-
|
33
|
+
strings = TXT.parse(input)
|
34
|
+
cpu = ""
|
35
|
+
os = ""
|
36
|
+
if (strings.length == 1)
|
37
|
+
cpu, os = input.split(" ")
|
38
|
+
else
|
39
|
+
cpu = strings[0]
|
40
|
+
os = strings[1]
|
41
|
+
end
|
34
42
|
cpu.sub!(/^\"/, "")
|
35
43
|
@cpu = cpu.sub(/\"$/, "")
|
36
44
|
os.sub!(/^\"/, "")
|
@@ -38,7 +46,15 @@ module Dnsruby
|
|
38
46
|
end
|
39
47
|
|
40
48
|
def rdata_to_string #:nodoc: all
|
41
|
-
|
49
|
+
if (defined?@cpu)
|
50
|
+
temp = []
|
51
|
+
[@cpu, @os].each {|str|
|
52
|
+
output = TXT.display(str)
|
53
|
+
temp.push("\"#{output}\"")
|
54
|
+
}
|
55
|
+
return temp.join(' ')
|
56
|
+
end
|
57
|
+
return ''
|
42
58
|
end
|
43
59
|
|
44
60
|
def encode_rdata(msg, canonical=false) #:nodoc: all
|
data/lib/Dnsruby/resource/LOC.rb
CHANGED
@@ -89,7 +89,7 @@ module Dnsruby
|
|
89
89
|
def dms2latlon(deg, min, sec, hem)
|
90
90
|
retval=0
|
91
91
|
|
92
|
-
retval = (deg * CONV_DEG) + (min * CONV_MIN) + (sec * CONV_SEC);
|
92
|
+
retval = (deg * CONV_DEG) + (min * CONV_MIN) + (sec * CONV_SEC).round;
|
93
93
|
retval = -retval if ((hem != nil) && ((hem == "S") || (hem == "W")));
|
94
94
|
retval += REFERENCE_LATLON;
|
95
95
|
return retval;
|
@@ -138,23 +138,28 @@ module Dnsruby
|
|
138
138
|
(\s+ ([\d.]+) m?)? # size
|
139
139
|
(\s+ ([\d.]+) m?)? # horiz precision
|
140
140
|
(\s+ ([\d.]+) m?)? # vert precision
|
141
|
-
/ix) #
|
141
|
+
/ix) #
|
142
|
+
|
143
|
+
size = DEFAULT_SIZE
|
142
144
|
|
143
145
|
# What to do for other versions?
|
144
146
|
version = 0;
|
145
147
|
|
146
148
|
horiz_pre = DEFAULT_HORIZ_PRE
|
147
149
|
vert_pre = DEFAULT_VERT_PRE
|
148
|
-
latdeg, latmin, latsec, lathem = $1.to_i, $3.to_i, $5.
|
149
|
-
londeg, lonmin, lonsec, lonhem = $7.to_i, $9.to_i, $11.
|
150
|
-
alt
|
150
|
+
latdeg, latmin, latsec, lathem = $1.to_i, $3.to_i, $5.to_f, $6;
|
151
|
+
londeg, lonmin, lonsec, lonhem = $7.to_i, $9.to_i, $11.to_f, $12
|
152
|
+
alt = $13.to_i
|
153
|
+
if ($15)
|
154
|
+
size = $15.to_f
|
155
|
+
end
|
151
156
|
if ($17)
|
152
|
-
horiz_pre = $17.
|
157
|
+
horiz_pre = $17.to_f
|
153
158
|
end
|
154
159
|
if ($19)
|
155
|
-
vert_pre = $19.
|
160
|
+
vert_pre = $19.to_f
|
156
161
|
end
|
157
|
-
|
162
|
+
|
158
163
|
latmin = DEFAULT_MIN unless latmin;
|
159
164
|
latsec = DEFAULT_SEC unless latsec;
|
160
165
|
lathem = lathem.upcase;
|
@@ -163,8 +168,6 @@ module Dnsruby
|
|
163
168
|
lonsec = DEFAULT_SEC unless lonsec;
|
164
169
|
lonhem = lonhem.upcase
|
165
170
|
|
166
|
-
size = DEFAULT_SIZE unless size;
|
167
|
-
|
168
171
|
@version = version;
|
169
172
|
@size = size * 100;
|
170
173
|
@horiz_pre = horiz_pre * 100;
|
@@ -178,7 +181,7 @@ module Dnsruby
|
|
178
181
|
def from_hash(hash) #:nodoc: all
|
179
182
|
super(hash)
|
180
183
|
if (@size == nil)
|
181
|
-
@size = DEFAULT_SIZE
|
184
|
+
@size = DEFAULT_SIZE * 100
|
182
185
|
end
|
183
186
|
if @horiz_pre == nil
|
184
187
|
@horiz_pre = DEFAULT_HORIZ_PRE * 100
|
@@ -71,22 +71,25 @@ module Dnsruby
|
|
71
71
|
end
|
72
72
|
|
73
73
|
def get_socket_pair
|
74
|
+
# Emulate socketpair on platforms which don't support it
|
75
|
+
srv = nil
|
74
76
|
begin
|
75
|
-
pair = Socket::socketpair(Socket::AF_LOCAL, Socket::SOCK_DGRAM, 0)
|
76
|
-
return pair
|
77
|
-
|
78
|
-
# Emulate socketpair on platforms which don't support it
|
79
|
-
rescue Exception
|
80
77
|
srv = TCPServer.new('localhost', 0)
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
78
|
+
rescue Errno::EADDRNOTAVAIL # OSX Snow Leopard issue - need to use explicit IP
|
79
|
+
begin
|
80
|
+
srv = TCPServer.new('127.0.0.1', 0)
|
81
|
+
rescue Error # Try IPv6
|
82
|
+
srv = TCPServer.new('::1', 0)
|
83
|
+
end
|
85
84
|
end
|
85
|
+
rsock = TCPSocket.new(srv.addr[3], srv.addr[1])
|
86
|
+
lsock = srv.accept
|
87
|
+
srv.close
|
88
|
+
return [lsock, rsock]
|
86
89
|
end
|
87
90
|
|
88
91
|
class QuerySettings
|
89
|
-
attr_accessor :query_bytes, :query, :ignore_truncation, :client_queue,
|
92
|
+
attr_accessor :query_bytes, :query, :ignore_truncation, :client_queue,
|
90
93
|
:client_query_id, :socket, :dest_server, :dest_port, :endtime, :udp_packet_size,
|
91
94
|
:single_resolver
|
92
95
|
# new(query_bytes, query, ignore_truncation, client_queue, client_query_id,
|
@@ -100,7 +103,7 @@ module Dnsruby
|
|
100
103
|
@socket = args[5]
|
101
104
|
@dest_server = args[6]
|
102
105
|
@dest_port=args[7]
|
103
|
-
@endtime = args[8]
|
106
|
+
@endtime = args[8]
|
104
107
|
@udp_packet_size = args[9]
|
105
108
|
@single_resolver = args[10]
|
106
109
|
end
|
@@ -156,8 +159,8 @@ module Dnsruby
|
|
156
159
|
sockets=[]
|
157
160
|
timeouts=[]
|
158
161
|
has_observer = false
|
159
|
-
@@mutex.synchronize {
|
160
|
-
sockets = @@sockets
|
162
|
+
@@mutex.synchronize {
|
163
|
+
sockets = @@sockets
|
161
164
|
timeouts = @@timeouts.values
|
162
165
|
has_observer = !@@observers.empty?
|
163
166
|
}
|
@@ -241,7 +244,7 @@ module Dnsruby
|
|
241
244
|
answeripaddr = IPAddr.new(answerip)
|
242
245
|
dest_server = IPAddr.new("0.0.0.0")
|
243
246
|
begin
|
244
|
-
|
247
|
+
destserveripaddr = IPAddr.new(dest_server)
|
245
248
|
rescue ArgumentError
|
246
249
|
# Host name not IP address
|
247
250
|
end
|
@@ -249,7 +252,7 @@ module Dnsruby
|
|
249
252
|
(answeripaddr != destserveripaddr) &&
|
250
253
|
(answerfrom != dest_server))
|
251
254
|
Dnsruby.log.warn("Unsolicited response received from #{answerip} instead of #{query_settings.dest_server}")
|
252
|
-
else
|
255
|
+
else
|
253
256
|
send_response_to_client(msg, bytes, socket)
|
254
257
|
end
|
255
258
|
end
|
@@ -308,8 +311,8 @@ module Dnsruby
|
|
308
311
|
@@mutex.synchronize{
|
309
312
|
socket = @@query_hash[id].socket
|
310
313
|
@@timeouts.delete(id)
|
311
|
-
@@query_hash.delete(id)
|
312
|
-
@@socket_hash.delete(socket)
|
314
|
+
@@query_hash.delete(id)
|
315
|
+
@@socket_hash.delete(socket)
|
313
316
|
@@sockets.delete(socket) # @TODO@ Not if persistent!
|
314
317
|
}
|
315
318
|
Dnsruby.log.debug{"Closing socket #{socket}"}
|
@@ -348,13 +351,13 @@ module Dnsruby
|
|
348
351
|
}
|
349
352
|
if (buf.length() < expected_length)
|
350
353
|
begin
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
354
|
+
input, = socket.recv_nonblock(expected_length-buf.length)
|
355
|
+
if (input=="")
|
356
|
+
TheLog.info("Bad response from server - no bytes read - ignoring")
|
357
|
+
# @TODO@ Should we do anything about this?
|
358
|
+
return false
|
359
|
+
end
|
360
|
+
buf += input
|
358
361
|
rescue
|
359
362
|
# Oh well - better luck next time!
|
360
363
|
return false
|
@@ -421,13 +424,13 @@ module Dnsruby
|
|
421
424
|
else
|
422
425
|
# recvfrom failed - why?
|
423
426
|
Dnsruby.log.error{"Error - recvfrom failed from #{socket}"}
|
424
|
-
handle_recvfrom_failure(socket, "")
|
427
|
+
handle_recvfrom_failure(socket, "")
|
425
428
|
return
|
426
|
-
end
|
429
|
+
end
|
427
430
|
end
|
428
431
|
rescue Exception => e
|
429
432
|
Dnsruby.log.error{"Error - recvfrom failed from #{socket}, exception : #{e}"}
|
430
|
-
handle_recvfrom_failure(socket, e)
|
433
|
+
handle_recvfrom_failure(socket, e)
|
431
434
|
return
|
432
435
|
end
|
433
436
|
Dnsruby.log.debug{";; answer from #{answerfrom} : #{answersize} bytes\n"}
|
@@ -435,14 +438,45 @@ module Dnsruby
|
|
435
438
|
begin
|
436
439
|
ans = Message.decode(buf)
|
437
440
|
rescue Exception => e
|
438
|
-
# print "DECODE ERROR\n"
|
439
441
|
Dnsruby.log.error{"Decode error! #{e.class}, #{e}\nfor msg (length=#{buf.length}) : #{buf}"}
|
440
|
-
# @TODO@ Should know this from the socket!
|
441
442
|
client_id=get_client_id_from_answerfrom(socket, answerip, answerport)
|
442
|
-
if (client_id
|
443
|
-
|
443
|
+
if (client_id == nil)
|
444
|
+
Dnsruby.log.error{"Decode error from #{answerip} but can't determine packet id"}
|
445
|
+
end
|
446
|
+
# We should check if the TC bit is set (if we can get that far)
|
447
|
+
if ((DecodeError === e) && (e.partial_message.header.tc))
|
448
|
+
Dnsruby.log.error{"Decode error (from {answerip})! Header shows truncation, so trying again over TCP"}
|
449
|
+
# If it is, then we should retry over TCP
|
450
|
+
sent = false
|
451
|
+
@@mutex.synchronize{
|
452
|
+
client_ids = @@socket_hash[socket]
|
453
|
+
# get the queries associated with them
|
454
|
+
client_ids.each do |id|
|
455
|
+
query_header_id=nil
|
456
|
+
query_header_id = @@query_hash[id].query.header.id
|
457
|
+
if (query_header_id == e.partial_message.header.id)
|
458
|
+
# process the response
|
459
|
+
client_queue = nil
|
460
|
+
res = nil
|
461
|
+
query=nil
|
462
|
+
client_queue = @@query_hash[id].client_queue
|
463
|
+
res = @@query_hash[id].single_resolver
|
464
|
+
query = @@query_hash[id].query
|
465
|
+
|
466
|
+
# NOW RESEND OVER TCP!
|
467
|
+
Thread.new {
|
468
|
+
res.send_async(query, client_queue, id, true)
|
469
|
+
}
|
470
|
+
sent = true
|
471
|
+
end
|
472
|
+
end
|
473
|
+
}
|
474
|
+
if !sent
|
475
|
+
send_exception_to_client(e, socket, client_id)
|
476
|
+
end
|
477
|
+
|
444
478
|
else
|
445
|
-
|
479
|
+
send_exception_to_client(e, socket, client_id)
|
446
480
|
end
|
447
481
|
return
|
448
482
|
end
|
@@ -486,7 +520,6 @@ module Dnsruby
|
|
486
520
|
query_settings = @@query_hash[id]
|
487
521
|
if (answerip == query_settings.dest_server && answerport == query_settings.dest_port)
|
488
522
|
# We have a match
|
489
|
-
# - @TODO@ as long as we're not speaking to the same server on two ports!
|
490
523
|
client_id = id
|
491
524
|
break
|
492
525
|
end
|
@@ -516,7 +549,7 @@ module Dnsruby
|
|
516
549
|
else
|
517
550
|
@@select_thread = Thread.new {
|
518
551
|
do_select
|
519
|
-
}
|
552
|
+
}
|
520
553
|
end
|
521
554
|
end
|
522
555
|
|
@@ -637,7 +670,7 @@ module Dnsruby
|
|
637
670
|
def add_observer(client_queue, observer)
|
638
671
|
@@mutex.synchronize {
|
639
672
|
@@observers[client_queue]=observer
|
640
|
-
check_select_thread_synchronized # Is this really necessary? The client should start the thread by sending a query, really...
|
673
|
+
check_select_thread_synchronized # Is this really necessary? The client should start the thread by sending a query, really...
|
641
674
|
if (!@@tick_observers.include?observer)
|
642
675
|
@@tick_observers.push(observer)
|
643
676
|
end
|
@@ -669,7 +702,7 @@ module Dnsruby
|
|
669
702
|
}
|
670
703
|
if (observer)
|
671
704
|
observer.handle_queue_event(client_queue, client_query_id)
|
672
|
-
end
|
705
|
+
end
|
673
706
|
end
|
674
707
|
|
675
708
|
def send_tick_to_observers
|
@@ -31,8 +31,6 @@ module Dnsruby
|
|
31
31
|
@added_dlv_key = false
|
32
32
|
# The DNSKEY RRs for the signed root (when it exists)
|
33
33
|
@root_anchors = KeyCache.new
|
34
|
-
# Could add methods for interacting with root anchors - see test/tc_itar.rb
|
35
|
-
# for example of how to load ITAR trust anchors into dnsruby
|
36
34
|
|
37
35
|
# The set of trust anchors.
|
38
36
|
# If the root is unsigned, then these must be initialised with at least
|
@@ -165,6 +163,10 @@ module Dnsruby
|
|
165
163
|
@trusted_keys.add(k)
|
166
164
|
end
|
167
165
|
|
166
|
+
def add_root_ds(ds)
|
167
|
+
@configured_ds_store.push(ds)
|
168
|
+
end
|
169
|
+
|
168
170
|
# Wipes the cache of trusted keys
|
169
171
|
def clear_trusted_keys
|
170
172
|
@trusted_keys = KeyCache.new
|
data/lib/Dnsruby/zone_reader.rb
CHANGED
@@ -89,7 +89,7 @@ module Dnsruby
|
|
89
89
|
# Add the next line until we see a ")"
|
90
90
|
# REMEMBER TO STRIP OFF COMMENTS!!!
|
91
91
|
@continued_line = strip_comments(@continued_line)
|
92
|
-
line = @continued_line.
|
92
|
+
line = @continued_line.rstrip.chomp + " " + line
|
93
93
|
if (line.index(")"))
|
94
94
|
# OK
|
95
95
|
@continued_line = false
|
data/lib/dnsruby.rb
CHANGED
@@ -104,7 +104,7 @@ require 'Dnsruby/TheLog'
|
|
104
104
|
module Dnsruby
|
105
105
|
|
106
106
|
# @TODO@ Remember to update version in dnsruby.gemspec!
|
107
|
-
VERSION = 1.
|
107
|
+
VERSION = 1.48
|
108
108
|
def Dnsruby.version
|
109
109
|
return VERSION
|
110
110
|
end
|
@@ -468,6 +468,7 @@ module Dnsruby
|
|
468
468
|
|
469
469
|
#Indicates an error in decoding an incoming DNS message
|
470
470
|
class DecodeError < ResolvError
|
471
|
+
attr_accessor :partial_message
|
471
472
|
end
|
472
473
|
|
473
474
|
#Indicates an error encoding a DNS message for transmission
|
data/test/tc_rr.rb
CHANGED
@@ -295,13 +295,22 @@ class TestRR < Test::Unit::TestCase
|
|
295
295
|
rr = RR.create("all.rr.org. IN LOC 42 21 54 N 71 06 18 W -24m 30m")
|
296
296
|
assert(rr.vert_pre == 1000)
|
297
297
|
assert(rr.horiz_pre == 1000000)
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
298
|
+
assert(rr.to_s.index("21"))
|
299
|
+
assert(rr.to_s.index("71"))
|
300
|
+
assert(rr.to_s.index("54"))
|
301
|
+
assert(rr.to_s.index("71"))
|
302
|
+
assert(rr.to_s.index("06"))
|
303
|
+
assert(rr.to_s.index("18"))
|
304
|
+
|
305
|
+
r2 = RR.create("helium IN LOC 51 49 17.9 N 4 39 22.9 E 0m")
|
306
|
+
assert(r2.size == 100)
|
307
|
+
assert(r2.to_s.index("17.9"))
|
308
|
+
assert(r2.to_s.index("22.9"))
|
309
|
+
end
|
310
|
+
|
311
|
+
def test_hinfo
|
312
|
+
rr = RR.create('helium IN HINFO "Shuttle-ST61G4 Intel PIV3000" "FreeBSD 7.0-STABLE"')
|
313
|
+
assert rr.to_s.index('"Shuttle-ST61G4 Intel PIV3000"')
|
314
|
+
assert rr.to_s.index('"FreeBSD 7.0-STABLE"')
|
306
315
|
end
|
307
316
|
end
|
data/test/tc_tcp.rb
CHANGED
@@ -29,13 +29,14 @@ class TestTcp < Test::Unit::TestCase
|
|
29
29
|
end
|
30
30
|
def test_TCP_port
|
31
31
|
# Need a test server so we can tell what port this message was actually sent on!
|
32
|
-
port =
|
32
|
+
port = nil
|
33
33
|
src_port = 57923
|
34
34
|
Dnsruby::PacketSender.clear_caches
|
35
35
|
received_port = nil
|
36
36
|
server_thread = Thread.new {
|
37
|
-
ts = TCPServer.new(
|
38
|
-
|
37
|
+
ts = TCPServer.new(0)
|
38
|
+
port = ts.addr[1]
|
39
|
+
t = ts.accept
|
39
40
|
# Check that the source port was src_port
|
40
41
|
received_port = t.peeraddr()[1]
|
41
42
|
packet = t.recvfrom(2)[0]
|
@@ -77,5 +78,114 @@ class TestTcp < Test::Unit::TestCase
|
|
77
78
|
assert(ret.header.tc, "Message should be truncated with no TCP")
|
78
79
|
end
|
79
80
|
|
81
|
+
class HackMessage < Dnsruby::Message
|
82
|
+
def wipe_additional
|
83
|
+
@additional = Dnsruby::Message::Section.new(self)
|
84
|
+
end
|
85
|
+
|
86
|
+
#Decode the encoded message
|
87
|
+
def HackMessage.decode(m)
|
88
|
+
o = HackMessage.new()
|
89
|
+
begin
|
90
|
+
Dnsruby::MessageDecoder.new(m) {|msg|
|
91
|
+
o.header = Dnsruby::Header.new(msg)
|
92
|
+
o.header.qdcount.times {
|
93
|
+
question = msg.get_question
|
94
|
+
o.question << question
|
95
|
+
}
|
96
|
+
o.header.ancount.times {
|
97
|
+
rr = msg.get_rr
|
98
|
+
o.answer << rr
|
99
|
+
}
|
100
|
+
o.header.nscount.times {
|
101
|
+
rr = msg.get_rr
|
102
|
+
o.authority << rr
|
103
|
+
}
|
104
|
+
o.header.arcount.times { |count|
|
105
|
+
start = msg.index
|
106
|
+
rr = msg.get_rr
|
107
|
+
if (rr.type == Dnsruby::Types::TSIG)
|
108
|
+
if (count!=o.header.arcount-1)
|
109
|
+
Dnsruby.log.Error("Incoming message has TSIG record before last record")
|
110
|
+
raise Dnsruby::DecodeError.new("TSIG record present before last record")
|
111
|
+
end
|
112
|
+
o.tsigstart = start # needed for TSIG verification
|
113
|
+
end
|
114
|
+
o.additional << rr
|
115
|
+
}
|
116
|
+
}
|
117
|
+
rescue Dnsruby::DecodeError => e
|
118
|
+
# So we got a decode error
|
119
|
+
# However, we might have been able to fill in many parts of the message
|
120
|
+
# So let's raise the DecodeError, but add the partially completed message
|
121
|
+
e.partial_message = o
|
122
|
+
raise e
|
123
|
+
end
|
124
|
+
return o
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_bad_truncation
|
130
|
+
# Some servers don't do truncation properly.
|
131
|
+
# Make a UDP server which returns large badly formatted packets (arcount > num_additional), with TC bit set
|
132
|
+
# And make a TCP server which returns large well formatted packets
|
133
|
+
# Then make sure that Dnsruby recieves response correctly.
|
134
|
+
Dnsruby::PacketSender.clear_caches
|
135
|
+
socket = UDPSocket.new
|
136
|
+
socket.bind("127.0.0.1", 0)
|
137
|
+
port = socket.addr[1]
|
138
|
+
Thread.new {
|
139
|
+
s = socket.recvfrom(65536)
|
140
|
+
received_query = s[0]
|
141
|
+
socket.connect(s[1][2], s[1][1])
|
142
|
+
ans = HackMessage.decode(received_query)
|
143
|
+
ans.wipe_additional
|
144
|
+
100.times {|i|
|
145
|
+
ans.add_additional(Dnsruby::RR.create("example.com 3600 IN A 1.2.3.#{i}"))
|
146
|
+
}
|
147
|
+
ans.header.arcount = 110
|
148
|
+
ans.header.tc = true
|
149
|
+
socket.send(ans.encode,0)
|
150
|
+
}
|
151
|
+
|
152
|
+
server_thread = Thread.new {
|
153
|
+
ts = TCPServer.new(port)
|
154
|
+
t = ts.accept
|
155
|
+
packet = t.recvfrom(2)[0]
|
156
|
+
|
157
|
+
len = (packet[0]<<8)+packet[1]
|
158
|
+
if (RUBY_VERSION >= "1.9")
|
159
|
+
len = (packet[0].getbyte(0)<<8)+packet[1].getbyte(0)# Ruby 1.9
|
160
|
+
end
|
161
|
+
packet = t.recvfrom(len)[0]
|
162
|
+
tcpPacket = HackMessage.decode(packet)
|
163
|
+
tcpPacket.wipe_additional
|
164
|
+
110.times {|i|
|
165
|
+
tcpPacket.add_additional(Dnsruby::RR.create("example.com 3600 IN A 1.2.3.#{i}"))
|
166
|
+
}
|
167
|
+
lenmsg = [tcpPacket.encode.length].pack('n')
|
168
|
+
t.send(lenmsg, 0)
|
169
|
+
t.write(tcpPacket.encode)
|
170
|
+
t.close
|
171
|
+
ts.close
|
172
|
+
}
|
173
|
+
|
174
|
+
|
175
|
+
|
176
|
+
# Now send query
|
177
|
+
res = Dnsruby::Resolver.new("127.0.0.1")
|
178
|
+
res.port = port
|
179
|
+
res.udp_size = 4096
|
180
|
+
assert(res.udp_size == 4096)
|
181
|
+
ret = res.query("example.com")
|
182
|
+
assert(ret.header.arcount == 110)
|
183
|
+
count = 0
|
184
|
+
ret.additional.each {|rr| count += 1}
|
185
|
+
assert(count == 110)
|
186
|
+
|
187
|
+
|
188
|
+
end
|
189
|
+
|
80
190
|
#@TODO@ Check stuff like persistent sockets
|
81
191
|
end
|
data/test/tc_validator.rb
CHANGED
@@ -25,6 +25,7 @@ class TestValidator < Test::Unit::TestCase
|
|
25
25
|
Dnsruby::Dnssec.clear_trust_anchors
|
26
26
|
res = Dnsruby::Resolver.new("dnssec.nominet.org.uk")
|
27
27
|
res.dnssec=true
|
28
|
+
res.do_validation = true
|
28
29
|
Dnsruby::Dnssec.do_validation_with_recursor(false)
|
29
30
|
Dnsruby::Dnssec.default_resolver=(res) # This is a closed zone (not reachable by recursion)
|
30
31
|
|
data/test/ts_online.rb
CHANGED
@@ -93,7 +93,6 @@ if (online)
|
|
93
93
|
puts "Running DNSSEC test - may fail if OpenSSL not complete"
|
94
94
|
puts "------------------------------------------------------"
|
95
95
|
require "test/tc_verifier.rb"
|
96
|
-
require "test/tc_itar.rb"
|
97
96
|
require "test/tc_dlv.rb"
|
98
97
|
require "test/tc_validator.rb"
|
99
98
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dnsruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: "1.
|
4
|
+
version: "1.48"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- AlexD
|
@@ -9,7 +9,7 @@ autorequire: dnsruby
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-
|
12
|
+
date: 2010-07-23 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -39,7 +39,6 @@ files:
|
|
39
39
|
- test/tc_header.rb
|
40
40
|
- test/tc_hip.rb
|
41
41
|
- test/tc_ipseckey.rb
|
42
|
-
- test/tc_itar.rb
|
43
42
|
- test/tc_misc.rb
|
44
43
|
- test/tc_name.rb
|
45
44
|
- test/tc_naptr.rb
|
@@ -141,7 +140,7 @@ files:
|
|
141
140
|
- demo/check_soa.rb
|
142
141
|
- demo/check_zone.rb
|
143
142
|
- demo/digdlv.rb
|
144
|
-
- demo/
|
143
|
+
- demo/digroot.rb
|
145
144
|
- demo/example_recurse.rb
|
146
145
|
- demo/mresolv.rb
|
147
146
|
- demo/mx.rb
|
data/test/tc_itar.rb
DELETED
@@ -1,82 +0,0 @@
|
|
1
|
-
#--
|
2
|
-
#Copyright 2007 Nominet UK
|
3
|
-
#
|
4
|
-
#Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
-
#you may not use this file except in compliance with the License.
|
6
|
-
#You may obtain a copy of the License at
|
7
|
-
#
|
8
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
-
#
|
10
|
-
#Unless required by applicable law or agreed to in writing, software
|
11
|
-
#distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
-
#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
-
#See the License for the specific language governing permissions and
|
14
|
-
#limitations under the License.
|
15
|
-
#++
|
16
|
-
|
17
|
-
require 'test/unit'
|
18
|
-
require 'dnsruby'
|
19
|
-
include Dnsruby
|
20
|
-
|
21
|
-
class TestItar < Test::Unit::TestCase
|
22
|
-
def test_itar
|
23
|
-
Dnsruby::Dnssec.reset
|
24
|
-
Dnsruby::PacketSender.clear_caches
|
25
|
-
Dnsruby::Recursor.clear_caches
|
26
|
-
run_test_se(true)
|
27
|
-
Dnsruby::Dnssec.reset
|
28
|
-
Dnsruby::Recursor.clear_caches
|
29
|
-
|
30
|
-
# Then try to validate some records in the published zones
|
31
|
-
Dnsruby::PacketSender.clear_caches
|
32
|
-
# Download the ITAR - add the DS records to dnssec
|
33
|
-
Dnssec.load_itar()
|
34
|
-
|
35
|
-
run_test_se(false)
|
36
|
-
end
|
37
|
-
|
38
|
-
def test_with_no_dlv_anchor
|
39
|
-
Dnsruby::Dnssec.reset
|
40
|
-
Dnsruby::PacketSender.clear_caches
|
41
|
-
Dnsruby::Recursor.clear_caches
|
42
|
-
# Make sure we don't have any other anchors configured!
|
43
|
-
res = Dnsruby::Recursor.new
|
44
|
-
ret = res.query("frobbit.se.", Dnsruby::Types.A)
|
45
|
-
assert(ret.security_level == Dnsruby::Message::SecurityLevel::INSECURE, "Level = #{ret.security_level.string}")
|
46
|
-
Dnsruby::Dnssec.reset
|
47
|
-
Dnsruby::PacketSender.clear_caches
|
48
|
-
Dnsruby::Recursor.clear_caches
|
49
|
-
Dnssec.load_itar
|
50
|
-
res = Dnsruby::Recursor.new
|
51
|
-
ret = res.query("frobbit.se.", Dnsruby::Types.A)
|
52
|
-
assert(ret.security_level == Dnsruby::Message::SecurityLevel::SECURE)
|
53
|
-
|
54
|
-
res = Dnsruby::Recursor.new
|
55
|
-
ret = res.query("ns2.nic.se.", Dnsruby::Types.A)
|
56
|
-
assert(ret.security_level == Dnsruby::Message::SecurityLevel::SECURE)
|
57
|
-
end
|
58
|
-
|
59
|
-
def run_test_se(should_fail)
|
60
|
-
res = Dnsruby::Recursor.new
|
61
|
-
r = res.query("frobbit.se.", Dnsruby::Types.A)
|
62
|
-
if (!should_fail)
|
63
|
-
assert(r.security_level == Dnsruby::Message::SecurityLevel::SECURE)
|
64
|
-
else
|
65
|
-
assert(r.security_level != Dnsruby::Message::SecurityLevel::SECURE)
|
66
|
-
end
|
67
|
-
# Haven't configured key for this, so should fail
|
68
|
-
begin
|
69
|
-
ret = Dnssec.verify(r)
|
70
|
-
if (should_fail)
|
71
|
-
fail
|
72
|
-
end
|
73
|
-
rescue (Dnsruby::VerifyError)
|
74
|
-
if (!should_fail)
|
75
|
-
fail
|
76
|
-
end
|
77
|
-
end
|
78
|
-
# assert(!ret, "Dnssec message verification failed")
|
79
|
-
|
80
|
-
end
|
81
|
-
|
82
|
-
end
|