15

I'm looking to verify domain ownership via a script, specifically a Python script, and would like know how to lookup the value of a DNS TXT entry. I know there are services and websites out there for this, but I would like to do it with a script.

orokusaki
  • 55,146
  • 59
  • 179
  • 257

5 Answers5

24

This is easy using dnspython. Here is an example:

import dns.resolver
print dns.resolver.resolve("aaa.asdflkjsadf.notatallsuspicio.us","TXT").response.answer[0][-1].strings[0]

This gives the following output:

PnCcKpPiGlLfApDbDoEcBbPjIfBnLpFaAaObAaAaMhNgNbIfPbHkMiEfPpGgJfOcPnLdDjBeHkOjFjIbPbIoKhIjHfJlAhAhFgGbGgNlMgKmFkLgNfBjMbCoBeNbGeOnAeHgLmKoFlLhLmDcKlEdEbDpFeHkFaBlGnHiOnChIoMlIhBgOnFfKoEhDnFkKfDaMgHbJhMgPgMjGiAoJpKjKkPaIcAdGiMbIbBbAfEiKjNbCeFoElKgOePmGjJaImL

Another option is to use dig in subprocess:

import subprocess

print subprocess.Popen(["dig","-t","txt","aaa.asdflkjsadf.notatallsuspicio.us","+short"], stdout=subprocess.PIPE).communicate()[0] 
ivall
  • 27
  • 7
jordanm
  • 33,009
  • 7
  • 61
  • 76
  • 2
    you could use `subprocess.check_output()` instead of `.communicate()` here – jfs Jul 29 '12 at 03:29
  • @J.F.Sebastian - Looks like a new 2.7 feature I have not used yet. Thanks for the tip. – jordanm Jul 29 '12 at 13:50
  • 1
    @CppLearner There isn't anything "wrong" with it, but `check_output` looks a little simpler. – jordanm Aug 13 '13 at 04:30
  • Okay I just googled it and seems like a lot pf people prefer the old communicate method. http://stackoverflow.com/questions/4814970/subprocess-check-output-doesnt-seem-to-exist-python-2-6-5 anyway, it has pro and cons. Thanks! – CppLearner Aug 15 '13 at 02:09
  • I don't know what cons you are talking about. `check_output` and a number of other wrappers simplify the logic for simple cases. You should use the bare `Popen` only if all of the `check_*` wrappers are *too* simple for your needs. – tripleee Sep 16 '16 at 10:07
  • today it'll likely be `print(dns.resolver.resolve("aaa.asdflkjsadf.notatallsuspicio.us","TXT").response.answer[0])`, but this should be parsed further (it's already a string but contains the whole entry, not the value). `.query` is deprecated and Python 3 has `print(stuff)` syntax, not `print stuff` – YakovL Feb 21 '22 at 08:30
6

This may be overly simplified, but if all you want is a quick read of the TXT record and don't mind dealing with parsing the result separately:

nslookup -q=txt somedomain.com

I found this did what I needed, short & sweet.

dharmatron
  • 81
  • 1
  • 2
0

Found another way to get list of all TXT records for a domain using dnspython.

import dns.resolver
[dns_record.to_text() for dns_record in dns.resolver.resolve("your-domain-here", "TXT").rrset]
lalit suthar
  • 81
  • 1
  • 6
0

update 2022/11/20

# -*- coding:utf-8 -*-
# Copyright (c) DadouLab.SIG MIT

import dns
import dns.query
import dns.resolver
import logging

logger = logging.getLogger(__name__)


class Digger(object):
    def __init__(self, resolvers=["1.1.1.1"]):
        self.mResolver = dns.resolver.Resolver()
        self.mResolver.timeout = 1
        self.mResolver.lifetime = 0.5
        self.mResolver.nameservers = resolvers
        self.spec_query_type = ['CNAME', 'TXT', 'MX', 'NS', 'SRV', 'CAA']

    def query(self, domain, query_type="A"):
        """
        answer = dns.resolver.resolve("_dnsauth.test.com", "TXT").rrset
        for dns_record in answer:
            print(dns_record.to_text())
        """
        try:
            query_type = query_type.upper()
            answer = self.mResolver.resolve(domain, query_type, raise_on_no_answer=False)
            answer_raw = answer.chaining_result.answer.to_text()
            logger.info("resolved response data => {}".format(answer_raw))
            if query_type in self.spec_query_type:
                records = [data.to_text() for data in answer]
            else:
                records = [data.address for data in answer]
            return records
        except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer,
                dns.resolver.NoNameservers, dns.exception.Timeout) as error:
            logger.warning("resolved error => {}".format(error))
            return

    def is_valid(self, domain, query_type="A"):
        try:
            self.mResolver.resolve(domain, query_type, raise_on_no_answer=False)
            return True
        except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer,
                dns.resolver.NoNameservers, dns.exception.Timeout) as error:
            logger.warning("resolved error => {}".format(error))
        return


if __name__ == '__main__':
    dig = Digger()
    print(dig.query("www.example.com", query_type="A"))

Ershan
  • 623
  • 8
  • 9
-7

Something like this should work to at least get the value for the URL, I used google.com for the example.

import pycurl
import StringIO
url = "whatsmyip.us/dns_txt.php?host=google.com"
c = pycurl.Curl()
c.setopt(pycurl.URL, url)
c.setopt(pycurl.HTTPHEADER, ["Accept:"])
txtcurl = StringIO.StringIO()
c.setopt(pycurl.WRITEFUNCTION, txtcurl.write)
c.perform

data = txtcurl.getvalue()
data = data.replace("Done!", "")
print data

I did not test any of this but pulled it from a previous project.

Best of luck!

sbrichards
  • 2,169
  • 2
  • 19
  • 32
  • 8
    You do not need ten lines of code to do a single HTTP request in Python. Either way, this is a bad answer, but if you're going to do it, at least use `requests.get("http://whatsmyip.us/dns_txt.php?host=google.com")` instead. – fletom Apr 28 '14 at 05:51