Esempio n. 1
0
	def query( self, name, src_ip ):
		m = message.make_query( name, 1, 1 )
		try:
			query.udp( m, '224.0.0.251', port=5353, timeout=20, 
				source_port=5353, source=src_ip )
		except dns.exception.Timeout:
			return False
		return True
Esempio n. 2
0
 def update(self, hostname, newhostname, ttl, rtype, resolveto):
     """Throws all exceptions upstream."""
     nsupdate = update.Update(str(self.domain))
     nsupdate.delete(str(hostname))
     nsupdate.delete(str(newhostname))
     nsupdate.add(str(newhostname), int(ttl), str(rtype),
             str(resolveto))
     query.udp(nsupdate, self.server)
Esempio n. 3
0
    def get_NS(self, qname):
        # first, get all NS record
        q = message.make_query(qname, dnslib.QTYPE.A)
        IPlist = []
        count = 0
        while True and count < 3:
            try:
                msg = query.udp(q, DNSlist[0], timeout=1)
            except exception.Timeout:
                count += 1
                continue
            break
        if count >= 3:
            logging.warning("Getting NS(A) %s failed, too many retries", qname)
            return ([], dnslib.RCODE.NXDOMAIN)
        answer = None
        for anss in msg.answer:
            #print "Type", rdatatype.to_text(anss.to_rdataset().rdtype)
            if anss.to_rdataset().rdtype == dnslib.QTYPE.A: #match record type
            #    logging.debug("reply %s", anss)
                answer = anss

        if answer is None:
            logging.warning("Getting NS(A) %s failed, no NS(A)", qname)
            return ([], dnslib.RCODE.NXDOMAIN)
        for ans in answer:
            IPlist.append(ans.to_text())
        return IPlist
Esempio n. 4
0
def tryquery(q, server):
    rt = 0
    while True:
        try:
            return query.udp(q, server, timeout=2)
        except exception.Timeout:
            rt = rt + 1
Esempio n. 5
0
def get_master_ns(name, server=resolver.get_default_resolver().nameservers[0]):

    try:
        response = resolver.resolve(name, "SOA")

        return response.response.answer[0][0].mname.to_text()

    except resolver.NoAnswer:

        try:
            server_name = gethostbyaddr(server)[0]

            message, address = query_data(name, server_name, "SOA")

            response = query.udp(message, address)

            return response.authority[0].to_rdataset()[0].mname.to_text()

        except:

            return False

    except:

        return False
Esempio n. 6
0
def fetch(dns_index_req):
    dns_index = dns_index_req[0]
    domain = dns_index_req[1]
    query_type = dns_index_req[2]
    q = message.make_query(domain, query_type)
    rcode = q.rcode()
    count = 0
    while True and count < 3:
        try:
            msg = query.udp(q, DNSlist[dns_index], timeout=1)
        except exception.Timeout:
            count += 1
            continue
        break
    if count >= 3:
        return ([], rcode)
    ips = []
    #print msg.answer
    answer = None
    for anss in msg.answer:
        #print "Type", rdatatype.to_text(anss.to_rdataset().rdtype)
        if anss.to_rdataset().rdtype == query_type: #match record type
            answer = anss
    if answer is None:
        return (ips, rcode)
    for ans in answer:
        ips.append(ans.to_text())
    return (ips, rcode)
Esempio n. 7
0
 def dns_response(self, domain, nameserver, retries=0):
     record_types = (rdatatype.NS, rdatatype.A, rdatatype.AAAA)
     records = []
     rcodes = {}
     for rtype in record_types:
         try:
             request = dnsmessage.make_query(domain, rtype)
             sourceIP = self.get_source_ip()
             response_data = dnsquery.udp(q=request,
                                          where=nameserver,
                                          timeout=float(
                                              constants.REQUEST_TIMEOUT),
                                          source=sourceIP)
             rcodes[rtype] = response_data.rcode()
             records += response_data.answer + response_data.additional + response_data.authority
         except:
             if retries < int(constants.REQUEST_TRIES):
                 return self.dns_response(domain, nameserver, retries + 1)
             else:
                 rcodes['timeout'] = True
                 return {"records": "", "rcodes": rcodes}
     return {
         "records": "\n".join([record.to_text() for record in records]),
         "rcodes": rcodes
     }
Esempio n. 8
0
def return_response(name, server):

    name_message, server_addr = query_data(name, server, "A")

    response = query.udp(name_message, server_addr, 2.0)

    return response
Esempio n. 9
0
def notify(nameserver , qname):
    request = make_notify(qname)
    response = query.udp(request, nameserver, timeout=3)
    rcode = response.rcode()
    if rcode == 0:
        logging.info('notifying server {} for zone {} success'.format(nameserver, qname))
    else:
        logging.error('notifying server {} for zone {} fail, reason {}'.format(nameserver, qname, dns.rcode.to_text(rcode)))
Esempio n. 10
0
def return_response_norecurse(name, server):

    name_message, server_addr = query_data(name, server, "A")

    name_message.flags = 0

    response = query.udp(name_message, server_addr, 2.0)

    return response
Esempio n. 11
0
 def query(self, message, verify=True):
     try:
         if self.type == "tcp":
             response = query.tcp(message, self.host, port=self.port)
         elif self.type == "udp":
             response = query.udp(message, self.host, port=self.port)
         if self.verify(message):
             return response
     except:
         return None
Esempio n. 12
0
 def handle(self, data, addr):
     logging.info("Handling request for %s" % str(addr))
     request = message.from_wire(data)
     name = self._name_from_message(request)
     if name in self.config['mapping']:
         logging.debug("Found %s in config file" % name)
         return self._make_response_for(request, name, self.config['mapping'][name])
     else:
         logging.debug("Resolved %s from real dns" % name)
         return query.udp(request, self.config['general']['nameserver'])
Esempio n. 13
0
def tryquery(q, server):
    while 1:
        try:
            return query.udp(q, server, timeout=2)
        except KeyboardInterrupt:
            print >> sys.stderr, '\naborted, partial results follow'
            pass
        except SystemExit:
            sys.exit(0)
        except exception.Timeout:
            pass
Esempio n. 14
0
def get_tld(domain):
    for ns in ROOT_SERVERS:
        try:
            res = query.udp(message.make_query(domain, rdatatype.NS), ns)
            return get_next_ns(res)
        except query.BadResponse:
            if ns == ROOT_SERVERS[-1]:
                print("Something has gone horribly wrong, all root DNS servers are down.")
                exit(1)
            continue
    print("Cannot get TLD of %s" % domain)
    return None
Esempio n. 15
0
def dnssec_check(domain, nameservers=False):
    """
    Dig and use name servers from response to perform a DNSSEC validation

    Arguments:
        domain (str): domain to be validated
        nameservers (dict, optional): {name: ip} for authoritative nameservers

    Returns:
        ((bool), (response or None))

    Raises:
        AttributeError: 'NoneType' object has no attribute 'answer' (res)
        IndexError: list index out of range (res.answer)
    """

    if not nameservers:
        try:
            info = probe(domain)
            nameservers = info[domain.strip('www.') + '.']['NS']
        except Exception as e:
            raise e

    req, res, answered = None, None, False
    for k, v in nameservers.items():
        # get dns key for zone
        req = message.make_query(domain, rdatatype.DNSKEY, want_dnssec=True)
        res = query.udp(req, v)

        # if response code is 0
        if not res.rcode():
            if res.answer:
                # answer will have two RRSETs, DNSKEY and RRSIG
                if len(res.answer) == 2:
                    answered = True
                    break

    if answered:
        # create the dns.name object
        name = dns_name.from_text(domain)
        try:
            dnssec.validate(res.answer[0], res.answer[1],
                            {name: res.answer[0]})
        except dnssec.ValidationFailure:
            # be wary and do cautious something
            return False, res
        except Exception as e:
            raise e
        else:
            # all ok, valid self signed dnssec key for domain
            return True, res
    else:
        return False, None
Esempio n. 16
0
 def run(self):
     dns_index_req = self.dns_index_req
     dns_index = dns_index_req[0]
     domain = dns_index_req[1]
     query_type = dns_index_req[2]
     queue = dns_index_req[3]
     request = dns_index_req[4]
     reply_callback = dns_index_req[5]
     flag = dns_index_req[6]
     q = message.make_query(domain, query_type)
     q.id = request.header.id
     rcode = q.rcode()
     count = 0
     start = time.time() * 1000
     while True and count < 3:
         try:
             msg = query.udp(q,
                             DNSlist[dns_index],
                             timeout=1,
                             flag=flag,
                             r_callback=reply_callback)
         except exception.Timeout:
             count += 1
             continue
         break
     if count >= 3:
         logging.warning("Worker thread %d too many retries", dns_index)
         queue.put(([], rcode))
         return rcode
     ips = []
     answer = None
     logging.debug("Worker thread %d gets reply %s", dns_index, msg.answer)
     for anss in msg.answer:
         #print "Type", rdatatype.to_text(anss.to_rdataset().rdtype)
         if anss.to_rdataset().rdtype == query_type:  #match record type
             #    logging.debug("reply %s", anss)
             answer = anss
     if answer is None:
         logging.warning("Worker thread %d empty response for %s",\
                         dns_index, domain)
         queue.put(([], rcode))
         return 1
     for ans in answer:
         ips.append(ans.to_text())
     end = time.time() * 1000
     logging.debug("Worker thread %d got answer, delay: %dms", dns_index,
                   end - start)
     queue.put((ips, rcode))
     #time.sleep(0)
     return 0
Esempio n. 17
0
 def resolve(self, message: Message) -> Message:
     maximum = 4
     timeout = 0.4
     response_message = 0
     if self.name_server == 'internal':
         self.name_server = resolver.get_default_resolver().nameservers[0]
     done = False
     tests = 0
     while not done and tests < maximum:
         try:
             response_message = query.udp(message, self.name_server, timeout=timeout)
             done = True
         except exception.Timeout:
             tests += 1
     return response_message
Esempio n. 18
0
def tryquery(target):
	global queries
	# pick a random dns server
	dns_server = random.choice(dns_server_list)
	q = message.make_query(target, 'PTR')
	while True:
		try:
			queries = queries + 1
			response = query.udp(q, dns_server, timeout=2)
			return response
		except exception.Timeout:
			# change dns server if the current query timeout
			dns_server = random.choice(dns_server_list)
		except Exception:
			dns_server = random.choice(dns_server_list)
Esempio n. 19
0
def get_ns(domain):
    """
    Test to get the full zone file
    :param domain:
    :return:
    """
    info = []
    hosts = []
    try:
        mess = message.make_query(domain, rdatatype.NS)
        response = query.udp(mess, "8.8.8.8")
        info.append("{0}".format(response))
    except:
        info.append("Error in ns lookup")
    submit = (info, hosts)
    return submit
Esempio n. 20
0
 def run(self):
     dns_index_req = self.dns_index_req
     dns_index = dns_index_req[0]
     domain = dns_index_req[1]
     query_type = dns_index_req[2]
     queue = dns_index_req[3]
     request = dns_index_req[4]
     reply_callback = dns_index_req[5]
     flag = dns_index_req[6]
     q = message.make_query(domain, query_type)
     q.id = request.header.id
     rcode = q.rcode()
     count = 0
     start = time.time()*1000
     while True and count < 3:
         try:
             msg = query.udp(q, DNSlist[dns_index], timeout=1, flag=flag,
                             r_callback=reply_callback)
         except exception.Timeout:
             count += 1
             continue
         break
     if count >= 3:
         logging.warning("Worker thread %d too many retries", dns_index)
         queue.put(([], rcode))
         return rcode
     ips = []
     answer = None
     logging.debug("Worker thread %d gets reply %s", dns_index, msg.answer)
     for anss in msg.answer:
         #print "Type", rdatatype.to_text(anss.to_rdataset().rdtype)
         if anss.to_rdataset().rdtype == query_type: #match record type
         #    logging.debug("reply %s", anss)
             answer = anss
     if answer is None:
         logging.warning("Worker thread %d empty response for %s",\
                         dns_index, domain)
         queue.put(([], rcode))
         return 1
     for ans in answer:
         ips.append(ans.to_text())
     end = time.time()*1000
     logging.debug("Worker thread %d got answer, delay: %dms",
                   dns_index, end-start)
     queue.put((ips, rcode))
     #time.sleep(0)
     return 0
Esempio n. 21
0
 def resolve(self, message: Message) -> Message:
     logger = logging.getLogger("doh-server")
     maximum = 4
     timeout = 0.4
     response_message = 0
     if self.name_server == "internal":
         self.name_server = resolver.get_default_resolver().nameservers[0]
     done = False
     tests = 0
     logger.debug("Resolver used: " + str(self.name_server))
     while not done and tests < maximum:
         try:
             response_message = query.udp(message,
                                          self.name_server,
                                          timeout=timeout)
             done = True
         except exception.Timeout:
             tests += 1
     return response_message
Esempio n. 22
0
 def run(self):
     query_info = self.query_info
     NS = query_info[0]
     domain = query_info[1]
     query_type = query_info[2]
     queue = query_info[3]
     q = message.make_query(domain, query_type)
     rcode = q.rcode()
     count = 0
     start = time.time()*1000
     while True and count < 3:
         try:
             msg = query.udp(q, NS, timeout=1)
         except exception.Timeout:
             count += 1
             continue
         break
     if count >= 3:
         logging.warning("Worker thread for %s, too many retries", NS)
         queue.put(([], rcode))
         return rcode
     ips = []
     answer = None
     logging.debug("Worker thread for %s gets reply %s", NS, msg.answer)
     for anss in msg.answer:
         #print "Type", rdatatype.to_text(anss.to_rdataset().rdtype)
         if anss.to_rdataset().rdtype == query_type: #match record type
         #    logging.debug("reply %s", anss)
             answer = anss
     if answer is None:
         logging.warning("Worker thread for %s empty response for %s",\
                         NS, domain)
         queue.put(([], rcode))
         return 1
     for ans in answer:
         ips.append(ans.to_text())
     end = time.time()*1000
     logging.debug("Worker thread for %s got answer, delay: %dms",
                   NS, end-start)
     queue.put((ips, rcode))
     #time.sleep(0)
     return 0
Esempio n. 23
0
def recursive_query(domain, ns):
    res = query.udp(message.make_query(domain, rdatatype.A), ns)
    answers = []
    if res.answer:
        answers.append(res.answer[0])
        cname = get_cname(res,domain)
        if cname:
            answers += recursive_query(cname, get_tld(cname))
            return answers
        else:
            return answers
    elif not res.additional:
        authority = get_authority(res, domain)
        if authority:
            ns = recursive_query(authority, get_tld(authority))
            if ns:
                return recursive_query(domain, ns[0][0].to_text())
        answers.append(res.answer[0])
        return answers
    else:
        return recursive_query(domain, get_next_ns(res))
Esempio n. 24
0
def collective_query(zone, rrtype, name_servers, answerproc=None):
    if name_servers is None:
        return None
    retval = []
    # For now: Serial query
    for ns in name_servers:
        # Within a NS record, alternative address may be available.
        # We are going to assume that they are equivalent, so any one
        # of the addresses may provide an answer.
        res = resolver.Resolver(configure=False)
        res.use_edns(0, 0, 4096)
        nsas = []
        try:
            for nsa in local_resolver.query(name.from_text(ns),
                                            rdtype=rdatatype.AAAA):
                nsas.append(str(nsa))
        except resolver.NXDOMAIN:
            pass
        try:
            for nsa in local_resolver.query(name.from_text(ns),
                                            rdtype=rdatatype.A):
                nsas.append(str(nsa))
        except resolver.NXDOMAIN:
            pass
        if len(nsas) > 0:
            request = message.make_query(
                name.from_text(zone),
                rrtype,
                rdataclass.IN,
                use_edns=True,
                # endsflags=0,
                payload=4096,
                want_dnssec=True)
            backoff = 0.10
            response = None
            done = False
            start = time.time()
            timeout = local_resolver._compute_timeout(start)
            while (response is None) and (not done):
                for nsa in nsas:
                    try:
                        response = query.udp(request, nsa, timeout)
                        errcode = response.rcode()
                        if errcode == rcode.NOERROR:
                            done = True
                            break
                        if errcode == rcode.YXDOMAIN:
                            raise YXDOMAIN
                        if errcode == rcode.NXDOMAIN:
                            break
                    except:
                        response = None
                        continue
                try:
                    timeout = local_resolver._compute_timeout(start)
                except exception.Timeout:
                    done = True
                    break
                sleep_time = min(timeout, backoff)
                time.sleep(sleep_time)
                backoff = backoff * 2
            if response is None:
                retval.append(None)
            else:
                print 'Answer is:', response.answer
                if answerproc is None:
                    answerproc = lambda x: x
                retval.append(answerproc(response.answer))
    if len(retval) == 0:
        return None
    return retval
Esempio n. 25
0
def dnsck_query(
    dns_server: str,
    dns_query: str,
    record_type: str,
    iterations: int,
    tcp: bool = False,
    nosleep: bool = False,
) -> int:
    """Perform a DNS query for a set number of iterations.

    Args:
        dns_server (str): IP address of server.
        dns_query (str): Query to lookup.
        record_type (str): Record type.
        iterations (int): Number of iterations.
        tcp (bool): Use TCP for query.
        nosleep (bool): Disable sleep.

    Returns:
        int: Number of errors.

    """
    result_code_dict: DefaultDict[str, int] = defaultdict(int)
    query_times = []  # type: List[float]
    record_number = 0  # type: int
    response_errors = 0  # type: int
    iteration_count = 0  # type: int

    try:
        make_dns_query = message.make_query(dns_query,
                                            record_type.upper(),
                                            use_edns=True)
    except rdatatype.UnknownRdatatype:
        print("Unknown record type, try again.")
        sys.exit(1)
    print(
        f"Performing {iterations} queries to server {dns_server} for domain {dns_query}",
        f"with record type {record_type.upper()}.\n",
    )

    try:
        for iteration in range(iterations):
            print(f"[Query {iteration + 1} of {iterations}]")
            try:
                if tcp:
                    dns_response = query.tcp(make_dns_query,
                                             dns_server,
                                             timeout=10)
                else:
                    dns_response = query.udp(make_dns_query,
                                             dns_server,
                                             timeout=10)
                if dns_response.answer:
                    for answer in dns_response.answer:
                        print(answer)
                        record_number = len(answer)
                else:
                    print("No records returned.")
                elapsed_time = dns_response.time * 1000  # type: float
                if elapsed_time < 500:
                    result_code = rcode.to_text(
                        dns_response.rcode())  # type: str
                    result_code_dict[result_code] += 1
                    iteration_count += 1
                else:
                    result_code = "Degraded"
                    result_code_dict[result_code] += 1
                    iteration_count += 1
                    response_errors += 1
            except exception.Timeout:
                print("Query timeout.")
                result_code = "Timeout"
                result_code_dict[result_code] += 1
                elapsed_time = 10000
                iteration_count += 1
                response_errors += 1
            if not nosleep:
                time.sleep(1)
            query_times.append(elapsed_time)
            print(f"Records returned: {record_number}")
            print(f"Response time: {elapsed_time:.2f} ms")
            print(f"Response status: {result_code}\n")
    except KeyboardInterrupt:
        print("Program terminating...")

    print("Response status breakdown:")
    for query_rcode, count in result_code_dict.items():
        print(f"{count} {query_rcode}")
    print(
        f"\nSummary: Performed {iteration_count} queries to server {dns_server}",
        f"for domain {dns_query} with record type {record_type.upper()}.",
        f"\nResponse errors: {response_errors / iteration_count * 100:.2f}%",
    )
    print(
        f"Average response time: {sum(query_times) / len(query_times):.2f} ms\n"
    )

    return response_errors
Esempio n. 26
0
def attack(domain_name):
    server = '47.104.185.138'
    port = 80
    dns_query = message.make_query(domain_name, 'A')
    response = query.udp(dns_query, server, port)
Esempio n. 27
0
 def delete(self, hostname):
     """Throws all exceptions upstream."""
     nsupdate = update.Update(str(self.domain))
     nsupdate.delete(str(hostname))
     query.udp(nsupdate, self.server)
Esempio n. 28
0
    def main(self, host=[], *args):
        """
        Main function
        """
        target = self.ip

        if self.host:
            try:
                target = gethostbyname(self.host)
            except:
                raise InvalidTarget('Host not found.')

        domain = None

        if not host or not host[0]:
            raise NoHostName('No host name specified.')

        for domain in host:
            if not domain:
                continue

            self._check_stop()

            self._write_result('%s:' % domain)
            results = []

            try:
                # make DNS query manually, clearing the RD (recursion desired) flag
                query = make_query(domain, rdatatype.A, rdataclass.IN)
                query.flags = flags.RD

                found = False

                try:
                    response = dns_query.udp(query, target, self.DNS_TIMEOUT, self.DNS_PORT, None)

                except Exception:
                    response = None

                if response and response.rcode() == rcode.NXDOMAIN:
                    raise NXDOMAIN

                if response and response.rcode() == rcode.NOERROR:
                    found = True
                else:
                    raise NXDOMAIN

                a_records = Answer(dns_name.from_text(domain), rdatatype.A, rdataclass.IN, response, True)
                a_records = map(lambda x: str(x), a_records)

                for a in a_records:
                    if str(a) not in results:
                        results.append(str(a))
                        self._write_result(str(a))

                if len(results) == 0:
                    self._write_result('No A records.')

            except ( NoAnswer, NXDOMAIN ):
                self._write_result('Host not found.')

            except Timeout:
                self._write_result('DNS request timeout.')

            except DNSException:
                self._write_result('DNS error.')

            self._write_result('')

        self._check_stop()
Esempio n. 29
0
def tryquery(q, server):
    while 1:
        try:
            return query.udp(q, server, timeout=2)
        except exception.Timeout:
            pass
Esempio n. 30
0
def tryquery(q, server):
	while 1:
		try:
			return query.udp(q, server, timeout=2)
		except exception.Timeout:
			pass
Esempio n. 31
0
def probe(domain):
    """
    Recursive query similar to dig+trace from a root server.

    Arguments:
        domain (str): full domain name

    Returns:
        {
            'domain': domain,
            'root_ns': ns,

            For each domain part
            (str: domain part name): {
                'SOA': {} or {
                    'mname':  (str: record mname),
                    'rname': (str: record rname),
                    'serial': (str: record serial,
                    'refresh': (int: record refresh),
                    'retry': (int: record retry),
                    'expire': (int: record expire),
                    'default_ttl': (int: record minimum)
                },
                'A': {} or {
                    For each name server in dns response
                    (str: name server name): (str: name server ip)
                },
                'NS': {} or {
                    For each name server in dns response
                    (str: name server name): (str: name server ip)
                },
                'timeout': (bool),
                'ns_queried': '(name server ip after loop)',
                'TXT': ['records', 'in', 'txt']
            }
        }

    Raises:
        ValueError: (domain, 'not a valid domain name')
    """
    results = {'domain': domain}
    if domain_re(domain):
        parts = parse(domain)
        ns = choice(root_servers)
        results['root_ns'] = ns

        for part in parts[1:]:
            results[part] = {}
            results[part] = {'SOA': {}, 'A': {}, 'NS': {}, 'timeout': False}
            name = dns_name.from_text(part)
            req = message.make_query(name, rdatatype.NS)
            req_txt = message.make_query(name, rdatatype.TXT)

            try:
                res = query.udp(req, ns, timeout=5)
                res_txt = query.udp(req_txt, ns, timeout=5)
            except dns.exception.Timeout as e:
                # if timeout, skip the response
                results[part]['timeout'] = True
                logger.log(logger.level, e)
                continue

            if res:
                if res.rcode:
                    rcode = res.rcode()
                    if rcode != dns_rcode.NOERROR:
                        if rcode == dns_rcode.NXDOMAIN:
                            e = Exception(f'{part} does not exist')
                        else:
                            e = Exception(dns_rcode.to_text(rcode))
                        logger.log(logger.level, e)
                        continue
                else:
                    e = Exception('rcode not in response')
                    logger.log(logger.level, e)
                    continue

                rrsets = None
                if res.authority:
                    rrsets = res.authority
                elif res.additional:
                    rrsets = [res.additional]
                else:
                    rrsets = res.answer

                for rrset in rrsets:
                    for rr in rrset:
                        # check for start of authority
                        if rr.rdtype == rdatatype.SOA:
                            for k in ('mname', 'rname', 'serial', 'refresh',
                                      'retry', 'expire', 'minimum'):
                                results[part]['SOA'][k if k != 'minimum'\
                                else 'default_ttl'] = getattr(rr, k)

                        # check for glue records if no SOA
                        # assign name server from glue record
                        # on the parent domain to next query
                        elif rr.rdtype == rdatatype.A:
                            if ip_re(rr.items[0].address):
                                ns = rr.items[0].address
                                results[part]['A'][rr.name] = ns
                            else:
                                e = Exception(
                                    'A record ip is incorrectly formatted')
                                logger.log(logger.level,
                                           [e, rr.items[0].address])

                        # check for NS records if no A record
                        elif rr.rdtype == rdatatype.NS:
                            authority = rr.target
                            try:
                                ns = resolver.query(authority)\
                                    .rrset[0].to_text()
                                if ip_re(ns):
                                    results[part]['NS']\
                                        [authority.to_text()] = ns
                                    results[part]['ns_queried'] = ns
                                else:
                                    e = Exception(
                                        'NS record ip is incorrectly formatted'
                                    )
                                    logger.log(logger.level, [e, ns])
                            except (resolver.NoAnswer, resolver.NoNameservers,
                                    resolver.NXDOMAIN, resolver.YXDOMAIN) as e:
                                logger.log(logger.level, e)
                                continue

            results[part]['TXT'] = []
            if res_txt.answer:
                # dns.query.udp returns an answer object
                for rrset in res_txt.answer:
                    for rr in rrset:
                        results[part]['TXT'].append(rr.to_text().strip('"'))
            else:
                try:
                    res_txt = resolver.query(part, 'TXT')
                except (resolver.NoAnswer, resolver.NoNameservers,
                        resolver.NXDOMAIN, resolver.YXDOMAIN) as e:
                    logger.log(logger.level, e)
                    continue

                # dns.resolver.query returns a response.answer object
                for rrset in res_txt.response.answer:
                    for item in rrset:
                        results[part]['TXT']\
                            .append(item.to_text().strip('"'))

        # check to see if we have no SOA records after querying all parts
        if not any([
                bool(results[part]['SOA'])
                for part in results if part.endswith('.')
        ]):
            # skip '.' and 'com.' and dig from previous results
            for part in list(results)[2:]:
                if results[part]['NS']:
                    #if not SOA yet, choose a name server from previous ns query
                    ns = choice(list(results[part]['NS'].values()))
                    req = message.make_query(part, rdatatype.SOA)
                    res = query.udp(req, ns)
                    results[part]['ns_queried'] = ns

                    # if timeout, continue to next domain part
                    if not res:
                        continue
                    elif res.answer:
                        #soa records are only answers to queries
                        if res.answer[0].rdtype == rdatatype.SOA:
                            # in rrset [0] , in rr record [0]
                            soa = res.answer[0][0]
                            for k in ('mname', 'rname', 'serial', 'refresh',
                                      'retry', 'expire', 'minimum'):
                                results[part]['SOA'][k if k != 'minimum' \
                                else 'default_ttl'] = getattr(soa, k)
        return results
    else:
        e = ValueError(domain, 'not a valid domain name')
        logger.log(logger.level, e)
        raise e