|
require 'fileutils'
|
|
|
|
module CyborgHood
|
|
module Services
|
|
class DNS
|
|
attr_reader :zone
|
|
attr_accessor :content
|
|
|
|
def initialize(zone)
|
|
@zone = zone
|
|
|
|
@config = Config.instance
|
|
@content = nil
|
|
|
|
# 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(":")
|
|
end
|
|
|
|
def read_zone
|
|
filename = @config.dns.master_zone_pattern.gsub("#ZONE#", @zone)
|
|
# TODO: should be checked at startup time
|
|
raise CyberError.new(:unrecoverable, "services/dns", "erroneous configuration: pattern is constant") if filename == @config.dns.master_zone_pattern
|
|
|
|
begin
|
|
@content = File.read(filename)
|
|
rescue
|
|
raise CyberError.new(:unrecoverable, "services/dns", "zone '#{@zone}' cannot be read (nonexistent or lack of permission)")
|
|
end
|
|
end
|
|
|
|
def serial
|
|
filename = @config.dns.master_zone_pattern.gsub("#ZONE#", @zone)
|
|
# TODO: should be checked at startup time
|
|
raise CyberError.new(:unrecoverable, "services/dns", "erroneous configuration: pattern is constant") if filename == @config.dns.master_zone_pattern
|
|
|
|
case @config.dns.nameserver || :bind
|
|
when :bind
|
|
output = []
|
|
begin
|
|
IO.popen("named-checkzone -i none '#{@zone}' #{filename}") do |fp|
|
|
output << fp.gets.chomp! until fp.eof?
|
|
end
|
|
rescue
|
|
raise CyberError.new(:unrecoverable, "services/dns", "zone serial for '#{@zone}' could not be found")
|
|
end
|
|
|
|
if $? == 0
|
|
serial = nil
|
|
output.each do |l|
|
|
if l =~ /: loaded serial (\d+)$/
|
|
serial = $1
|
|
break
|
|
end
|
|
end
|
|
|
|
if serial
|
|
serial
|
|
else
|
|
raise CyberError.new(:unrecoverable, "services/dns", "no serial returned")
|
|
end
|
|
else
|
|
nil
|
|
end
|
|
else
|
|
# TODO: should be checked at startup time
|
|
raise CyberError.new(:unrecoverable, "services/dns", "erroneous configuration: unknown nameserver")
|
|
end
|
|
end
|
|
|
|
def check_zone_file(filename)
|
|
case @config.dns.nameserver || :bind
|
|
when :bind
|
|
output = []
|
|
begin
|
|
IO.popen("named-checkzone '#{@zone}' #{filename}") do |fp|
|
|
output << fp.gets.chomp! until fp.eof?
|
|
end
|
|
rescue
|
|
raise CyberError.new(:unrecoverable, "services/dns", "zone '#{@zone}' could not be checked")
|
|
end
|
|
|
|
if $? == 0
|
|
serial = nil
|
|
messages = []
|
|
output.each do |l|
|
|
next if l == "OK"
|
|
if l =~ /: loaded serial (\d+)$/
|
|
serial = $1
|
|
next
|
|
end
|
|
messages << l
|
|
end
|
|
|
|
if serial
|
|
return {:ok => true, :serial => serial, :warnings => messages}.to_ostruct
|
|
else
|
|
raise CyberError.new(:unrecoverable, "services/dns", "zone validated but no serial returned")
|
|
end
|
|
else
|
|
return {:ok => false, :errors => messages}.to_ostruct
|
|
end
|
|
else
|
|
# TODO: should be checked at startup time
|
|
raise CyberError.new(:unrecoverable, "services/dns", "erroneous configuration: unknown nameserver")
|
|
end
|
|
end
|
|
|
|
def write_zone_from_file(new_zone_filename)
|
|
filename = @config.dns.master_zone_pattern.gsub("#ZONE#", @zone)
|
|
# TODO: should be checked at startup time
|
|
raise CyberError.new(:unrecoverable, "services/dns", "erroneous configuration: pattern is constant") if filename == @config.dns.master_zone_pattern
|
|
|
|
FileUtils.cp(new_zone_filename, filename)
|
|
end
|
|
end
|
|
end
|
|
end
|