Merge branch 'import-dissect' into master

refs #2931

Change-Id: Iecf80a6672e5c89dc80bf15ed58ec170c886b724
diff --git a/manpages/conf.py b/manpages/conf.py
index 1476da8..3c0f6bb 100644
--- a/manpages/conf.py
+++ b/manpages/conf.py
@@ -11,4 +11,5 @@
     ('ndnping', 'ndnping', 'reachability testing client', None, 1),
     ('ndnpingserver', 'ndnpingserver', 'reachability testing server', None, 1),
     ('ndndump', 'ndndump', 'traffic analysis tool', None, 8),
+    ('ndn-dissect', 'ndn-dissect', 'NDN packet format inspector', None, 1),
 ]
diff --git a/manpages/ndn-dissect.rst b/manpages/ndn-dissect.rst
new file mode 100644
index 0000000..be92682
--- /dev/null
+++ b/manpages/ndn-dissect.rst
@@ -0,0 +1,38 @@
+ndn-dissect
+===========
+
+Usage
+-----
+
+::
+
+    ndn-dissect [-hV] [input-file]
+
+Description
+-----------
+
+``ndn-dissect`` is an NDN packet format inspector.
+It reads zero or more NDN packets from either an input file or the standard input,
+and displays the Type-Length-Value (TLV) structure of those packets on the standard output.
+
+Options
+-------
+
+``-h``
+  Print help and exit.
+
+``-V``
+  Print version and exit.
+
+``input-file``
+  The file to read packets from.
+  If no :option:`input-file` is given, the standard input is used.
+
+Examples
+--------
+
+Inspect the response to Interest ``ndn:/app1/video``
+
+::
+
+    ndnpeek ndn:/app1/video | ndn-dissect
diff --git a/tools/dissect/README.md b/tools/dissect/README.md
new file mode 100644
index 0000000..b0da01a
--- /dev/null
+++ b/tools/dissect/README.md
@@ -0,0 +1,13 @@
+# ndn-dissect
+
+**ndn-dissect** is an NDN packet format inspector.
+It reads zero or more NDN packets from either an input file or the standard input,
+and displays the Type-Length-Value (TLV) structure of those packets on the standard output.
+
+Usage example:
+
+1. start NFD on local machine
+2. execute `echo 'HELLO WORLD' | ndnpoke ndn:/localhost/demo/hello`
+3. on another console, execute `ndnpeek ndn:/localhost/demo/hello | ndn-dissect`
+
+For more information, consult the manpage.
diff --git a/tools/dissect/main.cpp b/tools/dissect/main.cpp
new file mode 100644
index 0000000..fa1a962
--- /dev/null
+++ b/tools/dissect/main.cpp
@@ -0,0 +1,114 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015,  Regents of the University of California.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ndn-tools, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ndn-dissect.hpp"
+#include "core/version.hpp"
+
+#include <boost/program_options/options_description.hpp>
+#include <boost/program_options/variables_map.hpp>
+#include <boost/program_options/parsers.hpp>
+
+namespace po = boost::program_options;
+
+namespace ndn {
+namespace dissect {
+
+void
+usage(std::ostream& os, const std::string& appName, const po::options_description& options)
+{
+  os << "Usage:\n"
+     << "  " << appName << " [input-file] \n"
+     << "\n"
+     << options;
+}
+
+int
+main(int argc, char* argv[])
+{
+  po::options_description visibleOptions;
+  visibleOptions.add_options()
+    ("help,h", "Print help and exit.")
+    ("version,V", "Print version and exit.")
+    ;
+
+  std::string inputFileName;
+  po::options_description hiddenOptions;
+  hiddenOptions.add_options()
+    ("input-file", po::value<std::string>(&inputFileName));
+  ;
+  po::positional_options_description positionalArguments;
+  positionalArguments
+    .add("input-file", -1);
+
+  po::options_description allOptions;
+  allOptions
+    .add(visibleOptions)
+    .add(hiddenOptions)
+    ;
+
+  po::variables_map vm;
+  try {
+    po::store(po::command_line_parser(argc, argv)
+                .options(allOptions)
+                .positional(positionalArguments)
+                .run(),
+              vm);
+    po::notify(vm);
+  }
+  catch (po::error& e) {
+    std::cerr << "ERROR: " << e.what() << std::endl << std::endl;
+    usage(std::cerr, argv[0], visibleOptions);
+    return 2;
+  }
+
+  if (vm.count("help") > 0) {
+    usage(std::cout, argv[0], visibleOptions);
+    return 0;
+  }
+
+  if (vm.count("version") > 0) {
+    std::cout << "ndn-dissect " << tools::VERSION << std::endl;
+    return 0;
+  }
+
+  std::ifstream inputFile;
+  std::istream* inputStream;
+
+  if (vm.count("input-file") > 0 && inputFileName != "-") {
+    inputFile.open(inputFileName);
+    inputStream = &inputFile;
+  }
+  else {
+    inputStream = &std::cin;
+  }
+
+  NdnDissect program;
+  program.dissect(std::cout, *inputStream);
+
+  return 0;
+}
+
+} // namespace dissect
+} // namespace ndn
+
+int
+main(int argc, char** argv)
+{
+  return ndn::dissect::main(argc, argv);
+}
diff --git a/tools/dissect/ndn-dissect.cpp b/tools/dissect/ndn-dissect.cpp
new file mode 100644
index 0000000..f37195e
--- /dev/null
+++ b/tools/dissect/ndn-dissect.cpp
@@ -0,0 +1,148 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015,  Regents of the University of California.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ndn-tools, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/**
+ * Copyright (c) 2013-2014 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ *
+ * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ */
+
+#include "ndn-dissect.hpp"
+
+#include <algorithm>
+#include <map>
+
+#include <ndn-cxx/name-component.hpp>
+#include <ndn-cxx/util/indented-stream.hpp>
+
+namespace ndn {
+namespace dissect {
+
+std::map<uint32_t, std::string> TLV_DICT = {
+  {tlv::Interest                     , "Interest"},
+  {tlv::Data                         , "Data"},
+  {tlv::Name                         , "Name"},
+  {tlv::NameComponent                , "NameComponent"},
+  {tlv::ImplicitSha256DigestComponent, "ImplicitSha256DigestComponent"},
+  {tlv::Selectors                    , "Selectors"},
+  {tlv::Nonce                        , "Nonce"},
+  {tlv::InterestLifetime             , "InterestLifetime"},
+  {tlv::MinSuffixComponents          , "MinSuffixComponents"},
+  {tlv::MaxSuffixComponents          , "MaxSuffixComponents"},
+  {tlv::PublisherPublicKeyLocator    , "PublisherPublicKeyLocator"},
+  {tlv::Exclude                      , "Exclude"},
+  {tlv::ChildSelector                , "ChildSelector"},
+  {tlv::MustBeFresh                  , "MustBeFresh"},
+  {tlv::Any                          , "Any"},
+  {tlv::MetaInfo                     , "MetaInfo"},
+  {tlv::Content                      , "Content"},
+  {tlv::SignatureInfo                , "SignatureInfo"},
+  {tlv::SignatureValue               , "SignatureValue"},
+  {tlv::ContentType                  , "ContentType"},
+  {tlv::FreshnessPeriod              , "FreshnessPeriod"},
+  {tlv::FinalBlockId                 , "FinalBlockId"},
+  {tlv::SignatureType                , "SignatureType"},
+  {tlv::KeyLocator                   , "KeyLocator"},
+  {tlv::KeyDigest                    , "KeyDigest"},
+};
+
+void
+NdnDissect::printType(std::ostream& os, uint32_t type)
+{
+  os << type << " (";
+
+  if (TLV_DICT.count(type) != 0) {
+    os << TLV_DICT[type];
+  }
+  else if (type < tlv::AppPrivateBlock1) {
+    os << "RESERVED_1";
+  }
+  else if (tlv::AppPrivateBlock1 <= type && type < 253) {
+    os << "APP_TAG_1";
+  }
+  else if (253 <= type && type < tlv::AppPrivateBlock2) {
+    os << "RESERVED_3";
+  }
+  else {
+    os << "APP_TAG_3";
+  }
+  os << ")";
+}
+
+void
+NdnDissect::printBlock(std::ostream& os, const Block& block)
+{
+  this->printType(os, block.type());
+  os << " (size: " << block.value_size() << ")";
+
+  try {
+    // if (block.type() != tlv::Content && block.type() != tlv::SignatureValue)
+    block.parse();
+  }
+  catch (tlv::Error& e) {
+    // pass (e.g., leaf block reached)
+
+    // @todo: Figure how to deterministically figure out that value is not recursive TLV block
+  }
+
+  if (block.elements().empty()) {
+    os << " [[";
+    name::Component(block.value(), block.value_size()).toUri(os);
+    os<< "]]";
+  }
+  os << std::endl;
+
+  util::IndentedStream os2(os, "  ");
+  std::for_each(block.elements_begin(), block.elements_end(),
+    [this, &os2] (const Block& element) {
+      this->printBlock(os2, element);
+    });
+}
+
+void
+NdnDissect::dissect(std::ostream& os, std::istream& is)
+{
+  while (is.peek() != std::char_traits<char>::eof()) {
+    try {
+      Block block = Block::fromStream(is);
+      this->printBlock(os, block);
+    }
+    catch (std::exception& e) {
+      std::cerr << "ERROR: " << e.what() << std::endl;
+    }
+  }
+}
+
+} // namespace dissect
+} // namespace ndn
diff --git a/tools/dissect/ndn-dissect.hpp b/tools/dissect/ndn-dissect.hpp
new file mode 100644
index 0000000..a90b845
--- /dev/null
+++ b/tools/dissect/ndn-dissect.hpp
@@ -0,0 +1,41 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015,  Regents of the University of California.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ndn-tools, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <ndn-cxx/encoding/block.hpp>
+#include <fstream>
+
+namespace ndn {
+namespace dissect {
+
+class NdnDissect : noncopyable
+{
+public:
+  void
+  dissect(std::ostream& os, std::istream& is);
+
+private:
+  void
+  printType(std::ostream& os, uint32_t type);
+
+  void
+  printBlock(std::ostream& os, const Block& block);
+};
+
+} // namespace dissect
+} // namespace ndn
diff --git a/tools/dissect/wscript b/tools/dissect/wscript
new file mode 100644
index 0000000..070ece2
--- /dev/null
+++ b/tools/dissect/wscript
@@ -0,0 +1,17 @@
+# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+top = '../..'
+
+def build(bld):
+    bld(features='cxx',
+        name='dissect-objects',
+        source=bld.path.ant_glob('*.cpp', excl='main.cpp'),
+        includes='.',
+        export_includes='.',
+        use='core-objects',
+        )
+
+    bld(features='cxx cxxprogram',
+        target='../../bin/ndn-dissect',
+        source='main.cpp',
+        use='dissect-objects',
+        )