diff -r ff98fa9b84ce -r 8b633782f02d bind_conf.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bind_conf.py Thu Apr 02 22:52:26 2009 +0300 @@ -0,0 +1,380 @@ +""" + Configuration file output for the ISC DNS server: BIND +""" + +import conf + +class Object (conf.ConfObject) : + """ + Our own version of ConfObject that knows how to format comments + """ + + def _fmt_comments (self) : + """ + Format comments using the standard format: + ";" + """ + + for comment in self.comments : + if comment is not None : + yield "; %s" % (comment, ) + +class ZoneFile (Object, conf.File) : + """ + A zone file containing a bunch of directives, resource records and comments + """ + + def __init__ (self, name, path, ttl=None, comment=None) : + """ + @param name the name of the zonefile, for status stuff + @param path the path to the zonefile + @param ttl default TTL to use + @param comment optional comments + """ + + Object.__init__(comment) + conf.File.__init__(name, path) + + # init + self.objects = [] + + if ttl : + self.add_directive(TTLDirective(ttl)) + + def add_obj (self, obj) : + """ + Add an Object onto the list of things to include in this zone file + """ + + self.objects.append(obj) + + # various aliases... + add_comment = add_record = add_directive = add_obj + +class Comment (Object) : + """ + A comment, is, well, a comment :) + """ + + def __init__ (self, comment) : + """ + @param comment the comment string + """ + + Object.__init__(self, [comment]) + + def fmt_lines (self) : + """ + Yield a single line with the comment + """ + + return self._fmt_comments() + +class Label (object) : + """ + A label, as used in a ResourceRecord, either as the label, or the rdata for various resource types + + You can also use strs, this just implements a __str__ method + """ + + def __init__ (self, label) : + """ + @param label the literal label to use + """ + + self.label = label + + def __str__ (self) : + return self.label + +class Origin (Label) : + """ + A label that represents the zone's origin + """ + + def __init__ (self) : + pass + + def __str__ (self) : + return '@' + +class FQDN (Label) : + """ + A label that represents the given external domain (i.e. this adds the . to the end that people always forget). + """ + + def __init__ (self, fqdn) : + self.fqdn = fqdn + + def __str__ (self) : + return "%s." % (self.fqdn, ) + +class Interval (object) : + """ + A time interval suitable for use in SOA records + """ + + def __init__ (self, s=None, m=None, h=None, d=None) : + """ + @param s seconds + @param m minutes + @param h hours + @param d days + """ + + self.s = s + self.m = m + self.h = h + self.d = d + + def __str__ (self) : + """ + If only seconds were given, just return those directly, otherwise, apply units + """ + + if self.s and not self.m and not self.h and not self.d : + return str(self.s) + + else : + return "%s%s%s%s" % ( + "%ds" % self.s if self.s else '', + "%dm" % self.m if self.m else '', + "%dh" % self.h if self.h else '', + "%dd" % self.d if self.d else '' + ) + +class ResourceRecord (Object) : + """ + A generic resource record for a BIND zone file + """ + + def __init__ (self, label, type, rdata, cls='IN', ttl=None, **kwargs) : + """ + @param label the "name" of this record, or None to referr to the previous record's label + @param type the type as a string ('A', 'TXT', etc.) + @param rdata the rdata, as a raw string + @param cls the class, e.g. 'IN' + @param ttl the time-to-live value in seconds, or None to omit it + """ + + super(ResourceRecord, self).__init__(**kwargs) + + self.label = label + self.type = type + self.rdata = rdata + self.cls = cls + self.ttl = ttl + + def fmt_lines (self) : + """ + Just format the lines, eh + """ + + # prefix comments + for line in self._fmt_comments() : + yield line + + # then format the line + # XXX: TTL? + yield "%30s %4s%4s %8s %s" % (self.label if self.label is not None else '', str(self.ttl) if self.ttl else '', self.cls, self.type, self.rdata) + +class SOA (ResourceRecord) : + """ + "Identifies the start of a zone of authority", must be the first record + """ + + def __init__ (self, label, primary_ns, hostmaster, serial, refresh, retry, expire, minimum, **kwargs) : + """ + @param label the "name" of the zone, usually ORIGIN + @param primary_ns the address of the primary NS server + @param hostmaster the mailbox of the zone's hostmaster + @param serial the serial number of the zone as an integer + @param refresh time interval between zone refreshes in seconds + @param retry time interval between retrys for failed refreshes + @param expire time interval before zone data can no longer be considered authorative + @param minimum minimum TTL for RRs in this zone + """ + + super(SOA, self).__init__(label, 'SOA', "%s %s ( %s %s %s %s %s )" % ( + primary_ns, hostmaster, serial, refresh, retry, expire, minimum + ), **kwargs) + +class A (ResourceRecord) : + """ + An IPv4 forward address + """ + + def __init__ (self, label, addr, **kwargs) : + """ + @param label the "name" of the address + @param addr the IPv4 target address + """ + + assert(addr.is_v4()) + + super(A, self).__init__(label, 'A', addr, **kwargs) + +class AAAA (ResourceRecord) : + """ + An IPv6 forward address + """ + + def __init__ (self, label, addr, **kwargs) : + """ + @param label the "name" of the address + @param addr the IPv6 target address + """ + + assert(addr.is_v6()) + + super(AAAA, self).__init__(label, 'AAAA', addr.strCompressed(), **kwargs) + +class CNAME (ResourceRecord) : + """ + A canonical-name alias + """ + + def __init__ (self, label, target, **kwargs) : + """ + @param label the "name" of the alias + @param target the alias target + """ + + super(CNAME, self).__init__(label, 'CNAME', target, **kwargs) + +class TXT (ResourceRecord) : + """ + A human-readable information record + """ + + def __init__ (self, label, text, **kwargs) : + """ + @param label the "name" of the text record + @param text the text data, shouldn't contain any quotes... + """ + + super(TXT, self).__init__(label, 'TXT', '"%s"' % text, **kwargs) + +class MX (ResourceRecord) : + """ + A mail-exchange definition + """ + + def __init__ (self, label, pref, exchange, **kwargs) : + """ + @param label the "name" of the domain to handle mail for + @param pref the numerical preference for this exchange + @param exchange the domain name of the mail exchange (SMTP server) + """ + + super(MX, self).__init__(label, 'MX', "%d %s" % (pref, exchange), **kwargs) + +class NS (ResourceRecord) : + """ + An authorative name server + """ + + def __init__ (self, label, nsname, **kwargs) : + """ + @param label the "name" of the domain to have a nameserver for + @param nsname the name of the nameserver + """ + + super(NS, self).__init__(label, 'NS', nsname) + +class PTR (ResourceRecord) : + """ + An IPv4/IPv6 reverse address + """ + + def __init__ (self, addr, name, **kwargs) : + """ + @param addr the addr.IP to map via in-addr.arpa + @param name the name to map the address to + """ + + # XXX: quick hack, this gives an absolute name + label = addr.reverseName() + + super(PTR, self).__init__(label, 'PTR', name) + +class Directive (Object) : + """ + Special directives that can be used in zone files to control behaviour + """ + + def __init__ (self, name, *args, **kwargs) : + """ + @param name the $NAME bit + @param args optional list of space-seprated arguments, Nones are ignored + """ + + super(Directive, self).__init__(**kwargs) + + self.name = name + self.args = [arg for arg in args if arg is not None] + + def fmt_lines (self) : + # prefix comments + for line in self._fmt_comments() : + yield line + + # then format it + yield "$%s%s" % (self.name, (' ' + ' '.join(str(arg) for arg in self.args)) if self.args else '') + +class OriginDirective (Directive) : + """ + Set the origin used to resolve the zone's labels. + + Note that the origin label is not absolute by default - use FQDN for that + """ + + def __init__ (self, origin, **kwargs) : + """ + @param origin the origin label + """ + + super(OriginDirective, self).__init__('ORIGIN', origin, **kwargs) + +class TTLDirective (Directive) : + """ + Set the TTL used for records by default + """ + + def __init__ (self, ttl, **kwargs) : + """ + @param ttl the new ttl to use + """ + + super(TTLDirective, self).__init__('TTL', ttl, **kwargs) + +class IncludeDirective (Directive) : + """ + Include another zoen file, optionally with a different origin + """ + + def __init__ (self, filename, origin=None, **kwargs) : + """ + @param filename the zone file to include + @param origin the optional origin to process the zonefile with + """ + + super(IncludeDirective, self).__init__('INCLUDE', filename, origin, **kwargs) + +class GenerateDirective (Directive) : + """ + Generate a bunch of numbered records using an expression for the label and rdata. + + At the simplest, any "$" in the expression is replaced with the value of the iterator. + """ + + def __init__ (self, range, lhs, type, rhs, ttl=None, cls=None, **kwargs) : + """ + @param range (start, stop, step) tuple + @param lhs expression to generate the label + @param type the resource record type + @param rhs expression to generate the rdata + """ + + super(GenerateDirective, self).__init__('GENERATE', '%d-%d' % range, lhs, ttl, cls, type, rhs, **kwargs) +