commit 08c496f59ae5826181673dc7628b8b1ae04a6a38
Author: Marc Dequènes (Duck) <Duck@DuckCorp.org>
Date:   Sat Nov 27 16:47:53 2010 +0100

    [evol] DNS: add better support for signed zones and serials, and partial support for slave zones

diff --git a/lib/cyborghood/services/dns.rb b/lib/cyborghood/services/dns.rb
index ea16a88..b305df3 100644
--- a/lib/cyborghood/services/dns.rb
+++ b/lib/cyborghood/services/dns.rb
@@ -19,10 +19,12 @@
 require 'digest/md5'
 require 'tempfile'
 require 'fileutils'
+require 'dnsruby'
 
 # ensure we can find the needed programs (should be handled somewhere else)
 ENV['PATH'] = (ENV['PATH'].split(":") + ["/sbin", "/usr/sbin", "/usr/local/sbin"]).uniq.join(":")
 
+
 module CyborgHood
   module Services
     module DNS
@@ -30,20 +32,26 @@ module CyborgHood
         def initialize
           @config = Config.instance
 
-          @zone_files_pattern = @config.dns.master_zone_pattern.gsub("#ZONE#", "*")
-          @zone_files_regex = Regexp.new("^" + @config.dns.master_zone_pattern.gsub("#ZONE#", "(.*)") + "$")
+          @master_zone_files_pattern = @config.dns.master_zone_pattern.gsub("#ZONE#", "*")
+          @master_zone_files_regex = Regexp.new("^" + @config.dns.master_zone_pattern.gsub("#ZONE#", "(.*)") + "$")
+          @slave_zone_files_pattern = @config.dns.slave_zone_pattern.gsub("#ZONE#", "*")
+          @slave_zone_files_regex = Regexp.new("^" + @config.dns.slave_zone_pattern.gsub("#ZONE#", "(.*)") + "$")
         end
 
-        def zones
-          Dir.glob(@zone_files_pattern).collect do |file|
-            $1 if file =~ @zone_files_regex
+        def master_zones
+          Dir.glob(@master_zone_files_pattern).collect do |file|
+            $1 if file =~ @master_zone_files_regex
           end
         end
 
-        def [](zone)
-          return unless zones.include?(zone)
+        def slave_zones
+          Dir.glob(@slave_zone_files_pattern).collect do |file|
+            $1 if file =~ @slave_zone_files_regex
+          end
+        end
 
-          Zone.new(zone)
+        def type
+          @config.dns.software
         end
 
         def check_config
@@ -72,10 +80,57 @@ module CyborgHood
           @zone = zone
 
           @config = Config.instance
+          @resolver = Dnsruby::Resolver.new
           @content = nil
           @temp_file = nil
 
-          @filename = @config.dns.master_zone_pattern.gsub("#ZONE#", @zone)
+          master_filename = @config.dns.master_zone_pattern.gsub("#ZONE#", @name)
+          slave_filename = @config.dns.slave_zone_pattern.gsub("#ZONE#", @name)
+          if File.exists? master_filename
+            @master = true
+            @filename = master_filename
+            @filename_signed = @config.dns.signed_master_zone_pattern.gsub("#ZONE#", @name) if @config.dns.signed_master_zone_pattern
+          elsif File.exists? slave_filename
+            @master = false
+            @filename = slave_filename
+          else
+            raise CyberError.new(:unrecoverable, "services/dns", "nonexistent zone '#{@name}'")
+          end
+        end
+
+        def master?
+          @master
+        end
+
+        def has_signed_zone_file?
+          master? and File.exists?(@filename_signed)
+        end
+
+        def signed?
+          # check if really signed in DNS
+          not @resolver.query(@name, 'DNSKEY').answer.empty?
+        end
+
+        def serial_in_dns
+          soa = @resolver.query(@name, 'SOA').answer{|rr| rr.type == 'SOA'}.first
+          soa ? soa.serial : nil
+        end
+        alias_method :serial, :serial_in_dns
+
+        def serial_in_zone_file
+          reader = Dnsruby::ZoneReader.new(@name)
+          zone = reader.process_file(@filename)
+          soa = zone.select{|rr| rr.name.to_s == zone_name and rr.type == 'SOA' }.first
+          soa ? soa.serial : nil
+        end
+
+        def serial_in_signed_zone_file
+          return unless has_signed_zone_file?
+
+          reader = Dnsruby::ZoneReader.new(@name)
+          zone = reader.process_file(@filename_signed)
+          soa = zone.select{|rr| rr.name.to_s == zone_name and rr.type == 'SOA' }.first
+          soa ? soa.serial : nil
         end
 
         def content
@@ -111,15 +166,6 @@ module CyborgHood
           check_zone_file('full', force_real = false)
         end
 
-        def serial(force_real = false)
-          r = check_zone_file('none', force_real = false)
-          if r.ok
-            r.serial
-          else
-            raise CyberError.new(:unrecoverable, "services/dns", "zone serial for '#{@zone}' could not be found (#{r.errors.first})")
-          end
-        end
-
         def import_from_file(new_zone_filename)
           read_zone(new_zone_filename)
         end
