etc/services - sync with NetBSD-8
[minix.git] / external / bsd / bind / dist / bin / python / dnssec-checkds.py.in
blob3a66ee24b4a6c09a1c758044ef5d8b107a77755a
1 #!@PYTHON@
2 ############################################################################
3 # Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
5 # Permission to use, copy, modify, and/or distribute this software for any
6 # purpose with or without fee is hereby granted, provided that the above
7 # copyright notice and this permission notice appear in all copies.
9 # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 # REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 # AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 # PERFORMANCE OF THIS SOFTWARE.
16 ############################################################################
18 import argparse
19 import pprint
20 import os
22 prog='dnssec-checkds'
24 # These routines permit platform-independent location of BIND 9 tools
25 if os.name == 'nt':
26 import win32con
27 import win32api
29 def prefix(bindir = ''):
30 if os.name != 'nt':
31 return os.path.join('@prefix@', bindir)
33 bind_subkey = "Software\\ISC\\BIND"
34 hKey = None
35 keyFound = True
36 try:
37 hKey = win32api.RegOpenKeyEx(win32con.HKEY_LOCAL_MACHINE, bind_subkey)
38 except:
39 keyFound = False
40 if keyFound:
41 try:
42 (namedBase, _) = win32api.RegQueryValueEx(hKey, "InstallDir")
43 except:
44 keyFound = False
45 win32api.RegCloseKey(hKey)
46 if keyFound:
47 return os.path.join(namedBase, bindir)
48 return os.path.join(win32api.GetSystemDirectory(), bindir)
50 def shellquote(s):
51 if os.name == 'nt':
52 return '"' + s.replace('"', '"\\"') + '"'
53 return "'" + s.replace("'", "'\\''") + "'"
55 ############################################################################
56 # DSRR class:
57 # Delegation Signer (DS) resource record
58 ############################################################################
59 class DSRR:
60 hashalgs = {1: 'SHA-1', 2: 'SHA-256', 3: 'GOST', 4: 'SHA-384' }
61 rrname=''
62 rrclass='IN'
63 rrtype='DS'
64 keyid=None
65 keyalg=None
66 hashalg=None
67 digest=''
68 ttl=0
70 def __init__(self, rrtext):
71 if not rrtext:
72 return
74 fields = rrtext.split()
75 if len(fields) < 7:
76 return
78 self.rrname = fields[0].lower()
79 fields = fields[1:]
80 if fields[0].upper() in ['IN','CH','HS']:
81 self.rrclass = fields[0].upper()
82 fields = fields[1:]
83 else:
84 self.ttl = int(fields[0])
85 self.rrclass = fields[1].upper()
86 fields = fields[2:]
88 if fields[0].upper() != 'DS':
89 raise Exception
91 self.rrtype = 'DS'
92 self.keyid = int(fields[1])
93 self.keyalg = int(fields[2])
94 self.hashalg = int(fields[3])
95 self.digest = ''.join(fields[4:]).upper()
97 def __repr__(self):
98 return('%s %s %s %d %d %d %s' %
99 (self.rrname, self.rrclass, self.rrtype, self.keyid,
100 self.keyalg, self.hashalg, self.digest))
102 def __eq__(self, other):
103 return self.__repr__() == other.__repr__()
105 ############################################################################
106 # DLVRR class:
107 # DNSSEC Lookaside Validation (DLV) resource record
108 ############################################################################
109 class DLVRR:
110 hashalgs = {1: 'SHA-1', 2: 'SHA-256', 3: 'GOST', 4: 'SHA-384' }
111 parent=''
112 dlvname=''
113 rrname='IN'
114 rrclass='IN'
115 rrtype='DLV'
116 keyid=None
117 keyalg=None
118 hashalg=None
119 digest=''
120 ttl=0
122 def __init__(self, rrtext, dlvname):
123 if not rrtext:
124 return
126 fields = rrtext.split()
127 if len(fields) < 7:
128 return
130 self.dlvname = dlvname.lower()
131 parent = fields[0].lower().strip('.').split('.')
132 parent.reverse()
133 dlv = dlvname.split('.')
134 dlv.reverse()
135 while len(dlv) != 0 and len(parent) != 0 and parent[0] == dlv[0]:
136 parent = parent[1:]
137 dlv = dlv[1:]
138 if len(dlv) != 0:
139 raise Exception
140 parent.reverse()
141 self.parent = '.'.join(parent)
142 self.rrname = self.parent + '.' + self.dlvname + '.'
144 fields = fields[1:]
145 if fields[0].upper() in ['IN','CH','HS']:
146 self.rrclass = fields[0].upper()
147 fields = fields[1:]
148 else:
149 self.ttl = int(fields[0])
150 self.rrclass = fields[1].upper()
151 fields = fields[2:]
153 if fields[0].upper() != 'DLV':
154 raise Exception
156 self.rrtype = 'DLV'
157 self.keyid = int(fields[1])
158 self.keyalg = int(fields[2])
159 self.hashalg = int(fields[3])
160 self.digest = ''.join(fields[4:]).upper()
162 def __repr__(self):
163 return('%s %s %s %d %d %d %s' %
164 (self.rrname, self.rrclass, self.rrtype,
165 self.keyid, self.keyalg, self.hashalg, self.digest))
167 def __eq__(self, other):
168 return self.__repr__() == other.__repr__()
170 ############################################################################
171 # checkds:
172 # Fetch DS RRset for the given zone from the DNS; fetch DNSKEY
173 # RRset from the masterfile if specified, or from DNS if not.
174 # Generate a set of expected DS records from the DNSKEY RRset,
175 # and report on congruency.
176 ############################################################################
177 def checkds(zone, masterfile = None):
178 dslist=[]
179 fp=os.popen("%s +noall +answer -t ds -q %s" %
180 (shellquote(args.dig), shellquote(zone)))
181 for line in fp:
182 dslist.append(DSRR(line))
183 dslist = sorted(dslist, key=lambda ds: (ds.keyid, ds.keyalg, ds.hashalg))
184 fp.close()
186 dsklist=[]
188 if masterfile:
189 fp = os.popen("%s -f %s %s " %
190 (shellquote(args.dsfromkey), shellquote(masterfile),
191 shellquote(zone)))
192 else:
193 fp = os.popen("%s +noall +answer -t dnskey -q %s | %s -f - %s" %
194 (shellquote(args.dig), shellquote(zone),
195 shellquote(args.dsfromkey), shellquote(zone)))
197 for line in fp:
198 dsklist.append(DSRR(line))
200 fp.close()
202 if (len(dsklist) < 1):
203 print ("No DNSKEY records found in zone apex")
204 return False
206 found = False
207 for ds in dsklist:
208 if ds in dslist:
209 print ("DS for KSK %s/%03d/%05d (%s) found in parent" %
210 (ds.rrname.strip('.'), ds.keyalg,
211 ds.keyid, DSRR.hashalgs[ds.hashalg]))
212 found = True
213 else:
214 print ("DS for KSK %s/%03d/%05d (%s) missing from parent" %
215 (ds.rrname.strip('.'), ds.keyalg,
216 ds.keyid, DSRR.hashalgs[ds.hashalg]))
218 if not found:
219 print ("No DS records were found for any DNSKEY")
221 return found
223 ############################################################################
224 # checkdlv:
225 # Fetch DLV RRset for the given zone from the DNS; fetch DNSKEY
226 # RRset from the masterfile if specified, or from DNS if not.
227 # Generate a set of expected DLV records from the DNSKEY RRset,
228 # and report on congruency.
229 ############################################################################
230 def checkdlv(zone, lookaside, masterfile = None):
231 dlvlist=[]
232 fp=os.popen("%s +noall +answer -t dlv -q %s" %
233 (shellquote(args.dig), shellquote(zone + '.' + lookaside)))
234 for line in fp:
235 dlvlist.append(DLVRR(line, lookaside))
236 dlvlist = sorted(dlvlist,
237 key=lambda dlv: (dlv.keyid, dlv.keyalg, dlv.hashalg))
238 fp.close()
241 # Fetch DNSKEY records from DNS and generate DLV records from them
243 dlvklist=[]
244 if masterfile:
245 fp = os.popen("%s -f %s -l %s %s " %
246 (args.dsfromkey, masterfile, lookaside, zone))
247 else:
248 fp = os.popen("%s +noall +answer -t dnskey %s | %s -f - -l %s %s"
249 % (shellquote(args.dig), shellquote(zone),
250 shellquote(args.dsfromkey), shellquote(lookaside),
251 shellquote(zone)))
253 for line in fp:
254 dlvklist.append(DLVRR(line, lookaside))
256 fp.close()
258 if (len(dlvklist) < 1):
259 print ("No DNSKEY records found in zone apex")
260 return False
262 found = False
263 for dlv in dlvklist:
264 if dlv in dlvlist:
265 print ("DLV for KSK %s/%03d/%05d (%s) found in %s" %
266 (dlv.parent, dlv.keyalg, dlv.keyid,
267 DLVRR.hashalgs[dlv.hashalg], dlv.dlvname))
268 found = True
269 else:
270 print ("DLV for KSK %s/%03d/%05d (%s) missing from %s" %
271 (dlv.parent, dlv.keyalg, dlv.keyid,
272 DLVRR.hashalgs[dlv.hashalg], dlv.dlvname))
274 if not found:
275 print ("No DLV records were found for any DNSKEY")
277 return found
280 ############################################################################
281 # parse_args:
282 # Read command line arguments, set global 'args' structure
283 ############################################################################
284 def parse_args():
285 global args
286 parser = argparse.ArgumentParser(description=prog + ': checks DS coverage')
288 bindir = 'bin'
289 if os.name == 'nt':
290 sbindir = 'bin'
291 else:
292 sbindir = 'sbin'
294 parser.add_argument('zone', type=str, help='zone to check')
295 parser.add_argument('-f', '--file', dest='masterfile', type=str,
296 help='zone master file')
297 parser.add_argument('-l', '--lookaside', dest='lookaside', type=str,
298 help='DLV lookaside zone')
299 parser.add_argument('-d', '--dig', dest='dig',
300 default=os.path.join(prefix(bindir), 'dig'),
301 type=str, help='path to \'dig\'')
302 parser.add_argument('-D', '--dsfromkey', dest='dsfromkey',
303 default=os.path.join(prefix(sbindir),
304 'dnssec-dsfromkey'),
305 type=str, help='path to \'dig\'')
306 parser.add_argument('-v', '--version', action='version', version='9.9.1')
307 args = parser.parse_args()
309 args.zone = args.zone.strip('.')
310 if args.lookaside:
311 lookaside = args.lookaside.strip('.')
313 ############################################################################
314 # Main
315 ############################################################################
316 def main():
317 parse_args()
319 if args.lookaside:
320 found = checkdlv(args.zone, args.lookaside, args.masterfile)
321 else:
322 found = checkds(args.zone, args.masterfile)
324 exit(0 if found else 1)
326 if __name__ == "__main__":
327 main()