Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved. 

2# 

3# This software is provided under under a slightly modified version 

4# of the Apache Software License. See the accompanying LICENSE file 

5# for more information. 

6# 

7# Author: 

8# Andres Blanco 

9# Gustavo Moreira 

10 

11# 

12# RFCs for the DNS Server service 

13# 

14# 1034 - Domain Names -- Concepts and Facilities [https://www.ietf.org/rfc/rfc1034.txt] 

15# 1035 - Domain Names -- Implementation and Specification [https://www.ietf.org/rfc/rfc1035.txt] 

16# 1123 - Requirements for Internet Hosts -- Application and Support [https://www.ietf.org/rfc/rfc1123.txt] 

17# 1886 - DNS Extensions to Support IP Version 6 [https://www.ietf.org/rfc/rfc1886.txt] 

18# 1995 - Incremental Zone Transfer in DNS [https://www.ietf.org/rfc/rfc1995.txt] 

19# 1996 - A Mechanism for Prompt Notification of Zone Changes (DNS NOTIFY) [https://www.ietf.org/rfc/rfc1996.txt] 

20# 2136 - Dynamic Updates in the Domain Name System (DNS UPDATE) [https://www.ietf.org/rfc/rfc2136.txt] 

21# 2181 - Clarifications to the DNS Specification [https://www.ietf.org/rfc/rfc2181.txt] 

22# 2308 - Negative Caching of DNS Queries (DNS NCACHE) [https://www.ietf.org/rfc/rfc2308.txt] 

23# 2535 - Domain Name System Security Extensions (DNSSEC) [https://www.ietf.org/rfc/rfc2535.txt] 

24# 2671 - Extension Mechanisms for DNS (EDNS0) [https://www.ietf.org/rfc/rfc2671.txt] 

25# 2782 - A DNS RR for specifying the location of services (DNS SRV) [https://www.ietf.org/rfc/rfc2782.txt] 

26# 2930 - Secret Key Establishment for DNS (TKEY RR) [https://www.ietf.org/rfc/rfc2930.txt] 

27# 3645 - Generic Security Service Algorithm for Secret Key Transaction Authentication for DNS (GSS-TSIG) [https://www.ietf.org/rfc/rfc3645.txt] 

28# 3646 - DNS Configuration options for Dynamic Host Configuration Protocol for IPv6 (DHCPv6) [https://www.ietf.org/rfc/rfc3646.txt] 

29# 

30 

31import socket 

32import struct 

33 

34from impacket.ImpactPacket import ProtocolPacket 

35 

36 

37class DNSFlags(): 

38 'Bitmap with the flags of a dns packet.' 

39 # QR - Query/Response - 1 bit  

40 QR_QUERY = int("0000000000000000", 2) 

41 QR_RESPONSE = int("1000000000000000", 2) 

42 # OP - Opcode - 4 bits 

43 OP_STANDARD_QUERY = int("0000000000000000", 2) # Standard query. 

44 OP_INVERSE_QUERY = int("0100000000000000", 2) # Inverse query. 

45 OP_STATUS_QUERY = int("0010000000000000", 2) # Server status request. 

46 OP_NOTIFY = int("0000100000000000", 2) # Notify. 

47 OP_UPDATE = int("0100100000000000", 2) # Update. 

48 # AA - Authority Answer - 1 bit 

49 AA_NOT_AUTH_ANSWER = int("0000000000000000", 2) # Not authoritative. 

50 AA_AUTH_ANSWER = int("0000010000000000", 2) # Is authoritative. 

51 # TC - Truncated - 1 bit 

52 TC_NOT_TRUNCATED = int("0000000000000000", 2) # Not truncated. 

53 TC_TRUNCATED = int("0000001000000000", 2) # Message truncated. 

54 # RD - Recursion Desired - 1 bit 

55 RD_NOT_RECURSIVE_QUERY = int("0000000000000000", 2) # Recursion not desired. 

56 RD_RECURSIVE_QUERY = int("0000000100000000", 2) # Recursion desired. 

57 # RA - Recursion Available - 1 bit 

58 RA_NOT_AVAILABLE = int("0000000000000000", 2) # Recursive query support not available. 

59 RA_AVAILABLE = int("0000000010000000", 2) # Recursive query support available. 

60 # Z - 3 bits 

61 Z = int("0000000000000000", 2) 

62 # AD - Authenticated Data - 1 bit 

63 AUTHENTICATED_DATA = int("0000000000100000", 2) 

64 # CD - Checking Disabled - 1 bit 

65 CHECKING_DISABLED = int("0000000000010000", 2) 

66 # RCODE - 4 bits 

67 RCODE_NO_ERROR = int("0000000000000000", 2) # The request completed successfully. 

68 RCODE_FORMAT_ERROR = int("0000000000001000", 2) # The name server was unable to interpret the query. 

69 RCODE_SERVER_FAILURE = int("0000000000000100", 2) # The name server was unable to process this query due to a problem with the name server. 

70 RCODE_NAME_ERROR = int("0000000000001100", 2) # Meaningful only for responses from an authoritative name server, this code signifies that the domain name referenced in the query does not exist. 

71 RCODE_NOT_IMPLEMENTED = int("0000000000000010", 2) # Not Implemented. The name server does not support the requested kind of query. 

72 RCODE_REFUSED = int("0000000000001010", 2) # The name server refuses to perform the specified operation for policy reasons.  

73 RCODE_YXDOMAIN = int("0000000000000110", 2) # Name Exists when it should not. 

74 RCODE_YXRRSET = int("0000000000001110", 2) # RR Set Exists when it should not. 

75 RCODE_NXRRSET = int("0000000000000001", 2) # RR Set that should exist does not. 

76 RCODE_NOAUTH = int("0000000000001001", 2) # Server Not Authoritative for zone. 

77 RCODE_NOTZONE = int("0000000000000101", 2) # Name not contained in zone. 

78 

79class DNSType(): 

80 A = 1 # IPv4 address. 

81 NS = 2 # Authoritative name server. 

82 MD = 3 # Mail destination. Obsolete use MX instead. 

83 MF = 4 # Mail forwarder. Obsolete use MX instead. 

84 CNAME = 5 # Canonical name for an alias. 

85 SOA = 6 # Marks the start of a zone of authority. 

86 MB = 7 # Mailbox domain name. 

87 MG = 8 # Mail group member. 

88 MR = 9 # Mail rename domain name. 

89 NULL = 10 # Null resource record. 

90 WKS = 11 # Well known service description. 

91 PTR = 12 # Domain name pointer. 

92 HINFO = 13 # Host information. 

93 MINFO = 14 # Mailbox or mail list information. 

94 MX = 15 # Mail exchange. 

95 TXT = 16 # Text strings. 

96 RP = 17 # Responsible Person. 

97 AFSDB = 18 # AFS Data Base location. 

98 X25 = 19 # X.25 PSDN address. 

99 ISDN = 20 # ISDN address. 

100 RT = 21 # Route Through. 

101 NSAP = 22 # NSAP address. NSAP style A record. 

102 NSAP_PTR = 23 # NSAP pointer. 

103 SIG = 24 # Security signature. 

104 KEY = 25 # Security key. 

105 PX = 26 # X.400 mail mapping information. 

106 GPOS = 27 # Geographical Position. 

107 AAAA = 28 # IPv6 Address. 

108 LOC = 29 # Location Information. 

109 NXT = 30 # Next Domain (obsolete). 

110 EID = 31 # Endpoint Identifier. 

111 NB = 32 # NetBIOS general Name Service. 

112 NBSTAT = 33 # NetBIOS NODE STATUS. 

113 ATMA = 34 # ATM Address. 

114 NAPTR = 35 # Naming Authority Pointer. 

115 KX = 36 # Key Exchanger. 

116 CERT = 37 

117 A6 = 38 

118 DNAME = 39 

119 SINK = 40 

120 OPT = 41 

121 APL = 42 

122 DS = 43 # Delegation Signer. 

123 SSHFP = 44 # SSH Key Fingerprint. 

124 IPSECKEY = 45 

125 RRSIG = 46 

126 NSEC = 47 # NextSECure. 

127 DNSKEY = 48 

128 DHCID = 49 # DHCP identifier. 

129 NSEC3 = 50 

130 NSEC3PARAM = 51 

131 

132 HIP = 55 # Host Identity Protocol. 

133 NINFO = 56 

134 RKEY = 57 

135 

136 SPF = 99 # Sender Policy Framework. 

137 UINFO = 100 

138 UID = 101 

139 GID = 102 

140 UNSPEC = 103 

141 

142 TKEY = 249 

143 TSIG = 250 # Transaction Signature. 

144 IXFR = 251 # Incremental transfer. 

145 AXFR = 252 # A request for a transfer of an entire zone. 

146 MAILB = 253 # A request for mailbox-related records (MB, MG or MR). 

147 MAILA = 254 # A request for mail agent RRs. Obsolete. 

148 ALL = 255 # A request for all records. 

149 

150 DNSSEC = 32768 # Trust Authorities. 

151 DNSSEC = 32769 # DNSSEC Lookaside Validation. 

152 

153 @staticmethod 

154 def getTypeName(type): 

155 for item, value in list(DNSType.__dict__.items()): 155 ↛ exitline 155 didn't return from function 'getTypeName', because the loop on line 155 didn't complete

156 if value == type: 

157 return item 

158 

159 

160class DNSClass(): 

161 RESERVED = 0 

162 IN = 1 # Internet. 

163 CH = 3 # Chaos. 

164 HS = 4 # Hesiod. 

165 

166 NONE = 254 

167 ANY = 255 # QCLASS only 

168 

169 @staticmethod 

170 def getClassName(type): 

171 for item, value in list(DNSClass.__dict__.items()): 171 ↛ exitline 171 didn't return from function 'getClassName', because the loop on line 171 didn't complete

172 if value == type: 

173 return item 

174 

175class DNS(ProtocolPacket): 

176 '''The Message Header is present in all messages. Never empty. 

177 Contains various flags and values which control the transaction.''' 

178 

179 __TYPE_LEN = 2 # Unsigned 16 bit value. 

180 __CLASS_LEN = 2 # Unsigned 16 bit value. 

181 __POINTER_LEN = 2 # A pointer is an unsigned 16-bit value. 

182 __TTL_LEN = 4 # Unsigned 32 bit value. The time in seconds that the record may be cached. 

183 __RDLENGTH_LEN = 2 # Unsigned 16-bit value that defines the length in bytes (octets) of the RDATA record. 

184 __TYPE_A_LEN = 4 # Unsigned 32-bit value representing the IP address. 

185 __SERIAL_LEN = 4 # Serial Number Unsigned 32-bit integer. 

186 __REFRESH_LEN = 4 # Refresh interval Unsigned 32-bit integer. 

187 __RETRY_LEN = 4 # Retry Interval Unsigned 32-bit integer. 

188 __EXPIRATION_LEN = 4 # Expiration Limit Unsigned 32-bit integer. 

189 __MINTTL_LEN = 4 # Minimum TTL Unsigned 32-bit integer. 

190 __PREF_LEN = 2 # Preference Unsigned 16-bit integer. 

191 __IS_POINTER = int("11000000", 2) 

192 __OFFSETMASK = int("00111111", 2) 

193 

194 def __init__(self, aBuffer = None): 

195 self.__HEADER_BASE_SIZE = 12 

196 self.__TAIL_SIZE = 0 

197 ProtocolPacket.__init__(self, self.__HEADER_BASE_SIZE, self.__TAIL_SIZE) 

198 if aBuffer: 198 ↛ exitline 198 didn't return from function '__init__', because the condition on line 198 was never false

199 self.load_packet(aBuffer) 

200 

201 def get_transaction_id(self): 

202 'Get 16 bit message ID.' 

203 return self.header.get_word(0) 

204 

205 def set_transaction_id(self, value): 

206 'Set 16 bit message ID.' 

207 self.header.set_word(0, value) 

208 

209 def get_transaction_id_tcp(self): 

210 'Get 16 bit message ID.' 

211 return self.header.get_word(2) 

212 

213 def set_transaction_id_tcp(self, value): 

214 'Set 16 bit message ID.' 

215 self.header.set_word(2, value) 

216 

217 def get_flags(self): 

218 'Get 16 bit flags.' 

219 return self.header.get_word(2) 

220 

221 def set_flags(self, value): 

222 'Set 16 bit flags.' 

223 self.header.set_word(2, value) 

224 

225 def get_flags_tcp(self): 

226 'Get 16 bit flags.' 

227 return self.header.get_word(4) 

228 

229 def set_flags_tcp(self, value): 

230 'Set 16 bit flags.' 

231 self.header.set_word(4, value) 

232 

233 def get_qdcount(self): 

234 'Get Unsigned 16 bit integer specifying the number of entries in the question section.' 

235 return self.header.get_word(4) 

236 

237 def set_qdcount(self, value): 

238 'Set Unsigned 16 bit integer specifying the number of entries in the question section.' 

239 self.header.set_word(4, value) 

240 

241 def get_qdcount_tcp(self): 

242 'Get Unsigned 16 bit integer specifying the number of entries in the question section.' 

243 return self.header.get_word(6) 

244 

245 def set_qdcount_tcp(self, value): 

246 'Set Unsigned 16 bit integer specifying the number of entries in the question section.' 

247 self.header.set_word(6, value) 

248 

249 def get_ancount(self): 

250 'Get Unsigned 16 bit integer specifying the number of resource records in the answer section' 

251 return self.header.get_word(6) 

252 

253 def set_ancount(self, value): 

254 'Set Unsigned 16 bit integer specifying the number of resource records in the answer section' 

255 self.header.set_word(6, value) 

256 

257 def get_nscount(self): 

258 'Get Unsigned 16 bit integer specifying the number of name server resource records in the authority section.' 

259 return self.header.get_word(8) 

260 

261 def set_nscount(self, value): 

262 'Set Unsigned 16 bit integer specifying the number of name server resource records in the authority section.' 

263 self.header.set_word(8, value) 

264 

265 def get_arcount(self): 

266 'Get Unsigned 16 bit integer specifying the number of resource records in the additional records section.' 

267 return self.header.get_word(10) 

268 

269 def set_arcount(self, value): 

270 'Set Unsigned 16 bit integer specifying the number of resource records in the additional records section.' 

271 self.header.set_word(10, value) 

272 

273 def get_questions(self): 

274 'Get a list of the DNS Question.' 

275 return self.__get_questions()[0] 

276 

277 def __get_questions(self): 

278 aux = [] 

279 offset = 0 

280 qdcount = self.get_qdcount() 

281 data = self.get_body_as_string() 

282 for _ in range(qdcount): # number of questions 

283 offset, qname = self.parseCompressedMessage(data, offset) 

284 qtype = data[offset:offset+self.__TYPE_LEN] 

285 offset += self.__TYPE_LEN 

286 qclass = data[offset:offset+self.__CLASS_LEN] 

287 offset += self.__CLASS_LEN 

288 qtype = struct.unpack("!H", qtype)[0] 

289 qclass = struct.unpack("!H", qclass)[0] 

290 aux.append((qname, qtype, qclass)) 

291 return (aux, offset) 

292 

293 def get_questions_tcp(self): 

294 'Get a list of the DNS Question.' 

295 return self.__get_questions_tcp()[0] 

296 

297 def __get_questions_tcp(self): 

298 aux = [] 

299 offset = 2 

300 qdcount = self.get_qdcount_tcp() 

301 data = self.get_body_as_string() 

302 for _ in range(qdcount): # number of questions 

303 offset, qname = self.parseCompressedMessage(data, offset) 

304 qtype = data[offset:offset+self.__TYPE_LEN] 

305 offset += self.__TYPE_LEN 

306 qclass = data[offset:offset+self.__CLASS_LEN] 

307 offset += self.__CLASS_LEN 

308 qtype = struct.unpack("!H", qtype)[0] 

309 qclass = struct.unpack("!H", qclass)[0] 

310 aux.append((qname, qtype, qclass)) 

311 return (aux, offset) 

312 

313 def parseCompressedMessage(self, buf, offset=0): 

314 'Parse compressed message defined on rfc1035 4.1.4.' 

315 if offset >= len(buf): 315 ↛ 316line 315 didn't jump to line 316, because the condition on line 315 was never true

316 raise Exception("No more data to parse. Offset is bigger than length of buffer.") 

317 byte = struct.unpack("B", buf[offset:offset+1])[0] 

318 # if the first two bits are ones (11000000=0xC0), the next bits are the offset 

319 if byte & 0xC0 == 0xC0: 

320 # It's a pointer 

321 pointer = struct.unpack("!H", buf[offset:offset+2])[0] # network unsigned short 

322 pointer = (pointer & 0x3FFF) - self.__HEADER_BASE_SIZE 

323 if offset == pointer: 323 ↛ 324line 323 didn't jump to line 324, because the condition on line 323 was never true

324 raise Exception("The infinite loop is in DNS decompression. Encountered pointer points to the current offset.") 

325 offset += 2 

326 name = self.parseCompressedMessage(buf, pointer)[1] 

327 return (offset, name) 

328 else: 

329 # It's a label 

330 if byte == 0x00: 

331 offset += 1 

332 return (offset, '') 

333 offset += 1 

334 name = buf[offset:offset+byte] 

335 offset += byte 

336 offset, unnamed = self.parseCompressedMessage(buf, offset) 

337 if not unnamed: 

338 return (offset, name) 

339 else: 

340 return (offset, name + b"." + unnamed) 

341 

342 def get_answers(self): 

343 return self.__get_answers()[0] 

344 

345 def get_authoritative(self): 

346 return self.__get_authoritative()[0] 

347 

348 def get_additionals(self): 

349 return self.__get_additionals()[0] 

350 

351 def __get_answers(self): 

352 offset = self.__get_questions()[1] # get the initial offset 

353 ancount = self.get_ancount() 

354 return self.__process_answer_structure(offset, ancount) 

355 

356 def __get_authoritative(self): 

357 'Get a list of the DNS Authoritative.' 

358 offset = self.__get_answers()[1] # get the initial offset 

359 nscount = self.get_nscount() 

360 return self.__process_answer_structure(offset, nscount) 

361 

362 def __get_additionals(self): 

363 'Get a list of the DNS Additional Records.' 

364 offset = self.__get_authoritative()[1] # get the initial offset 

365 arcount = self.get_arcount() 

366 return self.__process_answer_structure(offset, arcount) 

367 

368 def __process_answer_structure(self, offset, num): 

369 aux = [] 

370 data = self.get_body_as_string() 

371 for _ in range(num): 

372 offset, qname = self.parseCompressedMessage(data, offset) 

373 qtype = data[offset:offset+self.__TYPE_LEN] 

374 qtype = struct.unpack("!H", qtype)[0] 

375 offset += self.__TYPE_LEN 

376 

377 qclass = data[offset:offset+self.__CLASS_LEN] 

378 qclass = struct.unpack("!H", qclass)[0] 

379 offset += self.__CLASS_LEN 

380 

381 qttl_raw = data[offset:offset+self.__TTL_LEN] 

382 qttl = struct.unpack("!L", qttl_raw)[0] 

383 offset += self.__TTL_LEN 

384 

385 qrdlength = data[offset:offset+self.__RDLENGTH_LEN] 

386 qrdlength = struct.unpack("!H", qrdlength)[0] 

387 offset += self.__RDLENGTH_LEN 

388 

389 qrdata = {} 

390 if qtype == DNSType.A: 

391 # IP Address Unsigned 32-bit value representing the IP address 

392 qrdata["IPAddress"] = socket.inet_ntoa(data[offset:offset+qrdlength]) 

393 offset += self.__TYPE_A_LEN 

394 elif qtype == DNSType.SOA: 394 ↛ 396line 394 didn't jump to line 396, because the condition on line 394 was never true

395 # Primary NS Variable length. The name of the Primary Master for the domain. May be a label, pointer or any combination. 

396 offset, primaryNs = self.parseCompressedMessage(data, offset) 

397 qrdata["PrimaryNS"] = primaryNs 

398 # Admin MB Variable length. The administrator's mailbox. May be a label, pointer or any combination. 

399 offset, adminMb = self.parseCompressedMessage(data, offset) 

400 qrdata["AdminMB"] = adminMb 

401 # Serial Number Unsigned 32-bit integer. 

402 qrdata["SerialNumber"] = struct.unpack("!L", data[offset:offset+self.__SERIAL_LEN])[0] 

403 offset += self.__SERIAL_LEN 

404 # Refresh interval Unsigned 32-bit integer. 

405 qrdata["RefreshInterval"] = struct.unpack("!L", data[offset:offset+self.__REFRESH_LEN])[0] 

406 offset += self.__REFRESH_LEN 

407 # Retry Interval Unsigned 32-bit integer. 

408 qrdata["RetryInterval"] = struct.unpack("!L", data[offset:offset+self.__RETRY_LEN])[0] 

409 offset += self.__RETRY_LEN 

410 # Expiration Limit Unsigned 32-bit integer. 

411 qrdata["ExpirationLimit"] = struct.unpack("!L", data[offset:offset+self.__EXPIRATION_LEN])[0] 

412 offset += self.__EXPIRATION_LEN 

413 # Minimum TTL Unsigned 32-bit integer. 

414 qrdata["MinimumTTL"] = struct.unpack("!L", data[offset:offset+self.__MINTTL_LEN])[0] 

415 offset += self.__MINTTL_LEN 

416 elif qtype == DNSType.MX: 416 ↛ 418line 416 didn't jump to line 418, because the condition on line 416 was never true

417 # Preference Unsigned 16-bit integer. 

418 qrdata["Preference"] = struct.unpack("!H", data[offset:offset+self.__PREF_LEN])[0] 

419 # Mail Exchanger The name host name that provides the service. May be a label, pointer or any combination. 

420 offset, mailExch = self.parseCompressedMessage(data, offset) 

421 qrdata["MailExchanger"] = mailExch 

422 elif qtype == DNSType.PTR or qtype == DNSType.NS or qtype == DNSType.CNAME: 422 ↛ 426line 422 didn't jump to line 426, because the condition on line 422 was never false

423 # Name The host name that represents the supplied IP address (in the case of a PTR) or the NS name for the supplied domain (in the case of NS). May be a label, pointer or any combination. 

424 offset, name = self.parseCompressedMessage(data, offset) 

425 qrdata["Name"] = str(name.decode('ascii')) 

426 elif qtype == DNSType.OPT: 

427 # rfc2671 4.3 

428 #NAME domain name empty (root domain) 

429 #TYPE u_int16_t OPT 

430 #CLASS u_int16_t sender's UDP payload size 

431 #TTL u_int32_t extended RCODE and flags 

432 #RDLEN u_int16_t describes RDATA 

433 #RDATA octet stream {attribute,value} pairs 

434 #udp_payload = qclass 

435 udp_payload_size = qclass 

436 ext_rcode = struct.unpack("B", qttl_raw[0:1])[0] 

437 version = struct.unpack("B", qttl_raw[1:2])[0] 

438 flags = struct.unpack("!H", qttl_raw[2:4])[0] 

439 qrdata["RDATA"] = data[offset:offset+qrdlength] 

440 offset += qrdlength 

441 aux.append((qname, qtype, udp_payload_size, ext_rcode, version, flags, qrdata)) 

442 continue 

443 else: 

444 # We don't know how to parse it, just skip it 

445 offset += qrdlength 

446 

447 aux.append((qname, qtype, qclass, qttl, qrdata)) 

448 return (aux, offset) 

449 

450 def get_header_size(self): 

451 return self.__HEADER_BASE_SIZE 

452 

453 def __str__(self): 

454 res = "" 

455 

456 id = self.get_transaction_id() 

457 flags = self.get_flags() 

458 qdcount = self.get_qdcount() 

459 ancount = self.get_ancount() 

460 nscount = self.get_nscount() 

461 arcount = self.get_arcount() 

462 

463 res += "DNS " 

464 if flags & DNSFlags.QR_RESPONSE: 

465 res += "RESPONSE\n" 

466 else: 

467 res += "QUERY\n" 

468 

469 res += " - Transaction ID -- [0x%04x] %d\n" % (id, id) 

470 res += " - Flags ----------- [0x%04x] %d\n" % (flags, flags) 

471 res += " - QdCount --------- [0x%04x] %d\n" % (qdcount, qdcount) 

472 res += " - AnCount --------- [0x%04x] %d\n" % (ancount, ancount) 

473 res += " - NsCount --------- [0x%04x] %d\n" % (nscount, nscount) 

474 res += " - ArCount --------- [0x%04x] %d\n" % (arcount, arcount) 

475 

476 if qdcount > 0: 476 ↛ 485line 476 didn't jump to line 485, because the condition on line 476 was never false

477 res += " - Questions:\n" 

478 questions = self.get_questions() 

479 questions.reverse() 

480 while(questions): 

481 qname, qtype, qclass = questions.pop() 

482 format = (str(qname.decode('ascii')), DNSType.getTypeName(qtype), qtype, DNSClass.getClassName(qclass), qclass) 

483 res += " * Domain: %s - Type: %s [0x%04x] - Class: %s [0x%04x]\n" % format 

484 

485 if ancount > 0: 

486 res += " - Answers:\n" 

487 answers = self.get_answers() 

488 answers.reverse() 

489 while(answers): 

490 qname, qtype, qclass, qttl, qrdata = answers.pop() 

491 format = (str(qname.decode('ascii')), DNSType.getTypeName(qtype), qtype, DNSClass.getClassName(qclass), qclass, qttl, repr(qrdata)) 

492 res += " * Domain: %s - Type: %s [0x%04x] - Class: %s [0x%04x] - TTL: %d seconds - %s\n" % format 

493 

494 if nscount > 0: 

495 res += " - Authoritative:\n" 

496 authoritative = self.get_authoritative() 

497 authoritative.reverse() 

498 while(authoritative): 

499 qname, qtype, qclass, qttl, qrdata = authoritative.pop() 

500 format = (str(qname.decode('ascii')), DNSType.getTypeName(qtype), qtype, DNSClass.getClassName(qclass), qclass, qttl, repr(qrdata)) 

501 res += " * Domain: %s - Type: %s [0x%04x] - Class: %s [0x%04x] - TTL: %d seconds - %s\n" % format 

502 

503 if arcount > 0: 

504 res += " - Additionals:\n" 

505 additionals = self.get_additionals() 

506 for additional in additionals: 

507 qtype = additional[1] 

508 if qtype == DNSType.OPT: 508 ↛ 510line 508 didn't jump to line 510, because the condition on line 508 was never true

509 

510 qname, qtype, udp_payload_size, ext_rcode, version, flags, qrdata = additional 

511 format = (DNSType.getTypeName(qtype), qtype, udp_payload_size, ext_rcode, version, flags, repr(qrdata['RDATA'])) 

512 res += " * Name: <Root> - Type: %s [0x%04x] - udp payload size: [%d] - extended RCODE: [0x%02x] - EDNS0 version: [0x%02x] - Z Flags: [0x%02x] - RDATA: [%s]\n" % format 

513 else: 

514 qname, qtype, qclass, qttl, qrdata = additional 

515 format = (str(qname.decode('ascii')), DNSType.getTypeName(qtype), qtype, DNSClass.getClassName(qclass), qclass, qttl, repr(qrdata)) 

516 res += " * Domain: %s - Type: %s [0x%04x] - Class: %s [0x%04x] - TTL: %d seconds - %s\n" % format 

517 

518 return res 

519 

520 def __get_questions_raw(self): 

521 if self.get_qdcount() == 0: 

522 return '' 

523 questions_offset = self.__get_questions()[1] 

524 raw_data = self.get_body_as_string()[:questions_offset] 

525 return raw_data 

526 

527 def __get_answers_raw(self): 

528 if self.get_ancount() == 0: 

529 return '' 

530 questions_offset = self.__get_questions()[1] 

531 answers_offset = self.__get_answers()[1] 

532 raw_data = self.get_body_as_string()[questions_offset: answers_offset] 

533 return raw_data 

534 

535 def __get_authoritative_raw(self): 

536 if self.get_nscount() == 0: 

537 return '' 

538 answers_offset = self.__get_answers()[1] 

539 authoritative_offset = self.__get_authoritative()[1] 

540 raw_data = self.get_body_as_string()[answers_offset:authoritative_offset] 

541 return raw_data 

542 

543 def __get_additionals_raw(self): 

544 if self.get_arcount() == 0: 

545 return '' 

546 authoritative_offset = self.__get_authoritative()[1] 

547 raw_data = self.get_body_as_string()[authoritative_offset:] 

548 return raw_data 

549 

550 def add_answer(self, answer_raw): 

551 '''Add a raw answer''' 

552 questions_raw = self.__get_questions_raw() 

553 answers_raw = self.__get_answers_raw() 

554 authoritative_raw = self.__get_authoritative_raw() 

555 additionals_raw = self.__get_additionals_raw() 

556 

557 answers_raw += answer_raw 

558 

559 body = questions_raw + answers_raw + authoritative_raw + additionals_raw 

560 self.load_body(body) # It breaks children hierarchy 

561 

562 # Increment the answer count  

563 cur_answer_count = self.get_ancount()+1 

564 self.set_ancount(cur_answer_count) 

565 

566 def is_edns0(self): 

567 additionals = self.get_additionals() 

568 for item in additionals: 

569 response_type = item[1] 

570 if response_type == DNSType.OPT: 

571 return True 

572 return False