Ejemplo n.º 1
0
 def iter_nested_ns(cls, zone):
     """
     Yield NS/A records for nested zones
     :param zone:
     :return:
     """
     suffix = ".%s." % zone.name
     length = len(zone.name)
     for z in zone.children:
         nested_nses = set()
         for ns in z.profile.authoritative_servers:
             # NS record
             ns_name = zone.get_ns_name(ns)
             yield RR(
                 zone=zone.name,
                 name=z.name[:-length - 1],
                 ttl=zone.profile.zone_ttl,
                 type="NS",
                 rdata=ns_name,
             )
             # Zone delegated to NS from the child zone
             if ns_name.endswith(suffix) and "." in ns_name[:-len(suffix)]:
                 nested_nses.add((ns_name[:-len(suffix)], ns.ip))
         # Yield glue A records for nested NSs
         for name, ip in nested_nses:
             yield RR(zone=zone.name,
                      name=name,
                      ttl=z.profile.zone_ttl,
                      type="A",
                      rdata=ip)
Ejemplo n.º 2
0
 def __init__(self, data):
     records = data.get("records", [])
     if not records:
         raise ValueError("Zone must contain SOA record")
     self.zone = data["name"]
     if RR(zone=self.zone, **records[0]).type != "SOA":
         raise ValueError("First record must be SOA")
     self.records = [RR(zone=self.zone, **r) for r in records]
Ejemplo n.º 3
0
 def iter_classless_delegation(cls, zone):
     """
     Yield classless zone delegations
     :return:
     """
     # Range delegations
     for r in AddressRange.objects.filter(action="D").extra(
             where=["from_address << %s", "to_address << %s"],
             params=[zone.reverse_prefix, zone.reverse_prefix]):
         nses = [ns.strip() for ns in r.reverse_nses.split(",")]
         for a in r.addresses:
             n = a.address.split(".")[-1]
             yield RR(zone=zone.name,
                      name=n,
                      ttl=zone.profile.zone_ttl,
                      type="CNAME",
                      rdata="%s.%s/32" % (n, n))
             for ns in nses:
                 if not ns.endswith("."):
                     ns += "."
                 yield RR(zone=zone.name,
                          name="%s/32" % n,
                          ttl=zone.profile.zone_ttl,
                          type="NS",
                          rdata=ns)
     # Subnet delegation macro
     delegations = defaultdict(list)
     for zr in DNSZoneRecord.objects.filter(zone=zone,
                                            type="NS",
                                            name__contains="/"):
         delegations[zr.name] += [zr.content]
     # Perform classless reverse zone delegation
     for d in delegations:
         nses = delegations[d]
         net, mask = [int(x) for x in d.split("/")]
         if net < 0 or net > 255 or mask <= 24 or mask > 32:
             continue  # Invalid record
         for ns in nses:
             ns = str(ns)
             if not ns.endswith("."):
                 ns += "."
             yield RR(zone=zone.name,
                      name=d,
                      ttl=zone.profile.zone_ttl,
                      type="NS",
                      rdata=ns)
         m = mask - 24
         bitmask = ((1 << m) - 1) << (8 - m)
         if net & bitmask != net:
             continue  # Invalid network
         for i in range(net, net + (1 << (8 - m))):
             yield RR(zone=zone.name,
                      name=str(i),
                      ttl=zone.profile.zone_ttl,
                      type="CNAME",
                      rdata="%d.%s" % (i, d))
Ejemplo n.º 4
0
    def iter_soa(cls, zone):
        """
        Yield SOA record
        :return:
        """
        def dotted(s):
            if not s.endswith("."):
                return s + "."
            return s

        yield RR(
            zone=zone.name,
            name=dotted(zone.to_idna(zone.name)),
            ttl=zone.profile.zone_ttl,
            type="SOA",
            rdata="%s %s %d %d %d %d %d" % (
                dotted(zone.profile.zone_soa),
                dotted(zone.profile.zone_contact),
                zone.serial,
                zone.profile.zone_refresh,
                zone.profile.zone_retry,
                zone.profile.zone_expire,
                zone.profile.zone_ttl,
            ),
        )
Ejemplo n.º 5
0
 def iter_missed_ns_a(cls, zone):
     """
     Yield missed A record for NS'es
     :param zone:
     :return:
     """
     suffix = ".%s." % zone.name
     # Create missed A records for NSses from zone
     # Find in-zone NSes
     in_zone_nses = {}
     for ns in zone.profile.authoritative_servers:
         if not ns.ip:
             continue
         ns_name = zone.get_ns_name(ns)
         # NS server from zone
         if ns_name.endswith(suffix) and "." not in ns_name[:-len(suffix)]:
             in_zone_nses[ns_name[:-len(suffix)]] = ns.ip
     # Find missed in-zone NSes
     for name in in_zone_nses:
         yield RR(
             zone=zone.name,
             name=name,
             type="A",
             ttl=zone.profile.zone_ttl,
             rdata=in_zone_nses[name],
         )
Ejemplo n.º 6
0
    def iter_ipam_ptr4(cls, zone):
        """
        Yield IPv4 PTR records from IPAM
        :param zone:
        :return:
        """
        def ptr(a):
            """
            Convert address to full PTR record
            """
            x = a.split(".")
            x.reverse()
            return "%s.in-addr.arpa" % (".".join(x))

        length = len(zone.name) + 1
        for a in Address.objects.filter(afi="4").extra(
                where=["address << %s"], params=[zone.reverse_prefix]):
            if not a.fqdn:
                continue
            yield RR(
                zone=zone.name,
                name=ptr(a.address)[:-length],
                ttl=zone.profile.zone_ttl,
                type="PTR",
                rdata=a.fqdn + ".",
            )
Ejemplo n.º 7
0
 def iter_ns(cls, zone):
     """
     Yield NS records
     :param zone:
     :return:
     """
     for ns in zone.ns_list:
         yield RR(zone=zone.name,
                  name="",
                  ttl=zone.profile.zone_ttl,
                  type="NS",
                  rdata=ns)
Ejemplo n.º 8
0
 def iter_ipam_ptr6(cls, zone):
     """
     Yield IPv6 PTR records from IPAM
     :return: (name, type, content, ttl, prio)
     :return:
     """
     origin_length = (len(zone.name) - 8 + 1) // 2
     for a in Address.objects.filter(afi="6").extra(
             where=["address << %s"], params=[zone.reverse_prefix]):
         yield RR(zone=zone.name,
                  name=IPv6(a.address).ptr(origin_length),
                  ttl=zone.profile.zone_ttl,
                  type="PTR",
                  rdata=a.fqdn + ".")
Ejemplo n.º 9
0
 def iter_rr(cls, zone):
     """
     Yield directly set RRs from database
     :return:
     """
     for zr in DNSZoneRecord.objects.filter(zone=zone):
         if "/" in zr.name:
             continue
         if not zr.type or not zr.content:
             continue
         yield RR(zone=zone.name,
                  name=zr.name,
                  type=zr.type,
                  ttl=zr.ttl if zr.ttl else zone.profile.zone_ttl,
                  priority=zr.priority,
                  rdata=zr.content)
Ejemplo n.º 10
0
 def iter_ipam_a(cls, zone):
     """
     Yield A/AAAA records from IPAM
     :return: (name, type, content, ttl, prio)
     """
     # @todo: Filter by VRF
     # @todo: Filter by profile
     # @todo: Get ttl from profile
     # Build query
     length = len(zone.name) + 1
     q = (Q(fqdn__iexact=zone.name) | Q(fqdn__iendswith=".%s" % zone.name))
     for z in DNSZone.objects.filter(name__iendswith=".%s" %
                                     zone.name).values_list("name",
                                                            flat=True):
         q &= ~(Q(fqdn__iexact=z) | Q(fqdn__iendswith=".%s" % z))
     for afi, fqdn, address in Address.objects.filter(q).values_list(
             "afi", "fqdn", "address"):
         yield RR(zone=zone.name,
                  name=fqdn[:-length],
                  type="A" if afi == "4" else "AAAA",
                  ttl=zone.profile.zone_ttl,
                  rdata=address)
Ejemplo n.º 11
0
 def iter_bind_zone_rr(self, data):
     """
     Parse bind-style zone and yields RRs
     :param data: Zone text
     """
     # ttl = None
     zone = None
     ttl = None
     seen_soa = False
     for l in self.iter_zone_lines(data):
         if l.startswith("$TTL "):
             ttl = self.parse_ttl(l[5:])
             continue
         if l.startswith("$ORIGIN "):
             zone = l[8:].strip()
             continue
         if not seen_soa:
             # Wait for SOA
             match = self.rx_soa.match(l)
             if match:
                 z = match.group("zone")
                 if z and z != "@":
                     if z.endswith("."):
                         zone = z
                     else:
                         zone = "%s.%s" % (z, zone)
                 yield RR(
                     zone=zone.strip("."),
                     name="",
                     type="SOA",
                     rdata=" ".join(match.groups()[-7:]),
                     ttl=ttl,
                 )
                 seen_soa = True
         else:
             parts = l.split()
             if parts[0] == "IN" or parts[0] in self.RR_TYPES:
                 # missed name
                 parts = [""] + parts
             # Record ttl
             if is_int(parts[1]):
                 rttl = int(parts.pop(1))
             else:
                 rttl = None
             if parts[1] == "IN":
                 # Remove IN
                 parts = [parts[0]] + parts[2:]
             # Normalize name
             name = parts[0]
             if name == "@" or not name:
                 name = zone
             elif not name.endswith("."):
                 name = name + "." + zone
             # Process value
             t = parts[1]
             v = parts[2:]
             if len(v) > 1 and is_int(v[0]):
                 rprio = int(v[0])
                 v = v[1:]
             else:
                 rprio = None
             value = " ".join(v)
             if t in ("CNAME", "PTR"):
                 value = self.from_idna(value)
             elif t == "TXT":
                 # Merge multiple values
                 value = self.merge_mq(value)
             yield RR(
                 zone=zone,
                 name=self.from_idna(name),
                 type=t,
                 rdata=value,
                 ttl=rttl,
                 priority=rprio,
             )