-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Vlad Vitan
committed
Jul 20, 2018
0 parents
commit 6ebfc52
Showing
7 changed files
with
777 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# simple dns resolver | ||
|
||
A simple DNS client written in python. It supports the following requests: | ||
- A | ||
- NS | ||
- CNAME | ||
- SOA | ||
- MX | ||
- AAAA | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# File for DNS server addresses | ||
# | ||
8.8.8.8 | ||
8.8.4.4 | ||
|
||
199.85.126.10 | ||
|
||
141.85.128.1 | ||
#192.162.16.21 | ||
213.154.128.1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
#!/usr/bin/python | ||
|
||
""" | ||
This is a simple dnsclient that supports A, AAAA, MX, SOA, NS and CNAME | ||
queries written in python. | ||
""" | ||
|
||
import sys | ||
import socket | ||
import binascii | ||
|
||
import src.serverconf as serverconf | ||
import src.queryfactory as queryfactory | ||
import src.queryhandler as queryhandler | ||
|
||
|
||
def main(): | ||
""" Main function of the DNS client | ||
""" | ||
|
||
usage() | ||
|
||
query_elem = sys.argv[1] | ||
query_type = sys.argv[2] | ||
|
||
### Create packet according to the requested query | ||
packet = "" | ||
query = queryfactory.get_dns_query(query_elem, query_type) | ||
|
||
# query[0] is the packet | ||
packet = query[0] | ||
|
||
raw_reply = query_dns_server(packet) | ||
# query[1] is qname length | ||
reply = queryhandler.parse_answer(raw_reply, query[1]) | ||
queryhandler.print_reply(reply) | ||
|
||
return 0 | ||
|
||
def query_dns_server(packet): | ||
""" Function used to create a UDP socket, to send the DNS query to the server | ||
and to receive the DNS reply. | ||
Args: | ||
packet = the DNS query message | ||
Returns: | ||
The reply of the server | ||
If none of the servers in the dns_servers.conf sends a reply, the program | ||
exits showing an error message. | ||
""" | ||
|
||
try: | ||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | ||
except socket.error: | ||
print "[Error]: Faild to create socket. Exiting..." | ||
exit(1) | ||
|
||
# get DNS server IPs from dns_servers.conf file | ||
dns_servers = serverconf.read_file() | ||
# default port for DNS | ||
server_port = 53 | ||
|
||
for server_ip in dns_servers: | ||
got_response = False | ||
|
||
# send message to server | ||
sock.sendto(packet, (server_ip, server_port)) | ||
# receive answer | ||
recv = sock.recvfrom(1024) | ||
|
||
# if no answer is received, try another server | ||
if recv: | ||
got_response = True | ||
break | ||
|
||
# output error message if no server could respond | ||
if not got_response: | ||
print "[Error]: No response received from server. Exiting..." | ||
exit(0) | ||
|
||
return recv[0] | ||
|
||
def usage(): | ||
""" Function that checks if the required arguments are given | ||
""" | ||
|
||
if len(sys.argv) != 3: | ||
print "Usage: ./dnsclient.py <DNS name/IP> <query type>" | ||
exit(0) | ||
|
||
if __name__ == "__main__": | ||
main() |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
""" | ||
Module used for creating DNS queries. | ||
""" | ||
|
||
from struct import pack | ||
from os import getpid | ||
|
||
# Opcodes | ||
QUERY = 0 | ||
IQUERY = 1 | ||
STATUS = 2 | ||
|
||
def create_header(opcode): | ||
""" Function used to create a DNS query header. | ||
Args: | ||
opcode = opcode of the query. It can take the following values: | ||
QUERY = 0, IQUERY = 1, STATUS = 2 | ||
Returns: | ||
The header | ||
""" | ||
|
||
header = '' | ||
flags = '' | ||
|
||
# Message ID | ||
header += pack(">H", getpid()) | ||
|
||
# Flags (QR, opcode, AA, TC, RD, RA, Z, RCODE) | ||
if opcode == QUERY: | ||
# Standard DNS query | ||
flags = 0b0000000100000000 | ||
elif opcode == IQUERY: | ||
flags = 0b0000100100000000 | ||
elif opcode == STATUS: | ||
flags = 0b0001000100000000 | ||
|
||
header += pack(">H", flags) | ||
|
||
# QDCOUNT | ||
header += pack(">H", 1) | ||
# ANCOUNT | ||
header += pack(">H", 0) | ||
# NSCOUNT | ||
header += pack(">H", 0) | ||
# ARCOUNT | ||
header += pack(">H", 0) | ||
|
||
return header | ||
|
||
def get_dns_query(domain_name, query_type): | ||
""" Function used to create a DNS query question section. | ||
Args: | ||
domain_name = the domain name that needs to be resolved | ||
query_type = the query type of the DNS message | ||
Returns: | ||
The DNS query question section and the length of the qname in a tuple | ||
form: (question, qname_len) | ||
""" | ||
|
||
# QNAME | ||
qname = create_qname(domain_name) | ||
|
||
code = 0 | ||
# QTYPE - query for A record | ||
if query_type == "A": | ||
# host address | ||
code = 1 | ||
elif query_type == "NS": | ||
# authoritative name server | ||
code = 2 | ||
elif query_type == "CNAME": | ||
# the canonical name for an alias | ||
code = 5 | ||
elif query_type == "SOA": | ||
# start of a zone of authority | ||
code = 6 | ||
elif query_type == "MX": | ||
# mail exchange | ||
code = 15 | ||
elif query_type == "TXT": | ||
# text strings | ||
print "[Error]: Not implemented. Exiting..." | ||
exit(1) | ||
code = 16 | ||
elif query_type == "PTR": | ||
# domain name pointer | ||
code = 12 | ||
print "[Error]: Not implemented. Exiting..." | ||
exit(1) | ||
elif query_type == "AAAA": | ||
# AAAA record | ||
code = 28 | ||
else: | ||
print "[Error]: Invalid query. Exiting..." | ||
exit(1) | ||
|
||
qtype = pack(">H", code) | ||
|
||
# QCLASS - internet | ||
qclass = pack(">H", 1) | ||
|
||
# whole question section | ||
question = create_header(QUERY) + qname + qtype + qclass | ||
|
||
return (question, len(qname)) | ||
|
||
def create_qname(domain_name): | ||
""" Function used to transfrom URL from normal form to DNS form. | ||
Args: | ||
domain_name = URL that needs to be converted | ||
Returns: | ||
The URL in DNS form | ||
Example: | ||
3www7example3com0 to www.example.com | ||
""" | ||
|
||
qname = '' | ||
|
||
split_name = domain_name.split(".") | ||
for atom in split_name: | ||
qname += pack(">B", len(atom)) | ||
for byte in bytes(atom): | ||
qname += byte | ||
qname += '\x00' | ||
|
||
return qname |
Oops, something went wrong.