"""
Configuration file output for the ISC DNS server: BIND
"""
from .. import file
class Item (file.Item) :
"""
Item that knows how to format comments
"""
COMMENT_PREFIX = ';'
class Zone (Item, file.Contents) :
"""
A zone containing a bunch of directives, resource records and comments
"""
def __init__ (self, 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
"""
Item.__init__(self, [comment])
file.Contents.__init__(self)
if ttl :
self.add(TTLDirective(ttl))
class Comment (Item) :
"""
A comment, is, well, a comment :)
"""
def __init__ (self, comment) :
"""
@param comment the comment string
"""
Item.__init__(self, [comment])
# only need to iterate over our comments
iter_lines = Item.iter_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 (Item) :
"""
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 iter_lines (self) :
"""
Just format the lines, eh
"""
# prefix comments
for line in self.iter_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...
"""
# XXX: escaping?
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 (Item) :
"""
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 iter_lines (self) :
# prefix comments
for line in self.iter_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)