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