dnsruby 1.55 → 1.56.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -1,18 +1,18 @@
1
- #--
2
- #Copyright 2009 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
- #
1
+ # --
2
+ # Copyright 2009 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
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
- #++
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
16
 
17
17
  # This class provides the facility to load a zone file.
18
18
  # It can either process one line at a time, or return an entire zone as a list of
@@ -22,8 +22,8 @@ module Dnsruby
22
22
  class ParseException < Exception
23
23
 
24
24
  end
25
- # Create a new ZoneReader. The zone origin is required. If the desired SOA minimum
26
- # and TTL are passed in, then they are used as default values.
25
+ # Create a new ZoneReader. The zone origin is required. If the desired SOA minimum
26
+ # and TTL are passed in, then they are used as default values.
27
27
  def initialize(origin, soa_minimum = nil, soa_ttl = nil)
28
28
  @origin = origin.to_s
29
29
 
@@ -42,8 +42,8 @@ module Dnsruby
42
42
  @in_quoted_section = false
43
43
  end
44
44
 
45
- # Takes a filename string and attempts to load a zone. Returns a list
46
- # of RRs if successful, nil otherwise.
45
+ # Takes a filename string and attempts to load a zone. Returns a list
46
+ # of RRs if successful, nil otherwise.
47
47
  def process_file(file)
48
48
  line_num = 0
49
49
  zone = nil
@@ -65,8 +65,8 @@ module Dnsruby
65
65
  return zone
66
66
  end
67
67
 
68
- # Process the next line of the file
69
- # Returns a string representing the normalised line.
68
+ # Process the next line of the file
69
+ # Returns a string representing the normalised line.
70
70
  def process_line(line, do_prefix_hack = false)
71
71
  return nil if (line[0,1] == ";")
72
72
  return nil if (line.strip.length == 0)
@@ -77,30 +77,30 @@ module Dnsruby
77
77
 
78
78
  if (line.index("$ORIGIN") == 0)
79
79
  @origin = line.split()[1].strip # $ORIGIN <domain-name> [<comment>]
80
- # print "Setting $ORIGIN to #{@origin}\n"
80
+ # print "Setting $ORIGIN to #{@origin}\n"
81
81
  return nil
82
82
  end
83
83
  if (line.index("$TTL") == 0)
84
84
  @last_explicit_ttl = get_ttl(line.split()[1].strip) # $TTL <ttl>
85
- # print "Setting $TTL to #{ttl}\n"
85
+ # print "Setting $TTL to #{ttl}\n"
86
86
  return nil
87
87
  end
88
88
  if (@continued_line)
89
- # Add the next line until we see a ")"
90
- # REMEMBER TO STRIP OFF COMMENTS!!!
89
+ # Add the next line until we see a ")"
90
+ # REMEMBER TO STRIP OFF COMMENTS!!!
91
91
  @continued_line = strip_comments(@continued_line)
92
92
  line = @continued_line.rstrip.chomp + " " + line
93
93
  if (line.index(")"))
94
- # OK
94
+ # OK
95
95
  @continued_line = false
96
96
  end
97
97
  end
98
98
  open_bracket = line.index("(")
99
99
  if (open_bracket)
100
- # Keep going until we see ")"
100
+ # Keep going until we see ")"
101
101
  index = line.index(")")
102
102
  if (index && (index > open_bracket))
103
- # OK
103
+ # OK
104
104
  @continued_line = false
105
105
  else
106
106
  @continued_line = line
@@ -110,45 +110,45 @@ module Dnsruby
110
110
 
111
111
  line = strip_comments(line) + "\n"
112
112
 
113
- # If SOA, then replace "3h" etc. with expanded seconds
114
- # begin
113
+ # If SOA, then replace "3h" etc. with expanded seconds
114
+ # begin
115
115
  return normalise_line(line, do_prefix_hack)
116
- # rescue Exception => e
117
- # print "ERROR parsing line #{@line_num} : #{line}\n"
118
- # return "\n", Types::ANY
119
- # end
116
+ # rescue Exception => e
117
+ # print "ERROR parsing line #{@line_num} : #{line}\n"
118
+ # return "\n", Types::ANY
119
+ # end
120
120
  end
121
121
 
122
122
  def strip_comments(line)
123
123
  last_index = 0
124
- # Are we currently in a quoted section?
125
- # Does a quoted section begin or end in this line?
126
- # Are there any semi-colons?
127
- # Ary any of the semi-colons inside a quoted section?
128
- # Handle escape characters
124
+ # Are we currently in a quoted section?
125
+ # Does a quoted section begin or end in this line?
126
+ # Are there any semi-colons?
127
+ # Ary any of the semi-colons inside a quoted section?
128
+ # Handle escape characters
129
129
  if (line.index"\\")
130
130
  return strip_comments_meticulously(line)
131
131
  end
132
132
  while (next_index = line.index(";", last_index + 1))
133
- # Have there been any quotes since we last looked?
133
+ # Have there been any quotes since we last looked?
134
134
  process_quotes(line[last_index, next_index - last_index])
135
135
 
136
- # Now use @in_quoted_section to work out if the ';' terminates the line
136
+ # Now use @in_quoted_section to work out if the ';' terminates the line
137
137
  if (!@in_quoted_section)
138
138
  return line[0,next_index]
139
139
  end
140
140
 
141
141
  last_index = next_index
142
142
  end
143
- # Check out the quote situation to the end of the line
143
+ # Check out the quote situation to the end of the line
144
144
  process_quotes(line[last_index, line.length-1])
145
145
 
146
146
  return line
147
147
  end
148
148
 
149
149
  def strip_comments_meticulously(line)
150
- # We have escape characters in the text. Go through it character by
151
- # character and work out what's escaped and quoted and what's not
150
+ # We have escape characters in the text. Go through it character by
151
+ # character and work out what's escaped and quoted and what's not
152
152
  escaped = false
153
153
  quoted = false
154
154
  pos = 0
@@ -188,8 +188,8 @@ module Dnsruby
188
188
  end
189
189
 
190
190
  def process_quotes(section)
191
- # Look through the section of text and set the @in_quoted_section
192
- # as it should be at the end of the given section
191
+ # Look through the section of text and set the @in_quoted_section
192
+ # as it should be at the end of the given section
193
193
  last_index = 0
194
194
  while (next_index = section.index("\"", last_index + 1))
195
195
  @in_quoted_section = !@in_quoted_section
@@ -197,14 +197,14 @@ module Dnsruby
197
197
  end
198
198
  end
199
199
 
200
- # Take a line from the input zone file, and return the normalised form
201
- # do_prefix_hack should always be false
200
+ # Take a line from the input zone file, and return the normalised form
201
+ # do_prefix_hack should always be false
202
202
  def normalise_line(line, do_prefix_hack = false)
203
- # Note that a freestanding "@" is used to denote the current origin - we can simply replace that straight away
204
- # Remove the ( and )
205
- # Note that no domain name may be specified in the RR - in that case, last_name should be used. How do we tell? Tab or space at start of line.
203
+ # Note that a freestanding "@" is used to denote the current origin - we can simply replace that straight away
204
+ # Remove the ( and )
205
+ # Note that no domain name may be specified in the RR - in that case, last_name should be used. How do we tell? Tab or space at start of line.
206
206
 
207
- # If we have text in the record, then ignore that in the parsing, and stick it on again at the end
207
+ # If we have text in the record, then ignore that in the parsing, and stick it on again at the end
208
208
  stored_line = "";
209
209
  if (line.index('"') != nil)
210
210
  stored_line = line[line.index('"'), line.length];
@@ -220,11 +220,11 @@ module Dnsruby
220
220
  line.strip!
221
221
 
222
222
 
223
- # o We need to identify the domain name in the record, and then
223
+ # o We need to identify the domain name in the record, and then
224
224
  split = line.split(' ') # split on whitespace
225
225
  name = split[0].strip
226
226
  if (name.index"\\")
227
-
227
+
228
228
  ls =[]
229
229
  Name.create(name).labels.each {|el| ls.push(Name.decode(el.to_s))}
230
230
  new_name = ls.join('.')
@@ -240,7 +240,7 @@ module Dnsruby
240
240
  line += "\n"
241
241
  name = new_name
242
242
  split = line.split
243
- # o add $ORIGIN to it if it is not absolute
243
+ # o add $ORIGIN to it if it is not absolute
244
244
  elsif !(/\.\z/ =~ name)
245
245
  new_name = name + "." + @origin
246
246
  line.sub!(name, new_name)
@@ -248,11 +248,11 @@ module Dnsruby
248
248
  split = line.split
249
249
  end
250
250
 
251
- # If the second field is not a number, then we should add the TTL to the line
252
- # Remember we can get "m" "w" "y" here! So need to check for appropriate regexp...
251
+ # If the second field is not a number, then we should add the TTL to the line
252
+ # Remember we can get "m" "w" "y" here! So need to check for appropriate regexp...
253
253
  found_ttl_regexp = (split[1]=~/^[0-9]+[smhdwSMHDW]/)
254
254
  if (found_ttl_regexp == 0)
255
- # Replace the formatted ttl with an actual number
255
+ # Replace the formatted ttl with an actual number
256
256
  ttl = get_ttl(split[1])
257
257
  line = name + " #{ttl} "
258
258
  @last_explicit_ttl = ttl
@@ -260,16 +260,16 @@ module Dnsruby
260
260
  line += "\n"
261
261
  split = line.split
262
262
  elsif (((split[1]).to_i == 0) && (split[1] != "0"))
263
- # Add the TTL
263
+ # Add the TTL
264
264
  if (!@last_explicit_ttl)
265
- # If this is the SOA record, and no @last_explicit_ttl is defined,
266
- # then we need to try the SOA TTL element from the config. Otherwise,
267
- # find the SOA Minimum field, and use that.
268
- # We should also generate a warning to that effect
269
- # How do we know if it is an SOA record at this stage? It must be, or
270
- # else @last_explicit_ttl should be defined
271
- # We could put a marker in the RR for now - and replace it once we know
272
- # the actual type. If the type is not SOA then, then we can raise an error
265
+ # If this is the SOA record, and no @last_explicit_ttl is defined,
266
+ # then we need to try the SOA TTL element from the config. Otherwise,
267
+ # find the SOA Minimum field, and use that.
268
+ # We should also generate a warning to that effect
269
+ # How do we know if it is an SOA record at this stage? It must be, or
270
+ # else @last_explicit_ttl should be defined
271
+ # We could put a marker in the RR for now - and replace it once we know
272
+ # the actual type. If the type is not SOA then, then we can raise an error
273
273
  line = name + " %MISSING_TTL% "
274
274
  else
275
275
  line = name + " #{@last_explicit_ttl} "
@@ -281,13 +281,13 @@ module Dnsruby
281
281
  @last_explicit_ttl = split[1].to_i
282
282
  end
283
283
 
284
- # Now see if the clas is included. If not, then we should default to the last class used.
284
+ # Now see if the clas is included. If not, then we should default to the last class used.
285
285
  begin
286
286
  klass = Classes.new(split[2])
287
287
  @last_explicit_class = klass
288
288
  rescue ArgumentError
289
- # Wasn't a CLASS
290
- # So add the last explicit class in
289
+ # Wasn't a CLASS
290
+ # So add the last explicit class in
291
291
  line = ""
292
292
  (2).times {|i| line += "#{split[i]} "}
293
293
  line += " #{@last_explicit_class} "
@@ -297,12 +297,12 @@ module Dnsruby
297
297
  rescue Error => e
298
298
  end
299
299
 
300
- # Add the type so we can load the zone one RRSet at a time.
300
+ # Add the type so we can load the zone one RRSet at a time.
301
301
  type = Types.new(split[3].strip)
302
302
  is_soa = (type == Types::SOA)
303
303
  type_was = type
304
304
  if (type == Types.RRSIG)
305
- # If this is an RRSIG record, then add the TYPE COVERED rather than the type - this allows us to load a complete RRSet at a time
305
+ # If this is an RRSIG record, then add the TYPE COVERED rather than the type - this allows us to load a complete RRSet at a time
306
306
  type = Types.new(split[4].strip)
307
307
  end
308
308
 
@@ -316,10 +316,10 @@ module Dnsruby
316
316
 
317
317
  if (is_soa)
318
318
  if (@soa_ttl)
319
- # Replace the %MISSING_TTL% text with the SOA TTL from the config
319
+ # Replace the %MISSING_TTL% text with the SOA TTL from the config
320
320
  line.sub!(" %MISSING_TTL% ", " #{@soa_ttl} ")
321
321
  else
322
- # Can we try the @last_explicit_ttl?
322
+ # Can we try the @last_explicit_ttl?
323
323
  if (@last_explicit_ttl)
324
324
  line.sub!(" %MISSING_TTL% ", " #{@last_explicit_ttl} ")
325
325
  end
@@ -337,20 +337,20 @@ module Dnsruby
337
337
  line += " " + stored_line.strip
338
338
  end
339
339
 
340
- # We need to fix up any non-absolute names in the RR
341
- # Some RRs have a single name, at the end of the string -
342
- # to do these, we can just check the last character for "." and add the
343
- # "." + origin string if necessary
340
+ # We need to fix up any non-absolute names in the RR
341
+ # Some RRs have a single name, at the end of the string -
342
+ # to do these, we can just check the last character for "." and add the
343
+ # "." + origin string if necessary
344
344
  if ([Types::MX, Types::NS, Types::AFSDB, Types::NAPTR, Types::RT,
345
345
  Types::SRV, Types::CNAME, Types::MB, Types::MG, Types::MR,
346
346
  Types::PTR, Types::DNAME].include?type_was)
347
- # if (line[line.length-1, 1] != ".")
347
+ # if (line[line.length-1, 1] != ".")
348
348
  if (!(/\.\z/ =~ line))
349
349
  line = line + "." + @origin.to_s + "."
350
350
  end
351
351
  end
352
- # Other RRs have several names. These should be parsed by Dnsruby,
353
- # and the names adjusted there.
352
+ # Other RRs have several names. These should be parsed by Dnsruby,
353
+ # and the names adjusted there.
354
354
  if ([Types::MINFO, Types::PX, Types::RP].include?type_was)
355
355
  parsed_rr = Dnsruby::RR.create(line)
356
356
  case parsed_rr.type
@@ -384,11 +384,11 @@ module Dnsruby
384
384
  return line+"\n"
385
385
  end
386
386
 
387
- # Get the TTL in seconds from the m, h, d, w format
387
+ # Get the TTL in seconds from the m, h, d, w format
388
388
  def get_ttl(ttl_text_in)
389
- # If no letter afterwards, then in seconds already
390
- # Could be e.g. "3d4h12m" - unclear if "4h5w" is legal - best assume it is
391
- # So, search out each letter in the string, and get the number before it.
389
+ # If no letter afterwards, then in seconds already
390
+ # Could be e.g. "3d4h12m" - unclear if "4h5w" is legal - best assume it is
391
+ # So, search out each letter in the string, and get the number before it.
392
392
  ttl_text = ttl_text_in.downcase
393
393
  index = ttl_text.index(/[whdms]/)
394
394
  if (!index)
@@ -421,7 +421,7 @@ module Dnsruby
421
421
  end
422
422
 
423
423
  def replace_soa_ttl_fields(line)
424
- # Replace any fields which evaluate to 0
424
+ # Replace any fields which evaluate to 0
425
425
  split = line.split
426
426
  4.times {|i|
427
427
  x = i + 7
@@ -431,16 +431,16 @@ module Dnsruby
431
431
  return split.join(" ") + "\n"
432
432
  end
433
433
 
434
- # This method is included only for OpenDNSSEC support. It should not be
435
- # used otherwise.
436
- # Frig the RR type so that NSEC records appear last in the RRSets.
437
- # Also make sure that DNSKEYs come first (so we have a key to verify
438
- # the RRSet with!).
434
+ # This method is included only for OpenDNSSEC support. It should not be
435
+ # used otherwise.
436
+ # Frig the RR type so that NSEC records appear last in the RRSets.
437
+ # Also make sure that DNSKEYs come first (so we have a key to verify
438
+ # the RRSet with!).
439
439
  def prefix_for_rrset_order(type, type_was) # :nodoc: all
440
- # Now make sure that NSEC(3) RRs go to the back of the list
440
+ # Now make sure that NSEC(3) RRs go to the back of the list
441
441
  if ['NSEC', 'NSEC3'].include?type.string
442
442
  if (type_was == Types::RRSIG)
443
- # Get the RRSIG first
443
+ # Get the RRSIG first
444
444
  type_string = "ZZ" + type.string
445
445
  else
446
446
  type_string = "ZZZ" + type.string
@@ -448,7 +448,7 @@ module Dnsruby
448
448
  elsif type == Types::DNSKEY
449
449
  type_string = "0" + type.string
450
450
  elsif type == Types::NS
451
- # Make sure that we see the NS records first so we know the delegation status
451
+ # Make sure that we see the NS records first so we know the delegation status
452
452
  type_string = "1" + type.string
453
453
  else
454
454
  type_string = type.string
@@ -1,378 +1,378 @@
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
- module Dnsruby
18
- # This class performs zone transfers as per RFC1034 (AXFR) and RFC1995 (IXFR).
19
- class ZoneTransfer
20
- # The nameserver to use for the zone transfer - defaults to system config
21
- attr_accessor :server
22
- # What type of transfer to do (IXFR or AXFR) - defaults to AXFR
23
- attr_accessor :transfer_type
24
- # The class - defaults to IN
25
- attr_accessor :klass
26
- # The port to connect to - defaults to 53
27
- attr_accessor :port
28
- # If using IXFR, this is the SOA serial number to start the incrementals from
29
- attr_accessor :serial
30
- # The TSIG record used to sign the transfer
31
- attr_reader :tsig
32
- # Returns the tsigstate of the last transfer (nil if no TSIG signed transfer has occurred)
33
- attr_reader :last_tsigstate
34
-
35
- #Sets the TSIG to sign the zone transfer with.
36
- #Pass in either a Dnsruby::RR::TSIG, or a key_name and key (or just a key)
37
- #Pass in nil to stop tsig signing.
38
- #* res.tsig=(tsig_rr)
39
- #* res.tsig=(key_name, key)
40
- #* res.tsig=nil # Don't sign the transfer
41
- def tsig=(*args)
42
- @tsig = SingleResolver.get_tsig(args)
43
- end
44
-
45
-
46
- def initialize
47
- @server=Config.new.nameserver[0]
48
- @transfer_type = Types.AXFR
49
- @klass=Classes.IN
50
- @port=53
51
- @serial=0
52
- @tsig = nil
53
- @axfr = nil
54
- end
55
-
56
- # Perform a zone transfer (RFC1995)
57
- # If an IXFR query is unsuccessful, then AXFR is tried (and @transfer_type is set
58
- # to AXFR)
59
- # TCP is used as the only transport
60
- #
61
- # If AXFR is performed, then the zone will be returned as a set of records :
62
- #
63
- # zt = Dnsruby::ZoneTransfer.new
64
- # zt.transfer_type = Dnsruby::Types.AXFR
65
- # zt.server = "ns0.validation-test-servers.nominet.org.uk"
66
- # zone = zt.transfer("validation-test-servers.nominet.org.uk")
67
- # soa = zone[0]
68
- # rec1 = zone[1]
69
- # print zone.to_s
70
- #
71
- #
72
- # If IXFR is performed, then the incrementals will be returned as a set of Deltas.
73
- # Each Delta contains the start and end SOA serial number, as well as an array of
74
- # adds and deletes that occurred between the start and end.
75
- #
76
- # zt = Dnsruby::ZoneTransfer.new
77
- # zt.transfer_type = Dnsruby::Types.IXFR
78
- # zt.server = "ns0.validation-test-servers.nominet.org.uk"
79
- # zt.serial = 2007090401
80
- # deltas = zt.transfer("validation-test-servers.nominet.org.uk")
81
- # assert_equal("Should show up in transfer", deltas[0].adds[1].data)
82
- def transfer(zone)
83
- servers = @server
84
- if (servers.class == String)
85
- servers=[servers]
86
- end
87
- xfr = nil
88
- exception = nil
89
- servers.each do |server|
90
- begin
91
- server=Config.resolve_server(server)
92
- xfr = do_transfer(zone, server)
93
- break
94
- rescue Exception => e
95
- exception = e
96
- end
97
- end
98
- if (xfr == nil && exception != nil)
99
- raise exception
100
- end
101
- return xfr
102
- end
103
-
104
- def do_transfer(zone, server) #:nodoc: all
105
- @transfer_type = Types.new(@transfer_type)
106
- @state = :InitialSoa
107
- socket = TCPSocket.new(server, @port)
108
- begin
109
- # Send an initial query
110
- msg = Message.new(zone, @transfer_type, @klass)
111
- if @transfer_type == Types.IXFR
112
- rr = RR.create("#{zone} 0 IN SOA" + '0 0 %u 0 0 0 0' % @serial)
113
- msg.add_authority(rr)
114
- end
115
- send_message(socket, msg)
116
-
117
- while (@state != :End)
118
- response = receive_message(socket)
119
-
120
- if (@state == :InitialSoa)
121
- rcode = response.rcode
122
- if (rcode != RCode.NOERROR)
123
- if (@transfer_type == Types.IXFR &&
124
- rcode == RCode.NOTIMP)
125
- # IXFR didn't work - let's try AXFR
126
- Dnsruby.log.debug("IXFR DID NOT WORK (rcode = NOTIMP) - TRYING AXFR!!")
127
- @state = :InitialSoa
128
- @transfer_type=Types.AXFR
129
- # Send an initial AXFR query
130
- msg = Message.new(zone, @transfer_type, @klass)
131
- send_message(socket, msg)
132
- next
133
- end
134
- raise ResolvError.new(rcode.string);
135
- end
136
-
137
- if (response.question[0].qtype != @transfer_type)
138
- raise ResolvError.new("invalid question section")
139
- end
140
-
141
- if (response.header.ancount == 0 && @transfer_type == Types.IXFR)
142
- Dnsruby.log.debug("IXFR DID NOT WORK (ancount = 0) - TRYING AXFR!!")
143
- # IXFR didn't work - let's try AXFR
144
- @transfer_type=Types.AXFR
145
- # Send an initial AXFR query
146
- @state = :InitialSoa
147
- msg = Message.new(zone, @transfer_type, @klass)
148
- send_message(socket, msg)
149
- next
150
- end
151
- end
152
-
153
- response.each_answer { |rr|
154
- parseRR(rr)
155
- }
156
- if (@state == :End &&
157
- response.tsigstate == :Intermediate)
158
- raise ResolvError.new("last message must be signed")
159
- end
160
- if (@state == :End && @tsig)
161
- if (response.tsigstate != :Verified)
162
- @last_tsigstate = :Failed
163
- raise ResolvError.new("Zone transfer not correctly signed")
164
- end
165
- @last_tsigstate = :Verified
166
- end
167
- end
168
- # This could return with an IXFR response, or an AXFR response.
169
- # If it fails completely, then try to send an AXFR query.
170
- # Once the query has been sent, then enter the main response loop.
171
- # Unless we know we're definitely AXFR, we should be prepared for either IXFR or AXFR
172
- # AXFR response : The first and the last RR of the response is the SOA record of the zone.
173
- # The whole zone is returned inbetween.
174
- # IXFR response : one or more difference sequences is returned. The list of difference
175
- # sequences is preceded and followed by a copy of the server's current
176
- # version of the SOA.
177
- # Each difference sequence represents one update to the zone (one SOA
178
- # serial change) consisting of deleted RRs and added RRs. The first RR
179
- # of the deleted RRs is the older SOA RR and the first RR of the added
180
- # RRs is the newer SOA RR.
181
- socket.close
182
- if (@axfr!=nil)
183
- return @axfr
184
- end
185
- return @ixfr
186
- rescue Exception => e
187
- socket.close
188
- raise e
189
- end
190
- end
191
-
192
- # All changes between two versions of a zone in an IXFR response.
193
- class Delta
194
-
195
- # The starting serial number of this delta.
196
- attr_accessor :start
197
-
198
- # The ending serial number of this delta.
199
- attr_accessor :end
200
-
201
- # A list of records added between the start and end versions
202
- attr_accessor :adds
203
-
204
- # A list of records deleted between the start and end versions
205
- attr_accessor :deletes
206
-
207
- def initialize()
208
- @adds = []
209
- @deletes = []
210
- end
211
-
212
- def to_s
213
- ret = "Adds : " + @adds.join(",")
214
- ret +=", Deletes : " + @deletes.join(",")
215
- end
216
- end
217
-
218
- #Compare two serials according to RFC 1982. Return 0 if equal,
219
- #-1 if s1 is bigger, 1 if s1 is smaller.
220
- def compare_serial(s1, s2)
221
- if s1 == s2
222
- return 0
223
- end
224
- if s1 < s2 and (s2 - s1) < (2**31)
225
- return 1
226
- end
227
- if s1 > s2 and (s1 - s2) > (2**31)
228
- return 1
229
- end
230
- if s1 < s2 and (s2 - s1) > (2**31)
231
- return -1
232
- end
233
- if s1 > s2 and (s1 - s2) < (2**31)
234
- return -1
235
- end
236
- return 0
237
- end
238
-
239
- def parseRR(rec) #:nodoc: all
240
- name = rec.name
241
- type = rec.type
242
- delta = Delta.new
243
-
244
- case @state
245
- when :InitialSoa
246
- if (type != Types.SOA)
247
- raise ResolvError.new("missing initial SOA")
248
- end
249
- @initialsoa = rec
250
- # Remember the serial number in the initial SOA; we need it
251
- # to recognize the end of an IXFR.
252
- @end_serial = rec.serial
253
- # if ((@transfer_type == Types.IXFR) && (@end_serial <= @serial))
254
- if ((@transfer_type == Types.IXFR) && (compare_serial(@end_serial, @serial) >= 0))
255
- Dnsruby.log.debug("zone up to date")
256
- raise ZoneSerialError.new("IXFR up to date: expected serial " +
257
- @serial.to_s + " , got " + rec.serial.to_s);
258
- @state = :End
259
- else
260
- @state = :FirstData
261
- end
262
- when :FirstData
263
- # If the transfer begins with 1 SOA, it's an AXFR.
264
- # If it begins with 2 SOAs, it's an IXFR.
265
- if (@transfer_type == Types.IXFR && type == Types.SOA &&
266
- rec.serial == @serial)
267
- Dnsruby.log.debug("IXFR response - using IXFR")
268
- @rtype = Types.IXFR
269
- @ixfr = []
270
- @state = :Ixfr_DelSoa
271
- else
272
- Dnsruby.log.debug("AXFR response - using AXFR")
273
- @rtype = Types.AXFR
274
- @transfer_type = Types.AXFR
275
- @axfr = []
276
- @axfr << @initialsoa
277
- @state = :Axfr
278
- end
279
- parseRR(rec) # Restart...
280
- return
281
-
282
- when :Ixfr_DelSoa
283
- delta = Delta.new
284
- @ixfr.push(delta)
285
- delta.start = rec.serial
286
- delta.deletes << rec
287
- @state = :Ixfr_Del
288
-
289
- when :Ixfr_Del
290
- if (type == Types.SOA)
291
- @current_serial = rec.serial
292
- @state = :Ixfr_AddSoa
293
- parseRR(rec); # Restart...
294
- return;
295
- end
296
- delta = @ixfr[@ixfr.length - 1]
297
- delta.deletes << rec
298
-
299
- when :Ixfr_AddSoa
300
- delta = @ixfr[@ixfr.length - 1]
301
- delta.end = rec.serial
302
- delta.adds << rec
303
- @state = :Ixfr_Add
304
-
305
- when :Ixfr_Add
306
- if (type == Types.SOA)
307
- soa_serial = rec.serial
308
- if (soa_serial == @end_serial)
309
- @state = :End
310
- return
311
- elsif (soa_serial != @current_serial)
312
- raise ZoneSerialError.new("IXFR out of sync: expected serial " +
313
- @current_serial.to_s + " , got " + soa_serial.to_s);
314
- else
315
- @state = :Ixfr_DelSoa
316
- parseRR(rec); # Restart...
317
- return;
318
- end
319
- end
320
- delta = @ixfr[@ixfr.length - 1]
321
- delta.adds << rec
322
-
323
- when :Axfr
324
- # Old BINDs sent cross class A records for non IN classes.
325
- if (type == Types.A && rec.klass() != @klass)
326
- else
327
- if (type == Types.SOA)
328
- @state = :End
329
- else
330
- @axfr << rec
331
- end
332
- end
333
- when :End
334
- raise ResolvError.new("extra data in zone transfer")
335
-
336
- else
337
- raise ResolvError.new("invalid state for zone transfer")
338
- end
339
- end
340
-
341
-
342
- def send_message(socket, msg) #:nodoc: all
343
- if (@tsig)
344
- @tsig.apply(msg)
345
- @tsig = msg.tsig
346
- end
347
- query_packet = msg.encode
348
- lenmsg = [query_packet.length].pack('n')
349
- socket.send(lenmsg, 0)
350
- socket.send(query_packet, 0)
351
- end
352
-
353
- def tcp_read(socket, len) #:nodoc: all
354
- buf=""
355
- while (buf.length < len) and not socket.eof? do
356
- buf += socket.read(len-buf.length)
357
- end
358
- return buf
359
- end
360
-
361
- def receive_message(socket) #:nodoc: all
362
- buf = tcp_read(socket, 2)
363
- answersize = buf.unpack('n')[0]
364
- # Some servers (e.g. dnscache) apparently hang up on some connections.
365
- # Thanks to Matt Palmer for the fix.
366
- raise ResolvError.new("Server did not send a valid answer") if answersize.nil?
367
- buf = tcp_read(socket, answersize)
368
- msg = Message.decode(buf)
369
- if (@tsig)
370
- if !@tsig.verify_envelope(msg, buf)
371
- Dnsruby.log.error("Bad signature on zone transfer - closing connection")
372
- raise ResolvError.new("Bad signature on zone transfer")
373
- end
374
- end
375
- return msg
376
- end
377
- end
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
+ module Dnsruby
18
+ # This class performs zone transfers as per RFC1034 (AXFR) and RFC1995 (IXFR).
19
+ class ZoneTransfer
20
+ # The nameserver to use for the zone transfer - defaults to system config
21
+ attr_accessor :server
22
+ # What type of transfer to do (IXFR or AXFR) - defaults to AXFR
23
+ attr_accessor :transfer_type
24
+ # The class - defaults to IN
25
+ attr_accessor :klass
26
+ # The port to connect to - defaults to 53
27
+ attr_accessor :port
28
+ # If using IXFR, this is the SOA serial number to start the incrementals from
29
+ attr_accessor :serial
30
+ # The TSIG record used to sign the transfer
31
+ attr_reader :tsig
32
+ # Returns the tsigstate of the last transfer (nil if no TSIG signed transfer has occurred)
33
+ attr_reader :last_tsigstate
34
+
35
+ # Sets the TSIG to sign the zone transfer with.
36
+ # Pass in either a Dnsruby::RR::TSIG, or a key_name and key (or just a key)
37
+ # Pass in nil to stop tsig signing.
38
+ # * res.tsig=(tsig_rr)
39
+ # * res.tsig=(key_name, key)
40
+ # * res.tsig=nil # Don't sign the transfer
41
+ def tsig=(*args)
42
+ @tsig = SingleResolver.get_tsig(args)
43
+ end
44
+
45
+
46
+ def initialize
47
+ @server=Config.new.nameserver[0]
48
+ @transfer_type = Types.AXFR
49
+ @klass=Classes.IN
50
+ @port=53
51
+ @serial=0
52
+ @tsig = nil
53
+ @axfr = nil
54
+ end
55
+
56
+ # Perform a zone transfer (RFC1995)
57
+ # If an IXFR query is unsuccessful, then AXFR is tried (and @transfer_type is set
58
+ # to AXFR)
59
+ # TCP is used as the only transport
60
+ #
61
+ # If AXFR is performed, then the zone will be returned as a set of records :
62
+ #
63
+ # zt = Dnsruby::ZoneTransfer.new
64
+ # zt.transfer_type = Dnsruby::Types.AXFR
65
+ # zt.server = "ns0.validation-test-servers.nominet.org.uk"
66
+ # zone = zt.transfer("validation-test-servers.nominet.org.uk")
67
+ # soa = zone[0]
68
+ # rec1 = zone[1]
69
+ # print zone.to_s
70
+ #
71
+ #
72
+ # If IXFR is performed, then the incrementals will be returned as a set of Deltas.
73
+ # Each Delta contains the start and end SOA serial number, as well as an array of
74
+ # adds and deletes that occurred between the start and end.
75
+ #
76
+ # zt = Dnsruby::ZoneTransfer.new
77
+ # zt.transfer_type = Dnsruby::Types.IXFR
78
+ # zt.server = "ns0.validation-test-servers.nominet.org.uk"
79
+ # zt.serial = 2007090401
80
+ # deltas = zt.transfer("validation-test-servers.nominet.org.uk")
81
+ # assert_equal("Should show up in transfer", deltas[0].adds[1].data)
82
+ def transfer(zone)
83
+ servers = @server
84
+ if (servers.class == String)
85
+ servers=[servers]
86
+ end
87
+ xfr = nil
88
+ exception = nil
89
+ servers.each do |server|
90
+ begin
91
+ server=Config.resolve_server(server)
92
+ xfr = do_transfer(zone, server)
93
+ break
94
+ rescue Exception => e
95
+ exception = e
96
+ end
97
+ end
98
+ if (xfr == nil && exception != nil)
99
+ raise exception
100
+ end
101
+ return xfr
102
+ end
103
+
104
+ def do_transfer(zone, server) #:nodoc: all
105
+ @transfer_type = Types.new(@transfer_type)
106
+ @state = :InitialSoa
107
+ socket = TCPSocket.new(server, @port)
108
+ begin
109
+ # Send an initial query
110
+ msg = Message.new(zone, @transfer_type, @klass)
111
+ if @transfer_type == Types.IXFR
112
+ rr = RR.create("#{zone} 0 IN SOA" + '0 0 %u 0 0 0 0' % @serial)
113
+ msg.add_authority(rr)
114
+ end
115
+ send_message(socket, msg)
116
+
117
+ while (@state != :End)
118
+ response = receive_message(socket)
119
+
120
+ if (@state == :InitialSoa)
121
+ rcode = response.rcode
122
+ if (rcode != RCode.NOERROR)
123
+ if (@transfer_type == Types.IXFR &&
124
+ rcode == RCode.NOTIMP)
125
+ # IXFR didn't work - let's try AXFR
126
+ Dnsruby.log.debug("IXFR DID NOT WORK (rcode = NOTIMP) - TRYING AXFR!!")
127
+ @state = :InitialSoa
128
+ @transfer_type=Types.AXFR
129
+ # Send an initial AXFR query
130
+ msg = Message.new(zone, @transfer_type, @klass)
131
+ send_message(socket, msg)
132
+ next
133
+ end
134
+ raise ResolvError.new(rcode.string);
135
+ end
136
+
137
+ if (response.question[0].qtype != @transfer_type)
138
+ raise ResolvError.new("invalid question section")
139
+ end
140
+
141
+ if (response.header.ancount == 0 && @transfer_type == Types.IXFR)
142
+ Dnsruby.log.debug("IXFR DID NOT WORK (ancount = 0) - TRYING AXFR!!")
143
+ # IXFR didn't work - let's try AXFR
144
+ @transfer_type=Types.AXFR
145
+ # Send an initial AXFR query
146
+ @state = :InitialSoa
147
+ msg = Message.new(zone, @transfer_type, @klass)
148
+ send_message(socket, msg)
149
+ next
150
+ end
151
+ end
152
+
153
+ response.each_answer { |rr|
154
+ parseRR(rr)
155
+ }
156
+ if (@state == :End &&
157
+ response.tsigstate == :Intermediate)
158
+ raise ResolvError.new("last message must be signed")
159
+ end
160
+ if (@state == :End && @tsig)
161
+ if (response.tsigstate != :Verified)
162
+ @last_tsigstate = :Failed
163
+ raise ResolvError.new("Zone transfer not correctly signed")
164
+ end
165
+ @last_tsigstate = :Verified
166
+ end
167
+ end
168
+ # This could return with an IXFR response, or an AXFR response.
169
+ # If it fails completely, then try to send an AXFR query.
170
+ # Once the query has been sent, then enter the main response loop.
171
+ # Unless we know we're definitely AXFR, we should be prepared for either IXFR or AXFR
172
+ # AXFR response : The first and the last RR of the response is the SOA record of the zone.
173
+ # The whole zone is returned inbetween.
174
+ # IXFR response : one or more difference sequences is returned. The list of difference
175
+ # sequences is preceded and followed by a copy of the server's current
176
+ # version of the SOA.
177
+ # Each difference sequence represents one update to the zone (one SOA
178
+ # serial change) consisting of deleted RRs and added RRs. The first RR
179
+ # of the deleted RRs is the older SOA RR and the first RR of the added
180
+ # RRs is the newer SOA RR.
181
+ socket.close
182
+ if (@axfr!=nil)
183
+ return @axfr
184
+ end
185
+ return @ixfr
186
+ rescue Exception => e
187
+ socket.close
188
+ raise e
189
+ end
190
+ end
191
+
192
+ # All changes between two versions of a zone in an IXFR response.
193
+ class Delta
194
+
195
+ # The starting serial number of this delta.
196
+ attr_accessor :start
197
+
198
+ # The ending serial number of this delta.
199
+ attr_accessor :end
200
+
201
+ # A list of records added between the start and end versions
202
+ attr_accessor :adds
203
+
204
+ # A list of records deleted between the start and end versions
205
+ attr_accessor :deletes
206
+
207
+ def initialize()
208
+ @adds = []
209
+ @deletes = []
210
+ end
211
+
212
+ def to_s
213
+ ret = "Adds : " + @adds.join(",")
214
+ ret +=", Deletes : " + @deletes.join(",")
215
+ end
216
+ end
217
+
218
+ # Compare two serials according to RFC 1982. Return 0 if equal,
219
+ # -1 if s1 is bigger, 1 if s1 is smaller.
220
+ def compare_serial(s1, s2)
221
+ if s1 == s2
222
+ return 0
223
+ end
224
+ if s1 < s2 and (s2 - s1) < (2**31)
225
+ return 1
226
+ end
227
+ if s1 > s2 and (s1 - s2) > (2**31)
228
+ return 1
229
+ end
230
+ if s1 < s2 and (s2 - s1) > (2**31)
231
+ return -1
232
+ end
233
+ if s1 > s2 and (s1 - s2) < (2**31)
234
+ return -1
235
+ end
236
+ return 0
237
+ end
238
+
239
+ def parseRR(rec) #:nodoc: all
240
+ name = rec.name
241
+ type = rec.type
242
+ delta = Delta.new
243
+
244
+ case @state
245
+ when :InitialSoa
246
+ if (type != Types.SOA)
247
+ raise ResolvError.new("missing initial SOA")
248
+ end
249
+ @initialsoa = rec
250
+ # Remember the serial number in the initial SOA; we need it
251
+ # to recognize the end of an IXFR.
252
+ @end_serial = rec.serial
253
+ # if ((@transfer_type == Types.IXFR) && (@end_serial <= @serial))
254
+ if ((@transfer_type == Types.IXFR) && (compare_serial(@end_serial, @serial) >= 0))
255
+ Dnsruby.log.debug("zone up to date")
256
+ raise ZoneSerialError.new("IXFR up to date: expected serial " +
257
+ @serial.to_s + " , got " + rec.serial.to_s);
258
+ @state = :End
259
+ else
260
+ @state = :FirstData
261
+ end
262
+ when :FirstData
263
+ # If the transfer begins with 1 SOA, it's an AXFR.
264
+ # If it begins with 2 SOAs, it's an IXFR.
265
+ if (@transfer_type == Types.IXFR && type == Types.SOA &&
266
+ rec.serial == @serial)
267
+ Dnsruby.log.debug("IXFR response - using IXFR")
268
+ @rtype = Types.IXFR
269
+ @ixfr = []
270
+ @state = :Ixfr_DelSoa
271
+ else
272
+ Dnsruby.log.debug("AXFR response - using AXFR")
273
+ @rtype = Types.AXFR
274
+ @transfer_type = Types.AXFR
275
+ @axfr = []
276
+ @axfr << @initialsoa
277
+ @state = :Axfr
278
+ end
279
+ parseRR(rec) # Restart...
280
+ return
281
+
282
+ when :Ixfr_DelSoa
283
+ delta = Delta.new
284
+ @ixfr.push(delta)
285
+ delta.start = rec.serial
286
+ delta.deletes << rec
287
+ @state = :Ixfr_Del
288
+
289
+ when :Ixfr_Del
290
+ if (type == Types.SOA)
291
+ @current_serial = rec.serial
292
+ @state = :Ixfr_AddSoa
293
+ parseRR(rec); # Restart...
294
+ return;
295
+ end
296
+ delta = @ixfr[@ixfr.length - 1]
297
+ delta.deletes << rec
298
+
299
+ when :Ixfr_AddSoa
300
+ delta = @ixfr[@ixfr.length - 1]
301
+ delta.end = rec.serial
302
+ delta.adds << rec
303
+ @state = :Ixfr_Add
304
+
305
+ when :Ixfr_Add
306
+ if (type == Types.SOA)
307
+ soa_serial = rec.serial
308
+ if (soa_serial == @end_serial)
309
+ @state = :End
310
+ return
311
+ elsif (soa_serial != @current_serial)
312
+ raise ZoneSerialError.new("IXFR out of sync: expected serial " +
313
+ @current_serial.to_s + " , got " + soa_serial.to_s);
314
+ else
315
+ @state = :Ixfr_DelSoa
316
+ parseRR(rec); # Restart...
317
+ return;
318
+ end
319
+ end
320
+ delta = @ixfr[@ixfr.length - 1]
321
+ delta.adds << rec
322
+
323
+ when :Axfr
324
+ # Old BINDs sent cross class A records for non IN classes.
325
+ if (type == Types.A && rec.klass() != @klass)
326
+ else
327
+ if (type == Types.SOA)
328
+ @state = :End
329
+ else
330
+ @axfr << rec
331
+ end
332
+ end
333
+ when :End
334
+ raise ResolvError.new("extra data in zone transfer")
335
+
336
+ else
337
+ raise ResolvError.new("invalid state for zone transfer")
338
+ end
339
+ end
340
+
341
+
342
+ def send_message(socket, msg) #:nodoc: all
343
+ if (@tsig)
344
+ @tsig.apply(msg)
345
+ @tsig = msg.tsig
346
+ end
347
+ query_packet = msg.encode
348
+ lenmsg = [query_packet.length].pack('n')
349
+ socket.send(lenmsg, 0)
350
+ socket.send(query_packet, 0)
351
+ end
352
+
353
+ def tcp_read(socket, len) #:nodoc: all
354
+ buf=""
355
+ while (buf.length < len) and not socket.eof? do
356
+ buf += socket.read(len-buf.length)
357
+ end
358
+ return buf
359
+ end
360
+
361
+ def receive_message(socket) #:nodoc: all
362
+ buf = tcp_read(socket, 2)
363
+ answersize = buf.unpack('n')[0]
364
+ # Some servers (e.g. dnscache) apparently hang up on some connections.
365
+ # Thanks to Matt Palmer for the fix.
366
+ raise ResolvError.new("Server did not send a valid answer") if answersize.nil?
367
+ buf = tcp_read(socket, answersize)
368
+ msg = Message.decode(buf)
369
+ if (@tsig)
370
+ if !@tsig.verify_envelope(msg, buf)
371
+ Dnsruby.log.error("Bad signature on zone transfer - closing connection")
372
+ raise ResolvError.new("Bad signature on zone transfer")
373
+ end
374
+ end
375
+ return msg
376
+ end
377
+ end
378
378
  end