dnsruby 1.55 → 1.56.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +96 -0
- data/Rakefile +30 -29
- data/demo/axfr.rb +93 -93
- data/demo/check_soa.rb +99 -99
- data/demo/check_zone.rb +59 -59
- data/demo/digdlv.rb +43 -43
- data/demo/digroot.rb +34 -34
- data/demo/example_recurse.rb +14 -14
- data/demo/mresolv.rb +30 -30
- data/demo/mx.rb +31 -31
- data/demo/rubydig.rb +37 -37
- data/demo/to_resolve.txt +3088 -3088
- data/demo/trace_dns.rb +46 -46
- data/lib/dnsruby.rb +161 -526
- data/lib/dnsruby/DNS.rb +305 -0
- data/lib/{Dnsruby/Cache.rb → dnsruby/cache.rb} +152 -152
- data/lib/{Dnsruby → dnsruby}/code_mapper.rb +48 -52
- data/lib/dnsruby/code_mappers.rb +295 -0
- data/lib/{Dnsruby/Config.rb → dnsruby/config.rb} +454 -454
- data/lib/{Dnsruby → dnsruby}/dnssec.rb +91 -91
- data/lib/{Dnsruby/Hosts.rb → dnsruby/hosts.rb} +125 -125
- data/lib/{Dnsruby → dnsruby}/ipv4.rb +26 -26
- data/lib/{Dnsruby → dnsruby}/ipv6.rb +42 -42
- data/lib/{Dnsruby → dnsruby}/key_cache.rb +29 -29
- data/lib/dnsruby/message/decoder.rb +164 -0
- data/lib/dnsruby/message/encoder.rb +75 -0
- data/lib/dnsruby/message/header.rb +249 -0
- data/lib/dnsruby/message/message.rb +629 -0
- data/lib/dnsruby/message/question.rb +86 -0
- data/lib/dnsruby/message/section.rb +96 -0
- data/lib/{Dnsruby → dnsruby}/name.rb +141 -141
- data/lib/dnsruby/packet_sender.rb +661 -0
- data/lib/{Dnsruby/Recursor.rb → dnsruby/recursor.rb} +235 -233
- data/lib/dnsruby/resolv.rb +113 -0
- data/lib/dnsruby/resolver.rb +1192 -0
- data/lib/dnsruby/resource/A.rb +56 -0
- data/lib/dnsruby/resource/AAAA.rb +54 -0
- data/lib/{Dnsruby → dnsruby}/resource/AFSDB.rb +68 -68
- data/lib/{Dnsruby → dnsruby}/resource/CERT.rb +105 -105
- data/lib/{Dnsruby → dnsruby}/resource/DHCID.rb +54 -54
- data/lib/dnsruby/resource/DLV.rb +27 -0
- data/lib/{Dnsruby → dnsruby}/resource/DNSKEY.rb +372 -372
- data/lib/{Dnsruby → dnsruby}/resource/DS.rb +255 -255
- data/lib/{Dnsruby → dnsruby}/resource/HINFO.rb +71 -71
- data/lib/{Dnsruby → dnsruby}/resource/HIP.rb +29 -29
- data/lib/{Dnsruby → dnsruby}/resource/IN.rb +30 -30
- data/lib/{Dnsruby → dnsruby}/resource/IPSECKEY.rb +31 -31
- data/lib/{Dnsruby → dnsruby}/resource/ISDN.rb +62 -62
- data/lib/{Dnsruby → dnsruby}/resource/KX.rb +65 -65
- data/lib/{Dnsruby → dnsruby}/resource/LOC.rb +263 -263
- data/lib/{Dnsruby → dnsruby}/resource/MINFO.rb +69 -69
- data/lib/{Dnsruby → dnsruby}/resource/MX.rb +65 -65
- data/lib/{Dnsruby → dnsruby}/resource/NAPTR.rb +98 -98
- data/lib/{Dnsruby → dnsruby}/resource/NSAP.rb +171 -171
- data/lib/dnsruby/resource/NSEC.rb +275 -0
- data/lib/dnsruby/resource/NSEC3.rb +332 -0
- data/lib/dnsruby/resource/NSEC3PARAM.rb +135 -0
- data/lib/dnsruby/resource/OPT.rb +272 -0
- data/lib/{Dnsruby → dnsruby}/resource/PX.rb +70 -70
- data/lib/{Dnsruby → dnsruby}/resource/RP.rb +75 -75
- data/lib/dnsruby/resource/RR.rb +421 -0
- data/lib/dnsruby/resource/RRSIG.rb +275 -0
- data/lib/dnsruby/resource/RRSet.rb +190 -0
- data/lib/{Dnsruby → dnsruby}/resource/RT.rb +67 -67
- data/lib/{Dnsruby → dnsruby}/resource/SOA.rb +94 -94
- data/lib/dnsruby/resource/SPF.rb +29 -0
- data/lib/dnsruby/resource/SRV.rb +112 -0
- data/lib/{Dnsruby → dnsruby}/resource/SSHFP.rb +14 -14
- data/lib/dnsruby/resource/TKEY.rb +163 -0
- data/lib/dnsruby/resource/TSIG.rb +593 -0
- data/lib/{Dnsruby → dnsruby}/resource/TXT.rb +191 -191
- data/lib/dnsruby/resource/X25.rb +55 -0
- data/lib/{Dnsruby → dnsruby}/resource/domain_name.rb +25 -25
- data/lib/{Dnsruby → dnsruby}/resource/generic.rb +80 -80
- data/lib/dnsruby/resource/resource.rb +25 -0
- data/lib/{Dnsruby → dnsruby}/select_thread.rb +148 -148
- data/lib/{Dnsruby/SingleResolver.rb → dnsruby/single_resolver.rb} +60 -60
- data/lib/{Dnsruby → dnsruby}/single_verifier.rb +344 -344
- data/lib/dnsruby/the_log.rb +44 -0
- data/lib/dnsruby/update.rb +278 -0
- data/lib/dnsruby/validator_thread.rb +124 -0
- data/lib/dnsruby/version.rb +3 -0
- data/lib/{Dnsruby → dnsruby}/zone_reader.rb +93 -93
- data/lib/{Dnsruby → dnsruby}/zone_transfer.rb +377 -377
- data/test/spec_helper.rb +16 -0
- data/test/tc_axfr.rb +31 -34
- data/test/tc_cache.rb +32 -32
- data/test/tc_dlv.rb +28 -28
- data/test/tc_dns.rb +73 -76
- data/test/tc_dnskey.rb +31 -32
- data/test/tc_dnsruby.rb +50 -44
- data/test/tc_ds.rb +36 -36
- data/test/tc_escapedchars.rb +252 -255
- data/test/tc_hash.rb +17 -21
- data/test/tc_header.rb +48 -57
- data/test/tc_hip.rb +19 -22
- data/test/tc_ipseckey.rb +18 -21
- data/test/tc_keith.rb +300 -0
- data/test/tc_message.rb +87 -0
- data/test/tc_misc.rb +83 -87
- data/test/tc_name.rb +81 -84
- data/test/tc_naptr.rb +18 -21
- data/test/tc_nsec.rb +55 -55
- data/test/tc_nsec3.rb +23 -24
- data/test/tc_nsec3param.rb +20 -21
- data/test/tc_packet.rb +90 -93
- data/test/tc_packet_unique_push.rb +48 -51
- data/test/tc_question.rb +30 -33
- data/test/tc_queue.rb +16 -17
- data/test/tc_recur.rb +16 -17
- data/test/tc_res_config.rb +38 -41
- data/test/tc_res_env.rb +29 -32
- data/test/tc_res_file.rb +26 -29
- data/test/tc_res_opt.rb +62 -65
- data/test/tc_resolver.rb +287 -242
- data/test/tc_rr-opt.rb +70 -63
- data/test/tc_rr-txt.rb +68 -71
- data/test/tc_rr-unknown.rb +45 -48
- data/test/tc_rr.rb +76 -70
- data/test/tc_rrset.rb +21 -22
- data/test/tc_rrsig.rb +19 -20
- data/test/tc_single_resolver.rb +294 -297
- data/test/tc_soak.rb +199 -202
- data/test/tc_soak_base.rb +29 -34
- data/test/tc_sshfp.rb +20 -23
- data/test/tc_tcp.rb +32 -35
- data/test/tc_tkey.rb +41 -44
- data/test/tc_tsig.rb +81 -84
- data/test/tc_update.rb +108 -111
- data/test/tc_validator.rb +29 -29
- data/test/tc_verifier.rb +81 -82
- data/test/ts_dnsruby.rb +16 -15
- data/test/ts_offline.rb +62 -63
- data/test/ts_online.rb +115 -115
- metadata +155 -90
- data/README +0 -59
- data/lib/Dnsruby/DNS.rb +0 -305
- data/lib/Dnsruby/PacketSender.rb +0 -656
- data/lib/Dnsruby/Resolver.rb +0 -1189
- data/lib/Dnsruby/TheLog.rb +0 -44
- data/lib/Dnsruby/message.rb +0 -1230
- data/lib/Dnsruby/resource/A.rb +0 -56
- data/lib/Dnsruby/resource/AAAA.rb +0 -54
- data/lib/Dnsruby/resource/DLV.rb +0 -27
- data/lib/Dnsruby/resource/NSEC.rb +0 -298
- data/lib/Dnsruby/resource/NSEC3.rb +0 -340
- data/lib/Dnsruby/resource/NSEC3PARAM.rb +0 -135
- data/lib/Dnsruby/resource/OPT.rb +0 -213
- data/lib/Dnsruby/resource/RRSIG.rb +0 -275
- data/lib/Dnsruby/resource/SPF.rb +0 -29
- data/lib/Dnsruby/resource/SRV.rb +0 -112
- data/lib/Dnsruby/resource/TKEY.rb +0 -163
- data/lib/Dnsruby/resource/TSIG.rb +0 -593
- data/lib/Dnsruby/resource/X25.rb +0 -55
- data/lib/Dnsruby/resource/resource.rb +0 -678
- data/lib/Dnsruby/update.rb +0 -278
- data/lib/Dnsruby/validator_thread.rb +0 -124
@@ -0,0 +1,75 @@
|
|
1
|
+
module Dnsruby
|
2
|
+
class MessageEncoder #:nodoc: all
|
3
|
+
def initialize
|
4
|
+
@data = ''
|
5
|
+
@names = {}
|
6
|
+
yield self if block_given?
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_s
|
10
|
+
@data
|
11
|
+
end
|
12
|
+
|
13
|
+
def put_bytes(d)
|
14
|
+
@data << d
|
15
|
+
end
|
16
|
+
|
17
|
+
def put_pack(template, *d)
|
18
|
+
@data << d.pack(template)
|
19
|
+
end
|
20
|
+
|
21
|
+
def put_length16
|
22
|
+
length_index = @data.length
|
23
|
+
@data << "\0\0"
|
24
|
+
data_start = @data.length
|
25
|
+
yield
|
26
|
+
data_end = @data.length
|
27
|
+
@data[length_index, 2] = [data_end - data_start].pack("n")
|
28
|
+
end
|
29
|
+
|
30
|
+
def put_string(d)
|
31
|
+
self.put_pack("C", d.length)
|
32
|
+
@data << d
|
33
|
+
end
|
34
|
+
|
35
|
+
def put_string_list(ds)
|
36
|
+
ds.each { |d| self.put_string(d) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def put_rr(rr, canonical=false)
|
40
|
+
# RFC4034 Section 6.2
|
41
|
+
put_name(rr.name, canonical)
|
42
|
+
put_pack('nnN', rr.type.code, rr.klass.code, rr.ttl)
|
43
|
+
put_length16 { rr.encode_rdata(self, canonical) }
|
44
|
+
end
|
45
|
+
|
46
|
+
def put_name(d, canonical = false, downcase = canonical)
|
47
|
+
# DNSSEC requires some records (e.g. NSEC, RRSIG) to be canonicalised, but
|
48
|
+
# not downcased. YUK!
|
49
|
+
d = d.downcase if downcase
|
50
|
+
put_labels(d.to_a, canonical)
|
51
|
+
end
|
52
|
+
|
53
|
+
def put_labels(d, do_canonical)
|
54
|
+
d.each_index do |i|
|
55
|
+
domain = d[i..-1].join('.')
|
56
|
+
if !do_canonical && (idx = @names[domain])
|
57
|
+
self.put_pack('n', 0xc000 | idx)
|
58
|
+
return
|
59
|
+
else
|
60
|
+
@names[domain] = @data.length
|
61
|
+
self.put_label(d[i])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
@data << "\0"
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
def put_label(d)
|
69
|
+
# s, = Name.encode(d)
|
70
|
+
s = d
|
71
|
+
raise RuntimeError, "length of #{s} is #{s.string.length} (larger than 63 octets)" if s.string.length > 63
|
72
|
+
self.put_string(s.string)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,249 @@
|
|
1
|
+
module Dnsruby
|
2
|
+
|
3
|
+
# The header portion of a DNS packet
|
4
|
+
#
|
5
|
+
# RFC 1035 Section 4.1.1
|
6
|
+
class Header
|
7
|
+
MAX_ID = 65535
|
8
|
+
|
9
|
+
# The header ID
|
10
|
+
attr_accessor :id
|
11
|
+
|
12
|
+
# The query response flag
|
13
|
+
attr_accessor :qr
|
14
|
+
|
15
|
+
# Authoritative answer flag
|
16
|
+
attr_accessor :aa
|
17
|
+
|
18
|
+
# Truncated flag
|
19
|
+
attr_accessor :tc
|
20
|
+
|
21
|
+
# Recursion Desired flag
|
22
|
+
attr_accessor :rd
|
23
|
+
|
24
|
+
# The Checking Disabled flag
|
25
|
+
attr_accessor :cd
|
26
|
+
|
27
|
+
# The Authenticated Data flag
|
28
|
+
# Relevant in DNSSEC context.
|
29
|
+
# (The AD bit is only set on answers where signatures have been
|
30
|
+
# cryptographically verified or the server is authoritative for the data
|
31
|
+
# and is allowed to set the bit by policy.)
|
32
|
+
attr_accessor :ad
|
33
|
+
|
34
|
+
# The query response flag
|
35
|
+
attr_accessor :qr
|
36
|
+
|
37
|
+
# Recursion available flag
|
38
|
+
attr_accessor :ra
|
39
|
+
|
40
|
+
# Query response code
|
41
|
+
# deprecated - use Message#rcode
|
42
|
+
# attr_reader :rcode
|
43
|
+
|
44
|
+
# This new get_header_rcode method is intended for use only by the Message class.
|
45
|
+
# This is because the Message OPT section may contain an extended rcode (see
|
46
|
+
# RFC 2671 section 4.6). Using the header rcode only ignores this extension, and
|
47
|
+
# is not recommended.
|
48
|
+
def get_header_rcode
|
49
|
+
@rcode
|
50
|
+
end
|
51
|
+
|
52
|
+
# The header opcode
|
53
|
+
attr_reader :opcode
|
54
|
+
|
55
|
+
# The number of records in the question section of the message
|
56
|
+
attr_accessor :qdcount
|
57
|
+
# The number of records in the authoriy section of the message
|
58
|
+
attr_accessor :nscount
|
59
|
+
# The number of records in the answer section of the message
|
60
|
+
attr_accessor :ancount
|
61
|
+
# The number of records in the additional record section og the message
|
62
|
+
attr_accessor :arcount
|
63
|
+
|
64
|
+
def initialize(*args)
|
65
|
+
if (args.length == 0)
|
66
|
+
@id = rand(MAX_ID)
|
67
|
+
@qr = false
|
68
|
+
@opcode = OpCode.Query
|
69
|
+
@aa = false
|
70
|
+
@ad = false
|
71
|
+
@tc = false
|
72
|
+
@rd = false # recursion desired
|
73
|
+
@ra = false # recursion available
|
74
|
+
@cd = false
|
75
|
+
@rcode = RCode.NoError
|
76
|
+
@qdcount = 0
|
77
|
+
@nscount = 0
|
78
|
+
@ancount = 0
|
79
|
+
@arcount = 0
|
80
|
+
elsif args.length == 1
|
81
|
+
decode(args[0])
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def opcode=(op)
|
86
|
+
@opcode = OpCode.new(op)
|
87
|
+
end
|
88
|
+
|
89
|
+
def rcode=(rcode)
|
90
|
+
@rcode = RCode.new(rcode)
|
91
|
+
end
|
92
|
+
|
93
|
+
def Header.new_from_data(data)
|
94
|
+
header = Header.new
|
95
|
+
MessageDecoder.new(data) { |msg| header.decode(msg) }
|
96
|
+
header
|
97
|
+
end
|
98
|
+
|
99
|
+
def data
|
100
|
+
MessageEncoder.new { |msg| self.encode(msg) }.to_s
|
101
|
+
end
|
102
|
+
|
103
|
+
def encode(msg)
|
104
|
+
msg.put_pack('nnnnnn',
|
105
|
+
@id,
|
106
|
+
(@qr ? 1:0) << 15 |
|
107
|
+
(@opcode.code & 15) << 11 |
|
108
|
+
(@aa ? 1:0) << 10 |
|
109
|
+
(@tc ? 1:0) << 9 |
|
110
|
+
(@rd ? 1:0) << 8 |
|
111
|
+
(@ra ? 1:0) << 7 |
|
112
|
+
(@ad ? 1:0) << 5 |
|
113
|
+
(@cd ? 1:0) << 4 |
|
114
|
+
(@rcode.code & 15),
|
115
|
+
@qdcount,
|
116
|
+
@ancount,
|
117
|
+
@nscount,
|
118
|
+
@arcount)
|
119
|
+
end
|
120
|
+
|
121
|
+
def Header.decrement_arcount_encoded(bytes)
|
122
|
+
header = Header.new
|
123
|
+
header_end = 0
|
124
|
+
MessageDecoder.new(bytes) do |msg|
|
125
|
+
header.decode(msg)
|
126
|
+
header_end = msg.index
|
127
|
+
end
|
128
|
+
header.arcount -= 1
|
129
|
+
bytes[0, header_end] = MessageEncoder.new { |msg| header.encode(msg) }.to_s
|
130
|
+
bytes
|
131
|
+
end
|
132
|
+
|
133
|
+
def ==(other)
|
134
|
+
@qr == other.qr &&
|
135
|
+
@opcode == other.opcode &&
|
136
|
+
@aa == other.aa &&
|
137
|
+
@tc == other.tc &&
|
138
|
+
@rd == other.rd &&
|
139
|
+
@ra == other.ra &&
|
140
|
+
@cd == other.cd &&
|
141
|
+
@ad == other.ad &&
|
142
|
+
@rcode == other.get_header_rcode
|
143
|
+
end
|
144
|
+
|
145
|
+
def to_s
|
146
|
+
to_s_with_rcode(@rcode)
|
147
|
+
end
|
148
|
+
|
149
|
+
def old_to_s
|
150
|
+
old_to_s_with_rcode(@rcode)
|
151
|
+
end
|
152
|
+
|
153
|
+
def to_s_with_rcode(rcode)
|
154
|
+
|
155
|
+
if @opcode == OpCode::Update
|
156
|
+
s = ";; id = #{@id}\n"
|
157
|
+
s << ";; qr = #{@qr} opcode = #{@opcode.string} rcode = #{@rcode.string}\n"
|
158
|
+
s << ";; zocount = #{@qdcount} "
|
159
|
+
s << "prcount = #{@ancount} "
|
160
|
+
s << "upcount = #{@nscount} "
|
161
|
+
s << "adcount = #{@arcount}\n"
|
162
|
+
s
|
163
|
+
else
|
164
|
+
|
165
|
+
flags_str = begin
|
166
|
+
flags = []
|
167
|
+
flags << 'qr' if @qr
|
168
|
+
flags << 'aa' if @aa
|
169
|
+
flags << 'tc' if @tc
|
170
|
+
flags << 'rd' if @rd
|
171
|
+
flags << 'ra' if @ra
|
172
|
+
flags << 'ad' if @ad
|
173
|
+
flags << 'cd' if @cd
|
174
|
+
|
175
|
+
";; flags: #{flags.join(' ')}; "
|
176
|
+
end
|
177
|
+
|
178
|
+
head_line_str =
|
179
|
+
";; ->>HEADER<<- opcode: #{opcode.string.upcase}, status: #{@rcode.string}, id: #{@id}\n"
|
180
|
+
|
181
|
+
section_counts_str =
|
182
|
+
"QUERY: #{@qdcount}, ANSWER: #{@ancount}, AUTHORITY: #{@nscount}, ADDITIONAL: #{@arcount}\n"
|
183
|
+
|
184
|
+
head_line_str + flags_str + section_counts_str
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
def old_to_s_with_rcode(rcode)
|
190
|
+
retval = ";; id = #{@id}\n"
|
191
|
+
|
192
|
+
if (@opcode == OpCode::Update)
|
193
|
+
retval += ";; qr = #{@qr} " +\
|
194
|
+
"opcode = #{@opcode.string} "+\
|
195
|
+
"rcode = #{@rcode.string}\n"
|
196
|
+
|
197
|
+
retval += ";; zocount = #{@qdcount} "+\
|
198
|
+
"prcount = #{@ancount} " +\
|
199
|
+
"upcount = #{@nscount} " +\
|
200
|
+
"adcount = #{@arcount}\n"
|
201
|
+
else
|
202
|
+
retval += ";; qr = #{@qr} " +\
|
203
|
+
"opcode = #{@opcode.string} " +\
|
204
|
+
"aa = #{@aa} " +\
|
205
|
+
"tc = #{@tc} " +\
|
206
|
+
"rd = #{@rd}\n"
|
207
|
+
|
208
|
+
retval += ";; ra = #{@ra} " +\
|
209
|
+
"ad = #{@ad} " +\
|
210
|
+
"cd = #{@cd} " +\
|
211
|
+
"rcode = #{rcode.string}\n"
|
212
|
+
|
213
|
+
retval += ";; qdcount = #{@qdcount} " +\
|
214
|
+
"ancount = #{@ancount} " +\
|
215
|
+
"nscount = #{@nscount} " +\
|
216
|
+
"arcount = #{@arcount}\n"
|
217
|
+
end
|
218
|
+
|
219
|
+
retval
|
220
|
+
end
|
221
|
+
|
222
|
+
def decode(msg)
|
223
|
+
@id, flag, @qdcount, @ancount, @nscount, @arcount =
|
224
|
+
msg.get_unpack('nnnnnn')
|
225
|
+
@qr = ((flag >> 15) & 1) == 1
|
226
|
+
@opcode = OpCode.new((flag >> 11) & 15)
|
227
|
+
@aa = ((flag >> 10) & 1) == 1
|
228
|
+
@tc = ((flag >> 9) & 1) == 1
|
229
|
+
@rd = ((flag >> 8) & 1) == 1
|
230
|
+
@ra = ((flag >> 7) & 1) == 1
|
231
|
+
@ad = ((flag >> 5) & 1) == 1
|
232
|
+
@cd = ((flag >> 4) & 1) == 1
|
233
|
+
@rcode = RCode.new(flag & 15)
|
234
|
+
end
|
235
|
+
|
236
|
+
alias zocount qdcount
|
237
|
+
alias zocount= qdcount=
|
238
|
+
|
239
|
+
alias prcount ancount
|
240
|
+
alias prcount= ancount=
|
241
|
+
|
242
|
+
alias upcount nscount
|
243
|
+
alias upcount= nscount=
|
244
|
+
|
245
|
+
alias adcount arcount
|
246
|
+
alias adcount= arcount=
|
247
|
+
|
248
|
+
end
|
249
|
+
end
|
@@ -0,0 +1,629 @@
|
|
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 'dnsruby/name'
|
18
|
+
require 'dnsruby/resource/resource'
|
19
|
+
|
20
|
+
|
21
|
+
module Dnsruby
|
22
|
+
# ===Defines a DNS packet.
|
23
|
+
#
|
24
|
+
# RFC 1035 Section 4.1, RFC 2136 Section 2, RFC 2845
|
25
|
+
#
|
26
|
+
# ===Sections
|
27
|
+
# Message objects have five sections:
|
28
|
+
#
|
29
|
+
# * The header section, a Dnsruby::Header object.
|
30
|
+
#
|
31
|
+
# msg.header=Header.new(...)
|
32
|
+
# header = msg.header
|
33
|
+
#
|
34
|
+
# * The question section, an array of Dnsruby::Question objects.
|
35
|
+
#
|
36
|
+
# msg.add_question(Question.new(domain, type, klass))
|
37
|
+
# msg.each_question do |question| .... end
|
38
|
+
#
|
39
|
+
# * The answer section, an array of Dnsruby::RR objects.
|
40
|
+
#
|
41
|
+
# msg.add_answer(RR.create({:name => 'a2.example.com',
|
42
|
+
# :type => 'A', :address => '10.0.0.2'}))
|
43
|
+
# msg.each_answer {|answer| ... }
|
44
|
+
#
|
45
|
+
# * The authority section, an array of Dnsruby::RR objects.
|
46
|
+
#
|
47
|
+
# msg.add_authority(rr)
|
48
|
+
# msg.each_authority {|rr| ... }
|
49
|
+
#
|
50
|
+
# * The additional section, an array of Dnsruby::RR objects.
|
51
|
+
#
|
52
|
+
# msg.add_additional(rr)
|
53
|
+
# msg.each_additional {|rr| ... }
|
54
|
+
#
|
55
|
+
# In addition, each_resource iterates the answer, additional
|
56
|
+
# and authority sections :
|
57
|
+
#
|
58
|
+
# msg.each_resource {|rr| ... }
|
59
|
+
#
|
60
|
+
# ===Packet format encoding
|
61
|
+
#
|
62
|
+
# Dnsruby::Message#encode
|
63
|
+
# Dnsruby::Message::decode(data)
|
64
|
+
#
|
65
|
+
# ===Additional information
|
66
|
+
# security_level records the current DNSSEC status of this Message.
|
67
|
+
# answerfrom records the server which this Message was received from.
|
68
|
+
# cached records whether this response came from the cache.
|
69
|
+
#
|
70
|
+
class Message
|
71
|
+
|
72
|
+
# The security level (see RFC 4035 section 4.3)
|
73
|
+
class SecurityLevel < CodeMapper
|
74
|
+
INDETERMINATE = -2
|
75
|
+
BOGUS = -1
|
76
|
+
UNCHECKED = 0
|
77
|
+
INSECURE = 1
|
78
|
+
SECURE = 2
|
79
|
+
update
|
80
|
+
end
|
81
|
+
|
82
|
+
# If dnssec is set on, then each message will have the security level set
|
83
|
+
# To find the precise error (if any), call Dnsruby::Dnssec::validate(msg) -
|
84
|
+
# the resultant exception will define the error.
|
85
|
+
attr_accessor :security_level
|
86
|
+
|
87
|
+
# If there was a problem verifying this message with DNSSEC, then securiy_error
|
88
|
+
# will hold a description of the problem. It defaults to ''
|
89
|
+
attr_accessor :security_error
|
90
|
+
|
91
|
+
# If the Message was returned from the cache, the cached flag will be set
|
92
|
+
# true. It will be false otherwise.
|
93
|
+
attr_accessor :cached
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
# Create a new Message. Takes optional name, type and class
|
98
|
+
#
|
99
|
+
# type defaults to A, and klass defaults to IN
|
100
|
+
#
|
101
|
+
# * Dnsruby::Message.new('example.com') # defaults to A, IN
|
102
|
+
# * Dnsruby::Message.new('example.com', 'AAAA')
|
103
|
+
# * Dnsruby::Message.new('example.com', Dnsruby::Types.PTR, 'HS')
|
104
|
+
#
|
105
|
+
def initialize(*args)
|
106
|
+
@header = Header.new()
|
107
|
+
# @question = Section.new(self)
|
108
|
+
@question = []
|
109
|
+
@answer = Section.new(self)
|
110
|
+
@authority = Section.new(self)
|
111
|
+
@additional = Section.new(self)
|
112
|
+
@tsigstate = :Unsigned
|
113
|
+
@signing = false
|
114
|
+
@tsigkey = nil
|
115
|
+
@answerfrom = nil
|
116
|
+
@answerip = nil
|
117
|
+
@send_raw = false
|
118
|
+
@do_validation = true
|
119
|
+
@do_caching = true
|
120
|
+
@security_level = SecurityLevel.UNCHECKED
|
121
|
+
@security_error = nil
|
122
|
+
@cached = false
|
123
|
+
type = Types::A
|
124
|
+
klass = Classes::IN
|
125
|
+
if (args.length > 0)
|
126
|
+
name = args[0]
|
127
|
+
if (args.length > 1)
|
128
|
+
type = Types.new(args[1])
|
129
|
+
if (args.length > 2)
|
130
|
+
klass = Classes.new(args[2])
|
131
|
+
end
|
132
|
+
end
|
133
|
+
add_question(name, type, klass)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# The question section, an array of Dnsruby::Question objects.
|
138
|
+
attr_reader :question
|
139
|
+
|
140
|
+
# The answer section, an array of Dnsruby::RR objects.
|
141
|
+
attr_reader :answer
|
142
|
+
# The authority section, an array of Dnsruby::RR objects.
|
143
|
+
attr_reader :authority
|
144
|
+
# The additional section, an array of Dnsruby::RR objects.
|
145
|
+
attr_reader :additional
|
146
|
+
# The header section, a Dnsruby::Header object.
|
147
|
+
attr_accessor :header
|
148
|
+
|
149
|
+
# If this Message is a response from a server, then answerfrom contains the address of the server
|
150
|
+
attr_accessor :answerfrom
|
151
|
+
|
152
|
+
# If this Message is a response from a server, then answerfrom contains the IP address of the server
|
153
|
+
attr_accessor :answerip
|
154
|
+
|
155
|
+
# If this Message is a response from a server, then answersize contains the size of the response
|
156
|
+
attr_accessor :answersize
|
157
|
+
|
158
|
+
# If this message has been verified using a TSIG RR then tsigerror contains
|
159
|
+
# the error code returned by the TSIG verification. The error will be an RCode
|
160
|
+
attr_accessor :tsigerror
|
161
|
+
|
162
|
+
# Can be
|
163
|
+
# * :Unsigned - the default state
|
164
|
+
# * :Signed - the outgoing message has been signed
|
165
|
+
# * :Verified - the incoming message has been verified by TSIG
|
166
|
+
# * :Intermediate - the incoming message is an intermediate envelope in a TCP session
|
167
|
+
# in which only every 100th envelope must be signed
|
168
|
+
# * :Failed - the incoming response failed verification
|
169
|
+
attr_accessor :tsigstate
|
170
|
+
|
171
|
+
# --
|
172
|
+
attr_accessor :tsigstart
|
173
|
+
# ++
|
174
|
+
|
175
|
+
# Set send_raw if you wish to send and receive the response to this Message
|
176
|
+
# with no additional processing. In other words, if set, then Dnsruby will
|
177
|
+
# not touch the Header of the outgoing Message. This option does not affect
|
178
|
+
# caching or dnssec validation
|
179
|
+
#
|
180
|
+
# This option should not normally be set.
|
181
|
+
attr_accessor :send_raw
|
182
|
+
|
183
|
+
# do_validation is set by default. If you do not wish dnsruby to validate
|
184
|
+
# this message (on a Resolver with @dnssec==true), then set do_validation
|
185
|
+
# to false. This option does not affect caching, or the header options
|
186
|
+
attr_accessor :do_validation
|
187
|
+
|
188
|
+
# do_caching is set by default. If you do not wish dnsruby to inspect the
|
189
|
+
# cache before sending the query, nor cache the result of the query, then
|
190
|
+
# set do_caching to false.
|
191
|
+
attr_accessor :do_caching
|
192
|
+
|
193
|
+
def get_exception
|
194
|
+
exception = nil
|
195
|
+
if rcode == RCode.NXDOMAIN
|
196
|
+
exception = NXDomain.new
|
197
|
+
elsif rcode == RCode.SERVFAIL
|
198
|
+
exception = ServFail.new
|
199
|
+
elsif rcode == RCode.FORMERR
|
200
|
+
exception = FormErr.new
|
201
|
+
elsif rcode == RCode.NOTIMP
|
202
|
+
exception = NotImp.new
|
203
|
+
elsif rcode == RCode.REFUSED
|
204
|
+
exception = Refused.new
|
205
|
+
elsif rcode == RCode.NOTZONE
|
206
|
+
exception = NotZone.new
|
207
|
+
elsif rcode == RCode.NOTAUTH
|
208
|
+
exception = NotAuth.new
|
209
|
+
elsif rcode == RCode.NXRRSET
|
210
|
+
exception = NXRRSet.new
|
211
|
+
elsif rcode == RCode.YXRRSET
|
212
|
+
exception = YXRRSet.new
|
213
|
+
elsif rcode == RCode.YXDOMAIN
|
214
|
+
exception = YXDomain.new
|
215
|
+
elsif rcode >= RCode.BADSIG && rcode <= RCode.BADALG
|
216
|
+
return VerifyError.new # @TODO@
|
217
|
+
end
|
218
|
+
exception
|
219
|
+
end
|
220
|
+
|
221
|
+
def ==(other)
|
222
|
+
other.kind_of?(Message) &&
|
223
|
+
@header == other.header &&
|
224
|
+
@question[0] == other.question[0] &&
|
225
|
+
@answer == other.answer &&
|
226
|
+
@authority == other.authority &&
|
227
|
+
@additional == other.additional
|
228
|
+
end
|
229
|
+
|
230
|
+
def remove_additional
|
231
|
+
@additional = Section.new(self)
|
232
|
+
@header.arcount = 0
|
233
|
+
end
|
234
|
+
|
235
|
+
# Return the first rrset of the specified attributes in the message
|
236
|
+
def rrset(name, type, klass = Classes::IN)
|
237
|
+
[@answer, @authority, @additional].each do |section|
|
238
|
+
if (rrset = section.rrset(name, type, klass)).length > 0
|
239
|
+
return rrset
|
240
|
+
end
|
241
|
+
end
|
242
|
+
RRSet.new
|
243
|
+
end
|
244
|
+
|
245
|
+
# Return the rrsets of the specified type in the message
|
246
|
+
def rrsets(type, klass=Classes::IN)
|
247
|
+
rrsetss = []
|
248
|
+
[@answer, @authority, @additional].each do |section|
|
249
|
+
if (rrsets = section.rrsets(type, klass)).length > 0
|
250
|
+
rrsets.each { |rrset| rrsetss.push(rrset) }
|
251
|
+
end
|
252
|
+
end
|
253
|
+
rrsetss
|
254
|
+
end
|
255
|
+
|
256
|
+
# Return a hash, with the section as key, and the RRSets in that
|
257
|
+
# section as the data : {section => section_rrs}
|
258
|
+
def section_rrsets(type = nil, include_opt = false)
|
259
|
+
ret = {}
|
260
|
+
%w(answer authority additional).each do |section|
|
261
|
+
ret[section] = self.send(section).rrsets(type, include_opt)
|
262
|
+
end
|
263
|
+
ret
|
264
|
+
end
|
265
|
+
|
266
|
+
# Add a new Question to the Message. Takes either a Question,
|
267
|
+
# or a name, and an optional type and class.
|
268
|
+
#
|
269
|
+
# * msg.add_question(Question.new('example.com', 'MX'))
|
270
|
+
# * msg.add_question('example.com') # defaults to Types.A, Classes.IN
|
271
|
+
# * msg.add_question('example.com', Types.LOC)
|
272
|
+
def add_question(question, type=Types.A, klass=Classes.IN)
|
273
|
+
unless question.kind_of?(Question)
|
274
|
+
question = Question.new(question, type, klass)
|
275
|
+
end
|
276
|
+
@question << question
|
277
|
+
update_counts
|
278
|
+
end
|
279
|
+
|
280
|
+
def each_question
|
281
|
+
@question.each {|rec|
|
282
|
+
yield rec
|
283
|
+
}
|
284
|
+
end
|
285
|
+
|
286
|
+
def update_counts # :nodoc:all
|
287
|
+
@header.ancount = @answer.length
|
288
|
+
@header.arcount = @additional.length
|
289
|
+
@header.qdcount = @question.length
|
290
|
+
@header.nscount = @authority.length
|
291
|
+
end
|
292
|
+
|
293
|
+
|
294
|
+
def add_answer(rr) #:nodoc: all
|
295
|
+
unless @answer.include?(rr)
|
296
|
+
@answer << rr
|
297
|
+
update_counts
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
def each_answer
|
302
|
+
@answer.each {|rec|
|
303
|
+
yield rec
|
304
|
+
}
|
305
|
+
end
|
306
|
+
|
307
|
+
def add_authority(rr) #:nodoc: all
|
308
|
+
unless @authority.include?(rr)
|
309
|
+
@authority << rr
|
310
|
+
update_counts
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
def each_authority
|
315
|
+
@authority.each {|rec|
|
316
|
+
yield rec
|
317
|
+
}
|
318
|
+
end
|
319
|
+
|
320
|
+
def add_additional(rr) #:nodoc: all
|
321
|
+
unless @additional.include?(rr)
|
322
|
+
@additional << rr
|
323
|
+
update_counts
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
def each_additional
|
328
|
+
@additional.each { |rec| yield rec }
|
329
|
+
end
|
330
|
+
|
331
|
+
# Yields each section (question, answer, authority, additional)
|
332
|
+
def each_section
|
333
|
+
[@answer, @authority, @additional].each { |section| yield section}
|
334
|
+
end
|
335
|
+
|
336
|
+
# Calls each_answer, each_authority, each_additional
|
337
|
+
def each_resource
|
338
|
+
each_answer {|rec| yield rec}
|
339
|
+
each_authority {|rec| yield rec}
|
340
|
+
each_additional {|rec| yield rec}
|
341
|
+
end
|
342
|
+
|
343
|
+
# Returns the TSIG record from the ADDITIONAL section, if one is present.
|
344
|
+
def tsig
|
345
|
+
if @additional.last
|
346
|
+
if @additional.last.rr_type == Types.TSIG
|
347
|
+
return @additional.last
|
348
|
+
end
|
349
|
+
end
|
350
|
+
nil
|
351
|
+
end
|
352
|
+
|
353
|
+
# Sets the TSIG to sign this message with. Can either be a Dnsruby::RR::TSIG
|
354
|
+
# object, or it can be a (name, key) tuple, or it can be a hash which takes
|
355
|
+
# Dnsruby::RR::TSIG attributes (e.g. name, key, fudge, etc.)
|
356
|
+
def set_tsig(*args)
|
357
|
+
if args.length == 1
|
358
|
+
if args[0].instance_of?(RR::TSIG)
|
359
|
+
@tsigkey = args[0]
|
360
|
+
elsif args[0].instance_of?(Hash)
|
361
|
+
@tsigkey = RR.create({:type=>'TSIG', :klass=>'ANY'}.merge(args[0]))
|
362
|
+
else
|
363
|
+
raise ArgumentError.new('Wrong type of argument to Dnsruby::Message#set_tsig - should be TSIG or Hash')
|
364
|
+
end
|
365
|
+
elsif args.length == 2
|
366
|
+
@tsigkey = RR.create({:type=>'TSIG', :klass=>'ANY', :name=>args[0], :key=>args[1]})
|
367
|
+
else
|
368
|
+
raise ArgumentError.new('Wrong number of arguments to Dnsruby::Message#set_tsig')
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
# Was this message signed by a TSIG?
|
373
|
+
def signed?
|
374
|
+
@tsigstate == :Signed ||
|
375
|
+
@tsigstate == :Verified ||
|
376
|
+
@tsigstate == :Failed
|
377
|
+
end
|
378
|
+
|
379
|
+
# If this message was signed by a TSIG, was the TSIG verified?
|
380
|
+
def verified?
|
381
|
+
@tsigstate == :Verified
|
382
|
+
end
|
383
|
+
|
384
|
+
def get_opt
|
385
|
+
@additional.detect { |r| r.type == Types::OPT }
|
386
|
+
end
|
387
|
+
|
388
|
+
def rcode
|
389
|
+
rcode = @header.get_header_rcode
|
390
|
+
opt = get_opt
|
391
|
+
if opt
|
392
|
+
rcode = rcode.code + (opt.xrcode.code << 4)
|
393
|
+
rcode = RCode.new(rcode)
|
394
|
+
end
|
395
|
+
rcode
|
396
|
+
end
|
397
|
+
|
398
|
+
def to_s
|
399
|
+
s = '' # the output string to return
|
400
|
+
|
401
|
+
if @answerfrom && (! @answerfrom.empty?)
|
402
|
+
s << ";; Answer received from #{@answerfrom} (#{@answersize} bytes)\n;;\n"
|
403
|
+
end
|
404
|
+
|
405
|
+
s << ";; Security Level : #{@security_level.string}\n"
|
406
|
+
|
407
|
+
# OPT pseudosection? EDNS flags, udpsize
|
408
|
+
opt = get_opt
|
409
|
+
|
410
|
+
if opt
|
411
|
+
s << @header.to_s_with_rcode(rcode) << "\n#{opt}\n"
|
412
|
+
else
|
413
|
+
s << "#{@header}\n"
|
414
|
+
end
|
415
|
+
|
416
|
+
section = (@header.opcode == OpCode.UPDATE) ? 'ZONE' : 'QUESTION'
|
417
|
+
s << ";; #{section} SECTION (#{@header.qdcount} record#{@header.qdcount == 1 ? '' : 's'})\n"
|
418
|
+
each_question { |qr| s << ";; #{qr}\n" }
|
419
|
+
|
420
|
+
if @answer.size > 0
|
421
|
+
s << "\n"
|
422
|
+
section = (@header.opcode == OpCode.UPDATE) ? 'PREREQUISITE' : 'ANSWER'
|
423
|
+
s << ";; #{section} SECTION (#{@header.ancount} record#{@header.ancount == 1 ? '' : 's'})\n"
|
424
|
+
each_answer { |rr| s << "#{rr}\n" }
|
425
|
+
end
|
426
|
+
|
427
|
+
if @authority.size > 0
|
428
|
+
s << "\n"
|
429
|
+
section = (@header.opcode == OpCode.UPDATE) ? 'UPDATE' : 'AUTHORITY'
|
430
|
+
s << ";; #{section} SECTION (#{@header.nscount} record#{@header.nscount == 1 ? '' : 's'})\n"
|
431
|
+
each_authority { |rr| s << rr.to_s + "\n" }
|
432
|
+
end
|
433
|
+
|
434
|
+
if (@additional.size > 0 && !opt) || (@additional.size > 1)
|
435
|
+
s << "\n;; ADDITIONAL SECTION (#{@header.arcount} record#{@header.arcount == 1 ? '' : 's'})\n"
|
436
|
+
each_additional { |rr|
|
437
|
+
if rr.type != Types::OPT
|
438
|
+
s << rr.to_s+ "\n"
|
439
|
+
end
|
440
|
+
}
|
441
|
+
end
|
442
|
+
|
443
|
+
s
|
444
|
+
end
|
445
|
+
|
446
|
+
|
447
|
+
def old_to_s
|
448
|
+
retval = ''
|
449
|
+
|
450
|
+
if (@answerfrom != nil && @answerfrom != '')
|
451
|
+
retval = retval + ";; Answer received from #{@answerfrom} (#{@answersize} bytes)\n;;\n"
|
452
|
+
end
|
453
|
+
retval = retval + ";; Security Level : #{@security_level.string}\n"
|
454
|
+
|
455
|
+
retval = retval + ";; HEADER SECTION\n"
|
456
|
+
|
457
|
+
# OPT pseudosection? EDNS flags, udpsize
|
458
|
+
opt = get_opt
|
459
|
+
if (!opt)
|
460
|
+
retval = retval + @header.old_to_s
|
461
|
+
else
|
462
|
+
retval = retval + @header.old_to_s_with_rcode(rcode())
|
463
|
+
end
|
464
|
+
retval = retval + "\n"
|
465
|
+
|
466
|
+
if (opt)
|
467
|
+
retval = retval + opt.to_s
|
468
|
+
retval = retval + "\n"
|
469
|
+
end
|
470
|
+
|
471
|
+
section = (@header.opcode == OpCode.UPDATE) ? "ZONE" : "QUESTION"
|
472
|
+
retval = retval + ";; #{section} SECTION (#{@header.qdcount} record#{@header.qdcount == 1 ? '' : 's'})\n"
|
473
|
+
each_question { |qr|
|
474
|
+
retval = retval + ";; #{qr.to_s}\n"
|
475
|
+
}
|
476
|
+
|
477
|
+
if (@answer.size > 0)
|
478
|
+
retval = retval + "\n"
|
479
|
+
section = (@header.opcode == OpCode.UPDATE) ? "PREREQUISITE" : "ANSWER"
|
480
|
+
retval = retval + ";; #{section} SECTION (#{@header.ancount} record#{@header.ancount == 1 ? '' : 's'})\n"
|
481
|
+
each_answer { |rr|
|
482
|
+
retval = retval + rr.to_s + "\n"
|
483
|
+
}
|
484
|
+
end
|
485
|
+
|
486
|
+
if (@authority.size > 0)
|
487
|
+
retval = retval + "\n"
|
488
|
+
section = (@header.opcode == OpCode.UPDATE) ? "UPDATE" : "AUTHORITY"
|
489
|
+
retval = retval + ";; #{section} SECTION (#{@header.nscount} record#{@header.nscount == 1 ? '' : 's'})\n"
|
490
|
+
each_authority { |rr|
|
491
|
+
retval = retval + rr.to_s + "\n"
|
492
|
+
}
|
493
|
+
end
|
494
|
+
|
495
|
+
if ((@additional.size > 0 && !opt) || (@additional.size > 1))
|
496
|
+
retval = retval + "\n"
|
497
|
+
retval = retval + ";; ADDITIONAL SECTION (#{@header.arcount} record#{@header.arcount == 1 ? '' : 's'})\n"
|
498
|
+
each_additional { |rr|
|
499
|
+
if (rr.type != Types::OPT)
|
500
|
+
retval = retval + rr.to_s+ "\n"
|
501
|
+
end
|
502
|
+
}
|
503
|
+
end
|
504
|
+
|
505
|
+
retval
|
506
|
+
end
|
507
|
+
|
508
|
+
# Signs the message. If used with no arguments, then the message must have already
|
509
|
+
# been set (set_tsig). Otherwise, the arguments can either be a Dnsruby::RR::TSIG
|
510
|
+
# object, or a (name, key) tuple, or a hash which takes
|
511
|
+
# Dnsruby::RR::TSIG attributes (e.g. name, key, fudge, etc.)
|
512
|
+
#
|
513
|
+
# NOTE that this method should only be called by the resolver, rather than the
|
514
|
+
# client code. To use signing from the client, call Dnsruby::Resolver#tsig=
|
515
|
+
def sign!(*args) #:nodoc: all
|
516
|
+
if args.length > 0
|
517
|
+
set_tsig(*args)
|
518
|
+
sign!
|
519
|
+
else
|
520
|
+
if @tsigkey && (@tsigstate == :Unsigned)
|
521
|
+
@tsigkey.apply(self)
|
522
|
+
end
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
# Return the encoded form of the message
|
527
|
+
# If there is a TSIG record present and the record has not been signed
|
528
|
+
# then sign it
|
529
|
+
def encode
|
530
|
+
if @tsigkey && (@tsigstate == :Unsigned) && !@signing
|
531
|
+
@signing = true
|
532
|
+
sign!
|
533
|
+
@signing = false
|
534
|
+
end
|
535
|
+
|
536
|
+
return MessageEncoder.new { |msg|
|
537
|
+
header = @header
|
538
|
+
header.encode(msg)
|
539
|
+
@question.each { |q|
|
540
|
+
msg.put_name(q.qname)
|
541
|
+
msg.put_pack('nn', q.qtype.code, q.qclass.code)
|
542
|
+
}
|
543
|
+
[@answer, @authority, @additional].each { |rr|
|
544
|
+
rr.each { |r|
|
545
|
+
msg.put_rr(r)
|
546
|
+
}
|
547
|
+
}
|
548
|
+
}.to_s
|
549
|
+
end
|
550
|
+
|
551
|
+
# Decode the encoded message
|
552
|
+
def Message.decode(m)
|
553
|
+
o = Message.new()
|
554
|
+
begin
|
555
|
+
MessageDecoder.new(m) {|msg|
|
556
|
+
o.header = Header.new(msg)
|
557
|
+
o.header.qdcount.times {
|
558
|
+
question = msg.get_question
|
559
|
+
o.question << question
|
560
|
+
}
|
561
|
+
o.header.ancount.times {
|
562
|
+
rr = msg.get_rr
|
563
|
+
o.answer << rr
|
564
|
+
}
|
565
|
+
o.header.nscount.times {
|
566
|
+
rr = msg.get_rr
|
567
|
+
o.authority << rr
|
568
|
+
}
|
569
|
+
o.header.arcount.times { |count|
|
570
|
+
start = msg.index
|
571
|
+
rr = msg.get_rr
|
572
|
+
if rr.type == Types::TSIG
|
573
|
+
if count != o.header.arcount-1
|
574
|
+
Dnsruby.log.Error('Incoming message has TSIG record before last record')
|
575
|
+
raise DecodeError.new('TSIG record present before last record')
|
576
|
+
end
|
577
|
+
o.tsigstart = start # needed for TSIG verification
|
578
|
+
end
|
579
|
+
o.additional << rr
|
580
|
+
}
|
581
|
+
}
|
582
|
+
rescue DecodeError => e
|
583
|
+
# So we got a decode error
|
584
|
+
# However, we might have been able to fill in many parts of the message
|
585
|
+
# So let's raise the DecodeError, but add the partially completed message
|
586
|
+
e.partial_message = o
|
587
|
+
raise e
|
588
|
+
end
|
589
|
+
o
|
590
|
+
end
|
591
|
+
|
592
|
+
def clone
|
593
|
+
Message.decode(self.encode)
|
594
|
+
end
|
595
|
+
|
596
|
+
# In dynamic update packets, the question section is known as zone and
|
597
|
+
# specifies the zone to be updated.
|
598
|
+
alias :zone :question
|
599
|
+
alias :add_zone :add_question
|
600
|
+
alias :each_zone :each_question
|
601
|
+
|
602
|
+
# In dynamic update packets, the answer section is known as pre or
|
603
|
+
# prerequisite and specifies the RRs or RRsets which must or
|
604
|
+
# must not preexist.
|
605
|
+
alias :pre :answer
|
606
|
+
alias :add_pre :add_answer
|
607
|
+
alias :each_pre :each_answer
|
608
|
+
|
609
|
+
# In dynamic update packets, the answer section is known as pre or
|
610
|
+
# prerequisite and specifies the RRs or RRsets which must or
|
611
|
+
# must not preexist.
|
612
|
+
alias :prerequisite :pre
|
613
|
+
alias :add_prerequisite :add_pre
|
614
|
+
alias :each_prerequisite :each_pre
|
615
|
+
|
616
|
+
# In dynamic update packets, the authority section is known as update and
|
617
|
+
# specifies the RRs or RRsets to be added or delted.
|
618
|
+
alias :update :authority
|
619
|
+
alias :add_update :add_authority
|
620
|
+
alias :each_update :each_authority
|
621
|
+
|
622
|
+
end
|
623
|
+
end
|
624
|
+
|
625
|
+
require 'dnsruby/message/section'
|
626
|
+
require 'dnsruby/message/header'
|
627
|
+
require 'dnsruby/message/decoder'
|
628
|
+
require 'dnsruby/message/encoder'
|
629
|
+
require 'dnsruby/message/question'
|