scapy / dns server / snippet
A few days ago, the Scapy project was brought to my attention.
Scapy is an internet packet manipulation library for Python2. It can be used to sniff and decode packets, or to generate your own custom packets.
In the most basic form, it runs on raw sockets, sniffing and decoding
traffic like tcpdump. See the sniff()
examples and the
send(IP(dst="1.2.3.4") / ICMP())
example for sending a simple packet.
But just as easily, it works on regular datagram sockets — those that
you don’t need CAP_NET_RAW
powers for. Like in the following example.
This example snippet demonstrates how Scapy takes the hassle out of
parsing and creating DNS requests and responses. It accepts DNS requests
on port 1053, in the form <NAME>.<IPV4>.example.com.
, returning
<IPV4>
as an A record.
This example provides a similar feature as the nip.io (or xip.io) service: a wildcard DNS for any IPv4 address. Useful when serving development sites on internal IP addresses.
# Use scapy2.3.1+ from pip (secdev original) or for Python3 use the
# https://github.com/phaethon/scapy Scapy3K version.
#
# Example DNS server that resolves NAME.IPV4.example.com A record
# requests to an A:IPV4 response.
#
# $ dig test.12.34.56.78.example.com -p 1053 @127.0.0.1 +short
# 12.34.56.78
from scapy.all import DNS, DNSQR, DNSRR, dnsqtypes
from socket import AF_INET, SOCK_DGRAM, socket
from traceback import print_exc
sock = socket(AF_INET, SOCK_DGRAM)
sock.bind(('0.0.0.0', 1053))
while True:
request, addr = sock.recvfrom(4096)
try:
dns = DNS(request)
assert dns.opcode == 0, dns.opcode # QUERY
assert dnsqtypes[dns[DNSQR].qtype] == 'A', dns[DNSQR].qtype
query = dns[DNSQR].qname.decode('ascii') # test.1.2.3.4.example.com.
head, domain, tld, tail = query.rsplit('.', 3)
assert domain == 'example' and tld == 'com' and tail == ''
head = head.split('.', 1)[-1] # drop leading "prefix." part
response = DNS(
id=dns.id, ancount=1, qr=1,
an=DNSRR(rrname=str(query), type='A', rdata=str(head), ttl=1234))
print(repr(response))
sock.sendto(bytes(response), addr)
except Exception as e:
print('')
print_exc()
print('garbage from {!r}? data {!r}'.format(addr, request))
As you can see, all the protocol stuff is nicely tucked away in pre-existing structures, so you get nice and readable Python. Thanks, Scapy!