This is a network project that mimics dig
! Thus the name mydig.py
😎
To get the program:
git clone https://github.com/Darim7/DNS-Resolver.git
To run the normal DNS resolver without DNSSec:
python3 normal_mydig.py <domain name> <record type>
To run the DNS resolver with DNSSec:
python3 mydig.py <domain name> <record type>
In this project, I use dnspython
to query for resource records on the internet.
The program normal_mydig.py
is the DNS resolver without implementing DNSSec.
The program mydig.py
is the DNS resolver with the implementation of DNSSec.
For the first part, I made a basic DNS resolver. It uses a recursive function recurse()
to query for a domain name.
I first make the query to the root servers. It is expected that the root servers doesn't have the records for the specific domain that I ask for if they are not any of the top level domains. Therefore, my program will then go through the additional section to see if there is given A
records of the related top level domain servers or the A
record of the place that we're querying for if we're lucky.
If there is nothing in the additional section, we will then go for the authority sections to resolve for the A
records for the tld servers that has manages the name that we're querying for.
Once we get the A
record of the tld server or next level domain, we will then query further in the Domain Name System until we found an answer section or we exhausted all the possiblities and couldn't resolve for the name 😭
Before explainations, I am going to clear out some acronyms:
ksk
: Key Signing Key
zsk
: Zone Signing Key
Then I implemented the DNSSec using the dnspython
package on the top of the existing resolver.
First, I put the want_dnssec
variable in dns.message.make_query()
to true so that I can get RRSIG
in the response.
When I get the RRSIG
field in the A
record set, I then make a query for DNSKEY
record to the server.
Since the DNSKEY
is a set of zsk
(including ksk
) that is generated by the zone that I am querying records for, I am able to use this key and the RRSIG
I got along the A
record to validate the validity of the record I received from this zone. To validate, I used the dns.dnssec.validate_rrsig()
function in dnssec. The expected outcome is that when we put the RRSIG
and the zsk
we got from DNSKEY
together, we will be able to get the record that has the RRSIG
attached. This way we can be sure that the result we get is at least valid in the zone.
But how can we be sure that our response is valid in the parent of the zone as well?!
Then we need to come to next step, validating the ksk
.
Since the ksk
is the public key from the parent zone, and the DS
record is the hash of the ksk
provided by the parent zone, we should be able to hash the ksk
from the current zone and compare the DS
record for this name from the parent zone to verify the current zone got the REAL ksk
from the parent zone.
To verify that, my dns resolver will request the DS
record for the name at the parent zone of the current zone, then it uses dns.dnssec.make_ds()
hash the ksk
then it compares the DS
record to see if everything matches.
Very lastly, I would traverse up to the parent zone and make sure the same things are checked and verified.
Before I start to explain anything, I would like to give out some basic informations.
For my own implemented resolver, it is the slowest out of the three resolvers. I think the reason is my resolver query the root servers everytime when I make the request, and the result is not cached. Therefore, each request would start a search from the root level servers. Because each query starts at the root server, the process will be definitely longer than query from other place that is closer to the target.
For using google's public DNS resolver, it is in the middle. I think this is the case because I specified to use server 8.8.8.8
. This would make the query longer because of physical distance, however, the query time is is still very quick and consistent overall all the queries, I think this is because this public server already has the name resolved in cache, what makes it slower than the local resolver is only because of the physical distance.
For the local resolver, it usually takes about 20ms to make the first query, when I make more queries after the first one, they all take 0ms, this is the reason why the average resolve time is this low. My guess is that the local resolver has cached the result and the local resolver is close to me physically, and this made the query this quick.