]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #2382 in SNORT/snort3 from ~SVLASIUK/snort3:dump_config_json to...
authorBhagya Tholpady (bbantwal) <bbantwal@cisco.com>
Wed, 19 Aug 2020 20:27:13 +0000 (20:27 +0000)
committerBhagya Tholpady (bbantwal) <bbantwal@cisco.com>
Wed, 19 Aug 2020 20:27:13 +0000 (20:27 +0000)
Squashed commit of the following:

commit bcba018627626465fbb8f55dacab8a6856690da3
Author: Serhii Vlasiuk <svlasiuk@cisco.com>
Date:   Thu Aug 6 14:34:18 2020 +0300

    dump_config: dump config in JSON format to stdout

    add new command-line option --dump-config=all to dump
    the config in JSON format

commit 67b5defe6f7a132ff62d2bd278364476cefe372c
Author: Oleksii Khomiakovskyi <okhomiak@cisco.com>
Date:   Wed Aug 5 17:13:33 2020 +0300

    helpers: add unit tests for special characters escaping

30 files changed:
src/CMakeLists.txt
src/detection/fp_create.cc
src/dump_config/CMakeLists.txt [new file with mode: 0644]
src/dump_config/config_data.cc [new file with mode: 0644]
src/dump_config/config_data.h [moved from src/main/config_tree.h with 74% similarity]
src/dump_config/config_output.cc [new file with mode: 0644]
src/dump_config/config_output.h [new file with mode: 0644]
src/dump_config/dev_notes.txt [new file with mode: 0644]
src/dump_config/json_config_output.cc [new file with mode: 0644]
src/dump_config/json_config_output.h [new file with mode: 0644]
src/dump_config/text_config_output.cc [moved from src/main/config_tree.cc with 51% similarity]
src/dump_config/text_config_output.h [new file with mode: 0644]
src/framework/value.cc
src/framework/value.h
src/helpers/test/json_stream_test.cc
src/log/messages.cc
src/log/messages.h
src/main.cc
src/main/CMakeLists.txt
src/main/dev_notes.txt
src/main/shell.cc
src/main/shell.h
src/main/snort.cc
src/main/snort_config.cc
src/main/snort_config.h
src/main/snort_module.cc
src/managers/module_manager.cc
src/parser/parser.cc
src/trace/trace_module.cc
src/trace/trace_swap.cc

index c5dc29dc4f0dae2e35d8529b09487ce22754c340..cc640a868515182983d4d157697c822af90307ff 100644 (file)
@@ -89,6 +89,7 @@ add_subdirectory(actions)
 add_subdirectory(codecs)
 add_subdirectory(control)
 add_subdirectory(detection)
+add_subdirectory(dump_config)
 add_subdirectory(events)
 add_subdirectory(file_api)
 add_subdirectory(filters)
@@ -139,6 +140,7 @@ add_executable( snort
     $<TARGET_OBJECTS:control>
     $<TARGET_OBJECTS:decompress>
     $<TARGET_OBJECTS:detection>
+    $<TARGET_OBJECTS:dump_config>
     $<TARGET_OBJECTS:events>
     $<TARGET_OBJECTS:file_api>
     $<TARGET_OBJECTS:file_connector>
index 21a0cd084ab261b6cefd4003f9912592a49960ab..cbc8405a216c01198a6839186aa00c278bbb1941 100644 (file)
@@ -1613,7 +1613,7 @@ int fpCreateFastPacketDetection(SnortConfig* sc)
     if ( log_rule_group_details )
         LogMessage("Service Based Rule Maps Done....\n");
 
-    if ( !sc->validation_mode() or sc->mem_check() )
+    if ( !sc->test_mode() or sc->mem_check() )
     {
         unsigned c = compile_mpses(sc, can_build_mt(fp));
         unsigned expected = mpse_count + offload_mpse_count;
diff --git a/src/dump_config/CMakeLists.txt b/src/dump_config/CMakeLists.txt
new file mode 100644 (file)
index 0000000..35e0c52
--- /dev/null
@@ -0,0 +1,12 @@
+
+add_library ( dump_config OBJECT
+    config_data.cc
+    config_data.h
+    config_output.cc
+    config_output.h
+    json_config_output.cc
+    json_config_output.h
+    text_config_output.cc
+    text_config_output.h
+)
+
diff --git a/src/dump_config/config_data.cc b/src/dump_config/config_data.cc
new file mode 100644 (file)
index 0000000..d2ffb3d
--- /dev/null
@@ -0,0 +1,334 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// config_data.cc author Serhii Vlasiuk <svlasiuk@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "config_data.h"
+
+#include <cassert>
+
+using namespace snort;
+
+BaseConfigNode::BaseConfigNode(BaseConfigNode* p) :
+    parent(p)
+{}
+
+void BaseConfigNode::add_child_node(BaseConfigNode* node)
+{
+    assert(node);
+    children.push_back(node);
+}
+
+void BaseConfigNode::clear_nodes(BaseConfigNode* node)
+{
+    for ( auto& config_node : node->children )
+        clear_nodes(config_node);
+
+    delete node;
+}
+
+TreeConfigNode::TreeConfigNode(BaseConfigNode* parent_node,
+    const std::string& node_name, const Parameter::Type node_type) :
+        BaseConfigNode(parent_node), name(node_name), type(node_type)
+{}
+
+BaseConfigNode* TreeConfigNode::get_node(const std::string& name)
+{
+    for ( auto node : children )
+    {
+        if ( node->get_name() == name )
+            return node;
+    }
+    return nullptr;
+}
+
+ValueConfigNode::ValueConfigNode(BaseConfigNode* parent_node, const Value& val,
+    const std::string& name) :
+        BaseConfigNode(parent_node), value(val), custom_name(name)
+{}
+
+void ValueConfigNode::set_value(const snort::Value& v)
+{
+    if ( value.get_param_type() == Parameter::PT_MULTI )
+    {
+        std::string origin = value.get_origin_string();
+        if ( !multi_value && value.has_default() )
+            origin.clear();
+
+        origin += " ";
+        origin += v.get_origin_string();
+        value.set_origin(origin.c_str());
+        multi_value = true;
+    }
+    else
+        value = v;
+}
+
+BaseConfigNode* ValueConfigNode::get_node(const std::string& name)
+{
+    if ( !custom_name.empty() )
+        return ((custom_name == name) and value.has_default()) ? this : nullptr; 
+    else
+        return value.is(name.c_str()) and value.has_default() ? this : nullptr;
+}
+
+ConfigData::ConfigData(const char* file)
+{
+    if ( file )
+        file_name = file;
+}
+
+void ConfigData::sort()
+{
+    config_trees.sort([](const BaseConfigNode* l, const BaseConfigNode* r)
+        { return l->get_name() < r->get_name(); });
+}
+
+void ConfigData::clear()
+{
+    for ( auto config_tree: config_trees )
+        BaseConfigNode::clear_nodes(config_tree);
+
+    config_trees.clear();
+}
+
+#ifdef UNIT_TEST
+#include <catch/snort_catch.h>
+
+TEST_CASE("add_nodes", "[ConfigData]")
+{
+    ConfigData config_data("test_file");
+
+    auto node_test1 = new TreeConfigNode(nullptr, "test1", Parameter::Type::PT_TABLE);
+    auto node_test2 = new TreeConfigNode(nullptr, "test2", Parameter::Type::PT_TABLE);
+
+    config_data.add_config_tree(node_test1);
+    config_data.add_config_tree(node_test2);
+
+    CHECK(config_data.config_trees.size() == 2);
+
+    config_data.clear();
+}
+
+TEST_CASE("clear_nodes", "[ConfigData]")
+{
+    ConfigData config_data("test_file");
+
+    auto node_test1 = new TreeConfigNode(nullptr, "test1", Parameter::Type::PT_TABLE);
+    auto node_test2 = new TreeConfigNode(nullptr, "test2", Parameter::Type::PT_TABLE);
+
+    config_data.add_config_tree(node_test1);
+    config_data.add_config_tree(node_test2);
+
+    config_data.clear();
+
+    CHECK(config_data.config_trees.size() == 0);
+}
+
+TEST_CASE("sort_nodes", "[ConfigData]")
+{
+    ConfigData config_data("test_file");
+
+    auto node_test1 = new TreeConfigNode(nullptr, "test1", Parameter::Type::PT_TABLE);
+    auto node_test2 = new TreeConfigNode(nullptr, "test2", Parameter::Type::PT_TABLE);
+    auto node_test3 = new TreeConfigNode(nullptr, "test3", Parameter::Type::PT_TABLE);
+    auto node_test4 = new TreeConfigNode(nullptr, "test4", Parameter::Type::PT_TABLE);
+
+    config_data.add_config_tree(node_test2);
+    config_data.add_config_tree(node_test3);
+    config_data.add_config_tree(node_test4);
+    config_data.add_config_tree(node_test1);
+
+    CHECK(config_data.config_trees.front()->get_name() == "test2");
+    CHECK(config_data.config_trees.back()->get_name() == "test1");
+
+    config_data.sort();
+
+    CHECK(config_data.config_trees.front()->get_name() == "test1");
+    CHECK(config_data.config_trees.back()->get_name() == "test4");
+
+    config_data.clear();
+}
+
+TEST_CASE("tree_config_node", "[TreeConfigNode]")
+{
+    BaseConfigNode* parent_node = new TreeConfigNode(nullptr, "parent_node",
+        Parameter::Type::PT_TABLE);
+    BaseConfigNode* child_node = new TreeConfigNode(parent_node, "child_node",
+        Parameter::Type::PT_LIST);
+
+    parent_node->add_child_node(child_node);
+
+    SECTION("get_name")
+    {
+        CHECK(parent_node->get_name() == "parent_node");
+        CHECK(child_node->get_name() == "child_node");
+    }
+    SECTION("get_type")
+    {
+        CHECK(parent_node->get_type() == Parameter::Type::PT_TABLE);
+        CHECK(child_node->get_type() == Parameter::Type::PT_LIST);
+    }
+    SECTION("get_node")
+    {
+        CHECK(parent_node->get_node("child_node") == child_node);
+        CHECK(parent_node->get_node("other_node") == nullptr);
+        CHECK(child_node->get_node("child_node") == nullptr);
+    }
+    SECTION("get_parent_node")
+    {
+        CHECK(child_node->get_parent_node() == parent_node);
+        CHECK(parent_node->get_parent_node() == nullptr);
+    }
+    SECTION("get_children")
+    {
+        CHECK(parent_node->get_children().size() == 1);
+        CHECK(child_node->get_children().size() == 0);
+    }
+    SECTION("get_value")
+    {
+        CHECK(parent_node->get_value() == nullptr);
+        CHECK(child_node->get_value() == nullptr);
+    }
+
+    BaseConfigNode::clear_nodes(parent_node);
+}
+
+TEST_CASE("value_config_node", "[ValueConfigNode]")
+{
+    BaseConfigNode* parent_node = new TreeConfigNode(nullptr, "parent_node",
+        Parameter::Type::PT_TABLE);
+    
+    const Parameter p_string("param_str", Parameter::PT_STRING, nullptr, nullptr,
+        "test param PT_STRING type");
+
+    const Parameter p_string_custom("param_str_custom", Parameter::PT_STRING, nullptr,
+        "custom_default", "test param PT_STRING type with custom name");
+
+    const Parameter p_bool_w_default("param_bool", Parameter::PT_BOOL, nullptr, "false",
+        "test param PT_BOOL type with default");
+
+    const Parameter p_multi_w_default("param_multi", Parameter::PT_MULTI,
+        "test1 | test2 | test3 | test4", "test2 test3", "test param PT_MULTI type with default");
+
+    Value val_str("test_str");
+    val_str.set(&p_string);
+
+    Value val_str_custom("test_str_custom");
+    val_str_custom.set(&p_string_custom);
+
+    Value val_bool(true);
+    val_bool.set(&p_bool_w_default);
+
+    Value val_multi("test2 test3");
+    val_multi.set(&p_multi_w_default);
+
+    BaseConfigNode* value_node_str = new ValueConfigNode(parent_node, val_str);
+    BaseConfigNode* value_node_bool = new ValueConfigNode(parent_node, val_bool);
+    BaseConfigNode* value_node_multi = new ValueConfigNode(parent_node, val_multi);
+    BaseConfigNode* value_node_custom_name = new ValueConfigNode(parent_node, val_str_custom,
+        "custom_name");
+
+    parent_node->add_child_node(value_node_str);
+    parent_node->add_child_node(value_node_bool);
+    parent_node->add_child_node(value_node_multi);
+    parent_node->add_child_node(value_node_custom_name);
+
+    SECTION("get_name")
+    {
+        CHECK(value_node_str->get_name() == "param_str");
+        CHECK(value_node_bool->get_name() == "param_bool");
+        CHECK(value_node_multi->get_name() == "param_multi");
+        CHECK(value_node_custom_name->get_name() == "custom_name");
+    }
+    SECTION("get_type")
+    {
+        CHECK(value_node_str->get_type() == Parameter::PT_STRING);
+        CHECK(value_node_bool->get_type() == Parameter::PT_BOOL);
+        CHECK(value_node_multi->get_type() == Parameter::PT_MULTI);
+        CHECK(value_node_custom_name->get_type() == Parameter::PT_STRING);
+    }
+    SECTION("get_node")
+    {
+        CHECK(parent_node->get_node("param_str") == value_node_str);
+        CHECK(parent_node->get_node("param_bool") == value_node_bool);
+        CHECK(parent_node->get_node("param_multi") == value_node_multi);
+        CHECK(parent_node->get_node("custom_name") == value_node_custom_name);
+
+        CHECK(value_node_str->get_node("param_str") == nullptr);
+        CHECK(value_node_bool->get_node("param_bool") == value_node_bool);
+        CHECK(value_node_multi->get_node("param_multi") == value_node_multi);
+        CHECK(value_node_custom_name->get_node("custom_name") == value_node_custom_name);
+    }
+    SECTION("get_parent_node")
+    {
+        CHECK(value_node_str->get_parent_node() == parent_node);
+        CHECK(value_node_bool->get_parent_node() == parent_node);
+        CHECK(value_node_multi->get_parent_node() == parent_node);
+        CHECK(value_node_custom_name->get_parent_node() == parent_node);
+    }
+    SECTION("get_children")
+    {
+        CHECK(parent_node->get_children().size() == 4);
+        CHECK(value_node_multi->get_children().size() == 0);
+        CHECK(value_node_str->get_children().size() == 0);
+        CHECK(value_node_bool->get_children().size() == 0);
+        CHECK(value_node_custom_name->get_children().size() == 0);
+    }
+    SECTION("get_value")
+    {
+        CHECK(value_node_str->get_value()->get_origin_string() == "test_str");
+        CHECK(value_node_bool->get_value()->get_bool() == true);
+        CHECK(value_node_multi->get_value()->get_origin_string() == "test2 test3");
+        CHECK(value_node_custom_name->get_value()->get_origin_string() == "test_str_custom");
+    }
+
+    Value new_val_str("new_value");
+    value_node_str->set_value(new_val_str);
+
+    Value new_custom_val_str("new_custom_value");
+    value_node_custom_name->set_value(new_custom_val_str);
+
+    Value new_val_bool(false);
+    value_node_bool->set_value(new_val_bool);
+
+    Value new_val1_multi("test1");
+    value_node_multi->set_value(new_val1_multi);
+
+    Value new_val2_multi("test2");
+    value_node_multi->set_value(new_val2_multi);
+
+    Value new_val3_multi("test3");
+    value_node_multi->set_value(new_val3_multi);
+    
+    SECTION("get_value_after_update")
+    {
+        CHECK(value_node_str->get_value()->get_origin_string() == "new_value");
+        CHECK(value_node_bool->get_value()->get_bool() == false);
+        CHECK(value_node_multi->get_value()->get_origin_string() == "test1 test2 test3");
+        CHECK(value_node_custom_name->get_value()->get_origin_string() == "new_custom_value");
+    }
+
+    BaseConfigNode::clear_nodes(parent_node);
+}
+
+#endif
+
similarity index 74%
rename from src/main/config_tree.h
rename to src/dump_config/config_data.h
index 598dbb12eb29f335bb8194359a140280527a1237..b6ad0603dc0cdae187233d94835dd5e1a518c478 100644 (file)
 // with this program; if not, write to the Free Software Foundation, Inc.,
 // 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 //--------------------------------------------------------------------------
-// config_tree.h author Serhii Vlasiuk <svlasiuk@cisco.com>
+// config_data.h author Serhii Vlasiuk <svlasiuk@cisco.com>
 
-#ifndef CONFIG_TREE_H
-#define CONFIG_TREE_H
+#ifndef CONFIG_DATA_H
+#define CONFIG_DATA_H
 
 #include <list>
 
 
 class BaseConfigNode;
 
-using ChildrenNodes = std::list<BaseConfigNode*>;
-
-class ConfigTextFormat
-{
-public:
-    static void print(const BaseConfigNode* parent, const std::string& config_name);
-};
+using ConfigTrees = std::list<BaseConfigNode*>;
 
 class BaseConfigNode
 {
@@ -43,10 +37,10 @@ public:
     virtual std::string get_name() const = 0;
     virtual snort::Parameter::Type get_type() const = 0;
     virtual BaseConfigNode* get_node(const std::string& name) = 0;
-    virtual std::string data() const { return ""; }
     virtual void set_value(const snort::Value&) {}
+    virtual const snort::Value* get_value() const { return nullptr; }
 
-    const ChildrenNodes& get_children() const
+    const ConfigTrees& get_children() const
     { return children; }
 
     BaseConfigNode* get_parent_node() const
@@ -57,7 +51,7 @@ public:
     static void clear_nodes(BaseConfigNode* root);
 
 protected:
-    ChildrenNodes children;
+    ConfigTrees children;
     BaseConfigNode* parent = nullptr;
 };
 
@@ -84,26 +78,43 @@ private:
 class ValueConfigNode : public BaseConfigNode
 {
 public:
-    ValueConfigNode(BaseConfigNode* parent, const snort::Value& value);
+    ValueConfigNode(BaseConfigNode* parent, const snort::Value& value,
+        const std::string& name = "");
 
 private:
     virtual std::string get_name() const override
-    { return value.get_name(); }
+    { return !custom_name.empty() ? custom_name : value.get_name(); }
 
     virtual snort::Parameter::Type get_type() const override
     { return value.get_param_type(); }
 
-    virtual std::string data() const override
-    { return value.get_origin_string(); }
-
-    virtual void set_value(const snort::Value& v) override
-    { value = v; }
+    virtual const snort::Value* get_value() const override
+    { return &value; }
 
+    virtual void set_value(const snort::Value& v) override;
     virtual BaseConfigNode* get_node(const std::string& name) override;
 
 private:
     snort::Value value;
+    bool multi_value = false;
+    std::string custom_name;
+};
+
+class ConfigData
+{
+public:
+    ConfigData(const char* file_name);
+
+    void add_config_tree(BaseConfigNode* root)
+    { config_trees.push_back(root); }
+
+    void sort();
+    void clear();
+
+public:
+    std::string file_name;
+    ConfigTrees config_trees;
 };
 
-#endif // CONFIG_TREE_H
+#endif // CONFIG_DATA_H
 
diff --git a/src/dump_config/config_output.cc b/src/dump_config/config_output.cc
new file mode 100644 (file)
index 0000000..27e3084
--- /dev/null
@@ -0,0 +1,34 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// config_output.cc author Serhii Vlasiuk <svlasiuk@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "config_output.h"
+
+#include "config_data.h"
+
+void ConfigOutput::dump_config(ConfigData& config_data)
+{
+    config_data.sort();
+    dump(config_data);
+    config_data.clear();
+}
+
diff --git a/src/dump_config/config_output.h b/src/dump_config/config_output.h
new file mode 100644 (file)
index 0000000..065caf7
--- /dev/null
@@ -0,0 +1,38 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// config_output.h author Serhii Vlasiuk <svlasiuk@cisco.com>
+
+#ifndef CONFIG_OUTPUT_H
+#define CONFIG_OUTPUT_H
+
+class ConfigData;
+
+class ConfigOutput
+{
+public:
+    ConfigOutput() = default;
+    virtual ~ConfigOutput() = default;
+
+    void dump_config(ConfigData&);
+
+private:
+    virtual void dump(const ConfigData&) = 0;
+};
+
+#endif // CONFIG_OUTPUT_H
+
diff --git a/src/dump_config/dev_notes.txt b/src/dump_config/dev_notes.txt
new file mode 100644 (file)
index 0000000..b9bb6e3
--- /dev/null
@@ -0,0 +1,29 @@
+This directory contains classes related to Snort configuration dump.
+
+Snort supports dumping config into a file for JSON and text formats.
+
+* ConfigData
+
+    The ConfigData structure represents the internal config data of a particular Lua file.
+    A unique shell is associated with every base and targeted policy file.
+    Every module in a Lua file is represented by the General Tree.
+    The Tree node can be one of the following types: TreeConfigNode, ValueConfigNode.
+    The TreeConfigNode represents a table or list.
+    The ValueConfigNode represents a config value itself.
+
+* ConfigOutput
+
+    The ConfigOutput class is a base class that encapsulates dumping config into a file.
+    Pure virtual function dump() should be overridden by derived classes for particular
+    output format.
+
+* JsonAllConfigOutput
+
+    The JsonAllConfigOutput class inherits from ConfigOutput and dumps base and targeted
+    policy file in JSON format.
+
+* TextConfigOutput
+
+    The TextConfigOutput class inherits from ConfigOutput and dumps base and targeted
+    policy file in text format.
+
diff --git a/src/dump_config/json_config_output.cc b/src/dump_config/json_config_output.cc
new file mode 100644 (file)
index 0000000..a8f9bae
--- /dev/null
@@ -0,0 +1,104 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// json_config_output.cc author Serhii Vlasiuk <svlasiuk@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "json_config_output.h"
+
+#include "config_data.h"
+
+using namespace snort;
+
+JsonAllConfigOutput::JsonAllConfigOutput() :
+    ConfigOutput(), json(std::cout)
+{ json.open_array(); }
+
+JsonAllConfigOutput::~JsonAllConfigOutput()
+{ json.close_array(); }
+
+void JsonAllConfigOutput::dump(const ConfigData& config_data)
+{
+    json.open();
+    json.put("filename", config_data.file_name);
+    json.open("config");
+
+    for ( const auto config_tree: config_data.config_trees )
+        dump_modules(config_tree);
+
+    json.close();
+    json.close();
+}
+
+void JsonAllConfigOutput::dump_modules(const BaseConfigNode* node)
+{
+    Parameter::Type type = node->get_type();
+    if ( type == Parameter::PT_LIST )
+        json.open_array(node->get_name().c_str());
+    else if ( type == Parameter::PT_TABLE )
+    {
+        std::string name = node->get_name();
+        name.empty() ? json.open() : json.open(name.c_str());
+    }
+    else
+        dump_value(node);
+
+    for ( const auto n : node->get_children() )
+        dump_modules(n);
+
+    if ( type == Parameter::PT_LIST )
+        json.close_array();
+    else if ( type == Parameter::PT_TABLE )
+        json.close();
+}
+
+void JsonAllConfigOutput::dump_value(const BaseConfigNode* node)
+{
+    const Value* value = node->get_value();
+    if ( !value )
+        return;
+
+    switch ( node->get_type() )
+    {
+    case Parameter::PT_BOOL:
+    case Parameter::PT_IMPLIED:
+        value->get_bool() ? json.put_true(node->get_name().c_str()) :
+            json.put_false(node->get_name().c_str());
+        break;
+    case Parameter::PT_INT:
+        json.put(node->get_name().c_str(), value->get_long());
+        break;
+    case Parameter::PT_REAL:
+    {
+        std::string value_str = value->get_as_string();
+        auto pos = value_str.find(".");
+        int precision = 0;
+        if ( pos != std::string::npos )
+            precision = value_str.size() - pos - 1;
+
+        json.put(node->get_name().c_str(), value->get_real(), precision);
+        break;
+    }
+    default:
+        json.put(node->get_name().c_str(), value->get_origin_string());
+        break;
+    }
+}
+
diff --git a/src/dump_config/json_config_output.h b/src/dump_config/json_config_output.h
new file mode 100644 (file)
index 0000000..c0c8c90
--- /dev/null
@@ -0,0 +1,45 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// json_config_output.h author Serhii Vlasiuk <svlasiuk@cisco.com>
+
+#ifndef JSON_CONFIG_OUTPUT_H
+#define JSON_CONFIG_OUTPUT_H
+
+#include "config_output.h"
+#include "helpers/json_stream.h"
+
+class BaseConfigNode;
+
+class JsonAllConfigOutput : public ConfigOutput
+{
+public:
+    JsonAllConfigOutput();
+    ~JsonAllConfigOutput() override;
+
+private:
+    void dump(const ConfigData&) override;
+
+    void dump_modules(const BaseConfigNode* node);
+    void dump_value(const BaseConfigNode* node);
+
+private:
+    JsonStream json;
+};
+
+#endif // JSON_CONFIG_OUTPUT_H
+
similarity index 51%
rename from src/main/config_tree.cc
rename to src/dump_config/text_config_output.cc
index a68986923b751516a1b6af0347269cde38f6d8df..f8607c5f5cca9a2063252f86f9b30b4d8fb9f3d0 100644 (file)
 // with this program; if not, write to the Free Software Foundation, Inc.,
 // 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 //--------------------------------------------------------------------------
-// config_tree.cc author Serhii Vlasiuk <svlasiuk@cisco.com>
+// text_config_output.cc author Serhii Vlasiuk <svlasiuk@cisco.com>
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
+#include "text_config_output.h"
 
-#include "config_tree.h"
+#include <iomanip>
+#include <iostream>
 
-#include <cassert>
-
-#include "log/messages.h"
+#include "config_data.h"
 
 using namespace snort;
 
-void ConfigTextFormat::print(const BaseConfigNode* parent, const std::string& config_name)
+void TextConfigOutput::dump_value(const BaseConfigNode* node, const std::string& config_name)
+{
+    const Value* value = node->get_value();
+    if ( !value )
+        return;
+
+    if ( value->get_as_string().empty() )
+        return;
+
+    switch ( node->get_type() )
+    {
+    case Parameter::PT_BOOL:
+    case Parameter::PT_IMPLIED:
+    {
+        std::string value_str = value->get_bool() ? "true" : "false";
+        std::cout << config_name << "=" << value_str << std::endl;
+        break;
+    }
+    case Parameter::PT_INT:
+        std::cout << config_name << "=" << value->get_long() << std::endl;
+        break;
+    case Parameter::PT_REAL:
+        std::cout << config_name << "=" << value->get_real() << std::endl;
+        break;
+    default:
+        std::cout << config_name << "=" << std::quoted(value->get_origin_string()) << std::endl;
+        break;
+    }
+}
+
+void TextConfigOutput::dump_modules(const BaseConfigNode* parent, const std::string& config_name)
 {
     static char buf[16];
     int list_index = 0;
@@ -51,53 +78,19 @@ void ConfigTextFormat::print(const BaseConfigNode* parent, const std::string& co
             full_config_name += "]";
         }
 
-        print(node, full_config_name);
+        dump_modules(node, full_config_name);
     }
 
-    std::string config_data = parent->data();
-    if ( !config_data.empty() )
-        LogConfig("%s=%s\n", config_name.c_str(), config_data.c_str());
-}
-
-BaseConfigNode::BaseConfigNode(BaseConfigNode* p) :
-    parent(p)
-{}
-
-void BaseConfigNode::add_child_node(BaseConfigNode* node)
-{
-    assert(node);
-    children.push_back(node);
-}
-
-void BaseConfigNode::clear_nodes(BaseConfigNode* node)
-{
-    for ( auto& config_node : node->children )
-        clear_nodes(config_node);
-
-    delete node;
+    dump_value(parent, config_name);
 }
 
-TreeConfigNode::TreeConfigNode(BaseConfigNode* parent_node,
-    const std::string& node_name, const Parameter::Type node_type) :
-        BaseConfigNode(parent_node), name(node_name), type(node_type)
-{}
-
-BaseConfigNode* TreeConfigNode::get_node(const std::string& name)
+void TextConfigOutput::dump(const ConfigData& config_data)
 {
-    for ( auto node : children )
-    {
-        if ( node->get_name() == name )
-            return node;
-    }
-    return nullptr;
-}
-
-ValueConfigNode::ValueConfigNode(BaseConfigNode* parent_node, const Value& val) :
-    BaseConfigNode(parent_node), value(val)
-{}
+    std::string output("consolidated config for ");
+    output += config_data.file_name;
+    std::cout << output << std::endl;
 
-BaseConfigNode* ValueConfigNode::get_node(const std::string& name)
-{
-    return value.is(name.c_str()) and value.has_default() ? this : nullptr;
+    for ( const auto config_tree: config_data.config_trees )
+        dump_modules(config_tree, config_tree->get_name());
 }
 
diff --git a/src/dump_config/text_config_output.h b/src/dump_config/text_config_output.h
new file mode 100644 (file)
index 0000000..92affbb
--- /dev/null
@@ -0,0 +1,42 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// text_config_output.h author Serhii Vlasiuk <svlasiuk@cisco.com>
+
+#ifndef TEXT_CONFIG_OUTPUT_H
+#define TEXT_CONFIG_OUTPUT_H
+
+#include <string>
+
+#include "config_output.h"
+
+class BaseConfigNode;
+
+class TextConfigOutput : public ConfigOutput
+{
+public:
+    TextConfigOutput() = default;
+
+private:
+    void dump(const ConfigData&) override;
+
+    void dump_modules(const BaseConfigNode* parent, const std::string& config_name);
+    void dump_value(const BaseConfigNode* parent, const std::string& config_name);
+};
+
+#endif // TEXT_CONFIG_OUTPUT_H
+
index 82bdfec8936832be61436b31411a9974c74591ee..9f1d9746e642364adf564b42038dcd73bc0dd297 100644 (file)
@@ -199,26 +199,39 @@ bool Value::strtol(long& n, const std::string& tok) const
     return true;
 }
 
-const char* Value::get_as_string()
+std::string Value::get_as_string() const
 {
+    std::string value_str = str;
     switch ( type )
     {
     case VT_BOOL:
-        str = num ? "true" : "false";
+        value_str = num ? "true" : "false";
         break;
     case VT_NUM:
-        ss = new stringstream;
-        *ss << num;
-        str = ss->str();
+    {
+        stringstream tmp;
+        tmp << std::fixed;
+        tmp << num;
+        value_str = tmp.str();
+        auto dot_pos = value_str.find('.');
+        auto pos = value_str.find_last_not_of("0");
+        if ( pos == dot_pos )
+            --pos;
+
+        value_str = value_str.substr(0, pos + 1);
         break;
+    }
     default:
         break;
     }
-    return str.c_str();
+    return value_str;
 }
 
 std::string Value::get_origin_string() const
 {
+    if ( origin_str.empty() )
+        return "";
+
     std::string value;
     std::string token;
 
@@ -230,15 +243,6 @@ std::string Value::get_origin_string() const
     }
     value.erase(value.size() - 1);
 
-    if ( param && param->type != Parameter::PT_BOOL
-        && param->type != Parameter::PT_REAL
-        && param->type != Parameter::PT_INT
-        && param->type != Parameter::PT_IMPLIED )
-    {
-        value.insert(0, "'");
-        value.insert(value.length(), "'");
-    }
-
     return value;
 }
 
@@ -418,19 +422,23 @@ TEST_CASE("token test", "[Value]")
 
 TEST_CASE("get as string", "[Value]")
 {
-    const char* str_val;
     bool bool_val = true;
     double num_val = 6;
 
     Value test_val(bool_val);
-    str_val = (const char *)test_val.get_as_string();
-    REQUIRE(str_val != nullptr);
-    CHECK((strcmp(str_val,"true")==0));
+    CHECK((strcmp(test_val.get_as_string().c_str(),"true") == 0));
 
     test_val.set(num_val);
-    str_val = (const char *)test_val.get_as_string();
-    REQUIRE(str_val != nullptr);
-    CHECK((strcmp(str_val,"6")==0));
+    CHECK((strcmp(test_val.get_as_string().c_str(),"6") == 0));
+
+    test_val.set(1234.2);
+    CHECK((strcmp(test_val.get_as_string().c_str(),"1234.2") == 0));
+
+    test_val.set(123456.0893);
+    CHECK((strcmp(test_val.get_as_string().c_str(),"123456.0893") == 0));
+
+    test_val.set(0.0803);
+    CHECK((strcmp(test_val.get_as_string().c_str(),"0.0803") == 0));
 }
 
 
index df176085762b26916ecc0d9b51657207a7f322d9..aa93044f08976cd1666cf87464fa86342137d328 100644 (file)
@@ -42,10 +42,10 @@ public:
     enum ValueType { VT_BOOL, VT_NUM, VT_STR };
 
     Value(bool b)
-    { set(b); set_origin(b); }
+    { set(b); }
 
     Value(double d)
-    { set(d); set_origin(d); }
+    { set(d); }
 
     Value(const char* s)
     { set(s); set_origin(s); }
@@ -85,20 +85,9 @@ public:
     void set(bool b)
     { type = VT_BOOL; num = b ? 1 : 0; str.clear(); }
 
-    void set_origin(bool val)
-    { origin_str = val ? "true" : "false"; }
-
     void set(double d)
     { type = VT_NUM; num = d; str.clear(); }
 
-    void set_origin(double val)
-    {
-        auto temp = new std::stringstream;
-        *temp << val;
-        origin_str = temp->str();
-        delete temp;
-    }
-
     void set(long n)
     { set((double)n); }
 
@@ -168,7 +157,7 @@ public:
     const char* get_string() const
     { return str.c_str(); }
 
-    const char* get_as_string();
+    std::string get_as_string() const;
     Parameter::Type get_param_type() const;
     std::string get_origin_string() const;
 
index e59c278aed0f7ad7b896b2060eac392971adfcbf..06c8550adc0749801a36f685567de9d641b02bb8 100644 (file)
@@ -153,6 +153,14 @@ TEST_CASE("basic", "[json_stream]")
         CHECK(ss.str() == x);
     }
 
+    SECTION("backslash")
+    {
+        const char* s = R"-(content:\test\;)-";
+        const char* x = R"-("content:\\test\\;")-";
+        js.put(nullptr, s);
+        CHECK(ss.str() == x);
+    }
+
     SECTION("embedded quotes")
     {
         const char* s = R"-(content:"foo";)-";
@@ -161,6 +169,14 @@ TEST_CASE("basic", "[json_stream]")
         CHECK(ss.str() == x);
     }
 
+    SECTION("special characters")
+    {
+        const char* s = R"-(content: / " \ $ # ! @ % ^ & * ' \b\f\t\r\n . )-";
+        const char* x = R"-("content: / \" \\ $ # ! @ % ^ & * ' \\b\\f\\t\\r\\n . ")-";
+        js.put(nullptr, s);
+        CHECK(ss.str() == x);
+    }
+
     SECTION("null list")
     {
         js.put("i");
index 7c308b38b2faf8646cc5e10d1d61badab1d186fc..e805d987fa1257e909c5ee397b266e9f62d5a8b6 100644 (file)
@@ -235,16 +235,6 @@ void LogMessage(FILE* fh, const char* format,...)
     va_end(ap);
 }
 
-void LogConfig(const char* format, ...)
-{
-    va_list ap;
-    va_start(ap, format);
-
-    WriteLogMessage(stdout, false, format, ap);
-
-    va_end(ap);
-}
-
 /*
  * Function: WarningMessage(const char *, ...)
  *
index d61e2e87009caf438e69c29343f296949a2308d5..a2ab4d28290e6484adf159fb45ccdbeb7b1d0ea7 100644 (file)
@@ -61,7 +61,6 @@ SO_PUBLIC void ParseError(const char*, ...) __attribute__((format (printf, 1, 2)
 SO_PUBLIC void ReloadError(const char*, ...) __attribute__((format (printf, 1, 2)));
 [[noreturn]] SO_PUBLIC void ParseAbort(const char*, ...) __attribute__((format (printf, 1, 2)));
 
-SO_PUBLIC void LogConfig(const char*, ...) __attribute__((format (printf, 1, 2)));
 SO_PUBLIC void LogMessage(const char*, ...) __attribute__((format (printf, 1, 2)));
 SO_PUBLIC void LogMessage(FILE*, const char*, ...) __attribute__((format (printf, 2, 3)));
 SO_PUBLIC void WarningMessage(const char*, ...) __attribute__((format (printf, 1, 2)));
index 42db721825a1ae2642f3d394bf37e240b08f8d26..fcb8696b6f3688188e41c01daabebc0e9828c7a8 100644 (file)
@@ -822,9 +822,6 @@ static bool set_mode()
         return false;
     }
 
-    if ( sc->dump_config() )
-        return false;
-
     if ( just_validate(sc) )
     {
         LogMessage("\nSnort successfully validated the configuration (with %u warnings).\n",
index 954e543a96dab0a3cbd0f4d2ca7393fb505c6beb..502e5710dc6951540aaa09681550ac78d3247e69 100644 (file)
@@ -21,8 +21,6 @@ add_library (main OBJECT
     analyzer.h
     analyzer_command.cc
     build.h
-    config_tree.cc
-    config_tree.h
     help.cc
     help.h
     modules.cc
index c4cae9e04c760e875992f3df9b83a559d69a6bf5..c4a535fd2fa2607d5eda609da08f8655b2c842a5 100644 (file)
@@ -63,11 +63,3 @@ information and management.  Currently it is being used as a cross-platform
 mechanism for managing CPU affinity of threads, but it will be used in the
 future for NUMA (non-uniform memory access) awareness among other things.
 
-Consolidated Config output
-
-The snort config parsed is stored as an internal tree structure within the Shell.
-A unique shell is associated with every base and targeted policy file.
-Every module in a lua file is represented by the General Tree.
-The Tree node can be one of the following types: TreeConfigNode, ValueConfigNode.
-The TreeConfigNode represents a table or list.
-The ValueConfigNode represents a config value itself.
index c8b1c9bcd260f49bc93b3407b13a878cb041d660..e757361f17be1fadeaa93beee5a4c8a13324d1cc 100644 (file)
@@ -28,7 +28,7 @@
 #include <cassert>
 #include <stdexcept>
 
-#include "config_tree.h"
+#include "dump_config/config_output.h"
 #include "log/messages.h"
 #include "lua/lua.h"
 #include "main/policy.h"
@@ -47,6 +47,9 @@ using namespace std;
 
 string Shell::fatal;
 std::stack<Shell*> Shell::current_shells;
+ConfigOutput* Shell::s_config_output = nullptr;
+BaseConfigNode* Shell::s_current_node = nullptr;
+bool Shell::s_close_table = true;
 
 // FIXIT-M Shell::panic() works on Linux but on OSX we can't throw from lua
 // to C++.  unprotected lua calls could be wrapped in a pcall to ensure lua
@@ -65,6 +68,15 @@ Shell* Shell::get_current_shell()
     return nullptr;
 }
 
+void Shell::set_config_output(ConfigOutput* output)
+{ s_config_output = output; }
+
+void Shell::clear_config_output()
+{
+    s_config_output = nullptr;
+    s_current_node = nullptr;
+}
+
 bool Shell::is_whitelisted(const std::string& key)
 {
     Shell* sh = Shell::get_current_shell();
@@ -112,6 +124,12 @@ void Shell::config_open_table(bool is_root_node, bool is_list, int idx,
         if ( p )
             node_type = p->type;
 
+        if ( node_type == Parameter::PT_MULTI )
+        {
+            s_close_table = false;
+            return;
+        }
+
         if ( node_type == Parameter::PT_TABLE )
             update_current_config_node(table_name);
         else
@@ -126,18 +144,16 @@ void Shell::config_open_table(bool is_root_node, bool is_list, int idx,
 
 void Shell::add_config_child_node(const std::string& node_name, snort::Parameter::Type type)
 {
-    Shell* sh = Shell::get_current_shell();
-
-    if ( !sh )
+    if ( !s_current_node )
         return;
 
     std::string name;
-    if ( sh->s_current_node->get_name() != node_name )
+    if ( s_current_node->get_name() != node_name )
         name = node_name;
 
-    auto new_node = new TreeConfigNode(sh->s_current_node, name, type);
-    sh->s_current_node->add_child_node(new_node);
-    sh->s_current_node = new_node;
+    auto new_node = new TreeConfigNode(s_current_node, name, type);
+    s_current_node->add_child_node(new_node);
+    s_current_node = new_node;
 }
 
 void Shell::add_config_root_node(const std::string& root_name, snort::Parameter::Type node_type)
@@ -148,74 +164,86 @@ void Shell::add_config_root_node(const std::string& root_name, snort::Parameter:
         return;
 
     sh->s_current_node = new TreeConfigNode(nullptr, root_name, node_type);
-    sh->config_trees.push_back(sh->s_current_node);
+    sh->config_data.add_config_tree(sh->s_current_node);
 }
 
 void Shell::update_current_config_node(const std::string& node_name)
 {
-    Shell* sh = Shell::get_current_shell();
-
-    if ( !sh )
-        return;
-
-    if ( !sh->s_current_node )
+    if ( !s_current_node )
         return;
 
     // node has been added during setting default options
     if ( !node_name.empty() )
-        sh->s_current_node = sh->s_current_node->get_node(node_name);
-    else if ( sh->s_current_node->get_parent_node() and
-        sh->s_current_node->get_type() == Parameter::PT_TABLE and
-        !sh->s_current_node->get_name().empty() )
-            sh->s_current_node = sh->s_current_node->get_parent_node();
+        s_current_node = s_current_node->get_node(node_name);
+    else if ( s_current_node->get_parent_node() and
+        s_current_node->get_type() == Parameter::PT_TABLE and
+        !s_current_node->get_name().empty() )
+            s_current_node = s_current_node->get_parent_node();
 
-    assert(sh->s_current_node);
+    assert(s_current_node);
 }
 
 void Shell::config_close_table()
 {
-    Shell* sh = Shell::get_current_shell();
-
-    if ( !sh )
+    if ( !s_close_table )
+    {
+        s_close_table = true;
         return;
+    }
 
-    if ( !sh->s_current_node )
+    if ( !s_current_node )
         return;
 
-    sh->s_current_node = sh->s_current_node->get_parent_node();
+    s_current_node = s_current_node->get_parent_node();
 }
 
-void Shell::set_config_value(const snort::Value& value)
+void Shell::set_config_value(const std::string& fqn, const snort::Value& value)
 {
-    Shell* sh = Shell::get_current_shell();
-
-    if ( !sh )
-        return;
-
-    if ( !sh->s_current_node )
+    if ( !s_current_node )
         return;
 
-    // lua interpreter does not call open_table for simple list items like (string)
-    // we have to add tree node for this item too
-    if ( sh->s_current_node->get_type() == Parameter::PT_LIST )
+    // lua interpreter does not call open_table for simple list items like (string) or
+    // special rule_state list items
+    // We have to add tree node for this item too
+    if ( s_current_node->get_type() == Parameter::PT_LIST )
     {
-        add_config_child_node("", Parameter::PT_TABLE);
-        sh->s_current_node->add_child_node(new ValueConfigNode(sh->s_current_node, value));
-        sh->s_current_node = sh->s_current_node->get_parent_node();
+        auto node = s_current_node->get_node("");
+        if ( !node || s_current_node->get_name().find(":") == std::string::npos )
+        {
+            node = new TreeConfigNode(s_current_node, "", Parameter::PT_TABLE);
+            s_current_node->add_child_node(node);
+        }
+
+        node->add_child_node(new ValueConfigNode(node, value));
 
         return;
     }
-    
+
     BaseConfigNode* child_node = nullptr;
-    for ( auto node : sh->s_current_node->get_children() )
+
+    std::string custom_name;
+    if ( strchr(value.get_name(), '$') )
+        custom_name = fqn.substr(fqn.find_last_of(".") + 1);
+
+    for ( auto node : s_current_node->get_children() )
     {
-        child_node = node->get_node(value.get_name());
+        if ( (node->get_type() == Parameter::PT_MULTI) and (node->get_name() == value.get_name()) )
+        {
+            child_node = node;
+            break;
+        }
+
+        if ( !custom_name.empty() )
+            child_node = node->get_node(custom_name);
+        else
+            child_node = node->get_node(value.get_name());
+
         if ( child_node )
             break;
     }
 
     if ( !child_node )
-        sh->s_current_node->add_child_node(new ValueConfigNode(sh->s_current_node, value));
+        s_current_node->add_child_node(new ValueConfigNode(s_current_node, value, custom_name));
     else
         child_node->set_value(value);
 }
@@ -278,7 +306,8 @@ static void load_string(lua_State* L, const char* s)
 // public methods
 //-------------------------------------------------------------------------
 
-Shell::Shell(const char* s, bool load_defaults)
+Shell::Shell(const char* s, bool load_defaults) :
+    config_data(s)
 {
     // FIXIT-M should wrap in Lua::State
     lua = luaL_newstate();
@@ -314,6 +343,7 @@ Shell::~Shell()
 void Shell::set_file(const char* s)
 {
     file = s;
+    config_data.file_name = file;
 }
 
 void Shell::set_overrides(const char* s)
@@ -383,12 +413,9 @@ bool Shell::configure(SnortConfig* sc, bool is_fatal, bool is_root)
 
     clear_whitelist();
 
-    if ( SnortConfig::get_conf()->dump_config() )
-    {
-        sort_config();
-        print_config_text();
-        clear_config_tree();
-    }
+    auto config_output = Shell::get_current_shell()->s_config_output;
+    if ( config_output )
+        config_output->dump_config(config_data);
 
     current_shells.pop();
 
@@ -459,29 +486,6 @@ static void print_list(const Shell::Whitelist& wlist, const std::string& msg)
 //-------------------------------------------------------------------------
 // private methods
 //-------------------------------------------------------------------------
-void Shell::sort_config()
-{
-    config_trees.sort([](const BaseConfigNode* l, const BaseConfigNode* r)
-        { return l->get_name() < r->get_name(); });
-}
-
-void Shell::print_config_text() const
-{
-    std::string output("consolidated config for ");
-    output += file;
-    LogConfig("%s\n", output.c_str());
-
-    for ( const auto config_tree: config_trees )
-        ConfigTextFormat::print(config_tree, config_tree->get_name());
-}
-
-void Shell::clear_config_tree()
-{
-    for ( auto config_tree: config_trees )
-        BaseConfigNode::clear_nodes(config_tree);
-
-    config_trees.clear();
-}
 
 void Shell::print_whitelist() const
 {
index ef6883d983cb6e659279d30f0d019f7e2db982d3..e72e056a97e6d8a74374292d5836b84f8a975bbf 100644 (file)
 #include <stack>
 #include <string>
 
+#include "dump_config/config_data.h"
 #include "framework/parameter.h"
 
 struct lua_State;
 
 class BaseConfigNode;
+class ConfigOutput;
 
 namespace snort
 {
@@ -70,10 +72,12 @@ public:
 
     static void config_open_table(bool is_root_node, bool is_list, int idx,
         const std::string& table_name, const snort::Parameter* p);
-    static void set_config_value(const snort::Value& value);
+    static void set_config_value(const std::string& fqn, const snort::Value& value);
     static void add_config_child_node(const std::string& node_name, snort::Parameter::Type type);
     static void update_current_config_node(const std::string& node_name = "");
     static void config_close_table();
+    static void set_config_output(ConfigOutput* config_output);
+    static void clear_config_output();
 
 private:
     static void add_config_root_node(const std::string& root_name, snort::Parameter::Type type);
@@ -85,6 +89,9 @@ private:
 private:
     static std::string fatal;
     static std::stack<Shell*> current_shells;
+    static ConfigOutput* s_config_output;
+    static BaseConfigNode* s_current_node;
+    static bool s_close_table;
 
 private:
     void clear_whitelist()
@@ -106,10 +113,6 @@ private:
     void print_whitelist() const;
     void whitelist_update(const char* keyword, bool is_prefix);
 
-    void sort_config();
-    void print_config_text() const;
-    void clear_config_tree();
-
 private:
     bool loaded;
     bool bootstrapped = false;
@@ -120,8 +123,7 @@ private:
     Whitelist whitelist;
     Whitelist internal_whitelist;
     Whitelist whitelist_prefixes;
-    std::list<BaseConfigNode*> config_trees;
-    BaseConfigNode* s_current_node = nullptr;
+    ConfigData config_data;
 };
 
 #endif
index 28e665b4395c99da1722bcb96fe9051c14647c3e..432cbf5c510b29ceb44fe1848eba704f8808e331 100644 (file)
@@ -426,7 +426,7 @@ void Snort::cleanup()
     SFDAQ::term();
     FileService::close();
 
-    if ( !SnortConfig::get_conf()->validation_mode() )  // FIXIT-M ideally the check is in one place
+    if ( !SnortConfig::get_conf()->test_mode() )  // FIXIT-M ideally the check is in one place
         PrintStatistics();
 
     CloseLogger();
index 46e9abccbe3e086a7426460eae8b9497f7af67eb..b22540e5975fa1d867644ca90aaf178c867bf2e4 100644 (file)
@@ -31,6 +31,8 @@
 #include "detection/detection_engine.h"
 #include "detection/fp_config.h"
 #include "detection/fp_create.h"
+#include "dump_config/json_config_output.h"
+#include "dump_config/text_config_output.h"
 #include "filters/detection_filter.h"
 #include "filters/rate_filter.h"
 #include "filters/sfrf.h"
@@ -946,6 +948,25 @@ bool SnortConfig::get_default_rule_state() const
     return true;
 }
 
+ConfigOutput* SnortConfig::create_config_output() const
+{
+    ConfigOutput* output = nullptr;
+
+    switch (dump_config_type)
+    {
+    case DUMP_CONFIG_JSON_ALL:
+        output = new JsonAllConfigOutput();
+        break;
+    case DUMP_CONFIG_TEXT:
+        output = new TextConfigOutput();
+        break;
+    default:
+        break;
+    }
+
+    return output;
+}
+
 bool SnortConfig::tunnel_bypass_enabled(uint16_t proto) const
 {
     return !((tunnel_mask & proto) or SFDAQ::get_tunnel_bypass(proto));
index 30a7fbbec2380424c78babcb503a9db871c146e9..f7d89d5b7730fb7e3bbd0e069ccf3f03f8f7beef 100644 (file)
@@ -81,7 +81,6 @@ enum RunFlag
     RUN_FLAG__IP_FRAGS_ONLY       = 0x08000000,
 
     RUN_FLAG__DUMP_RULE_STATE     = 0x10000000,
-    RUN_FLAG__DUMP_CONFIG         = 0x20000000,
 };
 
 enum OutputFlag
@@ -126,6 +125,14 @@ enum TunnelFlags
     TUNNEL_VXLAN  = 0x100
 };
 
+enum DumpConfigType
+{
+    DUMP_CONFIG_NONE = 0,
+    DUMP_CONFIG_JSON_ALL,
+    DUMP_CONFIG_TEXT
+};
+
+class ConfigOutput;
 class FastPatternConfig;
 class RuleStateMap;
 class TraceConfig;
@@ -424,6 +431,8 @@ public:
     bool cloned = false;
     Plugins* plugins = nullptr;
     SoRules* so_rules = nullptr;
+
+    DumpConfigType dump_config_type = DUMP_CONFIG_NONE;
 private:
     bool active_enabled = false;
     std::list<ReloadResourceTuner*> reload_tuners;
@@ -501,8 +510,8 @@ public:
     { return address_anomaly_check_enabled; }
 
     // mode related
-    bool dump_config() const
-    { return run_flags & RUN_FLAG__DUMP_CONFIG; }
+    bool dump_config_mode() const
+    { return dump_config_type > DUMP_CONFIG_NONE; }
 
     bool dump_msg_map() const
     { return run_flags & RUN_FLAG__DUMP_MSG_MAP; }
@@ -522,14 +531,11 @@ public:
     bool test_mode() const
     { return run_flags & RUN_FLAG__TEST; }
 
-    bool validation_mode() const
-    { return test_mode() or dump_config(); }
-
     bool mem_check() const
-    { return (run_flags & RUN_FLAG__MEM_CHECK) and !dump_config(); }
+    { return run_flags & RUN_FLAG__MEM_CHECK; }
 
     bool daemon_mode() const
-    { return (run_flags & RUN_FLAG__DAEMON) and !dump_config(); }
+    { return run_flags & RUN_FLAG__DAEMON; }
 
     bool read_mode() const
     { return run_flags & RUN_FLAG__READ; }
@@ -701,6 +707,8 @@ public:
 
     bool get_default_rule_state() const;
 
+    ConfigOutput* create_config_output() const;
+
     SO_PUBLIC bool tunnel_bypass_enabled(uint16_t proto) const;
 
     // FIXIT-L snort_conf needed for static hash before initialized
index dcbff4e200b77930b375378eeed6e28415f9d8a2..aa3adb786aeee5bf5a3611afb10c4fe8c9caece5 100644 (file)
@@ -347,6 +347,9 @@ static const Parameter s_params[] =
     { "--dump-builtin-rules", Parameter::PT_STRING, "(optional)", nullptr,
       "[<module prefix>] output stub rules for selected modules" },
 
+    { "--dump-config", Parameter::PT_SELECT, "all", nullptr,
+      "dump config in json format" },
+
     { "--dump-config-text", Parameter::PT_IMPLIED, nullptr, nullptr,
       "dump config in text format" },
 
@@ -871,10 +874,19 @@ bool SnortModule::set(const char*, Value& v, SnortConfig* sc)
     else if ( v.is("--dump-builtin-rules") )
         dump_builtin_rules(sc, v.get_string());
 
+    else if ( v.is("--dump-config") )
+    {
+        sc->set_quiet(true);
+        sc->run_flags |= RUN_FLAG__TEST;
+        if ( v.get_as_string() == "all" )
+            sc->dump_config_type = DUMP_CONFIG_JSON_ALL;
+    }
+
     else if ( v.is("--dump-config-text") )
     {
-      sc->run_flags |= RUN_FLAG__DUMP_CONFIG;
-      sc->set_quiet(true);
+        sc->set_quiet(true);
+        sc->run_flags |= RUN_FLAG__TEST;
+        sc->dump_config_type = DUMP_CONFIG_TEXT;
     }
 
     else if ( v.is("--dump-dynamic-rules") )
index 531844c1859e8bbb08ed9d06374547394b0be447..5276b1b08581bd3bcb812713f8aa4c791603ab72 100644 (file)
@@ -455,8 +455,8 @@ static bool set_var(const char* fqn, Value& v)
 
 static bool set_param(Module* mod, const char* fqn, Value& val)
 {
-    if ( s_config->dump_config() )
-        Shell::set_config_value(val);
+    if ( s_config->dump_config_mode() )
+        Shell::set_config_value(fqn, val);
 
     if ( !mod->verified_set(fqn, val, s_config) )
     {
@@ -595,7 +595,7 @@ static bool begin(Module* m, const Parameter* p, const char* s, int idx, int dep
             {
                 const Parameter* table_item_params = reinterpret_cast<const Parameter*>(p->range);
 
-                if ( s_config->dump_config() )
+                if ( s_config->dump_config_mode() )
                     Shell::add_config_child_node(get_sub_table(fqn), p->type);
 
                 if ( !begin(m, table_item_params, fqn.c_str(), idx, depth+1) )
@@ -634,7 +634,7 @@ static bool begin(Module* m, const Parameter* p, const char* s, int idx, int dep
         }
         ++p;
     }
-    if ( s_config->dump_config() )
+    if ( s_config->dump_config_mode() )
         Shell::update_current_config_node();
     return true;
 }
@@ -764,7 +764,7 @@ SO_PUBLIC bool open_table(const char* s, int idx)
         s_current = unique_key;
     }
 
-    if ( s_config->dump_config() )
+    if ( s_config->dump_config_mode() )
     {
         std::string table_name = get_sub_table(s);
         bool is_top_level = false;
@@ -817,7 +817,7 @@ SO_PUBLIC void close_table(const char* s, int idx)
         s_type.clear();
     }
 
-    if ( s_config->dump_config() )
+    if ( s_config->dump_config_mode() )
         Shell::config_close_table();
 }
 
index 5c1d4e5bf3341dea9df7dcbc6d7e6d9af6179fde..ce857a4f906012dc6f463d5d58c9e6f9ea49b87b 100644 (file)
@@ -33,6 +33,7 @@
 #include "detection/fp_config.h"
 #include "detection/rules.h"
 #include "detection/sfrim.h"
+#include "dump_config/config_output.h"
 #include "filters/detection_filter.h"
 #include "filters/rate_filter.h"
 #include "filters/sfthreshold.h"
@@ -320,6 +321,7 @@ SnortConfig* ParseSnortConf(const SnortConfig* boot_conf, const char* fname, boo
     sc->output_flags = boot_conf->output_flags;
     sc->logging_flags = boot_conf->logging_flags;
     sc->tweaks = boot_conf->tweaks;
+    sc->dump_config_type = boot_conf->dump_config_type;
 
     VarNode* tmp = boot_conf->var_list;
 
@@ -357,6 +359,9 @@ SnortConfig* ParseSnortConf(const SnortConfig* boot_conf, const char* fname, boo
         sh->set_file(fname);
     }
 
+    bool parse_file_failed = false;
+    auto output = SnortConfig::get_conf()->create_config_output();
+    Shell::set_config_output(output);
     for ( unsigned i = 0; true; i++ )
     {
         sh = sc->policy_map->get_shell(i);
@@ -367,10 +372,18 @@ SnortConfig* ParseSnortConf(const SnortConfig* boot_conf, const char* fname, boo
         set_policies(sc, sh);
 
         if (!parse_file(sc, sh, is_fatal, (i == 0)))
-            return sc;
+        {
+            parse_file_failed = true;
+            break;
+        }
     }
 
-    set_default_policy(sc);
+    delete output;
+    Shell::clear_config_output();
+
+    if ( !parse_file_failed )
+        set_default_policy(sc);
+
     return sc;
 }
 
index 6d4a0353a5a7d09f450106647ad6a0b3a808c9fd..c99f112a515a4a4e8d3e1f69cf7e8131d0ea4370 100644 (file)
@@ -178,20 +178,20 @@ bool TraceModule::end(const char* fqn, int, SnortConfig* sc)
     {
         assert(trace_parser);
 
-        if ( sc->dump_config() )
+        if ( sc->dump_config_mode() )
             trace_parser->clear_traces();
         else
         {
             switch ( log_output_type )
             {
-                case OUTPUT_TYPE_STDOUT:
-                    trace_parser->get_trace_config()->logger_factory = new StdoutLoggerFactory();
-                    break;
-                case OUTPUT_TYPE_SYSLOG:
-                    trace_parser->get_trace_config()->logger_factory = new SyslogLoggerFactory();
-                    break;
-                default:
-                    break;
+            case OUTPUT_TYPE_STDOUT:
+                trace_parser->get_trace_config()->logger_factory = new StdoutLoggerFactory();
+                break;
+            case OUTPUT_TYPE_SYSLOG:
+                trace_parser->get_trace_config()->logger_factory = new SyslogLoggerFactory();
+                break;
+            default:
+                break;
             }
 
             // "output=syslog" config override case
index cc9341485a056189be4a3f8ffefefb207cd30e62..5b1879c880e642c45ffe54a395e0213ace37fa29 100644 (file)
@@ -220,7 +220,7 @@ static int set(lua_State* L)
                          !trace_parser.set_traces(module_name, val) )
                     {
                         LogMessage("== invalid trace value is provided: %s.%s.%s = %s\n",
-                            root_element_key, module_name, val_name, val.get_as_string());
+                            root_element_key, module_name, val_name, val.get_as_string().c_str());
 
                         parse_err = true;
                     }
@@ -259,7 +259,7 @@ static int set(lua_State* L)
                      !trace_parser.set_constraints(val) )
                 {
                     LogMessage("== invalid constraints value is provided: %s.%s = %s\n",
-                        root_element_key, val_name, val.get_as_string());
+                        root_element_key, val_name, val.get_as_string().c_str());
 
                     parse_err = true;
                 }