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-resolvers.mddocs/

0

# DNS Resolvers

1

2

Ready-to-use resolver implementations including proxy resolvers, fixed response resolvers, zone file resolvers, and shell script resolvers. These implementations provide complete DNS server functionality for different use cases.

3

4

## Capabilities

5

6

### Proxy Resolver

7

8

DNS proxy resolver that forwards queries to upstream servers and returns responses.

9

10

```python { .api }

11

class ProxyResolver(BaseResolver):

12

"""

13

DNS proxy resolver forwarding queries to upstream server.

14

15

Args:

16

address (str): Upstream DNS server address

17

port (int, optional): Upstream DNS server port (default: 53)

18

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

19

tcp (bool, optional): Use TCP for upstream queries (default: False)

20

"""

21

def __init__(self, address, port=53, timeout=5, tcp=False): ...

22

23

def resolve(self, request, handler):

24

"""

25

Forward query to upstream server and return response.

26

27

Args:

28

request (DNSRecord): DNS query to forward

29

handler (DNSHandler): Request handler instance

30

31

Returns:

32

DNSRecord: Response from upstream server

33

"""

34

```

35

36

### Fixed Response Resolver

37

38

Resolver that returns fixed responses to all queries, useful for testing and blocking.

39

40

```python { .api }

41

class FixedResolver(BaseResolver):

42

"""

43

Resolver returning fixed responses to all queries.

44

45

Args:

46

zone (str): Zone file format responses to return

47

ttl (int, optional): TTL for responses (default: 60)

48

"""

49

def __init__(self, zone, ttl=60): ...

50

51

def resolve(self, request, handler):

52

"""

53

Return fixed response regardless of query.

54

55

Args:

56

request (DNSRecord): DNS query (ignored)

57

handler (DNSHandler): Request handler instance

58

59

Returns:

60

DNSRecord: Fixed DNS response

61

"""

62

```

63

64

### Zone File Resolver

65

66

Resolver that serves DNS responses from zone file data with wildcard support.

67

68

```python { .api }

69

class ZoneResolver(BaseResolver):

70

"""

71

Resolver serving responses from zone file data.

72

73

Args:

74

zone (str): Zone file format data

75

glob (bool, optional): Enable wildcard matching (default: False)

76

origin (str, optional): Default origin domain

77

ttl (int, optional): Default TTL value

78

"""

79

def __init__(self, zone, glob=False, origin=None, ttl=None): ...

80

81

def resolve(self, request, handler):

82

"""

83

Resolve query from zone file data.

84

85

Args:

86

request (DNSRecord): DNS query

87

handler (DNSHandler): Request handler instance

88

89

Returns:

90

DNSRecord: DNS response from zone data or NXDOMAIN

91

"""

92

```

93

94

### Shell Script Resolver

95

96

Resolver that calls external shell scripts to generate dynamic responses.

97

98

```python { .api }

99

class ShellResolver(BaseResolver):

100

"""

101

Resolver calling shell scripts for dynamic responses.

102

103

Args:

104

script (str): Path to shell script

105

timeout (int, optional): Script execution timeout (default: 10)

106

shell (bool, optional): Use shell for execution (default: True)

107

"""

108

def __init__(self, script, timeout=10, shell=True): ...

109

110

def resolve(self, request, handler):

111

"""

112

Execute shell script and parse response.

113

114

Args:

115

request (DNSRecord): DNS query

116

handler (DNSHandler): Request handler instance

117

118

Returns:

119

DNSRecord: DNS response from script output

120

"""

121

```

122

123

### Intercepting Proxy Resolver

124

125

Advanced proxy resolver that can intercept and modify responses for specific domains.

126

127

```python { .api }

128

class InterceptResolver(BaseResolver):

129

"""

130

Intercepting proxy resolver with response modification.

131

132

Args:

133

address (str): Upstream DNS server address

134

port (int, optional): Upstream DNS server port (default: 53)

135

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

136

intercept (dict, optional): Domain interception rules

137

"""

138

def __init__(self, address, port=53, timeout=5, intercept=None): ...

139

140

def resolve(self, request, handler):

141

"""

142

Forward query with optional response interception.

143

144

Args:

145

request (DNSRecord): DNS query

146

handler (DNSHandler): Request handler instance

147

148

Returns:

149

DNSRecord: Original or intercepted DNS response

150

"""

151

152

def add_intercept(self, domain, qtype, response):

153

"""

154

Add domain interception rule.

155

156

Args:

157

domain (str): Domain to intercept

158

qtype (str or int): Query type to intercept

159

response (str or DNSRecord): Replacement response

160

"""

161

162

def remove_intercept(self, domain, qtype=None):

163

"""

164

Remove domain interception rule.

165

166

Args:

167

domain (str): Domain to stop intercepting

168

qtype (str or int, optional): Specific query type

169

"""

170

```

171

172

## Command Line Interface

173

174

Each resolver can be run as a standalone DNS server from the command line.

175

176

### Proxy Resolver

177

178

```bash

179

# Basic proxy server

180

python -m dnslib.proxy

181

182

# Proxy with specific upstream server

183

python -m dnslib.proxy --upstream 8.8.8.8

184

185

# Proxy with custom port and TCP

186

python -m dnslib.proxy --port 5353 --upstream 1.1.1.1 --tcp

187

```

188

189

### Fixed Response Resolver

190

191

```bash

192

# Fixed response server

193

python -m dnslib.fixedresolver

194

195

# Fixed response with custom data

196

python -m dnslib.fixedresolver --zone "example.com 300 IN A 192.0.2.1"

197

198

# Fixed response on custom port

199

python -m dnslib.fixedresolver --port 5353

200

```

201

202

### Zone File Resolver

203

204

```bash

205

# Zone file server

206

python -m dnslib.zoneresolver --zone-file /path/to/zone.txt

207

208

# Zone with wildcard support

209

python -m dnslib.zoneresolver --zone-file zone.txt --glob

210

211

# Zone server on custom port

212

python -m dnslib.zoneresolver --zone-file zone.txt --port 5353

213

```

214

215

### Shell Script Resolver

216

217

```bash

218

# Shell script resolver

219

python -m dnslib.shellresolver --script /path/to/resolver.sh

220

221

# Shell resolver with timeout

222

python -m dnslib.shellresolver --script resolver.sh --timeout 5

223

224

# Shell resolver on custom address

225

python -m dnslib.shellresolver --script resolver.sh --address 127.0.0.1 --port 5353

226

```

227

228

## Usage Examples

229

230

### Basic Proxy Server

231

232

```python

233

from dnslib.server import DNSServer

234

from dnslib.proxy import ProxyResolver

235

236

# Create proxy resolver

237

resolver = ProxyResolver("8.8.8.8", port=53, timeout=10)

238

239

# Start server

240

server = DNSServer(resolver, port=5353, address="127.0.0.1")

241

print("Starting DNS proxy server on 127.0.0.1:5353")

242

print("Forwarding to 8.8.8.8:53")

243

server.start()

244

```

245

246

### Fixed Response Server

247

248

```python

249

from dnslib.server import DNSServer

250

from dnslib.fixedresolver import FixedResolver

251

252

# Fixed responses for blocking ads

253

fixed_zone = """

254

ads.example.com. 300 IN A 127.0.0.1

255

tracker.example.com. 300 IN A 127.0.0.1

256

malware.example.com. 300 IN A 127.0.0.1

257

"""

258

259

# Create resolver

260

resolver = FixedResolver(fixed_zone.strip())

261

262

# Start server

263

server = DNSServer(resolver, port=5353, address="127.0.0.1")

264

print("Starting ad-blocking DNS server on 127.0.0.1:5353")

265

server.start()

266

```

267

268

### Zone File Server

269

270

```python

271

from dnslib.server import DNSServer

272

from dnslib.zoneresolver import ZoneResolver

273

274

# Zone file data

275

zone_data = """

276

$TTL 300

277

$ORIGIN example.local.

278

279

@ IN SOA ns1.example.local. admin.example.local. (

280

2023010101 ; Serial

281

3600 ; Refresh

282

1800 ; Retry

283

604800 ; Expire

284

86400 ) ; Minimum

285

286

IN NS ns1.example.local.

287

IN A 192.168.1.1

288

IN MX 10 mail.example.local.

289

290

ns1 IN A 192.168.1.1

291

mail IN A 192.168.1.10

292

www IN A 192.168.1.20

293

ftp IN CNAME www.example.local.

294

295

; Wildcard support

296

*.subdomain IN A 192.168.1.100

297

"""

298

299

# Create resolver with wildcard support

300

resolver = ZoneResolver(zone_data.strip(), glob=True)

301

302

# Start server

303

server = DNSServer(resolver, port=5353, address="127.0.0.1")

304

print("Starting zone file DNS server on 127.0.0.1:5353")

305

server.start()

306

```

307

308

### Shell Script Resolver

309

310

```python

311

from dnslib.server import DNSServer

312

from dnslib.shellresolver import ShellResolver

313

314

# Create shell script resolver

315

# Script should output zone file format responses

316

resolver = ShellResolver("/path/to/dns_resolver.sh", timeout=5)

317

318

# Start server

319

server = DNSServer(resolver, port=5353, address="127.0.0.1")

320

print("Starting shell script DNS server on 127.0.0.1:5353")

321

server.start()

322

```

323

324

Example shell script (`dns_resolver.sh`):

325

```bash

326

#!/bin/bash

327

# DNS resolver shell script

328

# Arguments: QNAME QTYPE QCLASS

329

330

QNAME=$1

331

QTYPE=$2

332

QCLASS=$3

333

334

case "$QNAME" in

335

"time.local.")

336

echo "time.local. 60 IN TXT \"Current time: $(date)\""

337

;;

338

"ip.local.")

339

echo "ip.local. 60 IN TXT \"Your IP: $REMOTE_ADDR\""

340

;;

341

"random.local.")

342

IP="192.168.1.$((RANDOM % 254 + 1))"

343

echo "random.local. 60 IN A $IP"

344

;;

345

*)

346

# Return NXDOMAIN for unknown domains

347

exit 3

348

;;

349

esac

350

```

351

352

### Intercepting Proxy Server

353

354

```python

355

from dnslib.server import DNSServer

356

from dnslib.intercept import InterceptResolver

357

358

# Create intercepting proxy

359

resolver = InterceptResolver("8.8.8.8", port=53)

360

361

# Add interception rules

362

resolver.add_intercept("blocked.com", "A", "127.0.0.1")

363

resolver.add_intercept("ads.example.com", "A", "0.0.0.0")

364

resolver.add_intercept("tracking.com", "*", "127.0.0.1")

365

366

# Start server

367

server = DNSServer(resolver, port=5353, address="127.0.0.1")

368

print("Starting intercepting DNS proxy on 127.0.0.1:5353")

369

print("Intercepting blocked domains")

370

server.start()

371

```

372

373

### Dynamic Load Balancing Resolver

374

375

```python

376

from dnslib.server import DNSServer, BaseResolver

377

from dnslib import *

378

import random

379

import time

380

381

class LoadBalancingResolver(BaseResolver):

382

"""Resolver providing load balancing with health checks."""

383

384

def __init__(self, servers):

385

self.servers = servers

386

self.healthy_servers = servers.copy()

387

self.last_check = 0

388

389

def health_check(self):

390

"""Simple health check for servers."""

391

if time.time() - self.last_check > 60: # Check every minute

392

self.healthy_servers = []

393

for server in self.servers:

394

try:

395

# Simple TCP connect test

396

import socket

397

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

398

sock.settimeout(2)

399

result = sock.connect_ex((server, 80))

400

sock.close()

401

if result == 0:

402

self.healthy_servers.append(server)

403

except:

404

pass

405

self.last_check = time.time()

406

407

def resolve(self, request, handler):

408

reply = request.reply()

409

qname = request.q.qname

410

qtype = request.q.qtype

411

412

if qtype == QTYPE.A and str(qname) == "service.example.com.":

413

self.health_check()

414

if self.healthy_servers:

415

# Return random healthy server

416

server_ip = random.choice(self.healthy_servers)

417

reply.add_answer(RR(qname, QTYPE.A, rdata=A(server_ip), ttl=30))

418

else:

419

reply.header.rcode = RCODE.SERVFAIL

420

else:

421

reply.header.rcode = RCODE.NXDOMAIN

422

423

return reply

424

425

# Server pool

426

server_pool = ["192.168.1.10", "192.168.1.11", "192.168.1.12"]

427

428

# Create and start load balancing resolver

429

resolver = LoadBalancingResolver(server_pool)

430

server = DNSServer(resolver, port=5353, address="127.0.0.1")

431

432

print("Starting load balancing DNS server on 127.0.0.1:5353")

433

server.start()

434

```

435

436

### Conditional Forwarding Resolver

437

438

```python

439

from dnslib.server import DNSServer, BaseResolver

440

from dnslib.proxy import ProxyResolver

441

from dnslib import *

442

443

class ConditionalResolver(BaseResolver):

444

"""Resolver with conditional forwarding rules."""

445

446

def __init__(self, default_upstream, rules):

447

self.default_resolver = ProxyResolver(default_upstream)

448

self.rules = {}

449

450

# Create resolvers for each rule

451

for domain, upstream in rules.items():

452

self.rules[domain] = ProxyResolver(upstream)

453

454

def resolve(self, request, handler):

455

qname = str(request.q.qname).lower()

456

457

# Check forwarding rules

458

for domain, resolver in self.rules.items():

459

if qname.endswith(domain.lower()):

460

return resolver.resolve(request, handler)

461

462

# Use default resolver

463

return self.default_resolver.resolve(request, handler)

464

465

# Forwarding rules

466

forwarding_rules = {

467

"internal.company.com.": "192.168.1.1",

468

"dev.company.com.": "192.168.10.1",

469

"staging.company.com.": "192.168.20.1"

470

}

471

472

# Create conditional resolver

473

resolver = ConditionalResolver("8.8.8.8", forwarding_rules)

474

475

# Start server

476

server = DNSServer(resolver, port=5353, address="127.0.0.1")

477

print("Starting conditional forwarding DNS server on 127.0.0.1:5353")

478

print("Rules:")

479

for domain, upstream in forwarding_rules.items():

480

print(f" {domain} -> {upstream}")

481

print(f" * -> 8.8.8.8")

482

server.start()

483

```

484

485

### Caching Resolver

486

487

```python

488

from dnslib.server import DNSServer, BaseResolver

489

from dnslib.proxy import ProxyResolver

490

from dnslib import *

491

import time

492

493

class CachingResolver(BaseResolver):

494

"""Simple caching DNS resolver."""

495

496

def __init__(self, upstream):

497

self.upstream_resolver = ProxyResolver(upstream)

498

self.cache = {}

499

500

def cache_key(self, request):

501

"""Generate cache key for request."""

502

return (str(request.q.qname), request.q.qtype, request.q.qclass)

503

504

def resolve(self, request, handler):

505

key = self.cache_key(request)

506

now = time.time()

507

508

# Check cache

509

if key in self.cache:

510

response, timestamp, ttl = self.cache[key]

511

if now - timestamp < ttl:

512

# Adjust TTL in response

513

cached_response = DNSRecord.parse(response.pack())

514

age = int(now - timestamp)

515

for rr in cached_response.rr:

516

rr.ttl = max(1, rr.ttl - age)

517

return cached_response

518

else:

519

# Expired, remove from cache

520

del self.cache[key]

521

522

# Query upstream

523

response = self.upstream_resolver.resolve(request, handler)

524

525

# Cache successful responses

526

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

527

min_ttl = min(rr.ttl for rr in response.rr if rr.ttl > 0)

528

if min_ttl > 0:

529

self.cache[key] = (response, now, min_ttl)

530

531

return response

532

533

# Create caching resolver

534

resolver = CachingResolver("8.8.8.8")

535

536

# Start server

537

server = DNSServer(resolver, port=5353, address="127.0.0.1")

538

print("Starting caching DNS server on 127.0.0.1:5353")

539

server.start()

540

```