mgmt: Add addDelegatedRrset to automatically create AUTH records

Change-Id: I4d87ca52d3d176a8541236ec31526f1ca6033e34
diff --git a/src/daemon/rrset-factory.cpp b/src/daemon/rrset-factory.cpp
index 9395f42..2830dac 100644
--- a/src/daemon/rrset-factory.cpp
+++ b/src/daemon/rrset-factory.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2016, Regents of the University of California.
+ * Copyright (c) 2014-2017, Regents of the University of California.
  *
  * This file is part of NDNS (Named Data Networking Domain Name Service).
  * See AUTHORS.md for complete list of NDNS authors and contributors.
@@ -223,6 +223,31 @@
   return rrset;
 }
 
+Rrset
+RrsetFactory::generateAuthRrset(const Name& label,
+                                const name::Component& type,
+                                const uint64_t version,
+                                time::seconds ttl)
+{
+  if (!m_checked) {
+    BOOST_THROW_EXCEPTION(Error("You have to call checkZoneKey before call generate functions"));
+  }
+
+  if (ttl == DEFAULT_RR_TTL)
+    ttl = m_zone.getTtl();
+
+  Name name;
+  Rrset rrset;
+  std::tie(rrset, name) = generateBaseRrset(label, type, version, ttl);
+
+  Data data(name);
+
+  setContentType(data, NDNS_AUTH, ttl);
+  sign(data);
+  rrset.setData(data.wireEncode());
+
+  return rrset;
+}
 
 void
 RrsetFactory::sign(Data& data)
diff --git a/src/daemon/rrset-factory.hpp b/src/daemon/rrset-factory.hpp
index e5165d3..083b8a5 100644
--- a/src/daemon/rrset-factory.hpp
+++ b/src/daemon/rrset-factory.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2016, Regents of the University of California.
+ * Copyright (c) 2014-2017, Regents of the University of California.
  *
  * This file is part of NDNS (Named Data Networking Domain Name Service).
  * See AUTHORS.md for complete list of NDNS authors and contributors.
@@ -73,6 +73,12 @@
                    const std::vector<std::string>& contents);
 
   Rrset
+  generateAuthRrset(const Name& label,
+                    const name::Component& type,
+                    const uint64_t version,
+                    time::seconds ttl);
+
+  Rrset
   generateCertRrset(const Name& label,
                     const name::Component& type,
                     const uint64_t version,
diff --git a/src/mgmt/management-tool.cpp b/src/mgmt/management-tool.cpp
index 9ac19e5..b2f9d44 100644
--- a/src/mgmt/management-tool.cpp
+++ b/src/mgmt/management-tool.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2016, Regents of the University of California.
+ * Copyright (c) 2014-2017, Regents of the University of California.
  *
  * This file is part of NDNS (Named Data Networking Domain Name Service).
  * See AUTHORS.md for complete list of NDNS authors and contributors.
@@ -197,8 +197,73 @@
 }
 
 void
+ManagementTool::addMultiLevelLabelRrset(Rrset& rrset,
+                                        RrsetFactory& zoneRrFactory,
+                                        const time::seconds& authTtl)
+{
+  const Name& label = rrset.getLabel();
+
+  // Check whether it is legal to insert the rrset
+  for (size_t i = 1; i <= label.size() - 1; i++) {
+    Name prefix = label.getPrefix(i);
+    Rrset prefixNsRr(rrset.getZone());
+    prefixNsRr.setLabel(prefix);
+    prefixNsRr.setType(label::NS_RR_TYPE);
+    if (m_dbMgr.find(prefixNsRr)) {
+      Data data(prefixNsRr.getData());
+      if (data.getContentType() == NDNS_LINK) {
+        BOOST_THROW_EXCEPTION(Error("Cannot override " + boost::lexical_cast<std::string>(prefixNsRr) + " (NDNS_LINK)"));
+      }
+    }
+  }
+
+  // check that it does not override existing AUTH
+  if (rrset.getType() == label::NS_RR_TYPE) {
+    Rrset rrsetCopy = rrset;
+    if (m_dbMgr.find(rrsetCopy)) {
+      if (Data(rrsetCopy.getData()).getContentType() == NDNS_AUTH) {
+        BOOST_THROW_EXCEPTION(Error("Cannot override " + boost::lexical_cast<std::string>(rrsetCopy) + " (NDNS_AUTH)"));
+      }
+    }
+  }
+
+  for (size_t i = 1; i <= label.size() - 1; i++) {
+    Name prefix = label.getPrefix(i);
+    Rrset prefixNsRr(rrset.getZone());
+    prefixNsRr.setLabel(prefix);
+    prefixNsRr.setType(label::NS_RR_TYPE);
+    if (m_dbMgr.find(prefixNsRr)) {
+      NDNS_LOG_INFO("NDNS_AUTH Rrset Label=" << prefix << " is already existed, insertion skipped");
+      continue;
+    }
+
+    Rrset authRr = zoneRrFactory.generateAuthRrset(prefix, label::NS_RR_TYPE,
+                                                   VERSION_USE_UNIX_TIMESTAMP, authTtl);
+    NDNS_LOG_INFO("Adding NDNS_AUTH " << authRr);
+    m_dbMgr.insert(authRr);
+  }
+
+  checkRrsetVersion(rrset);
+  NDNS_LOG_INFO("Adding " << rrset);
+  m_dbMgr.insert(rrset);
+}
+
+void
 ManagementTool::addRrset(Rrset& rrset)
 {
+  if (rrset.getLabel().size() > 1) {
+    throw Error("Cannot add rrset with label size > 1, should use addMultiLevelLabelRrset instead");
+  }
+
+  // check that it does not override existing AUTH
+  Rrset rrsetCopy = rrset;
+  rrsetCopy.setType(label::NS_RR_TYPE);
+  if (m_dbMgr.find(rrsetCopy)) {
+    if (Data(rrsetCopy.getData()).getContentType() == NDNS_AUTH) {
+      BOOST_THROW_EXCEPTION(Error("Can not add this Rrset: it overrides a NDNS_AUTH record"));
+    }
+  }
+
   checkRrsetVersion(rrset);
   NDNS_LOG_INFO("Added " << rrset);
   m_dbMgr.insert(rrset);
diff --git a/src/mgmt/management-tool.hpp b/src/mgmt/management-tool.hpp
index d41bd2b..e63358d 100644
--- a/src/mgmt/management-tool.hpp
+++ b/src/mgmt/management-tool.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2016, Regents of the University of California.
+ * Copyright (c) 2014-2017, Regents of the University of California.
  *
  * This file is part of NDNS (Named Data Networking Domain Name Service).
  * See AUTHORS.md for complete list of NDNS authors and contributors.
@@ -25,6 +25,7 @@
 #include "./daemon/zone.hpp"
 #include "./daemon/db-mgr.hpp"
 #include "./daemon/rrset.hpp"
+#include "./daemon/rrset-factory.hpp"
 #include "./clients/response.hpp"
 
 #include <stdexcept>
@@ -75,19 +76,18 @@
    *
    *  Specifically, It will generate a KSK and a DSK (and their certificates) to the following
    *  places:
-   *  1) Local NDNS database: a new zone is added.
-   *  2) Local NDNS database: an ID-CERT of the DSK is added.
-   *  3) KeyChain: an identity named with zone name is added.
-   *  4) KeyChain: a KSK and its self-signed certificate is added. The ownership of the KSK is the
+   *  1. Local NDNS database: a new zone is added.
+   *  2. Local NDNS database: an ID-CERT of the DSK is added.
+   *  3. KeyChain: an identity named with zone name is added.
+   *  4. KeyChain: a KSK and its self-signed certificate is added. The ownership of the KSK is the
    *  parent zone.
-   *  5) KeyChain: a DSK and its KSK signed certificate is added.
+   *  5. KeyChain: a DSK and its KSK signed certificate is added.
    *
-   *  -SS.cert (self-signed)
-   *  -SKS.cert (self's Key signed)
-   *  -PKS.cert (parent's Key Signed)
+   *  - SS.cert (self-signed)
+   *  - SKS.cert (self's Key signed)
+   *  - PKS.cert (parent's Key Signed)
    *
-   *  @attention
-   *  1) to create root zone, supply zoneName and parentZoneName both with ROOT_ZONE
+   *  @note To create root zone, supply zoneName and parentZoneName both with ROOT_ZONE
    *
    *  @param zoneName zone's name
    *  @param parentZoneName parent zone's name
@@ -96,7 +96,7 @@
    *                      should not be empty)
    *  @param kskCertName if given, a zone will be created with this ksk certificate
    *  @param dskCertName if given, a zone will be created with this dsk certificate and provided
-   *  ksk certificate will be ignored
+   *                     ksk certificate will be ignored
    */
   void
   createZone(const Name& zoneName,
@@ -124,7 +124,7 @@
   void
   exportCertificate(const Name& certName, const std::string& outFile = DEFAULT_IO);
 
-  /** @brief add rrset to the NDNS local database
+  /** @brief Add rrset to the NDNS local database
    *
    *  This overload is capable of adding any data to the rrset as long as the supplied data is
    *  valid.
@@ -145,13 +145,44 @@
            const Name& dskCertName = DEFAULT_CERT,
            const ndn::io::IoEncoding encoding = ndn::io::BASE64);
 
-  /** @brief add rrset to the NDNS local database
+  /** @brief Add rrset to the NDNS local database
+   *
+   *  @throw Error if the @p rrset label size is larger than 1 or @p rrset will override an
+   *               existing AUTH record
    *
    *  @param rrset rrset
    */
   void
   addRrset(Rrset& rrset);
 
+  /** @brief Add rrset with multi-level label to the NDNS local database
+   *
+   *  The appropriate AUTH records will be created automatically if they do not yet exist. The
+   *  existing records are kept intact.
+   *
+   *  @throw Error If one of the levels has been delegated to another zone. For example, if
+   *               there is an NS record with label `/foo`, then inserting @p rrset having a
+   *               multi-level label that use `/foo` as prefix will cause an error.
+   *
+   *  @throw Error If @p rrset will override an AUTH record.  For example, if there is already
+   *               an AUTH record with label `/foo/bar`, then inserting NS-type @p rrset that
+   *               has the the same label will cause an error.
+   *
+   *  For example, inserting a rrset with `/foo/bar/test` label and TXT type into zone `/zone/NDNS`
+   *  will create:
+   *  - `/zone/NDNS/foo/NS` (.ContentType AUTH)
+   *  - `/zone/NDNS/foo/bar/NS` (.ContentType AUTH)
+   *  - `/zone/NDNS/foo/bar/test/TXT` (.ContentType NDNS-Resp)
+   *
+   *  @param rrset rrset
+   *  @param zoneRrFactory that is used for generate AUTH packet
+   *  @param authTtl
+   */
+  void
+  addMultiLevelLabelRrset(Rrset& rrset,
+                          RrsetFactory& zoneRrFactory,
+                          const time::seconds& authTtl);
+
   /** @brief remove rrset from the NDNS local database
    *
    *  @param zoneName the name of the zone holding the rrset
diff --git a/tests/unit/validator.cpp b/tests/unit/validator.cpp
index ff9547f..293cff0 100644
--- a/tests/unit/validator.cpp
+++ b/tests/unit/validator.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2016, Regents of the University of California.
+ * Copyright (c) 2014-2017, Regents of the University of California.
  *
  * This file is part of NDNS (Named Data Networking Domain Name Service).
  * See AUTHORS.md for complete list of NDNS authors and contributors.
@@ -122,7 +122,7 @@
     BOOST_CHECK_EQUAL(m_keyChain.doesCertificateExist(certName), true);
     auto cert = m_keyChain.getCertificate(certName);
     m_face.getIoService().post([this, cert] {
-        m_face.receive<Data>(*cert);
+        m_face.receive(*cert);
       });
   }
 
diff --git a/tools/ndns-add-rr.cpp b/tools/ndns-add-rr.cpp
index 2ec81aa..4e0a757 100644
--- a/tools/ndns-add-rr.cpp
+++ b/tools/ndns-add-rr.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2016, Regents of the University of California.
+ * Copyright (c) 2014-2017, Regents of the University of California.
  *
  * This file is part of NDNS (Named Data Networking Domain Name Service).
  * See AUTHORS.md for complete list of NDNS authors and contributors.
@@ -30,6 +30,8 @@
 
 #include <string>
 
+NDNS_LOG_INIT("AddRrTool")
+
 int
 main(int argc, char* argv[])
 {
@@ -159,7 +161,13 @@
     }
 
     ndn::ndns::ManagementTool tool(db, keyChain);
-    tool.addRrset(rrset);
+
+    if (label.size() > 1) {
+      NDNS_LOG_TRACE("add multi-level label Rrset, using the same TTL as the Rrset");
+      tool.addMultiLevelLabelRrset(rrset, rrsetFactory, ttl);
+    } else {
+      tool.addRrset(rrset);
+    }
 
     /// @todo Report success or failure
     //        May be also show the inserted record in ndns-list-zone format