The socket module of the Python standard library provides basic functions for resolving hostnames (gethostbyname and gethostbyname_ex) as implemented by the C library. The resolver of the dnspython allows to bypass the C library and query DNS servers directly.
pip install dnspython
Before we start querying DNS servers it is useful to know about some constants and convenience functions dnspython provides. The dns.rdataclass module defines the DNS data clasess out of which only Internet IN is relevant (or has anyone ever seen CHAOS or HESIOD implemented???).
In [4]:
import dns.rdataclass
dns.rdataclass.IN
Out[4]:
The constants of the dns.rdatatype module are a lot more important as they list the types of records commonly
served by DNS servers.
In [5]:
import dns.rdatatype
dns.rdatatype.A, dns.rdatatype.MX, dns.rdatatype.PTR, dns.rdatatype.SOA, dns.rdatatype.TXT
Out[5]:
On Unix systems the file /etc/resolv.conf may list (on Linux up to three) nameservers that the C library functions shall query for resolving host names that are not found in /etc/hosts or other local databases like LDAP or NIS as defined in /etc/nsswitch.conf.
Unless one requires exact control which DNS server(s) to query, the dns.resolver.query() function (actually a wrapper for the more versatile dns.resolver.Resolver.query() method) is the right choice. It will simply use the servers and other settings configured in /etc/resolv.conf. The function dns.resolver.get_default_resolver() returns the details. Check the resolv.conf(5) manual page for more information.
In [6]:
import dns.resolver
dns.resolver.get_default_resolver().nameservers
Out[6]:
In [7]:
dns.resolver.get_default_resolver().domain
Out[7]:
In [8]:
dns.resolver.get_default_resolver().search
Out[8]:
In [9]:
dns.resolver.get_default_resolver().timeout
Out[9]:
In [10]:
answers = dns.resolver.query('www.google.com')
answers
Out[10]:
In [11]:
answers.canonical_name.to_text(), answers.canonical_name.to_unicode()
Out[11]:
In [12]:
answers.expiration
Out[12]:
In [13]:
answers.qname
Out[13]:
In [14]:
answers.qname.to_text(), answers.qname.to_unicode()
Out[14]:
In [15]:
answers.rdclass == dns.rdataclass.IN
Out[15]:
In [16]:
answers.rdtype == dns.rdatatype.A
Out[16]:
In [17]:
answers.response
Out[17]:
In [18]:
answers.response.edns
Out[18]:
In [19]:
answers.response.time
Out[19]:
In [20]:
answers.response.flags # https://tools.ietf.org/html/rfc1035 4.1.1. Header section format
Out[20]:
In [21]:
answers.response.flags & 0b1000000000000000 # 0=query, 1=response
Out[21]:
In [22]:
answers.response.flags & 0b0000010000000000 # 1=authoratative
Out[22]:
In [23]:
answers.response.flags & 0b0000001000000000 # 1=truncated
Out[23]:
In [24]:
answers.response.flags & 0b0000000100000000 # 1=recursive desired (copied into response)
Out[24]:
In [25]:
answers.response.flags & 0b0000000010000000 # 1=recursion available
Out[25]:
In [26]:
answers.response.rcode() # errors?
Out[26]:
In [27]:
answers.rrset
Out[27]:
In [28]:
len(answers.rrset)
Out[28]:
In [29]:
list(answers.rrset)
Out[29]:
In [30]:
answers.rrset[0].address
Out[30]:
In [31]:
answers.rrset[0].rdclass == dns.rdataclass.IN
Out[31]:
In [32]:
answers.rrset[0].rdclass == dns.rdatatype.A
Out[32]:
Of course one query other records than A type. The second argument to dns.resolver.query accepts the contants defined in dns.rdatatype or simply a string value. The attributes of the returned records are specific ot the queried type, e.g. MX records have a preference attribute.
In [33]:
answers = dns.resolver.query('google.com', 'MX')
len(answers.rrset)
Out[33]:
In [34]:
answers.rrset[0]
Out[34]:
In [35]:
answers.rrset[0].exchange.to_text()
Out[35]:
In [36]:
answers.rrset[0].preference
Out[36]:
In [37]:
answers.rrset[0].rdtype == dns.rdatatype.MX
Out[37]:
In [38]:
answers = dns.resolver.query('google.com', 'SOA')
len(answers.rrset)
Out[38]:
In [39]:
answers.rrset[0]
Out[39]:
In [40]:
answers.rrset[0].mname.to_text()
Out[40]:
In [41]:
answers.rrset[0].serial
Out[41]:
In [42]:
answers.rrset[0].refresh
Out[42]:
In [44]:
answers = dns.resolver.query('4.4.8.8.in-addr.arpa', 'PTR')
len(answers)
Out[44]:
In [45]:
answers.rrset[0]
Out[45]:
In [51]:
answers.rrset[0].to_text()
Out[51]:
In [54]:
answers = dns.resolver.query('_http._tcp.juenemann.net', 'SRV')
len(answers)
Out[54]:
In [55]:
answers.rrset[0]
Out[55]:
In [56]:
answers.rrset[0].to_text()
Out[56]:
In [62]:
answers.rrset[0].target.to_text()
Out[62]:
In [58]:
answers.rrset[0].priority, answers.rrset[0].weight, answers.rrset[0].port
Out[58]:
There are cases where one does not want to use alternative settings to those configured in /etc/resolv.conf. For this purpose one has to create and customise an instance of the dns.resolver.Resolver class.
In [ ]:
In [ ]:
resolver = dns.resolver.Resolver()
resolver.nameservers = ['208.67.222.222', '208.67.220.220']
resolver.nameservers
In [ ]:
answers = resolver.query('google.com', 'NS')
list(answers.rrset)
In [ ]:
resolver = dns.resolver.Resolver()
resolver.nameservers = ['208.67.222.222', '208.67.220.220']
resolver.nameservers
In [ ]:
resolver.set_flags(0b0000000000000000) # Clear all flags, including 'recursive desired'
resolver.flags
In [ ]:
try:
answers = resolver.query('www.google.com', 'A')
except Exception as e:
print e
In [ ]:
answers = resolver.query('www.opendns.com', 'A')
list(answers.rrset)