You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
148 lines
3.8 KiB
148 lines
3.8 KiB
"""
|
|
Domain Name System (TCP/IP protocol stack)
|
|
"""
|
|
from construct import *
|
|
from construct.protocols.layer3.ipv4 import IpAddressAdapter
|
|
from binascii import unhexlify
|
|
import six
|
|
|
|
|
|
class DnsStringAdapter(Adapter):
|
|
def _encode(self, obj, context):
|
|
parts = obj.split(".")
|
|
parts.append("")
|
|
return parts
|
|
def _decode(self, obj, context):
|
|
return ".".join(obj[:-1])
|
|
|
|
dns_record_class = Enum(UBInt16("class"),
|
|
RESERVED = 0,
|
|
INTERNET = 1,
|
|
CHAOS = 3,
|
|
HESIOD = 4,
|
|
NONE = 254,
|
|
ANY = 255,
|
|
)
|
|
|
|
dns_record_type = Enum(UBInt16("type"),
|
|
IPv4 = 1,
|
|
AUTHORITIVE_NAME_SERVER = 2,
|
|
CANONICAL_NAME = 5,
|
|
NULL = 10,
|
|
MAIL_EXCHANGE = 15,
|
|
TEXT = 16,
|
|
X25 = 19,
|
|
ISDN = 20,
|
|
IPv6 = 28,
|
|
UNSPECIFIED = 103,
|
|
ALL = 255,
|
|
)
|
|
|
|
query_record = Struct("query_record",
|
|
DnsStringAdapter(
|
|
RepeatUntil(lambda obj, ctx: obj == "",
|
|
PascalString("name")
|
|
)
|
|
),
|
|
dns_record_type,
|
|
dns_record_class,
|
|
)
|
|
|
|
rdata = Field("rdata", lambda ctx: ctx.rdata_length)
|
|
|
|
resource_record = Struct("resource_record",
|
|
CString("name", terminators = six.b("\xc0\x00")),
|
|
Padding(1),
|
|
dns_record_type,
|
|
dns_record_class,
|
|
UBInt32("ttl"),
|
|
UBInt16("rdata_length"),
|
|
IfThenElse("data", lambda ctx: ctx.type == "IPv4",
|
|
IpAddressAdapter(rdata),
|
|
rdata
|
|
)
|
|
)
|
|
|
|
dns = Struct("dns",
|
|
UBInt16("id"),
|
|
BitStruct("flags",
|
|
Enum(Bit("type"),
|
|
QUERY = 0,
|
|
RESPONSE = 1,
|
|
),
|
|
Enum(Nibble("opcode"),
|
|
STANDARD_QUERY = 0,
|
|
INVERSE_QUERY = 1,
|
|
SERVER_STATUS_REQUEST = 2,
|
|
NOTIFY = 4,
|
|
UPDATE = 5,
|
|
),
|
|
Flag("authoritive_answer"),
|
|
Flag("truncation"),
|
|
Flag("recurssion_desired"),
|
|
Flag("recursion_available"),
|
|
Padding(1),
|
|
Flag("authenticated_data"),
|
|
Flag("checking_disabled"),
|
|
Enum(Nibble("response_code"),
|
|
SUCCESS = 0,
|
|
FORMAT_ERROR = 1,
|
|
SERVER_FAILURE = 2,
|
|
NAME_DOES_NOT_EXIST = 3,
|
|
NOT_IMPLEMENTED = 4,
|
|
REFUSED = 5,
|
|
NAME_SHOULD_NOT_EXIST = 6,
|
|
RR_SHOULD_NOT_EXIST = 7,
|
|
RR_SHOULD_EXIST = 8,
|
|
NOT_AUTHORITIVE = 9,
|
|
NOT_ZONE = 10,
|
|
),
|
|
),
|
|
UBInt16("question_count"),
|
|
UBInt16("answer_count"),
|
|
UBInt16("authority_count"),
|
|
UBInt16("additional_count"),
|
|
Array(lambda ctx: ctx.question_count,
|
|
Rename("questions", query_record),
|
|
),
|
|
Rename("answers",
|
|
Array(lambda ctx: ctx.answer_count, resource_record)
|
|
),
|
|
Rename("authorities",
|
|
Array(lambda ctx: ctx.authority_count, resource_record)
|
|
),
|
|
Array(lambda ctx: ctx.additional_count,
|
|
Rename("additionals", resource_record),
|
|
),
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
cap1 = unhexlify(six.b("2624010000010000000000000377777706676f6f676c6503636f6d0000010001"))
|
|
|
|
cap2 = unhexlify(six.b(
|
|
"2624818000010005000600060377777706676f6f676c6503636f6d0000010001c00c00"
|
|
"05000100089065000803777777016cc010c02c0001000100000004000440e9b768c02c"
|
|
"0001000100000004000440e9b793c02c0001000100000004000440e9b763c02c000100"
|
|
"0100000004000440e9b767c030000200010000a88600040163c030c030000200010000"
|
|
"a88600040164c030c030000200010000a88600040165c030c030000200010000a88600"
|
|
"040167c030c030000200010000a88600040161c030c030000200010000a88600040162"
|
|
"c030c0c00001000100011d0c0004d8ef3509c0d0000100010000ca7c000440e9b309c0"
|
|
"80000100010000c4c5000440e9a109c0900001000100004391000440e9b709c0a00001"
|
|
"00010000ca7c000442660b09c0b00001000100000266000440e9a709"
|
|
))
|
|
|
|
obj = dns.parse(cap1)
|
|
print (obj)
|
|
print (repr(dns.build(obj)))
|
|
|
|
print ("-" * 80)
|
|
|
|
obj = dns.parse(cap2)
|
|
print (obj)
|
|
print (repr(dns.build(obj)))
|
|
|
|
|
|
|
|
|