]>
Commit | Line | Data |
---|---|---|
38939e49 | 1 | //-------------------------------------------------------------------------- |
b88d016d | 2 | // Copyright (C) 2014-2024 Cisco and/or its affiliates. All rights reserved. |
38939e49 RC |
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 | //-------------------------------------------------------------------------- | |
75a57075 RC |
18 | // parameter.cc author Russ Combs <rucombs@cisco.com> |
19 | ||
02d63397 MA |
20 | #ifdef HAVE_CONFIG_H |
21 | #include "config.h" | |
22 | #endif | |
23 | ||
fc915f61 | 24 | #include "parameter.h" |
75a57075 | 25 | |
02d63397 | 26 | #include <cassert> |
75a57075 | 27 | |
75a57075 RC |
28 | #include <iomanip> |
29 | #include <sstream> | |
a478eca7 | 30 | #include <type_traits> |
75a57075 | 31 | #include <vector> |
75a57075 | 32 | |
fc915f61 RC |
33 | #include "utils/dnet_header.h" |
34 | ||
02d63397 MA |
35 | #include "value.h" |
36 | ||
a19c078d | 37 | using namespace snort; |
fe2d9c00 RC |
38 | using namespace std; |
39 | ||
020356df RC |
40 | //-------------------------------------------------------------------------- |
41 | // helpers | |
42 | //-------------------------------------------------------------------------- | |
43 | ||
44 | static bool is_sep(char c) | |
45 | { return !c || c == '|' || isspace(c); } | |
46 | ||
47 | static const char* find(const char* r, const char* s) | |
48 | { | |
49 | size_t n = strlen(s); | |
50 | ||
51 | if ( !n ) | |
52 | return nullptr; | |
53 | ||
54 | const char* t = strstr(r, s); | |
55 | ||
56 | while ( t ) | |
57 | { | |
58 | if ( (t == r || is_sep(t[-1])) && is_sep(t[n]) ) | |
59 | return t; | |
60 | ||
61 | t = strstr(t+n, s); | |
62 | } | |
63 | return nullptr; | |
64 | } | |
65 | ||
66 | static unsigned get_index(const char* r, const char* t) | |
67 | { | |
68 | unsigned idx = 0; | |
69 | const char* p = strchr(r, '|'); | |
70 | ||
71 | while ( p && p < t ) | |
72 | { | |
73 | ++idx; | |
74 | p = strchr(p+1, '|'); | |
75 | } | |
76 | return idx; | |
77 | } | |
78 | ||
3b8d4148 | 79 | static size_t split(const string& s, vector<string>& strs) |
020356df | 80 | { |
3b8d4148 | 81 | stringstream ss(s); |
82 | string tok; | |
020356df | 83 | |
3b8d4148 | 84 | while ( ss >> tok ) |
85 | strs.push_back(tok); | |
020356df RC |
86 | |
87 | return strs.size(); | |
88 | } | |
89 | ||
a478eca7 OSSIC |
90 | template <bool RV, typename T> |
91 | static bool str2num(const char* r, T& t) | |
020356df | 92 | { |
a478eca7 | 93 | const int n = 5; |
020356df | 94 | |
a478eca7 OSSIC |
95 | if ( *r != 'm' ) |
96 | return false; | |
020356df | 97 | |
a478eca7 OSSIC |
98 | if ( !strncmp(r, "maxSZ", n) ) |
99 | r = (sizeof(size_t) == 4) ? "max32" : "max53"; | |
020356df | 100 | |
a478eca7 | 101 | bool res = true; |
9d1d5865 | 102 | |
a478eca7 OSSIC |
103 | if ( !strncmp(r, "max31", n) ) |
104 | t = 2147483647; | |
9d1d5865 | 105 | |
a478eca7 OSSIC |
106 | else if ( !strncmp(r, "max32", n) ) |
107 | t = 4294967295; | |
108 | ||
109 | // Lua represents numbers in a 64-bit double. The max representable value is 9007199254740992 | |
110 | // and the min is -9007199254740992 | |
111 | else if ( !strncmp(r, "max53", n) ) | |
112 | t = 9007199254740992; | |
113 | ||
114 | else if ( !strncmp(r, "max63", n) ) | |
115 | t = 9223372036854775807; | |
116 | ||
117 | else if ( !strncmp(r, "max64", n) ) | |
118 | t = std::is_same_v<T, int64_t> ? -1 : 18446744073709551615ULL; | |
119 | ||
120 | else | |
121 | res = false; | |
122 | ||
123 | return res and (r[n] == '\0' or (RV and r[n] == ':')); | |
124 | } | |
125 | ||
126 | int64_t Parameter::get_int(const char* r) | |
127 | { | |
49af2b2c RC |
128 | char* end = nullptr; |
129 | int64_t i = (int64_t)strtoll(r, &end, 0); | |
a478eca7 OSSIC |
130 | |
131 | if (!str2num<true>(r, i)) | |
132 | assert(!*end or *end == ':'); | |
49af2b2c RC |
133 | |
134 | return i; | |
020356df RC |
135 | } |
136 | ||
9d1d5865 RD |
137 | uint64_t Parameter::get_uint(const char* r) |
138 | { | |
a478eca7 OSSIC |
139 | char* end = nullptr; |
140 | uint64_t i = (uint64_t)strtoull(r, &end, 0); | |
9d1d5865 | 141 | |
a478eca7 OSSIC |
142 | if (!str2num<true>(r, i)) |
143 | assert(!*end or *end == ':'); | |
9d1d5865 | 144 | |
a478eca7 OSSIC |
145 | return i; |
146 | } | |
9d1d5865 | 147 | |
a478eca7 OSSIC |
148 | int64_t Parameter::get_int(const char* r, bool& is_correct) |
149 | { | |
150 | char* end = nullptr; | |
151 | int64_t i = (int64_t)strtoll(r, &end, 0); | |
9d1d5865 | 152 | |
a478eca7 | 153 | is_correct = str2num<false>(r, i) or !*end; |
9d1d5865 | 154 | |
a478eca7 OSSIC |
155 | return i; |
156 | } | |
157 | ||
158 | uint64_t Parameter::get_uint(const char* r, bool& is_correct) | |
159 | { | |
9d1d5865 RD |
160 | char* end = nullptr; |
161 | uint64_t i = (uint64_t)strtoull(r, &end, 0); | |
a478eca7 OSSIC |
162 | |
163 | is_correct = str2num<false>(r, i) or !*end; | |
9d1d5865 RD |
164 | |
165 | return i; | |
166 | } | |
167 | ||
020356df RC |
168 | //-------------------------------------------------------------------------- |
169 | // validation methods | |
170 | //-------------------------------------------------------------------------- | |
171 | ||
98a4cad5 | 172 | static bool valid_bool(const Value& v, const char*) |
75a57075 RC |
173 | { |
174 | return v.get_type() == Value::VT_BOOL; | |
175 | } | |
176 | ||
32edb5f8 | 177 | // FIXIT-L allow multiple , separated ranges |
9d1d5865 | 178 | static bool valid_int(Value& v, const char* r) |
75a57075 | 179 | { |
9d1d5865 | 180 | bool signed_values; |
a478eca7 OSSIC |
181 | bool is_correct = true; |
182 | uint64_t num; | |
183 | ||
9d1d5865 RD |
184 | switch (v.get_type()) |
185 | { | |
a478eca7 OSSIC |
186 | case Value::VT_STR: |
187 | { | |
188 | const char* str = v.get_string(); | |
189 | const char f = str[0]; | |
190 | ||
191 | if (f == '\0' or isspace(f)) | |
9d1d5865 | 192 | return false; |
a478eca7 OSSIC |
193 | |
194 | signed_values = ('-' == f); | |
195 | num = signed_values ? Parameter::get_int(str, is_correct) : Parameter::get_uint(str, is_correct); | |
196 | break; | |
9d1d5865 | 197 | } |
75a57075 | 198 | |
a478eca7 OSSIC |
199 | case Value::VT_REAL: |
200 | { | |
201 | double d = v.get_real(); | |
202 | signed_values = (0.0 > d); | |
cf03e7d9 | 203 | num = (uint64_t)(int64_t)d; |
a478eca7 OSSIC |
204 | break; |
205 | } | |
206 | ||
207 | default: | |
208 | return false; | |
209 | } | |
210 | ||
211 | if (is_correct) | |
212 | signed_values ? v.set((int64_t)num) : v.set((uint64_t)num); | |
213 | else | |
214 | return false; | |
215 | ||
216 | if (!r || !r[0]) | |
217 | return true; | |
218 | ||
75a57075 RC |
219 | // require no leading or trailing whitespace |
220 | // and either # | #: | :# | #:# | |
221 | // where # is a valid pos or neg dec, hex, or octal number | |
222 | ||
020356df | 223 | const char* t = strchr(r, ':'); |
9d1d5865 | 224 | if (!signed_values) |
75a57075 | 225 | { |
9d1d5865 RD |
226 | if ('-' == r[0]) |
227 | signed_values = true; | |
228 | if (t && '-' == t[1]) | |
229 | signed_values = true; | |
020356df | 230 | } |
75a57075 | 231 | |
9d1d5865 | 232 | if (signed_values) |
75a57075 | 233 | { |
a478eca7 OSSIC |
234 | int64_t d = (int64_t)num; |
235 | ||
9d1d5865 RD |
236 | if (':' != *r) |
237 | { | |
238 | int64_t low = Parameter::get_int(r); | |
239 | if (d < low) | |
240 | return false; | |
241 | if (!t) | |
242 | return d == low; | |
243 | } | |
244 | if (t && *++t) | |
245 | { | |
246 | int64_t hi = Parameter::get_int(t); | |
247 | if (d > hi) | |
248 | return false; | |
249 | } | |
250 | } | |
251 | else | |
252 | { | |
a478eca7 OSSIC |
253 | uint64_t d = (uint64_t)num; |
254 | ||
9d1d5865 RD |
255 | if (':' != *r) |
256 | { | |
257 | uint64_t low = Parameter::get_uint(r); | |
258 | if (d < low) | |
259 | return false; | |
260 | if (!t) | |
261 | return d == low; | |
262 | } | |
263 | if (t && *++t) | |
264 | { | |
265 | uint64_t hi = Parameter::get_uint(t); | |
266 | if (d > hi) | |
267 | return false; | |
268 | } | |
75a57075 | 269 | } |
a478eca7 | 270 | |
75a57075 RC |
271 | return true; |
272 | } | |
273 | ||
d4ff2e2c RC |
274 | // interval is a special case because we support a<>b and a<=>b for convenience. |
275 | // if not for that, then dsize:1<>10; would be dsize:>1, <10; (2 parameters) but | |
276 | // that is the same as dsize:>1; dsize:<10; which is arguably easier to read and | |
277 | // not significantly worse performance and which we also, obviously, already | |
278 | // support. and note that <> and <=> are non-standard Snort-isms. so, we wind | |
279 | // up with a multivalued parameter which is best handled as a string. validation | |
280 | // must be done by the user. the advantage of using an interval instead of string | |
281 | // is that we can document the type in one place and the parameters can focus on | |
282 | // their actual, specific semantics instead of trying to explain the syntax. this | |
283 | // also ensures that an int-type range is not applied to a string. | |
284 | ||
98a4cad5 | 285 | static bool valid_interval(const Value&, const char*) |
d4ff2e2c RC |
286 | { return true; } |
287 | ||
32edb5f8 | 288 | // FIXIT-L allow multiple , separated ranges |
9d1d5865 | 289 | static bool valid_real(Value& v, const char* r) |
75a57075 | 290 | { |
9d1d5865 RD |
291 | if (Value::VT_STR == v.get_type()) |
292 | { | |
293 | const char* str = v.get_string(); | |
294 | if (!str[0]) | |
295 | return false; | |
296 | ||
297 | double d = strtod(str, nullptr); | |
298 | v.set(d); | |
299 | } | |
300 | else if (Value::VT_REAL != v.get_type()) | |
cd53d4b7 RC |
301 | return false; |
302 | ||
75a57075 RC |
303 | if ( !r ) |
304 | return true; | |
305 | ||
306 | double d = v.get_real(); | |
307 | ||
308 | // require no leading or trailing whitespace | |
309 | // and either # | #: | :# | #:# | |
310 | // where # is a valid pos or neg dec, hex, or octal number | |
311 | ||
020356df RC |
312 | const char* t = strchr(r, ':'); |
313 | ||
75a57075 RC |
314 | if ( *r != ':' ) |
315 | { | |
316 | double low = strtod(r, nullptr); | |
317 | ||
318 | if ( d < low ) | |
319 | return false; | |
75a57075 | 320 | |
020356df RC |
321 | if ( !t ) |
322 | return d == low; | |
323 | } | |
75a57075 RC |
324 | |
325 | if ( t && *++t ) | |
326 | { | |
327 | double hi = strtod(t, nullptr); | |
328 | ||
329 | if ( d > hi ) | |
330 | return false; | |
331 | } | |
332 | return true; | |
333 | } | |
334 | ||
98a4cad5 | 335 | static bool valid_string(const Value& v, const char* r) |
75a57075 | 336 | { |
cd53d4b7 RC |
337 | if ( v.get_type() != Value::VT_STR ) |
338 | return false; | |
339 | ||
7af7b4bc RC |
340 | if ( r && !strcmp(r, "(optional)") ) |
341 | return true; | |
342 | ||
020356df | 343 | size_t len = strlen(v.get_string()); |
75a57075 RC |
344 | |
345 | if ( !r ) | |
346 | return len > 0; | |
347 | ||
020356df | 348 | size_t max = strtoul(r, nullptr, 0); |
75a57075 RC |
349 | return len <= max; |
350 | } | |
351 | ||
98a4cad5 | 352 | static bool valid_select(const Value& v, const char* r) |
75a57075 | 353 | { |
cd53d4b7 RC |
354 | if ( v.get_type() != Value::VT_STR ) |
355 | return false; | |
356 | ||
75a57075 RC |
357 | if ( !r ) |
358 | return false; | |
359 | ||
360 | const char* s = v.get_string(); | |
ba277c02 | 361 | const char* t = find(r, s); |
75a57075 RC |
362 | |
363 | if ( !t ) | |
364 | return false; | |
365 | ||
366 | return true; | |
367 | } | |
368 | ||
75a57075 RC |
369 | static bool valid_enum(Value& v, const char* r) |
370 | { | |
cd53d4b7 RC |
371 | if ( v.get_type() != Value::VT_STR ) |
372 | return false; | |
373 | ||
75a57075 RC |
374 | if ( !r ) |
375 | return false; | |
376 | ||
377 | const char* s = v.get_string(); | |
ba277c02 | 378 | const char* t = find(r, s); |
75a57075 RC |
379 | |
380 | if ( !t ) | |
381 | return false; | |
382 | ||
383 | unsigned idx = get_index(r, t); | |
384 | ||
d90f9feb | 385 | v.set_enum(idx); |
75a57075 RC |
386 | return true; |
387 | } | |
388 | ||
75a57075 RC |
389 | static bool valid_multi(Value& v, const char* r) |
390 | { | |
cd53d4b7 RC |
391 | if ( v.get_type() != Value::VT_STR ) |
392 | return false; | |
393 | ||
75a57075 RC |
394 | if ( !r ) |
395 | return false; | |
396 | ||
3b8d4148 | 397 | const string str = v.get_string(); |
75a57075 | 398 | vector<string> list; |
3b8d4148 | 399 | split(str, list); |
75a57075 | 400 | |
020356df | 401 | uint64_t mask = 0; |
75a57075 | 402 | |
d4a79f9a | 403 | for ( const auto& p : list ) |
75a57075 | 404 | { |
ba277c02 | 405 | const char* t = find(r, p.c_str()); |
75a57075 RC |
406 | if ( !t ) |
407 | return false; | |
408 | ||
020356df | 409 | uint64_t idx = get_index(r, t); |
75a57075 RC |
410 | |
411 | if ( idx < Value::mask_bits ) | |
5f341c40 | 412 | mask |= (1ULL << idx); |
75a57075 RC |
413 | } |
414 | v.set_aux(mask); | |
415 | return true; | |
416 | } | |
417 | ||
418 | static bool valid_mac(Value& v, const char*) | |
419 | { | |
cd53d4b7 RC |
420 | if ( v.get_type() != Value::VT_STR ) |
421 | return false; | |
422 | ||
75a57075 RC |
423 | struct addr a; |
424 | ||
425 | if ( addr_pton(v.get_string(), &a) ) | |
426 | return false; | |
427 | ||
428 | if ( a.addr_type == ADDR_TYPE_ETH ) | |
429 | v.set(a.addr_data8, 6); | |
430 | ||
431 | else | |
432 | return false; | |
433 | ||
434 | return true; | |
435 | } | |
436 | ||
437 | static bool valid_ip4(Value& v, const char*) | |
438 | { | |
cd53d4b7 RC |
439 | if ( v.get_type() != Value::VT_STR ) |
440 | return false; | |
441 | ||
75a57075 RC |
442 | uint32_t ip4 = inet_addr(v.get_string()); |
443 | ||
444 | if ( ip4 == INADDR_NONE ) | |
445 | return false; | |
446 | ||
9d1d5865 | 447 | v.set((uint64_t)ip4); |
75a57075 RC |
448 | return true; |
449 | } | |
450 | ||
451 | static bool valid_addr(Value& v, const char*) | |
452 | { | |
cd53d4b7 RC |
453 | if ( v.get_type() != Value::VT_STR ) |
454 | return false; | |
455 | ||
75a57075 RC |
456 | struct addr a; |
457 | ||
458 | if ( addr_pton(v.get_string(), &a) ) | |
459 | return false; | |
466cadab | 460 | |
75a57075 RC |
461 | if ( a.addr_type == ADDR_TYPE_IP ) |
462 | v.set(a.addr_data8, 4); | |
463 | ||
464 | else if ( a.addr_type == ADDR_TYPE_IP6 ) | |
465 | v.set(a.addr_data8, 16); | |
466 | ||
467 | else | |
468 | return false; | |
469 | ||
470 | return true; | |
471 | } | |
472 | ||
473 | static bool valid_bit_list(Value& v, const char* r) | |
474 | { | |
cd53d4b7 RC |
475 | if ( v.get_type() != Value::VT_STR ) |
476 | return false; | |
477 | ||
75a57075 | 478 | string pl = v.get_string(); |
4f14885c | 479 | string bs; |
75a57075 | 480 | |
020356df | 481 | size_t max = r ? strtoul(r, nullptr, 0) : 0; |
75a57075 RC |
482 | assert(max > 0); |
483 | ||
4f14885c RC |
484 | if ( pl == "any" ) |
485 | { | |
486 | bs.assign(max+1, '1'); | |
487 | v.set(bs.c_str()); | |
488 | return true; | |
489 | } | |
490 | stringstream ss(pl); | |
491 | ss >> setbase(0); | |
75a57075 | 492 | |
4f14885c | 493 | bs.assign(max+1, '0'); |
75a57075 RC |
494 | int bit; |
495 | ||
496 | while ( ss >> bit ) | |
497 | { | |
020356df | 498 | if ( bit < 0 || (size_t)bit > max ) |
75a57075 RC |
499 | return false; |
500 | ||
501 | bs[bit] = '1'; | |
502 | } | |
503 | if ( !ss.eof() ) | |
504 | return false; | |
505 | ||
506 | v.set(bs.c_str()); | |
507 | return true; | |
508 | } | |
509 | ||
c06c8d5e | 510 | static bool valid_int_list(Value& v, const char* r) |
511 | { | |
512 | if ( v.get_type() != Value::VT_STR ) | |
513 | return false; | |
514 | ||
515 | string pl = v.get_string(); | |
516 | ||
517 | size_t max = r ? strtoul(r, nullptr, 0) : 0; | |
518 | assert(max < ULONG_MAX); | |
519 | ||
520 | stringstream ss(pl); | |
521 | ss >> setbase(0); | |
522 | ||
523 | int val; | |
524 | ||
525 | while ( ss >> val ) | |
526 | { | |
527 | if ( val < 0 || (r and (size_t)val > max) ) | |
528 | return false; | |
529 | } | |
530 | if ( !ss.eof() ) | |
531 | return false; | |
532 | ||
533 | return true; | |
534 | } | |
535 | ||
020356df RC |
536 | //-------------------------------------------------------------------------- |
537 | // Parameter methods | |
538 | //-------------------------------------------------------------------------- | |
539 | ||
75a57075 RC |
540 | bool Parameter::validate(Value& v) const |
541 | { | |
542 | switch ( type ) | |
543 | { | |
544 | // bool values | |
545 | case PT_BOOL: | |
98ef91c6 | 546 | return valid_bool(v, (const char*)range); |
75a57075 RC |
547 | |
548 | // num values | |
549 | case PT_PORT: | |
550 | if ( !range ) | |
551 | return valid_int(v, "0:65535"); | |
d78bacf8 | 552 | // fall through |
75a57075 | 553 | case PT_INT: |
98ef91c6 | 554 | return valid_int(v, (const char*)range); |
d4ff2e2c RC |
555 | case PT_INTERVAL: |
556 | return valid_interval(v, (const char*)range); | |
75a57075 | 557 | case PT_REAL: |
98ef91c6 | 558 | return valid_real(v, (const char*)range); |
75a57075 RC |
559 | |
560 | // string values | |
561 | case PT_STRING: | |
98ef91c6 | 562 | return valid_string(v, (const char*)range); |
75a57075 | 563 | case PT_SELECT: |
98ef91c6 | 564 | return valid_select(v, (const char*)range); |
75a57075 | 565 | case PT_MULTI: |
98ef91c6 | 566 | return valid_multi(v, (const char*)range); |
75a57075 | 567 | case PT_ENUM: |
98ef91c6 | 568 | return valid_enum(v, (const char*)range); |
7db3d57c | 569 | case PT_DYNAMIC: |
d4a79f9a | 570 | return valid_select(v, (*((const RangeQuery*)range))()); |
75a57075 RC |
571 | |
572 | // address values | |
573 | case PT_MAC: | |
98ef91c6 | 574 | return valid_mac(v, (const char*)range); |
75a57075 | 575 | case PT_IP4: |
98ef91c6 | 576 | return valid_ip4(v, (const char*)range); |
75a57075 | 577 | case PT_ADDR: |
98ef91c6 | 578 | return valid_addr(v, (const char*)range); |
75a57075 RC |
579 | |
580 | // list values | |
581 | case PT_BIT_LIST: | |
98ef91c6 | 582 | return valid_bit_list(v, (const char*)range); |
75a57075 | 583 | |
c06c8d5e | 584 | case PT_INT_LIST: |
585 | return valid_int_list(v, (const char*)range); | |
586 | ||
75a57075 | 587 | case PT_ADDR_LIST: |
c06c8d5e | 588 | // validated by user on sfip_from_string |
589 | // fall through | |
590 | ||
74957388 | 591 | case PT_IMPLIED: |
75a57075 RC |
592 | return true; |
593 | ||
594 | default: | |
595 | break; | |
596 | } | |
597 | return false; | |
598 | } | |
599 | ||
1950cbb0 | 600 | static const char* const pt2str[Parameter::PT_MAX] = |
75a57075 | 601 | { |
7db3d57c | 602 | "table", "list", "dynamic", |
d4ff2e2c | 603 | "bool", "int", "interval", "real", "port", |
75a57075 RC |
604 | "string", "select", "multi", "enum", |
605 | "mac", "ip4", "addr", | |
c06c8d5e | 606 | "bit_list", "int_list", "addr_list", "implied" |
75a57075 RC |
607 | }; |
608 | ||
609 | const char* Parameter::get_type() const | |
610 | { | |
611 | assert(type < Parameter::PT_MAX); | |
612 | return pt2str[type]; | |
613 | } | |
614 | ||
7db3d57c RC |
615 | const char* Parameter::get_range() const |
616 | { | |
617 | switch ( type ) | |
618 | { | |
619 | case PT_TABLE: | |
620 | case PT_LIST: | |
621 | return nullptr; | |
622 | ||
623 | case PT_DYNAMIC: | |
d4a79f9a | 624 | return (*((const RangeQuery*)range))(); |
7db3d57c RC |
625 | |
626 | default: | |
627 | break; | |
628 | } | |
d4a79f9a | 629 | return (const char*)range; |
7db3d57c RC |
630 | } |
631 | ||
b58b2e80 RC |
632 | bool Parameter::get_bool() const |
633 | { | |
634 | if ( !deflt ) | |
635 | return false; | |
636 | ||
637 | return ( strchr(deflt, 't') || strchr(deflt, 'T') ); | |
638 | } | |
639 | ||
640 | double Parameter::get_number() const | |
641 | { | |
642 | if ( !deflt ) | |
643 | return 0; | |
644 | ||
645 | return strtod(deflt, nullptr); | |
646 | } | |
647 | ||
648 | const char* Parameter::get_string() const | |
649 | { | |
650 | return deflt ? deflt : ""; | |
651 | } | |
652 | ||
4b9776eb RC |
653 | const Parameter* Parameter::find(const Parameter* p, const char* s) |
654 | { | |
127945e5 RC |
655 | if ( !p ) |
656 | return nullptr; | |
657 | ||
4b9776eb RC |
658 | while ( p->name ) |
659 | { | |
3a20f1b0 | 660 | if ( !strcmp(p->name, s) || p->is_wild_card() ) |
4b9776eb RC |
661 | return p; |
662 | ++p; | |
663 | } | |
664 | return nullptr; | |
665 | } | |
666 | ||
55dcf17f RC |
667 | int Parameter::index(const char* r, const char* s) |
668 | { | |
669 | const char* t = ::find(r, s); | |
670 | ||
671 | if ( !t ) | |
672 | return -1; | |
673 | ||
674 | unsigned idx = get_index(r, t); | |
675 | return (int)idx; | |
676 | } | |
677 | ||
020356df RC |
678 | //-------------------------------------------------------------------------- |
679 | // valid_* tests | |
680 | // we only test validation here | |
681 | // side effects applied to value are tested elsewhere | |
682 | //-------------------------------------------------------------------------- | |
683 | ||
6659401d MA |
684 | #ifdef CATCH_TEST_BUILD |
685 | ||
686 | #include "catch/catch.hpp" | |
687 | ||
020356df RC |
688 | TEST_CASE("bool", "[Parameter]") |
689 | { | |
690 | Value v(true); | |
9d1d5865 RD |
691 | CHECK(true == valid_bool(v, nullptr)); |
692 | CHECK(true == valid_interval(v, nullptr)); | |
020356df RC |
693 | } |
694 | ||
695 | struct | |
696 | { | |
697 | bool expected; | |
9d1d5865 | 698 | bool (*validate)(Value&, const char*); |
020356df RC |
699 | double value; |
700 | const char* range; | |
701 | } | |
702 | num_tests[] = | |
703 | { | |
704 | // __STRDUMP_DISABLE__ | |
705 | { true, valid_int, 0, nullptr }, | |
706 | { true, valid_int, 0, "" }, | |
707 | { true, valid_int, 0, "0" }, | |
708 | { true, valid_int, 0, "0:" }, | |
709 | { true, valid_int, 0, ":0" }, | |
710 | { true, valid_int, 0, ":1" }, | |
711 | { true, valid_int, 0, "-1:1" }, | |
712 | { true, valid_int, 0, "-1:" }, | |
713 | ||
714 | { false, valid_int, 1, "0" }, | |
715 | { true, valid_int, 1, "0:" }, | |
716 | { false, valid_int, 1, ":0" }, | |
a478eca7 OSSIC |
717 | { true, valid_int, -1, "-1" }, |
718 | { false, valid_int, 2, "-1" }, | |
020356df | 719 | |
49af2b2c RC |
720 | { false, valid_int, 1.5, ":0" }, |
721 | ||
020356df RC |
722 | { true, valid_int, -10, "-11:-9" }, |
723 | { true, valid_int, 10, "9:11" }, | |
724 | { true, valid_int, 10, "0xA:11" }, | |
9d1d5865 RD |
725 | { true, valid_int, -11, ":-11" }, |
726 | { true, valid_int, -30, ":-11" }, | |
727 | { false, valid_int, -10, ":-11" }, | |
728 | { false, valid_int, 10, ":-11" }, | |
020356df | 729 | |
49af2b2c RC |
730 | { true, valid_real, 0.0, nullptr }, |
731 | { true, valid_real, 0.0, "" }, | |
732 | { true, valid_real, 0.0, "0.0" }, | |
733 | { true, valid_real, 0.0, ":0" }, | |
734 | ||
735 | { true, valid_real, 0.1, "0:" }, | |
736 | { true, valid_real, 0.1, ":0.9" }, | |
737 | { true, valid_real, 0.1, "-0.9:0.9" }, | |
738 | { true, valid_real, 0.1, "-0.9:" }, | |
020356df RC |
739 | |
740 | { false, valid_real, 1, "0.9" }, | |
741 | { true, valid_real, 1, "0.9:" }, | |
742 | { false, valid_real, 1, ":0.9" }, | |
743 | ||
744 | { true, valid_real, -10, "-11.1:-9.9" }, | |
745 | { true, valid_real, 10, "9.9:11.1" }, | |
746 | { false, valid_real, 10, "011:11" }, | |
747 | { true, valid_real, 10, "0xA:11" }, | |
748 | ||
749 | { false, nullptr, 0, nullptr } | |
750 | // __STRDUMP_ENABLE__ | |
751 | }; | |
752 | ||
753 | TEST_CASE("num", "[Parameter]") | |
754 | { | |
755 | auto test = num_tests; | |
756 | ||
757 | while ( test->validate ) | |
758 | { | |
759 | Value v(test->value); | |
760 | bool result = test->validate(v, test->range); | |
761 | CHECK(result == test->expected); | |
762 | ++test; | |
763 | } | |
764 | } | |
765 | ||
9d1d5865 RD |
766 | struct |
767 | { | |
768 | bool expected; | |
769 | bool (*validate)(Value&, const char*); | |
770 | const char* value; | |
771 | const char* range; | |
772 | } | |
773 | str_num_tests[] = | |
774 | { | |
775 | // __STRDUMP_DISABLE__ | |
776 | { true, valid_int, "5", "-53:53" }, | |
777 | { true, valid_int, "-9223372036854775808", "-9223372036854775808:9223372036854775807" }, | |
778 | { true, valid_int, "0x7fffffffffffffff", "-9223372036854775808:9223372036854775807" }, | |
779 | { true, valid_int, "9223372036854775806", "-9223372036854775808:9223372036854775807" }, | |
780 | { true, valid_int, "0", "-9223372036854775808:9223372036854775807" }, | |
781 | { false, valid_int, "9223372036854775807", "-9223372036854775808:9223372036854775806" }, | |
782 | { false, valid_int, "-9223372036854775808", "-9223372036854775807:9223372036854775806" }, | |
783 | { true, valid_int, "0", "0:18446744073709551615" }, | |
784 | { true, valid_int, "0", "0:18446744073709551614" }, | |
785 | { true, valid_int, "1024", "0:18446744073709551614" }, | |
786 | { true, valid_int, "18446744073709551614", "0:18446744073709551614" }, | |
787 | { false, valid_int, "18446744073709551615", "0:18446744073709551614" }, | |
788 | { true, valid_int, "18446744073709551615", "18446744073709551615" }, | |
789 | { true, valid_int, "18446744073709551615", "max64" }, | |
790 | { true, valid_int, "4294967295", "max32" }, | |
791 | { false, valid_int, "4294967296", "max32" }, | |
792 | { true, valid_int, "2147483647", "max31" }, | |
793 | { false, valid_int, "2147483648", "max31" }, | |
794 | { true, valid_int, "50", "50:50" }, | |
795 | { false, valid_int, "51", "50:50" }, | |
796 | { true, valid_int, "-50", "-50:-50" }, | |
797 | { false, valid_int, "51", "-50:-50" }, | |
798 | { false, valid_int, "-51", "-50:-50" }, | |
799 | { true, valid_int, "-53", ":-53" }, | |
800 | { true, valid_int, "-54", ":-53" }, | |
801 | { true, valid_int, "-9223372036854775808", ":-53" }, | |
802 | { false, valid_int, "-52", ":-53" }, | |
803 | { false, valid_int, "2", ":-53" }, | |
804 | ||
a478eca7 OSSIC |
805 | { false, valid_int, "", "" }, |
806 | { false, valid_int, "foo", "" }, | |
807 | { false, valid_int, "1a", "" }, | |
808 | { false, valid_int, "a1", "" }, | |
809 | { false, valid_int, " 1", "" }, | |
810 | { false, valid_int, "1 ", "" }, | |
811 | { false, valid_int, "\t\t2", "" }, | |
812 | { false, valid_int, "2\t\t", "" }, | |
813 | { false, valid_int, " -3", "" }, | |
814 | { false, valid_int, "-3 ", "" }, | |
815 | { false, valid_int, "4 5", "" }, | |
816 | { true, valid_int, "+10", "" }, | |
817 | { false, valid_int, "", "0:1" }, | |
818 | { false, valid_int, "foo", "0:1" }, | |
819 | { false, valid_int, "1a", "0:1" }, | |
820 | { false, valid_int, "a1", "0:1" }, | |
821 | { false, valid_int, " 1", "0:1" }, | |
822 | { false, valid_int, "1 ", "0:1" }, | |
823 | { false, valid_int, "\t\t2", "2:3" }, | |
824 | { false, valid_int, "2\t\t", "2:3" }, | |
825 | { false, valid_int, " -3", "-5:5" }, | |
826 | { false, valid_int, "-3 ", "-5:5" }, | |
827 | { false, valid_int, "4 5", "-5:5" }, | |
828 | { true, valid_int, "+10", "0:20" }, | |
829 | ||
830 | { true, valid_int, "max31", "" }, | |
831 | { true, valid_int, "max32", "" }, | |
832 | { true, valid_int, "max53", "" }, | |
833 | { true, valid_int, "max63", "" }, | |
834 | { true, valid_int, "max64", "" }, | |
835 | { true, valid_int, "max31", "max31" }, | |
836 | { false, valid_int, "max32", "max31" }, | |
837 | ||
838 | { false, valid_int, " max32", "" }, | |
839 | { false, valid_int, "max32 ", "" }, | |
840 | { false, valid_int, "maxN", "" }, | |
841 | { false, valid_int, "max31:max32", "" }, | |
842 | ||
9d1d5865 RD |
843 | { true, valid_real, "0.0", "0.0" }, |
844 | { true, valid_real, "0.0", ":0" }, | |
845 | ||
846 | { true, valid_real, "0.1", "0:" }, | |
847 | { true, valid_real, "0.1", ":0.9" }, | |
848 | { true, valid_real, "0.1", "-0.9:0.9" }, | |
849 | { true, valid_real, "0.1", "-0.9:" }, | |
850 | ||
851 | { false, valid_real, "1", "0.9" }, | |
852 | { true, valid_real, "1", "0.9:" }, | |
853 | { false, valid_real, "1", ":0.9" }, | |
854 | ||
855 | { true, valid_real, "-10", "-11.1:-9.9" }, | |
856 | { true, valid_real, "10", "9.9:11.1" }, | |
857 | { false, valid_real, "10", "011:11" }, | |
858 | { true, valid_real, "10", "0xA:11" }, | |
859 | ||
860 | { false, nullptr, 0, nullptr } | |
861 | // __STRDUMP_ENABLE__ | |
862 | }; | |
863 | ||
864 | TEST_CASE("str_num", "[Parameter]") | |
865 | { | |
866 | auto test = str_num_tests; | |
867 | ||
868 | while ( test->validate ) | |
869 | { | |
870 | Value v(test->value); | |
871 | bool result = test->validate(v, test->range); | |
872 | CHECK(result == test->expected); | |
873 | ++test; | |
874 | } | |
875 | } | |
876 | ||
020356df RC |
877 | struct |
878 | { | |
879 | bool expected; | |
98a4cad5 | 880 | bool (*validate)(const Value&, const char*); |
020356df RC |
881 | const char* value; |
882 | const char* range; | |
883 | } | |
98a4cad5 | 884 | const_string_tests[] = |
020356df RC |
885 | { |
886 | // __STRDUMP_DISABLE__ | |
887 | { true, valid_string, "green", "(optional)" }, | |
888 | { true, valid_string, "green", nullptr }, | |
889 | { true, valid_string, "green", "5" }, | |
890 | { true, valid_string, "green", "6" }, | |
891 | { false, valid_string, "green", "4" }, | |
892 | ||
893 | { true, valid_select, "green", "red | green | yellow" }, | |
894 | { false, valid_select, "blue", "red | green | yellow" }, | |
895 | { false, valid_select, "green", nullptr }, | |
896 | ||
a524ad57 | 897 | { false, nullptr, nullptr, nullptr } |
98a4cad5 MA |
898 | // __STRDUMP_ENABLE__ |
899 | }; | |
900 | ||
901 | struct | |
902 | { | |
903 | bool expected; | |
904 | bool (*validate)(Value&, const char*); | |
905 | const char* value; | |
906 | const char* range; | |
907 | } | |
908 | string_tests[] = | |
909 | { | |
910 | // __STRDUMP_DISABLE__ | |
020356df RC |
911 | { true, valid_enum, "green", "red | green | yellow" }, |
912 | { false, valid_enum, "blue", "red | green | yellow" }, | |
913 | { false, valid_enum, "green", nullptr }, | |
914 | ||
915 | { true, valid_multi, "green", "red | green | yellow" }, | |
916 | { true, valid_multi, "red yellow", "red | green | yellow" }, | |
917 | { false, valid_multi, "redgreen", "red | green | yellow" }, | |
918 | { false, valid_multi, "blue", nullptr }, | |
919 | ||
920 | { true, valid_mac, "98:01:a7:9d:d8:41", nullptr }, | |
921 | { false, valid_mac, ":01:a7:9d:d8:41", nullptr }, | |
922 | { false, valid_mac, "01:a7:9d:d8:41", nullptr }, | |
923 | { false, valid_mac, "98:01:a7:9d:d8:419", nullptr }, | |
924 | { false, valid_mac, "98:01:a7:9d:d8:41x", nullptr }, | |
925 | ||
926 | { true, valid_ip4, "1.2.3.4", nullptr }, | |
927 | { true, valid_ip4, "1.2.3", nullptr }, | |
928 | { false, valid_ip4, "1.2.3.", nullptr }, | |
929 | { false, valid_ip4, "1.2.x", nullptr }, | |
930 | ||
931 | { true, valid_addr, "1.2.3.4", nullptr }, | |
932 | { true, valid_addr, "1.2.3.4/32", nullptr }, | |
933 | { true, valid_addr, "1.2.3.4/0", nullptr }, | |
934 | { false, valid_addr, "1.2.3.4/33", nullptr }, | |
935 | { false, valid_addr, "1.2.0x.4/33", nullptr }, | |
936 | ||
937 | { true, valid_addr, "2001:420:c0c4:1004::157", nullptr }, | |
938 | { true, valid_addr, "2001:420:c0c4:1004::157/128", nullptr }, | |
939 | { true, valid_addr, "2001:420:c0c4:1004::157/0", nullptr }, | |
940 | { false, valid_addr, "2001:420:c0c4:1004:0x:157/256", nullptr }, | |
941 | ||
942 | { true, valid_bit_list, "1 2", "3" }, | |
943 | { true, valid_bit_list, "1 2 3", "3" }, | |
944 | { false, valid_bit_list, "1 2 3 4", "3" }, | |
945 | { false, valid_bit_list, "128", "3" }, | |
946 | ||
c06c8d5e | 947 | { true, valid_int_list, "0 65535", nullptr }, |
948 | { true, valid_int_list, "0 65535", "65535" }, | |
949 | { false, valid_int_list, "-1", "1" }, | |
950 | { false, valid_int_list, "65535", "1" }, | |
951 | { false, valid_int_list, "1", "0" }, | |
952 | { true, valid_int_list, "0", "0" }, | |
953 | ||
a524ad57 | 954 | { false, nullptr, nullptr, nullptr } |
020356df RC |
955 | // __STRDUMP_ENABLE__ |
956 | }; | |
957 | ||
958 | TEST_CASE("string", "[Parameter]") | |
959 | { | |
98a4cad5 MA |
960 | auto ctest = const_string_tests; |
961 | while ( ctest->validate ) | |
962 | { | |
963 | Value v(ctest->value); | |
964 | bool result = ctest->validate(v, ctest->range); | |
965 | CHECK(result == ctest->expected); | |
966 | ++ctest; | |
967 | } | |
020356df | 968 | |
98a4cad5 | 969 | auto test = string_tests; |
020356df RC |
970 | while ( test->validate ) |
971 | { | |
972 | Value v(test->value); | |
973 | bool result = test->validate(v, test->range); | |
974 | CHECK(result == test->expected); | |
975 | ++test; | |
976 | } | |
977 | } | |
978 | ||
979 | TEST_CASE("max", "[Parameter]") | |
980 | { | |
f990379d BT |
981 | CHECK(Parameter::get_int("max31") == 2147483647); |
982 | CHECK(Parameter::get_int("max32") == 4294967295); | |
983 | CHECK(Parameter::get_int("max53") == 9007199254740992); | |
9d1d5865 RD |
984 | CHECK(Parameter::get_int("max63") == 9223372036854775807); |
985 | CHECK(Parameter::get_int("max64") == -1); | |
020356df RC |
986 | |
987 | if ( sizeof(size_t) == 4 ) | |
f990379d | 988 | CHECK(Parameter::get_int("maxSZ") == 4294967295); |
020356df | 989 | else |
f990379d | 990 | CHECK(Parameter::get_int("maxSZ") == 9007199254740992); |
9d1d5865 RD |
991 | |
992 | CHECK(Parameter::get_uint("max31") == 2147483647); | |
993 | CHECK(Parameter::get_uint("max32") == 4294967295); | |
994 | CHECK(Parameter::get_uint("max53") == 9007199254740992); | |
995 | CHECK(Parameter::get_uint("max63") == 9223372036854775807); | |
996 | CHECK(Parameter::get_uint("max64") == 18446744073709551615ULL); | |
997 | ||
998 | if ( sizeof(size_t) == 4 ) | |
999 | CHECK(Parameter::get_uint("maxSZ") == 4294967295); | |
1000 | else | |
1001 | CHECK(Parameter::get_uint("maxSZ") == 9007199254740992); | |
020356df | 1002 | } |
6659401d | 1003 | |
020356df RC |
1004 | #endif |
1005 |