From 3b4f23cdbfa3f285d06eea8c4101650d2ab4e945 Mon Sep 17 00:00:00 2001
From: Evan Hunt <each@isc.org>
Date: Thu, 26 Oct 2017 21:05:11 -0700
Subject: [PATCH 1314/3677] [master] dnssec-checkds -s

4794.	[func]		"dnssec-checkds -s" specifies a file from which
			to read a DS set rather than querying the parent.
			[RT #44667]
---
 CHANGES                                     |   8 +-
 bin/python/dnssec-checkds.docbook           |  24 +++---
 bin/python/isc/checkds.py.in                |  49 ++++++-----
 bin/tests/system/checkds/clean.sh           |   2 -
 bin/tests/system/checkds/dig.pl             |   2 -
 bin/tests/system/checkds/dig.sh             |   3 -
 bin/tests/system/checkds/prep.example.db    | 121 ++++++++++++++++++++++++++++
 bin/tests/system/checkds/prep.example.ds.db |   2 +
 bin/tests/system/checkds/tests.sh           |   9 +++
 doc/arm/notes.xml                           |   8 ++
 10 files changed, 190 insertions(+), 38 deletions(-)
 create mode 100644 bin/tests/system/checkds/prep.example.db
 create mode 100644 bin/tests/system/checkds/prep.example.ds.db

diff --git a/bin/python/dnssec-checkds.docbook b/bin/python/dnssec-checkds.docbook
index 91716bc..069d6e9 100644
--- a/bin/python/dnssec-checkds.docbook
+++ b/bin/python/dnssec-checkds.docbook
@@ -42,20 +42,13 @@
   <refsynopsisdiv>
     <cmdsynopsis sepchar=" ">
       <command>dnssec-checkds</command>
-      <arg choice="opt" rep="norepeat"><option>-l <replaceable class="parameter">domain</replaceable></option></arg>
-      <arg choice="opt" rep="norepeat"><option>-f <replaceable class="parameter">file</replaceable></option></arg>
       <arg choice="opt" rep="norepeat"><option>-d <replaceable class="parameter">dig path</replaceable></option></arg>
       <arg choice="opt" rep="norepeat"><option>-D <replaceable class="parameter">dsfromkey path</replaceable></option></arg>
-      <arg choice="req" rep="norepeat">zone</arg>
-    </cmdsynopsis>
-    <cmdsynopsis sepchar=" ">
-      <command>dnssec-dsfromkey</command>
-      <arg choice="opt" rep="norepeat"><option>-l <replaceable class="parameter">domain</replaceable></option></arg>
       <arg choice="opt" rep="norepeat"><option>-f <replaceable class="parameter">file</replaceable></option></arg>
-      <arg choice="opt" rep="norepeat"><option>-d <replaceable class="parameter">dig path</replaceable></option></arg>
-      <arg choice="opt" rep="norepeat"><option>-D <replaceable class="parameter">dsfromkey path</replaceable></option></arg>
+      <arg choice="opt" rep="norepeat"><option>-l <replaceable class="parameter">domain</replaceable></option></arg>
+      <arg choice="opt" rep="norepeat"><option>-s <replaceable class="parameter">file</replaceable></option></arg>
       <arg choice="req" rep="norepeat">zone</arg>
-   </cmdsynopsis>
+    </cmdsynopsis>
   </refsynopsisdiv>
 
   <refsection><info><title>DESCRIPTION</title></info>
@@ -93,6 +86,17 @@
       </varlistentry>
 
       <varlistentry>
+        <term>-s <replaceable class="parameter">file</replaceable></term>
+        <listitem>
+          <para>
+            Specifies a prepared dsset file, such as would be generated
+            by <command>dnssec-signzone</command>, to use as a source for
+            the DS RRset instead of querying the parent.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term>-d <replaceable class="parameter">dig path</replaceable></term>
         <listitem>
           <para>
diff --git a/bin/python/isc/checkds.py.in b/bin/python/isc/checkds.py.in
index ce50355..a161554 100644
--- a/bin/python/isc/checkds.py.in
+++ b/bin/python/isc/checkds.py.in
@@ -34,7 +34,11 @@ class SECRR:
         if not rrtext:
             raise Exception
 
-        fields = rrtext.decode('ascii').split()
+        # 'str' does not have decode method in python3
+        if type(rrtext) is not str:
+            fields = rrtext.decode('ascii').split()
+        else:
+            fields = rrtext.split()
         if len(fields) < 7:
             raise Exception
 
@@ -89,35 +93,39 @@ class SECRR:
 # Generate a set of expected DS/DLV records from the DNSKEY RRset,
 # and report on congruency.
 ############################################################################
-def check(zone, args, masterfile=None, lookaside=None):
+def check(zone, args):
     rrlist = []
-    cmd = [args.dig, "+noall", "+answer", "-t", "dlv" if lookaside else "ds",
-           "-q", zone + "." + lookaside if lookaside else zone]
-    fp, _ = Popen(cmd, stdout=PIPE).communicate()
+    if args.dssetfile:
+        fp = open(args.dssetfile).read()
+    else:
+        cmd = [args.dig, "+noall", "+answer", "-t",
+                "dlv" if args.lookaside else "ds", "-q",
+                zone + "." + args.lookaside if args.lookaside else zone]
+        fp, _ = Popen(cmd, stdout=PIPE).communicate()
 
     for line in fp.splitlines():
-        rrlist.append(SECRR(line, lookaside))
+        rrlist.append(SECRR(line, args.lookaside))
     rrlist = sorted(rrlist, key=lambda rr: (rr.keyid, rr.keyalg, rr.hashalg))
 
     klist = []
 
-    if masterfile:
-        cmd = [args.dsfromkey, "-f", masterfile]
-        if lookaside:
-            cmd += ["-l", lookaside]
+    if args.masterfile:
+        cmd = [args.dsfromkey, "-f", args.masterfile]
+        if args.lookaside:
+            cmd += ["-l", args.lookaside]
         cmd.append(zone)
         fp, _ = Popen(cmd, stdout=PIPE).communicate()
     else:
         intods, _ = Popen([args.dig, "+noall", "+answer", "-t", "dnskey",
                            "-q", zone], stdout=PIPE).communicate()
         cmd = [args.dsfromkey, "-f", "-"]
-        if lookaside:
-            cmd += ["-l", lookaside]
+        if args.lookaside:
+            cmd += ["-l", args.lookaside]
         cmd.append(zone)
         fp, _ = Popen(cmd, stdin=PIPE, stdout=PIPE).communicate(intods)
 
     for line in fp.splitlines():
-        klist.append(SECRR(line, lookaside))
+        klist.append(SECRR(line, args.lookaside))
 
     if len(klist) < 1:
         print("No DNSKEY records found in zone apex")
@@ -136,7 +144,8 @@ def check(zone, args, masterfile=None, lookaside=None):
                    rr.keyid, SECRR.hashalgs[rr.hashalg]))
 
     if not found:
-        print("No %s records were found for any DNSKEY" % ("DLV" if lookaside else "DS"))
+        print("No %s records were found for any DNSKEY" %
+                ("DLV" if args.lookaside else "DS"))
 
     return found
 
@@ -151,10 +160,6 @@ def parse_args():
     sbindir = 'bin' if os.name == 'nt' else 'sbin'
 
     parser.add_argument('zone', type=str, help='zone to check')
-    parser.add_argument('-f', '--file', dest='masterfile', type=str,
-                        help='zone master file')
-    parser.add_argument('-l', '--lookaside', dest='lookaside', type=str,
-                        help='DLV lookaside zone')
     parser.add_argument('-d', '--dig', dest='dig',
                         default=os.path.join(prefix(bindir), 'dig'),
                         type=str, help='path to \'dig\'')
@@ -162,6 +167,12 @@ def parse_args():
                         default=os.path.join(prefix(sbindir),
                                              'dnssec-dsfromkey'),
                         type=str, help='path to \'dig\'')
+    parser.add_argument('-f', '--file', dest='masterfile', type=str,
+                        help='zone master file')
+    parser.add_argument('-l', '--lookaside', dest='lookaside', type=str,
+                        help='DLV lookaside zone')
+    parser.add_argument('-s', '--dsset', dest='dssetfile', type=str,
+                        help='prepared DSset file')
     parser.add_argument('-v', '--version', action='version',
                         version=version)
     args = parser.parse_args()
@@ -178,5 +189,5 @@ def parse_args():
 ############################################################################
 def main():
     args = parse_args()
-    found = check(args.zone, args, args.masterfile, args.lookaside)
+    found = check(args.zone, args)
     exit(0 if found else 1)
 
-- 
1.8.3.1

