bind_conf.py
changeset 4 8b633782f02d
child 5 86b05c0ab5cd
equal deleted inserted replaced
3:ff98fa9b84ce 4:8b633782f02d
       
     1 """
       
     2     Configuration file output for the ISC DNS server: BIND
       
     3 """
       
     4 
       
     5 import conf
       
     6 
       
     7 class Object (conf.ConfObject) :
       
     8     """
       
     9         Our own version of ConfObject that knows how to format comments
       
    10     """
       
    11 
       
    12     def _fmt_comments (self) :
       
    13         """
       
    14             Format comments using the standard format:
       
    15                 ";" <comment>
       
    16         """
       
    17 
       
    18         for comment in self.comments :
       
    19             if comment is not None :
       
    20                 yield "; %s" % (comment, )
       
    21 
       
    22 class ZoneFile (Object, conf.File) :
       
    23     """
       
    24         A zone file containing a bunch of directives, resource records and comments
       
    25     """
       
    26 
       
    27     def __init__ (self, name, path, ttl=None, comment=None) :
       
    28         """
       
    29             @param name the name of the zonefile, for status stuff
       
    30             @param path the path to the zonefile
       
    31             @param ttl default TTL to use
       
    32             @param comment optional comments
       
    33         """
       
    34         
       
    35         Object.__init__(comment)
       
    36         conf.File.__init__(name, path)
       
    37 
       
    38         # init
       
    39         self.objects = []
       
    40 
       
    41         if ttl :
       
    42             self.add_directive(TTLDirective(ttl))
       
    43 
       
    44     def add_obj (self, obj) :
       
    45         """
       
    46             Add an Object onto the list of things to include in this zone file
       
    47         """
       
    48 
       
    49         self.objects.append(obj)
       
    50 
       
    51     # various aliases...
       
    52     add_comment = add_record = add_directive = add_obj
       
    53 
       
    54 class Comment (Object) :
       
    55     """
       
    56         A comment, is, well, a comment :)
       
    57     """
       
    58 
       
    59     def __init__ (self, comment) :
       
    60         """
       
    61             @param comment the comment string
       
    62         """
       
    63 
       
    64         Object.__init__(self, [comment])
       
    65     
       
    66     def fmt_lines (self) :
       
    67         """
       
    68             Yield a single line with the comment
       
    69         """
       
    70 
       
    71         return self._fmt_comments()
       
    72 
       
    73 class Label (object) :
       
    74     """
       
    75         A label, as used in a ResourceRecord, either as the label, or the rdata for various resource types
       
    76 
       
    77         You can also use strs, this just implements a __str__ method
       
    78     """
       
    79 
       
    80     def __init__ (self, label) :
       
    81         """
       
    82             @param label the literal label to use
       
    83         """
       
    84 
       
    85         self.label = label
       
    86 
       
    87     def __str__ (self) :
       
    88         return self.label
       
    89 
       
    90 class Origin (Label) :
       
    91     """
       
    92         A label that represents the zone's origin
       
    93     """
       
    94 
       
    95     def __init__ (self) :
       
    96         pass
       
    97 
       
    98     def __str__ (self) :
       
    99         return '@'
       
   100 
       
   101 class FQDN (Label) :
       
   102     """
       
   103         A label that represents the given external domain (i.e. this adds the . to the end that people always forget).
       
   104     """
       
   105 
       
   106     def __init__ (self, fqdn) :
       
   107         self.fqdn = fqdn
       
   108 
       
   109     def __str__ (self) :
       
   110         return "%s." % (self.fqdn, )
       
   111 
       
   112 class Interval (object) :
       
   113     """
       
   114         A time interval suitable for use in SOA records
       
   115     """
       
   116 
       
   117     def __init__ (self, s=None, m=None, h=None, d=None) :
       
   118         """
       
   119             @param s seconds
       
   120             @param m minutes
       
   121             @param h hours
       
   122             @param d days
       
   123         """
       
   124 
       
   125         self.s = s
       
   126         self.m = m
       
   127         self.h = h
       
   128         self.d = d
       
   129 
       
   130     def __str__ (self) :
       
   131         """
       
   132             If only seconds were given, just return those directly, otherwise, apply units
       
   133         """
       
   134 
       
   135         if self.s and not self.m and not self.h and not self.d :
       
   136             return str(self.s)
       
   137 
       
   138         else :
       
   139             return "%s%s%s%s" % (
       
   140                     "%ds" % self.s if self.s else '',
       
   141                     "%dm" % self.m if self.m else '',
       
   142                     "%dh" % self.h if self.h else '',
       
   143                     "%dd" % self.d if self.d else ''
       
   144                 )
       
   145 
       
   146 class ResourceRecord (Object) :
       
   147     """
       
   148         A generic resource record for a BIND zone file
       
   149     """
       
   150 
       
   151     def __init__ (self, label, type, rdata, cls='IN', ttl=None, **kwargs) :
       
   152         """
       
   153             @param label the "name" of this record, or None to referr to the previous record's label
       
   154             @param type the type as a string ('A', 'TXT', etc.)
       
   155             @param rdata the rdata, as a raw string
       
   156             @param cls the class, e.g. 'IN'
       
   157             @param ttl the time-to-live value in seconds, or None to omit it
       
   158         """
       
   159 
       
   160         super(ResourceRecord, self).__init__(**kwargs)
       
   161 
       
   162         self.label = label
       
   163         self.type = type
       
   164         self.rdata = rdata
       
   165         self.cls = cls
       
   166         self.ttl = ttl
       
   167 
       
   168     def fmt_lines (self) :
       
   169         """
       
   170             Just format the lines, eh
       
   171         """
       
   172         
       
   173         # prefix comments
       
   174         for line in self._fmt_comments() :
       
   175             yield line
       
   176 
       
   177         # then format the line
       
   178         # XXX: TTL?
       
   179         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)
       
   180 
       
   181 class SOA (ResourceRecord) :
       
   182     """
       
   183         "Identifies the start of a zone of authority", must be the first record
       
   184     """
       
   185 
       
   186     def __init__ (self, label, primary_ns, hostmaster, serial, refresh, retry, expire, minimum, **kwargs) :
       
   187         """
       
   188             @param label the "name" of the zone, usually ORIGIN
       
   189             @param primary_ns the address of the primary NS server
       
   190             @param hostmaster the mailbox of the zone's hostmaster
       
   191             @param serial the serial number of the zone as an integer
       
   192             @param refresh time interval between zone refreshes in seconds
       
   193             @param retry time interval between retrys for failed refreshes
       
   194             @param expire time interval before zone data can no longer be considered authorative
       
   195             @param minimum minimum TTL for RRs in this zone
       
   196         """
       
   197 
       
   198         super(SOA, self).__init__(label, 'SOA', "%s %s ( %s %s %s %s %s )" % (
       
   199                 primary_ns, hostmaster, serial, refresh, retry, expire, minimum
       
   200             ), **kwargs)
       
   201 
       
   202 class A (ResourceRecord) :
       
   203     """
       
   204         An IPv4 forward address
       
   205     """
       
   206 
       
   207     def __init__ (self, label, addr, **kwargs) :
       
   208         """
       
   209             @param label the "name" of the address
       
   210             @param addr the IPv4 target address
       
   211         """
       
   212 
       
   213         assert(addr.is_v4())
       
   214 
       
   215         super(A, self).__init__(label, 'A', addr, **kwargs)
       
   216 
       
   217 class AAAA (ResourceRecord) :
       
   218     """
       
   219         An IPv6 forward address
       
   220     """
       
   221 
       
   222     def __init__ (self, label, addr, **kwargs) :
       
   223         """
       
   224             @param label the "name" of the address
       
   225             @param addr the IPv6 target address
       
   226         """
       
   227         
       
   228         assert(addr.is_v6())
       
   229 
       
   230         super(AAAA, self).__init__(label, 'AAAA', addr.strCompressed(), **kwargs)
       
   231 
       
   232 class CNAME (ResourceRecord) :
       
   233     """
       
   234         A canonical-name alias
       
   235     """
       
   236 
       
   237     def __init__ (self, label, target, **kwargs) :
       
   238         """
       
   239             @param label the "name" of the alias
       
   240             @param target the alias target
       
   241         """
       
   242 
       
   243         super(CNAME, self).__init__(label, 'CNAME', target, **kwargs)
       
   244 
       
   245 class TXT (ResourceRecord) :
       
   246     """
       
   247         A human-readable information record
       
   248     """
       
   249 
       
   250     def __init__ (self, label, text, **kwargs) :
       
   251         """
       
   252             @param label the "name" of the text record
       
   253             @param text the text data, shouldn't contain any quotes...
       
   254         """
       
   255 
       
   256         super(TXT, self).__init__(label, 'TXT', '"%s"' % text, **kwargs)
       
   257 
       
   258 class MX (ResourceRecord) :
       
   259     """
       
   260         A mail-exchange definition
       
   261     """
       
   262 
       
   263     def __init__ (self, label, pref, exchange, **kwargs) :
       
   264         """
       
   265             @param label the "name" of the domain to handle mail for
       
   266             @param pref the numerical preference for this exchange
       
   267             @param exchange the domain name of the mail exchange (SMTP server)
       
   268         """
       
   269 
       
   270         super(MX, self).__init__(label, 'MX', "%d %s" % (pref, exchange), **kwargs)
       
   271 
       
   272 class NS (ResourceRecord) :
       
   273     """
       
   274         An authorative name server
       
   275     """
       
   276 
       
   277     def __init__ (self, label, nsname, **kwargs) :
       
   278         """
       
   279             @param label the "name" of the domain to have a nameserver for
       
   280             @param nsname the name of the nameserver
       
   281         """
       
   282 
       
   283         super(NS, self).__init__(label, 'NS', nsname)
       
   284 
       
   285 class PTR (ResourceRecord) :
       
   286     """
       
   287         An IPv4/IPv6 reverse address
       
   288     """
       
   289 
       
   290     def __init__ (self, addr, name, **kwargs) :
       
   291         """
       
   292             @param addr the addr.IP to map via in-addr.arpa
       
   293             @param name the name to map the address to
       
   294         """
       
   295 
       
   296         # XXX: quick hack, this gives an absolute name
       
   297         label = addr.reverseName()
       
   298         
       
   299         super(PTR, self).__init__(label, 'PTR', name)
       
   300 
       
   301 class Directive (Object) :
       
   302     """
       
   303         Special directives that can be used in zone files to control behaviour
       
   304     """
       
   305 
       
   306     def __init__ (self, name, *args, **kwargs) :
       
   307         """
       
   308             @param name the $NAME bit
       
   309             @param args optional list of space-seprated arguments, Nones are ignored
       
   310         """
       
   311 
       
   312         super(Directive, self).__init__(**kwargs)
       
   313 
       
   314         self.name = name
       
   315         self.args = [arg for arg in args if arg is not None]
       
   316 
       
   317     def fmt_lines (self) :
       
   318         # prefix comments
       
   319         for line in self._fmt_comments() :
       
   320             yield line
       
   321 
       
   322         # then format it
       
   323         yield "$%s%s" % (self.name, (' ' + ' '.join(str(arg) for arg in self.args)) if self.args else '')
       
   324 
       
   325 class OriginDirective (Directive) :
       
   326     """
       
   327         Set the origin used to resolve the zone's labels.
       
   328 
       
   329         Note that the origin label is not absolute by default - use FQDN for that
       
   330     """
       
   331 
       
   332     def __init__ (self, origin, **kwargs) :
       
   333         """
       
   334             @param origin the origin label
       
   335         """
       
   336 
       
   337         super(OriginDirective, self).__init__('ORIGIN', origin, **kwargs)
       
   338 
       
   339 class TTLDirective (Directive) :
       
   340     """
       
   341         Set the TTL used for records by default
       
   342     """
       
   343 
       
   344     def __init__ (self, ttl, **kwargs) :
       
   345         """
       
   346             @param ttl the new ttl to use
       
   347         """
       
   348 
       
   349         super(TTLDirective, self).__init__('TTL', ttl, **kwargs)
       
   350 
       
   351 class IncludeDirective (Directive) :
       
   352     """
       
   353         Include another zoen file, optionally with a different origin
       
   354     """
       
   355 
       
   356     def __init__ (self, filename, origin=None, **kwargs) :
       
   357         """
       
   358             @param filename the zone file to include
       
   359             @param origin the optional origin to process the zonefile with
       
   360         """
       
   361 
       
   362         super(IncludeDirective, self).__init__('INCLUDE', filename, origin, **kwargs)
       
   363 
       
   364 class GenerateDirective (Directive) :
       
   365     """
       
   366         Generate a bunch of numbered records using an expression for the label and rdata.
       
   367 
       
   368         At the simplest, any "$" in the expression is replaced with the value of the iterator.
       
   369     """
       
   370 
       
   371     def __init__ (self, range, lhs, type, rhs, ttl=None, cls=None, **kwargs) :
       
   372         """
       
   373             @param range (start, stop, step) tuple
       
   374             @param lhs expression to generate the label
       
   375             @param type the resource record type
       
   376             @param rhs expression to generate the rdata
       
   377         """
       
   378 
       
   379         super(GenerateDirective, self).__init__('GENERATE', '%d-%d' % range, lhs, ttl, cls, type, rhs, **kwargs)
       
   380