1# Copyright (C) Internet Systems Consortium, Inc. ("ISC") 2# 3# SPDX-License-Identifier: MPL-2.0 4# 5# This Source Code Form is subject to the terms of the Mozilla Public 6# License, v. 2.0. If a copy of the MPL was not distributed with this 7# file, you can obtain one at https://mozilla.org/MPL/2.0/. 8# 9# See the COPYRIGHT file distributed with this work for additional 10# information regarding copyright ownership. 11 12from __future__ import print_function 13import os 14import sys 15import signal 16import socket 17import select 18import struct 19 20import dns, dns.message 21from dns.rcode import * 22 23modes = [ 24 b"silent", # Do not respond 25 b"close", # UDP: same as silent; TCP: also close the connection 26 b"servfail", # Always respond with SERVFAIL 27 b"unstable", # Constantly switch between "silent" and "servfail" 28] 29mode = modes[0] 30n = 0 31 32 33def ctrl_channel(msg): 34 global modes, mode, n 35 36 msg = msg.splitlines().pop(0) 37 print("Received control message: %s" % msg) 38 39 if msg in modes: 40 mode = msg 41 n = 0 42 print("New mode: %s" % str(mode)) 43 44 45def create_servfail(msg): 46 m = dns.message.from_wire(msg) 47 qname = m.question[0].name.to_text() 48 rrtype = m.question[0].rdtype 49 typename = dns.rdatatype.to_text(rrtype) 50 51 with open("query.log", "a") as f: 52 f.write("%s %s\n" % (typename, qname)) 53 print("%s %s" % (typename, qname), end=" ") 54 55 r = dns.message.make_response(m) 56 r.set_rcode(SERVFAIL) 57 return r 58 59 60def sigterm(signum, frame): 61 print("Shutting down now...") 62 os.remove("ans.pid") 63 running = False 64 sys.exit(0) 65 66 67ip4 = "10.53.0.8" 68 69try: 70 port = int(os.environ["PORT"]) 71except: 72 port = 5300 73 74try: 75 ctrlport = int(os.environ["EXTRAPORT1"]) 76except: 77 ctrlport = 5300 78 79query4_udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 80query4_udp.bind((ip4, port)) 81 82query4_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 83query4_tcp.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 84query4_tcp.bind((ip4, port)) 85query4_tcp.listen(100) 86 87ctrl4_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 88ctrl4_tcp.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 89ctrl4_tcp.bind((ip4, ctrlport)) 90ctrl4_tcp.listen(100) 91 92signal.signal(signal.SIGTERM, sigterm) 93 94f = open("ans.pid", "w") 95pid = os.getpid() 96print(pid, file=f) 97f.close() 98 99running = True 100 101print("Listening on %s port %d" % (ip4, port)) 102print("Listening on %s port %d" % (ip4, ctrlport)) 103print("Ctrl-c to quit") 104 105input = [query4_udp, query4_tcp, ctrl4_tcp] 106 107hung_conns = [] 108 109while running: 110 try: 111 inputready, outputready, exceptready = select.select(input, [], []) 112 except select.error as e: 113 break 114 except socket.error as e: 115 break 116 except KeyboardInterrupt: 117 break 118 119 for s in inputready: 120 if s == query4_udp: 121 n = n + 1 122 print("UDP query received on %s" % ip4, end=" ") 123 msg = s.recvfrom(65535) 124 if ( 125 mode == b"silent" 126 or mode == b"close" 127 or (mode == b"unstable" and n % 2 == 1) 128 ): 129 # Do not respond. 130 print("NO RESPONSE (%s)" % str(mode)) 131 continue 132 elif mode == b"servfail" or (mode == b"unstable" and n % 2 == 0): 133 rsp = create_servfail(msg[0]) 134 if rsp: 135 print(dns.rcode.to_text(rsp.rcode())) 136 s.sendto(rsp.to_wire(), msg[1]) 137 else: 138 print("NO RESPONSE (can not create a response)") 139 else: 140 raise (Exception("unsupported mode: %s" % mode)) 141 elif s == query4_tcp: 142 n = n + 1 143 print("TCP query received on %s" % ip4, end=" ") 144 conn = None 145 try: 146 if mode == b"silent" or (mode == b"unstable" and n % 2 == 1): 147 conn, addr = s.accept() 148 # Do not respond and hang the connection. 149 print("NO RESPONSE (%s)" % str(mode)) 150 hung_conns.append(conn) 151 continue 152 elif mode == b"close": 153 conn, addr = s.accept() 154 # Do not respond and close the connection. 155 print("NO RESPONSE (%s)" % str(mode)) 156 conn.close() 157 continue 158 elif mode == b"servfail" or (mode == b"unstable" and n % 2 == 0): 159 conn, addr = s.accept() 160 # get TCP message length 161 msg = conn.recv(2) 162 if len(msg) != 2: 163 print("NO RESPONSE (can not read the message length)") 164 conn.close() 165 continue 166 length = struct.unpack(">H", msg[:2])[0] 167 msg = conn.recv(length) 168 if len(msg) != length: 169 print("NO RESPONSE (can not read the message)") 170 conn.close() 171 continue 172 rsp = create_servfail(msg) 173 if rsp: 174 print(dns.rcode.to_text(rsp.rcode())) 175 wire = rsp.to_wire() 176 conn.send(struct.pack(">H", len(wire))) 177 conn.send(wire) 178 else: 179 print("NO RESPONSE (can not create a response)") 180 else: 181 raise (Exception("unsupported mode: %s" % mode)) 182 except socket.error as e: 183 print("NO RESPONSE (error: %s)" % str(e)) 184 if conn: 185 conn.close() 186 elif s == ctrl4_tcp: 187 print("Control channel connected") 188 conn = None 189 try: 190 # Handle control channel input 191 conn, addr = s.accept() 192 msg = conn.recv(1024) 193 if msg: 194 ctrl_channel(msg) 195 conn.close() 196 except s.timeout: 197 pass 198 if conn: 199 conn.close() 200 201 if not running: 202 break 203