user12713780
user12713780

Reputation:

server name reachability in dnspython

I am currently trying to find a way to check whether or not the name servers can respond to either TCP or UDP packets.

My idea behind that was, to get all the name servers from a website (for example google.com), store them in a list, and then try to send TCP and UDP messages to all of them.

Although I am getting the name servers, my interpreter shows a problem when I am trying to make a query on udp(check udpPacket on the code) saying:

"TypeError: coercing to Unicode: need string or buffer, NS found"

I am new in Python(coming from C and C++) and I am guessing this is just incompatible types.

I checked dnspython's documentation and could not find what kind of type NS is (probably it's a type by itself) and why it cannot be passed as an argument.

What do you think the problem is? Is there maybe a better way to solve that kind of problem?

def getNSResults(url):

    #create an empty list where we can store all the nameservers we found
    nameServers = []

    nameServers = dns.resolver.query(url,dns.rdatatype.NS, raise_on_no_answer=False)

    #create a dictionary where based on all the nameservers.
    #1st label refers to the ns name of our url that we inserted.
    #2nd label shows wether or not we received a UDP response or not.
    #3rd label shows wether or not we received a TCP response or not.
    results = {}

    for nameServer in nameServers:

        #make a dns ns query, acts as a dumb message since whatever we send we just care of what we get back
        query = dns.message.make_query(dns.name.from_text(url), dns.rdatatype.ANY)

        query.flags |= dns.flags.AD

        query.find_rrset(query.additional, dns.name.root, 65535, dns.rdatatype.OPT, create=True, force_unique=True)

        #try sending a udp packet to see if it's listening on UDP
        udpPacket = dns.query.udp(query,nameServer)

        #try sending a tcp packet to see if it's listening on TCP
        tcpPacket = dns.query.tcp(None,nameServer)

        #add the results in a dictionary and return it, to be checked later by the user.
        results.update({"nsName" == nameServer, "receivedUDPPacket" == isNotNone(udpPacket),"receivedTCPPacket" == isNotNone(tcpPacket)})

Thanks in advance!

Upvotes: 0

Views: 1046

Answers (1)

kimbo
kimbo

Reputation: 2693

Looking at your code, I see some DNS problems, some Python problems, and some dnspython problems. Let's see if we can't learn something together.

DNS

First, the parameter to your function getNSResults is called url. When you send DNS queries, you query for a domain name. A URL is something totally different (e.g. https://example.com/index.html). I would rename url to something like domain_name, domain, or name. For more on the difference between URLs and domain names, see https://www.copahost.com/blog/domain-vs-url/.

Second, let's talk about what you're trying to do.

i am currently trying to find a way to check wether or not the name servers can respond to either tcp or udp packets.

My idea behind that was, to get all the name servers from a website (for example google.com), store them in a list, and then, try to send tcp and udp messages to all of them.

That sounds like a great approach. I think you might be missing a few details here. so let me explain the steps you can take to do this:

  1. Do an NS query for a domain name. You already have this step in your code. What you'll actually get from that query is just another domain name (or multiple domain names). For example, if you run dig +short NS google.com, you'll get this output:
ns3.google.com.
ns1.google.com.
ns4.google.com.
ns2.google.com.
  1. At this step, we have a list of one or more names of authoritative servers. Now we need an IP address to use to send them queries. So we'll do a type A query for each of the names we got from step 1.
  2. Now we have a list of IP addresses. We can send a DNS query over UDP and one over TCP to see if they're supported.

Python

For the most part, your Python syntax is okay. The biggest red flag I see is the following code:

results.update({"nsName" == nameServer, 
    "receivedUDPPacket" == isNotNone(udpPacket),
    "receivedTCPPacket" == isNotNone(tcpPacket)})

Let's break this down a bit. First, you have results, which is a dict. Then you have this:

{"nsName" == nameServer, 
"receivedUDPPacket" == isNotNone(udpPacket),
"receivedTCPPacket" == isNotNone(tcpPacket)}

which is a set of bools. What I think you meant to do was something like this:

results.update({
    "nsName": nameServer,
    "receivedUDPPacket": true,
    "receivedTCPPacket": true
})

Function and variables names in Python are usually written in lowercase, with words separated by underscores (e.g. my_variable, def my_function()). Class names are usually upper camel case (e.g. class MyClass). None of this is required, you can name your stuff however you want, plenty of super popular libraries and builtins break this convention, just figured I'd throw it out there because it can be helpful when reading Python code.

dnspython

When you're not sure about the types of things, or what attributes things have, remember these four friends, all builtin to Python: 1. pdb 2. dir 3. type 4. print

pdb is a Python debugger. Just import pdb, and the put pdb.set_trace() where you want to break. Your code will stop there, and then you can check out the values of all the variables.

dir will return the attributes and methods of whatever you pass to it. Example: print(dir(udpPacket)).

type will return the type of an object.

print as you probably already know, will print out stuff so you can see it.

I'm going to leave this part for you to test out. Run dir() on everything if you don't know what it is. I also should probably mention help(), which is super useful for built-in stuff.

The summary for this section is that sometimes documentation isn't all there, or hard to find, especially when you're new to a language/library/whatever. So you have to figure stuff out on your own, and that means using all the tools I've just mentioned, looking at the source code, things like that.

Summary

I hope this was helpful. I know it's a lot, it's probably too much, but just be patient and know that DNS and Python are some very useful and fun things to learn about.

I went ahead and wrote something up that is a start at what I think you're hoping to achieve. I recommend walking through the whole thing and making sure you understand what's going on. If you don't understand something, remember pdb and dir (and there's always Google, SO, etc).

import dns.resolver
import dns.message
import dns.rdatatype

import json
import sys

def check_tcp_and_udp_support(name):
    # this will give me the first default system resolver from /etc/resolv.conf
    # (or Windows registry)
    where = dns.resolver.Resolver().nameservers[0]

    q = dns.message.make_query(name, dns.rdatatype.NS)
    ns_response = dns.query.udp(q, where)

    ns_names = [t.target.to_text() for ans in ns_response.answer for t in ans]

    # this code is the same as the one-liner above
    # ns_names = []
    # for ans in ns_response.answer:
    #     for t in ans:
    #         ns_names.append(t.target.to_text())

    results = {}

    for ns_name in ns_names:

        # do type A lookup for nameserver
        q = dns.message.make_query(ns_name, dns.rdatatype.A)
        response = dns.query.udp(q, where)

        nameserver_ips = [item.address for ans in response.answer for item in ans.items if ans.rdtype == dns.rdatatype.A]

        # now send queries to the nameserver IPs
        for nameserver_ip in nameserver_ips:
            q = dns.message.make_query('example.com.', dns.rdatatype.A)
            try:
                udp_response = dns.query.udp(q, nameserver_ip)
                supports_udp = True
            except dns.exception.Timeout:
                supports_udp = False
            try:
                tcp_response = dns.query.tcp(q, nameserver_ip)
                supports_tcp = True
            except dns.exception.Timeout:
                supports_tcp = True

            results[nameserver_ip] = {
                'supports_udp': supports_udp,
                'supports_tcp': supports_tcp
            }

    return results

def main():
    results = check_tcp_and_udp_support('google.com')

    # this is just fancy JSON printing
    # you could do print(results) instead
    json.dump(results, sys.stdout, indent=4)

if __name__ == '__main__':
    main()

Again, I hope this is helpful. It's hard when I don't know exactly what's going on in your head, but this is what I've got for you.

Upvotes: 0

Related Questions