dnsruby 1.47 → 1.48
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/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
|