Parent

Class Index [+]

Quicksearch

Dnsruby::ZoneReader

Public Class Methods

new(origin, soa_minimum = nil, soa_ttl = nil) click to toggle source

Create a new ZoneReader. The zone origin is required. If the desired SOA minimum and TTL are passed in, then they are used as default values.

    # File lib/Dnsruby/zone_reader.rb, line 27
27:     def initialize(origin, soa_minimum = nil, soa_ttl = nil)
28:       @origin = origin.to_s
29: 
30:       if (!Name.create(@origin).absolute?)
31:         @origin = @origin.to_s + "."
32:       end
33:       @soa_ttl = soa_ttl
34:       if (soa_minimum && !@last_explicit_ttl)
35:         @last_explicit_ttl = soa_minimum
36:       else
37:         @last_explicit_ttl = 0
38:       end
39:       @last_explicit_class = Classes.new("IN")
40:       @last_name = nil
41:       @continued_line = nil
42:       @in_quoted_section = false
43:     end

Public Instance Methods

get_ttl(ttl_text_in) click to toggle source

Get the TTL in seconds from the m, h, d, w format

     # File lib/Dnsruby/zone_reader.rb, line 377
377:     def get_ttl(ttl_text_in)
378:       # If no letter afterwards, then in seconds already
379:       # Could be e.g. "3d4h12m" - unclear if "4h5w" is legal - best assume it is
380:       # So, search out each letter in the string, and get the number before it.
381:       ttl_text = ttl_text_in.downcase
382:       index = ttl_text.index(/[whdms]/)
383:       if (!index)
384:         return ttl_text.to_i
385:       end
386:       last_index = 1
387:       total = 0
388:       while (index)
389:         letter = ttl_text[index]
390:         number = ttl_text[last_index + 1, index-last_index-1].to_i
391:         new_number = 0
392:         case letter
393:         when 115 then # "s"
394:           new_number = number
395:         when 109 then # "m"
396:           new_number = number * 60
397:         when 104 then # "h"
398:           new_number = number * 3600
399:         when 100 then # "d"
400:           new_number = number * 86400
401:         when 119 then # "w"
402:           new_number = number * 604800
403:         end
404:         total += new_number
405: 
406:         last_index = index
407:         index = ttl_text.index(/[whdms]/, last_index + 1)
408:       end
409:       return total
410:     end
normalise_line(line, do_prefix_hack = false) click to toggle source

Take a line from the input zone file, and return the normalised form do_prefix_hack should always be false

     # File lib/Dnsruby/zone_reader.rb, line 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.
206:       if ((line[0,1] == " ") || (line[0,1] == "\t"))
207:         line = @last_name + " " + line
208:       end
209:       line.chomp!
210:       line.sub!(/\s+@$/, " #{@origin}") # IN CNAME @
211:       line.sub!(/^@\s+/, "#{@origin} ") # IN CNAME @
212:       line.sub!(/\s+@\s+/, " #{@origin} ")
213:       line.strip!
214: 
215: 
216:       # o We need to identify the domain name in the record, and then
217:       split = line.split(' ') # split on whitespace
218:       name = split[0].strip
219:       if (name.index"\\")
220: 
221:         ls =[]
222:         Name.create(name).labels.each {|el| ls.push(Name.decode(el.to_s))}
223:         new_name = ls.join('.')
224: 
225: 
226:         if (!(/\.\z/ =~ name))
227:           new_name += "." + @origin
228:         else
229:           new_name += "."
230:         end
231:         line = new_name + " "
232:         (split.length - 1).times {|i| line += "#{split[i+1]} "}
233:         line += "\n"
234:         name = new_name
235:         split = line.split
236:         # o add $ORIGIN to it if it is not absolute
237:       elsif !(/\.\z/ =~ name)
238:         new_name = name + "." + @origin
239:         line.sub!(name, new_name)
240:         name = new_name
241:         split = line.split
242:       end
243: 
244:       # If the second field is not a number, then we should add the TTL to the line
245:       # Remember we can get "m" "w" "y" here! So need to check for appropriate regexp...
246:       found_ttl_regexp = (split[1]=~/^[0-9]+[smhdwSMHDW]/)
247:       if (found_ttl_regexp == 0)
248:         # Replace the formatted ttl with an actual number
249:         ttl = get_ttl(split[1])
250:         line = name + " #{ttl} "
251:         @last_explicit_ttl = ttl
252:         (split.length - 2).times {|i| line += "#{split[i+2]} "}
253:         line += "\n"
254:         split = line.split
255:       elsif (((split[1]).to_i == 0) && (split[1] != "0"))
256:         # Add the TTL
257:         if (!@last_explicit_ttl)
258:           # If this is the SOA record, and no @last_explicit_ttl is defined,
259:           # then we need to try the SOA TTL element from the config. Otherwise,
260:           # find the SOA Minimum field, and use that.
261:           # We should also generate a warning to that effect
262:           # How do we know if it is an SOA record at this stage? It must be, or
263:           # else @last_explicit_ttl should be defined
264:           # We could put a marker in the RR for now - and replace it once we know
265:           # the actual type. If the type is not SOA then, then we can raise an error
266:           line = name + " %MISSING_TTL% "
267:         else
268:           line = name + " #{@last_explicit_ttl} "
269:         end
270:         (split.length - 1).times {|i| line += "#{split[i+1]} "}
271:         line += "\n"
272:         split = line.split
273:       else
274:         @last_explicit_ttl = split[1].to_i
275:       end
276: 
277:       # Now see if the clas is included. If not, then we should default to the last class used.
278:       begin
279:         klass = Classes.new(split[2])
280:         @last_explicit_class = klass
281:       rescue ArgumentError
282:         # Wasn't a CLASS
283:         # So add the last explicit class in
284:         line = ""
285:         (2).times {|i| line += "#{split[i]} "}
286:         line += " #{@last_explicit_class} "
287:         (split.length - 2).times {|i| line += "#{split[i+2]} "}
288:         line += "\n"
289:         split = line.split
290:       rescue Error => e
291:       end
292: 
293:       # Add the type so we can load the zone one RRSet at a time.
294:       type = Types.new(split[3].strip)
295:       is_soa = (type == Types::SOA)
296:       type_was = type
297:       if (type == Types.RRSIG)
298:         # 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
299:         type = Types.new(split[4].strip)
300:       end
301: 
302:       type_string=prefix_for_rrset_order(type, type_was)
303:       @last_name = name
304: 
305:       if !([Types::NAPTR, Types::TXT].include?type_was)
306:         line.sub!("(", "")
307:         line.sub!(")", "")
308:       end
309: 
310:       if (is_soa)
311:         if (@soa_ttl)
312:           # Replace the %MISSING_TTL% text with the SOA TTL from the config
313:           line.sub!(" %MISSING_TTL% ", " #{@soa_ttl} ")
314:         else
315:           # Can we try the @last_explicit_ttl?
316:           if (@last_explicit_ttl)
317:             line.sub!(" %MISSING_TTL% ", " #{@last_explicit_ttl} ")
318:           end
319:         end
320:         line = replace_soa_ttl_fields(line)
321:         if (!@last_explicit_ttl)
322:           soa_rr = Dnsruby::RR.create(line)
323:           @last_explicit_ttl = soa_rr.minimum
324:         end
325:       end
326: 
327:       line = line.split.join(' ').strip
328:       # We need to fix up any non-absolute names in the RR
329:       # Some RRs have a single name, at the end of the string -
330:       #   to do these, we can just check the last character for "." and add the
331:       #   "." + origin string if necessary
332:       if ([Types::MX, Types::NS, Types::AFSDB, Types::NAPTR, Types::RT,
333:             Types::SRV, Types::CNAME, Types::MB, Types::MG, Types::MR,
334:             Types::PTR].include?type_was)
335:         #        if (line[line.length-1, 1] != ".")
336:         if (!(/\.\z/ =~ line))
337:           line = line + "." + @origin.to_s + "."
338:         end
339:       end
340:       # Other RRs have several names. These should be parsed by Dnsruby,
341:       #   and the names adjusted there.
342:       if ([Types::MINFO, Types::PX, Types::RP].include?type_was)
343:         parsed_rr = Dnsruby::RR.create(line)
344:         case parsed_rr.type
345:         when Types::MINFO
346:           if (!parsed_rr.rmailbx.absolute?)
347:             parsed_rr.rmailbx = parsed_rr.rmailbx.to_s + "." + @origin.to_s
348:           end
349:           if (!parsed_rr.emailbx.absolute?)
350:             parsed_rr.emailbx = parsed_rr.emailbx.to_s + "." + @origin.to_s
351:           end
352:         when Types::PX
353:           if (!parsed_rr.map822.absolute?)
354:             parsed_rr.map822 = parsed_rr.map822.to_s + "." + @origin.to_s
355:           end
356:           if (!parsed_rr.mapx400.absolute?)
357:             parsed_rr.mapx400 = parsed_rr.mapx400.to_s + "." + @origin.to_s
358:           end
359:         when Types::RP
360:           if (!parsed_rr.mailbox.absolute?)
361:             parsed_rr.mailbox = parsed_rr.mailbox.to_s + "." + @origin.to_s
362:             if (!parsed_rr.txtdomain.absolute?)
363:               parsed_rr.txtdomain = parsed_rr.txtdomain.to_s + "." + @origin.to_s
364:             end
365:           end
366:         end
367:         line = parsed_rr.to_s
368:       end
369: 
370:       if (do_prefix_hack)
371:         return line + "\n", type_string, @last_name
372:       end
373:       return line+"\n"
374:     end
process_file(file) click to toggle source

Takes a filename string and attempts to load a zone. Returns a list of RRs if successful, nil otherwise.

    # File lib/Dnsruby/zone_reader.rb, line 47
47:     def process_file(file)
48:       line_num = 0
49:       zone = nil
50:       IO.foreach(file) { |line|
51:         begin
52: 
53:           ret = process_line(line)
54:           if (ret)
55:             rr = RR.create(ret)
56:             if (!zone)
57:               zone = []
58:             end
59:             zone.push(rr)
60:           end
61:         rescue Exception => e
62:           raise ParseException.new("Error reading line #{line_num} of #{file} : [#{line}]")
63:         end
64:       }
65:       return zone
66:     end
process_line(line, do_prefix_hack = false) click to toggle source

Process the next line of the file Returns a string representing the normalised line.

     # File lib/Dnsruby/zone_reader.rb, line 70
 70:     def process_line(line, do_prefix_hack = false)
 71:       return nil if (line[0,1] == ";")
 72:       return nil if (line.strip.length == 0)
 73:       return nil if (!line || (line.length == 0))
 74:       @in_quoted_section = false if !@continued_line
 75: 
 76:       line = strip_comments(line)
 77: 
 78:       if (line.index("$ORIGIN") == 0)
 79:         @origin = line.split()[1].strip #  $ORIGIN <domain-name> [<comment>]
 80:         #                print "Setting $ORIGIN to #{@origin}\n"
 81:         return nil
 82:       end
 83:       if (line.index("$TTL") == 0)
 84:         @last_explicit_ttl = get_ttl(line.split()[1].strip) #  $TTL <ttl>
 85:         #                print "Setting $TTL to #{ttl}\n"
 86:         return nil
 87:       end
 88:       if (@continued_line)
 89:         # Add the next line until we see a ")"
 90:         # REMEMBER TO STRIP OFF COMMENTS!!!
 91:         @continued_line = strip_comments(@continued_line)
 92:         line = @continued_line.rstrip.chomp + " " + line
 93:         if (line.index(")"))
 94:           # OK
 95:           @continued_line = false
 96:         end
 97:       end
 98:       open_bracket = line.index("(")
 99:       if (open_bracket)
100:         # Keep going until we see ")"
101:         index = line.index(")")
102:         if (index && (index > open_bracket))
103:           # OK
104:           @continued_line = false
105:         else
106:           @continued_line = line
107:         end
108:       end
109:       return nil if @continued_line
110: 
111:       line = strip_comments(line) + "\n"
112: 
113:       # If SOA, then replace "3h" etc. with expanded seconds
114:       #      begin
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
120:     end
process_quotes(section) click to toggle source
     # File lib/Dnsruby/zone_reader.rb, line 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
193:       last_index = 0
194:       while (next_index = section.index("\"", last_index + 1))
195:         @in_quoted_section = !@in_quoted_section
196:         last_index = next_index
197:       end
198:     end
replace_soa_ttl_fields(line) click to toggle source
     # File lib/Dnsruby/zone_reader.rb, line 412
412:     def replace_soa_ttl_fields(line)
413:       # Replace any fields which evaluate to 0
414:       split = line.split
415:       4.times {|i|
416:         x = i + 7
417:         split[x].strip!
418:         split[x] = get_ttl(split[x]).to_s
419:       }
420:       return split.join(" ") + "\n"
421:     end
strip_comments(line) click to toggle source
     # File lib/Dnsruby/zone_reader.rb, line 122
122:     def strip_comments(line)
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
129:       if (line.index"\\")
130:         return strip_comments_meticulously(line)
131:       end
132:       while (next_index = line.index(";", last_index + 1))
133:         # Have there been any quotes since we last looked?
134:         process_quotes(line[last_index, next_index - last_index])
135: 
136:         # Now use @in_quoted_section to work out if the ';' terminates the line
137:         if (!@in_quoted_section)
138:           return line[0,next_index]
139:         end
140: 
141:         last_index = next_index
142:       end
143:       # Check out the quote situation to the end of the line
144:       process_quotes(line[last_index, line.length-1])
145: 
146:       return line
147:     end
strip_comments_meticulously(line) click to toggle source
     # File lib/Dnsruby/zone_reader.rb, line 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
152:       escaped = false
153:       quoted = false
154:       pos = 0
155:       line.each_char {|c|
156:         if (c == "\\")
157:           if (!escaped)
158:             escaped = true
159:           else
160:             escaped = false
161:           end
162:         else
163:           if (escaped)
164:             if (c >= "0" && c <= "9") # rfc 1035 5.1 \DDD
165:               pos = pos + 2
166:             end
167:             escaped = false
168:             next
169:           else
170:             if (c == "\"")
171:               if (quoted)
172:                 quoted = false
173:               else
174:                 quoted = true
175:               end
176:             else
177:               if (c == ";")
178:                 if (!quoted)
179:                   return line[0, pos]
180:                 end
181:               end
182:             end
183:           end
184:         end
185:         pos +=1
186:       }
187:       return line
188:     end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.