1 //--------------------------------------------------------------------------
2 // Copyright (C) 2016-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 
19 // search_tool_test.cc author Steve Chew <stechew@cisco.com>
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 //  Change private to public to give access to private members.
26 #define private public
27 #include "search_engines/search_tool.h"
28 #undef private
29 
30 #include <cstring>
31 
32 #include "detection/fp_config.h"
33 #include "framework/base_api.h"
34 #include "framework/mpse.h"
35 #include "framework/mpse_batch.h"
36 #include "main/snort_config.h"
37 #include "managers/mpse_manager.h"
38 
39 // must appear after snort_config.h to avoid broken c++ map include
40 #include <CppUTest/CommandLineTestRunner.h>
41 #include <CppUTest/TestHarness.h>
42 
43 using namespace snort;
44 
45 //-------------------------------------------------------------------------
46 // base stuff
47 //-------------------------------------------------------------------------
48 
49 namespace snort
50 {
51 SnortConfig s_conf;
52 
53 THREAD_LOCAL SnortConfig* snort_conf = &s_conf;
54 
55 static std::vector<void *> s_state;
56 
SnortConfig(const SnortConfig * const,const char *)57 SnortConfig::SnortConfig(const SnortConfig* const, const char*)
58 {
59     state = &s_state;
60     num_slots = 1;
61     fast_pattern_config = nullptr;
62 }
63 
64 SnortConfig::~SnortConfig() = default;
65 
get_conf()66 const SnortConfig* SnortConfig::get_conf()
67 { return snort_conf; }
68 
get_instance_id()69 unsigned get_instance_id()
70 { return 0; }
71 
LogValue(const char *,const char *,FILE *)72 void LogValue(const char*, const char*, FILE*) { }
LogMessage(const char *,...)73 void LogMessage(const char*, ...) { }
FatalError(const char *,...)74 [[noreturn]] void FatalError(const char*,...) { exit(1); }
LogCount(char const *,uint64_t,FILE *)75 void LogCount(char const*, uint64_t, FILE*) { }
LogStat(const char *,double,FILE *)76 void LogStat(const char*, double, FILE*) { }
77 
78 static void* s_tree = (void*)"tree";
79 static void* s_list = (void*)"list";
80 
81 static MpseAgent s_agent =
82 {
83     [](struct SnortConfig* sc, void*, void** ppt)
__anon529a470f0102() 84     {
85         CHECK(sc == nullptr);
86         *ppt = s_tree;
87         return 0;
88     },
89     [](void*, void** ppl)
__anon529a470f0202() 90     {
91         *ppl = s_list;
92         return 0;
93     },
94 
__anon529a470f0302() 95     [](void*) { },
__anon529a470f0402() 96     [](void** ppt) { CHECK(*ppt == s_tree); },
__anon529a470f0502() 97     [](void** ppl) { CHECK(*ppl == s_list); }
98 };
99 
Mpse(const char *)100 Mpse::Mpse(const char*) { }
101 
search(const unsigned char * T,int n,MpseMatch match,void * context,int * current_state)102 int Mpse::search(
103     const unsigned char* T, int n, MpseMatch match,
104     void* context, int* current_state)
105 {
106     return _search(T, n, match, context, current_state);
107 }
108 
search_all(const unsigned char * T,int n,MpseMatch match,void * context,int * current_state)109 int Mpse::search_all(
110     const unsigned char* T, int n, MpseMatch match,
111     void* context, int* current_state)
112 {
113     return _search(T, n, match, context, current_state);
114 }
115 
search(MpseBatch &,MpseType)116 void Mpse::search(MpseBatch&, MpseType) { }
_search(MpseBatch &,MpseType)117 void Mpse::_search(MpseBatch&, MpseType) { }
118 
119 }
120 
get_search_method()121 const char* FastPatternConfig::get_search_method()
122 { return "ac_bnfa"; }
123 
124 extern const BaseApi* se_ac_bnfa;
125 extern const BaseApi* se_ac_full;
126 Mpse* mpse = nullptr;
127 
delete_search_engine(Mpse * eng)128 void MpseManager::delete_search_engine(Mpse* eng)
129 {
130     const MpseApi* api = eng->get_api();
131     api->dtor(eng);
132 }
133 
~MpseGroup()134 MpseGroup::~MpseGroup()
135 {
136     if (normal_mpse)
137     {
138         MpseManager::delete_search_engine(normal_mpse);
139         normal_mpse = nullptr;
140     }
141     if (offload_mpse)
142     {
143         MpseManager::delete_search_engine(offload_mpse);
144         offload_mpse = nullptr;
145     }
146 }
147 
create_normal_mpse(const SnortConfig *,const char * type)148 bool MpseGroup::create_normal_mpse(const SnortConfig*, const char* type)
149 {
150     const MpseApi* api;
151 
152     if ( !strcmp(type, "ac_bnfa") )
153         api = (const MpseApi*) se_ac_bnfa;
154 
155     else if ( !strcmp(type, "ac_full") )
156         api = (const MpseApi*) se_ac_full;
157 
158     else
159         return false;
160 
161     api->init();
162     mpse = api->ctor(snort_conf, nullptr, &s_agent);
163 
164     CHECK(mpse);
165 
166     mpse->set_api(api);
167     normal_mpse = mpse;
168 
169     return true;
170 }
171 
create_offload_mpse(const SnortConfig *)172 bool MpseGroup::create_offload_mpse(const SnortConfig*)
173 {
174     offload_mpse = nullptr;
175     return false;
176 }
177 
178 struct ExpectedMatch
179 {
180     int id;
181     int offset;
182 };
183 
184 static const ExpectedMatch* s_expect = nullptr;
185 static int s_found = 0;
186 
Test_SearchStrFound(void * pid,void *,int index,void *,void *)187 static int Test_SearchStrFound(
188     void* pid, void* /*tree*/, int index, void* /*context*/, void* /*neg_list*/)
189 {
190     auto id = reinterpret_cast<std::uintptr_t>(pid);
191 
192     if ( s_expect and s_found >= 0 and
193         s_expect[s_found].id == (int)id and
194         s_expect[s_found].offset == index )
195     {
196         ++s_found;
197     }
198     else s_found = -1;
199 
200     return s_found == -1;
201 }
202 
203 //-------------------------------------------------------------------------
204 // ac_bnfa tests
205 //-------------------------------------------------------------------------
206 
TEST_GROUP(search_tool_bnfa)207 TEST_GROUP(search_tool_bnfa)
208 {
209     SearchTool* stool;
210 
211     void setup() override
212     {
213         CHECK(se_ac_bnfa);
214         SearchTool::set_conf(snort_conf);
215         stool = new SearchTool("ac_bnfa");
216         SearchTool::set_conf(nullptr);
217 
218         CHECK(stool->mpsegrp->normal_mpse);
219 
220         int pattern_id = 1;
221         stool->add("the", 3, pattern_id);
222         CHECK(stool->max_len == 3);
223 
224         pattern_id = 77;
225         stool->add("tuba", 4, pattern_id);
226         CHECK(stool->max_len == 4);
227 
228         pattern_id = 78;
229         stool->add("uba", 3, pattern_id);
230         CHECK(stool->max_len == 4);
231 
232         pattern_id = 2112;
233         stool->add("away", 4, pattern_id);
234         CHECK(stool->max_len == 4);
235 
236         pattern_id = 1000;
237         stool->add("nothere", 7, pattern_id);
238         CHECK(stool->max_len == 7);
239 
240         stool->prep();
241 
242     }
243     void teardown() override
244     {
245         delete stool;
246     }
247 };
248 
TEST(search_tool_bnfa,search)249 TEST(search_tool_bnfa, search)
250 {
251     //                     0         1         2         3
252     //                     0123456789012345678901234567890
253     const char* datastr = "the tuba ran away with the tuna";
254     const ExpectedMatch xm[] =
255     {
256         { 1, 3 },
257         { 78, 8 },
258         { 2112, 17 },
259         { 1, 26 },
260         { 0, 0 }
261     };
262 
263     s_expect = xm;
264     s_found = 0;
265 
266     int result = stool->find(datastr, strlen(datastr), Test_SearchStrFound);
267 
268     CHECK(result == 4);
269     CHECK(s_found == 4);
270 }
271 
TEST(search_tool_bnfa,search_all)272 TEST(search_tool_bnfa, search_all)
273 {
274     //                     0         1         2         3
275     //                     0123456789012345678901234567890
276     const char* datastr = "the tuba ran away with the tuna";
277     const ExpectedMatch xm[] =
278     {
279         { 1, 3 },
280         { 78, 8 },
281         { 2112, 17 },
282         { 1, 26 },
283         { 0, 0 }
284     };
285 
286     s_expect = xm;
287     s_found = 0;
288 
289     int result = stool->find_all(datastr, strlen(datastr), Test_SearchStrFound);
290 
291     CHECK(result == 4);
292     CHECK(s_found == 4);
293 }
294 
295 //-------------------------------------------------------------------------
296 // ac_full tests
297 //-------------------------------------------------------------------------
298 
TEST_GROUP(search_tool_full)299 TEST_GROUP(search_tool_full)
300 {
301     SearchTool* stool;
302 
303     void setup() override
304     {
305         CHECK(se_ac_full);
306         SearchTool::set_conf(snort_conf);
307         stool = new SearchTool("ac_full", true);
308         SearchTool::set_conf(nullptr);
309 
310         CHECK(stool->mpsegrp->normal_mpse);
311 
312         int pattern_id = 1;
313         stool->add("the", 3, pattern_id);
314         CHECK(stool->max_len == 3);
315 
316         pattern_id = 77;
317         stool->add("tuba", 4, pattern_id);
318         CHECK(stool->max_len == 4);
319 
320         pattern_id = 78;
321         stool->add("uba", 3, pattern_id);
322         CHECK(stool->max_len == 4);
323 
324         pattern_id = 2112;
325         stool->add("away", 4, pattern_id);
326         CHECK(stool->max_len == 4);
327 
328         pattern_id = 1000;
329         stool->add("nothere", 7, pattern_id);
330         CHECK(stool->max_len == 7);
331 
332         stool->prep();
333 
334     }
335     void teardown() override
336     {
337         delete stool;
338     }
339 };
340 
TEST(search_tool_full,search)341 TEST(search_tool_full, search)
342 {
343     //                     0         1         2         3
344     //                     0123456789012345678901234567890
345     const char* datastr = "the tuba ran away with the tuna";
346     const ExpectedMatch xm[] =
347     {
348         { 1, 3 },
349         { 78, 8 },
350         { 2112, 17 },
351         { 1, 26 },
352         { 0, 0 }
353     };
354 
355     s_expect = xm;
356     s_found = 0;
357 
358     int result = stool->find(datastr, strlen(datastr), Test_SearchStrFound);
359 
360     CHECK(result == 4);
361     CHECK(s_found == 4);
362 }
363 
TEST(search_tool_full,search_all)364 TEST(search_tool_full, search_all)
365 {
366     //                     0         1         2         3
367     //                     0123456789012345678901234567890
368     const char* datastr = "the tuba ran away with the tuna";
369     const ExpectedMatch xm[] =
370     {
371         { 1, 3 },
372         { 78, 8 },
373         { 77, 8 },
374         { 2112, 17 },
375         { 1, 26 },
376         { 0, 0 }
377     };
378 
379     s_expect = xm;
380     s_found = 0;
381 
382     int result = stool->find_all(datastr, strlen(datastr), Test_SearchStrFound);
383 
384     CHECK(result == 5);
385     CHECK(s_found == 5);
386 }
387 
388 //-------------------------------------------------------------------------
389 // main
390 //-------------------------------------------------------------------------
391 
main(int argc,char ** argv)392 int main(int argc, char** argv)
393 {
394     return CommandLineTestRunner::RunAllTests(argc, argv);
395 }
396 
397