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 18from datetime import datetime, timedelta 19import time 20import functools 21 22import dns, dns.message, dns.query, dns.flags 23from dns.rdatatype import * 24from dns.rdataclass import * 25from dns.rcode import * 26from dns.name import * 27 28 29# Log query to file 30def logquery(type, qname): 31 with open("qlog", "a") as f: 32 f.write("%s %s\n", type, qname) 33 34 35############################################################################ 36# Respond to a DNS query. 37# If there are EDNS options present return FORMERR copying the OPT record. 38# Otherwise: 39# SOA gets a unsigned response. 40# NS gets a unsigned response. 41# A gets a unsigned response. 42# All other types get a unsigned NODATA response. 43############################################################################ 44def create_response(msg): 45 m = dns.message.from_wire(msg) 46 qname = m.question[0].name.to_text() 47 rrtype = m.question[0].rdtype 48 typename = dns.rdatatype.to_text(rrtype) 49 50 with open("query.log", "a") as f: 51 f.write("%s %s\n" % (typename, qname)) 52 print("%s %s" % (typename, qname), end=" ") 53 54 if m.edns != -1 and len(m.options) != 0: 55 r = dns.message.make_response(m) 56 r.use_edns( 57 edns=m.edns, ednsflags=m.ednsflags, payload=m.payload, options=m.options 58 ) 59 r.set_rcode(FORMERR) 60 else: 61 r = dns.message.make_response(m) 62 r.set_rcode(NOERROR) 63 if rrtype == A: 64 r.answer.append(dns.rrset.from_text(qname, 1, IN, A, "10.53.0.10")) 65 elif rrtype == NS: 66 r.answer.append(dns.rrset.from_text(qname, 1, IN, NS, ".")) 67 elif rrtype == SOA: 68 r.answer.append(dns.rrset.from_text(qname, 1, IN, SOA, ". . 0 0 0 0 0")) 69 else: 70 r.authority.append(dns.rrset.from_text(qname, 1, IN, SOA, ". . 0 0 0 0 0")) 71 r.flags |= dns.flags.AA 72 return r 73 74 75def sigterm(signum, frame): 76 print("Shutting down now...") 77 os.remove("ans.pid") 78 running = False 79 sys.exit(0) 80 81 82############################################################################ 83# Main 84# 85# Set up responder and control channel, open the pid file, and start 86# the main loop, listening for queries on the query channel or commands 87# on the control channel and acting on them. 88############################################################################ 89ip4 = "10.53.0.10" 90ip6 = "fd92:7065:b8e:ffff::10" 91 92try: 93 port = int(os.environ["PORT"]) 94except: 95 port = 5300 96 97query4_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 98query4_socket.bind((ip4, port)) 99havev6 = True 100try: 101 query6_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) 102 try: 103 query6_socket.bind((ip6, port)) 104 except: 105 query6_socket.close() 106 havev6 = False 107except: 108 havev6 = False 109signal.signal(signal.SIGTERM, sigterm) 110 111f = open("ans.pid", "w") 112pid = os.getpid() 113print(pid, file=f) 114f.close() 115 116running = True 117 118print("Listening on %s port %d" % (ip4, port)) 119if havev6: 120 print("Listening on %s port %d" % (ip6, port)) 121print("Ctrl-c to quit") 122 123if havev6: 124 input = [query4_socket, query6_socket] 125else: 126 input = [query4_socket] 127 128while running: 129 try: 130 inputready, outputready, exceptready = select.select(input, [], []) 131 except select.error as e: 132 break 133 except socket.error as e: 134 break 135 except KeyboardInterrupt: 136 break 137 138 for s in inputready: 139 if s == query4_socket or s == query6_socket: 140 print( 141 "Query received on %s" % (ip4 if s == query4_socket else ip6), end=" " 142 ) 143 # Handle incoming queries 144 msg = s.recvfrom(65535) 145 rsp = create_response(msg[0]) 146 if rsp: 147 print(dns.rcode.to_text(rsp.rcode())) 148 s.sendto(rsp.to_wire(), msg[1]) 149 else: 150 print("NO RESPONSE") 151 if not running: 152 break 153