|
|
@ -1,5 +1,4 @@
|
|
|
|
#!/usr/bin/env /usr/bin/python
|
|
|
|
#!/usr/bin/env /usr/bin/python
|
|
|
|
# Why don't people use shebangs anymore. It's muppetry I say
|
|
|
|
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
"""
|
|
|
@ -7,52 +6,48 @@
|
|
|
|
to all requests
|
|
|
|
to all requests
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
from __future__ import print_function
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import copy
|
|
|
|
|
|
|
|
import os
|
|
|
|
import os
|
|
|
|
import os.path
|
|
|
|
import os.path
|
|
|
|
import base64
|
|
|
|
import base64
|
|
|
|
import time
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
from dnslib import RR, QTYPE, TXT, CLASS, RCODE
|
|
|
|
from dnslib import RR, QTYPE, TXT
|
|
|
|
from dnslib.server import DNSServer,DNSHandler,BaseResolver,DNSLogger
|
|
|
|
from dnslib.server import DNSServer, DNSHandler, BaseResolver, DNSLogger
|
|
|
|
from dnslib.label import DNSLabel
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FileResolver(BaseResolver):
|
|
|
|
class FileResolver(BaseResolver):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Respond with fixed response to all requests
|
|
|
|
Respond with fixed response to all requests
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
def __init__(self,directory,domain):
|
|
|
|
|
|
|
|
self.filelist=[]
|
|
|
|
def __init__(self, directory, domain):
|
|
|
|
self.domain=domain
|
|
|
|
self.filelist = []
|
|
|
|
self.ttl=60
|
|
|
|
self.domain = domain
|
|
|
|
self.directory=directory
|
|
|
|
self.ttl = 60
|
|
|
|
self.cache={}
|
|
|
|
self.directory = directory
|
|
|
|
|
|
|
|
self.cache = {}
|
|
|
|
if directory:
|
|
|
|
if directory:
|
|
|
|
if not os.path.isdir(directory):
|
|
|
|
if not os.path.isdir(directory):
|
|
|
|
print("Directory " + directory + " doesn't exist")
|
|
|
|
print(f"Directory {directory} doesn't exist")
|
|
|
|
exit()
|
|
|
|
exit()
|
|
|
|
|
|
|
|
|
|
|
|
def getcache(self,path):
|
|
|
|
def getcache(self, path):
|
|
|
|
cooked=""
|
|
|
|
pname = os.path.basename(path)
|
|
|
|
pname=os.path.basename(path);
|
|
|
|
|
|
|
|
if pname in self.cache:
|
|
|
|
if pname in self.cache:
|
|
|
|
# it's in the cache - so lets just read it from the cache
|
|
|
|
# it's in the cache - so lets just read it from the cache
|
|
|
|
print("Taking from cache")
|
|
|
|
print("Taking from cache")
|
|
|
|
cooked=self.cache[pname]["base64"]
|
|
|
|
cooked = self.cache[pname]["base64"]
|
|
|
|
# And update time
|
|
|
|
# And update time
|
|
|
|
self.cache[pname]["time"]=time.time()
|
|
|
|
self.cache[pname]["time"] = time.time()
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
# It's not in cache - read the file and cache it
|
|
|
|
# It's not in cache - read the file and cache it
|
|
|
|
print("Adding to cache")
|
|
|
|
print("Adding to cache")
|
|
|
|
fin=open(path,"rb")
|
|
|
|
fin = open(path, "rb")
|
|
|
|
raw=fin.read()
|
|
|
|
raw = fin.read()
|
|
|
|
fin.close()
|
|
|
|
fin.close()
|
|
|
|
cooked=base64.b64encode(raw)
|
|
|
|
cooked = base64.b64encode(raw)
|
|
|
|
self.cache[pname]={}
|
|
|
|
self.cache[pname] = {}
|
|
|
|
self.cache[pname]["base64"]=cooked
|
|
|
|
self.cache[pname]["base64"] = cooked
|
|
|
|
self.cache[pname]["time"]=time.time()
|
|
|
|
self.cache[pname]["time"] = time.time()
|
|
|
|
|
|
|
|
|
|
|
|
self.cachecheck()
|
|
|
|
self.cachecheck()
|
|
|
|
return cooked
|
|
|
|
return cooked
|
|
|
@ -62,132 +57,104 @@ class FileResolver(BaseResolver):
|
|
|
|
curtime = time.time()
|
|
|
|
curtime = time.time()
|
|
|
|
staletime = 3600
|
|
|
|
staletime = 3600
|
|
|
|
for key in self.cache:
|
|
|
|
for key in self.cache:
|
|
|
|
if curtime-self.cache[key]["time"] > staletime:
|
|
|
|
if curtime - self.cache[key]["time"] > staletime:
|
|
|
|
# it's stale
|
|
|
|
# it's stale
|
|
|
|
del self.cache[key]
|
|
|
|
del self.cache[key]
|
|
|
|
|
|
|
|
|
|
|
|
def resolve(self,request,handler):
|
|
|
|
def resolve(self, request, handler):
|
|
|
|
type=QTYPE[request.q.qtype]
|
|
|
|
qry_type = QTYPE[request.q.qtype]
|
|
|
|
name=request.q.qname
|
|
|
|
name = request.q.qname
|
|
|
|
reply = request.reply()
|
|
|
|
reply = request.reply()
|
|
|
|
|
|
|
|
|
|
|
|
if type == "TXT":
|
|
|
|
# First check domain is at the end of the name
|
|
|
|
# Check for Chaos stuff first
|
|
|
|
if not name.matchSuffix(self.domain):
|
|
|
|
if request.q.qclass == CLASS.CH:
|
|
|
|
reply.add_answer(RR(name, QTYPE.TXT, ttl=self.ttl, rdata=TXT("Domain not found")))
|
|
|
|
if name == "version.bind":
|
|
|
|
|
|
|
|
reply.add_answer(*RR.fromZone('version.bind 60 CH TXT "Uninvited Guest 0.3"'))
|
|
|
|
|
|
|
|
if name == "authors.bind":
|
|
|
|
|
|
|
|
reply.add_answer(*RR.fromZone('authors.bind 60 CH TXT "David Lodge"'))
|
|
|
|
|
|
|
|
reply.add_answer(*RR.fromZone('authors.bind 60 CH TXT "Ian Williams"'))
|
|
|
|
|
|
|
|
if not reply.rr:
|
|
|
|
|
|
|
|
reply.header.rcode = RCODE.NXDOMAIN
|
|
|
|
|
|
|
|
return reply
|
|
|
|
return reply
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Request method for uploads
|
|
|
|
|
|
|
|
if qry_type == "SRV":
|
|
|
|
|
|
|
|
with open('/tmp/dns-srv.log', 'w') as f:
|
|
|
|
|
|
|
|
f.write(name.stripSuffix(self.domain))
|
|
|
|
|
|
|
|
reply.add_answer(RR(name, QTYPE.SRV, ttl=self.ttl, rdata=TXT("accepted")))
|
|
|
|
|
|
|
|
# Request method for downloads
|
|
|
|
|
|
|
|
if qry_type == "TXT":
|
|
|
|
# Format is filename.count.domain for count
|
|
|
|
# Format is filename.count.domain for count
|
|
|
|
# First check domain is at the end of the name
|
|
|
|
|
|
|
|
if not name.matchSuffix(self.domain):
|
|
|
|
if not name.matchSuffix(self.domain):
|
|
|
|
reply.add_answer(RR(name, QTYPE.TXT, ttl=self.ttl,
|
|
|
|
reply.add_answer(RR(name, QTYPE.TXT, ttl=self.ttl, rdata=TXT("Domain not found")))
|
|
|
|
rdata=TXT("Domain not found")))
|
|
|
|
|
|
|
|
return reply
|
|
|
|
return reply
|
|
|
|
|
|
|
|
|
|
|
|
# Now look whether we're looking for a count or a part
|
|
|
|
# Now look whether we're looking for a count or a part
|
|
|
|
parts=str(name.stripSuffix(self.domain)).split(".")
|
|
|
|
parts = str(name.stripSuffix(self.domain)).split(".")
|
|
|
|
# parts[0] should be filename, parts[1] should be segment or count
|
|
|
|
# parts[0] should be filename, parts[1] should be segment or count
|
|
|
|
pname='.'.join(parts[:-2])
|
|
|
|
pname = '.'.join(parts[:-2])
|
|
|
|
path=self.directory + "/" + pname
|
|
|
|
path = self.directory + "/" + pname
|
|
|
|
if not os.path.isfile(path):
|
|
|
|
if not os.path.isfile(path):
|
|
|
|
reply.add_answer(RR(name, QTYPE.TXT, ttl=self.ttl,
|
|
|
|
reply.add_answer(RR(name, QTYPE.TXT, ttl=self.ttl, rdata=TXT("File not found")))
|
|
|
|
rdata=TXT("File not found")))
|
|
|
|
|
|
|
|
return reply
|
|
|
|
return reply
|
|
|
|
|
|
|
|
|
|
|
|
# work out the count mathematically
|
|
|
|
# work out the count mathematically
|
|
|
|
# First work out the base64 size
|
|
|
|
# First work out the base64 size
|
|
|
|
l=os.path.getsize(path)
|
|
|
|
length = os.path.getsize(path)
|
|
|
|
l=(4*l)/3
|
|
|
|
length = (4 * length) / 3
|
|
|
|
# And padding
|
|
|
|
# And padding
|
|
|
|
l+=(l%4)
|
|
|
|
length += (length % 4)
|
|
|
|
# Finally divide into number of 254 byte chunks
|
|
|
|
# Finally divide into number of 254 byte chunks
|
|
|
|
chunks=(l/254)
|
|
|
|
chunks = (length / 254)
|
|
|
|
|
|
|
|
|
|
|
|
if ''.join(parts[-2:-1]) == "count":
|
|
|
|
if ''.join(parts[-2:-1]) == "count":
|
|
|
|
reply.add_answer(RR(name, QTYPE.TXT, ttl=self.ttl,
|
|
|
|
reply.add_answer(RR(name, QTYPE.TXT, ttl=self.ttl, rdata=TXT(str(chunks))))
|
|
|
|
rdata=TXT(str(chunks))))
|
|
|
|
|
|
|
|
return reply
|
|
|
|
return reply
|
|
|
|
|
|
|
|
|
|
|
|
if ''.join(parts[-2:-1]).isdigit():
|
|
|
|
if ''.join(parts[-2:-1]).isdigit():
|
|
|
|
# Woo it's a number
|
|
|
|
# Woo it's a number
|
|
|
|
# lets base64 the file
|
|
|
|
# lets base64 the file
|
|
|
|
chunk=int(''.join(parts[-2:-1]))
|
|
|
|
chunk = int(''.join(parts[-2:-1]))
|
|
|
|
if chunk > chunks or chunk < 0:
|
|
|
|
if chunk > chunks or chunk < 0:
|
|
|
|
reply.add_answer(RR(name, QTYPE.TXT, ttl=self.ttl,
|
|
|
|
reply.add_answer(RR(name, QTYPE.TXT, ttl=self.ttl, rdata=TXT("Chunk out of range")))
|
|
|
|
rdata=TXT("Chunk out of range")))
|
|
|
|
|
|
|
|
return reply
|
|
|
|
return reply
|
|
|
|
# It's actually trying to read the file, so let's cache it in memory
|
|
|
|
# It's actually trying to read the file, so let's cache it in memory
|
|
|
|
cooked=self.getcache(path)
|
|
|
|
cooked = self.getcache(path)
|
|
|
|
|
|
|
|
|
|
|
|
# Now lets just grab the chunk
|
|
|
|
# Now lets just grab the chunk
|
|
|
|
start=chunk*254
|
|
|
|
start = chunk * 254
|
|
|
|
txtr=cooked[start:start+254]
|
|
|
|
txtr = cooked[start:start + 254]
|
|
|
|
reply.add_answer(RR(name, QTYPE.TXT, ttl=self.ttl,
|
|
|
|
reply.add_answer(RR(name, QTYPE.TXT, ttl=self.ttl, rdata=TXT(txtr)))
|
|
|
|
rdata=TXT(txtr)))
|
|
|
|
|
|
|
|
return reply
|
|
|
|
return reply
|
|
|
|
|
|
|
|
|
|
|
|
# Replace labels with request label
|
|
|
|
# Replace labels with request label
|
|
|
|
return reply
|
|
|
|
return reply
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import argparse,sys,time
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
|
|
|
|
import time
|
|
|
|
|
|
|
|
|
|
|
|
p = argparse.ArgumentParser(description="Fixed DNS Resolver")
|
|
|
|
p = argparse.ArgumentParser(description="Fixed DNS Resolver")
|
|
|
|
p.add_argument("--directory","-r",
|
|
|
|
p.add_argument("--directory", "-r", metavar="<directory>", help="Directory to pull files from")
|
|
|
|
metavar="<directory>",
|
|
|
|
p.add_argument("--domain", "-d", metavar="<domain>", help="Domain we're working in")
|
|
|
|
help="Directory to pull files from")
|
|
|
|
p.add_argument("--port", "-p", type=int, default=53, metavar="<port>", help="Server port (default:53)")
|
|
|
|
p.add_argument("--domain","-d",
|
|
|
|
p.add_argument("--address", "-a", default="", metavar="<address>", help="Listen address (default:all)")
|
|
|
|
metavar="<domain>",
|
|
|
|
p.add_argument("--udplen", "-u", type=int, default=0, metavar="<udplen>", help="Max UDP packet length (default:0)")
|
|
|
|
help="Domain we're working in")
|
|
|
|
p.add_argument("--tcp", action='store_true', default=False, help="TCP server (default: UDP only)")
|
|
|
|
p.add_argument("--port","-p",type=int,default=53,
|
|
|
|
p.add_argument("--log", default="request,reply,truncated,error",
|
|
|
|
metavar="<port>",
|
|
|
|
help="Log hooks (default: +request,+reply,+truncated,+error,-recv,-send,-data)")
|
|
|
|
help="Server port (default:53)")
|
|
|
|
p.add_argument("--log-prefix", action='store_true', default=False, help="Log prefix (default: False)")
|
|
|
|
p.add_argument("--address","-a",default="",
|
|
|
|
|
|
|
|
metavar="<address>",
|
|
|
|
|
|
|
|
help="Listen address (default:all)")
|
|
|
|
|
|
|
|
p.add_argument("--udplen","-u",type=int,default=0,
|
|
|
|
|
|
|
|
metavar="<udplen>",
|
|
|
|
|
|
|
|
help="Max UDP packet length (default:0)")
|
|
|
|
|
|
|
|
p.add_argument("--tcp",action='store_true',default=False,
|
|
|
|
|
|
|
|
help="TCP server (default: UDP only)")
|
|
|
|
|
|
|
|
p.add_argument("--log",default="request,reply,truncated,error",
|
|
|
|
|
|
|
|
help="Log hooks to enable (default: +request,+reply,+truncated,+error,-recv,-send,-data)")
|
|
|
|
|
|
|
|
p.add_argument("--log-prefix",action='store_true',default=False,
|
|
|
|
|
|
|
|
help="Log prefix (timestamp/handler/resolver) (default: False)")
|
|
|
|
|
|
|
|
args = p.parse_args()
|
|
|
|
args = p.parse_args()
|
|
|
|
|
|
|
|
|
|
|
|
resolver = FileResolver(args.directory,args.domain)
|
|
|
|
resolver = FileResolver(args.directory, args.domain)
|
|
|
|
logger = DNSLogger(args.log,args.log_prefix)
|
|
|
|
logger = DNSLogger(args.log, args.log_prefix)
|
|
|
|
|
|
|
|
|
|
|
|
print("Starting File Resolver (%s:%d) [%s]" % (
|
|
|
|
print(f"Starting File Resolver ({args.address or '*'}:{args.port}) [{'UDP/TCP' if args.tcp else 'UDP'}]")
|
|
|
|
args.address or "*",
|
|
|
|
|
|
|
|
args.port,
|
|
|
|
|
|
|
|
"UDP/TCP" if args.tcp else "UDP"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if args.udplen:
|
|
|
|
if args.udplen:
|
|
|
|
DNSHandler.udplen = args.udplen
|
|
|
|
DNSHandler.udplen = args.udplen
|
|
|
|
|
|
|
|
|
|
|
|
udp_server = DNSServer(resolver,
|
|
|
|
udp_server = DNSServer(resolver, port=args.port, address=args.address, logger=logger)
|
|
|
|
port=args.port,
|
|
|
|
|
|
|
|
address=args.address,
|
|
|
|
|
|
|
|
logger=logger)
|
|
|
|
|
|
|
|
udp_server.start_thread()
|
|
|
|
udp_server.start_thread()
|
|
|
|
|
|
|
|
|
|
|
|
if args.tcp:
|
|
|
|
if args.tcp:
|
|
|
|
tcp_server = DNSServer(resolver,
|
|
|
|
tcp_server = DNSServer(resolver, port=args.port, address=args.address, tcp=True, logger=logger)
|
|
|
|
port=args.port,
|
|
|
|
|
|
|
|
address=args.address,
|
|
|
|
|
|
|
|
tcp=True,
|
|
|
|
|
|
|
|
logger=logger)
|
|
|
|
|
|
|
|
tcp_server.start_thread()
|
|
|
|
tcp_server.start_thread()
|
|
|
|
|
|
|
|
|
|
|
|
while udp_server.isAlive():
|
|
|
|
while udp_server.isAlive():
|
|
|
|
time.sleep(1)
|
|
|
|
time.sleep(1)
|
|
|
|
|
|
|
|
|
|
|
|