/src/libs/construct/protocols/application/dns.py

https://github.com/AnonLEF/vomun · Python · 147 lines · 126 code · 18 blank · 3 comment · 1 complexity · cdb6d338e0413f1db6bc2631ffa1d605 MD5 · raw file

  1. """
  2. Domain Name System (TCP/IP protocol stack)
  3. """
  4. from construct import *
  5. from construct.protocols.layer3.ipv4 import IpAddressAdapter
  6. class DnsStringAdapter(Adapter):
  7. def _encode(self, obj, context):
  8. parts = obj.split(".")
  9. parts.append("")
  10. return parts
  11. def _decode(self, obj, context):
  12. return ".".join(obj[:-1])
  13. dns_record_class = Enum(UBInt16("class"),
  14. RESERVED = 0,
  15. INTERNET = 1,
  16. CHAOS = 3,
  17. HESIOD = 4,
  18. NONE = 254,
  19. ANY = 255,
  20. )
  21. dns_record_type = Enum(UBInt16("type"),
  22. IPv4 = 1,
  23. AUTHORITIVE_NAME_SERVER = 2,
  24. CANONICAL_NAME = 5,
  25. NULL = 10,
  26. MAIL_EXCHANGE = 15,
  27. TEXT = 16,
  28. X25 = 19,
  29. ISDN = 20,
  30. IPv6 = 28,
  31. UNSPECIFIED = 103,
  32. ALL = 255,
  33. )
  34. query_record = Struct("query_record",
  35. DnsStringAdapter(
  36. RepeatUntil(lambda obj, ctx: obj == "",
  37. PascalString("name")
  38. )
  39. ),
  40. dns_record_type,
  41. dns_record_class,
  42. )
  43. rdata = Field("rdata", lambda ctx: ctx.rdata_length)
  44. resource_record = Struct("resource_record",
  45. CString("name", terminators = "\xc0\x00"),
  46. Padding(1),
  47. dns_record_type,
  48. dns_record_class,
  49. UBInt32("ttl"),
  50. UBInt16("rdata_length"),
  51. IfThenElse("data", lambda ctx: ctx.type == "IPv4",
  52. IpAddressAdapter(rdata),
  53. rdata
  54. )
  55. )
  56. dns = Struct("dns",
  57. UBInt16("id"),
  58. BitStruct("flags",
  59. Enum(Bit("type"),
  60. QUERY = 0,
  61. RESPONSE = 1,
  62. ),
  63. Enum(Nibble("opcode"),
  64. STANDARD_QUERY = 0,
  65. INVERSE_QUERY = 1,
  66. SERVER_STATUS_REQUEST = 2,
  67. NOTIFY = 4,
  68. UPDATE = 5,
  69. ),
  70. Flag("authoritive_answer"),
  71. Flag("truncation"),
  72. Flag("recurssion_desired"),
  73. Flag("recursion_available"),
  74. Padding(1),
  75. Flag("authenticated_data"),
  76. Flag("checking_disabled"),
  77. Enum(Nibble("response_code"),
  78. SUCCESS = 0,
  79. FORMAT_ERROR = 1,
  80. SERVER_FAILURE = 2,
  81. NAME_DOES_NOT_EXIST = 3,
  82. NOT_IMPLEMENTED = 4,
  83. REFUSED = 5,
  84. NAME_SHOULD_NOT_EXIST = 6,
  85. RR_SHOULD_NOT_EXIST = 7,
  86. RR_SHOULD_EXIST = 8,
  87. NOT_AUTHORITIVE = 9,
  88. NOT_ZONE = 10,
  89. ),
  90. ),
  91. UBInt16("question_count"),
  92. UBInt16("answer_count"),
  93. UBInt16("authority_count"),
  94. UBInt16("additional_count"),
  95. Array(lambda ctx: ctx.question_count,
  96. Rename("questions", query_record),
  97. ),
  98. Rename("answers",
  99. Array(lambda ctx: ctx.answer_count, resource_record)
  100. ),
  101. Rename("authorities",
  102. Array(lambda ctx: ctx.authority_count, resource_record)
  103. ),
  104. Array(lambda ctx: ctx.additional_count,
  105. Rename("additionals", resource_record),
  106. ),
  107. )
  108. if __name__ == "__main__":
  109. cap1 = (
  110. "2624010000010000000000000377777706676f6f676c6503636f6d0000010001"
  111. ).decode("hex")
  112. cap2 = (
  113. "2624818000010005000600060377777706676f6f676c6503636f6d0000010001c00c00"
  114. "05000100089065000803777777016cc010c02c0001000100000004000440e9b768c02c"
  115. "0001000100000004000440e9b793c02c0001000100000004000440e9b763c02c000100"
  116. "0100000004000440e9b767c030000200010000a88600040163c030c030000200010000"
  117. "a88600040164c030c030000200010000a88600040165c030c030000200010000a88600"
  118. "040167c030c030000200010000a88600040161c030c030000200010000a88600040162"
  119. "c030c0c00001000100011d0c0004d8ef3509c0d0000100010000ca7c000440e9b309c0"
  120. "80000100010000c4c5000440e9a109c0900001000100004391000440e9b709c0a00001"
  121. "00010000ca7c000442660b09c0b00001000100000266000440e9a709"
  122. ).decode("hex")
  123. obj = dns.parse(cap1)
  124. print obj
  125. print repr(dns.build(obj))
  126. print "-" * 80
  127. obj = dns.parse(cap2)
  128. print obj
  129. print repr(dns.build(obj))