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# SOA gets a unsigned response. 38# NS gets a unsigned response. 39# DNSKEY get a unsigned NODATA response. 40# A gets a signed response. 41# All other types get a unsigned NODATA response. 42############################################################################ 43def create_response(msg): 44 m = dns.message.from_wire(msg) 45 qname = m.question[0].name.to_text() 46 rrtype = m.question[0].rdtype 47 typename = dns.rdatatype.to_text(rrtype) 48 49 with open("query.log", "a") as f: 50 f.write("%s %s\n" % (typename, qname)) 51 print("%s %s" % (typename, qname), end=" ") 52 53 r = dns.message.make_response(m) 54 r.set_rcode(NOERROR) 55 if rrtype == A: 56 now = datetime.today() 57 expire = now + timedelta(days=30) 58 inception = now - timedelta(days=1) 59 rrsig = ( 60 "A 13 2 60 " 61 + expire.strftime("%Y%m%d%H%M%S") 62 + " " 63 + inception.strftime("%Y%m%d%H%M%S") 64 + " 12345 " 65 + qname 66 + " gB+eISXAhSPZU2i/II0W9ZUhC2SCIrb94mlNvP5092WAeXxqN/vG43/1nmDl" 67 + "y2Qs7y5VCjSMOGn85bnaMoAc7w==" 68 ) 69 r.answer.append(dns.rrset.from_text(qname, 1, IN, A, "10.53.0.10")) 70 r.answer.append(dns.rrset.from_text(qname, 1, IN, RRSIG, rrsig)) 71 elif rrtype == NS: 72 r.answer.append(dns.rrset.from_text(qname, 1, IN, NS, ".")) 73 elif rrtype == SOA: 74 r.answer.append(dns.rrset.from_text(qname, 1, IN, SOA, ". . 0 0 0 0 0")) 75 else: 76 r.authority.append(dns.rrset.from_text(qname, 1, IN, SOA, ". . 0 0 0 0 0")) 77 r.flags |= dns.flags.AA 78 return r 79 80 81def sigterm(signum, frame): 82 print("Shutting down now...") 83 os.remove("ans.pid") 84 running = False 85 sys.exit(0) 86 87 88############################################################################ 89# Main 90# 91# Set up responder and control channel, open the pid file, and start 92# the main loop, listening for queries on the query channel or commands 93# on the control channel and acting on them. 94############################################################################ 95ip4 = "10.53.0.10" 96ip6 = "fd92:7065:b8e:ffff::10" 97 98try: 99 port = int(os.environ["PORT"]) 100except: 101 port = 5300 102 103query4_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 104query4_socket.bind((ip4, port)) 105havev6 = True 106try: 107 query6_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) 108 try: 109 query6_socket.bind((ip6, port)) 110 except: 111 query6_socket.close() 112 havev6 = False 113except: 114 havev6 = False 115signal.signal(signal.SIGTERM, sigterm) 116 117f = open("ans.pid", "w") 118pid = os.getpid() 119print(pid, file=f) 120f.close() 121 122running = True 123 124print("Listening on %s port %d" % (ip4, port)) 125if havev6: 126 print("Listening on %s port %d" % (ip6, port)) 127print("Ctrl-c to quit") 128 129if havev6: 130 input = [query4_socket, query6_socket] 131else: 132 input = [query4_socket] 133 134while running: 135 try: 136 inputready, outputready, exceptready = select.select(input, [], []) 137 except select.error as e: 138 break 139 except socket.error as e: 140 break 141 except KeyboardInterrupt: 142 break 143 144 for s in inputready: 145 if s == query4_socket or s == query6_socket: 146 print( 147 "Query received on %s" % (ip4 if s == query4_socket else ip6), end=" " 148 ) 149 # Handle incoming queries 150 msg = s.recvfrom(65535) 151 rsp = create_response(msg[0]) 152 if rsp: 153 print(dns.rcode.to_text(rsp.rcode())) 154 s.sendto(rsp.to_wire(), msg[1]) 155 else: 156 print("NO RESPONSE") 157 if not running: 158 break 159