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
```