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