dnsruby 1.55 → 1.56.0

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.
Files changed (158) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +96 -0
  3. data/Rakefile +30 -29
  4. data/demo/axfr.rb +93 -93
  5. data/demo/check_soa.rb +99 -99
  6. data/demo/check_zone.rb +59 -59
  7. data/demo/digdlv.rb +43 -43
  8. data/demo/digroot.rb +34 -34
  9. data/demo/example_recurse.rb +14 -14
  10. data/demo/mresolv.rb +30 -30
  11. data/demo/mx.rb +31 -31
  12. data/demo/rubydig.rb +37 -37
  13. data/demo/to_resolve.txt +3088 -3088
  14. data/demo/trace_dns.rb +46 -46
  15. data/lib/dnsruby.rb +161 -526
  16. data/lib/dnsruby/DNS.rb +305 -0
  17. data/lib/{Dnsruby/Cache.rb → dnsruby/cache.rb} +152 -152
  18. data/lib/{Dnsruby → dnsruby}/code_mapper.rb +48 -52
  19. data/lib/dnsruby/code_mappers.rb +295 -0
  20. data/lib/{Dnsruby/Config.rb → dnsruby/config.rb} +454 -454
  21. data/lib/{Dnsruby → dnsruby}/dnssec.rb +91 -91
  22. data/lib/{Dnsruby/Hosts.rb → dnsruby/hosts.rb} +125 -125
  23. data/lib/{Dnsruby → dnsruby}/ipv4.rb +26 -26
  24. data/lib/{Dnsruby → dnsruby}/ipv6.rb +42 -42
  25. data/lib/{Dnsruby → dnsruby}/key_cache.rb +29 -29
  26. data/lib/dnsruby/message/decoder.rb +164 -0
  27. data/lib/dnsruby/message/encoder.rb +75 -0
  28. data/lib/dnsruby/message/header.rb +249 -0
  29. data/lib/dnsruby/message/message.rb +629 -0
  30. data/lib/dnsruby/message/question.rb +86 -0
  31. data/lib/dnsruby/message/section.rb +96 -0
  32. data/lib/{Dnsruby → dnsruby}/name.rb +141 -141
  33. data/lib/dnsruby/packet_sender.rb +661 -0
  34. data/lib/{Dnsruby/Recursor.rb → dnsruby/recursor.rb} +235 -233
  35. data/lib/dnsruby/resolv.rb +113 -0
  36. data/lib/dnsruby/resolver.rb +1192 -0
  37. data/lib/dnsruby/resource/A.rb +56 -0
  38. data/lib/dnsruby/resource/AAAA.rb +54 -0
  39. data/lib/{Dnsruby → dnsruby}/resource/AFSDB.rb +68 -68
  40. data/lib/{Dnsruby → dnsruby}/resource/CERT.rb +105 -105
  41. data/lib/{Dnsruby → dnsruby}/resource/DHCID.rb +54 -54
  42. data/lib/dnsruby/resource/DLV.rb +27 -0
  43. data/lib/{Dnsruby → dnsruby}/resource/DNSKEY.rb +372 -372
  44. data/lib/{Dnsruby → dnsruby}/resource/DS.rb +255 -255
  45. data/lib/{Dnsruby → dnsruby}/resource/HINFO.rb +71 -71
  46. data/lib/{Dnsruby → dnsruby}/resource/HIP.rb +29 -29
  47. data/lib/{Dnsruby → dnsruby}/resource/IN.rb +30 -30
  48. data/lib/{Dnsruby → dnsruby}/resource/IPSECKEY.rb +31 -31
  49. data/lib/{Dnsruby → dnsruby}/resource/ISDN.rb +62 -62
  50. data/lib/{Dnsruby → dnsruby}/resource/KX.rb +65 -65
  51. data/lib/{Dnsruby → dnsruby}/resource/LOC.rb +263 -263
  52. data/lib/{Dnsruby → dnsruby}/resource/MINFO.rb +69 -69
  53. data/lib/{Dnsruby → dnsruby}/resource/MX.rb +65 -65
  54. data/lib/{Dnsruby → dnsruby}/resource/NAPTR.rb +98 -98
  55. data/lib/{Dnsruby → dnsruby}/resource/NSAP.rb +171 -171
  56. data/lib/dnsruby/resource/NSEC.rb +275 -0
  57. data/lib/dnsruby/resource/NSEC3.rb +332 -0
  58. data/lib/dnsruby/resource/NSEC3PARAM.rb +135 -0
  59. data/lib/dnsruby/resource/OPT.rb +272 -0
  60. data/lib/{Dnsruby → dnsruby}/resource/PX.rb +70 -70
  61. data/lib/{Dnsruby → dnsruby}/resource/RP.rb +75 -75
  62. data/lib/dnsruby/resource/RR.rb +421 -0
  63. data/lib/dnsruby/resource/RRSIG.rb +275 -0
  64. data/lib/dnsruby/resource/RRSet.rb +190 -0
  65. data/lib/{Dnsruby → dnsruby}/resource/RT.rb +67 -67
  66. data/lib/{Dnsruby → dnsruby}/resource/SOA.rb +94 -94
  67. data/lib/dnsruby/resource/SPF.rb +29 -0
  68. data/lib/dnsruby/resource/SRV.rb +112 -0
  69. data/lib/{Dnsruby → dnsruby}/resource/SSHFP.rb +14 -14
  70. data/lib/dnsruby/resource/TKEY.rb +163 -0
  71. data/lib/dnsruby/resource/TSIG.rb +593 -0
  72. data/lib/{Dnsruby → dnsruby}/resource/TXT.rb +191 -191
  73. data/lib/dnsruby/resource/X25.rb +55 -0
  74. data/lib/{Dnsruby → dnsruby}/resource/domain_name.rb +25 -25
  75. data/lib/{Dnsruby → dnsruby}/resource/generic.rb +80 -80
  76. data/lib/dnsruby/resource/resource.rb +25 -0
  77. data/lib/{Dnsruby → dnsruby}/select_thread.rb +148 -148
  78. data/lib/{Dnsruby/SingleResolver.rb → dnsruby/single_resolver.rb} +60 -60
  79. data/lib/{Dnsruby → dnsruby}/single_verifier.rb +344 -344
  80. data/lib/dnsruby/the_log.rb +44 -0
  81. data/lib/dnsruby/update.rb +278 -0
  82. data/lib/dnsruby/validator_thread.rb +124 -0
  83. data/lib/dnsruby/version.rb +3 -0
  84. data/lib/{Dnsruby → dnsruby}/zone_reader.rb +93 -93
  85. data/lib/{Dnsruby → dnsruby}/zone_transfer.rb +377 -377
  86. data/test/spec_helper.rb +16 -0
  87. data/test/tc_axfr.rb +31 -34
  88. data/test/tc_cache.rb +32 -32
  89. data/test/tc_dlv.rb +28 -28
  90. data/test/tc_dns.rb +73 -76
  91. data/test/tc_dnskey.rb +31 -32
  92. data/test/tc_dnsruby.rb +50 -44
  93. data/test/tc_ds.rb +36 -36
  94. data/test/tc_escapedchars.rb +252 -255
  95. data/test/tc_hash.rb +17 -21
  96. data/test/tc_header.rb +48 -57
  97. data/test/tc_hip.rb +19 -22
  98. data/test/tc_ipseckey.rb +18 -21
  99. data/test/tc_keith.rb +300 -0
  100. data/test/tc_message.rb +87 -0
  101. data/test/tc_misc.rb +83 -87
  102. data/test/tc_name.rb +81 -84
  103. data/test/tc_naptr.rb +18 -21
  104. data/test/tc_nsec.rb +55 -55
  105. data/test/tc_nsec3.rb +23 -24
  106. data/test/tc_nsec3param.rb +20 -21
  107. data/test/tc_packet.rb +90 -93
  108. data/test/tc_packet_unique_push.rb +48 -51
  109. data/test/tc_question.rb +30 -33
  110. data/test/tc_queue.rb +16 -17
  111. data/test/tc_recur.rb +16 -17
  112. data/test/tc_res_config.rb +38 -41
  113. data/test/tc_res_env.rb +29 -32
  114. data/test/tc_res_file.rb +26 -29
  115. data/test/tc_res_opt.rb +62 -65
  116. data/test/tc_resolver.rb +287 -242
  117. data/test/tc_rr-opt.rb +70 -63
  118. data/test/tc_rr-txt.rb +68 -71
  119. data/test/tc_rr-unknown.rb +45 -48
  120. data/test/tc_rr.rb +76 -70
  121. data/test/tc_rrset.rb +21 -22
  122. data/test/tc_rrsig.rb +19 -20
  123. data/test/tc_single_resolver.rb +294 -297
  124. data/test/tc_soak.rb +199 -202
  125. data/test/tc_soak_base.rb +29 -34
  126. data/test/tc_sshfp.rb +20 -23
  127. data/test/tc_tcp.rb +32 -35
  128. data/test/tc_tkey.rb +41 -44
  129. data/test/tc_tsig.rb +81 -84
  130. data/test/tc_update.rb +108 -111
  131. data/test/tc_validator.rb +29 -29
  132. data/test/tc_verifier.rb +81 -82
  133. data/test/ts_dnsruby.rb +16 -15
  134. data/test/ts_offline.rb +62 -63
  135. data/test/ts_online.rb +115 -115
  136. metadata +155 -90
  137. data/README +0 -59
  138. data/lib/Dnsruby/DNS.rb +0 -305
  139. data/lib/Dnsruby/PacketSender.rb +0 -656
  140. data/lib/Dnsruby/Resolver.rb +0 -1189
  141. data/lib/Dnsruby/TheLog.rb +0 -44
  142. data/lib/Dnsruby/message.rb +0 -1230
  143. data/lib/Dnsruby/resource/A.rb +0 -56
  144. data/lib/Dnsruby/resource/AAAA.rb +0 -54
  145. data/lib/Dnsruby/resource/DLV.rb +0 -27
  146. data/lib/Dnsruby/resource/NSEC.rb +0 -298
  147. data/lib/Dnsruby/resource/NSEC3.rb +0 -340
  148. data/lib/Dnsruby/resource/NSEC3PARAM.rb +0 -135
  149. data/lib/Dnsruby/resource/OPT.rb +0 -213
  150. data/lib/Dnsruby/resource/RRSIG.rb +0 -275
  151. data/lib/Dnsruby/resource/SPF.rb +0 -29
  152. data/lib/Dnsruby/resource/SRV.rb +0 -112
  153. data/lib/Dnsruby/resource/TKEY.rb +0 -163
  154. data/lib/Dnsruby/resource/TSIG.rb +0 -593
  155. data/lib/Dnsruby/resource/X25.rb +0 -55
  156. data/lib/Dnsruby/resource/resource.rb +0 -678
  157. data/lib/Dnsruby/update.rb +0 -278
  158. 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'