1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 //
4 // This program is free software; you can redistribute it and/or modify it
5 // under the terms of the GNU General Public License Version 2 as published
6 // by the Free Software Foundation.  You may not use, modify or distribute
7 // this program under any other version of the GNU General Public License.
8 //
9 // This program is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License along
15 // with this program; if not, write to the Free Software Foundation, Inc.,
16 // 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 //--------------------------------------------------------------------------
18 // kws_file.cc author Josh Rosenbaum <jrosenba@cisco.com>
19 
20 #include <sstream>
21 #include <unordered_set>
22 #include <vector>
23 
24 #include "conversion_state.h"
25 #include "helpers/converter.h"
26 #include "helpers/s2l_util.h"
27 
28 namespace keywords
29 {
30 namespace
31 {
32 
33 class File : public ConversionState
34 {
35 public:
File(Converter & c)36     File(Converter& c) : ConversionState(c) { }
37     bool convert(std::istringstream& data) override;
38 
39 private:
40 };
41 } // namespace
42 
43 static std::unordered_set<std::string> string_keys = {
44     "category",
45     "msg",
46     "type",
47     "ver",
48     "group",
49 };
50 
51 static std::unordered_set<std::string> int_keys = {
52     "id",
53     "rev",
54 };
55 
56 static std::unordered_set<std::string> content_keys = {
57     "content",
58     "offset",
59 };
60 
convert(std::istringstream & data_stream)61 bool File::convert(std::istringstream& data_stream)
62 {
63     std::string key_value_pair;
64     bool retval = true;
65     bool success = true;
66     bool content_seen = false;
67     std::vector<std::pair<std::string,std::string>> magics;
68 
69     table_api.open_table("file_magic");
70     table_api.open_table(true);
71 
72     while(util::get_string(data_stream, key_value_pair, ";"))
73     {
74         std::istringstream arg_stream(key_value_pair);
75         util::trim(key_value_pair);
76 
77         size_t pos = key_value_pair.find_first_of(':');
78         if(pos == std::string::npos)
79         {
80             data_api.failed_conversion(arg_stream, key_value_pair);
81             retval = false;
82             continue;
83         }
84 
85         std::string key = key_value_pair.substr(0, pos);
86         std::string value = key_value_pair.substr(pos+1);
87 
88         if(key.empty() or value.empty())
89         {
90             table_api.add_comment("Empty field before or after ':' in: " + key_value_pair);
91             data_api.failed_conversion(arg_stream, key_value_pair);
92             retval = false;
93             continue;
94         }
95 
96         util::trim_quotes(value);
97 
98         if(string_keys.find(key) != string_keys.end())
99         {
100             if(key == "ver")
101             {
102                 table_api.add_diff_option_comment("ver", "version");
103                 key = "version";
104             }
105             table_api.add_option(key, value);
106         }
107         else if(int_keys.find(key) != int_keys.end())
108         {
109             std::istringstream stream_value(value);
110             success = parse_int_option(key, stream_value, false);
111         }
112         else if(content_keys.find(key) != content_keys.end())
113         {
114             // Save the content/offset pairs for later so they can be
115             // added to a sub-table. Content must always come before offset
116             // so start a new pair when content is seen.
117 
118             if(key == "content")
119                 content_seen = true;
120 
121             if(key == "offset" and not content_seen)
122             {
123                 success = false;
124                 table_api.add_comment("Offset came before content field: " + key_value_pair);
125             }
126             else
127             {
128                 if(key == "offset")
129                     content_seen = false;   // Prevent two offsets in a row
130                 std::pair<std::string, std::string> pair(key, value);
131                 magics.push_back(pair);
132             }
133 
134         }
135         else
136         {
137             table_api.add_comment("Unknown rule field: " + key_value_pair);
138             success = false;
139         }
140 
141         if (!success)
142         {
143             data_api.failed_conversion(arg_stream, key_value_pair);
144             retval = false;
145         }
146     }
147 
148     if(magics.size() > 0)
149     {
150         bool sub_table_open = false;
151 
152         table_api.open_table("magic");
153 
154         for(const std::pair<std::string, std::string>& key_value: magics)
155         {
156             if(key_value.first == "offset")
157             {
158                 std::istringstream stream_value(key_value.second);
159                 success = parse_int_option(key_value.first, stream_value, false);
160             }
161             else if(key_value.first == "content")
162             {
163                 // Create a new sub-table each time we see content.
164                 if(sub_table_open)
165                     table_api.close_table();
166                 table_api.open_table();
167                 table_api.add_option(key_value.first, key_value.second);
168                 sub_table_open = true;
169             }
170 
171             if (!success)
172             {
173                 std::istringstream stream(key_value.first + ":" + key_value.second);
174                 data_api.failed_conversion(stream, stream.str());
175                 retval = false;
176             }
177         }
178 
179         if(sub_table_open)
180             table_api.close_table();
181         table_api.close_table();
182     }
183 
184     table_api.close_table();
185     table_api.close_table();
186 
187     return retval;
188 }
189 
190 /**************************
191  *******  A P I ***********
192  **************************/
193 
ctor(Converter & c)194 static ConversionState* ctor(Converter& c)
195 { return new File(c); }
196 
197 static const ConvertMap keyword_file =
198 {
199     "file",
200     ctor,
201 };
202 
203 const ConvertMap* file_map = &keyword_file;
204 } // namespace keywords
205 
206