def __init__(self, cname):
        """Create RecordData for CNAME type.

        Args:
            cname (Name): cname.
        """
        self.cname = Name(cname)
Beispiel #2
0
def process_zone_file(origin, zonetext, owner, overwrite=False):
    """Imports zone to the database.
    
    No checks for existence are performed in this file. For form processing,
    see the ``import_zone_view`` view.
    

    
    """
    if origin:
        origin = Name((origin.rstrip('.') + '.').split('.'))
    else:
        origin = None
    
    zonetext = clean_zonefile(str(zonetext))
    
    try:
        zone = dns.zone.from_text(zonetext, origin=origin, relativize=False)
        if not str(zone.origin).rstrip('.'):
            raise UnknownOrigin
        
        process_and_import_zone_data(zone, owner, overwrite)
        
    except NoSOA:
        raise Exception('The zone has no SOA RR at its origin')
    except NoNS:
        raise Exception('The zone has no NS RRset at its origin')
    except UnknownOrigin:
        raise Exception('The zone\'s origin is unknown')
    except BadZone:
        raise Exception('The zone is malformed')
    except DNSException, e:
        #raise Exception(str(e))
        raise Exception('The zone is malformed')
Beispiel #3
0
def process_axfr_response(origin, nameserver, owner, overwrite=False):
    """
    origin: string domain name
    nameserver: IP of the DNS server
    
    """
    origin = Name((origin.rstrip('.') + '.').split('.'))
    axfr_query = dns.query.xfr(nameserver, origin, timeout=5, relativize=False, lifetime=10)

    try:
        zone = dns.zone.from_xfr(axfr_query, relativize=False)
        if not str(zone.origin).rstrip('.'):
            raise UnknownOrigin
        
        process_and_import_zone_data(zone, owner, overwrite)

    except NoSOA:
        raise Exception('The zone has no SOA RR at its origin')
    except NoNS:
        raise Exception('The zone has no NS RRset at its origin')
    except UnknownOrigin:
        raise Exception('The zone\'s origin is unknown')
    except BadZone:
        raise Exception('The zone is malformed')
    except DNSException, e:
        if not str(e):
            raise Exception('Transfer Failed')
        raise Exception(str(e))
Beispiel #4
0
def process_zone_file(origin, zonetext, owner, overwrite=False):
    """Imports zone to the database.
    
    No checks for existence are performed in this file. For form processing,
    see the ``import_zone_view`` view.
    

    
    """
    if origin:
        origin = Name((origin.rstrip('.') + '.').split('.'))
    else:
        origin = None
    
    zonetext = clean_zonefile(str(zonetext))
    
    try:
        zone = dns.zone.from_text(zonetext, origin=origin, relativize=False)
        if not str(zone.origin).rstrip('.'):
            raise UnknownOrigin
        
        process_and_import_zone_data(zone, owner, overwrite)
        
    except NoSOA:
        raise Exception('The zone has no SOA RR at its origin')
    except NoNS:
        raise Exception('The zone has no NS RRset at its origin')
    except UnknownOrigin:
        raise Exception('The zone\'s origin is unknown')
    except BadZone:
        raise Exception('The zone is malformed')
    except DNSException as e:
        #raise Exception(str(e))
        raise Exception('The zone is malformed')
Beispiel #5
0
def process_axfr_response(origin, nameserver, owner, overwrite=False):
    """
    origin: string domain name
    nameserver: IP of the DNS server
    
    """
    origin = Name((origin.rstrip('.') + '.').split('.'))
    axfr_query = dns.query.xfr(nameserver, origin, timeout=5, relativize=False, lifetime=10)

    try:
        zone = dns.zone.from_xfr(axfr_query, relativize=False)
        if not str(zone.origin).rstrip('.'):
            raise UnknownOrigin
        
        process_and_import_zone_data(zone, owner, overwrite)

    except NoSOA:
        raise Exception('The zone has no SOA RR at its origin')
    except NoNS:
        raise Exception('The zone has no NS RRset at its origin')
    except UnknownOrigin:
        raise Exception('The zone\'s origin is unknown')
    except BadZone:
        raise Exception('The zone is malformed')
    except DNSException as e:
        if not str(e):
            raise Exception('Transfer Failed')
        raise Exception(str(e))
 def test_authority_domain(self):
     question = Question(Name("server1.gumpe"), Type.A, Class.IN)
     header = Header(1337, 0, 1, 0, 0, 0)
     query = Message(header, questions=[question])
     s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
     s.sendto(query.to_bytes(), (SERVER, PORT))
     data = s.recv(512)
     s.close()
     message = Message.from_bytes(data)
     self.assertCountEqual(message.answers, [
         ResourceRecord(
             name=Name("server1.gumpe."),
             type_=Type.A,
             class_=Class.IN,
             ttl=0,
             rdata=ARecordData("10.0.1.5"),
         ),
         ResourceRecord(
             name=Name("server1.gumpe."),
             type_=Type.A,
             class_=Class.IN,
             ttl=0,
             rdata=ARecordData("10.0.1.4"),
         ),
     ])
 def test_question_from_bytes(self, MockName):
     packet = b"\x07example\x03com\x00\x00\x02\x00\x01"
     MockName.from_bytes.return_value = (Name("example.com"), 13)
     question1, offset = Question.from_bytes(packet, 0)
     question2 = Question(Name("example.com"), Type.NS, Class.IN)
     self.assertEqual(question1, question2)
     self.assertEqual(offset, 17)
     MockName.from_bytes.assert_called_with(packet, 0)
Beispiel #8
0
 def test_resource_from_bytes(self, MockName, MockRData):
     MockName.from_bytes.return_value = (Name("example.com"), 13)
     MockRData.create_from_bytes.return_value = ARecordData("1.1.1.1")
     packet = (b"\x07example\x03com\x00\x00\x01\x00\x02\x00\x00\x00\x03\x00"
               b"\x04\x01\x01\x01\x01")
     record1, offset = ResourceRecord.from_bytes(packet, 0)
     record2 = ResourceRecord(Name("example.com"), Type.A, Class.CS, 3,
                              ARecordData("1.1.1.1"))
     self.assertEqual(record1, record2)
     MockName.from_bytes.assert_called_with(packet, 0)
     MockRData.create_from_bytes.assert_called_with(Type.A, packet, 23, 4)
 def test_invalid_domain_from_cache(self):
     cache = RecordCache(0)
     record = ResourceRecord(
         name=Name("bonobo.putin"),
         type_=Type.A,
         class_=Class.IN,
         ttl=60,
         rdata=ARecordData("1.0.0.1"),
     )
     cache.add_record(record)
     self.assertEqual(cache.lookup(Name("bonobo.putin"), Type.A, Class.IN),
                      record)
 def test_expired_cache_entry(self):
     cache = RecordCache(0)
     record = ResourceRecord(
         name=Name("bonobo.putin"),
         type_=Type.A,
         class_=Class.IN,
         ttl=0,
         rdata=ARecordData("1.0.0.1"),
     )
     cache.add_record(record)
     self.assertEqual(cache.lookup(Name("bonobo.putin"), Type.A, Class.IN),
                      None)
 def test_ttl_overwrite(self):
     cache = RecordCache(60)
     record = ResourceRecord(
         name=Name("bonobo.putin"),
         type_=Type.A,
         class_=Class.IN,
         ttl=0,
         rdata=ARecordData("1.0.0.1"),
     )
     cache.add_record(record)
     cache_entry = cache.lookup(Name("bonobo.putin"), Type.A, Class.IN)
     self.assertEqual(cache_entry, record)
     self.assertEqual(cache_entry.ttl, 60)
Beispiel #12
0
 def matchByLabel(self, dname, type_, class_):
     """
     Args:
         dname (Name): domain name
         type_ (Type): type
         class_ (Class): class
     """
     dname = Name(dname)
     while dname.labels:
         rrs = self.lookup(str(dname), type_, class_)
         if rrs:
             return rrs
         dname.labels = dname.labels[1:]
     return []
Beispiel #13
0
    def from_bytes(cls, packet, offset, rdlength):
        """Create a RecordData object from bytes.

        Args:
            packet (bytes): packet.
            offset (int): offset in message.
            rdlength (int): length of rdata.
        """
        mname, offset = Name.from_bytes(packet, offset)
        rname, offset = Name.from_bytes(packet, offset)
        serial = struct.unpack_from("!I", packet, offset)[0]
        refresh = struct.unpack_from("!i", packet, offset + 4)[0]
        retry = struct.unpack_from("!i", packet, offset + 8)[0]
        expire = struct.unpack_from("!i", packet, offset + 12)[0]
        minimum = struct.unpack_from("!I", packet, offset + 16)[0]
        return cls(mname, rname, serial, refresh, retry, expire, minimum), offset + 20
Beispiel #14
0
def main():
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    question = Question(Name("google.com."), Type.A, Class.IN)
    header = Header(9001, 0, 1, 0, 0, 0)
    header.qr = 0  # 0 for query
    header.opcode = 0  # standad query
    header.rd = 1  #  recursive
    query = Message(header, [question])
    ip = (([
        ip for ip in socket.gethostbyname_ex(socket.gethostname())[2]
        if not ip.startswith("127.")
    ] or [[(s.connect(("8.8.8.8", 53)), s.getsockname()[0], s.close())
           for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]])
          + ["no IP found"])[0]

    sock.sendto(query.to_bytes(), (ip, 53))
    data = sock.recv(1024)
    mess = Message.from_bytes(data)
    """
  answer =  ResourceRecord.to_dict(mess.answers[0])
  auth = ResourceRecord.to_dict(mess.authorities[0])
  addi = ResourceRecord.to_dict(mess.additionals[0])
"""
    rrs = []
    rrs += mess.answers + mess.authorities + mess.additionals
    for r in rrs:
        print("R", r.to_dict())
Beispiel #15
0
    def read_master_file(self, filename):
        """Read the zone from a master file

        See section 5 of RFC 1035.

        Args:
            filename (str): the filename of the master file
        """
        records = re.finditer(
            Zone.record_re, open(filename, "r").read(), flags=re.MULTILINE
        )
        for r in records:
            domain, ttl, class_, type_, rdata = r.groups()
            domain = domain or self.last_domain
            self.last_domain = domain
            record = ResourceRecord(
                name=Name(domain),
                type_=Type[type_],
                class_=Class[class_],
                ttl=int(ttl) if ttl is not None else Zone.default_ttl,
                rdata=RecordData.create_from_str(Type[type_], rdata)
            )
            if domain in self.records:
                self.records[domain].append(record)
            else:
                self.records[domain] = [record]
Beispiel #16
0
    def load_and_parse(self, content):
        content = re.sub(re.compile(";.*?\n"), "\n", content)  #Remove comments
        content = re.sub(re.compile("\n\n*\n"), "\n",
                         content)  #Remove whitespaces
        content = re.sub(re.compile("  * "), " ",
                         content)  #Remove multiple spaces between words
        content = re.sub(re.compile("\t\t*\t"), " ",
                         content)  #Remove tabs between words
        content = re.sub(re.compile("\n *"), "\n",
                         content)  #Remove spaces at start of line
        content = re.sub(re.compile("\n\t*"), "\n",
                         content)  #Remove tabs at start of line
        content = re.compile(r'\(.*?\)', re.DOTALL)\
            .sub(lambda x: x.group().replace('\n', ''), content) #Remove newlines between ()
        content = re.sub(re.compile("\t+"), " ",
                         content)  #Remove tabs between words
        default_ttl = None
        origin = None

        recordSet = []

        for line in content.split('\n'):
            if line[:4] == "$TTL":
                prev_ttl = self.time_to_seconds(line[4:].strip())
            elif line[:7] == "$ORIGIN":
                origin = line[7:].strip()
            elif "SOA" not in line:
                parts = line.split(' ')
                rr_name = parts[0]

                rr_ttl = self.time_to_seconds(parts[1])
                offset = int(rr_ttl == 0)
                if offset:
                    rr_ttl = prev_ttl

                rr_class = Class[parts[1 + offset]]
                rr_type = parts[2 + offset]
                if Type[rr_type] == Type.CNAME or Type[rr_type] == Type.NS:
                    rr_data = RecordData.create(
                        Type[rr_type], Name(parts[3 + offset].rstrip('.')))
                else:
                    rr_data = RecordData.create(Type[rr_type],
                                                parts[3 + offset].rstrip('.'))
                self.add_node(
                    rr_name,
                    ResourceRecord(Name(rr_name), Type[rr_type], rr_class,
                                   rr_ttl, rr_data))
 def test_outside_zone(self):
     question = Question(Name("gaia.cs.umass.edu"), Type.A, Class.IN)
     header = Header(1337, 0, 1, 0, 0, 0)
     header.rd = 1
     query = Message(header, questions=[question])
     s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
     s.sendto(query.to_bytes(), (SERVER, PORT))
     data = s.recv(512)
     s.close()
     message = Message.from_bytes(data)
     self.assertEqual(message.answers, [
         ResourceRecord(name=Name("gaia.cs.umass.edu"),
                        type_=Type.A,
                        class_=Class.IN,
                        ttl=0,
                        rdata=ARecordData("128.119.245.12")),
     ])
Beispiel #18
0
 def test_name_to_bytes_compress4(self):
     name1 = Name("www.example.com")
     name2 = Name("example.com")
     compress = {}
     name1.to_bytes(0, compress)
     self.assertEqual(name2.to_bytes(17),
                      b"\x07example\x03com\x00")
Beispiel #19
0
 def test_name_to_bytes_compress5(self):
     name1 = Name("www.example.com")
     name2 = Name("WWW.example.com")
     compress = {}
     name1.to_bytes(0, compress)
     self.assertEqual(name2.to_bytes(17, compress),
                      b"\xc0\x00")
Beispiel #20
0
def DnsName(*args):
    lst = []
    for arg in args:
        if isinstance(arg, list):
            lst.extend(arg)
        else:
            lst.extend(arg.split('.'))

    return Name(lst)
Beispiel #21
0
 def from_bytes(cls, packet, offset):
     """Convert ResourceRecord from bytes."""
     name, offset = Name.from_bytes(packet, offset)
     type_ = Type(struct.unpack_from("!H", packet, offset)[0])
     class_ = Class(struct.unpack_from("!H", packet, offset + 2)[0])
     ttl, rdlength = struct.unpack_from("!iH", packet, offset + 4)
     offset += 10
     rdata = RecordData.create_from_bytes(type_, packet, offset, rdlength)
     offset += rdlength
     return cls(name, type_, class_, ttl, rdata), offset
Beispiel #22
0
    def from_bytes(cls, packet, offset, rdlength):
        """Create a RecordData object from bytes.

        Args:
            packet (bytes): packet.
            offset (int): offset in message.
            rdlength (int): length of rdata.
        """
        nsdname, offset = Name.from_bytes(packet, offset)
        return cls(nsdname)
 def test_concurrent_requests(self):
     queries = []
     answers = []
     question = Question(Name("gaia.cs.umass.edu"), Type.A, Class.IN)
     header = Header(1337, 0, 1, 0, 0, 0)
     header.rd = 1
     queries.append(Message(header, questions=[question]))
     answers.append([
         ResourceRecord(name=Name("gaia.cs.umass.edu"),
                        type_=Type.A,
                        class_=Class.IN,
                        ttl=0,
                        rdata=ARecordData("128.119.245.12")),
     ])
     question = Question(Name("server2.gumpe"), Type.A, Class.IN)
     header = Header(420, 0, 1, 0, 0, 0)
     queries.append(Message(header, questions=[question]))
     answers.append([
         ResourceRecord(name=Name("server2.gumpe"),
                        type_=Type.A,
                        class_=Class.IN,
                        ttl=0,
                        rdata=ARecordData("10.0.1.7")),
     ])
     header = Header(69, 0, 1, 0, 0, 0)
     question = Question(Name("www.gumpe"), Type.A, Class.IN)
     queries.append(Message(header, questions=[question]))
     answers.append([
         ResourceRecord(name=Name("www.gumpe"),
                        type_=Type.A,
                        class_=Class.IN,
                        ttl=0,
                        rdata=ARecordData("10.0.1.7")),
         ResourceRecord(name=Name("www.gumpe"),
                        type_=Type.CNAME,
                        class_=Class.IN,
                        ttl=0,
                        rdata=CNAMERecordData(Name("server2.gumpe"))),
     ])
     sockets = [
         socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
         for _ in range(len(queries))
     ]
     for i in range(len(sockets)):
         sockets[i].sendto(queries[i].to_bytes(), (SERVER, PORT))
     responses = []
     for i in range(len(sockets)):
         responses.append(Message.from_bytes(sockets[i].recv(1024)))
     for i in range(len(sockets)):
         sockets[i].close()
     for i in range(len(queries)):
         self.assertCountEqual(responses[i].answers, answers[i])
    def check_cache(self, hostname):
        iplist = self.cache.lookup(Name(hostname), Type.A, Class.IN)
        namelist = self.cache.lookup(Name(hostname), Type.CNAME, Class.IN)
        if not (iplist == [] and namelist == []):
            iplist = [x.rdata.address for x in iplist]
            namelist = [x.rdata.cname for x in namelist]
            return True, iplist, namelist

        hostname = hostname.split('.')
        for i in range(len(hostname)):
            test = '.'.join(hostname[i:])
            namelist = self.cache.lookup(Name(test), Type.NS, Class.IN)
            namelist = [x.rdata.nsdname for x in namelist]
            if not namelist == []:
                for x in namelist:
                    newips = self.cache.lookup(x, Type.A, Class.IN)
                    iplist.extend(newips)
                iplist = [x.rdata.address for x in iplist]
                return False, iplist, namelist
        return False, [], []
Beispiel #25
0
def DnsAbsName(*args):
    lst = []
    for arg in args:
        if isinstance(arg, list):
            lst.extend(arg)
        else:
            lst.extend(arg.split('.'))

    if lst[-1]:
        lst.append("")  # make it an absolute name

    return Name(lst)
 def test_invalid_domain_from_cache(self):
     cache = RecordCache(0)
     resolver = Resolver(5, cache)
     cache.add_record(
         ResourceRecord(
             name=Name("bonobo.putin"),
             type_=Type.A,
             class_=Class.IN,
             ttl=60,
             rdata=ARecordData("1.0.0.1"),
         ))
     cache.add_record(
         ResourceRecord(
             name=Name("bonobo.putin"),
             type_=Type.CNAME,
             class_=Class.IN,
             ttl=60,
             rdata=CNAMERecordData(Name("putin.bonobo")),
         ))
     self.assertEqual(resolver.gethostbyname("bonobo.putin"),
                      ("bonobo.putin", ["putin.bonobo."], ["1.0.0.1"]))
 def test_expired_cache_entry(self):
     cache = RecordCache(0)
     resolver = Resolver(5, cache)
     cache.add_record(
         ResourceRecord(
             name=Name("hw.gumpe"),
             type_=Type.A,
             class_=Class.IN,
             ttl=0,
             rdata=ARecordData("1.0.0.2"),
         ))
     cache.add_record(
         ResourceRecord(
             name=Name("hw.gumpe"),
             type_=Type.CNAME,
             class_=Class.IN,
             ttl=0,
             rdata=CNAMERecordData(Name("gumpe.hw")),
         ))
     self.assertEqual(resolver.gethostbyname("hw.gumpe"),
                      ("hw.gumpe", [], []))
Beispiel #28
0
 def mock_resolver(_, record_type):
     if record_type == 'SRV':
         return [
             SRV(0, 0, 0, 0, 27017, Name(labels=hostname))
             for hostname in [
                 b'mongo1.example.com'.split(
                     b'.'), b'mongo2.example.com'.split(b'.'),
                 b'mongo3.example.com'.split(b'.')
             ]
         ]
     elif record_type == 'TXT':
         return [TXT(0, 0, [b'replicaSet=rs0'])]
Beispiel #29
0
    def send_query(sock, hostname, ip):
        # Create and send query
        question = Question(Name(hostname), Type.A, Class.IN)
        header = Header(randint(0, 2**16), 0, 1, 0, 0, 0)
        header.qr = 0
        header.opcode = 0
        header.rd = 0  # no recursion desired
        query = Message(header, [question])
        sock.sendto(query.to_bytes(), (ip, 53))

        # Receive response
        data = sock.recv(512)
        return Message.from_bytes(data)
Beispiel #30
0
        def mock_resolver(_, rdtype, rdclass=None, lifetime=None, **kwargs):

            if rdtype == 'SRV':
                return [
                    SRV(0, 0, 0, 0, 27017, Name(labels=hostname))
                    for hostname in [
                        b'mongo1.example.com'.split(
                            b'.'), b'mongo2.example.com'.split(b'.'),
                        b'mongo3.example.com'.split(b'.')
                    ]
                ]
            elif rdtype == 'TXT':
                return [TXT(0, 0, [b'replicaSet=rs0'])]
Beispiel #31
0
    def query_recursive(self, sock, hostname, ip):
        if self.cache is not None:
            answer = []
            record = self.cache.lookup(Name(hostname), Type.A, Class.IN)
            alias = self.cache.lookup(Name(hostname), Type.CNAME, Class.IN)
            if record is not None:
                answer.append(record)
            if alias is not None:
                answer.append(alias)
            if len(answer):
                return answer

        response = Resolver.send_query(sock, hostname, ip)
        if (response.header.an_count > 0 or response.header.rcode != 0):
            if self.cache is not None:
                self.cache.add_records(response.answers)
            return response.answers
        ips = []
        for record in response.additionals:
            if record.type_ is Type.A:
                ips.append(record.rdata.address)
                if self.cache is not None:
                    self.cache.add_record(record)
            if record.type_ is Type.CNAME and self.cache is not None:
                self.cache.add_record(record)
        if len(ips) == 0:
            for record in response.authorities:
                ipaddrlist = self.gethostbyname(record.rdata.nsdname)[2]
                for new_ip in ipaddrlist:
                    res = self.query_recursive(sock, hostname, new_ip)
                    if res is not None:
                        return res
        for new_ip in ips:
            res = self.query_recursive(sock, hostname, new_ip)
            if res is not None:
                return res
        return []
class CNAMERecordData(RecordData):
    """Record data for CNAME type."""
    def __init__(self, cname):
        """Create RecordData for CNAME type.

        Args:
            cname (Name): cname.
        """
        self.cname = Name(cname)

    def __str__(self):
        return "Canonical Name: {}".format(self.cname)

    def __eq__(self, other):
        return (isinstance(other, CNAMERecordData)
                and self.cname == other.cname)

    def to_bytes(self, offset, compress):
        """Convert to bytes.

        Args:
            offset (int): offset in packet.
            compress (dict): dict from domain names to pointers.
        """
        return self.cname.to_bytes(offset, compress)

    @classmethod
    def from_bytes(cls, packet, offset, rdlength):
        """Create a RecordData object from bytes.

        Args:
            packet (bytes): packet.
            offset (int): offset in message.
            rdlength (int): length of rdata.
        """
        cname, offset = Name.from_bytes(packet, offset)
        return cls(cname)

    def to_dict(self):
        """Convert to dict."""
        return {"cname": str(self.cname)}

    @classmethod
    def from_dict(cls, dct):
        """Create a RecordData object from dict."""
        return cls(Name(dct["cname"]))
Beispiel #33
0
def pdnssec_hash_zone_record(zone_name, record_name):
    """Generates hash for ordername field in NSEC3 non-narrow mode.
    
    *****
    Special kudos to the folks at the #powerdns IRC channel for the help,
    especially Maik for pointing me to dns.name.Name.to_digestable() method.
    *****
    
    Notes from RFCs for easy reference
    ----------------------------------
    
    NSEC3PARAM Presentation Format:
    
        http://tools.ietf.org/html/rfc5155#section-4.3
    
    4.3. Presentation Format
     
    The presentation format of the RDATA portion is as follows:
    
    - The Hash Algorithm field is represented as an unsigned decimal
    integer.  The value has a maximum of 255.
    
    - The Flags field is represented as an unsigned decimal integer.
    The value has a maximum value of 255.
    
    - The Iterations field is represented as an unsigned decimal
    integer.  The value is between 0 and 65535, inclusive.
    
    - The Salt Length field is not represented.
    
    - The Salt field is represented as a sequence of case-insensitive
    hexadecimal digits.  Whitespace is not allowed within the
    sequence.  This field is represented as "-" (without the quotes)
    when the Salt Length field is zero.


    5. Calculation of the Hash
       http://tools.ietf.org/html/rfc5155#section-5
    The hash calculation uses three of the NSEC3 RDATA fields: Hash
    Algorithm, Salt, and Iterations.
    
    Define H(x) to be the hash of x using the Hash Algorithm selected by
    the NSEC3 RR, k to be the number of Iterations, and || to indicate
    concatenation.  Then define:
    
    IH(salt, x, 0) = H(x || salt), and
    
    IH(salt, x, k) = H(IH(salt, x, k-1) || salt), if k > 0
    
    Then the calculated hash of an owner name is
    
    IH(salt, owner name, iterations),
    
    where the owner name is in the canonical form, defined as:
    
    The wire format of the owner name where:
    
    1.  The owner name is fully expanded (no DNS name compression) and
    fully qualified;
    
    2.  All uppercase US-ASCII letters are replaced by the corresponding
    lowercase US-ASCII letters;
    
    3.  If the owner name is a wildcard name, the owner name is in its
    original unexpanded form, including the "*" label (no wildcard
    substitution);
    
    This form is as defined in Section 6.2 of [RFC4034].
    
    The method to calculate the Hash is based on [RFC2898].
    
    "DNSSEC NSEC3 Hash Algorithms".
   The initial contents of this registry are:

      0 is Reserved.

      1 is SHA-1.

      2-255 Available for assignment.
    """
    
    Domain = get_model('powerdns_manager', 'Domain')
    DomainMetadata = get_model('powerdns_manager', 'DomainMetadata')
    
    the_domain = Domain.objects.get(name__exact=zone_name)
    nsec3param = DomainMetadata.objects.get(domain=the_domain, kind='NSEC3PARAM')
    algo, flags, iterations, salt = nsec3param.content.split()
    
    # dns.name.NAME expects an absolute name (with trailing dot)
    record_name = '%s.' % record_name.rstrip('.')
    record_name = Name(record_name.split('.'))
    
    # Prepare salt
    salt = salt.decode('hex')
    
    hashed_name = sha1hash(record_name.to_digestable(), salt)
    i = 0
    while i < int(iterations):
        hashed_name = sha1hash(hashed_name, salt)
        i += 1
    
    # Do standard base32 encoding
    final_data = base64.b32encode(hashed_name)
    # Apply the translation table to convert to base32hex encoding.
    final_data = final_data.translate(b32_to_ext_hex)
    # Return lower case representation as required by PowerDNS
    return final_data.lower()
Beispiel #34
0
def generate_zone_file(origin):
    """Generates a zone file.
    
    Accepts the zone origin as string (no trailing dot).
     
    Returns the contents of a zone file that contains all the resource records
    associated with the domain with the provided origin.
    
    """
    Domain = get_model('powerdns_manager', 'Domain')
    Record = get_model('powerdns_manager', 'Record')
    
    the_domain = Domain.objects.get(name__exact=origin)
    the_rrs = Record.objects.filter(domain=the_domain).order_by('-type')
    
    # Generate the zone file
    
    origin = Name((origin.rstrip('.') + '.').split('.'))
    
    # Create an empty dns.zone object.
    # We set check_origin=False because the zone contains no records.
    zone = dns.zone.from_text('', origin=origin, relativize=False, check_origin=False)
    
    rdclass = dns.rdataclass._by_text.get('IN')
    
    for rr in the_rrs:
        
        # Add trailing dot to rr.name
        record_name = rr.name.rstrip('.') + '.'
        
        if rr.type == 'SOA':
            # Add SOA Resource Record
            
            # SOA content:  primary hostmaster serial refresh retry expire default_ttl
            bits = rr.content.split()
            # Primary nameserver of SOA record
            primary = bits[0].rstrip('.') + '.'
            mname = Name(primary.split('.'))
            # Responsible hostmaster from SOA record
            hostmaster = bits[1].rstrip('.') + '.'
            rname = Name(hostmaster.split('.'))
            
            rdtype = dns.rdatatype._by_text.get('SOA')
            rdataset = zone.find_rdataset(record_name, rdtype=rdtype, create=True)
            rdata = dns.rdtypes.ANY.SOA.SOA(rdclass, rdtype,
                mname = mname,
                rname = rname,
                serial = int(bits[2]),
                refresh = int(bits[3]),
                retry = int(bits[4]),
                expire = int(bits[5]),
                minimum = int(bits[6])
            )
            rdataset.add(rdata, ttl=int(rr.ttl))
        
        elif rr.type == 'NS':
            # Add NS Resource Record
            rdtype = dns.rdatatype._by_text.get('NS')
            rdataset = zone.find_rdataset(record_name, rdtype=rdtype, create=True)
            rdata = dns.rdtypes.ANY.NS.NS(rdclass, rdtype,
                target = Name((rr.content.rstrip('.') + '.').split('.'))
            )
            rdataset.add(rdata, ttl=int(rr.ttl))
        
        elif rr.type == 'MX':
            # Add MX Resource Record
            rdtype = dns.rdatatype._by_text.get('MX')
            rdataset = zone.find_rdataset(record_name, rdtype=rdtype, create=True)
            rdata = dns.rdtypes.ANY.MX.MX(rdclass, rdtype,
                preference = int(rr.prio),
                exchange = Name((rr.content.rstrip('.') + '.').split('.'))
            )
            rdataset.add(rdata, ttl=int(rr.ttl))
        
        elif rr.type == 'TXT':
            # Add TXT Resource Record
            rdtype = dns.rdatatype._by_text.get('TXT')
            rdataset = zone.find_rdataset(record_name, rdtype=rdtype, create=True)
            rdata = dns.rdtypes.ANY.TXT.TXT(rdclass, rdtype,
                strings = [rr.content.strip('"')]
            )
            rdataset.add(rdata, ttl=int(rr.ttl))
        
        elif rr.type == 'CNAME':
            # Add CNAME Resource Record
            rdtype = dns.rdatatype._by_text.get('CNAME')
            rdataset = zone.find_rdataset(record_name, rdtype=rdtype, create=True)
            rdata = dns.rdtypes.ANY.CNAME.CNAME(rdclass, rdtype,
                target = Name((rr.content.rstrip('.') + '.').split('.'))
            )
            rdataset.add(rdata, ttl=int(rr.ttl))
        
        elif rr.type == 'A':
            # Add A Resource Record
            rdtype = dns.rdatatype._by_text.get('A')
            rdataset = zone.find_rdataset(record_name, rdtype=rdtype, create=True)
            rdata = dns.rdtypes.IN.A.A(rdclass, rdtype,
                address = rr.content
            )
            rdataset.add(rdata, ttl=int(rr.ttl))
        
        elif rr.type == 'AAAA':
            # Add AAAA Resource Record
            rdtype = dns.rdatatype._by_text.get('AAAA')
            rdataset = zone.find_rdataset(record_name, rdtype=rdtype, create=True)
            rdata = dns.rdtypes.IN.AAAA.AAAA(rdclass, rdtype,
                address = rr.content
            )
            rdataset.add(rdata, ttl=int(rr.ttl))
        
        elif rr.type == 'SPF':
            # Add SPF Resource Record
            rdtype = dns.rdatatype._by_text.get('SPF')
            rdataset = zone.find_rdataset(record_name, rdtype=rdtype, create=True)
            rdata = dns.rdtypes.ANY.SPF.SPF(rdclass, rdtype,
                strings = [rr.content.strip('"')]
            )
            rdataset.add(rdata, ttl=int(rr.ttl))
        
        elif rr.type == 'PTR':
            # Add PTR Resource Record
            rdtype = dns.rdatatype._by_text.get('PTR')
            rdataset = zone.find_rdataset(record_name, rdtype=rdtype, create=True)
            rdata = dns.rdtypes.ANY.PTR.PTR(rdclass, rdtype,
                target = Name((rr.content.rstrip('.') + '.').split('.'))
            )
            rdataset.add(rdata, ttl=int(rr.ttl))
        
        elif rr.type == 'SRV':
            # Add SRV Resource Record
            
            # weight port target
            weight, port, target = rr.content.split()
            
            rdtype = dns.rdatatype._by_text.get('SRV')
            rdataset = zone.find_rdataset(record_name, rdtype=rdtype, create=True)
            rdata = dns.rdtypes.IN.SRV.SRV(rdclass, rdtype,
                priority = int(rr.prio),
                weight = int(weight),
                port = int(port),
                target = Name((target.rstrip('.') + '.').split('.'))
            )
            rdataset.add(rdata, ttl=int(rr.ttl))
            
    
    # Export text (from the source code of http://www.dnspython.org/docs/1.10.0/html/dns.zone.Zone-class.html#to_file)
    EOL = '\n'
    f = StringIO.StringIO()
    f.write('$ORIGIN %s%s' % (origin, EOL))
    zone.to_file(f, sorted=True, relativize=False, nl=EOL)
    data = f.getvalue()
    f.close()
    return data