}
return p;
}
-
-const char* HexBook::find_spell(
- const uint8_t* data, unsigned len, unsigned max, const MagicPage*& p) const
-{
- if ( max and len > max )
- len = max;
-
- p = find_spell(data, len, p, 0);
-
- if ( p and !p->value.empty() )
- return p->value.c_str();
-
- return nullptr;
-}
-
#include "magic.h"
+#include <cassert>
+
MagicPage::MagicPage(const MagicBook& b) : book(b)
{
for ( int i = 0; i < 256; ++i )
delete any;
}
+const char* MagicBook::find_spell(const uint8_t* data, unsigned len, const MagicPage*& p) const
+{
+ assert(p);
+
+ p = find_spell(data, len, p, 0);
+
+ if ( p && !p->value.empty() )
+ return p->value.c_str();
+
+ return nullptr;
+}
+
MagicBook::MagicBook()
{ root = new MagicPage(*this); }
MagicBook& operator=(const MagicBook&) = delete;
virtual bool add_spell(const char* key, const char*& val) = 0;
- virtual const char* find_spell(const uint8_t*, unsigned len, unsigned max,
- const MagicPage*&) const = 0;
+ virtual const char* find_spell(const uint8_t*, unsigned len, const MagicPage*&) const;
- const MagicPage* page1()
+ const MagicPage* page1() const
{ return root; }
+ virtual void set_bookmark(const MagicPage* page = nullptr) const
+ { (void)page; }
+ virtual const MagicPage* get_bookmark() const
+ { return nullptr; }
+
protected:
MagicBook();
MagicPage* root;
+
+ virtual const MagicPage* find_spell(const uint8_t*, unsigned,
+ const MagicPage*, unsigned) const = 0;
};
//-------------------------------------------------------------------------
SpellBook();
bool add_spell(const char*, const char*&) override;
- const char* find_spell(const uint8_t*, unsigned len, unsigned max,
- const MagicPage*&) const override;
+
+ void set_bookmark(const MagicPage* page = nullptr) const override
+ { glob = page; }
+
+ const MagicPage* get_bookmark() const override
+ { return glob; }
private:
bool translate(const char*, HexVector&);
void add_spell(const char*, const char*, HexVector&, unsigned, MagicPage*);
- const MagicPage* find_spell(const uint8_t*, unsigned, const MagicPage*, unsigned) const;
+ const MagicPage* find_spell(const uint8_t*, unsigned, const MagicPage*, unsigned) const override;
+
+ mutable const MagicPage* glob;
};
//-------------------------------------------------------------------------
HexBook() = default;
bool add_spell(const char*, const char*&) override;
- const char* find_spell(const uint8_t*, unsigned len, unsigned,
- const MagicPage*&) const override;
private:
bool translate(const char*, HexVector&);
void add_spell(const char*, const char*, HexVector&, unsigned, MagicPage*);
- const MagicPage* find_spell(const uint8_t*, unsigned, const MagicPage*, unsigned) const;
+ const MagicPage* find_spell(const uint8_t*, unsigned, const MagicPage*, unsigned) const override;
};
#endif
#define WILD 0x100
-SpellBook::SpellBook()
+SpellBook::SpellBook() : glob(nullptr)
{
// allows skipping leading whitespace only
root->next[(int)' '] = root;
while ( i < n )
{
if ( const MagicPage* q = find_spell(s, n, p->any, i) )
+ {
+ glob = q->any ? q : p;
return q;
+ }
++i;
}
- return p->any;
+ return p;
}
+
+ // If no match but has glob, continue lookup from glob
+ if ( p->value.empty() && glob )
+ {
+ p = glob;
+ glob = nullptr;
+
+ return find_spell(s, n, p, i);
+ }
+
return p->value.empty() ? nullptr : p;
}
return p;
}
-
-const char* SpellBook::find_spell(
- const uint8_t* data, unsigned len, unsigned max, const MagicPage*& p) const
-{
- assert(p);
-
- if ( max and len > max )
- len = max;
-
- p = find_spell(data, len, p, 0);
-
- if ( p and !p->value.empty() )
- return p->value.c_str();
-
- return nullptr;
-}
-
{ "curses", Parameter::PT_MULTI, "dce_smb | dce_udp | dce_tcp | sslv2", nullptr,
"enable service identification based on internal algorithm" },
- { "max_pattern", Parameter::PT_INT, "0:65535", "64",
- "maximum scan depth per segment (0 is unlimited)" },
+ { "max_search_depth", Parameter::PT_INT, "0:65535", "64",
+ "maximum scan depth per flow" },
{ nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
};
else if ( v.is("curses") )
curses->add_curse(v.get_string());
- else if ( v.is("max_pattern") )
- max_pattern = v.get_uint16();
+ else if ( v.is("max_search_depth") )
+ max_search_depth = v.get_uint16();
return true;
}
MagicBook* get_book(bool c2s, bool hex);
CurseBook* get_curse_book();
- uint16_t get_max_pattern()
- { return max_pattern; }
+ uint16_t get_max_search_depth()
+ { return max_search_depth; }
Usage get_usage() const override
{ return INSPECT; }
MagicBook* s2c_spells = nullptr;
CurseBook* curses = nullptr;
- uint16_t max_pattern = 0;
+ uint16_t max_search_depth = 0;
};
#endif
private:
Wizard* wizard;
Wand wand;
+ uint16_t wizard_processed_bytes;
+ const MagicPage* bookmark; // pointer to last glob
};
class Wizard : public Inspector
void reset(Wand&, bool tcp, bool c2s);
bool finished(Wand&);
- bool cast_spell(Wand&, Flow*, const uint8_t*, unsigned);
+ bool cast_spell(Wand&, Flow*, const uint8_t*, unsigned, uint16_t&);
bool spellbind(const MagicPage*&, Flow*, const uint8_t*, unsigned);
bool cursebind(const vector<CurseServiceTracker>&, Flow*, const uint8_t*, unsigned);
MagicBook* s2c_spells;
CurseBook* curses;
- uint16_t max_pattern;
+
+ uint16_t max_search_depth;
};
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
MagicSplitter::MagicSplitter(bool c2s, class Wizard* w) :
- StreamSplitter(c2s)
+ StreamSplitter(c2s), wizard_processed_bytes(0), bookmark(nullptr)
{
wizard = w;
w->add_ref();
delete wand.curse_tracker[i].tracker;
}
-// FIXIT-M stop search on hit and failure (no possible match)
StreamSplitter::Status MagicSplitter::scan(
Packet* pkt, const uint8_t* data, uint32_t len,
uint32_t, uint32_t*)
Profile profile(wizPerfStats);
count_scan(pkt->flow);
+ // setting last glob from current flow
+ if ( wand.spell )
+ wand.spell->book.set_bookmark(bookmark);
bytes_scanned += len;
- if ( wizard->cast_spell(wand, pkt->flow, data, len) )
+
+ if ( wizard->cast_spell(wand, pkt->flow, data, len, wizard_processed_bytes) )
{
trace_logf(wizard_trace, pkt, "%s streaming search found service %s\n",
to_server() ? "c2s" : "s2c", pkt->flow->service);
count_hit(pkt->flow);
+ wizard_processed_bytes = 0;
+ return STOP;
}
else if ( wizard->finished(wand) || bytes_scanned >= max(pkt->flow) )
{
count_miss(pkt->flow);
trace_logf(wizard_trace, pkt, "%s streaming search abandoned\n", to_server() ? "c2s" : "s2c");
+ wizard_processed_bytes = 0;
return ABORT;
}
+ // saving new last glob from current flow
+ if ( wand.spell )
+ bookmark = wand.spell->book.get_bookmark();
+
// ostensibly continue but splitter will be swapped out upon hit
return SEARCH;
}
s2c_spells = m->get_book(false, false);
curses = m->get_curse_book();
- max_pattern = m->get_max_pattern();
+ max_search_depth = m->get_max_search_depth();
}
Wizard::~Wizard()
{
w.hex = c2s_hexes->page1();
w.spell = c2s_spells->page1();
+ c2s_spells->set_bookmark();
}
else
{
w.hex = s2c_hexes->page1();
w.spell = s2c_spells->page1();
+ s2c_spells->set_bookmark();
}
if (w.curse_tracker.empty())
bool c2s = p->is_from_client();
Wand wand;
reset(wand, false, c2s);
+ uint16_t udp_processed_bytes = 0;
++tstats.udp_scans;
- if ( cast_spell(wand, p->flow, p->data, p->dsize) )
+ if ( cast_spell(wand, p->flow, p->data, p->dsize, udp_processed_bytes) )
{
trace_logf(wizard_trace, p, "%s datagram search found service %s\n",
c2s ? "c2s" : "s2c", p->flow->service);
bool Wizard::spellbind(
const MagicPage*& m, Flow* f, const uint8_t* data, unsigned len)
{
- f->service = m->book.find_spell(data, len, max_pattern, m);
+ f->service = m->book.find_spell(data, len, m);
return ( f->service != nullptr );
}
}
bool Wizard::cast_spell(
- Wand& w, Flow* f, const uint8_t* data, unsigned len)
+ Wand& w, Flow* f, const uint8_t* data, unsigned len, uint16_t& wizard_processed_bytes)
{
+ auto curse_len = len;
+
+ len = std::min(len, static_cast<unsigned>(max_search_depth - wizard_processed_bytes));
+
+ wizard_processed_bytes += len;
+
if ( w.hex && spellbind(w.hex, f, data, len) )
return true;
if ( w.spell && spellbind(w.spell, f, data, len) )
return true;
- if (cursebind(w.curse_tracker, f, data, len))
+ if (cursebind(w.curse_tracker, f, data, curse_len))
return true;
+ // If we reach max value of wizard_processed_bytes,
+ // but not assign any inspector - raise tcp_miss and stop
+ if ( !f->service && wizard_processed_bytes >= max_search_depth )
+ {
+ w.spell = nullptr;
+ w.hex = nullptr;
+ }
+
return false;
}
ps->fpt = 0;
ps->paf = ss->scan(pkt, data, len, flags, &ps->fpt);
- if ( ps->paf == StreamSplitter::ABORT )
+ if ( ps->paf == StreamSplitter::ABORT || ps->paf == StreamSplitter::STOP )
return false;
if ( ps->paf != StreamSplitter::SEARCH )
enum Status
{
- ABORT, // non-paf operation
- START, // internal use only
- SEARCH, // searching for next flush point
- FLUSH, // flush at given offset
- LIMIT, // flush to given offset upon reaching paf_max
- SKIP, // skip ahead to given offset
- LIMITED // previously did limit flush
+ ABORT, // non-paf operation
+ START, // internal use only
+ SEARCH, // searching for next flush point
+ FLUSH, // flush at given offset
+ LIMIT, // flush to given offset upon reaching paf_max
+ SKIP, // skip ahead to given offset
+ LIMITED, // previously did limit flush
+ STOP // stop paf scan loop
};
// scan(), finish(), reassemble() are called in this order: