Skip to content
81 changes: 75 additions & 6 deletions dnscommon.r2py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ _type_lookup_table = {
14 : 'MINFO', # Mailbox Information
15 : 'MX', # Mail Exchange
16 : 'TXT', # Text Strings
28 : 'AAAA', # IPv6 address
252 : 'AXFR', # Request for transfer of zone
253 : 'MAILB', # Request for mailbox-related RRs
254 : 'MAILA', # Request for mail agent RRs (Obsolete)
Expand All @@ -130,6 +131,7 @@ _type_lookup_table = {
'MINFO' : 14,
'MX' : 15,
'TXT' : 16,
'AAAA' : 28,
'AXFR' : 252,
'MAILB' : 253,
'MAILA' : 254,
Expand Down Expand Up @@ -494,17 +496,16 @@ def _read_single_answer(answer_index, dns_query_data):
"""
# Parse answer address.
read_index, answer_name = _parse_address(answer_index, dns_query_data)

# Read the type.
answer_type = _type_lookup_table[
_charpair_to_int16(dns_query_data[read_index:read_index + 2])]
read_index += 2

read_index += 2
# Read the class.
answer_class = _class_lookup_table[
_charpair_to_int16(dns_query_data[read_index:read_index + 2])]
read_index += 2

# Some math magic with the TTL.
time_to_live = _charpair_to_int16(dns_query_data[read_index:read_index + 2]) * 2 ** 16
read_index += 2
Expand All @@ -519,9 +520,25 @@ def _read_single_answer(answer_index, dns_query_data):

# There are a lot of different types of queries. We can parse all of these ones
# in the same way, though.
simple_answers = ['A', 'NS', 'CNAME', 'MD', 'MB', 'MF', 'MG', 'MR', 'MX', 'PTR']
simple_answers = ['A', 'CNAME', 'MD', 'MB', 'MF', 'MG', 'MR']
if answer_type in simple_answers:
read_index, resource_data['address'] = _parse_answer_address(read_index, dns_query_data)
elif answer_type == 'NS':
read_index, resource_data['nameserver'] = _parse_address(read_index,dns_query_data)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please combine all the answer types that are treated the same way under a single elif branch.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not combine all because when you query for MX or reverse or AAAA etc then you have additional section containing extra data. We don't need that but if we don't separate then packet dictionary will contain mixed details. So by separating, packet dictionary will contain all information with their respective query types.
for eg.

;; QUESTION SECTION:
;google.com.            IN  MX

;; ANSWER SECTION:
google.com.     600 IN  MX  20 alt1.aspmx.l.google.com.
google.com.     600 IN  MX  30 alt2.aspmx.l.google.com.
google.com.     600 IN  MX  40 alt3.aspmx.l.google.com.
google.com.     600 IN  MX  10 aspmx.l.google.com.
google.com.     600 IN  MX  50 alt4.aspmx.l.google.com.

;; ADDITIONAL SECTION:
alt1.aspmx.l.google.com. 156    IN  A   64.233.186.27

or

;google.com.            IN  NS

;; ANSWER SECTION:
google.com.     14400   IN  NS  ns4.google.com.
google.com.     14400   IN  NS  ns1.google.com.
google.com.     14400   IN  NS  ns2.google.com.
google.com.     14400   IN  NS  ns3.google.com.

;; ADDITIONAL SECTION:
ns1.google.com.     13328   IN  A   216.239.32.10
ns2.google.com.     13956   IN  A   216.239.34.10
ns3.google.com.     2225    IN  A   216.239.36.10
ns4.google.com.     2315    IN  A   216.239.38.10

elif answer_type == 'PTR':
read_index, resource_data['domain'] = _parse_address(read_index, dns_query_data)
elif answer_type == 'MX':
read_index+=2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come the index must be modified for MX records?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

;; QUESTION SECTION:
;google.com.            IN  MX

;; ANSWER SECTION:
google.com.     301 IN  MX  40 alt3.aspmx.l.google.com.
google.com.     301 IN  MX  30 alt2.aspmx.l.google.com.
google.com.     301 IN  MX  50 alt4.aspmx.l.google.com.
google.com.     301 IN  MX  20 alt1.aspmx.l.google.com.
google.com.     301 IN  MX  10 aspmx.l.google.com.

As we can see there is some values like 10,20,30... before the MX records, we need to increase index to get to correct data from raw packet.

read_index, resource_data['records'] = _parse_address(read_index, dns_query_data)
elif answer_type == 'AAAA':
read_index, resource_data['address'] = _parse_aaaa_address(read_index, dns_query_data)
elif answer_type == 'TXT':
txt_length = len(dns_query_data)-read_index
resource_text = []
for i in range(0,txt_length):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coding Style?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, why does the loop exist?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As 'TXT' query contains text information like:

;; QUESTION SECTION:
;google.com.            IN  TXT

;; ANSWER SECTION:
google.com.     3600    IN  TXT "v=spf1 include:_spf.google.com ip4:216.73.93.70/31 ip4:216.73.93.72/31 ~all"

We need controlled loop to convert raw packet to txt format and save it into answer section.

resource_text.append(dns_query_data[read_index])
read_index+=1
resource_data['text'] = ''.join(resource_text)
elif answer_type == 'SOA':
read_index, mname = _parse_address(read_index, dns_query_data)
resource_data['mname'] = mname
Expand Down Expand Up @@ -552,7 +569,6 @@ def _read_single_answer(answer_index, dns_query_data):
resource_data['retry'] = retry
resource_data['expire'] = expire
resource_data['minimum'] = minimum

answer_dict = {
'name' : answer_name,
'type' : answer_type,
Expand All @@ -564,7 +580,60 @@ def _read_single_answer(answer_index, dns_query_data):
return read_index, answer_dict


def _parse_aaaa_address(address_index, dns_query_data):
"""
<Purpose>
This method parses a IPv6 directly out of an answer
section. This method is needed because answer IPs are not delimited,
since their labels are not of variable length.

<Arguments>
address_index
The integer index at which the address starts.
dns_query_data
The string containing raw query data

<Exceptions>
If the address is improperly formatted, this will produce an integer
parsing failure.

<Side Effects>
None

<Returns>
A tuple containing the new read index and IPv6 Address.
"""
#converts packet data to hex
add = ''
address = ''
new_list = []
for i in range(0,16):
add+=str(hex(ord(dns_query_data[address_index])))
add+=":"
address_index += 1
add = add[:-1]

#converts hex data to readable IPv6
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use plain Python string formatting?

add_list = add.split(":")
for i in add_list:
i = i[2:]
if len(i) == 2:
new_list.append(i)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please fix indent level.

elif len(i)==1:
i = '0'+i
new_list.append(i)

for i in range(0,16,2):
try:
address+= new_list[i]+new_list[i+1]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong indent level. See Coding Style Guidelines.

address+=':'
except IndexError:
pass
address=address[:-1]
#address = address.replace('0000','')
#address = address.replace('::',':')

return address_index, address

def _parse_answer_address(address_index, dns_query_data):
"""
Expand Down Expand Up @@ -600,7 +669,7 @@ def _parse_answer_address(address_index, dns_query_data):
address_index += 1
address += str(ord(dns_query_data[address_index]))
address_index += 1

return address_index, address


Expand Down
Loading