202 lines
6 KiB
Python
202 lines
6 KiB
Python
# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
|
#
|
|
# SPDX-License-Identifier: MPL-2.0
|
|
#
|
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
# file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
|
#
|
|
# See the COPYRIGHT file distributed with this work for additional
|
|
# information regarding copyright ownership.
|
|
|
|
from __future__ import print_function
|
|
import os
|
|
import sys
|
|
import signal
|
|
import socket
|
|
import select
|
|
import struct
|
|
|
|
import dns, dns.message
|
|
from dns.rcode import *
|
|
|
|
modes = [
|
|
b"silent", # Do not respond
|
|
b"close", # UDP: same as silent; TCP: also close the connection
|
|
b"servfail", # Always respond with SERVFAIL
|
|
b"unstable", # Constantly switch between "silent" and "servfail"
|
|
]
|
|
mode = modes[0]
|
|
n = 0
|
|
|
|
|
|
def ctrl_channel(msg):
|
|
global modes, mode, n
|
|
|
|
msg = msg.splitlines().pop(0)
|
|
print("Received control message: %s" % msg)
|
|
|
|
if msg in modes:
|
|
mode = msg
|
|
n = 0
|
|
print("New mode: %s" % str(mode))
|
|
|
|
|
|
def create_servfail(msg):
|
|
m = dns.message.from_wire(msg)
|
|
qname = m.question[0].name.to_text()
|
|
rrtype = m.question[0].rdtype
|
|
typename = dns.rdatatype.to_text(rrtype)
|
|
|
|
with open("query.log", "a") as f:
|
|
f.write("%s %s\n" % (typename, qname))
|
|
print("%s %s" % (typename, qname), end=" ")
|
|
|
|
r = dns.message.make_response(m)
|
|
r.set_rcode(SERVFAIL)
|
|
return r
|
|
|
|
|
|
def sigterm(signum, frame):
|
|
print("Shutting down now...")
|
|
os.remove("ans.pid")
|
|
running = False
|
|
sys.exit(0)
|
|
|
|
|
|
ip4 = "10.53.0.8"
|
|
|
|
try:
|
|
port = int(os.environ["PORT"])
|
|
except:
|
|
port = 5300
|
|
|
|
try:
|
|
ctrlport = int(os.environ["EXTRAPORT1"])
|
|
except:
|
|
ctrlport = 5300
|
|
|
|
query4_udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
query4_udp.bind((ip4, port))
|
|
|
|
query4_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
query4_tcp.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
query4_tcp.bind((ip4, port))
|
|
query4_tcp.listen(100)
|
|
|
|
ctrl4_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
ctrl4_tcp.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
ctrl4_tcp.bind((ip4, ctrlport))
|
|
ctrl4_tcp.listen(100)
|
|
|
|
signal.signal(signal.SIGTERM, sigterm)
|
|
|
|
f = open("ans.pid", "w")
|
|
pid = os.getpid()
|
|
print(pid, file=f)
|
|
f.close()
|
|
|
|
running = True
|
|
|
|
print("Listening on %s port %d" % (ip4, port))
|
|
print("Listening on %s port %d" % (ip4, ctrlport))
|
|
print("Ctrl-c to quit")
|
|
|
|
input = [query4_udp, query4_tcp, ctrl4_tcp]
|
|
|
|
hung_conns = []
|
|
|
|
while running:
|
|
try:
|
|
inputready, outputready, exceptready = select.select(input, [], [])
|
|
except select.error as e:
|
|
break
|
|
except socket.error as e:
|
|
break
|
|
except KeyboardInterrupt:
|
|
break
|
|
|
|
for s in inputready:
|
|
if s == query4_udp:
|
|
n = n + 1
|
|
print("UDP query received on %s" % ip4, end=" ")
|
|
msg = s.recvfrom(65535)
|
|
if (
|
|
mode == b"silent"
|
|
or mode == b"close"
|
|
or (mode == b"unstable" and n % 2 == 1)
|
|
):
|
|
# Do not respond.
|
|
print("NO RESPONSE (%s)" % str(mode))
|
|
continue
|
|
elif mode == b"servfail" or (mode == b"unstable" and n % 2 == 0):
|
|
rsp = create_servfail(msg[0])
|
|
if rsp:
|
|
print(dns.rcode.to_text(rsp.rcode()))
|
|
s.sendto(rsp.to_wire(), msg[1])
|
|
else:
|
|
print("NO RESPONSE (can not create a response)")
|
|
else:
|
|
raise (Exception("unsupported mode: %s" % mode))
|
|
elif s == query4_tcp:
|
|
n = n + 1
|
|
print("TCP query received on %s" % ip4, end=" ")
|
|
conn = None
|
|
try:
|
|
if mode == b"silent" or (mode == b"unstable" and n % 2 == 1):
|
|
conn, addr = s.accept()
|
|
# Do not respond and hang the connection.
|
|
print("NO RESPONSE (%s)" % str(mode))
|
|
hung_conns.append(conn)
|
|
continue
|
|
elif mode == b"close":
|
|
conn, addr = s.accept()
|
|
# Do not respond and close the connection.
|
|
print("NO RESPONSE (%s)" % str(mode))
|
|
conn.close()
|
|
continue
|
|
elif mode == b"servfail" or (mode == b"unstable" and n % 2 == 0):
|
|
conn, addr = s.accept()
|
|
# get TCP message length
|
|
msg = conn.recv(2)
|
|
if len(msg) != 2:
|
|
print("NO RESPONSE (can not read the message length)")
|
|
conn.close()
|
|
continue
|
|
length = struct.unpack(">H", msg[:2])[0]
|
|
msg = conn.recv(length)
|
|
if len(msg) != length:
|
|
print("NO RESPONSE (can not read the message)")
|
|
conn.close()
|
|
continue
|
|
rsp = create_servfail(msg)
|
|
if rsp:
|
|
print(dns.rcode.to_text(rsp.rcode()))
|
|
wire = rsp.to_wire()
|
|
conn.send(struct.pack(">H", len(wire)))
|
|
conn.send(wire)
|
|
else:
|
|
print("NO RESPONSE (can not create a response)")
|
|
else:
|
|
raise (Exception("unsupported mode: %s" % mode))
|
|
except socket.error as e:
|
|
print("NO RESPONSE (error: %s)" % str(e))
|
|
if conn:
|
|
conn.close()
|
|
elif s == ctrl4_tcp:
|
|
print("Control channel connected")
|
|
conn = None
|
|
try:
|
|
# Handle control channel input
|
|
conn, addr = s.accept()
|
|
msg = conn.recv(1024)
|
|
if msg:
|
|
ctrl_channel(msg)
|
|
conn.close()
|
|
except s.timeout:
|
|
pass
|
|
if conn:
|
|
conn.close()
|
|
|
|
if not running:
|
|
break
|