00001 //--------------------------------------------------------------------------
00002 // Copyright (C) 2014-2017 Cisco and/or its affiliates. All rights reserved.
00003 // Copyright (C) 2002-2013 Sourcefire, Inc.
00004 // Copyright (C) 1998-2002 Martin Roesch <roesch@sourcefire.com>
00005 //
00006 // This program is free software; you can redistribute it and/or modify it
00007 // under the terms of the GNU General Public License Version 2 as published
00008 // by the Free Software Foundation. You may not use, modify or distribute
00009 // this program under any other version of the GNU General Public License.
00010 //
00011 // This program is distributed in the hope that it will be useful, but
00012 // WITHOUT ANY WARRANTY; without even the implied warranty of
00013 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00014 // General Public License for more details.
00015 //
00016 // You should have received a copy of the GNU General Public License along
00017 // with this program; if not, write to the Free Software Foundation, Inc.,
00018 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019 //--------------------------------------------------------------------------
00020
00021 #ifdef HAVE_CONFIG_H
00022 #include "config.h"
00023 #endif
00024
00025 #include "detection/detection_defines.h"
00026 #include "detection/pattern_match_data.h"
00027 #include "framework/cursor.h"
00028 #include "framework/ips_option.h"
00029 #include "framework/module.h"
00030 #include "hash/sfhashfcn.h"
00031 #include "log/messages.h"
00032 #include "parser/parse_utils.h"
00033 #include "profiler/profiler.h"
00034 #include "utils/boyer_moore.h"
00035 #include "utils/util.h"
00036
00037 #include "extract.h"
00038
00039 #define MAX_PATTERN_SIZE 2048
00040
00041 #define s_name "content"
00042
00043 static THREAD_LOCAL ProfileStats contentPerfStats;
00044
00045 static int CheckANDPatternMatch(struct ContentData*, Cursor&);
00046
00047 //-------------------------------------------------------------------------
00048 // instance data
00049 //-------------------------------------------------------------------------
00050
00051 struct ContentData
00052 {
00053 PatternMatchData pmd;
00054
00055 int8_t offset_var; /* byte_extract variable indices for offset, */
00056 int8_t depth_var; /* depth, distance, within */
00057
00058 unsigned match_delta; /* Maximum distance we can jump to search for this pattern again. */
00059
00060 int* skip_stride; /* B-M skip array */
00061 int* shift_stride; /* B-M shift array */
00062
00063 void init();
00064 void setup_bm();
00065 void set_max_jump_size();
00066 };
00067
00068 void ContentData::init()
00069 {
00070 offset_var = IPS_OPTIONS_NO_VAR;
00071 depth_var = IPS_OPTIONS_NO_VAR;
00072 }
00073
00074 void ContentData::setup_bm()
00075 {
00076 skip_stride = make_skip(pmd.pattern_buf, pmd.pattern_size);
00077 shift_stride = make_shift(pmd.pattern_buf, pmd.pattern_size);
00078 }
00079
00080 // find the maximum number of characters we can jump ahead
00081 // from the current offset when checking for this pattern again
00082
00083 void ContentData::set_max_jump_size()
00084 {
00085 unsigned j = 0;
00086
00087 for ( unsigned i = 1; i < pmd.pattern_size; i++ )
00088 {
00089 if ( pmd.pattern_buf[j] != pmd.pattern_buf[i] )
00090 {
00091 j = 0;
00092 continue;
00093 }
00094 if ( i == (pmd.pattern_size - 1) )
00095 {
00096 match_delta = pmd.pattern_size - j - 1;
00097 return;
00098 }
00099 j++;
00100 }
00101 match_delta = pmd.pattern_size;
00102 }
00103
00104 //-------------------------------------------------------------------------
00105 // rule option
00106 //-------------------------------------------------------------------------
00107
00108 class ContentOption : public IpsOption
00109 {
00110 public:
00111 ContentOption(ContentData* c) : IpsOption(s_name, RULE_OPTION_TYPE_CONTENT)
00112 { config = c; }
00113
00114 ~ContentOption() override;
00115
00116 uint32_t hash() const override;
00117 bool operator==(const IpsOption&) const override;
00118
00119 CursorActionType get_cursor_type() const override
00120 { return CAT_ADJUST; }
00121
00122 bool is_relative() override
00123 { return config->pmd.is_relative(); }
00124
00125 bool retry() override
00126 { return !config->pmd.is_negated(); }
00127
00128 ContentData* get_data()
00129 { return config; }
00130
00131 void set_data(ContentData* cd)
00132 { config = cd; }
00133
00134 int eval(Cursor& c, Packet*) override
00135 { return CheckANDPatternMatch(config, c); }
00136
00137 PatternMatchData* get_pattern(int, RuleDirection) override
00138 { return &config->pmd; }
00139
00140 protected:
00141 ContentData* config;
00142 };
00143
00144 ContentOption::~ContentOption()
00145 {
00146 ContentData* cd = config;
00147
00148 if ( !cd )
00149 return;
00150
00151 if ( cd->pmd.pattern_buf )
00152 snort_free(const_cast<char*>(cd->pmd.pattern_buf));
00153
00154 if ( cd->pmd.last_check )
00155 snort_free(cd->pmd.last_check);
00156
00157 if ( cd->skip_stride )
00158 snort_free(cd->skip_stride);
00159
00160 if ( cd->shift_stride )
00161 snort_free(cd->shift_stride);
00162
00163 snort_free(cd);
00164 }
00165
00166 uint32_t ContentOption::hash() const
00167 {
00168 uint32_t a,b,c;
00169 const ContentData* cd = config;
00170
00171 a = cd->pmd.flags;
00172 b = cd->pmd.offset;
00173 c = cd->pmd.depth;
00174
00175 mix(a,b,c);
00176
00177 a += cd->pmd.pattern_size;
00178 b += cd->pmd.fp_offset;
00179 c += cd->pmd.fp_length;
00180
00181 mix(a,b,c);
00182
00183 if ( cd->pmd.pattern_size )
00184 mix_str(a, b, c, cd->pmd.pattern_buf, cd->pmd.pattern_size);
00185
00186 mix_str(a,b,c,get_name());
00187
00188 a += cd->depth_var;
00189 b += cd->offset_var;
00190 c += cd->match_delta;
00191
00192 mix(a,b,c);
00193 finalize(a,b,c);
00194
00195 return c;
00196 }
00197
00198 #if 0
00199 // see below for why this is disabled
00200 static bool same_buffers(
00201 unsigned len1, const char* buf1, bool no_case1,
00202 unsigned len2, const char* buf2, bool no_case2)
00203 {
00204 /* Sizes will be most different, check that first */
00205 if ( len1 != len2 or no_case1 != no_case2 )
00206 return false;
00207
00208 if ( !len1 )
00209 return true;
00210
00211 /* Next compare the patterns for uniqueness */
00212 if ( no_case1 )
00213 {
00214 /* If no_case is set, do case insensitive compare on pattern */
00215 for ( unsigned i = 0; i < len1; ++i )
00216 {
00217 if ( toupper(buf1[i]) != toupper(buf2[i]) )
00218 return false;
00219 }
00220 }
00221 else
00222 {
00223 /* If no_case is not set, do case sensitive compare on pattern */
00224 if ( memcmp(buf1, buf2, len1) )
00225 return false;
00226 }
00227 return true;
00228 }
00229
00230 #endif
00231
00232 // FIXIT-P FAST_PAT and fp_only are set after hash table comparisons so this must
00233 // return this == &ips to avoid unnecessary reevaluation and false positives.
00234 // when this is fixed, add PatternMatchData::operator==().
00235 bool ContentOption::operator==(const IpsOption& ips) const
00236 {
00237 #if 0
00238 if ( !IpsOption::operator==(ips) )
00239 return false;
00240
00241 ContentOption& rhs = (ContentOption&)ips;
00242 const ContentData& left = *config;
00243 const ContentData& right = *rhs.config;
00244
00245 if ( !same_buffers(left.pmd.pattern_size, left.pmd.pattern_buf, left.pmd.is_no_case(),
00246 right.pmd.pattern_size, right.pmd.pattern_buf, right.pmd.is_no_case()) )
00247 return false;
00248
00249 /* Now check the rest of the options */
00250 if ((left.pmd.flags == right.pmd.flags) &&
00251 (left.pmd.fp_offset == right.pmd.fp_offset) &&
00252 (left.pmd.fp_length == right.pmd.fp_length) &&
00253 (left.pmd.offset == right.pmd.offset) &&
00254 (left.pmd.depth == right.pmd.depth) &&
00255 // pattern_size and pattern_buf already checked
00256 // pm_type set later (but determined by CAT)
00257 (left.match_delta == right.match_delta) &&
00258 (left.offset_var == right.offset_var) &&
00259 (left.depth_var == right.depth_var) )
00260 {
00261 return true;
00262 }
00263 #endif
00264 return this == &ips;
00265 }
00266
00267 //-------------------------------------------------------------------------
00268 // runtime functions
00269 //-------------------------------------------------------------------------
00270
00271 /*
00272 * single search function.
00273 *
00274 * return 1 for found
00275 * return 0 for not found
00276 * return -1 for error (search out of bounds)
00277 */
00278 static int uniSearchReal(ContentData* cd, Cursor& c)
00279 {
00280 int offset, depth;
00281
00282 /* Get byte_extract variables */
00283 if (cd->offset_var >= 0 && cd->offset_var < NUM_IPS_OPTIONS_VARS)
00284 {
00285 uint32_t extract;
00286 GetVarValueByIndex(&extract, cd->offset_var);
00287 offset = (int)extract;
00288 }
00289 else
00290 offset = cd->pmd.offset;
00291
00292 if (cd->depth_var >= 0 && cd->depth_var < NUM_IPS_OPTIONS_VARS)
00293 {
00294 uint32_t extract;
00295 GetVarValueByIndex(&extract, cd->depth_var);
00296 depth = (int)extract;
00297 }
00298 else
00299 depth = cd->pmd.depth;
00300
00301 int pos = c.get_delta();
00302
00303 if ( !pos )
00304 {
00305 if ( cd->pmd.is_relative() )
00306 pos = c.get_pos();
00307
00308 pos += offset;
00309 }
00310
00311 if ( pos < 0 )
00312 pos = 0;
00313
00314 int len = c.size() - pos;
00315
00316 if ( !depth || len < depth )
00317 depth = len;
00318
00319 unsigned end = pos + cd->pmd.pattern_size;
00320
00321 // If the pattern size is greater than the amount of data we have to
00322 // search, there's no way we can match, but return 0 here for the
00323 // case where the match is inverted and there is at least some data.
00324 if ( end > c.size() || (int)end > pos + depth )
00325 {
00326 if ( cd->pmd.is_negated() && (depth > 0) )
00327 return 0;
00328
00329 return -1;
00330 }
00331
00332 const uint8_t* base = c.buffer() + pos;
00333 int found;
00334
00335 if ( cd->pmd.is_no_case() )
00336 {
00337 found = mSearchCI(
00338 (const char*)base, depth, cd->pmd.pattern_buf, cd->pmd.pattern_size,
00339 cd->skip_stride, cd->shift_stride);
00340 }
00341 else
00342 {
00343 found = mSearch(
00344 (const char*)base, depth, cd->pmd.pattern_buf, cd->pmd.pattern_size,
00345 cd->skip_stride, cd->shift_stride);
00346 }
00347
00348 if ( found >= 0 )
00349 {
00350 int at = pos + found;
00351 c.set_delta(at + cd->match_delta);
00352 c.set_pos(at + cd->pmd.pattern_size);
00353 return 1;
00354 }
00355
00356 return 0;
00357 }
00358
00359 static int CheckANDPatternMatch(ContentData* idx, Cursor& c)
00360 {
00361 Profile profile(contentPerfStats);
00362
00363 DebugMessage(DEBUG_PATTERN_MATCH, "CheckPatternANDMatch: ");
00364
00365 int found = uniSearchReal(idx, c);
00366
00367 if ( found == -1 )
00368 {
00369 /* On error, mark as not found. This is necessary to handle !content
00370 cases. In that case, a search that is outside the given buffer will
00371 return 0, and !0 is 1, so a !content out of bounds will return true,
00372 which is not what we want. */
00373 found = 0;
00374 }
00375 else
00376 {
00377 found ^= idx->pmd.is_negated();
00378 }
00379
00380 if ( found )
00381 {
00382 DebugMessage(DEBUG_PATTERN_MATCH, "Pattern match found\n");
00383 return DETECTION_OPTION_MATCH;
00384 }
00385 else
00386 {
00387 DebugMessage(DEBUG_PATTERN_MATCH, "Pattern match failed\n");
00388 return DETECTION_OPTION_NO_MATCH;
00389 }
00390 }
00391
00392 //-------------------------------------------------------------------------
00393 // helper foo
00394 //-------------------------------------------------------------------------
00395
00396 typedef enum
00397 {
00398 CMF_DISTANCE = 0x1, CMF_WITHIN = 0x2, CMF_OFFSET = 0x4, CMF_DEPTH = 0x8
00399 } ContentModifierFlags;
00400
00401 static unsigned GetCMF(ContentData* cd)
00402 {
00403 unsigned cmf = 0;
00404 if ( (cd->pmd.offset != 0) || (cd->offset_var != -1) )
00405 cmf |= CMF_OFFSET;
00406 if ( (cd->pmd.depth != 0) || (cd->depth_var != -1) )
00407 cmf |= CMF_DEPTH;
00408 return cmf;
00409 }
00410
00411 #define BAD_DISTANCE (CMF_DISTANCE | CMF_OFFSET | CMF_DEPTH)
00412 #define BAD_WITHIN (CMF_WITHIN | CMF_OFFSET | CMF_DEPTH)
00413 #define BAD_OFFSET (CMF_OFFSET | CMF_DISTANCE | CMF_WITHIN)
00414 #define BAD_DEPTH (CMF_DEPTH | CMF_DISTANCE | CMF_WITHIN)
00415
00416 //-------------------------------------------------------------------------
00417 // parsing methods
00418 //-------------------------------------------------------------------------
00419
00420 static void parse_content(ContentData* cd, const char* rule)
00421 {
00422 bool negated;
00423 std::string buf;
00424
00425 if ( !parse_byte_code(rule, negated, buf) )
00426 return;
00427
00428 const char* tmp_buf = buf.c_str();
00429 unsigned dummy_size = buf.size();
00430
00431 char* pattern_buf = (char*)snort_alloc(dummy_size+1);
00432 memcpy(pattern_buf, tmp_buf, dummy_size);
00433 pattern_buf[dummy_size] = '\0';
00434
00435 cd->pmd.pattern_buf = pattern_buf;
00436 cd->pmd.pattern_size = dummy_size;
00437
00438 cd->pmd.set_literal();
00439
00440 if ( negated )
00441 cd->pmd.set_negated();
00442
00443 cd->set_max_jump_size();
00444 }
00445
00446 static void parse_offset(ContentData* cd, const char* data)
00447 {
00448 if ( GetCMF(cd) & BAD_OFFSET && cd->pmd.is_relative() )
00449 {
00450 ParseError("offset can't be used with itself, distance, or within");
00451 return;
00452 }
00453
00454 if (data == nullptr)
00455 {
00456 ParseError("missing argument to 'offset' option");
00457 return;
00458 }
00459
00460 if (isdigit(data[0]) || data[0] == '-')
00461 {
00462 cd->pmd.offset = parse_int(data, "offset");
00463 cd->offset_var = IPS_OPTIONS_NO_VAR;
00464 }
00465 else
00466 {
00467 cd->offset_var = GetVarByName(data);
00468 if (cd->offset_var == IPS_OPTIONS_NO_VAR)
00469 {
00470 ParseError(INVALID_VAR_ERR_STR, "content offset", data);
00471 return;
00472 }
00473 }
00474
00475 DebugFormat(DEBUG_PARSER, "Pattern offset = %d\n", cd->pmd.offset);
00476 }
00477
00478 static void parse_depth(ContentData* cd, const char* data)
00479 {
00480 if ( GetCMF(cd) & BAD_DEPTH && cd->pmd.is_relative() )
00481 {
00482 ParseError("depth can't be used with itself, distance, or within");
00483 return;
00484 }
00485
00486 if (data == nullptr)
00487 {
00488 ParseError("missing argument to 'depth' option");
00489 return;
00490 }
00491
00492 if (isdigit(data[0]) || data[0] == '-')
00493 {
00494 cd->pmd.depth = parse_int(data, "depth");
00495
00496 /* check to make sure that this the depth allows this rule to fire */
00497 if (cd->pmd.depth < (int)cd->pmd.pattern_size)
00498 {
00499 ParseError("the depth (%d) is less than the size of the content(%u)",
00500 cd->pmd.depth, cd->pmd.pattern_size);
00501 return;
00502 }
00503 cd->depth_var = IPS_OPTIONS_NO_VAR;
00504 }
00505 else
00506 {
00507 cd->depth_var = GetVarByName(data);
00508 if (cd->depth_var == IPS_OPTIONS_NO_VAR)
00509 {
00510 ParseError(INVALID_VAR_ERR_STR, "content depth", data);
00511 return;
00512 }
00513 }
00514
00515 DebugFormat(DEBUG_PATTERN_MATCH, "Pattern depth = %d\n", cd->pmd.depth);
00516 }
00517
00518 static void parse_distance(ContentData* cd, const char* data)
00519 {
00520 if ( GetCMF(cd) & BAD_DISTANCE && !cd->pmd.is_relative() )
00521 {
00522 ParseError("distance can't be used with itself, offset, or depth");
00523 return;
00524 }
00525
00526 if (data == nullptr)
00527 {
00528 ParseError("missing argument to 'distance' option");
00529 return;
00530 }
00531
00532 if (isdigit(data[0]) || data[0] == '-')
00533 {
00534 cd->pmd.offset = parse_int(data, "distance");
00535 cd->offset_var = IPS_OPTIONS_NO_VAR;
00536 }
00537 else
00538 {
00539 cd->offset_var = GetVarByName(data);
00540 if (cd->offset_var == IPS_OPTIONS_NO_VAR)
00541 {
00542 ParseError(INVALID_VAR_ERR_STR, "content distance", data);
00543 return;
00544 }
00545 }
00546
00547 cd->pmd.set_relative();
00548 }
00549
00550 static void parse_within(ContentData* cd, const char* data)
00551 {
00552 if ( GetCMF(cd) & BAD_WITHIN && !cd->pmd.is_relative() )
00553 {
00554 ParseError("within can't be used with itself, offset, or depth");
00555 return;
00556 }
00557
00558 if (data == nullptr)
00559 {
00560 ParseError("missing argument to 'within' option");
00561 return;
00562 }
00563
00564 if (isdigit(data[0]) || data[0] == '-')
00565 {
00566 cd->pmd.depth = parse_int(data, "within");
00567
00568 if (cd->pmd.depth < (int)cd->pmd.pattern_size)
00569 {
00570 ParseError("within (%d) is smaller than size of pattern", cd->pmd.depth);
00571 return;
00572 }
00573 cd->depth_var = IPS_OPTIONS_NO_VAR;
00574 }
00575 else
00576 {
00577 cd->depth_var = GetVarByName(data);
00578 if (cd->depth_var == IPS_OPTIONS_NO_VAR)
00579 {
00580 ParseError(INVALID_VAR_ERR_STR, "content within", data);
00581 return;
00582 }
00583 }
00584
00585 DebugFormat(DEBUG_PATTERN_MATCH, "Pattern within = %d\n", cd->pmd.depth);
00586
00587 cd->pmd.set_relative();
00588 }
00589
00590 //-------------------------------------------------------------------------
00591 // module
00592 //-------------------------------------------------------------------------
00593
00594 static const Parameter s_params[] =
00595 {
00596 { "~data", Parameter::PT_STRING, nullptr, nullptr,
00597 "data to match" },
00598
00599 { "nocase", Parameter::PT_IMPLIED, nullptr, nullptr,
00600 "case insensitive match" },
00601
00602 { "fast_pattern", Parameter::PT_IMPLIED, nullptr, nullptr,
00603 "use this content in the fast pattern matcher instead of the content selected by default" },
00604
00605 { "fast_pattern_offset", Parameter::PT_INT, "0:", "0",
00606 "number of leading characters of this content the fast pattern matcher should exclude" },
00607
00608 { "fast_pattern_length", Parameter::PT_INT, "1:", nullptr,
00609 "maximum number of characters from this content the fast pattern matcher should use" },
00610
00611 { "offset", Parameter::PT_STRING, nullptr, nullptr,
00612 "var or number of bytes from start of buffer to start search" },
00613
00614 { "depth", Parameter::PT_STRING, nullptr, nullptr,
00615 "var or maximum number of bytes to search from beginning of buffer" },
00616
00617 { "distance", Parameter::PT_STRING, nullptr, nullptr,
00618 "var or number of bytes from cursor to start search" },
00619
00620 { "within", Parameter::PT_STRING, nullptr, nullptr,
00621 "var or maximum number of bytes to search from cursor" },
00622
00623 { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
00624 };
00625
00626 #define s_help \
00627 "payload rule option for basic pattern matching"
00628
00629 class ContentModule : public Module
00630 {
00631 public:
00632 ContentModule() : Module(s_name, s_help, s_params)
00633 { cd = nullptr; }
00634
00635 ~ContentModule() override
00636 { delete cd; }
00637
00638 bool begin(const char*, int, SnortConfig*) override;
00639 bool end(const char*, int, SnortConfig*) override;
00640 bool set(const char*, Value&, SnortConfig*) override;
00641
00642 ProfileStats* get_profile() const override
00643 { return &contentPerfStats; }
00644
00645 ContentData* get_data();
00646
00647 Usage get_usage() const override
00648 { return DETECT; }
00649
00650 private:
00651 ContentData* cd;
00652 };
00653
00654 ContentData* ContentModule::get_data()
00655 {
00656 ContentData* tmp = cd;
00657 cd = nullptr;
00658 return tmp;
00659 }
00660
00661 bool ContentModule::begin(const char*, int, SnortConfig*)
00662 {
00663 cd = (ContentData*)snort_calloc(sizeof(ContentData));
00664 cd->init();
00665 return true;
00666 }
00667
00668 bool ContentModule::end(const char*, int, SnortConfig*)
00669 {
00670 if ( (int)cd->pmd.pattern_size <= cd->pmd.fp_offset )
00671 {
00672 ParseError(
00673 "fast_pattern_offset must be less "
00674 "than the actual pattern length which is %u.",
00675 cd->pmd.pattern_size);
00676 return false;
00677 }
00678 if ( (int)cd->pmd.pattern_size < (cd->pmd.fp_offset + cd->pmd.fp_length) )
00679 {
00680 ParseError(
00681 "fast_pattern_offset + fast_pattern_length must be less "
00682 "than or equal to the actual pattern length which is %u.",
00683 cd->pmd.pattern_size);
00684 return false;
00685 }
00686 if ( cd->pmd.is_no_case() )
00687 {
00688 char* s = const_cast<char*>(cd->pmd.pattern_buf);
00689
00690 for ( unsigned i = 0; i < cd->pmd.pattern_size; i++ )
00691 s[i] = toupper(cd->pmd.pattern_buf[i]);
00692 }
00693 cd->setup_bm();
00694 return true;
00695 }
00696
00697 bool ContentModule::set(const char*, Value& v, SnortConfig*)
00698 {
00699 if ( v.is("~data") )
00700 parse_content(cd, v.get_string());
00701
00702 else if ( v.is("offset") )
00703 parse_offset(cd, v.get_string());
00704
00705 else if ( v.is("depth") )
00706 parse_depth(cd, v.get_string());
00707
00708 else if ( v.is("distance") )
00709 parse_distance(cd, v.get_string());
00710
00711 else if ( v.is("within") )
00712 parse_within(cd, v.get_string());
00713
00714 else if ( v.is("nocase") )
00715 cd->pmd.set_no_case();
00716
00717 else if ( v.is("fast_pattern") )
00718 cd->pmd.set_fast_pattern();
00719
00720 else if ( v.is("fast_pattern_offset") )
00721 {
00722 cd->pmd.fp_offset = v.get_long();
00723 cd->pmd.set_fast_pattern();
00724 }
00725 else if ( v.is("fast_pattern_length") )
00726 {
00727 cd->pmd.fp_length = v.get_long();
00728 cd->pmd.set_fast_pattern();
00729 }
00730 else
00731 return false;
00732
00733 return true;
00734 }
00735
00736 //-------------------------------------------------------------------------
00737 // api methods
00738 //-------------------------------------------------------------------------
00739
00740 static Module* mod_ctor()
00741 {
00742 return new ContentModule;
00743 }
00744
00745 static void mod_dtor(Module* m)
00746 {
00747 delete m;
00748 }
00749
00750 static IpsOption* content_ctor(Module* p, OptTreeNode*)
00751 {
00752 ContentModule* m = (ContentModule*)p;
00753 ContentData* cd = m->get_data();
00754 return new ContentOption(cd);
00755 }
00756
00757 static void content_dtor(IpsOption* p)
00758 {
00759 delete p;
00760 }
00761
00762 static const IpsApi content_api =
00763 {
00764 {
00765 PT_IPS_OPTION,
00766 sizeof(IpsApi),
00767 IPSAPI_VERSION,
00768 0,
00769 API_RESERVED,
00770 API_OPTIONS,
00771 s_name,
00772 s_help,
00773 mod_ctor,
00774 mod_dtor
00775 },
00776 OPT_TYPE_DETECTION,
00777 0, 0,
00778 nullptr,
00779 nullptr,
00780 nullptr,
00781 nullptr,
00782 content_ctor,
00783 content_dtor,
00784 nullptr
00785 };
00786
00787 // FIXIT-L need boyer_moore.cc funcs but they
00788 // aren't otherwise called
00789 //#ifdef BUILDING_SO
00790 //SO_PUBLIC const BaseApi* snort_plugins[] =
00791 //{
00792 // &content_api.base,
00793 // nullptr
00794 //};
00795 //#else
00796 const BaseApi* ips_content = &content_api.base;
00797 //#endif
00798
END OF CODE