]> git.ipfire.org Git - thirdparty/snort3.git/blob - src/network_inspectors/appid/test/service_state_test.cc
b747ef508c2fb1557956acd69efe448d48f50dc1
[thirdparty/snort3.git] / src / network_inspectors / appid / test / service_state_test.cc
1 //--------------------------------------------------------------------------
2 // Copyright (C) 2018-2023 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 // service_state.cc author Masud Hasan <mashasan@cisco.com>
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "network_inspectors/appid/service_state.cc"
25
26 #include <CppUTest/CommandLineTestRunner.h>
27 #include <CppUTest/TestHarness.h>
28
29 #include <vector>
30
31 namespace snort
32 {
33 Packet::Packet(bool)
34 {
35 memset((char*) this , 0, sizeof(*this));
36 ip_proto_next = IpProtocol::PROTO_NOT_SET;
37 packet_flags = PKT_FROM_CLIENT;
38 }
39 Packet::~Packet() = default;
40 Packet* DetectionEngine::get_current_packet() { return nullptr; }
41
42 // Stubs for logs
43 char test_log[256];
44 void LogMessage(const char* format, va_list& args)
45 {
46 vsprintf(test_log, format, args);
47 }
48 void LogMessage(const char* format,...)
49 {
50 va_list args;
51 va_start(args, format);
52 LogMessage(format, args);
53 va_end(args);
54 }
55
56 void LogLabel(const char*, FILE*) {}
57 void LogText(const char* s, FILE*) { LogMessage("%s\n", s); }
58
59 unsigned DataBus::get_id(const PubKey&)
60 { return 0; }
61
62 // Stubs for utils
63 char* snort_strdup(const char* str)
64 {
65 assert(str);
66 size_t n = strlen(str) + 1;
67 char* p = (char*)snort_alloc(n);
68 memcpy(p, str, n);
69 return p;
70 }
71 time_t packet_time() { return std::time(nullptr); }
72
73 AppIdSessionApi::AppIdSessionApi(const AppIdSession*, const SfIp&) :
74 StashGenericObject(STASH_GENERIC_OBJECT_APPID) {}
75 }
76
77 DiscoveryFilter::~DiscoveryFilter(){}
78 // Stubs for AppInfoManager
79 AppInfoTableEntry* AppInfoManager::get_app_info_entry(AppId)
80 {
81 return nullptr;
82 }
83
84 // Stubs for appid classes
85 class AppIdInspector{};
86 FlowData::FlowData(unsigned, Inspector*) : next(nullptr), prev(nullptr), handler(nullptr), id(0)
87 { }
88 FlowData::~FlowData() = default;
89
90 // Stubs for AppIdDebug
91 THREAD_LOCAL AppIdDebug* appidDebug = nullptr;
92 THREAD_LOCAL AppIdStats appid_stats;
93
94 void AppIdDebug::activate(const Flow*, const AppIdSession*, bool) { active = true; }
95
96 void ApplicationDescriptor::set_id(const Packet&, AppIdSession&, AppidSessionDirection, AppId, AppidChangeBits&) { }
97 void ApplicationDescriptor::set_id(AppId){}
98 void ServiceAppDescriptor::set_id(AppId, OdpContext&){}
99 void ServiceAppDescriptor::set_port_service_id(AppId){}
100 void ClientAppDescriptor::update_user(AppId, const char*, AppidChangeBits&){}
101 AppIdConfig::~AppIdConfig() = default;
102 OdpContext::OdpContext(const AppIdConfig&, snort::SnortConfig*) { }
103 AppIdConfig stub_config;
104 AppIdContext stub_ctxt(stub_config);
105 OdpContext stub_odp_ctxt(stub_config, nullptr);
106 AppIdSession::AppIdSession(IpProtocol, const SfIp* ip, uint16_t, AppIdInspector&,
107 OdpContext&, uint32_t) : FlowData(0), config(stub_config),
108 api(*(new AppIdSessionApi(this, *ip))), odp_ctxt(stub_odp_ctxt) { }
109 AppIdSession::~AppIdSession() = default;
110 AppIdDiscovery::~AppIdDiscovery() = default;
111 void ClientDiscovery::initialize(AppIdInspector&) { }
112 void ClientDiscovery::reload() { }
113 void AppIdDiscovery::register_detector(const std::string&, AppIdDetector*, IpProtocol) {}
114 void AppIdDiscovery::add_pattern_data(AppIdDetector*, SearchTool&, int, const uint8_t* const,
115 unsigned, unsigned) {}
116 void AppIdDiscovery::register_tcp_pattern(AppIdDetector*, const uint8_t* const, unsigned,
117 int, unsigned) {}
118 void AppIdDiscovery::register_udp_pattern(AppIdDetector*, const uint8_t* const, unsigned,
119 int, unsigned) {}
120 int AppIdDiscovery::add_service_port(AppIdDetector*,
121 const ServiceDetectorPort&) { return APPID_EINVALID; }
122 void ServiceDiscovery::initialize(AppIdInspector&) {}
123 void ServiceDiscovery::reload() {}
124 void ServiceDiscovery::finalize_service_patterns() {}
125 void ServiceDiscovery::match_by_pattern(AppIdSession&, const Packet*, IpProtocol) {}
126 void ServiceDiscovery::get_port_based_services(IpProtocol, uint16_t, AppIdSession&) {}
127 void ServiceDiscovery::get_next_service(const Packet*, const AppidSessionDirection, AppIdSession&) {}
128 int ServiceDiscovery::identify_service(AppIdSession&, Packet*, AppidSessionDirection,
129 AppidChangeBits&) { return 0; }
130 int ServiceDiscovery::add_ftp_service_state(AppIdSession&) { return 0; }
131 bool ServiceDiscovery::do_service_discovery(AppIdSession&, Packet*, AppidSessionDirection,
132 AppidChangeBits&) { return false; }
133 int ServiceDiscovery::incompatible_data(AppIdSession&, const Packet*,AppidSessionDirection,
134 ServiceDetector*) { return 0; }
135 int ServiceDiscovery::fail_service(AppIdSession&, const Packet*, AppidSessionDirection,
136 ServiceDetector*, ServiceDiscoveryState*) { return 0; }
137 int ServiceDiscovery::add_service_port(AppIdDetector*,
138 const ServiceDetectorPort&) { return APPID_EINVALID; }
139 DnsPatternMatchers::~DnsPatternMatchers() = default;
140 EveCaPatternMatchers::~EveCaPatternMatchers() = default;
141 HttpPatternMatchers::~HttpPatternMatchers() = default;
142 SipPatternMatchers::~SipPatternMatchers() = default;
143 SslPatternMatchers::~SslPatternMatchers() = default;
144 AlpnPatternMatchers::~AlpnPatternMatchers() = default;
145 CipPatternMatchers::~CipPatternMatchers() = default;
146 snort::SearchTool::SearchTool(bool, const char*) { }
147 snort::SearchTool::~SearchTool() = default;
148 void appid_log(const snort::Packet*, unsigned char, char const* fmt, ...)
149 {
150 va_list args;
151 va_start(args, fmt);
152 LogMessage(fmt, args);
153 va_end(args);
154 }
155
156 TEST_GROUP(service_state_tests)
157 {
158 void setup() override
159 {
160 appidDebug = new AppIdDebug();
161 appidDebug->activate(nullptr, nullptr, false);
162 }
163
164 void teardown() override
165 {
166 delete appidDebug;
167 }
168 };
169
170 TEST(service_state_tests, select_detector_by_brute_force)
171 {
172 ServiceDiscovery sd;
173 ServiceDiscoveryState sds;
174 // Testing end of brute-force walk for supported and unsupported protocols
175 test_log[0] = '\0';
176 sds.select_detector_by_brute_force(IpProtocol::TCP, sd);
177 STRCMP_EQUAL(test_log, "Brute-force state failed - no more TCP detectors\n");
178
179 test_log[0] = '\0';
180 sds.select_detector_by_brute_force(IpProtocol::UDP, sd);
181 STRCMP_EQUAL(test_log, "Brute-force state failed - no more UDP detectors\n");
182
183 test_log[0] = '\0';
184 sds.select_detector_by_brute_force(IpProtocol::IP, sd);
185 STRCMP_EQUAL(test_log, "");
186 }
187
188 TEST(service_state_tests, set_service_id_failed)
189 {
190 ServiceDiscoveryState sds;
191 AppIdInspector inspector;
192 SfIp client_ip;
193 client_ip.set("1.2.3.4");
194 AppIdSession asd(IpProtocol::PROTO_NOT_SET, &client_ip, 0, inspector, stub_odp_ctxt);
195
196 // Testing 3+ failures to exceed STATE_ID_NEEDED_DUPE_DETRACT_COUNT with valid_count = 0
197 sds.set_state(ServiceState::VALID);
198 sds.set_service_id_failed(asd, &client_ip, 0);
199 sds.set_service_id_failed(asd, &client_ip, 0);
200 sds.set_service_id_failed(asd, &client_ip, 0);
201 sds.set_service_id_failed(asd, &client_ip, 0);
202 CHECK_TRUE(sds.get_state() == ServiceState::SEARCHING_PORT_PATTERN);
203
204 delete &asd.get_api();
205 }
206
207
208 TEST(service_state_tests, set_service_id_failed_with_valid)
209 {
210 ServiceDiscoveryState sds;
211 AppIdInspector inspector;
212 SfIp client_ip;
213 client_ip.set("1.2.3.4");
214 AppIdSession asd(IpProtocol::PROTO_NOT_SET, &client_ip, 0, inspector, stub_odp_ctxt);
215
216 // Testing 3+ failures to exceed STATE_ID_NEEDED_DUPE_DETRACT_COUNT with valid_count > 1
217 sds.set_state(ServiceState::VALID);
218 sds.set_service_id_valid(nullptr);
219 sds.set_service_id_valid(nullptr);
220 sds.set_service_id_failed(asd, &client_ip, 0);
221 sds.set_service_id_failed(asd, &client_ip, 0);
222 sds.set_service_id_failed(asd, &client_ip, 0);
223 sds.set_service_id_failed(asd, &client_ip, 0);
224 CHECK_TRUE(sds.get_state() == ServiceState::VALID);
225
226 delete &asd.get_api();
227 }
228
229 TEST(service_state_tests, appid_service_state_key_comparison_test)
230 {
231 SfIp ip4, ip6;
232 ip4.set("1.2.3.4");
233 ip6.set("1111:2222:3333:4444:5555:6666:7777:8888");
234 IpProtocol proto = IpProtocol::TCP;
235 uint16_t port=3000;
236
237 AppIdServiceStateKey A(&ip4, proto, port, 0, DAQ_PKTHDR_UNKNOWN, false);
238 AppIdServiceStateKey B(&ip6, proto, port, 0, DAQ_PKTHDR_UNKNOWN, false);
239
240 // We must never be in a situation where !( A<B ) and !( B<A ),
241 // because then map will consider A=B.
242 CHECK_TRUE(A<B || B<A);
243 }
244
245 TEST(service_state_tests, service_cache)
246 {
247 size_t num_entries = 10, max_entries = 3;
248 size_t memcap = max_entries*MapList::sz;
249 MapList ServiceCache(memcap);
250
251 IpProtocol proto = IpProtocol::TCP;
252 uint16_t port = 3000;
253 SfIp ip4, ip6;
254 ip4.set("1.2.3.4");
255 ip6.set("1111:2222:3333:4444:5555:6666:7777:8888");
256
257 ServiceDiscoveryState* ss = nullptr;
258 std::vector<ServiceDiscoveryState*> ssvec;
259
260
261 // Insert (ipv4 and ipv6) past the memcap, and check the memcap is not exceeded.
262 for( size_t i = 1; i <= num_entries; i++, port++ )
263 {
264 const SfIp* ip = ( i%2 == 1 ? &ip4 : &ip6 );
265 ss = ServiceCache.add( AppIdServiceStateKey(ip, proto, port, 0, DAQ_PKTHDR_UNKNOWN, false) );
266 CHECK_TRUE(ServiceCache.size() == ( i <= max_entries ? i : max_entries));
267 ssvec.push_back(ss);
268 }
269
270 // The cache should now be ip6:3007, ip4:3008, ip6:3009.
271 // Check that the order in the cache is correct.
272 Queue_t::iterator it = ServiceCache.newest();
273 std::vector<ServiceDiscoveryState*>::iterator vit = --ssvec.end();
274 for( size_t i=0; i<max_entries; i++, --it, --vit )
275 {
276 Map_t::iterator mit = *it;
277 CHECK_TRUE( mit->second == *vit );
278 }
279
280 // Now get e.g. the oldest from the cache and check that it got touched:
281 it = ServiceCache.oldest();
282 ss = ServiceCache.get( (*it)->first, true );
283 CHECK_TRUE( ss != nullptr );
284 CHECK_TRUE( ss->qptr == ServiceCache.newest() );
285 }
286
287 int main(int argc, char** argv)
288 {
289 int rc = CommandLineTestRunner::RunAllTests(argc, argv);
290 return rc;
291 }