Skip to content

Commit

Permalink
adding client files
Browse files Browse the repository at this point in the history
  • Loading branch information
Vlad Vitan committed Jul 20, 2018
0 parents commit 6ebfc52
Show file tree
Hide file tree
Showing 7 changed files with 777 additions and 0 deletions.
12 changes: 12 additions & 0 deletions README.md
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



10 changes: 10 additions & 0 deletions conf/dns_servers.conf
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
96 changes: 96 additions & 0 deletions dnsclient.py
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 added src/__init__.py
Empty file.
136 changes: 136 additions & 0 deletions src/queryfactory.py
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
Loading

0 comments on commit 6ebfc52

Please sign in to comment.