/** * wiresniff.cpp * This file is part of the YATE Project http://YATE.null.ro * * Capture interface for YATE messages. * * Yet Another Telephony Engine - a fully featured software PBX and IVR * Copyright (C) 2018 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 #include #define MAX_BUFFER_SIZE 65507 using namespace TelEngine; namespace { //anonymous static Configuration s_cfg; static Socket s_socket; static SocketAddr addr; static SocketAddr s_addr; static Regexp s_filter; static bool s_timer = false; static Mutex s_mutex(false,"WireSniff"); class WireSniffPlugin : public Plugin { public: WireSniffPlugin(); virtual void initialize(); private: bool m_first; }; class WireSniffHandler : public MessageHandler { public: WireSniffHandler() : MessageHandler(0,0) { } virtual bool received(Message &msg); }; class WireSniffHook : public MessagePostHook { public: virtual void dispatched(const Message& msg, bool handled); }; static void addTag(DataBlock& data, uint8_t tag, uint8_t length = 0, const void* value = 0) { unsigned char buf[2]; buf[0] = tag; buf[1] = length; data.append(buf,2); if (length) data.append(const_cast(value),length); } static void addTag(DataBlock& data, uint8_t tag, const char* text) { if (text && *text) addTag(data,tag,::strlen(text),text); } static void addTag(DataBlock& data, uint8_t tag, void* ptr) { if (!ptr) return; intptr_t val = reinterpret_cast(ptr); addTag(data,tag,sizeof(val),ptr); } static bool sendMessage(const Message& msg, bool result, bool handled) { DataBlock buf; String signature("yate-msg"); buf.append(signature); uint8_t i = result ? 1: 0; addTag(buf,YSNIFF_RESULT,1,&i); Thread* t = Thread::current(); addTag(buf,YSNIFF_THREAD_ADDR,t); const char* name = Thread::currentName(); addTag(buf,YSNIFF_THREAD_NAME,name); RefObject* data = msg.userData(); addTag(buf, YSNIFF_DATA, data); uint8_t broadcast = msg.broadcast() ? 1 : 0; addTag(buf,YSNIFF_BROADCAST,1,&broadcast); uint8_t finaltag = 0; addTag(buf,YSNIFF_FINAL_TAG,1,&finaltag); String id; id.printf("%p",&msg); if (result) buf.append(msg.encode(handled,id)); else buf.append(msg.encode(id)); if (!(buf.length() < MAX_BUFFER_SIZE)) DDebug(DebugWarn,"Buffer Overrun"); s_mutex.lock(); unsigned int len = s_socket.sendTo(buf.data(),buf.length(),addr); s_mutex.unlock(); if (!len) DDebug(DebugWarn,"Unable to send package"); return(len == buf.length()); } bool WireSniffHandler::received(Message &msg) { if (!s_timer && (msg == YSTRING("engine.timer"))) return false; if (!s_socket.valid()) { Debug(DebugWarn,"Socket %s is not valid", strerror(s_socket.error())); return false; } if (!s_addr.valid()){ Debug(DebugWarn,"SocketAddr is not valid"); return false; } if (s_filter && !s_filter.matches(msg)) return false; sendMessage(msg,false,false); return false; } void WireSniffHook::dispatched(const Message& msg, bool handled) { if ((!s_timer && (msg == YSTRING("engine.timer")))) return; if (s_filter && !s_filter.matches(msg)) return; sendMessage(msg,true,handled); } WireSniffPlugin::WireSniffPlugin() : Plugin("wiresniff"), m_first(true) { Output("Loaded module WireSniff"); } void WireSniffPlugin::initialize() { Output("Initializing module WireSniff"); s_mutex.lock(); s_cfg = Engine::configFile("wiresniff"); s_cfg.load(); s_mutex.unlock(); String remoteName = s_cfg.getValue("general","remote_host"); addr.host(remoteName); addr.port(s_cfg.getIntValue("general","remote_port")); if(addr.valid() && addr.port()) { DDebug(this,DebugNote,"Remote address '%s' and remote port '%u' are set", remoteName.c_str(), addr.port()); } if(!(addr.host() && addr.port())) { s_socket.terminate(); Debug(this,DebugWarn,"Remote address and remote port are not configured"); } if (!(addr.valid())) { DDebug(this,DebugWarn,"Invalid address '%s'",remoteName.c_str()); return; } if(addr.family() != SocketAddr::IPv6) s_addr.host(s_cfg.getValue("general","local_host","0.0.0.0")); else s_addr.host(s_cfg.getValue("general","local_host","::")); s_addr.port(s_cfg.getIntValue("general","local_port",0)); if (!(s_addr.valid() && s_addr.host())) { DDebug(this,DebugWarn,"Invalid address '%s'", s_addr.host().c_str()); return; } if (s_addr.family() != addr.family()) { Debug(this,DebugWarn,"Socket Addresses are not compatible"); return; } if (!s_socket.create(s_addr.family(),SOCK_DGRAM)) { Debug(this,DebugWarn,"Could not create socket %s", strerror(s_socket.error())); s_socket.terminate(); } if (!s_socket.bind(s_addr)) { Debug(this,DebugWarn,"Unable to bind to %s:%u : %s",s_addr.host().c_str(),s_addr.port(),strerror(s_socket.error())); return; } int val = 1; if (!s_socket.setOption(IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))) { Debug(this,DebugWarn,"Socket %s option is not set!", strerror(s_socket.error())); return; } if (!s_socket.setBlocking(false)) { Debug(DebugWarn,"Unable to set socket %s to nonblocking mode", strerror(s_socket.error())); return; } if (!s_socket.getSockName(s_addr)) { Debug(this,DebugWarn,"Error getting address: %s",strerror(s_socket.error())); return; } DDebug(this,DebugNote,"Socket bound to: %s:%u",s_addr.host().c_str(),s_addr.port()); Debug(this,DebugNote,"Sending from %s:%u to %s:%u",s_addr.host().c_str(),s_addr.port(),addr.host().c_str(),addr.port()); if (m_first) { m_first = false; Engine::install(new WireSniffHandler); Engine::self()->setHook(new WireSniffHook); } s_mutex.lock(); s_filter = s_cfg.getValue("general", "filter"); s_mutex.unlock(); Output("WireSniff was initialized"); } INIT_PLUGIN(WireSniffPlugin); }; // anonymous namespace /* vi: set ts=8 sw=4 sts=4 noet: */