summaryrefslogtreecommitdiff
path: root/project2/proj2_s4498062/dns/domainname.py
blob: 6dbb039cd6b8b6b05e808653f3bb634c9794d380 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
""" Parsing and composing domain names

This module contains two classes for converting domain names to and from bytes.
You won't have to use these classes. They're used internally in Message,
Question, ResourceRecord and RecordData. You can read section 4.1.4 of RFC 1035
if you want more info.
"""

import struct


class Composer(object):
    def __init__(self):
        self.offsets = dict()

    def to_bytes(self, offset, dnames):
        # Convert each domain name in to bytes
        result = b""
        for _, dname in enumerate(dnames):
            # Split domain name into labels
            labels = dname.split(".")

            # Determine keys of subdomains in offset dict
            keys = []
            for label in reversed(labels):
                name = label
                if keys:
                    name += "." + keys[-1]
                keys.append(name)
            keys.reverse()

            # Convert label to bytes
            add_null = True
            for j, label in enumerate(labels):
                if keys[j] in self.offsets:
                    offset = self.offsets[keys[j]]
                    pointer = (3 << 14) + offset
                    result += struct.pack("!H", pointer)
                    add_null = False
                    offset += 2
                    break
                else:
                    self.offsets[keys[j]] = offset
                    result += struct.pack(
                        "!B{}s".format(len(label)),
                        len(label),
                        label)
                    offset += 1 + len(label)

            # Add null character at end
            if add_null:
                result += b"\x00"
                offset += 1

        return result


class Parser(object):
    def __init__(self):
        self.labels = dict()

    def from_bytes(self, packet, offset, num):
        dnames = []

        # Read the domain names
        for _ in range(num):
            # Read a new domain name
            dname = ""
            prev_offsets = []
            done = False
            while done is False:
                # Read length of next label
                llength = struct.unpack_from("!B", packet, offset)[0]

                # Done reading domain when length is zero
                if llength == 0:
                    offset += 1
                    break

                # Compression label
                elif (llength >> 6) == 3:
                    new_offset = offset + 2
                    target = struct.unpack_from("!H", packet, offset)[0]
                    target -= 3 << 14
                    label = self.labels[target]
                    done = True

                # Normal label
                else:
                    new_offset = offset + llength + 1
                    label = struct.unpack_from(
                        "{}s".format(llength),
                        packet, offset+1)[0]

                # Add label to dictionary
                self.labels[offset] = label
                for prev_offset in prev_offsets:
                    self.labels[prev_offset] += "." + label
                prev_offsets.append(offset)

                # Update offset
                offset = new_offset

                # Append label to domain name
                if len(dname) > 0:
                    dname += "."
                dname += label

            # Append domain name to list
            dnames.append(dname)

        return dnames, offset