Exemple #1
0
 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
Exemple #2
0
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
Exemple #3
0
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
Exemple #4
0
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)
Exemple #5
0
            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)