#!/usr/bin/env python
# -*- coding: utf-8 -*-

#    Copyright (C) 2020  Carlos Perez
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; Applies version 2 of the License.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA


import random
import socket

import dns.message
import dns.query
import dns.resolver
import dns.reversename
from dns.dnssec import algorithm_to_text
from dns.zone import *

from lib.msf_print import *

DNS_PORT_NUMBER = 53
DNS_QUERY_TIMEOUT = 4.0


class DnsHelper:
    def __init__(self, domain, ns_server=None, request_timeout=3.0, proto="tcp"):
        self._domain = domain
        self._proto = proto
        if ns_server:
            self._res = dns.resolver.Resolver(configure=False)
            self._res.nameservers = ns_server
            if len(ns_server) > 1:
                self._res.rotate = True
        else:
            self._res = dns.resolver.Resolver(configure=True)
        # Set timing
        self._res.timeout = request_timeout
        self._res.lifetime = request_timeout

    def check_tcp_dns(self, address):
        """
        Function to check if a server is listening at port 53 TCP. This will aid
        in IDS/IPS detection since a AXFR will not be tried if port 53 is found to
        be closed.
        """
        sock = socket.socket()

        sock.settimeout(DNS_QUERY_TIMEOUT)
        try:
            sock.connect((address, DNS_PORT_NUMBER))
        except Exception:
            return False
        else:
            return True

    def resolve(self, target, type, ns=None):
        """
        Function for performing general resolution types returning the RDATA
        """
        if ns:
            res = dns.resolver.Resolver(configure=False, )
            res.nameservers = [ns]
        else:
            res = dns.resolver.Resolver(configure=True)

        tcp = True if self._proto == "tcp" else False
        answers = res.query(target, type, tcp=tcp)
        return answers

    def query(self, q, where, timeout=None, port=53, af=None, source=None, source_port=0, one_rr_per_rrset=False):

        if isinstance(where, list):
            random.shuffle(where)
            target_server = where[0]
        else:
            target_server = where

        if self._proto == "tcp":
            return dns.query.tcp(q, target_server, timeout, port, af, source, source_port, one_rr_per_rrset)
        else:
            return dns.query.udp(q, target_server, timeout, port, af, source, source_port, False, one_rr_per_rrset)

    def get_a(self, host_trg):
        """
        Function for resolving the A Record for a given host. Returns an Array of
        the IP Address it resolves to. It will also return CNAME data.
        """
        address = []
        tcp = True if self._proto == "tcp" else False
        try:
            ipv4_answers = self._res.query(host_trg, 'A', tcp=tcp)
            for ardata in ipv4_answers.response.answer:
                for rdata in ardata:
                    if rdata.rdtype == 5:
                        if rdata.target.to_text().endswith('.'):
                            address.append(["CNAME", host_trg, rdata.target.to_text()[:-1]])
                            host_trg = rdata.target.to_text()[:-1]
                        else:
                            address.append(["CNAME", host_trg, rdata.target.to_text()])
                            host_trg = rdata.target.to_text()
                    else:
                        address.append(["A", host_trg, rdata.address])
        except Exception:
            return address
        return address

    def get_aaaa(self, host_trg):
        """
        Function for resolving the AAAA Record for a given host. Returns an Array of
        the IP Address it resolves to. It will also return CNAME data.
        """
        address = []
        tcp = True if self._proto == "tcp" else False
        try:
            ipv6_answers = self._res.query(host_trg, 'AAAA', tcp=tcp)
            for ardata in ipv6_answers.response.answer:
                for rdata in ardata:
                    if rdata.rdtype == 5:
                        if rdata.target.to_text().endswith('.'):
                            address.append(["CNAME", host_trg, rdata.target.to_text()[:-1]])
                            host_trg = rdata.target.to_text()[:-1]
                        else:
                            address.append(["CNAME", host_trg, rdata.target.to_text()])
                            host_trg = rdata.target.to_text()
                    else:
                        address.append(["AAAA", host_trg, rdata.address])
        except Exception:
            return address
        return address

    def get_ip(self, hostname):
        """
        Function resolves a host name to its given A and/or AAAA record. Returns Array
        of found hosts and IPv4 or IPv6 Address.
        """
        found_ip_add = []
        found_ip_add.extend(self.get_a(hostname))
        found_ip_add.extend(self.get_aaaa(hostname))

        return found_ip_add

    def get_mx(self):
        """
        Function for MX Record resolving. Returns all MX records. Returns also the IP
        address of the host both in IPv4 and IPv6. Returns an Array
        """
        mx_records = []
        tcp = True if self._proto == "tcp" else False
        answers = self._res.query(self._domain, 'MX', tcp=tcp)
        for rdata in answers:
            try:
                name = rdata.exchange.to_text()
                ipv4_answers = self._res.query(name, 'A', tcp=tcp)
                for ardata in ipv4_answers:
                    if name.endswith('.'):
                        mx_records.append(['MX', name[:-1], ardata.address,
                                          rdata.preference])
                    else:
                        mx_records.append(['MX', name, ardata.address,
                                          rdata.preference])
            except Exception:
                pass
        try:
            for rdata in answers:
                name = rdata.exchange.to_text()
                ipv6_answers = self._res.query(name, 'AAAA', tcp=tcp)
                for ardata in ipv6_answers:
                    if name.endswith('.'):
                        mx_records.append(['MX', name[:-1], ardata.address,
                                          rdata.preference])
                    else:
                        mx_records.append(['MX', name, ardata.address,
                                          rdata.preference])
            return mx_records
        except Exception:
            return mx_records

    def get_ns(self):
        """
        Function for NS Record resolving. Returns all NS records. Returns also the IP
        address of the host both in IPv4 and IPv6. Returns an Array.
        """
        name_servers = []
        tcp = True if self._proto == "tcp" else False
        answer = self._res.query(self._domain, 'NS', tcp=tcp)
        if answer is not None:
            for aa in answer:
                name = aa.target.to_text()[:-1]
                ip_addrs = self.get_ip(name)
                for addresses in ip_addrs:
                    if re.search(r'^A', addresses[0]):
                        name_servers.append(['NS', name, addresses[2]])
        return name_servers

    def get_soa(self):
        """
        Function for SOA Record resolving. Returns all SOA records. Returns also the IP
        address of the host both in IPv4 and IPv6. Returns an Array.
        """
        soa_records = []
        tcp = True if self._proto == "tcp" else False
        querymsg = dns.message.make_query(self._domain, dns.rdatatype.SOA)
        try:
            if tcp:
                response = dns.query.tcp(querymsg, self._res.nameservers[0], self._res.timeout)
            else:
                response = dns.query.udp(querymsg, self._res.nameservers[0], self._res.timeout)

            if len(response.authority) > 0:
                answers = response.authority
            elif len(response.answer) > 0:
                answers = response.answer
            else:
                return soa_records

            for rdata in answers:
                name = None
                if isinstance(rdata[0], dns.rdtypes.ANY.SOA.SOA):
                    name = rdata[0].mname.to_text()
                else:
                    continue

                if name:
                    ipv4_answers = self._res.query(name, 'A', tcp=tcp)
                    for ardata in ipv4_answers:
                        if name.endswith('.'):
                            soa_records.append(['SOA', name[:-1], ardata.address])
                        else:
                            soa_records.append(['SOA', name, ardata.address])
        except (dns.resolver.NXDOMAIN, dns.exception.Timeout, dns.resolver.NoAnswer, socket.error, dns.query.BadResponse):
            print_error('Error while resolving SOA record.')
            return soa_records

        try:
            for rdata in answers:
                name = None
                if isinstance(rdata, dns.rdtypes.ANY.SOA.SOA):
                    name = rdata.mname.to_text()

                if name:
                    ipv6_answers = self._res.query(name, 'AAAA', tcp=tcp)
                    for ardata in ipv6_answers:
                        if name.endswith('.'):
                            soa_records.append(['SOA', name[:-1], ardata.address])
                        else:
                            soa_records.append(['SOA', name, ardata.address])

            return soa_records
        except Exception:
            return soa_records

    def get_spf(self):
        """
        Function for SPF Record resolving returns the string with the SPF definition.
        Prints the string for the SPF Record and Returns the string
        """
        spf_record = []
        tcp = True if self._proto == "tcp" else False
        try:
            answers = self._res.query(self._domain, 'SPF', tcp=tcp)
            for rdata in answers:
                name = bytes.join(b'', rdata.strings).decode('utf-8')
                spf_record.append(['SPF', name])
        except Exception:
            return None

        return spf_record

    def get_txt(self, target=None):
        """
        Function for TXT Record resolving returns the string.
        """
        txt_record = []
        tcp = True if self._proto == "tcp" else False
        if target is None:
            target = self._domain
        try:
            answers = self._res.query(target, 'TXT', tcp=tcp)
            for rdata in answers:
                string = bytes.join(b'', rdata.strings).decode('utf-8')
                txt_record.append(['TXT', target, string])
        except Exception:
            return []

        return txt_record

    def get_ptr(self, ipaddress):
        """
        Function for resolving PTR Record given it's IPv4 or IPv6 Address.
        """
        found_ptr = []
        tcp = True if self._proto == "tcp" else False
        n = dns.reversename.from_address(ipaddress)
        try:
            answers = self._res.query(n, 'PTR', tcp=tcp)
            for a in answers:
                if a.target.to_text().endswith('.'):
                    found_ptr.append(['PTR', a.target.to_text()[:-1], ipaddress])
                else:
                    found_ptr.append(['PTR', a.target.to_text(), ipaddress])
            return found_ptr
        except Exception:
            return None

    def get_srv(self, host):
        """
        Function for resolving SRV Records.
        """
        record = []
        tcp = True if self._proto == "tcp" else False
        try:
            answers = self._res.query(host, 'SRV', tcp=tcp)
            for a in answers:
                if a.target.to_text().endswith('.'):
                    target = a.target.to_text()[:-1]
                else:
                    target = a.target.to_text()

                ips = self.get_ip(target)

                if ips:
                    for ip in ips:
                        if re.search('(^A|AAAA)', ip[0]):
                            record.append(['SRV', host, target, ip[2],
                                          str(a.port), str(a.weight)])

                else:
                    record.append(['SRV', host, target, "no_ip",
                                  str(a.port), str(a.weight)])
        except Exception:
            return record
        return record

    def get_nsec(self, host):
        """
        Function for querying for a NSEC record and retrieving the rdata object.
        This function is used mostly for performing a Zone Walk against a zone.
        """
        tcp = True if self._proto == "tcp" else False
        answer = self._res.query(host, 'NSEC', tcp=tcp)
        return answer

    def from_wire(self, xfr, zone_factory=Zone, relativize=True):
        """
        Method for turning returned data from a DNS AXFR in to RRSET, this method will not perform a
        check origin on the zone data as the method included with dnspython
        """
        z = None
        for r in xfr:
            if z is None:
                if relativize:
                    origin = r.origin
                else:
                    origin = r.answer[0].name
                rdclass = r.answer[0].rdclass
                z = zone_factory(origin, rdclass, relativize=relativize)
            for rrset in r.answer:
                znode = z.nodes.get(rrset.name)
                if not znode:
                    znode = z.node_factory()
                    z.nodes[rrset.name] = znode
                zrds = znode.find_rdataset(rrset.rdclass, rrset.rdtype,
                                           rrset.covers, True)
                zrds.update_ttl(rrset.ttl)
                for rd in rrset:
                    try:
                        rd.choose_relativity(z.origin, relativize)
                    except AttributeError:
                        pass
                    zrds.add(rd)

        return z

    def zone_transfer(self):
        """
        Function for testing for zone transfers for a given Domain, it will parse the
        output by record type.
        """
        # if anyone reports a record not parsed I will add it, the list is a long one
        # I tried to include those I thought where the most common.

        zone_records = []
        ns_records = []
        print_status(f'Checking for Zone Transfer for {self._domain} name servers')

        # Find SOA for Domain
        print_status("Resolving SOA Record")
        try:
            soa_srvs = self.get_soa()
            for s in soa_srvs:
                print(s)
                print_good("\t {0}".format(" ".join(s)))
                ns_records.append(s[2])
        except Exception:
            print_error("Could not obtain the domains SOA Record.")
            return

        # Find NS for Domain
        print_status("Resolving NS Records")
        try:
            ns_srvs = []
            ns_srvs = self.get_ns()
            for ns in ns_srvs:
                ns_ip = ''.join(ns[2])
                ns_records.append(ns_ip)
        except Exception as e:
            print_error(f"Could not Resolve NS Records: {e}")

        # Remove duplicates
        print_status("Removing any duplicate NS server IP Addresses...")
        ns_records = list(set(ns_records))
        # Test each NS Server
        for ns_srv in ns_records:
            print_status(" ")
            print_status(f'Trying NS server {ns_srv}')
            if self.check_tcp_dns(ns_srv):
                print_good(f'{ns_srvs} Has port 53 TCP Open')
                try:
                    zone = self.from_wire(dns.query.xfr(ns_srv, self._domain))
                    print_good('Zone Transfer was successful!!')
                    zone_records.append({'type': 'info', 'zone_transfer': 'success', 'ns_server': ns_srv})
                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.SOA):
                        for rdata in rdataset:
                            for mn_ip in self.get_ip(rdata.mname.to_text()):
                                if re.search(r'^A', mn_ip[0]):
                                    zone_records.append({'zone_server': ns_srv, 'type': 'SOA',
                                                         'mname': rdata.mname.to_text()[:-1], 'address': mn_ip[2]})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.NS):
                        for rdata in rdataset:

                            # Check if target is only the host name or a full FQDN.
                            # If only a hostname we will append the domain name of the
                            # Zone being transfered.
                            target = rdata.target.to_text()
                            target_split = target.split('.')
                            appended = False
                            if len(target_split) == 1:
                                target = target + '.' + self._domain
                                appended = True

                            for n_ip in self.get_ip(target):
                                if re.search(r'^A', n_ip[0]):

                                    if appended:
                                        zone_records.append({'zone_server': ns_srv, 'type': 'NS',
                                                             'target': target, 'address': n_ip[2]})
                                    else:
                                        if rdata.target.to_text().endswith('.'):
                                            target = rdata.target.to_text()[:-1]
                                        else:
                                            target = rdata.target.to_text()

                                        zone_records.append({'zone_server': ns_srv, 'type': 'NS',
                                                             'target': target, 'address': n_ip[2]})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.TXT):
                        for rdata in rdataset:
                            rdata = [string.decode() for string in rdata.strings]
                            zone_records.append({'zone_server': ns_srv, 'type': 'TXT',
                                                'strings': ''.join(rdata)})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.SPF):
                        for rdata in rdataset:
                            #print_status('\t SPF {0}'.format(''.join(rdata.strings)))
                            zone_records.append({'zone_server': ns_srv, 'type': 'SPF',
                                                 'strings': ''.join(rdata.strings)})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.PTR):
                        for rdata in rdataset:
                            for n_ip in self.get_ip(rdata.target.to_text() + "." + self._domain):
                                if re.search(r'^A', n_ip[0]):
                                    zone_records.append({'zone_server': ns_srv, 'type': 'PTR',
                                                         'name': rdata.target.to_text() + "." + self._domain, 'address': n_ip[2]})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.MX):
                        for rdata in rdataset:
                            for e_ip in self.get_ip(rdata.exchange.to_text()):
                                if re.search(r'^A', e_ip[0]):
                                    if rdata.exchange.to_text().endswith('.'):
                                        exchange = rdata.exchange.to_text()[:-1]
                                    else:
                                        exchange = rdata.exchange.to_text()
                                    print_status('\t MX {0} {1} {2}'.format(str(name) + '.' + self._domain,
                                                 exchange, e_ip[2]))
                                zone_records.append({'zone_server': ns_srv, 'type': 'MX',
                                                     'name': str(name) + '.' + self._domain,
                                                     'exchange': exchange,
                                                     'address': e_ip[2]})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.AAAA):
                        for rdata in rdataset:
                            zone_records.append({'zone_server': ns_srv, 'type': 'AAAA',
                                                'name': str(name) + '.' + self._domain,
                                                 'address': rdata.address})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.A):
                        for rdata in rdataset:
                            zone_records.append({'zone_server': ns_srv, 'type': 'A',
                                                'name': str(name) + '.' + self._domain,
                                                 'address': rdata.address})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.CNAME):
                        for rdata in rdataset:
                            for t_ip in self.get_ip(rdata.target.to_text()):
                                if re.search(r'^A', t_ip[0]):
                                    if rdata.target.to_text().endswith('.'):
                                        ctarget = rdata.target.to_text()[:-1]
                                    else:
                                        ctarget = rdata.target.to_text()
                                    zone_records.append({'zone_server': ns_srv, 'type': 'CNAME',
                                                         'name': str(name) + '.' + self._domain,
                                                         'target': str(ctarget),
                                                         'address': t_ip[2]})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.SRV):
                        for rdata in rdataset:
                            ip_list = self.get_ip(rdata.target.to_text())
                            if ip_list:
                                for t_ip in self.get_ip(rdata.target.to_text()):
                                    if re.search(r'^A', t_ip[0]):
                                        zone_records.append({'zone_server': ns_srv, 'type': 'SRV',
                                                            'name': str(name) + '.' + self._domain,
                                                             'target': rdata.target.to_text()[:-1],
                                                             'address': t_ip[2],
                                                             'port': str(rdata.port),
                                                             'weight': str(rdata.weight)})
                            else:
                                zone_records.append({'zone_server': ns_srv, 'type': 'SRV',
                                                    'name': str(name) + '.' + self._domain,
                                                     'target': rdata.target.to_text()[:-1],
                                                     'address': "no_ip",
                                                     'port': str(rdata.port),
                                                     'weight': str(rdata.weight)})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.HINFO):
                        for rdata in rdataset:
                            zone_records.append({'zone_server': ns_srv, 'type': 'HINFO',
                                                'cpu': rdata.cpu.decode(), 'os': rdata.os.decode()})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.WKS):
                        for rdata in rdataset:
                            zone_records.append({'zone_server': ns_srv, 'type': 'WKS',
                                                'address': rdata.address, 'bitmap': rdata.bitmap,
                                                 'protocol': rdata.protocol})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.RP):
                        for rdata in rdataset:
                            zone_records.append({'zone_server': ns_srv, 'type': 'RP',
                                                'mbox': rdata.mbox.to_text(), 'txt': rdata.txt.to_text()})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.AFSDB):
                        for rdata in rdataset:
                            zone_records.append({'zone_server': ns_srv, 'type': 'AFSDB',
                                                'subtype': str(rdata.subtype), 'hostname': rdata.hostname.to_text()})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.LOC):
                        for rdata in rdataset:
                            zone_records.append({'zone_server': ns_srv, 'type': 'LOC',
                                                'coordinates': rdata.to_text()})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.X25):
                        for rdata in rdataset:
                            zone_records.append({'zone_server': ns_srv, 'type': 'X25',
                                                'address': rdata.address})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.ISDN):
                        for rdata in rdataset:
                            zone_records.append({'zone_server': ns_srv, 'type': 'ISDN',
                                                 'address': rdata.address})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.RT):
                        for rdata in rdataset:
                            zone_records.append({'zone_server': ns_srv, 'type': 'X25',
                                                 'address': rdata.address})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.NSAP):
                        for rdata in rdataset:
                            zone_records.append({'zone_server': ns_srv, 'type': 'NSAP',
                                                 'address': rdata.address})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.NAPTR):
                        for rdata in rdataset:
                            zone_records.append({'zone_server': ns_srv, 'type': 'NAPTR',
                                                 'order': str(rdata.order),
                                                 'preference': str(rdata.preference),
                                                 'regex': rdata.regexp.decode(),
                                                 'replacement': rdata.replacement.to_text(),
                                                 'service': rdata.service.decode()})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.CERT):
                        for rdata in rdataset:
                            zone_records.append({'zone_server': ns_srv, 'type': 'CERT',
                                                 'algorithm': rdata.algorithm,
                                                 'certificate': rdata.certificate,
                                                 'certificate_type': rdata.certificate_type,
                                                 'key_tag': rdata.key_tag})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.SIG):
                        for rdata in rdataset:
                            zone_records.append({'zone_server': ns_srv, 'type': 'SIG',
                                                'algorithm': algorithm_to_text(rdata.algorithm),
                                                 'expiration': rdata.expiration,
                                                 'inception': rdata.inception,
                                                 'key_tag': rdata.key_tag,
                                                 'labels': rdata.labels,
                                                 'original_ttl': rdata.original_ttl,
                                                 'signature': rdata.signature,
                                                 'signer': str(rdata.signer),
                                                 'type_covered': rdata.type_covered})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.RRSIG):
                        for rdata in rdataset:
                            zone_records.append({'zone_server': ns_srv, 'type': 'RRSIG',
                                                 'algorithm': algorithm_to_text(rdata.algorithm),
                                                 'expiration': rdata.expiration,
                                                 'inception': rdata.inception,
                                                 'key_tag': rdata.key_tag,
                                                 'labels': rdata.labels,
                                                 'original_ttl': rdata.original_ttl,
                                                 'signature': rdata.signature,
                                                 'signer': str(rdata.signer),
                                                 'type_covered': rdata.type_covered})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.DNSKEY):
                        for rdata in rdataset:
                            zone_records.append({'zone_server': ns_srv, 'type': 'DNSKEY',
                                                 'algorithm': algorithm_to_text(rdata.algorithm),
                                                 'flags': rdata.flags,
                                                 'key': dns.rdata._hexify(rdata.key),
                                                 'protocol': rdata.protocol})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.DS):
                        for rdata in rdataset:
                            zone_records.append({'zone_server': ns_srv, 'type': 'DS',
                                                'algorithm': algorithm_to_text(rdata.algorithm),
                                                 'digest': dns.rdata._hexify(rdata.digest),
                                                 'digest_type': rdata.digest_type,
                                                 'key_tag': rdata.key_tag})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.NSEC):
                        for rdata in rdataset:
                            zone_records.append({'zone_server': ns_srv, 'type': 'NSEC',
                                                 'next': rdata.next.to_text()})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.NSEC3):
                        for rdata in rdataset:
                            zone_records.append({'zone_server': ns_srv, 'type': 'NSEC3',
                                                 'algorithm': algorithm_to_text(rdata.algorithm),
                                                 'flags': rdata.flags,
                                                 'iterations': rdata.iterations,
                                                 'salt': dns.rdata._hexify(rdata.salt)})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.NSEC3PARAM):
                        for rdata in rdataset:
                            zone_records.append({'zone_server': ns_srv, 'type': 'NSEC3PARAM',
                                                 'algorithm': algorithm_to_text(rdata.algorithm),
                                                 'flags': rdata.flags,
                                                 'iterations': rdata.iterations,
                                                 'salt': dns.rdata._hexify(rdata.salt)})

                    for (name, rdataset) in zone.iterate_rdatasets(dns.rdatatype.IPSECKEY):
                        for rdata in rdataset:
                            zone_records.append({'zone_server': ns_srv, 'type': 'IPSECKEY',
                                                 'algorithm': algorithm_to_text(rdata.algorithm),
                                                 'gateway': rdata.gateway,
                                                 'gateway_type': rdata.gateway_type,
                                                 'key': dns.rdata._hexify(rdata.key),
                                                 'precedence': rdata.precedence})
                except Exception as e:
                    print_error('Zone Transfer Failed!')
                    print_error(e)
                    import traceback as t
                    t.print_exc()
                    zone_records.append({'type': 'info', 'zone_transfer': 'failed', 'ns_server': ns_srv})
            else:
                print_error('Zone Transfer Failed for {0}!'.format(ns_srv))
                print_error('Port 53 TCP is being filtered')
                zone_records.append({'type': 'info', 'zone_transfer': 'failed', 'ns_server': ns_srv})
        return zone_records


def main():
    resolver = DnsHelper('google.com')
    print(resolver.get_a("www.yahoo.com"))
    print(resolver.get_aaaa('baddata-cname-to-baddata-aaaa.test.dnssec-tools.org'))
    print(resolver.get_mx())
    print(resolver.get_ip('www.google.com'))
    print(resolver.get_txt("3rdparty1._spf.paypal.com"))
    print(resolver.get_ns())
    print(resolver.get_soa())
    print(resolver.get_txt())
    print(resolver.get_spf())
    tresolver = DnsHelper('google.com')
    print(tresolver.zone_transfer())


if __name__ == "__main__":
    main()
