0
# DNS Server Framework
1
2
Framework for creating custom DNS resolvers with UDP/TCP server support, request handling, and logging capabilities. The framework provides a simple interface for building DNS servers by subclassing BaseResolver and implementing custom resolution logic.
3
4
## Capabilities
5
6
### DNS Server
7
8
Main DNS server class providing UDP and TCP protocol support with threading and logging.
9
10
```python { .api }
11
class DNSServer:
12
"""
13
DNS server with UDP/TCP protocol support and request handling.
14
15
Args:
16
resolver (BaseResolver): Resolver instance for handling queries
17
port (int, optional): Server port (default: 53)
18
address (str, optional): Server bind address (default: "")
19
logger (DNSLogger, optional): Logger instance
20
tcp (bool, optional): Enable TCP server (default: False)
21
ipv6 (bool, optional): Enable IPv6 support (default: False)
22
timeout (int, optional): Request timeout in seconds (default: 5)
23
handler (class, optional): Custom request handler class
24
"""
25
def __init__(self, resolver, port=53, address="", logger=None, **kwargs): ...
26
27
def start(self):
28
"""
29
Start DNS server and handle requests (blocking).
30
Server runs until interrupted.
31
"""
32
33
def start_thread(self):
34
"""
35
Start DNS server in background thread (non-blocking).
36
37
Returns:
38
threading.Thread: Server thread object
39
"""
40
41
def stop(self):
42
"""
43
Stop DNS server and cleanup resources.
44
"""
45
46
def isAlive(self):
47
"""
48
Check if server thread is running.
49
50
Returns:
51
bool: True if server is running
52
"""
53
```
54
55
### DNS Request Handler
56
57
Base request handler for processing DNS queries and generating responses.
58
59
```python { .api }
60
class DNSHandler:
61
"""
62
Base DNS request handler for UDP and TCP protocols.
63
Processes incoming DNS queries and generates responses.
64
"""
65
def handle(self):
66
"""
67
Handle incoming DNS request.
68
Parses request, calls resolver, and sends response.
69
"""
70
71
def get_reply(self, data):
72
"""
73
Process DNS query data and generate response.
74
75
Args:
76
data (bytes): Raw DNS query packet
77
78
Returns:
79
bytes: DNS response packet
80
"""
81
```
82
83
### Protocol Servers
84
85
Low-level UDP and TCP server implementations with threading support.
86
87
```python { .api }
88
class UDPServer:
89
"""
90
Threaded UDP server for DNS queries.
91
92
Args:
93
server_address (tuple): (address, port) tuple
94
handler (class): Request handler class
95
"""
96
def __init__(self, server_address, handler): ...
97
98
def serve_forever(self):
99
"""Start serving requests until interrupted."""
100
101
def shutdown(self):
102
"""Shutdown server and close socket."""
103
104
class TCPServer:
105
"""
106
Threaded TCP server for DNS queries.
107
108
Args:
109
server_address (tuple): (address, port) tuple
110
handler (class): Request handler class
111
"""
112
def __init__(self, server_address, handler): ...
113
114
def serve_forever(self):
115
"""Start serving requests until interrupted."""
116
117
def shutdown(self):
118
"""Shutdown server and close socket."""
119
```
120
121
### Base Resolver
122
123
Abstract base class for implementing custom DNS resolvers.
124
125
```python { .api }
126
class BaseResolver:
127
"""
128
Abstract base class for DNS resolvers.
129
Subclass this and implement resolve() method for custom logic.
130
"""
131
def resolve(self, request, handler):
132
"""
133
Resolve DNS query and return response.
134
Must be implemented by subclasses.
135
136
Args:
137
request (DNSRecord): Parsed DNS query
138
handler (DNSHandler): Request handler instance
139
140
Returns:
141
DNSRecord: DNS response record
142
"""
143
raise NotImplementedError
144
```
145
146
### DNS Logger
147
148
Configurable logging system for DNS server operations with request/response tracking.
149
150
```python { .api }
151
class DNSLogger:
152
"""
153
DNS server logger with configurable output and formatting.
154
155
Args:
156
log (str, optional): Comma-separated log types
157
("request", "reply", "truncated", "error", "recv", "send")
158
(default: "request,reply,truncated,error")
159
prefix (bool, optional): Include timestamp prefix (default: True)
160
logf (callable, optional): Custom log function (default: print)
161
"""
162
def __init__(self, log="request,reply,truncated,error", prefix=True, logf=None): ...
163
164
def log_recv(self, handler, data):
165
"""
166
Log received data.
167
168
Args:
169
handler (DNSHandler): Request handler
170
data (bytes): Received data
171
"""
172
173
def log_send(self, handler, data):
174
"""
175
Log sent data.
176
177
Args:
178
handler (DNSHandler): Request handler
179
data (bytes): Sent data
180
"""
181
182
def log_request(self, handler, request):
183
"""
184
Log DNS request.
185
186
Args:
187
handler (DNSHandler): Request handler
188
request (DNSRecord): DNS request
189
"""
190
191
def log_reply(self, handler, reply):
192
"""
193
Log DNS reply.
194
195
Args:
196
handler (DNSHandler): Request handler
197
reply (DNSRecord): DNS reply
198
"""
199
200
def log_truncated(self, handler, reply):
201
"""
202
Log truncated response.
203
204
Args:
205
handler (DNSHandler): Request handler
206
reply (DNSRecord): Truncated DNS reply
207
"""
208
209
def log_error(self, handler, e):
210
"""
211
Log error during request processing.
212
213
Args:
214
handler (DNSHandler): Request handler
215
e (Exception): Exception that occurred
216
"""
217
```
218
219
### UDP Server
220
221
UDP DNS server implementation with threading support.
222
223
```python { .api }
224
class UDPServer:
225
"""
226
UDP DNS server implementation.
227
Inherits from socketserver.ThreadingUDPServer.
228
229
Args:
230
server_address (tuple): (address, port) tuple
231
RequestHandlerClass (class): Request handler class
232
resolver (BaseResolver): DNS resolver instance
233
logger (DNSLogger, optional): Logger instance
234
timeout (int, optional): Request timeout
235
"""
236
def __init__(self, server_address, RequestHandlerClass, resolver, logger=None, timeout=5): ...
237
```
238
239
### TCP Server
240
241
TCP DNS server implementation with threading support.
242
243
```python { .api }
244
class TCPServer:
245
"""
246
TCP DNS server implementation.
247
Inherits from socketserver.ThreadingTCPServer.
248
249
Args:
250
server_address (tuple): (address, port) tuple
251
RequestHandlerClass (class): Request handler class
252
resolver (BaseResolver): DNS resolver instance
253
logger (DNSLogger, optional): Logger instance
254
timeout (int, optional): Request timeout
255
"""
256
def __init__(self, server_address, RequestHandlerClass, resolver, logger=None, timeout=5): ...
257
```
258
259
## Usage Examples
260
261
### Basic Custom Resolver
262
263
```python
264
from dnslib import *
265
from dnslib.server import DNSServer, BaseResolver
266
267
class SimpleResolver(BaseResolver):
268
"""Simple resolver that returns fixed IP for all A queries."""
269
270
def resolve(self, request, handler):
271
# Create reply based on request
272
reply = request.reply()
273
274
# Get the query
275
qname = request.q.qname
276
qtype = request.q.qtype
277
278
# Handle A record queries
279
if qtype == QTYPE.A:
280
# Add answer record
281
reply.add_answer(RR(qname, QTYPE.A, rdata=A("192.0.2.1"), ttl=300))
282
else:
283
# Set NXDOMAIN for other types
284
reply.header.rcode = RCODE.NXDOMAIN
285
286
return reply
287
288
# Create and start server
289
resolver = SimpleResolver()
290
server = DNSServer(resolver, port=5353, address="127.0.0.1")
291
print("Starting DNS server on 127.0.0.1:5353")
292
server.start()
293
```
294
295
### Advanced Custom Resolver with Logging
296
297
```python
298
from dnslib import *
299
from dnslib.server import DNSServer, BaseResolver, DNSLogger
300
import time
301
302
class TimestampResolver(BaseResolver):
303
"""Resolver that returns current timestamp in TXT records."""
304
305
def resolve(self, request, handler):
306
reply = request.reply()
307
qname = request.q.qname
308
qtype = request.q.qtype
309
310
if qtype == QTYPE.TXT:
311
# Return current timestamp
312
timestamp = str(int(time.time()))
313
reply.add_answer(RR(qname, QTYPE.TXT, rdata=TXT(f"timestamp={timestamp}"), ttl=1))
314
elif qtype == QTYPE.A:
315
# Return fixed IP
316
reply.add_answer(RR(qname, QTYPE.A, rdata=A("203.0.113.1"), ttl=300))
317
else:
318
reply.header.rcode = RCODE.NXDOMAIN
319
320
return reply
321
322
# Create logger with custom configuration
323
logger = DNSLogger(log="request,reply,error", prefix=True)
324
325
# Create resolver and server
326
resolver = TimestampResolver()
327
server = DNSServer(resolver, port=5353, address="127.0.0.1", logger=logger)
328
329
print("Starting DNS server with logging on 127.0.0.1:5353")
330
server.start()
331
```
332
333
### Multi-Protocol Server (UDP + TCP)
334
335
```python
336
from dnslib import *
337
from dnslib.server import DNSServer, BaseResolver
338
import threading
339
340
class EchoResolver(BaseResolver):
341
"""Resolver that echoes query information."""
342
343
def resolve(self, request, handler):
344
reply = request.reply()
345
qname = request.q.qname
346
qtype = request.q.qtype
347
348
# Create echo response in TXT record
349
echo_data = f"You queried {qname} for {QTYPE[qtype]}"
350
reply.add_answer(RR(qname, QTYPE.TXT, rdata=TXT(echo_data), ttl=60))
351
352
return reply
353
354
# Create resolver
355
resolver = EchoResolver()
356
357
# Start UDP server in thread
358
udp_server = DNSServer(resolver, port=5353, address="127.0.0.1", tcp=False)
359
udp_thread = udp_server.start_thread()
360
361
# Start TCP server in thread
362
tcp_server = DNSServer(resolver, port=5353, address="127.0.0.1", tcp=True)
363
tcp_thread = tcp_server.start_thread()
364
365
print("DNS servers running on UDP and TCP port 5353")
366
print("Press Ctrl+C to stop")
367
368
try:
369
# Keep main thread alive
370
while True:
371
time.sleep(1)
372
except KeyboardInterrupt:
373
print("Stopping servers...")
374
```
375
376
### Zone-Based Resolver
377
378
```python
379
from dnslib import *
380
from dnslib.server import DNSServer, BaseResolver
381
382
class ZoneResolver(BaseResolver):
383
"""Resolver that serves from zone data."""
384
385
def __init__(self, zone_data):
386
self.records = {}
387
# Parse zone data and index by name/type
388
for rr in RR.fromZone(zone_data):
389
key = (str(rr.rname), rr.rtype)
390
if key not in self.records:
391
self.records[key] = []
392
self.records[key].append(rr)
393
394
def resolve(self, request, handler):
395
reply = request.reply()
396
qname = str(request.q.qname)
397
qtype = request.q.qtype
398
399
# Look for exact match
400
key = (qname, qtype)
401
if key in self.records:
402
for rr in self.records[key]:
403
reply.add_answer(rr)
404
else:
405
# Check for ANY query
406
if qtype == QTYPE.ANY:
407
found = False
408
for (name, rtype), records in self.records.items():
409
if name == qname:
410
for rr in records:
411
reply.add_answer(rr)
412
found = True
413
if not found:
414
reply.header.rcode = RCODE.NXDOMAIN
415
else:
416
reply.header.rcode = RCODE.NXDOMAIN
417
418
return reply
419
420
# Zone data
421
zone_data = """
422
$TTL 300
423
example.com. IN A 192.0.2.1
424
example.com. IN MX 10 mail.example.com.
425
www.example.com. IN A 192.0.2.2
426
mail.example.com. IN A 192.0.2.3
427
ftp.example.com. IN CNAME www.example.com.
428
"""
429
430
# Create and start server
431
resolver = ZoneResolver(zone_data.strip())
432
server = DNSServer(resolver, port=5353, address="127.0.0.1")
433
434
print("Starting zone-based DNS server on 127.0.0.1:5353")
435
server.start()
436
```
437
438
### Server with Custom Request Handler
439
440
```python
441
from dnslib import *
442
from dnslib.server import DNSServer, BaseResolver, DNSHandler
443
import socketserver
444
445
class CustomDNSHandler(DNSHandler):
446
"""Custom handler with request filtering."""
447
448
def handle(self):
449
# Get client address
450
client_addr = self.client_address[0]
451
452
# Simple rate limiting example
453
if hasattr(self.server, 'client_counts'):
454
if client_addr not in self.server.client_counts:
455
self.server.client_counts[client_addr] = 0
456
self.server.client_counts[client_addr] += 1
457
458
# Reject if too many requests
459
if self.server.client_counts[client_addr] > 10:
460
print(f"Rate limiting client {client_addr}")
461
return
462
463
# Call parent handler
464
super().handle()
465
466
class RateLimitedResolver(BaseResolver):
467
"""Simple resolver with rate limiting."""
468
469
def resolve(self, request, handler):
470
reply = request.reply()
471
qname = request.q.qname
472
473
# Simple response
474
reply.add_answer(RR(qname, QTYPE.A, rdata=A("198.51.100.1"), ttl=300))
475
476
return reply
477
478
# Create server with custom handler
479
resolver = RateLimitedResolver()
480
server = DNSServer(resolver, port=5353, address="127.0.0.1", handler=CustomDNSHandler)
481
482
# Add client tracking
483
server.server.client_counts = {}
484
485
print("Starting rate-limited DNS server on 127.0.0.1:5353")
486
server.start()
487
```
488
489
### Server with Error Handling
490
491
```python
492
from dnslib import *
493
from dnslib.server import DNSServer, BaseResolver, DNSLogger
494
495
class RobustResolver(BaseResolver):
496
"""Resolver with comprehensive error handling."""
497
498
def resolve(self, request, handler):
499
try:
500
reply = request.reply()
501
qname = request.q.qname
502
qtype = request.q.qtype
503
504
# Validate query
505
if len(str(qname)) > 253: # Max domain length
506
reply.header.rcode = RCODE.FORMERR
507
return reply
508
509
# Handle different query types
510
if qtype == QTYPE.A:
511
reply.add_answer(RR(qname, QTYPE.A, rdata=A("203.0.113.10"), ttl=300))
512
elif qtype == QTYPE.AAAA:
513
reply.add_answer(RR(qname, QTYPE.AAAA, rdata=AAAA("2001:db8::10"), ttl=300))
514
elif qtype == QTYPE.TXT:
515
reply.add_answer(RR(qname, QTYPE.TXT, rdata=TXT("Hello from robust resolver"), ttl=60))
516
else:
517
reply.header.rcode = RCODE.NXDOMAIN
518
519
return reply
520
521
except Exception as e:
522
# Log error and return SERVFAIL
523
print(f"Error in resolver: {e}")
524
reply = request.reply()
525
reply.header.rcode = RCODE.SERVFAIL
526
return reply
527
528
# Create logger for debugging
529
logger = DNSLogger(log="request,reply,error,truncated", prefix=True)
530
531
# Create and start server
532
resolver = RobustResolver()
533
server = DNSServer(resolver, port=5353, address="127.0.0.1", logger=logger)
534
535
print("Starting robust DNS server with error handling on 127.0.0.1:5353")
536
server.start()
537
```