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 // dt_table_api.cc author Josh Rosenbaum <jrosenba@cisco.com>
19
20 #include <iostream>
21 #include <sstream>
22 #include <cstring>
23
24 #include "data/dt_table_api.h"
25 #include "data/dt_data.h"
26 #include "helpers/s2l_util.h"
27 #include "data/data_types/dt_table.h"
28 #include "data/data_types/dt_var.h"
29 #include "data/data_types/dt_comment.h"
30 #include "data/data_types/dt_rule.h"
31 #include "data/data_types/dt_include.h"
32
~TableApi()33 TableApi::~TableApi()
34 {
35 for (auto t : tables)
36 delete t;
37 }
38
should_delegate() const39 bool TableApi::should_delegate() const
40 { return delegating; }
41
should_delegate(const std::string & table_name) const42 bool TableApi::should_delegate(const std::string& table_name) const
43 {
44 if ( delegating )
45 return true;
46
47 if ( delegate == this )
48 return false;
49
50 auto d = delegations.find(table_name);
51 if ( d != delegations.end() )
52 return d->second;
53
54 return false;
55 }
56
reset_state()57 void TableApi::reset_state()
58 {
59 // DO NOT RESET DELEGATE STATE. IT HAS ITS OWN LIFECYCLE.
60 std::stack<Table*> empty;
61 open_tables.swap(empty);
62 std::stack<unsigned> empty_two;
63 top_level_tables.swap(empty_two);
64 curr_data_bad = false;
65 }
66
open_top_level_table(const std::string & table_name,bool one_line)67 void TableApi::open_top_level_table(const std::string& table_name, bool one_line)
68 {
69 if ( should_delegate(table_name) )
70 {
71 delegate->open_top_level_table(table_name, one_line);
72 delegating++;
73 return;
74 }
75
76 Table* t = util::find_table(tables, table_name);
77 bool existed = (t != nullptr);
78
79 if ( !existed )
80 {
81 t = new Table(table_name, 0);
82 tables.push_back(t);
83 }
84
85 t->set_one_line(one_line);
86 open_tables.push(t);
87
88 // ignore the initial table
89 if ( open_tables.size() > 1 )
90 top_level_tables.push(open_tables.size());
91
92 if ( !existed )
93 {
94 auto p = pending.find(table_name);
95 if ( p != pending.end() )
96 {
97 auto& q = p->second;
98 while ( q.size() )
99 {
100 q.front()(*this);
101 q.pop();
102 }
103
104 pending.erase(p);
105 }
106 }
107 }
108
open_table(const std::string & table_name,bool one_line)109 void TableApi::open_table(const std::string& table_name, bool one_line)
110 {
111 if ( should_delegate(table_name) )
112 {
113 delegate->open_table(table_name, one_line);
114 delegating++;
115 return;
116 }
117
118 // if no open tables, create a top-level table
119 if (!open_tables.empty())
120 {
121 Table* t = open_tables.top()->open_table(table_name);
122 t->set_one_line(one_line);
123 open_tables.push(t);
124 }
125 else
126 open_top_level_table(table_name, one_line);
127 }
128
open_table(bool one_line)129 void TableApi::open_table(bool one_line)
130 {
131 if ( should_delegate() )
132 {
133 delegate->open_table(one_line);
134 delegating++;
135 return;
136 }
137
138 // if no open tables, create a top-level table
139 if (open_tables.empty())
140 {
141 DataApi::developer_error("A nameless table must be nested inside a table!!");
142 }
143 else
144 {
145 Table* t = open_tables.top()->open_table();
146 t->set_one_line(one_line);
147 open_tables.push(t);
148 }
149 }
150
open_associative_table(const char * name,const char * key)151 void TableApi::open_associative_table(const char* name, const char* key)
152 {
153 if ( should_delegate(name) )
154 {
155 delegate->open_associative_table(name, key);
156 delegating++;
157 return;
158 }
159
160 Table* t = new Table(name, key, 0);
161 tables.push_back(t);
162 open_tables.push(t);
163 }
164
close_table()165 void TableApi::close_table()
166 {
167 if ( should_delegate() )
168 {
169 delegate->close_table();
170 delegating--;
171 return;
172 }
173
174 if (open_tables.empty())
175 DataApi::developer_error("No open tables to close!!");
176 else
177 {
178 if ( !top_level_tables.empty() )
179 if ( open_tables.size() == top_level_tables.top() )
180 top_level_tables.pop();
181
182 open_tables.pop();
183 }
184 }
185
186 template<typename T>
do_add_option(const std::string & opt_name,T val,const std::string & s_val)187 bool TableApi::do_add_option(const std::string& opt_name, T val, const std::string& s_val)
188 {
189 if ( should_delegate() )
190 return delegate->add_option(opt_name, val);
191
192 if (open_tables.empty())
193 {
194 DataApi::developer_error("Must open table before adding an option!!: " +
195 opt_name + " = " + s_val);
196 return false;
197 }
198
199 Table* t = open_tables.top();
200 t->add_option(opt_name, val);
201 return true;
202 }
203
add_option(const std::string & val)204 bool TableApi::add_option(const std::string& val)
205 {
206 if ( should_delegate() )
207 return delegate->add_option(val);
208
209 if (open_tables.empty())
210 {
211 DataApi::developer_error("Must open table before adding an option!!: "
212 "<anonymous> = " + val);
213 return false;
214 }
215
216 Table* t = open_tables.top();
217 t->add_option(val);
218 return true;
219 }
220
add_option(const std::string & option_name,const std::string & val)221 bool TableApi::add_option(const std::string& option_name, const std::string& val)
222 { return do_add_option(option_name, val, val); }
223
add_option(const std::string & option_name,const int val)224 bool TableApi::add_option(const std::string& option_name, const int val)
225 { return do_add_option(option_name, val, std::to_string(val)); }
226
add_option(const std::string & option_name,const bool val)227 bool TableApi::add_option(const std::string& option_name, const bool val)
228 { return do_add_option(option_name, val, std::to_string(val)); }
229
230 // compilers are fickle and dangerous creatures. Ensure a literal gets
231 // sent here rather to become a bool
add_option(const char * const v)232 bool TableApi::add_option(const char* const v)
233 { return add_option(std::string(v)); }
234
add_option(const std::string & name,const char * const v)235 bool TableApi::add_option(const std::string& name, const char* const v)
236 { return add_option(name, std::string(v)); }
237
create_append_data(std::string & fqn,Table * & t)238 void TableApi::create_append_data(std::string& fqn, Table*& t)
239 {
240 unsigned start = 0;
241
242 if (!top_level_tables.empty())
243 start = top_level_tables.top();
244
245 // I need to iterate over the stack of open tables. However,
246 // stack's don't allow iteration without popping. So, rather
247 // than change the underlying stack data structure, I am going
248 // to just copy the entire data structure. Inefficient, but
249 // not pressed for speed here.
250 std::stack<Table*> copy(open_tables);
251
252 while (!copy.empty() && copy.size() >= start)
253 {
254 fqn = copy.top()->get_name() + "." + fqn;
255 t = copy.top();
256 copy.pop();
257 }
258 }
259
260 template<typename T>
do_append_option(const std::string & option_name,const T val,const std::string & s_val)261 void TableApi::do_append_option(const std::string& option_name, const T val, const std::string& s_val)
262 {
263 if ( should_delegate() )
264 {
265 delegate->append_option(option_name, val);
266 return;
267 }
268
269 if (open_tables.empty())
270 {
271 DataApi::developer_error("Must open table before adding an option!!: " +
272 option_name + " = " + s_val);
273 return;
274 }
275
276 Table* t = nullptr;
277 std::string opt_name(option_name);
278 create_append_data(opt_name, t);
279
280 if ( t != nullptr)
281 t->append_option(opt_name, val);
282 else
283 DataApi::developer_error("Snort2lua cannot find a Table to append the option: "
284 + option_name + " = " + s_val);
285 }
286
append_option(const std::string & option_name,const std::string & val)287 void TableApi::append_option(const std::string& option_name, const std::string& val)
288 { do_append_option(option_name, val, val); }
289
append_option(const std::string & option_name,const int val)290 void TableApi::append_option(const std::string& option_name, const int val)
291 { do_append_option(option_name, val, std::to_string(val)); }
292
append_option(const std::string & option_name,const bool val)293 void TableApi::append_option(const std::string& option_name, const bool val)
294 { do_append_option(option_name, val, std::to_string(val)); }
295
append_option(const std::string & name,const char * const v)296 void TableApi::append_option(const std::string& name, const char* const v)
297 { append_option(name, std::string(v)); }
298
add_list(const std::string & list_name,const std::string & next_elem)299 bool TableApi::add_list(const std::string& list_name, const std::string& next_elem)
300 {
301 if ( should_delegate() )
302 return delegate->add_list(list_name, next_elem);
303
304 if (open_tables.empty())
305 {
306 DataApi::developer_error("Must open table before adding an option!!: " +
307 list_name + " = " + next_elem);
308 return false;
309 }
310
311 Table* t = open_tables.top();
312
313 if (t)
314 {
315 t->add_list(list_name, next_elem);
316 return true;
317 }
318 else
319 {
320 DataApi::developer_error("Must open table before adding an list!!: " +
321 list_name + " += " + next_elem);
322 return false;
323 }
324 }
325
add_comment(const std::string & comment)326 bool TableApi::add_comment(const std::string& comment)
327 {
328 if ( should_delegate() )
329 return delegate->add_comment(comment);
330
331 if (open_tables.empty())
332 {
333 DataApi::developer_error("Must open table before adding comment !!: '" +
334 comment + "'");
335 DataApi::developer_error("comment added to as a general lua comment");
336 // data_api.add_comment(comment);
337 return false;
338 }
339
340 open_tables.top()->add_comment(comment);
341 return true;
342 }
343
option_exists(const std::string & name)344 bool TableApi::option_exists(const std::string& name)
345 {
346 if ( should_delegate() )
347 return delegate->option_exists(name);
348
349 if (open_tables.empty())
350 {
351 DataApi::developer_error("Must open table before calling option_exists() !!");
352 return false;
353 }
354
355 return open_tables.top()->has_option(name);
356 }
357
add_diff_option_comment(const std::string & orig_var,const std::string & new_var)358 bool TableApi::add_diff_option_comment(const std::string& orig_var, const std::string& new_var)
359 {
360 if ( should_delegate() )
361 return delegate->add_diff_option_comment(orig_var, new_var);
362
363 std::string error_string = "option change: '" + orig_var + "' --> '"
364 + new_var + "'";
365
366 if (open_tables.empty())
367 {
368 DataApi::developer_error("Must open table before adding an option!!: " +
369 orig_var + " = " + new_var);
370 return false;
371 }
372
373 open_tables.top()->add_comment(error_string);
374 return true;
375 }
376
add_deleted_comment(const std::string & dep_var)377 bool TableApi::add_deleted_comment(const std::string& dep_var)
378 {
379 if ( should_delegate() )
380 return delegate->add_deleted_comment(dep_var);
381
382 std::string error_string = "option deleted: '" + dep_var + "'";
383
384 if (open_tables.empty())
385 {
386 DataApi::developer_error("Must open a table before adding "
387 "deprecated comment!!: " + dep_var);
388 return false;
389 }
390
391 open_tables.top()->add_comment(error_string);
392 return true;
393 }
394
add_unsupported_comment(const std::string & unsupported_var)395 bool TableApi::add_unsupported_comment(const std::string& unsupported_var)
396 {
397 if ( should_delegate() )
398 return delegate->add_unsupported_comment(unsupported_var);
399
400 std::string unsupported_str = "option '" + unsupported_var +
401 "' is currently unsupported";
402
403 if (open_tables.empty())
404 {
405 DataApi::developer_error("Must open a table before adding an "
406 "'unsupported' comment");
407 return false;
408 }
409
410 open_tables.top()->add_comment(unsupported_str);
411 return true;
412 }
413
operator <<(std::ostream & out,const TableApi & table)414 std::ostream& operator<<(std::ostream& out, const TableApi& table)
415 {
416 table.print_tables(out);
417 return out;
418 }
419
print_tables(std::ostream & out) const420 void TableApi::print_tables(std::ostream& out) const
421 {
422 if ( empty() )
423 return;
424
425 for (Table* t : tables)
426 out << (*t) << "\n\n";
427 }
428
swap_tables(std::vector<Table * > & new_tables)429 void TableApi::swap_tables(std::vector<Table*>& new_tables)
430 {
431 tables.swap(new_tables);
432 }
433
get_option_value(const std::string & name,std::string & value)434 bool TableApi::get_option_value(const std::string& name, std::string& value)
435 {
436 if ( should_delegate() )
437 return delegate->get_option_value(name, value);
438
439 if (open_tables.empty())
440 {
441 DataApi::developer_error("Must open table before calling option_exists() !!");
442 return false;
443 }
444
445 return open_tables.top()->get_option(name, value);
446 }
447
run_when_exists(const char * table_name,PendingFunction action)448 void TableApi::run_when_exists(const char* table_name, PendingFunction action)
449 {
450 if ( should_delegate(table_name) )
451 delegate->run_when_exists(table_name, action);
452
453 if ( util::find_table(tables, table_name) )
454 action(*this);
455 else
456 {
457 if ( pending.find(table_name) == pending.end() )
458 pending[table_name] = std::queue<PendingFunction>();
459
460 pending[table_name].push(action);
461 }
462 }
463