bug fix: DNS response too slow

This commit is contained in:
lijiejie
2024-06-02 23:56:15 +08:00
parent cfc0f90439
commit 14ded0d495
8 changed files with 122 additions and 49 deletions
+67 -34
View File
@@ -1,13 +1,11 @@
# -*- coding: utf-8 -*-
import logging
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dnslog.settings")
import django
django.setup()
import copy
import re
import json
import requests
import struct
import socket
import random
@@ -15,6 +13,60 @@ from dnslib import RR, QTYPE, RCODE, TXT, A
from dnslib.server import DNSServer, DNSHandler, BaseResolver, DNSLogger
from logview.models import *
from dnslog import settings
import queue
import threading
from django.db import close_old_connections
from django.utils import timezone
q_query = queue.Queue()
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO, format='[%(asctime)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S',
filename='dns_server.log', filemode='a')
def process_log():
global q_query
while True:
try:
user_domain, ip, domain, qtype, timestamp = q_query.get()
user = User.objects.filter(user_domain__exact=user_domain)
# 有需要也可以记录下不属于任何用戶的请求记录
# 你需要创建1个用户,然后把他的 user_domain 更新为 @
# if not user and domain.strip(".") != settings.ADMIN_DOMAIN:
# user = User.objects.filter(user_domain__exact='@')
if not user:
logger.error('No such user: %s' % str(e), exc_info=True)
# 由于顺序获取客户端的IP地理位置过于耗时,大约300ms,不再顺序获取
# try:
# doc = requests.get('https://whois.pconline.com.cn/ip.jsp?ip=%s' % ip, timeout=10.0).text.strip()
# city = doc.split(' ')[0]
# except Exception as e:
# city = ''
city = ''
try:
sub_name = domain.split('.')[-3-len(settings.DNS_DOMAIN.split('.'))]
except:
sub_name = ''
for _ in range(5):
try:
log = DNSLog(user=user[0], host=domain.strip('.'), sub_name=sub_name, type=QTYPE[qtype], ip=ip,
city=city, created_time=timestamp)
log.save()
break
except django.db.utils.OperationalError as e:
logger.error('process_log.exception.1: %s' % str(e), exc_info=True)
close_old_connections()
except Exception as e:
logger.error('process_log.exception.2: %s' % str(e), exc_info=True)
except django.db.utils.OperationalError as e:
close_old_connections()
logger.error('process_log.exception.3: %s' % str(e), exc_info=True)
except Exception as e:
logger.error('process_log.exception.4: %s' % str(e))
class MySQLLogger:
@@ -40,6 +92,7 @@ class MySQLLogger:
pass
def log_request(self, handler, request):
global q_query
if QTYPE[request.q.qtype] == 'AAAA':
return
domain = request.q.qname.__str__().lower()
@@ -48,20 +101,11 @@ class MySQLLogger:
matches = re.search(r'\.?([^\.]+)\.%s\.' % settings.DNS_DOMAIN, domain)
if not matches:
return
user = User.objects.filter(user_domain__exact=matches.group(1))
# 有需要可以记录
# if not user and domain.strip(".") != settings.ADMIN_DOMAIN:
# user = User.objects.filter(user_domain__exact='@')
if not user:
return
user_domain = matches.group(1)
ip = handler.client_address[0]
try:
doc = requests.get('https://whois.pconline.com.cn/ip.jsp?ip=%s' % ip).text
city = doc.split(' ')[0]
except Exception as e:
city = ''
log = DNSLog(user=user[0], host=domain.strip('.'), type=QTYPE[request.q.qtype], ip=ip, city=city)
log.save()
qtype = request.q.qtype
item = (user_domain, ip, domain, qtype, timezone.now())
q_query.put(item)
def log_send(self, handler, data):
pass
@@ -71,31 +115,18 @@ class MySQLLogger:
class ZoneResolver(BaseResolver):
"""
Simple fixed zone file resolver.
"""
def __init__(self, zone, glob=False):
"""
Initialise resolver from zone file.
Stores RRs as a list of (label,type,rr) tuples
If 'glob' is True use glob match against zone file
"""
self.zone = [(rr.rname, QTYPE[rr.rtype], rr) for rr in RR.fromZone(zone)]
self.glob = glob
self.eq = 'matchGlob' if glob else '__eq__'
def resolve(self, request, handler):
"""
Respond to DNS request - parameters are request packet & handler.
Method is expected to return DNS response
"""
reply = request.reply()
qname = request.q.qname
qtype = QTYPE[request.q.qtype]
if qtype == 'TXT':
reply.add_answer(RR(qname, QTYPE.TXT, rdata=TXT('Nothing to response')))
# rebind
# DNS rebind
try:
if qtype == 'A' and qname.__str__().endswith(settings.DNS_DOMAIN + '.'):
subs = qname.__str__().replace('.' + settings.DNS_DOMAIN + '.', '')
@@ -110,7 +141,7 @@ class ZoneResolver(BaseResolver):
reply.add_answer(rr)
return reply
except Exception as e:
pass
logger.error('DNS rebind resolve.exception: %s' % str(e), exc_info=True)
#
for name, rtype, rr in self.zone:
# Check if label & type match
@@ -143,6 +174,8 @@ def main():
*.{dns_domain}. IN NS {ns2_domain}.
*.{dns_domain}. IN A {server_ip}
{dns_domain}. IN A {server_ip}
*.{dns_domain}. IN AAAA 2408:871a:2100:3:0:ff:b025:348d
{dns_domain}. IN AAAA 2408:871a:2100:3:0:ff:b025:348d
'''.format(
dns_domain=settings.DNS_DOMAIN,
ns1_domain=settings.NS1_DOMAIN,
@@ -150,10 +183,10 @@ def main():
server_ip=settings.SERVER_IP
)
resolver = ZoneResolver(zone, True)
logger = MySQLLogger()
udp_server = DNSServer(resolver, port=53, address='', logger=logger)
threading.Thread(target=process_log).start()
udp_server = DNSServer(resolver, port=53, address='', logger=MySQLLogger())
udp_server.start()
print("Zone Resolver started (%s:%d) [%s]" % ("*", 53, "UDP"))
logger.info("Zone Resolver started (%s:%d) [%s]" % ("*", 53, "UDP"))
if __name__ == '__main__':