/* * msgsniff.cpp * This file is part of the YATE Project http://YATE.null.ro * * A sample message sniffer that inserts a wildcard message handler * * Yet Another Telephony Engine - a fully featured software PBX and IVR * Copyright (C) 2004-2014 Null Team * * This software is distributed under multiple licenses; * see the COPYING file in the main directory for licensing * information for this specific distribution. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #include #include #include using namespace TelEngine; namespace { // anonymous static const char* s_debugs[] = { "on", "off", "enable", "disable", "true", "false", "yes", "no", "filter", "timer", "age", 0 }; class MsgSniff : public Plugin { public: MsgSniff(); virtual void initialize(); private: bool m_first; }; class SniffHandler : public MessageHandler { public: SniffHandler() : MessageHandler(0,0) { } virtual bool received(Message &msg); }; class HookHandler : public MessagePostHook { public: virtual void dispatched(const Message& msg, bool handled); }; static bool s_active = true; static bool s_timer = false; static u_int64_t s_minAge = 0; static Regexp s_filter; static Mutex s_mutex(false,"FilterSniff"); static void dumpParams(const Message &msg, String& par) { unsigned n = msg.length(); for (unsigned i = 0; i < n; i++) { const NamedString *s = msg.getParam(i); if (s) { par << "\r\n param['" << s->name() << "'] = "; if (s->name() == YSTRING("password")) par << "(hidden)"; else par << "'" << *s << "'"; if (const NamedPointer* p = YOBJECT(NamedPointer,s)) { char buf[64]; GenObject* obj = p->userData(); ::sprintf(buf," [%p]",obj); par << buf; if (obj) par << " '" << obj->toString() << "'"; } } } } bool SniffHandler::received(Message &msg) { if (!s_timer && (msg == YSTRING("engine.timer"))) return false; if (msg == YSTRING("engine.command")) { static const String name("sniffer"); String line(msg.getValue(YSTRING("line"))); if (line.startSkip(name)) { line >> s_active; line.trimSpaces(); if (line.startSkip("timer")) (line >> s_timer).trimSpaces(); if (line.startSkip("filter")) { s_mutex.lock(); s_filter = line; s_mutex.unlock(); } if (line.startSkip("age")) s_minAge = (u_int64_t)(1000000.0 * fabs(line.toDouble())); msg.retValue() << "Message sniffer: " << (s_active ? "on" : "off"); if (s_active) msg.retValue() << ", timer: " << (s_timer ? "on" : "off"); if (s_active && s_filter) msg.retValue() << ", filter: " << s_filter; if (s_active && s_minAge) msg.retValue() << ", age: " << String().printf("%u.%06u", (unsigned int)(s_minAge / 1000000),(unsigned int)(s_minAge % 1000000)); msg.retValue() << "\r\n"; return true; } line = msg.getParam(YSTRING("partline")); if (line.null()) { if (name.startsWith(msg.getValue(YSTRING("partword")))) msg.retValue().append(name,"\t"); } else if (name == line) { line = msg.getValue(YSTRING("partword")); for (const char** b = s_debugs; *b; b++) if (line.null() || String(*b).startsWith(line)) msg.retValue().append(*b,"\t"); } } if (!s_active) return false; Lock lock(s_mutex); if (s_filter && !s_filter.matches(msg)) return false; lock.drop(); u_int64_t mt = msg.msgTime().usec(); u_int64_t dt = Time::now() - mt; if (s_minAge && (dt < s_minAge)) return false; String par; dumpParams(msg,par); Output("Sniffed '%s' time=%u.%06u age=%u.%06u%s\r\n thread=%p '%s'\r\n data=%p\r\n retval='%s'%s", msg.c_str(), (unsigned int)(mt / 1000000), (unsigned int)(mt % 1000000), (unsigned int)(dt / 1000000), (unsigned int)(dt % 1000000), (msg.broadcast() ? " (broadcast)" : ""), Thread::current(), Thread::currentName(), msg.userData(), msg.retValue().c_str(), par.safe()); return false; }; void HookHandler::dispatched(const Message& msg, bool handled) { if (!s_active || (!s_timer && (msg == YSTRING("engine.timer")))) return; Lock lock(s_mutex); if (s_filter && !s_filter.matches(msg)) return; lock.drop(); u_int64_t dt = Time::now() - msg.msgTime().usec(); if (s_minAge && (dt < s_minAge)) return; String par; dumpParams(msg,par); const char* rval = msg.retValue().c_str(); const char* rsep = "'"; if (handled && rval && (rval[0] != '-' || rval[1]) && (msg == YSTRING("user.auth"))) { rval = "(hidden)"; rsep = ""; } Output("Returned %s '%s' delay=%u.%06u%s\r\n thread=%p '%s'\r\n data=%p\r\n retval=%s%s%s%s", String::boolText(handled), msg.c_str(), (unsigned int)(dt / 1000000), (unsigned int)(dt % 1000000), (msg.broadcast() ? " (broadcast)" : ""), Thread::current(), Thread::currentName(), msg.userData(), rsep,rval,rsep, par.safe()); } MsgSniff::MsgSniff() : Plugin("msgsniff"), m_first(true) { Output("Loaded module MsgSniffer"); } void MsgSniff::initialize() { Output("Initializing module MsgSniffer"); if (m_first) { m_first = false; s_active = Engine::config().getBoolValue("general","msgsniff",false); s_mutex.lock(); s_filter = Engine::config().getValue("general","filtersniff"); s_mutex.unlock(); s_minAge = (u_int64_t)(1000000.0 * fabs(Engine::config().getDoubleValue("general","agesniff"))); Engine::install(new SniffHandler); Engine::self()->setHook(new HookHandler); } } INIT_PLUGIN(MsgSniff); }; // anonymous namespace /* vi: set ts=8 sw=4 sts=4 noet: */