# = ildus/server/backend - ldap domain backend library # # Copyright (C) 2005 Paul van Tilburg # # Ildus is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free # Software Foundation; either version 2 of the License, or (at your option) # any later version. require 'ldap' module Ildus::Server::DomainBackend # = LDAP domain backend # # The domain backend class that uses an LDAP database. It uses the LDAP # record format used by the PowerDNS backend. (see: # http://www.linuxnetworks.de/pdnsldap/dnsdomain2.schema) Next to that it # uses an extra ildusOwner field to couple LDAP dNSdomain records to # users. class Ldap < Basic # Sets up the LDAP backend by switching to LDAPv3 protocol # and binding to the server. def initialize(*args) super @ldap = LDAP::Conn.new(config['host']) @ldap.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3) @ldap.simple_bind(config['user'], config['pass']) end # Updates the serial on the SOA record and calls the inherited # commit (invoking the hook). def commit update_serial super end # Returns the list (a Hash actually) of hosts for the selected user. # It looks for A, AAAA and CNAME records. def hosts entries = Hash.new { |h, k| h[k] = [[], []] } all_entries.each do |entry| assoc_dom, a_rr, aaaa_rr, cname_rr = ["associatedDomain", "aRecord", "aAAArecord", "cNAMErecord"].map { |attr| entry[attr] } host = assoc_dom.first.gsub(/\.#{config['domain']}$/, '') entries[host].first.push(*a_rr) if a_rr entries[host].first.push(*aaaa_rr) if aaaa_rr if cname_rr cname = cname_rr.first.gsub(/\.#{config['domain']}$/, '') entries[cname].last << host end end # search return entries end # def hosts # Updates the address of _host_ to _addr_ providing that _addr_ is in a # correct IPv4 or IPv6 address format. def update_host(host, addr) entry = all_entries.find do |entry| entry['associatedDomain'][0] == host + "." + config['domain'] end raise Handler::HostNotFoundError if entry.nil? if addr.ipv4? @ldap.modify(entry['dn'][0], {"aRecord" => [addr.to_s]}) elsif addr.ipv6? @ldap.modify(entry['dn'][0], {"aAAARecord" => [addr.to_s]}) else return false end true end ######### private ######### # Returns a list of all LDAP IldusRecord entries for the selected # user. def all_entries @ldap.search2(config['base'], LDAP::LDAP_SCOPE_SUBTREE, "(&(objectClass=ildusRecord)(ildusOwner=#{user}))") end # Returns the LDAP sOARecord for the configured domain. def soa_record @ldap.search2(config['base'], LDAP::LDAP_SCOPE_BASE, "(&(associatedDomain=#{config['domain'].downcase})" \ " (sOARecord=*))").first end # Updates the serial of the SOA record of the configured domain. # The serial consists of two parts, the date (yyyymmdd) and # counter (nn) part. On every update the date is set to the current # date, it will increase the counter. # # Note that the counter may overflow so that actually the day is # incremented. This is no problem unless there is an almost infinite # sequance of days with more then 100 updates/day. def update_serial record = soa_record soa = record['sOARecord'].first.split soa[2] = serial_succ(soa[2]) @ldap.modify(record['dn'][0], {"sOARecord" => [soa.join(" ")]}) end # Determines the successor of the current serial number, see # also #update_serial(). def serial_succ(serial) date, num = serial.scan(/\d{8}|\d{2}/) today = Time.now.strftime("%Y%m%d") if (date < today) date, num = today, '00' elsif (num == '99') date, num = date.succ, '00' else num = num.succ end return "%08s%02s" % [date, num] end end # class Ldap end # module Ildus::Server::DomainBackend