def zf_string(self, zone, subzone=None): """String representation for zonefile export.""" if subzone: subzone = idna_encode(qualify(subzone, zone)) data = { 'subzone': clear_none(subzone), 'ttl': clear_none(self.ttl), 'record_type': 'NS', 'record_data': idna_encode(qualify(self.name, zone)) } return '{subzone:24} {ttl:5} IN {record_type:6} {record_data}\n'.format_map( data)
def zf_string(self, zone): """String representation for zonefile export.""" data = { 'name': idna_encode(qualify(self.name, zone)), 'ttl': clear_none(self.ttl), 'record_type': 'SRV', 'priority': self.priority, 'weight': self.weight, 'port': self.port, 'target': idna_encode(qualify(self.target, zone)) } return '{name:24} {ttl:5} IN {record_type:6} {priority} {weight} {port} {target}\n'.format_map( data)
def host_data(self, host): data = "" first = True name_idna = idna_encode(qualify(host.name, self.zone.name)) ttl = clear_none(host.ttl) for values, func in ( (self.ipaddresses, self.ip_zf_string), (self.mxs, self.mx_zf_string), (self.txts, self.txt_zf_string), (self.naptrs, self.naptr_zf_string), ): if host.name in values: for i in values[host.name]: if first: first = False name = name_idna else: name = "" data += func(name, ttl, *i) # XXX: add caching for this one, if we populate it.. if host.hinfo is not None: data += host.hinfo.zf_string if host.loc: data += host.loc_string(self.zone.name) # For entries where the host is the resource record if host.name in self.host_cnames: for alias, ttl in self.host_cnames[host.name]: data += self.cname_zf_string(alias, ttl, name_idna) return data
def get_glue(self, ns): """Returns glue for a nameserver. If already used return blank""" if ns in self.glue_done: return "" else: self.glue_done.add(ns) if not ns.endswith("." + self.zone.name): return "" try: host = Host.objects.get(name=ns) except Host.DoesNotExist: #XXX: signal hostmaster? return f"OPS: missing glue for {ns}\n" if not host.ipaddresses.exists(): #XXX: signal hostmaster? return f"OPS: no ipaddress for name server {ns}\n" # self's name servers do not need glue, as they will come later # in the zonefile. if host.zone == self.zone: return "" data = "" name_idna = idna_encode(qualify(host.name, self.zone.name)) ttl = clear_none(host.ttl) for ip in host.ipaddresses.all(): data += self.ip_zf_string(name_idna, ttl, ipaddress.ip_address(ip.ipaddress)) return data
def host_data(self, host): data = "" idna_name = idna_encode(qualify(host.name, self.zone.name)) name = f'{idna_name:24}' ttl = prep_ttl(host.ttl) for values, func in ((self.ipaddresses, self.ip_zf_string), (self.mxs, self.mx_zf_string), (self.txts, self.txt_zf_string), (self.sshfps, self.sshfp_zf_string), (self.naptrs, self.naptr_zf_string), ): if host.name in values: for i in values[host.name]: data += func(name, ttl, *i) if data: name = f'{" ":24}' # Values only in use once by each host for values, func in ((self.hinfos, self.hinfo_zf_string), (self.locs, self.loc_zf_string) ): if host.name in values: data += func(name, ttl, *values[host.name]) if data: name = f'{" ":24}' # For entries where the host is the resource record for values, func in ((self.host_cnames, self.cname_zf_string), (self.srvs, self.srv_zf_string), ): if host.name in values: for i in values[host.name]: data += func(*i, idna_name) return data
def loc_string(self, zone): """String representation for zonefile export.""" data = { 'name': idna_encode(qualify(self.name, zone)), 'record_type': 'LOC', 'record_data': self.loc } return '{name:30} IN {record_type:6} {record_data}\n'.format_map(data)
def mx_zf_string(self, name, ttl, priority, mx): data = { 'name': name, 'ttl': ttl, 'record_type': "MX", 'priority': priority, 'mx': idna_encode(qualify(mx, self.zone.name)) } return '{name} {ttl} IN {record_type} {priority:6} {mx}\n'.format_map(data)
def cname_zf_string(self, alias, ttl, target): """String representation for zonefile export.""" data = { 'alias': idna_encode(qualify(alias, self.zone.name)), 'ttl': prep_ttl(ttl), 'record_type': 'CNAME ', 'record_data': target, } return '{alias:24} {ttl} IN {record_type} {record_data}\n'.format_map(data)
def generate(self): zone = self.zone self.cache_hostdata() # Print info about Zone and its nameservers data = zone.zf_string data += ';\n; Name servers\n;\n' for ns in zone.nameservers.all(): data += ns.zf_string(zone.name) data += self.get_delegations() data += self.get_subdomains() try: root = Host.objects.get(name=zone.name) root_data = self.host_data(root) if root_data: data += ";\n" data += "@" + root_data data += ";\n" except Host.DoesNotExist: pass # Print info about hosts and their corresponding data hosts = Host.objects.filter(zone=zone.id).order_by('name') hosts = hosts.exclude(name=zone.name) if hosts: data += ';\n; Host addresses\n;\n' for host in hosts: data += self.host_data(host) # Print misc entries srvs = Srv.objects.filter(zone=zone.id).exclude(host__zone=zone.id) if srvs: data += ';\n; Services pointing out of the zone\n;\n' for i in srvs.values_list('name', 'ttl', 'priority', 'weight', 'port', 'host__name'): host = idna_encode(qualify(i[-1], self.zone.name)) data += self.srv_zf_string(*i[:-1], host) cnames = Cname.objects.filter(zone=zone.id).exclude(host__zone=zone.id) if cnames: data += ';\n; Cnames pointing out of the zone\n;\n' for i in cnames.values_list('name', 'ttl', 'host__name'): host = idna_encode(qualify(i[-1], self.zone.name)) data += self.cname_zf_string(*i[:-1], host) return data
def srv_zf_string(self, name, ttl, priority, weight, port, target): """String representation for zonefile export.""" data = { 'name': idna_encode(qualify(name, self.zone.name)), 'ttl': prep_ttl(ttl), 'record_type': 'SRV ', 'priority': priority, 'weight': weight, 'port': port, 'target': target, } return '{name:24} {ttl} IN {record_type} {priority} {weight} {port} {target}\n'.format_map(data)
def zf_string(self): """String representation for zonefile export.""" data = { 'origin': idna_encode(qualify(self.name, self.name, shortform=False)), 'ttl': self.ttl, 'name': '@', 'record_type': 'SOA', 'mname': idna_encode(qualify(self.primary_ns, self.name, shortform=False)), 'rname': idna_encode(encode_mail(self.email)), 'serial': self.serialno, 'refresh': self.refresh, 'retry': self.retry, 'expire': self.expire, 'zupdated_at': timezone.localtime(self.updated_at), 'supdated_at': timezone.localtime(self.serialno_updated_at) } zf = """$ORIGIN {origin} $TTL {ttl} {name:30} IN {record_type:6} {mname} {rname} ( {serial} ; Serialnumber {refresh} ; Refresh {retry} ; Retry {expire} ; Expire {ttl} ) ; Negative Cache ; zone.updated_at: {zupdated_at} ; zone.serialno_updated_at: {supdated_at} """.format_map(data) return zf
def generate(self): zone = self.zone data = zone.zf_string data += ';\n; Name servers\n;\n' for ns in zone.nameservers.all(): data += ns.zf_string(zone.name) data += self.get_delegations() _prev_net = 'z' for ip, ttl, hostname in zone.get_ipaddresses(): rev = ip.reverse_pointer # Add $ORIGIN between every new /64 found if not rev.endswith(_prev_net): _prev_net = rev[32:] data += "$ORIGIN {}.\n".format(_prev_net) data += "{} {}\tPTR\t{}.\n".format(rev[:31], ttl, idna_encode(hostname)) return data
def naptr_zf_string(self, name, ttl, order, preference, flag, service, regex, replacement): """String representation for zonefile export.""" if flag in ('a', 's'): replacement = idna_encode(qualify(replacement, self.zone.name)) data = { 'name': name, 'ttl': ttl, 'record_type': 'NAPTR ', 'order': order, 'preference': preference, 'flag': flag, 'service': service, 'regex': regex, 'replacement': replacement, } return '{name} {ttl} IN {record_type} {order} {preference} ' \ '\"{flag}\" \"{service}\" \"{regex}\" {replacement}\n'.format_map(data)