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

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

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434

435

436

437

438

439

440

441

442

443

444

445

446

447

448

449

450

451

452

453

454

455

456

457

458

459

460

461

462

463

464

465

466

467

468

469

470

471

472

473

474

475

476

477

478

479

480

481

482

483

484

485

486

487

488

489

490

491

492

493

494

495

496

497

498

499

500

501

502

503

504

505

506

507

508

509

510

511

512

513

514

515

516

517

518

519

520

521

522

523

524

525

526

527

528

529

530

531

532

533

534

535

536

537

538

539

540

541

542

543

544

545

546

547

548

549

550

551

552

553

554

555

556

557

558

559

560

561

562

563

564

565

566

567

568

569

570

571

572

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

# 

# This software is provided under under a slightly modified version 

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

# for more information. 

# 

# Author: 

# Andres Blanco 

# Gustavo Moreira 

 

# 

# RFCs for the DNS Server service 

# 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

# 

 

import socket 

import struct 

 

from impacket.ImpactPacket import ProtocolPacket 

 

 

class DNSFlags(): 

'Bitmap with the flags of a dns packet.' 

# QR - Query/Response - 1 bit  

QR_QUERY = int("0000000000000000", 2) 

QR_RESPONSE = int("1000000000000000", 2) 

# OP - Opcode - 4 bits 

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

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

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

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

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

# AA - Authority Answer - 1 bit 

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

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

# TC - Truncated - 1 bit 

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

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

# RD - Recursion Desired - 1 bit 

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

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

# RA - Recursion Available - 1 bit 

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

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

# Z - 3 bits 

Z = int("0000000000000000", 2) 

# AD - Authenticated Data - 1 bit 

AUTHENTICATED_DATA = int("0000000000100000", 2) 

# CD - Checking Disabled - 1 bit 

CHECKING_DISABLED = int("0000000000010000", 2) 

# RCODE - 4 bits 

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

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

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

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. 

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

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

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

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

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

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

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

 

class DNSType(): 

A = 1 # IPv4 address. 

NS = 2 # Authoritative name server. 

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

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

CNAME = 5 # Canonical name for an alias. 

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

MB = 7 # Mailbox domain name. 

MG = 8 # Mail group member. 

MR = 9 # Mail rename domain name. 

NULL = 10 # Null resource record. 

WKS = 11 # Well known service description. 

PTR = 12 # Domain name pointer. 

HINFO = 13 # Host information. 

MINFO = 14 # Mailbox or mail list information. 

MX = 15 # Mail exchange. 

TXT = 16 # Text strings. 

RP = 17 # Responsible Person. 

AFSDB = 18 # AFS Data Base location. 

X25 = 19 # X.25 PSDN address. 

ISDN = 20 # ISDN address. 

RT = 21 # Route Through. 

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

NSAP_PTR = 23 # NSAP pointer. 

SIG = 24 # Security signature. 

KEY = 25 # Security key. 

PX = 26 # X.400 mail mapping information. 

GPOS = 27 # Geographical Position. 

AAAA = 28 # IPv6 Address. 

LOC = 29 # Location Information. 

NXT = 30 # Next Domain (obsolete). 

EID = 31 # Endpoint Identifier. 

NB = 32 # NetBIOS general Name Service. 

NBSTAT = 33 # NetBIOS NODE STATUS. 

ATMA = 34 # ATM Address. 

NAPTR = 35 # Naming Authority Pointer. 

KX = 36 # Key Exchanger. 

CERT = 37 

A6 = 38 

DNAME = 39 

SINK = 40 

OPT = 41 

APL = 42 

DS = 43 # Delegation Signer. 

SSHFP = 44 # SSH Key Fingerprint. 

IPSECKEY = 45 

RRSIG = 46 

NSEC = 47 # NextSECure. 

DNSKEY = 48 

DHCID = 49 # DHCP identifier. 

NSEC3 = 50 

NSEC3PARAM = 51 

 

HIP = 55 # Host Identity Protocol. 

NINFO = 56 

RKEY = 57 

 

SPF = 99 # Sender Policy Framework. 

UINFO = 100 

UID = 101 

GID = 102 

UNSPEC = 103 

 

TKEY = 249 

TSIG = 250 # Transaction Signature. 

IXFR = 251 # Incremental transfer. 

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

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

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

ALL = 255 # A request for all records. 

 

DNSSEC = 32768 # Trust Authorities. 

DNSSEC = 32769 # DNSSEC Lookaside Validation. 

 

@staticmethod 

def getTypeName(type): 

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

if value == type: 

return item 

 

 

class DNSClass(): 

RESERVED = 0 

IN = 1 # Internet. 

CH = 3 # Chaos. 

HS = 4 # Hesiod. 

 

NONE = 254 

ANY = 255 # QCLASS only 

 

@staticmethod 

def getClassName(type): 

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

if value == type: 

return item 

 

class DNS(ProtocolPacket): 

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

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

 

__TYPE_LEN = 2 # Unsigned 16 bit value. 

__CLASS_LEN = 2 # Unsigned 16 bit value. 

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

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

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

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

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

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

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

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

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

__PREF_LEN = 2 # Preference Unsigned 16-bit integer. 

__IS_POINTER = int("11000000", 2) 

__OFFSETMASK = int("00111111", 2) 

 

def __init__(self, aBuffer = None): 

self.__HEADER_BASE_SIZE = 12 

self.__TAIL_SIZE = 0 

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

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

self.load_packet(aBuffer) 

 

def get_transaction_id(self): 

'Get 16 bit message ID.' 

return self.header.get_word(0) 

 

def set_transaction_id(self, value): 

'Set 16 bit message ID.' 

self.header.set_word(0, value) 

 

def get_transaction_id_tcp(self): 

'Get 16 bit message ID.' 

return self.header.get_word(2) 

 

def set_transaction_id_tcp(self, value): 

'Set 16 bit message ID.' 

self.header.set_word(2, value) 

 

def get_flags(self): 

'Get 16 bit flags.' 

return self.header.get_word(2) 

 

def set_flags(self, value): 

'Set 16 bit flags.' 

self.header.set_word(2, value) 

 

def get_flags_tcp(self): 

'Get 16 bit flags.' 

return self.header.get_word(4) 

 

def set_flags_tcp(self, value): 

'Set 16 bit flags.' 

self.header.set_word(4, value) 

 

def get_qdcount(self): 

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

return self.header.get_word(4) 

 

def set_qdcount(self, value): 

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

self.header.set_word(4, value) 

 

def get_qdcount_tcp(self): 

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

return self.header.get_word(6) 

 

def set_qdcount_tcp(self, value): 

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

self.header.set_word(6, value) 

 

def get_ancount(self): 

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

return self.header.get_word(6) 

 

def set_ancount(self, value): 

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

self.header.set_word(6, value) 

 

def get_nscount(self): 

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

return self.header.get_word(8) 

 

def set_nscount(self, value): 

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

self.header.set_word(8, value) 

 

def get_arcount(self): 

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

return self.header.get_word(10) 

 

def set_arcount(self, value): 

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

self.header.set_word(10, value) 

 

def get_questions(self): 

'Get a list of the DNS Question.' 

return self.__get_questions()[0] 

 

def __get_questions(self): 

aux = [] 

offset = 0 

qdcount = self.get_qdcount() 

data = self.get_body_as_string() 

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

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

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

offset += self.__TYPE_LEN 

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

offset += self.__CLASS_LEN 

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

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

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

return (aux, offset) 

 

def get_questions_tcp(self): 

'Get a list of the DNS Question.' 

return self.__get_questions_tcp()[0] 

 

def __get_questions_tcp(self): 

aux = [] 

offset = 2 

qdcount = self.get_qdcount_tcp() 

data = self.get_body_as_string() 

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

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

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

offset += self.__TYPE_LEN 

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

offset += self.__CLASS_LEN 

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

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

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

return (aux, offset) 

 

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

'Parse compressed message defined on rfc1035 4.1.4.' 

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

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

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

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

if byte & 0xC0 == 0xC0: 

# It's a pointer 

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

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

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

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

offset += 2 

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

return (offset, name) 

else: 

# It's a label 

if byte == 0x00: 

offset += 1 

return (offset, '') 

offset += 1 

name = buf[offset:offset+byte] 

offset += byte 

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

if not unnamed: 

return (offset, name) 

else: 

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

 

def get_answers(self): 

return self.__get_answers()[0] 

 

def get_authoritative(self): 

return self.__get_authoritative()[0] 

 

def get_additionals(self): 

return self.__get_additionals()[0] 

 

def __get_answers(self): 

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

ancount = self.get_ancount() 

return self.__process_answer_structure(offset, ancount) 

 

def __get_authoritative(self): 

'Get a list of the DNS Authoritative.' 

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

nscount = self.get_nscount() 

return self.__process_answer_structure(offset, nscount) 

 

def __get_additionals(self): 

'Get a list of the DNS Additional Records.' 

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

arcount = self.get_arcount() 

return self.__process_answer_structure(offset, arcount) 

 

def __process_answer_structure(self, offset, num): 

aux = [] 

data = self.get_body_as_string() 

for _ in range(num): 

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

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

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

offset += self.__TYPE_LEN 

 

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

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

offset += self.__CLASS_LEN 

 

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

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

offset += self.__TTL_LEN 

 

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

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

offset += self.__RDLENGTH_LEN 

 

qrdata = {} 

if qtype == DNSType.A: 

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

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

offset += self.__TYPE_A_LEN 

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

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

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

qrdata["PrimaryNS"] = primaryNs 

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

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

qrdata["AdminMB"] = adminMb 

# Serial Number Unsigned 32-bit integer. 

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

offset += self.__SERIAL_LEN 

# Refresh interval Unsigned 32-bit integer. 

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

offset += self.__REFRESH_LEN 

# Retry Interval Unsigned 32-bit integer. 

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

offset += self.__RETRY_LEN 

# Expiration Limit Unsigned 32-bit integer. 

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

offset += self.__EXPIRATION_LEN 

# Minimum TTL Unsigned 32-bit integer. 

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

offset += self.__MINTTL_LEN 

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

# Preference Unsigned 16-bit integer. 

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

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

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

qrdata["MailExchanger"] = mailExch 

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

# 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. 

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

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

elif qtype == DNSType.OPT: 

# rfc2671 4.3 

#NAME domain name empty (root domain) 

#TYPE u_int16_t OPT 

#CLASS u_int16_t sender's UDP payload size 

#TTL u_int32_t extended RCODE and flags 

#RDLEN u_int16_t describes RDATA 

#RDATA octet stream {attribute,value} pairs 

#udp_payload = qclass 

udp_payload_size = qclass 

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

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

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

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

offset += qrdlength 

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

continue 

else: 

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

offset += qrdlength 

 

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

return (aux, offset) 

 

def get_header_size(self): 

return self.__HEADER_BASE_SIZE 

 

def __str__(self): 

res = "" 

 

id = self.get_transaction_id() 

flags = self.get_flags() 

qdcount = self.get_qdcount() 

ancount = self.get_ancount() 

nscount = self.get_nscount() 

arcount = self.get_arcount() 

 

res += "DNS " 

if flags & DNSFlags.QR_RESPONSE: 

res += "RESPONSE\n" 

else: 

res += "QUERY\n" 

 

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

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

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

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

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

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

 

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

res += " - Questions:\n" 

questions = self.get_questions() 

questions.reverse() 

while(questions): 

qname, qtype, qclass = questions.pop() 

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

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

 

if ancount > 0: 

res += " - Answers:\n" 

answers = self.get_answers() 

answers.reverse() 

while(answers): 

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

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

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

 

if nscount > 0: 

res += " - Authoritative:\n" 

authoritative = self.get_authoritative() 

authoritative.reverse() 

while(authoritative): 

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

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

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

 

if arcount > 0: 

res += " - Additionals:\n" 

additionals = self.get_additionals() 

for additional in additionals: 

qtype = additional[1] 

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

 

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

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

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 

else: 

qname, qtype, qclass, qttl, qrdata = additional 

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

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

 

return res 

 

def __get_questions_raw(self): 

if self.get_qdcount() == 0: 

return '' 

questions_offset = self.__get_questions()[1] 

raw_data = self.get_body_as_string()[:questions_offset] 

return raw_data 

 

def __get_answers_raw(self): 

if self.get_ancount() == 0: 

return '' 

questions_offset = self.__get_questions()[1] 

answers_offset = self.__get_answers()[1] 

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

return raw_data 

 

def __get_authoritative_raw(self): 

if self.get_nscount() == 0: 

return '' 

answers_offset = self.__get_answers()[1] 

authoritative_offset = self.__get_authoritative()[1] 

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

return raw_data 

 

def __get_additionals_raw(self): 

if self.get_arcount() == 0: 

return '' 

authoritative_offset = self.__get_authoritative()[1] 

raw_data = self.get_body_as_string()[authoritative_offset:] 

return raw_data 

 

def add_answer(self, answer_raw): 

'''Add a raw answer''' 

questions_raw = self.__get_questions_raw() 

answers_raw = self.__get_answers_raw() 

authoritative_raw = self.__get_authoritative_raw() 

additionals_raw = self.__get_additionals_raw() 

 

answers_raw += answer_raw 

 

body = questions_raw + answers_raw + authoritative_raw + additionals_raw 

self.load_body(body) # It breaks children hierarchy 

 

# Increment the answer count  

cur_answer_count = self.get_ancount()+1 

self.set_ancount(cur_answer_count) 

 

def is_edns0(self): 

additionals = self.get_additionals() 

for item in additionals: 

response_type = item[1] 

if response_type == DNSType.OPT: 

return True 

return False