Revision 157c68c9
Added by Marc Dequènes almost 16 years ago
- ID 157c68c922e40f82f4bf567b4139c48cb7eda82a
lib/cyborghood/services/dns.rb | ||
---|---|---|
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
|
||
file = @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 file == @config.dns.master_zone_pattern
|
||
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
|
||
File.read(file)
|
||
@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
|
postman | ||
---|---|---|
$: << "./lib"
|
||
|
||
#require 'socket'
|
||
#require 'fileutils'
|
||
#require 'tempfile'
|
||
require 'tempfile'
|
||
require 'shellwords'
|
||
require 'cyborghood/imap'
|
||
require 'cyborghood/mail'
|
||
... | ... | |
ok = false
|
||
end
|
||
when "SET"
|
||
result.message = "Command not yet implemented."
|
||
case cmdline.shift.upcase
|
||
when "ZONE"
|
||
zone = cmdline.shift.downcase
|
||
dom = CyborgHood::DnsDomain.new(zone)
|
||
logger.info "User requesting zone content for '#{zone}'"
|
||
if dom.hosted?
|
||
if dom.managed_by? user
|
||
logger.info "User is manager of the zone"
|
||
srv_dns = CyborgHood::Services::DNS.new(zone)
|
||
|
||
content_ref = cmdline.shift.downcase
|
||
if content_ref =~ /^@(\d+)$/
|
||
part_ref = $1.to_i
|
||
if (1..refs.size).include? part_ref
|
||
part = refs[part_ref]
|
||
if part.content_type == "text/plain"
|
||
f = Tempfile.new(zone)
|
||
f.write(part.body)
|
||
f.close
|
||
logger.debug "Created temporary zone file '#{f.path}'"
|
||
|
||
srv_dns = CyborgHood::Services::DNS.new(zone)
|
||
current_serial = srv_dns.serial
|
||
logger.debug "Current serial: #{current_serial}"
|
||
|
||
result = srv_dns.check_zone_file(f.path)
|
||
if result.ok
|
||
logger.debug "New serial: #{result.serial}"
|
||
# allow new serial or missing serial (to allow creating a new zone or replacing a broken zone)
|
||
if current_serial.nil? or result.serial > current_serial
|
||
begin
|
||
srv_dns.write_zone_from_file(f.path)
|
||
logger.info "zone changed"
|
||
rescue
|
||
logger.debug "Writing zone file failed"
|
||
raise
|
||
ensure
|
||
f.close!
|
||
end
|
||
else
|
||
logger.info "zone serial is not superior to current serial"
|
||
result.message = "Zone serial is not superior to current serial."
|
||
f.close!
|
||
return result
|
||
end
|
||
else
|
||
logger.info "new zone file is invalid"
|
||
result.message = "invalid zone data"
|
||
f.close!
|
||
return result
|
||
end
|
||
|
||
f.close!
|
||
else
|
||
logger.info "attachment for zone is not plain text"
|
||
result.message = "Attachment has wrong type."
|
||
return result
|
||
end
|
||
else
|
||
logger.info "attachement for zone not found"
|
||
result.message = "Attachment number not found."
|
||
return result
|
||
end
|
||
else
|
||
ok = false
|
||
end
|
||
else
|
||
logger.info "User is not allowed to manage the zone"
|
||
result.message = "You are not allowed to manage this zone."
|
||
end
|
||
else
|
||
logger.info "Zone not hosted"
|
||
result.message "This zone is not hosted here."
|
||
end
|
||
else
|
||
ok = false
|
||
end
|
||
else
|
||
ok = false
|
||
end
|
Also available in: Unified diff
[evol] implement changing a zone