lsdb: Segment LSAs larger than max segment size

refs: #2965

Change-Id: I5dd091b663db2cc5e2d925144e57ee55a486b3cd
diff --git a/src/lsdb.cpp b/src/lsdb.cpp
index d4b7193..8d7f1ec 100644
--- a/src/lsdb.cpp
+++ b/src/lsdb.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  The University of Memphis,
+ * Copyright (c) 2014-2016,  The University of Memphis,
  *                           Regents of the University of California,
  *                           Arizona Board of Regents.
  *
@@ -19,24 +19,114 @@
  * NLSR, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  **/
 
-#include <string>
-#include <utility>
-
 #include "lsdb.hpp"
-#include "nlsr.hpp"
-#include "conf-parameter.hpp"
-#include "utility/name-helper.hpp"
+
 #include "logger.hpp"
+#include "nlsr.hpp"
+#include "publisher/segment-publisher.hpp"
+#include "utility/name-helper.hpp"
+
+#include <ndn-cxx/security/signing-helpers.hpp>
+#include <ndn-cxx/util/segment-fetcher.hpp>
+
+#include <string>
 
 namespace nlsr {
 
 INIT_LOGGER("Lsdb");
 
+class LsaContentPublisher : public SegmentPublisher<ndn::Face>
+{
+public:
+  LsaContentPublisher(ndn::Face& face,
+                      ndn::KeyChain& keyChain,
+                      const ndn::time::milliseconds& freshnessPeriod,
+                      const std::string& content)
+    : SegmentPublisher(face, keyChain, freshnessPeriod)
+    , m_content(content)
+  {
+  }
+
+  virtual size_t
+  generate(ndn::EncodingBuffer& outBuffer) {
+    size_t totalLength = 0;
+    totalLength += outBuffer.prependByteArray(reinterpret_cast<const uint8_t*>(m_content.c_str()),
+                                              m_content.size());
+    return totalLength;
+  }
+
+private:
+  const std::string m_content;
+};
+
 const ndn::Name::Component Lsdb::NAME_COMPONENT = ndn::Name::Component("lsdb");
 const ndn::time::seconds Lsdb::GRACE_PERIOD = ndn::time::seconds(10);
 const steady_clock::TimePoint Lsdb::DEFAULT_LSA_RETRIEVAL_DEADLINE = steady_clock::TimePoint::min();
 
-using namespace std;
+Lsdb::Lsdb(Nlsr& nlsr, ndn::Scheduler& scheduler, SyncLogicHandler& sync)
+  : m_nlsr(nlsr)
+  , m_scheduler(scheduler)
+  , m_sync(sync)
+  , m_lsaRefreshTime(0)
+  , m_adjLsaBuildInterval(ADJ_LSA_BUILD_INTERVAL_DEFAULT)
+{
+}
+
+void
+Lsdb::onFetchLsaError(uint32_t errorCode,
+                      const std::string& msg,
+                      ndn::Name& interestName,
+                      uint32_t retransmitNo,
+                      const ndn::time::steady_clock::TimePoint& deadline,
+                      ndn::Name lsaName,
+                      uint64_t seqNo)
+{
+  _LOG_DEBUG("Failed to fetch LSA: " << lsaName << ", Error code: " << errorCode
+                                                << ", Message: " << msg);
+
+  if (ndn::time::steady_clock::now() < deadline) {
+    SequenceNumberMap::const_iterator it = m_highestSeqNo.find(lsaName);
+
+    if (it != m_highestSeqNo.end() && it->second == seqNo) {
+      // If the SegmentFetcher failed due to an Interest timeout, it is safe to re-express
+      // immediately since at the least the LSA Interest lifetime has elapsed.
+      // Otherwise, it is necessary to delay the Interest re-expression to prevent
+      // the potential for constant Interest flooding.
+      ndn::time::seconds delay = m_nlsr.getConfParameter().getLsaInterestLifetime();
+
+      if (errorCode == ndn::util::SegmentFetcher::ErrorCode::INTEREST_TIMEOUT) {
+        delay = ndn::time::seconds(0);
+      }
+
+      m_scheduler.scheduleEvent(delay, std::bind(&Lsdb::expressInterest, this,
+                                                 interestName, retransmitNo + 1, deadline));
+    }
+  }
+}
+
+void
+Lsdb::afterFetchLsa(const ndn::ConstBufferPtr& bufferPtr, ndn::Name& interestName)
+{
+  shared_ptr<ndn::Data> data = make_shared<ndn::Data>(ndn::Name(interestName));
+  data->setContent(bufferPtr);
+
+  _LOG_DEBUG("Received data for LSA(name): " << data->getName());
+
+  ndn::Name lsaName = interestName.getSubName(0, interestName.size()-1);
+  uint64_t seqNo = interestName[-1].toNumber();
+
+  if (m_highestSeqNo.find(lsaName) == m_highestSeqNo.end()) {
+    m_highestSeqNo[lsaName] = seqNo;
+  }
+  else if (seqNo > m_highestSeqNo[lsaName]) {
+    m_highestSeqNo[lsaName] = seqNo;
+  }
+  else if (seqNo < m_highestSeqNo[lsaName]) {
+    return;
+  }
+
+  onContentValidated(data);
+}
 
 void
 Lsdb::cancelScheduleLsaExpiringEvent(ndn::EventId eid)
@@ -50,7 +140,6 @@
   return nlsa1.getKey() == key;
 }
 
-
 bool
 Lsdb::buildAndInstallOwnNameLsa()
 {
@@ -640,7 +729,7 @@
 }
 
 void
-Lsdb::setThisRouterPrefix(string trp)
+Lsdb::setThisRouterPrefix(std::string trp)
 {
   m_thisRouterPrefix = trp;
 }
@@ -748,7 +837,6 @@
   }
 }
 
-
 void
 Lsdb::expressInterest(const ndn::Name& interestName, uint32_t timeoutCount,
                       steady_clock::TimePoint deadline)
@@ -758,8 +846,6 @@
   }
 
   ndn::Name lsaName = interestName.getSubName(0, interestName.size()-1);
-
-  ndn::Interest interest(interestName);
   uint64_t seqNo = interestName[-1].toNumber();
 
   if (m_highestSeqNo.find(lsaName) == m_highestSeqNo.end()) {
@@ -772,14 +858,15 @@
     return;
   }
 
+  ndn::Interest interest(interestName);
   interest.setInterestLifetime(m_nlsr.getConfParameter().getLsaInterestLifetime());
 
-  _LOG_DEBUG("Expressing Interest for LSA: " << interestName << " Seq number: " << seqNo);
-  m_nlsr.getNlsrFace().expressInterest(interest,
-                                       ndn::bind(&Lsdb::onContent,
-                                                 this, _2, deadline, lsaName, seqNo),
-                                       ndn::bind(&Lsdb::processInterestTimedOut,
-                                                 this, _1, timeoutCount, deadline, lsaName, seqNo));
+  _LOG_DEBUG("Fetching Data for LSA: " << interestName << " Seq number: " << seqNo);
+  ndn::util::SegmentFetcher::fetch(m_nlsr.getNlsrFace(), interest,
+                                   m_nlsr.getValidator(),
+                                   ndn::bind(&Lsdb::afterFetchLsa, this, _1, interestName),
+                                   ndn::bind(&Lsdb::onFetchLsaError, this, _1, _2, interestName,
+                                             timeoutCount, deadline, lsaName, seqNo));
 }
 
 void
@@ -788,7 +875,7 @@
   const ndn::Name& interestName(interest.getName());
   _LOG_DEBUG("Interest received for LSA: " << interestName);
 
-  string chkString("LSA");
+  std::string chkString("LSA");
   int32_t lsaPosition = util::getNameComponentPosition(interest.getName(), chkString);
 
   if (lsaPosition >= 0) {
@@ -820,16 +907,12 @@
 void
 Lsdb::putLsaData(const ndn::Interest& interest, const std::string& content)
 {
-  ndn::shared_ptr<ndn::Data> data = ndn::make_shared<ndn::Data>();
-  data->setName(ndn::Name(interest.getName()).appendVersion());
-  data->setFreshnessPeriod(m_lsaRefreshTime);
-  data->setContent(reinterpret_cast<const uint8_t*>(content.c_str()), content.size());
-  m_nlsr.getKeyChain().sign(*data, m_nlsr.getDefaultCertName());
-  ndn::SignatureSha256WithRsa signature(data->getSignature());
-  ndn::Name signingCertName = signature.getKeyLocator().getName();
-  _LOG_DEBUG("Sending data for LSA(name): " << interest.getName());
-  _LOG_DEBUG("Data signed with: " << signingCertName);
-  m_nlsr.getNlsrFace().put(*data);
+  LsaContentPublisher publisher(m_nlsr.getNlsrFace(),
+                                m_nlsr.getKeyChain(),
+                                m_lsaRefreshTime,
+                                content);
+  publisher.publish(interest.getName(),
+                    ndn::security::signingByCertificate(m_nlsr.getDefaultCertName()));
 }
 
 void
@@ -875,58 +958,23 @@
 }
 
 void
-Lsdb::onContent(const ndn::Data& data,
-                const steady_clock::TimePoint& deadline, ndn::Name lsaName,
-                uint64_t seqNo)
-{
-  _LOG_DEBUG("Received data for LSA(name): " << data.getName());
-  if (data.getSignature().hasKeyLocator()) {
-    if (data.getSignature().getKeyLocator().getType() == ndn::KeyLocator::KeyLocator_Name) {
-      _LOG_DEBUG("Data signed with: " << data.getSignature().getKeyLocator().getName());
-    }
-  }
-  m_nlsr.getValidator().validate(data,
-                                 ndn::bind(&Lsdb::onContentValidated, this, _1),
-                                 ndn::bind(&Lsdb::onContentValidationFailed, this, _1, _2,
-                                           deadline, lsaName, seqNo));
-
-}
-
-void
-Lsdb::retryContentValidation(const ndn::shared_ptr<const ndn::Data>& data,
-                             const steady_clock::TimePoint& deadline, ndn::Name lsaName,
-                             uint64_t seqNo)
-{
-  _LOG_DEBUG("Retrying validation of LSA(name): " << data->getName());
-  if (data->getSignature().hasKeyLocator()) {
-    if (data->getSignature().getKeyLocator().getType() == ndn::KeyLocator::KeyLocator_Name) {
-      _LOG_DEBUG("Data signed with: " << data->getSignature().getKeyLocator().getName());
-    }
-  }
-  m_nlsr.getValidator().validate(*data,
-                                 ndn::bind(&Lsdb::onContentValidated, this, _1),
-                                 ndn::bind(&Lsdb::onContentValidationFailed, this, _1, _2,
-                                           deadline, lsaName, seqNo));
-}
-
-void
 Lsdb::onContentValidated(const ndn::shared_ptr<const ndn::Data>& data)
 {
   const ndn::Name& dataName = data->getName();
   _LOG_DEBUG("Data validation successful for LSA: " << dataName);
 
-  string chkString("LSA");
+  std::string chkString("LSA");
   int32_t lsaPosition = util::getNameComponentPosition(dataName, chkString);
 
   if (lsaPosition >= 0) {
 
     ndn::Name originRouter = m_nlsr.getConfParameter().getNetwork();
-    originRouter.append(dataName.getSubName(lsaPosition + 1, dataName.size() - lsaPosition - 4));
+    originRouter.append(dataName.getSubName(lsaPosition + 1, dataName.size() - lsaPosition - 3));
 
-    uint64_t seqNo = dataName[-2].toNumber();
-    string dataContent(reinterpret_cast<const char*>(data->getContent().value()));
+    uint64_t seqNo = dataName[-1].toNumber();
+    std::string dataContent(reinterpret_cast<const char*>(data->getContent().value()));
 
-    std::string interestedLsType  = dataName[-3].toUri();
+    std::string interestedLsType  = dataName[-2].toUri();
 
     if (interestedLsType == NameLsa::TYPE_STRING) {
       processContentNameLsa(originRouter.append(interestedLsType), seqNo, dataContent);
@@ -944,32 +992,6 @@
 }
 
 void
-Lsdb::onContentValidationFailed(const ndn::shared_ptr<const ndn::Data>& data,
-                                const std::string& msg,
-                                const steady_clock::TimePoint& deadline, ndn::Name lsaName,
-                                uint64_t seqNo)
-{
-  _LOG_DEBUG("Validation Error: " << msg);
-
-  // Delay re-validation by LSA Interest Lifetime.  When error callback will have an error
-  // code, re-validation should be done only when some keys from certification chain failed
-  // to be fetched.  After that change, delaying will no longer be necessary.
-
-  // Stop retrying if delayed re-validation will be scheduled pass the deadline
-  if (steady_clock::now() + m_nlsr.getConfParameter().getLsaInterestLifetime() < deadline) {
-
-    SequenceNumberMap::const_iterator it = m_highestSeqNo.find(lsaName);
-
-    if (it != m_highestSeqNo.end() && it->second == seqNo) {
-      _LOG_DEBUG("Scheduling revalidation attempt");
-      m_scheduler.scheduleEvent(m_nlsr.getConfParameter().getLsaInterestLifetime(),
-                                ndn::bind(&Lsdb::retryContentValidation, this, data,
-                                          deadline, lsaName, seqNo));
-    }
-  }
-}
-
-void
 Lsdb::processContentNameLsa(const ndn::Name& lsaKey,
                             uint64_t lsSeqNo, std::string& dataContent)
 {
@@ -1014,24 +1036,6 @@
   }
 }
 
-void
-Lsdb::processInterestTimedOut(const ndn::Interest& interest, uint32_t retransmitNo,
-                              const ndn::time::steady_clock::TimePoint& deadline, ndn::Name lsaName,
-                              uint64_t seqNo)
-{
-  const ndn::Name& interestName = interest.getName();
-  _LOG_DEBUG("Interest timed out for  LSA: " << interestName);
-
-  if (ndn::time::steady_clock::now() < deadline) {
-
-    SequenceNumberMap::const_iterator it = m_highestSeqNo.find(lsaName);
-
-    if (it != m_highestSeqNo.end() &&  it->second == seqNo) {
-      expressInterest(interestName, retransmitNo + 1, deadline);
-    }
-  }
-}
-
 ndn::time::system_clock::TimePoint
 Lsdb::getLsaExpirationTimePoint()
 {