Fix enum bug (was on dictionary even when no data). PcapDecoder can load multiple...
[anna.git] / example / diameter / pcapDecoder / main.cpp
index 80278e4..d296a40 100644 (file)
@@ -55,6 +55,9 @@
 #include <anna/diameter/stack/Engine.hpp>
 #include <anna/diameter/codec/Engine.hpp>
 #include <anna/diameter/codec/Message.hpp>
+//#include <anna/diameter/defines.hpp> // typedef unsigned int ApplicationId;
+#include <anna/diameter/codec/functions.hpp> // ApplicationId anna::diameter::codec::functions::getApplicationId(const anna::DataBlock &) throw(anna::RuntimeException);
+
 
 using namespace anna;
 using namespace anna::diameter;
@@ -147,6 +150,7 @@ typedef std::map < int /* frame */, Payload > payloads_t;
 typedef std::map < int /* frame */, Payload >::const_iterator payloads_it;
 payloads_t G_payloads;
 anna::diameter::codec::Message G_codecMsg;
+anna::diameter::codec::Engine *G_codecEngine;
 
 // Sniffing structures ////////////////////////////////////////////////////////////////////////////////////
 
@@ -323,6 +327,17 @@ void _exit(const std::string &message, int resultCode = 1) {
   exit(resultCode);
 }
 
+// Decodes a diameter message coming from a datablock
+void decodeDataBlock(const anna::DataBlock &db, unsigned int & detectedApplicationId) throw() {
+  try {
+    detectedApplicationId = anna::diameter::codec::functions::getApplicationId(db);
+    G_codecEngine->setDictionary(detectedApplicationId);
+    G_codecMsg.decode(db);
+  } catch(RuntimeException &ex) {
+    _exit(ex.asString());
+  }
+}
+
 
 //-------------------------------------------------------------------
 int main(int argc, char **argv) {
@@ -334,88 +349,94 @@ int main(int argc, char **argv) {
   //check command line arguments
   if(argc < 3) {
     std::string msg = "Usage: "; msg += exec;
-    msg += " <dictionaries> <input file> [--non-strict-validation]\n\n";
-    msg += "       dictionaries:            list of comma-separated xml dictionaries (one or more can be provided).\n";
+    msg += " <stacks> <input file> [--no-validation] [--ignore-flags]\n\n";
+    msg += "       stacks:                  <id1#dictionary1,id2#dictionary2,...,idN#dictionaryN>\n";
+    msg += "                                This is a list of comma-separated stacks defined by a #-separated pair <application-id#xml dictionary pathfile>\n";
+    msg += "                                If only one stack is provided, application-id could be omitted and then, all the messages will be decoded with the\n";
+    msg += "                                dictionary regardless the value of the application-id (the stack will be registered with id=0).\n";
     msg += "       Input file:              normally a pcap file, but hexadecimal content (colons allowed) can also be decoded (use '.hex' extension).\n";
-    msg += "       --non-strict-validation: no validation is performed, and wrong flags regarding dictionary are ignored in xml representation.";
+    msg += "       --no-validation:         no validation is performed.\n";
+    msg += "       --ignore-flags:          wrong flags regarding dictionary are ignored in xml representation.";
     _exit(msg);
   }
 
   // Command-line parameters:
-  std::string dictionaries = argv[1];
+  std::string stacks = argv[1];
   std::string inputFile = argv[2];
   bool isHex = (inputFile.substr(inputFile.find_last_of(".") + 1) == "hex");
   std::string outputFile = inputFile; // extension will be added later
-  std::string optional = argv[3] ? argv[3] : "";
-  bool non_strict_validation = ((argc == 4) && (optional == "--non-strict-validation"));
-  std::cout << "Dictionary(ies) provided: " << dictionaries << std::endl;
-  std::cout << "Input file provided:      " << inputFile << std::endl;
-  std::cout << "Validation kindness:      " << (non_strict_validation ? "non strict" : "strict") << std::endl;
-  // Logger and engines:
+  std::string optionals;
+  int indx = 3;
+
+  while(indx < argc) { optionals += " "; optionals += argv[indx]; indx++; }
+
+  bool no_validation = (optionals.find("--no-validation") != std::string::npos);
+  bool ignore_flags = (optionals.find("--ignore-flags") != std::string::npos);
   Logger::setLevel(Logger::Debug);
   Logger::initialize(execBN.c_str(), new TraceWriter(filetrace.c_str(), 2048000));
-  anna::diameter::codec::Engine *codecEngine =
-    new anna::diameter::codec::Engine();
+  G_codecEngine = new anna::diameter::codec::Engine();
   anna::diameter::stack::Engine &stackEngine =
     anna::diameter::stack::Engine::instantiate();
 
+  // Register stacks:
   try {
-    anna::diameter::stack::Dictionary * d = stackEngine.createDictionary(
-        0 /* stack id */);
-    // Analyze comma-separated list:
-    anna::Tokenizer lst;
-    lst.apply(dictionaries, ",");
-
-    if(lst.size() >= 1) {  // always true (at least one, because -dictionary is mandatory)
-      anna::Tokenizer::const_iterator tok_min(lst.begin());
-      anna::Tokenizer::const_iterator tok_max(lst.end());
-      anna::Tokenizer::const_iterator tok_iter;
-      std::string pathFile;
-      d->allowUpdates();
-
-      for(tok_iter = tok_min; tok_iter != tok_max; tok_iter++) {
-        pathFile = anna::Tokenizer::data(tok_iter);
-        d->load(pathFile);
+    anna::Tokenizer stacksTok;
+    stacksTok.apply(stacks, ",");
+    anna::Tokenizer::const_iterator stacks_it, stack_it;
+
+    for(stacks_it = stacksTok.begin(); stacks_it != stacksTok.end(); stacks_it++) {
+      std::string stack = anna::Tokenizer::data(stacks_it);
+      anna::Tokenizer stackTok;
+      stackTok.apply(stack, "#");
+
+      if(stackTok.size() == 1) {
+        if(stacksTok.size() != 1)
+          throw anna::RuntimeException("Application Id value is mandatory when more than one stack is going to be configured", ANNA_FILE_LOCATION);
+
+        anna::diameter::stack::Dictionary * d = stackEngine.createDictionary(0 /* no matter */, stack); // the stack is the dictionary
+        G_codecEngine->setDictionary(d);
+        break;
       }
-    }
 
-    codecEngine->setDictionary(d);
-    //LOGDEBUG(anna::Logger::debug(codecEngine->asString(), ANNA_FILE_LOCATION));
-
-    if(lst.size() > 1) {
-      std::string all_in_one = "./dictionary-all-in-one.xml";
-      std::ofstream out(all_in_one, std::ifstream::out);
-      std::string buffer = d->asXMLString();
-      out.write(buffer.c_str(), buffer.size());
-      out.close();
-      std::cout << "Written accumulated '" << all_in_one
-                << "' (provide it next time to be more comfortable)." << std::endl;
+      if(stackTok.size() != 2)
+        throw anna::RuntimeException("Each stack must be in the form '<application-id>#<xml dictionary pathfile>'", ANNA_FILE_LOCATION);
+
+      stack_it = stackTok.begin();
+      unsigned int stackId = atoll(anna::Tokenizer::data(stack_it));
+      stack_it++;
+      std::string file = anna::Tokenizer::data(stack_it);
+      anna::diameter::stack::Dictionary * d = stackEngine.createDictionary(stackId, file);
     }
+
+    std::cout << "Stacks provided:          " << std::endl;
+    std::cout << anna::functions::tab(stackEngine.asString(false /* light */));
+    std::cout << std::endl;
+    std::cout << "Input file provided:      " << inputFile << std::endl;
+    std::cout << "Validation:               " << (!no_validation ? "yes" : "no") << std::endl;
+    std::cout << "Ignore Flags:             " << (ignore_flags ? "yes" : "no") << std::endl;
+    std::cout << std::endl;
   } catch(anna::RuntimeException &ex) {
     _exit(ex.asString());
   }
 
-  if(non_strict_validation) {
-    codecEngine->setValidationMode(anna::diameter::codec::EngineImpl::ValidationMode::Never);
-    codecEngine->ignoreFlagsOnValidation(true);
-  }
+  // Validation kindness
+  if(no_validation) G_codecEngine->setValidationMode(anna::diameter::codec::EngineImpl::ValidationMode::Never);
+
+  if(ignore_flags) G_codecEngine->ignoreFlagsOnValidation(true);
 
   // Tracing:
   //if (cl.exists("trace"))
   //   anna::Logger::setLevel(anna::Logger::asLevel(cl.getValue("trace")));
   // Check hex content input file (look extension):
   anna::DataBlock db_aux(true);
+  unsigned int detectedApplicationId;
 
   if(isHex) {
     if(!getDataBlockFromHexFile(inputFile, db_aux))
       _exit("Error reading hex file provided");
 
-    try {
-      G_codecMsg.decode(db_aux);
-    } catch(RuntimeException &ex) {
-      _exit(ex.asString());
-    }
-
+    // Decode datablock:
+    decodeDataBlock(db_aux, detectedApplicationId);
     // Open output file:
     outputFile += ".as.xml";
     std::ofstream out(outputFile, std::ifstream::out);
@@ -467,21 +488,19 @@ int main(int argc, char **argv) {
     out
         << "==================================================================================================="
         << std::endl;
-    out << "Date:           " << ts_str << std::endl;
-    out << "Timestamp:      " << std::to_string(ts) << "."
+    out << "Date:            " << ts_str << std::endl;
+    out << "Timestamp:       " << std::to_string(ts) << "."
         << std::to_string(tsu) << std::endl;
-    out << "Origin IP:      " << (it->second).getSourceIP() << std::endl;
-    out << "Destination IP: " << (it->second).getDestinationIP() << std::endl;
-    out << std::endl;
+    out << "Origin IP:       " << (it->second).getSourceIP() << std::endl;
+    out << "Destination IP:  " << (it->second).getDestinationIP() << std::endl;
     // decode hex string:
     anna::functions::fromHexString((it->second).getDataAsHex(), db_aux);
-
-    try {
-      G_codecMsg.decode(db_aux);
-    } catch(RuntimeException &ex) {
-      _exit(ex.asString());
-    }
-
+    // Decode datablock:
+    decodeDataBlock(db_aux, detectedApplicationId);
+    // Stack identification:
+    //out << "Application Id:  " << detectedApplicationId << std::endl;
+    out << "Dictionary used: " << G_codecEngine->getDictionary()->getName() << std::endl;
+    out << std::endl;
     out << G_codecMsg.asXMLString();
   }