or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

dns-client.mddns-core.mddns-records.mddns-resolvers.mddns-server.mddns-utils.mdindex.md

dns-client.mddocs/

0

# DNS Client Utilities

1

2

DiG-like DNS client functionality provided as a command-line tool for querying DNS servers, comparing responses, and debugging DNS configurations. The primary interface is through the command-line utility, with programmatic access available through the core DNSRecord API.

3

4

## Capabilities

5

6

### Programmatic DNS Queries

7

8

DNS queries can be performed programmatically using the core DNSRecord.send() method and related functionality.

9

10

```python { .api }

11

# Core query functionality (from dnslib.dns)

12

def send(self, dest, port=53, tcp=False, timeout=None, ipv6=False):

13

"""

14

Send DNS query and return response packet.

15

Available on DNSRecord instances.

16

17

Args:

18

dest (str): Destination server address

19

port (int, optional): Destination port (default: 53)

20

tcp (bool, optional): Use TCP instead of UDP (default: False)

21

timeout (int, optional): Query timeout in seconds (default: None)

22

ipv6 (bool, optional): Use IPv6 (default: False)

23

24

Returns:

25

bytes: DNS response packet in wire format

26

"""

27

```

28

29

### DiG Output Parser

30

31

Integration with DiG output parsing for response comparison and validation.

32

33

```python { .api }

34

# From dnslib.digparser

35

class DigParser:

36

"""

37

Parser for DiG textual output format.

38

Enables comparison between dnslib and DiG responses.

39

"""

40

def parse(self, dig_output):

41

"""

42

Parse DiG output into DNSRecord objects.

43

44

Args:

45

dig_output (str): DiG command output text

46

47

Returns:

48

list[DNSRecord]: Parsed DNS records

49

"""

50

```

51

52

## Command Line Interface

53

54

The dnslib.client module provides a comprehensive DiG-like command-line interface for DNS queries.

55

56

### Basic Usage

57

58

```bash

59

# Query A record for domain

60

python -m dnslib.client example.com

61

62

# Query specific record type

63

python -m dnslib.client example.com MX

64

65

# Query specific server

66

python -m dnslib.client --server 8.8.8.8 example.com

67

68

# Use TCP protocol

69

python -m dnslib.client --tcp example.com

70

71

# Enable DNSSEC

72

python -m dnslib.client --dnssec example.com

73

74

# Show query packet

75

python -m dnslib.client --query example.com

76

77

# Show packet in hex

78

python -m dnslib.client --hex example.com

79

80

# Short output (rdata only)

81

python -m dnslib.client --short example.com

82

```

83

84

### Advanced Options

85

86

```bash

87

# Compare responses from different servers

88

python -m dnslib.client --diff 1.1.1.1 --server 8.8.8.8 example.com

89

90

# Compare with DiG output

91

python -m dnslib.client --dig example.com

92

93

# Combine comparison and DiG

94

python -m dnslib.client --diff 1.1.1.1 --dig example.com

95

96

# Disable TCP fallback for truncated responses

97

python -m dnslib.client --noretry example.com

98

99

# Debug mode - drop into CLI after request

100

python -m dnslib.client --debug example.com

101

```

102

103

### Command Line Parameters

104

105

```bash

106

python -m dnslib.client [OPTIONS] <domain> [<type>]

107

108

Options:

109

--server, -s ADDRESS:PORT Server address:port (default: 8.8.8.8:53)

110

--query Show query packet

111

--hex Dump packet in hex format

112

--tcp Use TCP instead of UDP

113

--noretry Don't retry with TCP if truncated

114

--diff ADDRESS:PORT Compare with alternate nameserver

115

--dig Compare result with DiG

116

--short Short output - rdata only

117

--dnssec Set DNSSEC (DO/AD) flags

118

--debug Drop into CLI after request

119

```

120

121

## Usage Examples

122

123

### Basic Programmatic Queries

124

125

```python

126

from dnslib import *

127

128

# Create DNS query

129

q = DNSRecord.question("example.com", "A")

130

131

# Send query to server

132

response_packet = q.send("8.8.8.8", 53)

133

134

# Parse response

135

response = DNSRecord.parse(response_packet)

136

137

# Validate transaction ID

138

if q.header.id != response.header.id:

139

raise DNSError('Response transaction ID mismatch')

140

141

# Extract A records

142

for rr in response.rr:

143

if rr.rtype == QTYPE.A:

144

print(f"IP: {rr.rdata}")

145

```

146

147

### DNSSEC Queries

148

149

```python

150

from dnslib import *

151

152

# Create DNSSEC query

153

q = DNSRecord.question("example.com", "A")

154

q.add_ar(EDNS0(flags="do", udp_len=4096))

155

q.header.ad = 1 # Authentic data flag

156

157

# Send query

158

response_packet = q.send("1.1.1.1", 53)

159

response = DNSRecord.parse(response_packet)

160

161

# Check for DNSSEC data

162

has_rrsig = any(rr.rtype == QTYPE.RRSIG for rr in response.rr)

163

authenticated = bool(response.header.ad)

164

165

print(f"DNSSEC signatures: {has_rrsig}")

166

print(f"Authenticated data: {authenticated}")

167

```

168

169

### TCP Fallback Handling

170

171

```python

172

from dnslib import *

173

174

def query_with_fallback(domain, qtype, server="8.8.8.8"):

175

"""Query with automatic TCP fallback for truncated responses."""

176

177

# Create query

178

q = DNSRecord.question(domain, qtype)

179

180

# Try UDP first

181

try:

182

response_packet = q.send(server, 53, tcp=False)

183

response = DNSRecord.parse(response_packet)

184

185

# Check for truncation

186

if response.header.tc:

187

print("Response truncated, retrying with TCP...")

188

response_packet = q.send(server, 53, tcp=True)

189

response = DNSRecord.parse(response_packet)

190

191

return response

192

193

except Exception as e:

194

print(f"Query failed: {e}")

195

return None

196

197

# Use with large responses that might be truncated

198

response = query_with_fallback("google.com", "TXT")

199

if response:

200

print(f"Got {len(response.rr)} records")

201

```

202

203

### Multiple Server Comparison

204

205

```python

206

from dnslib import *

207

208

def compare_servers(domain, qtype, servers):

209

"""Compare responses from multiple DNS servers."""

210

211

results = {}

212

q = DNSRecord.question(domain, qtype)

213

214

for server in servers:

215

try:

216

response_packet = q.send(server, 53)

217

response = DNSRecord.parse(response_packet)

218

219

# Extract answer data

220

answers = []

221

for rr in response.rr:

222

if rr.rtype == QTYPE[qtype]:

223

answers.append(str(rr.rdata))

224

225

results[server] = {

226

'answers': answers,

227

'rcode': RCODE[response.header.rcode],

228

'response_time': None # Would need timing code

229

}

230

231

except Exception as e:

232

results[server] = {'error': str(e)}

233

234

return results

235

236

# Compare multiple servers

237

servers = ["8.8.8.8", "1.1.1.1", "9.9.9.9"]

238

results = compare_servers("example.com", "A", servers)

239

240

for server, result in results.items():

241

if 'error' in result:

242

print(f"{server}: ERROR - {result['error']}")

243

else:

244

print(f"{server}: {result['rcode']} - {', '.join(result['answers'])}")

245

```

246

247

### DiG Output Parsing

248

249

```python

250

from dnslib.digparser import DigParser

251

import subprocess

252

253

def compare_with_dig(domain, qtype, server="8.8.8.8"):

254

"""Compare dnslib response with DiG output."""

255

256

# Query using dnslib

257

q = DNSRecord.question(domain, qtype)

258

dnslib_packet = q.send(server, 53)

259

dnslib_response = DNSRecord.parse(dnslib_packet)

260

261

# Query using DiG

262

dig_cmd = f"dig +qr +noedns +noadflag -p 53 {domain} {qtype} @{server}"

263

dig_output = subprocess.getoutput(dig_cmd)

264

265

# Parse DiG output

266

parser = DigParser()

267

dig_records = parser.parse(dig_output)

268

269

# Compare responses

270

print("dnslib response:")

271

print(dnslib_response)

272

print("\nDigg response:")

273

for record in dig_records:

274

print(record)

275

276

# Example comparison

277

compare_with_dig("example.com", "A")

278

```

279

280

### Bulk DNS Queries

281

282

```python

283

from dnslib import *

284

import concurrent.futures

285

import time

286

287

def query_domain(domain, qtype="A", server="8.8.8.8"):

288

"""Query single domain with timing."""

289

start_time = time.time()

290

291

try:

292

q = DNSRecord.question(domain, qtype)

293

response_packet = q.send(server, 53)

294

response = DNSRecord.parse(response_packet)

295

query_time = (time.time() - start_time) * 1000

296

297

answers = []

298

for rr in response.rr:

299

if rr.rtype == QTYPE[qtype]:

300

answers.append(str(rr.rdata))

301

302

return {

303

'domain': domain,

304

'answers': answers,

305

'time_ms': query_time,

306

'rcode': RCODE[response.header.rcode]

307

}

308

309

except Exception as e:

310

return {'domain': domain, 'error': str(e)}

311

312

# Bulk query multiple domains

313

domains = ["google.com", "github.com", "stackoverflow.com", "example.com"]

314

315

# Concurrent queries

316

with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:

317

results = list(executor.map(query_domain, domains))

318

319

# Display results

320

for result in results:

321

if 'error' in result:

322

print(f"{result['domain']}: ERROR - {result['error']}")

323

else:

324

print(f"{result['domain']} ({result['time_ms']:.1f}ms): {', '.join(result['answers'])}")

325

```

326

327

### Custom Query Types

328

329

```python

330

from dnslib import *

331

332

def query_all_types(domain, server="8.8.8.8"):

333

"""Query domain for multiple record types."""

334

335

types = ["A", "AAAA", "MX", "NS", "TXT", "CNAME", "SOA"]

336

results = {}

337

338

for qtype in types:

339

try:

340

q = DNSRecord.question(domain, qtype)

341

response_packet = q.send(server, 53)

342

response = DNSRecord.parse(response_packet)

343

344

if response.header.rcode == RCODE.NOERROR and response.rr:

345

records = []

346

for rr in response.rr:

347

if rr.rtype == QTYPE[qtype]:

348

records.append(str(rr.rdata))

349

if records:

350

results[qtype] = records

351

352

except Exception as e:

353

results[qtype] = f"Error: {e}"

354

355

return results

356

357

# Query all common record types

358

all_records = query_all_types("example.com")

359

for record_type, data in all_records.items():

360

print(f"{record_type}: {data}")

361

```