summaryrefslogtreecommitdiff
path: root/orkaudio/audiocaptureplugins/voip/VoIp.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'orkaudio/audiocaptureplugins/voip/VoIp.cpp')
-rw-r--r--orkaudio/audiocaptureplugins/voip/VoIp.cpp465
1 files changed, 465 insertions, 0 deletions
diff --git a/orkaudio/audiocaptureplugins/voip/VoIp.cpp b/orkaudio/audiocaptureplugins/voip/VoIp.cpp
new file mode 100644
index 0000000..e7a3b6d
--- /dev/null
+++ b/orkaudio/audiocaptureplugins/voip/VoIp.cpp
@@ -0,0 +1,465 @@
+/*
+ * Oreka -- A media capture and retrieval platform
+ *
+ * Copyright (C) 2005, orecx LLC
+ *
+ * http://www.orecx.com
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License.
+ * Please refer to http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#define _WINSOCKAPI_ // prevents the inclusion of winsock.h
+
+#include "ace/OS_NS_unistd.h"
+#include "ace/OS_NS_string.h"
+#include "ace/Singleton.h"
+#include "ace/Min_Max.h"
+#include "ace/OS_NS_arpa_inet.h"
+#include "ace/OS_NS_ctype.h"
+#include "AudioCapturePlugin.h"
+#include "AudioCapturePluginCommon.h"
+#include "Utils.h"
+#include "VoIpConfig.h"
+#include "pcap.h"
+#include "PacketHeaderDefs.h"
+#include "Rtp.h"
+#include "SipSession.h"
+
+extern AudioChunkCallBackFunction g_audioChunkCallBack;
+extern CaptureEventCallBackFunction g_captureEventCallBack;
+extern LogManager* g_logManager;
+
+#include "LogManager.h"
+
+static LoggerPtr s_log;
+static LoggerPtr s_sipExtractionLog;
+time_t lastHooveringTime;
+
+VoIpConfigTopObjectRef g_VoIpConfigTopObjectRef;
+#define DLLCONFIG g_VoIpConfigTopObjectRef.get()->m_config
+
+// find the address that follows the given search string between start and stop pointers
+char* memFindAfter(char* toFind, char* start, char* stop)
+{
+ for(char * ptr = start; (ptr<stop) && (ptr != NULL); ptr = (char *)memchr(ptr+1, toFind[0],(stop - start)))
+ {
+ if(memcmp(toFind, ptr, strlen(toFind)) == 0)
+ {
+ return (ptr+strlen(toFind));
+ }
+ }
+ return NULL;
+}
+
+char* memFindEOL(char* start, char* limit)
+{
+ char* c = start;
+ while(*c != '\r' && *c != '\n' && c<limit)
+ {
+ c++;
+ }
+ if(*c == '\r' || *c == '\n')
+ {
+ return c;
+ }
+ return start;
+}
+
+void GrabToken(char* in, CStdString& out)
+{
+ for(char* c = in; *c != '\0' && *c != 0x20 && *c != 0x0D && *c != 0x0A; c = c+1)
+ {
+ out += *c;
+ }
+}
+
+void GrabAlphaNumToken(char * in, char* limit, CStdString& out)
+{
+ // Look for first alphanum character
+ char* start = in;
+ while (!ACE_OS::ace_isalnum(*start) && start<limit)
+ {
+ start++;
+ }
+
+ if(start != (limit -1))
+ {
+ for(char* c = start; ACE_OS::ace_isalnum(*c); c = c+1)
+ {
+ out += *c;
+ }
+ }
+}
+
+void GrabString(char* start, char* stop, CStdString& out)
+{
+ char* c = start;
+ while(c <= stop)
+ {
+ out += *c++;
+ }
+}
+
+// Grabs a line of characters in memory from start pointer
+// returns the end of line
+char* GrabLine(char* start, char* limit, CStdString& out)
+{
+ char* c = start;
+ while(c < limit && *c != 0x0D && *c != 0x0A)
+ {
+ out += *c++;
+ }
+ return c;
+}
+
+bool TryRtp(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader, UdpHeaderStruct* udpHeader, u_char* udpPayload)
+{
+ bool result = false;
+ RtpHeaderStruct* rtpHeader = (RtpHeaderStruct*)udpPayload;
+
+ if (rtpHeader->version == 2)
+ {
+ u_short source = ntohs(udpHeader->source);
+ u_short dest = ntohs(udpHeader->dest);
+ if(!(ntohs(udpHeader->source)%2) && !(ntohs(udpHeader->dest)%2)) // udp ports must be even
+ {
+ if(rtpHeader->pt == RTP_PT_PCMU || rtpHeader->pt == RTP_PT_PCMA)
+ {
+ result = true;
+ u_char* payload = (u_char *)rtpHeader + sizeof(RtpHeaderStruct);
+ u_char* packetEnd = (u_char *)ipHeader + ntohs(ipHeader->ip_len);
+ u_int payloadLength = packetEnd - payload;
+
+ RtpPacketInfoRef rtpInfo(new RtpPacketInfo());
+ rtpInfo->m_sourceIp = ipHeader->ip_src;
+ rtpInfo->m_destIp = ipHeader->ip_dest;
+ rtpInfo->m_sourcePort = ntohs(udpHeader->source);
+ rtpInfo->m_destPort = ntohs(udpHeader->dest);
+ rtpInfo->m_payloadSize = payloadLength;
+ rtpInfo->m_payloadType = rtpHeader->pt;
+ rtpInfo->m_seqNum = ntohs(rtpHeader->seq);
+ rtpInfo->m_timestamp = ntohl(rtpHeader->ts);
+ rtpInfo->m_payload = payload;
+
+ CStdString debug;
+ debug.Format("%s,%d seq:%u ts:%u len:%d", ACE_OS::inet_ntoa(rtpInfo->m_sourceIp), rtpInfo->m_sourcePort, ntohs(rtpHeader->seq), ntohl(rtpHeader->ts), payloadLength);
+ LOG4CXX_DEBUG(s_log, debug);
+
+ SipSessionsSingleton::instance()->ReportRtpPacket(rtpInfo);
+ }
+ }
+ }
+ return result;
+}
+
+
+bool TrySipBye(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader, UdpHeaderStruct* udpHeader, u_char* udpPayload)
+{
+ bool result = false;
+ if (memcmp("BYE", (void*)udpPayload, 1) == 0)
+ {
+ result = true;
+ int sipLength = ntohs(udpHeader->len);
+ char* sipEnd = (char*)udpPayload + sipLength;
+ SipByeInfo info;
+ char* callIdField = memFindAfter("Call-ID: ", (char*)udpPayload, sipEnd);
+ if(callIdField)
+ {
+ GrabToken(callIdField, info.m_callId);
+ SipSessionsSingleton::instance()->ReportSipBye(info);
+ }
+ LOG4CXX_DEBUG(s_log, "SIP BYE");
+ }
+ return result;
+}
+
+bool TrySipInvite(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader, UdpHeaderStruct* udpHeader, u_char* udpPayload)
+{
+ bool result = false;
+ if (memcmp("INVITE", (void*)udpPayload, 1) == 0)
+ {
+ result = true;
+
+ int sipLength = ntohs(udpHeader->len);
+ char* sipEnd = (char*)udpPayload + sipLength;
+
+ SipInviteInfoRef info(new SipInviteInfo());
+
+ char* fromField = memFindAfter("From: ", (char*)udpPayload, sipEnd);
+ char* toField = NULL;
+ char* callIdField = NULL;
+ char* audioField = NULL;
+
+ if(fromField)
+ {
+ if(s_sipExtractionLog->isDebugEnabled())
+ {
+ CStdString from;
+ GrabLine(fromField, sipEnd, from);
+ s_sipExtractionLog->forcedLog(Level::DEBUG, "from: " + from);
+ }
+
+ char* fromFieldEnd = memFindEOL(fromField, sipEnd);
+
+ char* sipUser = memFindAfter("sip:", fromField, fromFieldEnd);
+ if(sipUser)
+ {
+ GrabAlphaNumToken(sipUser, fromFieldEnd, info->m_from);
+ }
+ else
+ {
+ GrabAlphaNumToken(fromField, fromFieldEnd, info->m_from);
+ }
+ toField = memFindAfter("To: ", fromField, sipEnd);
+ }
+ if(toField)
+ {
+ char* toFieldEnd = NULL;
+ if(s_sipExtractionLog->isDebugEnabled())
+ {
+ CStdString to;
+ toFieldEnd = GrabLine(toField, sipEnd, to);
+ s_sipExtractionLog->forcedLog(Level::DEBUG, "to: " + to);
+ }
+
+ char* sipUser = memFindAfter("sip:", toField, toFieldEnd);
+ if(sipUser)
+ {
+ GrabAlphaNumToken(sipUser, toFieldEnd, info->m_to);
+ }
+ else
+ {
+ GrabAlphaNumToken(toField, toFieldEnd, info->m_to);
+ }
+ callIdField = memFindAfter("Call-ID: ", toField, sipEnd);
+ }
+ if(callIdField)
+ {
+ GrabToken(callIdField, info->m_callId);
+ audioField = memFindAfter("m=audio ", callIdField, sipEnd);
+ }
+ if(audioField)
+ {
+ GrabToken(audioField, info->m_fromRtpPort);
+ info->m_fromIp = ipHeader->ip_src;
+ SipSessionsSingleton::instance()->ReportSipInvite(info);
+ }
+ LOG4CXX_DEBUG(s_log, "SIP INVITE");
+ }
+ return result;
+}
+
+void HandlePacket(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
+{
+ EthernetHeaderStruct* ethernetHeader = (EthernetHeaderStruct *)pkt_data;
+ IpHeaderStruct* ipHeader = (IpHeaderStruct*)((char*)ethernetHeader + sizeof(EthernetHeaderStruct));
+ int ipHeaderLength = ipHeader->ip_hl*4;
+
+ //CStdString source = ACE_OS::inet_ntoa(ipHeader->ip_src);
+ //CStdString dest = ACE_OS::inet_ntoa(ipHeader->ip_dest);
+ //CStdString debug;
+ //debug.Format("%x, %x", ethernetHeader, ipHeader);
+ //LOG4CXX_INFO(s_log, source + "-" + dest);
+
+ //LOG4CXX_DEBUG(s_log, "*");
+
+ if(ipHeader->ip_p == IPPROTO_UDP)
+ {
+ UdpHeaderStruct* udpHeader = (UdpHeaderStruct*)((char *)ipHeader + ipHeaderLength);
+
+ if( ntohs(udpHeader->source) > 5000 && ntohs(udpHeader->dest) > 5000 )
+ {
+ u_char* udpPayload = (u_char *)udpHeader + sizeof(UdpHeaderStruct);
+
+
+ bool detectedUsefulPacket = TryRtp(ethernetHeader, ipHeader, udpHeader, udpPayload);
+
+ if(!detectedUsefulPacket)
+ {
+ detectedUsefulPacket= TrySipInvite(ethernetHeader, ipHeader, udpHeader, udpPayload);
+ }
+ if(!detectedUsefulPacket)
+ {
+ detectedUsefulPacket = TrySipBye(ethernetHeader, ipHeader, udpHeader, udpPayload);
+ }
+ }
+ }
+
+ time_t now = time(NULL);
+ if((now - lastHooveringTime) > 5)
+ {
+ lastHooveringTime = now;
+ SipSessionsSingleton::instance()->Hoover(now);
+ }
+}
+
+
+class VoIp
+{
+public:
+ VoIp();
+ void Initialize();
+ void Run();
+ void StartCapture(CStdString& port);
+ void StopCapture(CStdString& port);
+private:
+ pcap_t* m_pcapHandle;
+};
+
+typedef ACE_Singleton<VoIp, ACE_Thread_Mutex> VoIpSingleton;
+
+VoIp::VoIp()
+{
+ m_pcapHandle = NULL;
+}
+
+void Configure(DOMNode* node)
+{
+ if (node)
+ {
+ VoIpConfigTopObjectRef VoIpConfigTopObjectRef(new VoIpConfigTopObject);
+ try
+ {
+ VoIpConfigTopObjectRef.get()->DeSerializeDom(node);
+ g_VoIpConfigTopObjectRef = VoIpConfigTopObjectRef;
+ }
+ catch (CStdString& e)
+ {
+ LOG4CXX_WARN(g_logManager->rootLog, "VoIp.dll: " + e);
+ }
+ }
+ else
+ {
+ LOG4CXX_WARN(g_logManager->rootLog, "VoIp.dll: got empty DOM tree");
+ }
+}
+
+
+void VoIp::Initialize()
+{
+ s_log = Logger::getLogger("voip");
+ s_sipExtractionLog = Logger::getLogger("sipextraction");
+ LOG4CXX_INFO(s_log, "Initializing VoIP plugin");
+
+ // create a default config object in case it was not properly initialized by Configure
+ if(!g_VoIpConfigTopObjectRef.get())
+ {
+ g_VoIpConfigTopObjectRef.reset(new VoIpConfigTopObject);
+ }
+
+/*
+ char addr1[] = "10.1.2.3";
+ char addr2[] = "192.168.5.6";
+ char addr3[] = "45.168.5.6";
+ struct in_addr addr;
+ ACE_OS::inet_aton(addr1, &addr);
+ bool result = DLLCONFIG.IsPartOfLan(addr);
+ result = DLLCONFIG.IsMediaGateway(addr);
+ ACE_OS::inet_aton(addr2, &addr);
+ result = DLLCONFIG.IsPartOfLan(addr);
+ result = DLLCONFIG.IsMediaGateway(addr);
+ ACE_OS::inet_aton(addr3, &addr);
+ result = DLLCONFIG.IsPartOfLan(addr);
+ result = DLLCONFIG.IsMediaGateway(addr);
+*/
+
+ pcap_if_t* devices = NULL;
+ pcap_if_t* deviceToSniff = NULL;
+ lastHooveringTime = time(NULL);
+
+ char * error = NULL;
+ if (pcap_findalldevs(&devices, error) == -1)
+ {
+ LOG4CXX_ERROR(s_log, CStdString("pcap error when discovering devices: ") + error);
+ }
+ else
+ {
+ if(devices)
+ {
+ LOG4CXX_INFO(s_log, CStdString("Available pcap devices:"));
+
+ for (pcap_if_t* device = devices; device != NULL; device = device->next)
+ {
+ LOG4CXX_INFO(s_log, CStdString("\t* ") + device->description + " " + device->name );
+ if(DLLCONFIG.m_device.Equals(device->name))
+ {
+ deviceToSniff = device;
+ }
+ }
+ if (!deviceToSniff)
+ {
+ LOG4CXX_ERROR(s_log, CStdString("pcap could not find wanted device: ") + DLLCONFIG.m_device);
+ }
+ else
+ {
+ #define PROMISCUOUS 1
+ if ((m_pcapHandle = pcap_open_live(deviceToSniff->name, 1500, PROMISCUOUS,
+ 500, error)) == NULL)
+ {
+ LOG4CXX_ERROR(s_log, CStdString("pcap error when opening device: ") + deviceToSniff->name);
+ }
+ else
+ {
+ LOG4CXX_INFO(s_log, CStdString("successfully opened device: ") + deviceToSniff->name);
+
+ //unsigned int netMask = 0xffffff;
+ //if(deviceToSniff->addresses != NULL)
+ //{
+ // netMask=((struct sockaddr_in *)(device->addresses->netmask))->sin_addr.s_addr;
+ //}
+
+ //struct bpf_program program;
+ //if (pcap_compile(m_pcapHandle, &program, "ip proto icmp", 1, 0xffffffff) < 0)
+ //{
+ // LOG4CXX_ERROR(s_log, CStdString("unable to compile pcap filter"));
+ //}
+ }
+ }
+ }
+ else
+ {
+ LOG4CXX_ERROR(s_log, CStdString("pcap could not find any device"));
+ }
+ }
+
+}
+
+void VoIp::Run()
+{
+ LOG4CXX_INFO(s_log, "Running VoIp.dll");
+ pcap_loop(m_pcapHandle, 0, HandlePacket, NULL);
+}
+
+void VoIp::StartCapture(CStdString& port)
+{
+ ;
+}
+
+void VoIp::StopCapture(CStdString& port)
+{
+ ;
+}
+
+void __CDECL__ Initialize()
+{
+ VoIpSingleton::instance()->Initialize();
+}
+
+void __CDECL__ Run()
+{
+ VoIpSingleton::instance()->Run();
+}
+
+void __CDECL__ StartCapture(CStdString& capturePort)
+{
+ ;
+}
+
+void __CDECL__ StopCapture(CStdString& capturePort)
+{
+ ;
+}
+