def validate_name(self, value): possibles = [] for part in value.split(".")[:0:-1]: suffix = "" if len(possibles) == 0 else ("." + possibles[-1]) possibles.append(part + suffix) try: zone = DNSZoneSerializer.filter(lambda zone: r.expr(possibles).contains(zone['name']), reql=True).order_by(r.desc(r.row['name'].count())).nth(0).run(self.conn) except r.errors.ReqlNonExistenceError: raise serializers.ValidationError("no zone matching %s could be found" % value) if 'request' in self.context and not self.context['request'].user.is_superuser: user_groups = set(self.context['request'].user.groups.all().values_list('name', flat=True)) if self.instance is not None and len(user_groups.intersection(set( self.instance.get('permissions', {}).get('write', []) ))) > 0: pass elif len(user_groups.intersection(set( zone.get('permissions', {}).get('create', []) + zone.get('permissions', {}).get('write', []) ))) == 0: raise serializers.ValidationError("you do not have permission to create names in %s" % zone['name']) try: ip_address = IPAddressSerializer.get(name=value) if self.instance is None or ip_address['id'] != self.instance['id']: raise serializers.ValidationError("%r is already in use by %s" % (value, ip_address['ip'])) except RethinkObjectNotFound: pass return value
def get_records_2(mongo_db, zone, no_parse=False, app=None, debug=False): doc = mongo_db.dns_servers.find_one({'type': 'Master'}) data = { 'request': 'zone.get', 'zone': zone.get('zone'), 'view': zone.get('view') } # z = dns.zone.from_xfr(dns.query.xfr(doc['ip'], zone)) res = execute_action(mongo_db, data, only_master=True, debug=debug) print(res) soa = res['data'][0] records = res['data'][1] return soa, records
def update_zonefile(path, origin_records, zone_name, records): try: zone = dns.zone.from_text(open(path)) origin_node = zone.get("@") soa_rdataset = origin_node.get_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA) previous_serial = soa_rdataset.items[0].serial except: traceback.print_exc() previous_serial = 0 new_serial = todays_serial = int(datetime.datetime.today().strftime("%Y%m%d00")) if os.path.exists(path): new_serial = max(previous_serial + 1, todays_serial) new_serial_str = str(new_serial) assert len(new_serial_str) == len("2000123100") vars = { "dns_zone": zone_name, "dns_contact": os.environ["DNS_CONTACT"], "dns_serial": new_serial_str, "dns_refresh_time": os.environ["DNS_REFRESH_TIME"], "dns_retry_time": os.environ["DNS_RETRY_TIME"], "dns_expire_time": os.environ["DNS_EXPIRE_TIME"], "dns_negative_cache_time": os.environ["DNS_NEGATIVE_CACHE_TIME"], "dns_servers": [s.strip() for s in os.environ["DNS_SERVERS"].split(",")], "dns_source": "netbox", "origin_records": origin_records, "records": records, } env = jinja2.Environment(loader=jinja2.FileSystemLoader("."), undefined=jinja2.StrictUndefined) text = env.get_template("zonefile.j2").render(vars) with tempfile.NamedTemporaryFile(dir=os.path.dirname(path), delete=False) as temp_file: temp_file.write(env.get_template("zonefile.j2").render(vars).encode("utf-8")) os.replace(temp_file.name, path) os.chmod(path, 420) # dec 420 == oct 644
def zeroconf_to_zone(target_zone='example.com', target_ns='localhost', zeroconf_results={}, locmap={}, priomap={}, ttl=1800): import dns.name import dns.reversename import dns.resolver import dns.query from dns.exception import DNSException, Timeout from dns.resolver import NoAnswer, NXDOMAIN import dns.zone import dns.node import dns.rdataset import dns.rdata #from dns.rdatatype import * import dns.rdatatype import dns.rdataclass if not isinstance(locmap, dict): raise TypeError if not isinstance(priomap, dict): raise TypeError if target_zone == 'example.com': zone = """@ 86400 IN SOA {ns}. administrator.example.com. 1970000000 \ 28800 7200 604800 1800 @ 86400 IN NS {ns}.""".format(ns=target_ns) else: zone = dns.zone.from_xfr(dns.query.xfr(target_ns, target_zone)) zone = zone.get('@').to_text(zone.origin) zone = dns.zone.from_text(zone, origin=target_zone) # ttl = ttl if not ttl == None else \ # zone.get_rdataset('@', dns.rdatatype.SOA).ttl reverse_resolved = {} for key in zeroconf_results: inst_name, inst_type, inst_domain, inst_name_orig = key inst_subtypes = zeroconf_results[key]['subtypes'] \ if 'subtypes' in zeroconf_results[key] else [] # create service type and subtype nodes (empty nodes deleted at the # end) type_node = zone.find_node(dns.name.from_text(inst_type, origin=zone.origin), create=True) subtype_nodes = [ zone.find_node(dns.name.from_text(subtype + '._sub.' + inst_type, origin=zone.origin), create=True) for subtype in inst_subtypes ] # <Instance> must be a single DNS label, any dots should be escaped # before concatenating all portions of a Service Instance Name, # according to DNS-SD (RFC6763). # A workaround is necessary for buggy software that does not adhere to # the rules: inst_name = re.sub(r'(?<!\\)\.', r'\.', inst_name) inst_fullname = dns.name.from_text("%s.%s" % (inst_name, inst_type), origin=zone.origin) inst_addr = zeroconf_results[key]['address'] if inst_addr not in reverse_resolved: try: reverse_resolved[inst_addr] = dns.resolver.query( dns.reversename.from_address(inst_addr), dns.rdatatype.PTR) except DNSException, e: reverse_resolved[inst_addr] = None continue # inst_hostname_rev_rr = dns.resolver.query(dns.reversename. # from_address(inst_addr), # dns.rdatatype.PTR) inst_hostname_rev_rr = reverse_resolved[inst_addr] \ if reverse_resolved[inst_addr] is not None else [] zeroconf_results[key]['hostname_rev'] = [ i.to_text(relativize=False) for i in inst_hostname_rev_rr ] if not zeroconf_results[key]['hostname_rev']: continue node_ptr_rdata = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.PTR, inst_fullname.to_text()) # fill service type and subtype nodes with PTR rdata type_node.find_rdataset(dns.rdataclass.IN, dns.rdatatype.PTR, create=True).add(node_ptr_rdata, ttl=ttl) for subtype_node in subtype_nodes: subtype_node.find_rdataset(dns.rdataclass.IN, dns.rdatatype.PTR, create=True).add(node_ptr_rdata, ttl=ttl) # create instance node inst_node = zone.find_node(inst_fullname, create=True) # avahi-browse returns a single SRV and TXT record so we should # try to resolve again qname = dns.name.from_text("%s.%s" % (inst_name_orig, inst_type), origin=dns.name.from_text(inst_domain)) instresolver = dns.resolver.Resolver() mdns_addr = '224.0.0.251' for l, rdt in (('srv', dns.rdatatype.SRV), ('txt', dns.rdatatype.TXT)): if inst_domain == 'local': # mDNS: # * try a multicast query first: # - with unicast-response (QU) bit set # - legacy per RFC6762 sec. 6.7 as we wont set # source_port=5353 # * fallback to direct unicast query (RFC6762 sec. 5.5) # caveat: # unicast responses must be less than 512 bytes # or else we will not get an answer (no EDNS0) a = mudns_query(qname, rdt, qwhere=mdns_addr, qport=5353, qtimeout=2, qu=True) #print "mudns returned %s" % str(a) if a is None: a = mudns_query(qname, rdt, qwhere=inst_addr, qport=5353, qtimeout=2, res=instresolver) else: a = mudns_query(qname, rdt, qtimeout=4) if isinstance(a, dns.resolver.Answer): zeroconf_results[key][l] = [rd.to_text() for rd in a[::]] else: if l == 'srv': zeroconf_results[key][l] = [ '0 0 %s %s' % (zeroconf_results[key]['port'], zeroconf_results[key]['hostname']) ] elif l == 'txt': # reverse the order of fields as returned by avahi txt_rec_rev = re.split('(?<=")\s+(?=")', zeroconf_results[key][l])[::-1] zeroconf_results[key][l] = [' '.join(txt_rec_rev)] #print "" #continue # replace hostname.local or whatever avahi returns with # reverse-resolved fqdn for h in zeroconf_results[key]['hostname_rev']: zeroconf_results[key]['srv'] = [ re.sub(r'%s\.?' % zeroconf_results[key]['hostname'], r'%s' % h, r) for r in zeroconf_results[key]['srv'] ] zeroconf_results[key]['txt'] = [ re.sub(r'%s\.?' % zeroconf_results[key]['hostname'], r'%s' % h.rstrip('.'), r) for r in zeroconf_results[key]['txt'] ] # txt record mangling for (i, txt_rec) in enumerate(zeroconf_results[key]['txt']): # Bonjour Printing mangling # discard ephemeral fields: printer-state txt_rec = txt_field_mangle(txt_rec, 'printer-state', None) zeroconf_results[key]['txt'][i] = txt_rec # note field mangling if inst_name in locmap: txt_rec = txt_field_mangle(txt_rec, 'note', locmap[inst_name]) zeroconf_results[key]['txt'][i] = txt_rec # priority field mangling if (inst_name, inst_type) in priomap: newprio = priomap[(inst_name, inst_type)] elif inst_name in priomap: newprio = priomap[inst_name] elif inst_type in priomap: newprio = priomap[inst_type] else: newprio = None if newprio is not None: prio = txt_field_mangle(txt_rec, 'priority') if prio is not None: prio = int(prio) if prio == 0: continue else: # Bonjour Printing spec v1.2 sec. 9.2.5 # if not specified, priority defaults to 50 prio = 50 # keep the modulo 10 part of original priority prio = prio % 10 txt_rec = txt_field_mangle(txt_rec, 'priority', newprio + prio) zeroconf_results[key]['txt'][i] = txt_rec # fill instance node with SRV and TXT rdata for rec_type in ('srv', 'txt'): for r in zeroconf_results[key][rec_type]: inst_node.find_rdataset(dns.rdataclass.IN, dns.rdatatype.from_text(rec_type), create=True).add(dns.rdata.from_text( dns.rdataclass.IN, dns.rdatatype.from_text(rec_type), r), ttl=ttl)
else: zones[reverse_zone]['records'].append({ 'name': str(reverse_name)[:-1], 'zone': reverse_zone, 'type': 'PTR', 'value': [ip['name'] + '.'], 'ttl': ip.get('ttl', ''), }) flattened = [] for zone in zones.itervalues(): into = zone.get('tags', {}).get('flatten', None) if into is not None and into in zones: zones[into]['records'].extend(zone['records']) flattened.append(zone['name']) for name in flattened: zones.pop(name) isc = iscBonk(config) named_slave_conf_path = None if config.has_option('iscbrf', 'named_slave_conf_path'): named_slave_conf_path = config.get('iscbrf', 'named_slave_conf_path') isc.buildBindConfig(zones, config.get('iscbrf', 'named_conf_path'), named_slave_conf_path) isc.buildDhcpdConfig(prefixes, addresses, config.get('iscbrf', 'dhcpd_conf_path'))
def zeroconf_to_zone(target_zone='example.com', target_ns='localhost', zeroconf_results={}, locmap={}, priomap={}, ttl=1800): import dns.name import dns.reversename import dns.resolver import dns.query from dns.exception import DNSException, Timeout from dns.resolver import NoAnswer, NXDOMAIN import dns.zone import dns.node import dns.rdataset import dns.rdata #from dns.rdatatype import * import dns.rdatatype import dns.rdataclass if not isinstance(locmap, dict): raise TypeError if not isinstance(priomap, dict): raise TypeError if target_zone == 'example.com': zone = """@ 86400 IN SOA {ns}. administrator.example.com. 1970000000 \ 28800 7200 604800 1800 @ 86400 IN NS {ns}.""".format(ns=target_ns) else: zone = dns.zone.from_xfr(dns.query.xfr(target_ns, target_zone)) zone = zone.get('@').to_text(zone.origin) zone = dns.zone.from_text(zone, origin=target_zone) # ttl = ttl if not ttl == None else \ # zone.get_rdataset('@', dns.rdatatype.SOA).ttl reverse_resolved = {} for key in zeroconf_results: inst_name, inst_type, inst_domain, inst_name_orig = key inst_subtypes = zeroconf_results[key]['subtypes'] \ if 'subtypes' in zeroconf_results[key] else [] # create service type and subtype nodes (empty nodes deleted at the # end) type_node = zone.find_node(dns.name.from_text(inst_type, origin=zone.origin), create=True) subtype_nodes = [zone.find_node(dns.name.from_text(subtype + '._sub.' + inst_type, origin=zone.origin), create=True) for subtype in inst_subtypes] # <Instance> must be a single DNS label, any dots should be escaped # before concatenating all portions of a Service Instance Name, # according to DNS-SD (RFC6763). # A workaround is necessary for buggy software that does not adhere to # the rules: inst_name = re.sub(r'(?<!\\)\.', r'\.', inst_name) inst_fullname = dns.name.from_text("%s.%s" % (inst_name, inst_type), origin=zone.origin) inst_addr = zeroconf_results[key]['address'] if inst_addr not in reverse_resolved: try: reverse_resolved[inst_addr] = dns.resolver.query( dns.reversename.from_address(inst_addr), dns.rdatatype.PTR) except DNSException, e: reverse_resolved[inst_addr] = None continue # inst_hostname_rev_rr = dns.resolver.query(dns.reversename. # from_address(inst_addr), # dns.rdatatype.PTR) inst_hostname_rev_rr = reverse_resolved[inst_addr] \ if reverse_resolved[inst_addr] is not None else [] zeroconf_results[key]['hostname_rev'] = [i.to_text(relativize=False) for i in inst_hostname_rev_rr] if not zeroconf_results[key]['hostname_rev']: continue node_ptr_rdata = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.PTR, inst_fullname.to_text()) # fill service type and subtype nodes with PTR rdata type_node.find_rdataset(dns.rdataclass.IN, dns.rdatatype.PTR, create=True).add(node_ptr_rdata, ttl=ttl) for subtype_node in subtype_nodes: subtype_node.find_rdataset(dns.rdataclass.IN, dns.rdatatype.PTR, create=True).add(node_ptr_rdata, ttl=ttl) # create instance node inst_node = zone.find_node(inst_fullname, create=True) # avahi-browse returns a single SRV and TXT record so we should # try to resolve again qname = dns.name.from_text("%s.%s" % (inst_name_orig, inst_type), origin=dns.name.from_text(inst_domain)) instresolver = dns.resolver.Resolver() mdns_addr = '224.0.0.251' for l, rdt in (('srv', dns.rdatatype.SRV), ('txt', dns.rdatatype.TXT)): if inst_domain == 'local': # mDNS: # * try a multicast query first: # - with unicast-response (QU) bit set # - legacy per RFC6762 sec. 6.7 as we wont set # source_port=5353 # * fallback to direct unicast query (RFC6762 sec. 5.5) # caveat: # unicast responses must be less than 512 bytes # or else we will not get an answer (no EDNS0) a = mudns_query(qname, rdt, qwhere=mdns_addr, qport=5353, qtimeout=2, qu=True) #print "mudns returned %s" % str(a) if a is None: a = mudns_query(qname, rdt, qwhere=inst_addr, qport=5353, qtimeout=2, res=instresolver) else: a = mudns_query(qname, rdt, qtimeout=4) if isinstance(a, dns.resolver.Answer): zeroconf_results[key][l] = [rd.to_text() for rd in a[::]] else: if l == 'srv': zeroconf_results[key][l] = [ '0 0 %s %s' % (zeroconf_results[key]['port'], zeroconf_results[key]['hostname']) ] elif l == 'txt': # reverse the order of fields as returned by avahi txt_rec_rev = re.split('(?<=")\s+(?=")', zeroconf_results[key][l] )[::-1] zeroconf_results[key][l] = [' '.join(txt_rec_rev)] #print "" #continue # replace hostname.local or whatever avahi returns with # reverse-resolved fqdn for h in zeroconf_results[key]['hostname_rev']: zeroconf_results[key]['srv'] = [ re.sub( r'%s\.?' % zeroconf_results[key]['hostname'], r'%s' % h, r) for r in zeroconf_results[key]['srv'] ] zeroconf_results[key]['txt'] = [ re.sub( r'%s\.?' % zeroconf_results[key]['hostname'], r'%s' % h.rstrip('.'), r) for r in zeroconf_results[key]['txt'] ] # txt record mangling for (i, txt_rec) in enumerate(zeroconf_results[key]['txt']): # Bonjour Printing mangling # discard ephemeral fields: printer-state txt_rec = txt_field_mangle(txt_rec, 'printer-state', None) zeroconf_results[key]['txt'][i] = txt_rec # note field mangling if inst_name in locmap: txt_rec = txt_field_mangle(txt_rec, 'note', locmap[inst_name]) zeroconf_results[key]['txt'][i] = txt_rec # priority field mangling if (inst_name, inst_type) in priomap: newprio = priomap[(inst_name, inst_type)] elif inst_name in priomap: newprio = priomap[inst_name] elif inst_type in priomap: newprio = priomap[inst_type] else: newprio = None if newprio is not None: prio = txt_field_mangle(txt_rec, 'priority') if prio is not None: prio = int(prio) if prio == 0: continue else: # Bonjour Printing spec v1.2 sec. 9.2.5 # if not specified, priority defaults to 50 prio = 50 # keep the modulo 10 part of original priority prio = prio % 10 txt_rec = txt_field_mangle(txt_rec, 'priority', newprio + prio) zeroconf_results[key]['txt'][i] = txt_rec # fill instance node with SRV and TXT rdata for rec_type in ('srv', 'txt'): for r in zeroconf_results[key][rec_type]: inst_node.find_rdataset(dns.rdataclass.IN, dns.rdatatype.from_text(rec_type), create=True).add( dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.from_text(rec_type), r), ttl=ttl)