# 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