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
@@ -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