summaryrefslogtreecommitdiff
path: root/orkaudio/audiocaptureplugins/voip
diff options
context:
space:
mode:
authorGerald Begumisa <ben_g@users.sourceforge.net>2007-02-06 06:56:19 +0000
committerGerald Begumisa <ben_g@users.sourceforge.net>2007-02-06 06:56:19 +0000
commit6ea47988e23c732814f792cb9dde7a35f9b26885 (patch)
tree886a952c4570e6d62d44bac3bdd9999daaf1ecfc /orkaudio/audiocaptureplugins/voip
parent94e3eb2d0cda7df2d876d1698db2e8e75cd0a0b1 (diff)
IAX2 support added by Gerald Begumisa
git-svn-id: https://oreka.svn.sourceforge.net/svnroot/oreka/trunk@399 09dcff7a-b715-0410-9601-b79a96267cd0
Diffstat (limited to 'orkaudio/audiocaptureplugins/voip')
-rw-r--r--orkaudio/audiocaptureplugins/voip/Iax2Session.cpp990
-rw-r--r--orkaudio/audiocaptureplugins/voip/Iax2Session.h275
-rw-r--r--orkaudio/audiocaptureplugins/voip/Makefile.am2
-rw-r--r--orkaudio/audiocaptureplugins/voip/PacketHeaderDefs.h49
-rw-r--r--orkaudio/audiocaptureplugins/voip/VoIp.cpp835
-rw-r--r--orkaudio/audiocaptureplugins/voip/VoIp.dsp8
6 files changed, 2148 insertions, 11 deletions
diff --git a/orkaudio/audiocaptureplugins/voip/Iax2Session.cpp b/orkaudio/audiocaptureplugins/voip/Iax2Session.cpp
new file mode 100644
index 0000000..6b072b5
--- /dev/null
+++ b/orkaudio/audiocaptureplugins/voip/Iax2Session.cpp
@@ -0,0 +1,990 @@
+/*
+ * 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
+ *
+ */
+#pragma warning( disable: 4786 ) // disables truncated symbols in browse-info warning
+#define _WINSOCKAPI_ // prevents the inclusion of winsock.h
+
+#include "Utils.h"
+#include "AudioCapture.h"
+#include "Iax2Session.h"
+#include "AudioCapturePlugin.h"
+#include "AudioCapturePluginCommon.h"
+#include <list>
+#include "ConfigManager.h"
+#include "VoIpConfig.h"
+#include "ace/OS_NS_arpa_inet.h"
+
+extern AudioChunkCallBackFunction g_audioChunkCallBack;
+extern CaptureEventCallBackFunction g_captureEventCallBack;
+
+extern VoIpConfigTopObjectRef g_VoIpConfigTopObjectRef;
+#define DLLCONFIG g_VoIpConfigTopObjectRef.get()->m_config
+
+Iax2Session::Iax2Session(CStdString& trackingId)
+{
+ m_trackingId = trackingId;
+ m_lastUpdated = time(NULL);
+ m_log = Logger::getLogger("Iax2session");
+ m_invitorIp.s_addr = 0;
+ m_inviteeIp.s_addr = 0;
+ m_localIp.s_addr = 0;
+ m_remoteIp.s_addr = 0;
+ m_direction = CaptureEvent::DirUnkn;
+ m_numIax2Packets = 0;
+ m_started = false;
+ m_stopped = false;
+ m_beginDate = 0;
+ m_codec = 0;
+ m_hasDuplicateIax2 = false;
+ m_iax2_state = IAX2_STATE_WAITING;
+ m_invitor_scallno = 0;
+ m_invitee_scallno = 0;
+
+ /* Not sure if IAX2 needs these... */
+ m_highestIax2SeqNumDelta = 0;
+ m_minIax2SeqDelta = (double)DLLCONFIG.m_rtpDiscontinuityMinSeqDelta;
+ m_minIax2TimestampDelta = (double)DLLCONFIG.m_rtpDiscontinuityMinSeqDelta * 160;
+}
+
+void Iax2Session::Stop()
+{
+ CStdString logMsg;
+
+ logMsg.Format("[%s] %s Session stop, numIax2Pkts:%d dupl:%d seqDelta:%d "
+ "lastUpdated:%u", m_trackingId, m_capturePort, m_numIax2Packets,
+ m_hasDuplicateIax2, m_highestIax2SeqNumDelta, m_lastUpdated);
+
+ LOG4CXX_INFO(m_log, logMsg);
+
+ if(m_started && !m_stopped) {
+ CaptureEventRef stopEvent(new CaptureEvent);
+ stopEvent->m_type = CaptureEvent::EtStop;
+ stopEvent->m_timestamp = m_lastUpdated;
+ g_captureEventCallBack(stopEvent, m_capturePort);
+ m_stopped = true;
+ }
+}
+
+void Iax2Session::Start()
+{
+ CaptureEventRef startEvent(new CaptureEvent);
+
+ m_started = true;
+ time(&m_beginDate);
+ GenerateOrkUid();
+ startEvent->m_type = CaptureEvent::EtStart;
+ startEvent->m_timestamp = m_beginDate;
+ startEvent->m_value = m_trackingId;
+ CStdString timestamp = IntToString(startEvent->m_timestamp);
+
+ LOG4CXX_INFO(m_log, "[" + m_trackingId + "] " + m_capturePort + " " +
+ IAX2_PROTOCOL_STR + " Session start, timestamp:" + timestamp);
+
+ g_captureEventCallBack(startEvent, m_capturePort);
+}
+
+void Iax2Session::GenerateOrkUid()
+{
+ struct tm date = {0};
+ int month, year;
+
+ ACE_OS::localtime_r(&m_beginDate, &date);
+
+ month = date.tm_mon + 1;
+ year = date.tm_year + 1900;
+
+ m_orkUid.Format("%.4d%.2d%.2d_%.2d%.2d%.2d_%s", year, month, date.tm_mday,
+ date.tm_hour, date.tm_min, date.tm_sec, m_trackingId);
+}
+
+/* We index with the invitor because the invitor is the first party
+ * that provides us with complete information i.e the IP address
+ * and the call number, which we need. Remember, though that
+ * call numbers are scarce by nature and may be reused. */
+void Iax2Session::ProcessMetadataIax2Incoming()
+{
+ char szNewSrcIp[16];
+
+ ACE_OS::inet_ntop(AF_INET, (void*)&m_invitorIp, szNewSrcIp, sizeof(szNewSrcIp));
+
+ m_remoteParty = m_new->m_caller;
+ m_localParty = m_new->m_callee;
+ m_direction = CaptureEvent::DirIn;
+ m_localIp = m_inviteeIp;
+ m_remoteIp = m_invitorIp;
+ m_capturePort.Format("%s,%d", szNewSrcIp, m_invitor_scallno);
+}
+
+/* We index with the invitor because the invitor is the first party
+ * that provides us with complete information i.e the IP address
+ * and the call number, which we need. Remember, though that
+ * call numbers are scarce by nature and may be reused. */
+void Iax2Session::ProcessMetadataIax2Outgoing()
+{
+ char szNewSrcIp[16];
+
+ ACE_OS::inet_ntop(AF_INET, (void*)&m_invitorIp, szNewSrcIp, sizeof(szNewSrcIp));
+ m_remoteParty = m_new->m_callee;
+ m_localParty = m_new->m_caller;
+ m_direction = CaptureEvent::DirOut;
+ m_capturePort.Format("%s,%d", szNewSrcIp, m_invitor_scallno);
+ m_localIp = m_invitorIp;
+ m_remoteIp = m_inviteeIp;
+}
+
+/* Is this function ever necessary? */
+void Iax2Session::UpdateMetadataIax2(Iax2PacketInfoRef& iax2Packet, bool sourceIax2AddressIsNew)
+{
+ // Find out if the new IAX2 packet could match one of the IAX2 NEW messages associated with the session
+ Iax2NewInfoRef invite, tmpNew;
+ std::list<Iax2NewInfoRef>::iterator it;
+
+ for(it = m_news.begin(); it != m_news.end(); it++) {
+ tmpNew = *it;
+
+ if(tmpNew->m_validated)
+ break;
+
+ if(sourceIax2AddressIsNew) {
+ if((unsigned int)(iax2Packet->m_sourceIp.s_addr) ==
+ (unsigned int)(tmpNew->m_receiverIp.s_addr))
+ invite = tmpNew;
+ } else {
+ if((unsigned int)(iax2Packet->m_destIp.s_addr) ==
+ (unsigned int)(tmpNew->m_receiverIp.s_addr))
+ invite = tmpNew;
+ }
+ }
+
+ if(invite.get()) {
+ CStdString inviteString, iax2String, logMsg;
+
+ // The NEW has generated an IAX2 voice stream
+ invite->m_validated = true;
+
+ // Update session metadata with NEW info
+ m_remoteParty = invite->m_caller;
+ m_localParty = invite->m_callee;
+ m_localIp = invite->m_receiverIp;
+
+ // Do some logging
+ invite->ToString(inviteString);
+ iax2Packet->ToString(iax2String);
+
+ logMsg.Format("[%s] metadata update: local:%s remote:%s "
+ "IAX2 Pkt:%s NEW Info:%s", m_trackingId, m_localParty,
+ m_remoteParty, iax2String, inviteString);
+ LOG4CXX_INFO(m_log, logMsg);
+
+ // Report Local party
+ CaptureEventRef event(new CaptureEvent());
+ event->m_type = CaptureEvent::EtLocalParty;
+ event->m_value = m_localParty;
+ g_captureEventCallBack(event, m_capturePort);
+
+ // Report remote party
+ event.reset(new CaptureEvent());
+ event->m_type = CaptureEvent::EtRemoteParty;
+ event->m_value = m_remoteParty;
+ g_captureEventCallBack(event, m_capturePort);
+
+ // Report Local IP
+ char szLocalIp[16];
+ ACE_OS::inet_ntop(AF_INET, (void*)&m_localIp, szLocalIp, sizeof(szLocalIp));
+ event.reset(new CaptureEvent());
+ event->m_type = CaptureEvent::EtLocalIp;
+ event->m_value = szLocalIp;
+ g_captureEventCallBack(event, m_capturePort);
+
+ // Trigger metadata update
+ event.reset(new CaptureEvent());
+ event->m_type = CaptureEvent::EtUpdate;
+ g_captureEventCallBack(event, m_capturePort);
+ }
+}
+
+void Iax2Session::ProcessMetadataIax2(Iax2PacketInfoRef& iax2Packet)
+{
+ /* This is the first full IAX2_FRAME_VOICE frame which will tell us
+ * the codec being used */
+ if(((unsigned int)iax2Packet->m_sourceIp.s_addr != (unsigned int)m_invitorIp.s_addr) &&
+ ((unsigned int)iax2Packet->m_destIp.s_addr != (unsigned int)m_invitorIp.s_addr))
+ {
+ LOG4CXX_ERROR(m_log, "[" + m_trackingId + "] " + m_srcIpAndCallNo + " alien IAX2 packet");
+ }
+
+ /* Obtain the codec information */
+ m_codec = iax2Packet->m_payloadType;
+
+ // work out capture port and direction
+ if(DLLCONFIG.IsMediaGateway(m_invitorIp)) {
+ if(DLLCONFIG.IsMediaGateway(m_inviteeIp)) {
+ // Media gateway talking to media gateway, this is probably incoming
+ ProcessMetadataIax2Incoming();
+ } else if(DLLCONFIG.IsPartOfLan(m_inviteeIp)) {
+ // Gateway to LAN, this is pobably incoming
+ ProcessMetadataIax2Incoming();
+ } else {
+ // Gateway to outside address, probably outgoing but treat as incoming for now because
+ // It can be due to misconfigured LAN Mask, odds are it's still incoming.
+ ProcessMetadataIax2Incoming();
+ }
+ } else if (DLLCONFIG.IsPartOfLan(m_invitorIp)) {
+ ProcessMetadataIax2Outgoing();
+ } else {
+ // SIP invitor media IP address is an outside IP address
+ if(DLLCONFIG.IsMediaGateway(m_inviteeIp)) {
+ ProcessMetadataIax2Incoming();
+ } else if(DLLCONFIG.IsPartOfLan(m_inviteeIp)) {
+ ProcessMetadataIax2Incoming();
+ } else {
+ // SIP invitee media address is an outside IP address
+ ProcessMetadataIax2Outgoing();
+ }
+ }
+}
+
+void Iax2Session::ReportMetadata()
+{
+ char szLocalIp[16], szRemoteIp[16];
+
+ ACE_OS::inet_ntop(AF_INET, (void*)&m_localIp, szLocalIp, sizeof(szLocalIp));
+ ACE_OS::inet_ntop(AF_INET, (void*)&m_remoteIp, szRemoteIp, sizeof(szRemoteIp));
+
+ // Make sure Local Party is always reported
+ if(m_localParty.IsEmpty()) {
+ m_localParty = szLocalIp;
+ }
+
+ // Report Local party
+ CaptureEventRef event(new CaptureEvent());
+ event->m_type = CaptureEvent::EtLocalParty;
+ event->m_value = m_localParty;
+ g_captureEventCallBack(event, m_capturePort);
+
+ // Report remote party
+ event.reset(new CaptureEvent());
+ event->m_type = CaptureEvent::EtRemoteParty;
+ event->m_value = m_remoteParty;
+ g_captureEventCallBack(event, m_capturePort);
+
+ // Report direction
+ event.reset(new CaptureEvent());
+ event->m_type = CaptureEvent::EtDirection;
+ event->m_value = CaptureEvent::DirectionToString(m_direction);
+ g_captureEventCallBack(event, m_capturePort);
+
+ // Report Local IP address
+ event.reset(new CaptureEvent());
+ event->m_type = CaptureEvent::EtLocalIp;
+ event->m_value = szLocalIp;
+ g_captureEventCallBack(event, m_capturePort);
+
+ // Report Remote IP address
+ event.reset(new CaptureEvent());
+ event->m_type = CaptureEvent::EtRemoteIp;
+ event->m_value = szRemoteIp;
+ g_captureEventCallBack(event, m_capturePort);
+
+ // Report OrkUid
+ event.reset(new CaptureEvent());
+ event->m_type = CaptureEvent::EtOrkUid;
+ event->m_value = m_orkUid;
+ g_captureEventCallBack(event, m_capturePort);
+
+ // Report native Call ID
+ event.reset(new CaptureEvent());
+ event->m_type = CaptureEvent::EtCallId;
+ event->m_value = m_srcIpAndCallNo;
+ g_captureEventCallBack(event, m_capturePort);
+
+ // Report end of metadata
+ event.reset(new CaptureEvent());
+ event->m_type = CaptureEvent::EtEndMetadata;
+ g_captureEventCallBack(event, m_capturePort);
+}
+
+static char *iax2_state_to_str(int iax2state)
+{
+ switch(iax2state) {
+ case IAX2_STATE_WAITING:
+ return "WAITING";
+ case IAX2_STATE_LINKED:
+ return "LINKED";
+ case IAX2_STATE_UP:
+ return "UP";
+ default:
+ return "UNKNOWN";
+ }
+
+ /* NOT REACHED */
+ return "UNKNOWN";
+}
+
+/*
+ * Returns false if packet is received out-of-sync (when we are in the wrong state)
+ */
+bool Iax2Session::AddIax2Packet(Iax2PacketInfoRef& iax2Packet)
+{
+ CStdString logMsg;
+ unsigned char channel = 0;
+
+ /* What is our state? We need to be in the IAX2_STATE_LINKED state
+ * or the IAX2_STATE_UP state to receive any voice frames */
+ if((m_iax2_state != IAX2_STATE_LINKED) && (m_iax2_state != IAX2_STATE_UP)) {
+ logMsg.Format("[%s] receiving voice packets while in %s state? "
+ "Destroying session...", m_trackingId,
+ iax2_state_to_str(m_iax2_state));
+ return false;
+ }
+
+ /* We do not have a voice packet yet */
+ if(m_lastIax2Packet.get() == NULL) {
+ /* The first IAX2 voice frame is expected to be a full frame,
+ * complete with the payload type information. If this is not
+ * the case, we will continue dropping any mini frames */
+
+ if(iax2Packet->m_frame_type != IAX2_FRAME_FULL) {
+ logMsg.Format("[%s] 1st packet is not a full frame! Dropping...", m_trackingId);
+ LOG4CXX_ERROR(m_log, logMsg);
+ return true;
+ }
+
+ ProcessMetadataIax2(iax2Packet);
+ m_iax2_state = IAX2_STATE_UP;
+ }
+
+ /* If we get another full voice packet, we need to update our codec
+ * information */
+ if(iax2Packet->m_frame_type == IAX2_FRAME_FULL)
+ m_codec = iax2Packet->m_payloadType;
+
+ m_lastIax2Packet = iax2Packet;
+ if(m_lastIax2PacketSide1.get() == NULL) {
+ // First IAX2 packet for side 1
+ m_lastIax2PacketSide1 = iax2Packet;
+ channel = 1;
+ if(m_log->isInfoEnabled())
+ {
+ iax2Packet->ToString(logMsg);
+ logMsg = "[" + m_trackingId + "] 1st packet s1: " + logMsg;
+ LOG4CXX_INFO(m_log, logMsg);
+ }
+ } else {
+ // Comparing destination IP address to find out if side1, see (1)
+ if((unsigned int)iax2Packet->m_destIp.s_addr == (unsigned int)m_lastIax2PacketSide1->m_destIp.s_addr)
+ {
+ if(iax2Packet->m_timestamp == m_lastIax2PacketSide1->m_timestamp)
+ {
+ m_hasDuplicateIax2 = true;
+ return true; // dismiss duplicate IAX2 packet
+ }
+ /* XXX Detect discontinuity using timestamps? */
+ /*
+ else
+ {
+ double seqNumDelta = (double)iax2Packet->m_seqNum - (double)m_lastIax2PacketSide1->m_seqNum;
+ if(DLLCONFIG.m_iax2DiscontinuityDetect)
+ {
+ double timestampDelta = (double)iax2Packet->m_timestamp - (double)m_lastIax2PacketSide1->m_timestamp;
+ if( abs(seqNumDelta) > m_minIax2SeqDelta &&
+ abs(timestampDelta) > m_minIax2TimestampDelta)
+ {
+ logMsg.Format("[%s] IAX2 discontinuity s1: before: seq:%u ts:%u after: seq:%u ts:%u",
+ m_trackingId, m_lastIax2PacketSide1->m_seqNum, m_lastIax2PacketSide1->m_timestamp,
+ iax2Packet->m_seqNum, iax2Packet->m_timestamp);
+ LOG4CXX_INFO(m_log, logMsg);
+ return false;
+ }
+ }
+ if(seqNumDelta > (double)m_highestIax2SeqNumDelta)
+ {
+ m_highestIax2SeqNumDelta = (unsigned int)seqNumDelta;
+ }
+ } */
+ m_lastIax2PacketSide1 = iax2Packet;
+ channel = 1;
+ }
+ else
+ {
+ if(m_lastIax2PacketSide2.get() == NULL)
+ {
+ // First IAX2 packet for side 2
+ if(m_log->isInfoEnabled())
+ {
+ iax2Packet->ToString(logMsg);
+ logMsg = "[" + m_trackingId + "] 1st packet s2: " + logMsg;
+ LOG4CXX_INFO(m_log, logMsg);
+ }
+ }
+ else
+ {
+ if(iax2Packet->m_timestamp == m_lastIax2PacketSide2->m_timestamp)
+ {
+ m_hasDuplicateIax2 = true;
+ return true; // dismiss duplicate IAX2 packet
+ }
+ /* XXX Detect discontinuity using timestamps? */
+ /*
+ else
+ {
+ double seqNumDelta = (double)iax2Packet->m_seqNum - (double)m_lastIax2PacketSide2->m_seqNum;
+ if(DLLCONFIG.m_iax2DiscontinuityDetect)
+ {
+ double timestampDelta = (double)iax2Packet->m_timestamp - (double)m_lastIax2PacketSide2->m_timestamp;
+ if( abs(seqNumDelta) > m_minIax2SeqDelta &&
+ abs(timestampDelta) > m_minIax2TimestampDelta)
+ {
+ logMsg.Format("[%s] IAX2 discontinuity s2: before: seq:%u ts:%u after: seq:%u ts:%u",
+ m_trackingId, m_lastIax2PacketSide2->m_seqNum, m_lastIax2PacketSide2->m_timestamp,
+ iax2Packet->m_seqNum, iax2Packet->m_timestamp);
+ LOG4CXX_INFO(m_log, logMsg);
+ return false;
+ }
+ }
+ if(seqNumDelta > (double)m_highestIax2SeqNumDelta)
+ {
+ m_highestIax2SeqNumDelta = (unsigned int)seqNumDelta;
+ }
+ }*/
+ }
+ m_lastIax2PacketSide2 = iax2Packet;
+ channel = 2;
+ }
+ }
+
+ m_numIax2Packets++;
+
+ /* Can stream change happen with IAX2??
+ bool hasSourceAddress = m_iax2AddressList.HasAddressOrAdd(iax2Packet->m_sourceIp, iax2Packet->m_sourcePort);
+ bool hasDestAddress = m_iax2AddressList.HasAddressOrAdd(iax2Packet->m_destIp, iax2Packet->m_destPort);
+ if( hasSourceAddress == false || hasDestAddress == false )
+ {
+ iax2Packet->ToString(logMsg);
+ logMsg.Format("[%s] new IAX2 stream s%d: %s",
+ m_trackingId, channel, logMsg);
+ LOG4CXX_INFO(m_log, logMsg);
+
+ if(m_protocol == ProtIax2 && m_started) // make sure this only happens if ReportMetadata() already been called for the session
+ {
+ UpdateMetadataIax2(iax2Packet, hasDestAddress);
+ }
+ }
+ */
+
+ if(m_numIax2Packets == 1) {
+ Start();
+ ReportMetadata();
+ }
+
+ if(m_started) {
+ AudioChunkDetails details;
+ AudioChunkRef chunk(new AudioChunk());
+
+ details.m_arrivalTimestamp = iax2Packet->m_arrivalTimestamp;
+ details.m_numBytes = iax2Packet->m_payloadSize;
+ details.m_timestamp = iax2Packet->m_timestamp;
+ details.m_rtpPayloadType = m_codec;
+ details.m_channel = channel;
+ details.m_encoding = AlawAudio;
+
+ chunk->SetBuffer(iax2Packet->m_payload, details);
+ g_audioChunkCallBack(chunk, m_capturePort);
+
+ m_lastUpdated = iax2Packet->m_arrivalTimestamp;
+ }
+
+ return true;
+}
+
+/* Report AUTHREQ so as to get the invitee call number */
+void Iax2Session::ReportIax2Authreq(Iax2AuthreqInfoRef& authreq)
+{
+ m_invitee_scallno = StringToInt(authreq->m_sender_callno);
+}
+
+/* Obtain the invitee call number from the ACCEPT, in case an AUTHREQ
+ * was not sent */
+void Iax2Session::ReportIax2Accept(Iax2AcceptInfoRef& acceptinfo)
+{
+ m_invitee_scallno = StringToInt(acceptinfo->m_sender_callno);
+}
+
+/* Report NEW */
+void Iax2Session::ReportIax2New(Iax2NewInfoRef& invite)
+{
+ if(m_new.get() == NULL) {
+ char szFromIax2Ip[16];
+
+ ACE_OS::inet_ntop(AF_INET, (void*)&invite->m_senderIp, szFromIax2Ip, sizeof(szFromIax2Ip));
+
+ m_new = invite;
+ m_invitorIp = invite->m_senderIp;
+ m_inviteeIp = invite->m_receiverIp;
+ m_invitor_scallno = StringToInt(invite->m_callNo);
+ m_srcIpAndCallNo = CStdString(szFromIax2Ip) + "," + invite->m_callNo;
+ } else {
+ CStdString inviteString;
+ invite->ToString(inviteString);
+ CStdString logMsg;
+ logMsg.Format("[%s] associating NEW:%s", m_trackingId, inviteString);
+ LOG4CXX_INFO(m_log, logMsg);
+ }
+
+ m_news.push_front(invite);
+}
+
+
+//=====================================================================
+Iax2Sessions::Iax2Sessions()
+{
+ m_log = Logger::getLogger("iax2sessions");
+ if(CONFIG.m_debug) {
+ m_alphaCounter.Reset();
+ }
+}
+
+/* In the cases where AUTHREQ was not sent, we need to still obtain the
+ * invitee call number and make a map for it */
+void Iax2Sessions::ReportIax2Accept(Iax2AcceptInfoRef& acceptinfo)
+{
+ CStdString srcIpAndCallNo, log_msg, destIpAndCallNo, numSessions, acceptString;
+ std::map<CStdString, Iax2SessionRef>::iterator pair;
+ char invitor_ip[16], invitee_ip[16];
+ Iax2SessionRef session;
+
+ /* Assumption is that the receiver is the invitor */
+ ACE_OS::inet_ntop(AF_INET, (void*)&acceptinfo->m_receiverIp, invitor_ip, sizeof(invitor_ip));
+ ACE_OS::inet_ntop(AF_INET, (void*)&acceptinfo->m_senderIp, invitee_ip, sizeof(invitee_ip));
+ srcIpAndCallNo = CStdString(invitor_ip) + "," + acceptinfo->m_receiver_callno;
+ destIpAndCallNo = CStdString(invitee_ip) + "," + acceptinfo->m_sender_callno;
+
+ pair = m_bySrcIpAndCallNo.find(srcIpAndCallNo);
+ if(pair != m_bySrcIpAndCallNo.end()) {
+ session = pair->second;
+ if(session.get() != NULL) {
+ /* Excellent! We have an existing session. Now check if we have
+ * already mapped the invitee call number and IP address */
+
+ pair = m_byDestIpAndCallNo.find(destIpAndCallNo);
+ if(pair != m_byDestIpAndCallNo.end()) {
+ /* We already have a mapping and all necessary information */
+
+ session = pair->second;
+ if(session.get() != NULL) {
+ session->m_iax2_state = IAX2_STATE_LINKED;
+ LOG4CXX_INFO(m_log, "[" + session->m_trackingId + "] "
+ "received ACCEPT. Session already "
+ "registered. Now LINKED.");
+ return;
+ }
+ }
+
+ /* We need to enter the information */
+ log_msg.Format("[%s] got ACCEPT for this session. Registering "
+ "sender call number", session->m_trackingId);
+ LOG4CXX_INFO(m_log, log_msg);
+
+ session->m_destIpAndCallNo = destIpAndCallNo;
+ session->m_iax2_state = IAX2_STATE_LINKED;
+
+ m_byDestIpAndCallNo.insert(std::make_pair(session->m_destIpAndCallNo, session));
+ session->ReportIax2Accept(acceptinfo);
+
+ acceptinfo->ToString(acceptString);
+ LOG4CXX_INFO(m_log, "[" + session->m_trackingId + "] supplemented "
+ "by IAX2 ACCEPT:" + acceptString + ". Status now LINKED");
+
+ return;
+ }
+ }
+
+ /* We don't have an existing session? */
+ acceptinfo->ToString(acceptString);
+ log_msg.Format("Got ACCEPT [%s] but couldn't find corresponding NEW", acceptString);
+ LOG4CXX_ERROR(m_log, log_msg);
+
+ return;
+}
+
+/* After a NEW has been sent, an AUTHREQ may be sent by the sender and this
+ * is the stage where we grab the callno of the sender */
+void Iax2Sessions::ReportIax2Authreq(Iax2AuthreqInfoRef& authreq)
+{
+ CStdString srcIpAndCallNo, log_msg, destIpAndCallNo, numSessions, authreqString;
+ std::map<CStdString, Iax2SessionRef>::iterator pair;
+ char invitor_ip[16], invitee_ip[16];
+ Iax2SessionRef session;
+
+ /* Assumption is that the receiver is the invitor */
+ ACE_OS::inet_ntop(AF_INET, (void*)&authreq->m_receiverIp, invitor_ip, sizeof(invitor_ip));
+ ACE_OS::inet_ntop(AF_INET, (void*)&authreq->m_senderIp, invitee_ip, sizeof(invitee_ip));
+ srcIpAndCallNo = CStdString(invitor_ip) + "," + authreq->m_receiver_callno;
+ destIpAndCallNo = CStdString(invitee_ip) + "," + authreq->m_sender_callno;
+
+ pair = m_bySrcIpAndCallNo.find(srcIpAndCallNo);
+ if(pair != m_bySrcIpAndCallNo.end()) {
+ /* Excellent! We have an existing session */
+ session = pair->second;
+
+ if(session.get() != NULL) {
+ session->m_destIpAndCallNo = destIpAndCallNo;
+ m_byDestIpAndCallNo.insert(std::make_pair(session->m_destIpAndCallNo, session));
+ session->ReportIax2Authreq(authreq);
+ session->m_iax2_state = IAX2_STATE_WAITING;
+
+ authreq->ToString(authreqString);
+ LOG4CXX_INFO(m_log, "[" + session->m_trackingId + "] "
+ "supplemented by IAX2 AUTHREQ:" + authreqString +
+ ". Status now WAITING");
+
+ return;
+ }
+ }
+
+ /* We don't have an existing session? */
+ authreq->ToString(authreqString);
+ log_msg.Format("Got AUTHREQ [%s] but couldn't find corresponding NEW", authreqString);
+ LOG4CXX_ERROR(m_log, log_msg);
+
+ return;
+}
+
+void Iax2Sessions::ReportIax2New(Iax2NewInfoRef& invite)
+{
+ char szFromIax2Ip[16];
+ std::map<CStdString, Iax2SessionRef>::iterator pair;
+
+ ACE_OS::inet_ntop(AF_INET, (void*)&invite->m_senderIp, szFromIax2Ip, sizeof(szFromIax2Ip));
+ CStdString IpAndCallNo = CStdString(szFromIax2Ip) + "," + invite->m_callNo;
+
+ pair = m_bySrcIpAndCallNo.find(IpAndCallNo);
+ if (pair != m_bySrcIpAndCallNo.end()) {
+ // The session already exists, check the state
+ CStdString logmsg;
+
+ Iax2SessionRef session = pair->second;
+
+ if(session.get() != NULL) {
+ if(session->m_iax2_state == IAX2_STATE_UP) {
+ CStdString log_msg;
+
+ log_msg.Format("[%s] is in the UP state but we've "
+ "got another NEW from this same IP %s and same "
+ "source call number %s", session->m_trackingId,
+ szFromIax2Ip, invite->m_callNo);
+
+ LOG4CXX_ERROR(m_log, log_msg);
+ return;
+ }
+ }
+
+ /* Stop this session and proceed */
+ Stop(session);
+ }
+
+ /* Create a new session */
+ CStdString trackingId = m_alphaCounter.GetNext();
+ Iax2SessionRef session(new Iax2Session(trackingId));
+ session->m_srcIpAndCallNo = IpAndCallNo;
+ session->ReportIax2New(invite);
+ m_bySrcIpAndCallNo.insert(std::make_pair(session->m_srcIpAndCallNo, session));
+
+ CStdString numSessions = IntToString(m_bySrcIpAndCallNo.size());
+ LOG4CXX_DEBUG(m_log, CStdString("BySrcIpAndCallNo: ") + numSessions);
+
+ CStdString inviteString;
+ invite->ToString(inviteString);
+ LOG4CXX_INFO(m_log, "[" + trackingId + "] created by IAX2 NEW:" + inviteString + " for " + session->m_srcIpAndCallNo);
+}
+
+/* This function is called as a result of a) an IAX2 HANGUP; b) a
+ * CONTROL HANGUP; or c) an IAX2 REJECT */
+void Iax2Sessions::ReportIax2Hangup(Iax2HangupInfoRef& bye)
+{
+ CStdString senderIpAndCallNo, log_msg, receiverIpAndCallNo, numSessions, hangupString;
+ std::map<CStdString, Iax2SessionRef>::iterator pair;
+ char sender_ip[16], receiver_ip[16];
+ Iax2SessionRef session;
+
+ ACE_OS::inet_ntop(AF_INET, (void*)&bye->m_receiverIp, receiver_ip, sizeof(receiver_ip));
+ ACE_OS::inet_ntop(AF_INET, (void*)&bye->m_senderIp, sender_ip, sizeof(sender_ip));
+ receiverIpAndCallNo = CStdString(receiver_ip) + "," + bye->m_receiver_callno;
+ senderIpAndCallNo = CStdString(sender_ip) + "," + bye->m_sender_callno;
+
+ /* The recipient of the HANGUP is the Invitor */
+ pair = m_bySrcIpAndCallNo.find(receiverIpAndCallNo);
+ if (pair != m_bySrcIpAndCallNo.end()) {
+ Iax2SessionRef session = pair->second;
+ if(session.get() != NULL) {
+ log_msg.Format("[%s] %s: Hanging up session", session->m_trackingId, session->m_srcIpAndCallNo);
+ LOG4CXX_INFO(m_log, log_msg);
+ Stop(session);
+ }
+ }
+
+ /* The sender of the HANGUP is the Invitor */
+ pair = m_bySrcIpAndCallNo.find(senderIpAndCallNo);
+ if (pair != m_bySrcIpAndCallNo.end()) {
+ Iax2SessionRef session = pair->second;
+ if(session.get() != NULL) {
+ log_msg.Format("[%s] %s: Hanging up session", session->m_trackingId, session->m_srcIpAndCallNo);
+ LOG4CXX_INFO(m_log, log_msg);
+ Stop(session);
+ }
+ }
+
+ /* The recipient of the HANGUP is the Invitee */
+ pair = m_byDestIpAndCallNo.find(receiverIpAndCallNo);
+ if (pair != m_byDestIpAndCallNo.end()) {
+ Iax2SessionRef session = pair->second;
+ if(session.get() != NULL) {
+ log_msg.Format("[%s] %s: Hanging up session", session->m_trackingId, session->m_srcIpAndCallNo);
+ LOG4CXX_INFO(m_log, log_msg);
+ Stop(session);
+ }
+ }
+
+ /* The sender of the HANGUP is the Invitee */
+ pair = m_byDestIpAndCallNo.find(senderIpAndCallNo);
+ if (pair != m_byDestIpAndCallNo.end()) {
+ Iax2SessionRef session = pair->second;
+ if(session.get() != NULL) {
+ log_msg.Format("[%s] %s: Hanging up session", session->m_trackingId, session->m_srcIpAndCallNo);
+ LOG4CXX_INFO(m_log, log_msg);
+ Stop(session);
+ }
+ }
+}
+
+void Iax2Sessions::Stop(Iax2SessionRef& session)
+{
+ CStdString numSessions;
+
+ session->Stop();
+
+ if(session->m_srcIpAndCallNo.size() > 0) {
+ m_bySrcIpAndCallNo.erase(session->m_srcIpAndCallNo);
+
+ numSessions = IntToString(m_bySrcIpAndCallNo.size());
+ LOG4CXX_DEBUG(m_log, CStdString("BySrcIpAndPort: ") + numSessions);
+ }
+
+ if(session->m_destIpAndCallNo.size() > 0) {
+ m_byDestIpAndCallNo.erase(session->m_destIpAndCallNo);
+
+ numSessions = IntToString(m_byDestIpAndCallNo.size());
+ LOG4CXX_DEBUG(m_log, CStdString("ByDestIpAndPort: ") + numSessions);
+ }
+}
+
+
+void Iax2Sessions::ReportIax2Packet(Iax2PacketInfoRef& iax2Packet)
+{
+ Iax2SessionRef session;
+ CStdString logMsg, sourcecallno, IpAndCallNo;
+ std::map<CStdString, Iax2SessionRef>::iterator pair;
+ char szSourceIp[16];
+
+ /* Add this IAX2 voice frame to a session which matches either the source
+ * IP address and call number or the destination IP address and call number.
+ * In this context "source IP" refers to the IP address of the machine which
+ * initiated the session i.e sent the "NEW". Likewise "destination IP"
+ * refers to the machine to whom the "NEW" was sent. There is no chance
+ * that there could be a duplicate */
+
+ ACE_OS::inet_ntop(AF_INET, (void*)&iax2Packet->m_sourceIp, szSourceIp,
+ sizeof(szSourceIp));
+ sourcecallno = IntToString(iax2Packet->m_sourcecallno);
+
+ IpAndCallNo = CStdString(szSourceIp) + "," + sourcecallno;
+
+ pair = m_bySrcIpAndCallNo.find(IpAndCallNo);
+ if(pair != m_bySrcIpAndCallNo.end()) {
+ session = pair->second;
+
+ if(session.get() != NULL) {
+ if(!session->AddIax2Packet(iax2Packet)) {
+ /* Discontinuity detected? */
+ Stop(session);
+ }
+ }
+
+ return;
+ }
+
+ /* Search in the destination IP map */
+ pair = m_byDestIpAndCallNo.find(IpAndCallNo);
+ if(pair != m_byDestIpAndCallNo.end()) {
+ session = pair->second;
+
+ if(session.get() != NULL) {
+ if(!session->AddIax2Packet(iax2Packet)) {
+ /* Discontinuity detected? */
+ Stop(session);
+ }
+ }
+
+ return;
+ }
+
+ /* XXX Tracking?? */
+ CStdString pktinfo;
+ iax2Packet->ToString(pktinfo);
+ //LOG4CXX_INFO(m_log, "Could not figure out where to place packet from "+IpAndCallNo+": [" + pktinfo +"]");
+}
+
+void Iax2Sessions::StopAll()
+{
+ time_t forceExpiryTime = time(NULL) + 2*DLLCONFIG.m_rtpSessionWithSignallingTimeoutSec;
+ Hoover(forceExpiryTime);
+}
+
+void Iax2Sessions::Hoover(time_t now)
+{
+ CStdString numSessions;
+ int timeoutSeconds = 0;
+ Iax2SessionRef session;
+ std::map<CStdString, Iax2SessionRef>::iterator pair;
+ std::list<Iax2SessionRef> toDismiss;
+ std::list<Iax2SessionRef>::iterator it;
+
+ numSessions = IntToString(m_bySrcIpAndCallNo.size());
+ LOG4CXX_DEBUG(m_log, "Hoover - check " + numSessions + " sessions time:" + IntToString(now));
+ timeoutSeconds = DLLCONFIG.m_rtpSessionWithSignallingTimeoutSec;
+
+ for(pair=m_bySrcIpAndCallNo.begin(); pair!=m_bySrcIpAndCallNo.end(); pair++) {
+ session = pair->second;
+
+ if((now - session->m_lastUpdated) > timeoutSeconds)
+ toDismiss.push_back(session);
+ }
+
+ for (it=toDismiss.begin(); it!=toDismiss.end(); it++) {
+ session = *it;
+ LOG4CXX_INFO(m_log, "[" + session->m_trackingId + "] " + session->m_srcIpAndCallNo + " Expired");
+ Stop(session);
+ }
+
+ /* Just in case? */
+ toDismiss.clear();
+ for(pair=m_byDestIpAndCallNo.begin(); pair!=m_byDestIpAndCallNo.end(); pair++) {
+ session = pair->second;
+
+ if((now - session->m_lastUpdated) > timeoutSeconds)
+ toDismiss.push_back(session);
+ }
+
+ for (it=toDismiss.begin(); it!=toDismiss.end(); it++) {
+ session = *it;
+ LOG4CXX_INFO(m_log, "[" + session->m_trackingId + "] " + session->m_destIpAndCallNo + " Expired");
+ Stop(session);
+ }
+}
+
+//==========================================================
+Iax2NewInfo::Iax2NewInfo()
+{
+ m_senderIp.s_addr = 0;
+ m_receiverIp.s_addr = 0;
+ m_caller = "HIDDEN"; /* Unless obtained */
+ m_validated= false;
+}
+
+void Iax2NewInfo::ToString(CStdString& string)
+{
+ char senderIp[16], receiverIp[16];
+
+ ACE_OS::inet_ntop(AF_INET, (void*)&m_senderIp, senderIp, sizeof(senderIp));
+ ACE_OS::inet_ntop(AF_INET, (void*)&m_receiverIp, receiverIp,
+ sizeof(receiverIp));
+
+ string.Format("sender:%s receiver: %s caller:%s callee:%s srccallno: %s",
+ senderIp, receiverIp, m_caller, m_callee, m_callNo);
+}
+
+//==========================================================
+Iax2AuthreqInfo::Iax2AuthreqInfo()
+{
+ m_senderIp.s_addr = 0;
+ m_receiverIp.s_addr = 0;
+}
+
+void Iax2AuthreqInfo::ToString(CStdString& string)
+{
+ char senderIp[16], receiverIp[16];
+
+ ACE_OS::inet_ntop(AF_INET, (void*)&m_senderIp, senderIp, sizeof(senderIp));
+ ACE_OS::inet_ntop(AF_INET, (void*)&m_receiverIp, receiverIp,
+ sizeof(receiverIp));
+
+ string.Format("sender:%s receiver:%s sender_callno:%s receiver_callno:%s",
+ senderIp, receiverIp, m_sender_callno, m_receiver_callno);
+}
+
+//==========================================================
+Iax2AcceptInfo::Iax2AcceptInfo()
+{
+ m_senderIp.s_addr = 0;
+ m_receiverIp.s_addr = 0;
+}
+
+void Iax2AcceptInfo::ToString(CStdString& string)
+{
+ char senderIp[16], receiverIp[16];
+
+ ACE_OS::inet_ntop(AF_INET, (void*)&m_senderIp, senderIp, sizeof(senderIp));
+ ACE_OS::inet_ntop(AF_INET, (void*)&m_receiverIp, receiverIp,
+ sizeof(receiverIp));
+
+ string.Format("sender:%s receiver:%s sender_callno:%s receiver_callno:%s",
+ senderIp, receiverIp, m_sender_callno, m_receiver_callno);
+}
+
+//==========================================================
+Iax2HangupInfo::Iax2HangupInfo()
+{
+ m_senderIp.s_addr = 0;
+ m_receiverIp.s_addr = 0;
+}
+
+void Iax2HangupInfo::ToString(CStdString& string)
+{
+ char senderIp[16], receiverIp[16];
+
+ ACE_OS::inet_ntop(AF_INET, (void*)&m_senderIp, senderIp, sizeof(senderIp));
+ ACE_OS::inet_ntop(AF_INET, (void*)&m_receiverIp, receiverIp,
+ sizeof(receiverIp));
+
+ string.Format("sender:%s receiver:%s sender_callno:%s receiver_callno:%s",
+ senderIp, receiverIp, m_sender_callno, m_receiver_callno);
+}
+
+//==========================================================
+Iax2PacketInfo::Iax2PacketInfo()
+{
+}
+
+void Iax2PacketInfo::ToString(CStdString& string)
+{
+ char senderIp[16], receiverIp[16];
+
+ ACE_OS::inet_ntop(AF_INET, (void*)&m_sourceIp, senderIp, sizeof(senderIp));
+ ACE_OS::inet_ntop(AF_INET, (void*)&m_destIp, receiverIp,
+ sizeof(receiverIp));
+
+ string.Format("sender:%s receiver:%s sender_callno:%d receiver_callno:%d "
+ "type:%d size:%d timestamp: %d", senderIp, receiverIp,
+ m_sourcecallno, m_destcallno, m_payloadType,
+ m_payloadSize, m_timestamp);
+}
+
diff --git a/orkaudio/audiocaptureplugins/voip/Iax2Session.h b/orkaudio/audiocaptureplugins/voip/Iax2Session.h
new file mode 100644
index 0000000..ef1944f
--- /dev/null
+++ b/orkaudio/audiocaptureplugins/voip/Iax2Session.h
@@ -0,0 +1,275 @@
+/*
+ * 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
+ *
+ */
+
+#ifndef __IAX2SESSION_H__
+#define __IAX2SESSION_H__
+
+#include <log4cxx/logger.h>
+#include "Iax2Session.h"
+#include <map>
+#include "ace/Singleton.h"
+#include "PacketHeaderDefs.h"
+
+using namespace log4cxx;
+
+/* The frame types we're interested in */
+#define IAX2_FRAME_VOICE 2
+#define IAX2_FRAME_CONTROL 4
+#define IAX2_FRAME_IAX 6
+
+/* The full frame subclass values for IAX2_FRAME_IAX frames
+ * which we're interested in */
+#define IAX2_COMMAND_NEW 1
+#define IAX2_COMMAND_HANGUP 5
+#define IAX2_COMMAND_REJECT 6
+#define IAX2_COMMAND_ACCEPT 7
+#define IAX2_COMMAND_AUTHREQ 8
+
+/* The control frame subclass values for IAX2_FRAME_CONTROL
+ * which we're interested in */
+#define IAX2_CONTROL_HANGUP 1
+
+/* The information elements we're interested in */
+#define IAX2_IE_CALLED_NUMBER 1
+#define IAX2_IE_CALLING_NUMBER 2
+#define IAX2_IE_USERNAME 6
+#define IAX2_IE_FORMAT 9
+#define IAX2_IE_AUTHMETHODS 14
+#define IAX2_IE_CHALLENGE 15
+
+struct iax2_ies {
+ char *caller;
+ char *callee; /* Mandatory for NEW */
+ unsigned int format; /* Format Mandatory for ACCEPT */
+ char *username; /* Mandatory for AUTHREQ */
+ unsigned int authmethods; /* Mandatory for AUTHREQ */
+ char *challenge; /* Mandatory for AUTHREQ */
+};
+
+/* Supported Voice Codecs */
+/* G.723.1 compression */
+#define IAX2_CODEC_G723_1 (1 << 0)
+/* GSM compression */
+#define IAX2_CODEC_GSM (1 << 1)
+/* Raw mu-law data (G.711) */
+#define IAX2_CODEC_ULAW (1 << 2)
+/* Raw A-law data (G.711) */
+#define IAX2_CODEC_ALAW (1 << 3)
+/* ADPCM (G.726, 32kbps) */
+#define IAX2_CODEC_G726 (1 << 4)
+/* ADPCM (IMA) */
+#define IAX2_CODEC_ADPCM (1 << 5)
+/* Raw 16-bit Signed Linear (8000 Hz) PCM */
+#define IAX2_CODEC_SLINEAR (1 << 6)
+/* LPC10, 180 samples/frame */
+#define IAX2_CODEC_LPC10 (1 << 7)
+/* G.729A audio */
+#define IAX2_CODEC_G729A (1 << 8)
+/* SpeeX Free Compression */
+#define IAX2_CODEC_SPEEX (1 << 9)
+/* iLBC Free Compression */
+#define IAX2_CODEC_ILBC (1 << 10)
+
+class Iax2AcceptInfo
+{
+public:
+ Iax2AcceptInfo();
+ void ToString(CStdString& string);
+
+ struct in_addr m_senderIp;
+ struct in_addr m_receiverIp;
+
+ CStdString m_sender_callno;
+ CStdString m_receiver_callno;
+};
+typedef boost::shared_ptr<Iax2AcceptInfo> Iax2AcceptInfoRef;
+
+
+class Iax2AuthreqInfo
+{
+public:
+ Iax2AuthreqInfo();
+ void ToString(CStdString& string);
+
+ struct in_addr m_senderIp;
+ struct in_addr m_receiverIp;
+
+ CStdString m_sender_callno;
+ CStdString m_receiver_callno;
+};
+typedef boost::shared_ptr<Iax2AuthreqInfo> Iax2AuthreqInfoRef;
+
+class Iax2NewInfo
+{
+public:
+ Iax2NewInfo();
+ void ToString(CStdString& string);
+
+ struct in_addr m_senderIp;
+ struct in_addr m_receiverIp;
+
+ CStdString m_caller; /* CALLING NUMBER */
+ CStdString m_callee; /* CALLED NUMBER */
+ CStdString m_callNo; /* This is the source call number */
+
+ bool m_validated; // true when an IAX2 voice stream has been seen for the NEW
+};
+typedef boost::shared_ptr<Iax2NewInfo> Iax2NewInfoRef;
+
+class Iax2HangupInfo
+{
+public:
+ Iax2HangupInfo();
+ void ToString(CStdString& string);
+ /* Even if we did not have the destination call number,
+ * we will be able to look it up using m_byDestIpAndCallNo */
+ struct in_addr m_senderIp;
+ struct in_addr m_receiverIp;
+
+ CStdString m_sender_callno;
+ CStdString m_receiver_callno;
+};
+typedef boost::shared_ptr<Iax2HangupInfo> Iax2HangupInfoRef;
+
+#define IAX2_FRAME_FULL 1
+#define IAX2_FRAME_MINI 2
+#define IAX2_FRAME_TRUNK 3
+
+/* IAX2_FRAME_VOICE Packet Information. May be full or mini.*/
+class Iax2PacketInfo
+{
+public:
+ Iax2PacketInfo();
+ void ToString(CStdString& string);
+
+ struct in_addr m_sourceIp;
+ struct in_addr m_destIp;
+ unsigned short m_sourcecallno; /* Always there */
+ unsigned short m_destcallno; /* May be zero for mini frames */
+
+ unsigned int m_frame_type;
+ unsigned int m_payloadSize;
+ unsigned short m_payloadType;
+ unsigned char* m_payload;
+ unsigned short m_seqNum;
+ unsigned int m_timestamp;
+ time_t m_arrivalTimestamp;
+};
+typedef boost::shared_ptr<Iax2PacketInfo> Iax2PacketInfoRef;
+
+// ============================================================
+
+#define IAX2_STATE_WAITING 1
+#define IAX2_STATE_LINKED 2
+#define IAX2_STATE_UP 3
+
+class Iax2Session
+{
+public:
+ Iax2Session(CStdString& trackingId);
+#define IAX2_PROTOCOL_NUM 1
+#define IAX2_PROTOCOL_STR "IAX2"
+ void Stop();
+ void Start();
+ bool AddIax2Packet(Iax2PacketInfoRef& iax2Packet);
+ void ReportIax2New(Iax2NewInfoRef& newinfo);
+ void ReportIax2Accept(Iax2AcceptInfoRef& acceptinfo);
+ void ReportIax2Authreq(Iax2AuthreqInfoRef& authreq);
+
+ CStdString m_trackingId;
+ CStdString m_srcIpAndCallNo;
+ CStdString m_destIpAndCallNo;
+ Iax2NewInfoRef m_new;
+ time_t m_lastUpdated;
+ CStdString m_localParty;
+ CStdString m_remoteParty;
+ CaptureEvent::DirectionEnum m_direction;
+ int m_numIax2Packets;
+ int m_codec;
+ int m_iax2_state;
+
+private:
+ /* XXX Way of "freeing" sessions which've been idle for a
+ * while? Just in case they never hang up. Answer is Hoover() */
+ void ProcessMetadataIax2(Iax2PacketInfoRef&);
+ void ProcessMetadataIax2Incoming();
+ void ProcessMetadataIax2Outgoing();
+ void UpdateMetadataIax2(Iax2PacketInfoRef& iax2Packet, bool);
+ void ProcessMetadataRawIax2(Iax2PacketInfoRef&);
+ void ProcessMetadataSkinny(Iax2PacketInfoRef& iax2Packet);
+ void ReportMetadata();
+ void GenerateOrkUid();
+
+ Iax2PacketInfoRef m_lastIax2Packet;
+ Iax2PacketInfoRef m_lastIax2PacketSide1;
+ Iax2PacketInfoRef m_lastIax2PacketSide2;
+
+ struct in_addr m_invitorIp;
+ struct in_addr m_inviteeIp;
+ struct in_addr m_localIp;
+ struct in_addr m_remoteIp;
+
+ unsigned short m_invitor_scallno;
+ unsigned short m_invitee_scallno;
+
+ //struct in_addr m_localMac;
+ //struct in_addr m_remoteMac;
+ LoggerPtr m_log;
+ CStdString m_capturePort;
+ bool m_started;
+ bool m_stopped;
+ time_t m_beginDate;
+ CStdString m_orkUid;
+ bool m_hasDuplicateIax2;
+ unsigned int m_highestIax2SeqNumDelta;
+ double m_minIax2SeqDelta;
+ double m_minIax2TimestampDelta;
+ //TcpAddressList m_iax2AddressList;
+ std::list<Iax2NewInfoRef> m_news;
+};
+typedef boost::shared_ptr<Iax2Session> Iax2SessionRef;
+
+//===================================================================
+class Iax2Sessions
+{
+public:
+ Iax2Sessions();
+ void Stop(Iax2SessionRef& session);
+ void StopAll();
+ void ReportIax2New(Iax2NewInfoRef& invite);
+ void ReportIax2Hangup(Iax2HangupInfoRef& hangup);
+ void ReportIax2Accept(Iax2AcceptInfoRef& acceptinfo);
+ void ReportIax2Authreq(Iax2AuthreqInfoRef& authreq);
+ void ReportIax2Packet(Iax2PacketInfoRef& iax2Packet);
+ void Hoover(time_t now);
+private:
+ std::map<CStdString, Iax2SessionRef> m_bySrcIpAndCallNo; /* With IAX2 the callnos can easily
+ * be duplicated across machines
+ * so we need to use the ip and callno */
+ std::map<CStdString, Iax2SessionRef> m_byDestIpAndCallNo; /* Invitee Ip and Call Id */
+ LoggerPtr m_log;
+ AlphaCounter m_alphaCounter;
+};
+typedef ACE_Singleton<Iax2Sessions, ACE_Thread_Mutex> Iax2SessionsSingleton;
+
+/* Miscellaneous */
+static inline unsigned int get_unaligned_uint32(void *p)
+{
+ struct pckd { unsigned int d; } __attribute__((packed));
+ struct pckd *pp = (struct pckd *)p;
+
+ return pp->d;
+}
+
+#endif
+
diff --git a/orkaudio/audiocaptureplugins/voip/Makefile.am b/orkaudio/audiocaptureplugins/voip/Makefile.am
index 6fe21b1..5ea43a8 100644
--- a/orkaudio/audiocaptureplugins/voip/Makefile.am
+++ b/orkaudio/audiocaptureplugins/voip/Makefile.am
@@ -1,6 +1,6 @@
METASOURCES = AUTO
lib_LTLIBRARIES = libvoip.la
-libvoip_la_SOURCES = VoIpConfig.cpp VoIp.cpp Rtp.cpp RtpSession.cpp \
+libvoip_la_SOURCES = VoIpConfig.cpp VoIp.cpp Rtp.cpp Iax2Session.cpp RtpSession.cpp \
AudioCapturePluginCommon.cpp PacketHeaderDefs.cpp
libvoip_la_LDFLAGS = -module
AM_CPPFLAGS = -D_REENTRANT
diff --git a/orkaudio/audiocaptureplugins/voip/PacketHeaderDefs.h b/orkaudio/audiocaptureplugins/voip/PacketHeaderDefs.h
index efa959a..654ba95 100644
--- a/orkaudio/audiocaptureplugins/voip/PacketHeaderDefs.h
+++ b/orkaudio/audiocaptureplugins/voip/PacketHeaderDefs.h
@@ -215,5 +215,54 @@ typedef enum
int SkinnyMessageToEnum(CStdString& msg);
CStdString SkinnyMessageToString(int msgEnum);
+/*
+ * IAX2 Packet Headers
+ */
+
+/* XXX No support for encryption */
+
+/* Full frame */
+struct Iax2FullHeader {
+ unsigned short scallno;
+ unsigned short dcallno;
+ unsigned int ts;
+ unsigned char oseqno;
+ unsigned char iseqno;
+ unsigned char type;
+ unsigned char c_sub;
+ unsigned char ie_data[0];
+} __attribute__ ((__packed__));
+
+/* Mini frame for voice */
+struct Iax2MiniHeader {
+ unsigned short scallno;
+ unsigned short ts; /* Low 16 bits from transmitting peer's full 32-bit ts */
+ unsigned char data[0];
+} __attribute__ ((__packed__));
+
+/* Meta trunk frame */
+struct Iax2MetaTrunkHeader {
+ unsigned short meta; /* zero for meta frames */
+ unsigned char metacmd;
+ unsigned char cmddata;
+ unsigned int ts;
+ unsigned char data[0];
+} __attribute__ ((__packed__));
+
+/* Mini trunked frame with timestamps (trunk timestamps
+ * flag is set to 0 */
+struct Iax2MetaTrunkEntry {
+ unsigned short scallno;
+ unsigned short len;
+ unsigned char data[0];
+} __attribute__ ((__packed__));
+
+/* Mini trunked frame with timestamps (trunk timestamps
+ * flag is set to 1 */
+struct Iax2MetaTrunkEntryTs {
+ unsigned short len;
+ struct Iax2MiniHeader mini;
+} __attribute__ ((__packed__));
+
#endif
diff --git a/orkaudio/audiocaptureplugins/voip/VoIp.cpp b/orkaudio/audiocaptureplugins/voip/VoIp.cpp
index ab3de80..ef6807f 100644
--- a/orkaudio/audiocaptureplugins/voip/VoIp.cpp
+++ b/orkaudio/audiocaptureplugins/voip/VoIp.cpp
@@ -37,6 +37,7 @@
#include "PacketHeaderDefs.h"
#include "Rtp.h"
#include "RtpSession.h"
+#include "Iax2Session.h"
extern AudioChunkCallBackFunction g_audioChunkCallBack;
extern CaptureEventCallBackFunction g_captureEventCallBack;
@@ -204,11 +205,765 @@ char* GrabLine(char* start, char* limit, CStdString& out)
return c;
}
+static int iax2_codec_to_rtp_payloadtype(int codec)
+{
+ switch(codec) {
+ case IAX2_CODEC_ULAW:
+ return 0;
+ case IAX2_CODEC_G726:
+ return 2;
+ case IAX2_CODEC_GSM:
+ return 3;
+ case IAX2_CODEC_G723_1:
+ return 4;
+ case IAX2_CODEC_ADPCM:
+ return 5;
+ case IAX2_CODEC_LPC10:
+ return 7;
+ case IAX2_CODEC_ALAW:
+ return 8;
+ case IAX2_CODEC_SLINEAR:
+ return 9;
+ case IAX2_CODEC_G729A:
+ return 18;
+ case IAX2_CODEC_ILBC:
+ return 97;
+ default:
+ return -1;
+ }
+
+ /* NOT REACHED */
+ return -1;
+}
+
+static int get_uncompressed_subclass(unsigned char c_sub)
+{
+ if (c_sub & 0x80) {
+ /* 'C' bit is set (refer to standard) */
+ if (c_sub == 0xFF)
+ return -1;
+ else
+ return 1 << (c_sub & ~0x80 & 0x1F);
+ } else {
+ /* 'C' bit in SubClass component not set */
+ return c_sub;
+ }
+}
+
+static int parse_iax2_ies(struct iax2_ies *ies, unsigned char *data, int datalen)
+{
+ int len = 0, ie = 0, odlen = datalen, pass=1;
+ CStdString logmsg;
+
+ memset(ies, 0, (int)sizeof(struct iax2_ies));
+ while(datalen >= 2) {
+ ie = data[0];
+ len = data[1];
+
+ //logmsg.Format("Looking up IE %d (len=%d)", ie, len);
+ //LOG4CXX_INFO(s_packetLog, logmsg);
+
+ if (len > datalen - 2) {
+ /* Strange. The quoted length of the IE is past the actual
+ * bounds of the IEs size */
+ logmsg.Format("Error parsing IEs Pass=%d Length of IE=%d, "
+ "datalen-2=%d, IE=%d, OrigDlen=%d", pass, len, datalen-2, ie, odlen);
+ LOG4CXX_INFO(s_packetLog, logmsg);
+ return -1;
+ }
+
+ switch(ie) {
+ case IAX2_IE_CALLED_NUMBER:
+ ies->callee = (char *)data + 2;
+ break;
+ case IAX2_IE_CALLING_NUMBER:
+ ies->caller = (char *)data + 2;
+ break;
+ case IAX2_IE_FORMAT:
+ if(len == (int)sizeof(unsigned int))
+ ies->format = ntohl(get_unaligned_uint32(data+2));
+ else
+ ies->format = 0; /* Invalid */
+ break;
+ case IAX2_IE_USERNAME:
+ ies->username = (char *)data + 2;
+ break;
+ case IAX2_IE_AUTHMETHODS:
+ if(len == (int)sizeof(unsigned int))
+ ies->authmethods = ntohl(get_unaligned_uint32(data+2));
+ else
+ ies->authmethods = 0; /* Invalid */
+ break;
+ case IAX2_IE_CHALLENGE:
+ ies->challenge = (char *)data + 2;
+ break;
+ default:
+ /* Ignore the rest */
+ break;
+ }
+
+#if 0 /* Debug headaches caused by udpHeader->len */
+ char tmpt[256];
+ memset(tmpt, 0, sizeof(tmpt));
+ memcpy(tmpt, data+2, len);
+ logmsg.Format("Got %s", tmpt);
+ LOG4CXX_INFO(s_packetLog, logmsg);
+#endif
+
+ data[0] = 0;
+ datalen -= (len + 2);
+ data += (len + 2);
+ pass++;
+ }
+
+ *data = '\0';
+ if(datalen) {
+ /* IE contents likely to be invalid because we should have totally
+ * consumed the entire amount of data */
+ CStdString logmsg;
+
+ logmsg.Format("Error parsing IEs. datalen left=%d", len, datalen, ie);
+ LOG4CXX_INFO(s_packetLog, logmsg);
+
+ return -1;
+ }
+
+ return 0;
+}
+
+void iax2_dump_frame(struct Iax2FullHeader *fh, char *source, char *dest)
+{
+ const char *frames[] = {
+ "(0?)",
+ "DTMF ",
+ "VOICE ",
+ "VIDEO ",
+ "CONTROL",
+ "NULL ",
+ "IAX ",
+ "TEXT ",
+ "IMAGE ",
+ "HTML ",
+ "CNG " };
+ const char *iaxs[] = {
+ "(0?)",
+ "NEW ",
+ "PING ",
+ "PONG ",
+ "ACK ",
+ "HANGUP ",
+ "REJECT ",
+ "ACCEPT ",
+ "AUTHREQ",
+ "AUTHREP",
+ "INVAL ",
+ "LAGRQ ",
+ "LAGRP ",
+ "REGREQ ",
+ "REGAUTH",
+ "REGACK ",
+ "REGREJ ",
+ "REGREL ",
+ "VNAK ",
+ "DPREQ ",
+ "DPREP ",
+ "DIAL ",
+ "TXREQ ",
+ "TXCNT ",
+ "TXACC ",
+ "TXREADY",
+ "TXREL ",
+ "TXREJ ",
+ "QUELCH ",
+ "UNQULCH",
+ "POKE ",
+ "PAGE ",
+ "MWI ",
+ "UNSPRTD",
+ "TRANSFR",
+ "PROVISN",
+ "FWDWNLD",
+ "FWDATA "
+ };
+ const char *cmds[] = {
+ "(0?)",
+ "HANGUP ",
+ "RING ",
+ "RINGING",
+ "ANSWER ",
+ "BUSY ",
+ "TKOFFHK",
+ "OFFHOOK" };
+ char class2[20];
+ char subclass2[20];
+ CStdString tmp;
+ const char *cclass;
+ const char *subclass;
+ char *dir;
+
+ if (fh->type >= (int)sizeof(frames)/(int)sizeof(frames[0])) {
+ snprintf(class2, sizeof(class2), "(%d?)", fh->type);
+ cclass = class2;
+ } else {
+ cclass = frames[(int)fh->type];
+ }
+
+ if (fh->type == IAX2_FRAME_IAX) {
+ if (fh->c_sub >= (int)sizeof(iaxs)/(int)sizeof(iaxs[0])) {
+ snprintf(subclass2, sizeof(subclass2), "(%d?)", fh->c_sub);
+ subclass = subclass2;
+ } else {
+ subclass = iaxs[(int)fh->c_sub];
+ }
+ } else if (fh->type == IAX2_FRAME_CONTROL) {
+ if (fh->c_sub >= (int)sizeof(cmds)/(int)sizeof(cmds[0])) {
+ snprintf(subclass2, sizeof(subclass2), "(%d?)", fh->c_sub);
+ subclass = subclass2;
+ } else {
+ subclass = cmds[(int)fh->c_sub];
+ }
+ } else {
+ snprintf(subclass2, sizeof(subclass2), "%d", fh->c_sub);
+ subclass = subclass2;
+ }
+
+ tmp.Format("IAX2-Frame -- OSeqno: %3.3d ISeqno: %3.3d Type: %s Subclass: %s",
+ fh->oseqno, fh->iseqno, cclass, subclass);
+ LOG4CXX_INFO(s_packetLog, tmp);
+ tmp.Format(" Timestamp: %05lums SCall: %5.5d DCall: %5.5d [Source: %s Dest: %s]",
+ (unsigned long)ntohl(fh->ts),
+ ntohs(fh->scallno) & ~0x8000, ntohs(fh->dcallno) & ~0x8000, source, dest);
+
+ LOG4CXX_INFO(s_packetLog, tmp);
+}
+
+/* See if this is an IAX2 NEW. If so, process */
+bool TryIax2New(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader,
+ UdpHeaderStruct* udpHeader, u_char* udpPayload)
+{
+ struct Iax2FullHeader *fh = (struct Iax2FullHeader *)udpPayload;
+ struct iax2_ies ies;
+ int ies_len = 0, udp_act_payload_len = 0;
+ Iax2NewInfoRef info(new Iax2NewInfo());
+ char source_ip[16], dest_ip[16];
+ CStdString logmsg;
+
+ memset(&ies, 0, sizeof(ies));
+ udp_act_payload_len = (ntohs(udpHeader->len)-sizeof(UdpHeaderStruct));
+ if(udp_act_payload_len < sizeof(*fh))
+ return false; /* Frame too small */
+
+ if(!(ntohs(fh->scallno) & 0x8000))
+ return false; /* Not a full frame */
+
+ ies_len = ((u_char*)ipHeader+ntohs(ipHeader->ip_len))-(udpPayload+sizeof(*fh));
+
+#if 0 /* Debug headaches caused by udpHeader->len */
+ /* Beware that udpHeader->len is not the length of the udpPayload
+ * but rather this includes the length of the UDP header as well.
+ * I.e watch out for the figure "8" as you debug ;-) */
+ {
+ char source_ip[16], dest_ip[16];
+
+ ACE_OS::inet_ntop(AF_INET, (void*)&ipHeader->ip_src, source_ip, sizeof(source_ip));
+ ACE_OS::inet_ntop(AF_INET, (void*)&ipHeader->ip_dest, dest_ip, sizeof(dest_ip));
+ iax2_dump_frame(fh, source_ip, dest_ip);
+ }
+
+ logmsg.Format("UDP_Payload=%p UDP+FH_Payload=%p FH->IEDATA=%p ies_len=%d "
+ "udpHeader->len-sizeof(fullhdr)=%d (ntohs(udpHeader->len)"
+ "-sizeof(UdpHeaderStruct))=%d", udpPayload, udpPayload+
+ sizeof(*fh), fh->ie_data, ies_len, (ntohs(udpHeader->len)-
+ sizeof(*fh)), (ntohs(udpHeader->len)-sizeof(UdpHeaderStruct)));
+ LOG4CXX_INFO(s_packetLog, logmsg);
+#endif
+
+ if(fh->type != IAX2_FRAME_IAX)
+ return false; /* Frame type must be IAX */
+
+ if(get_uncompressed_subclass(fh->c_sub) != IAX2_COMMAND_NEW)
+ return false; /* Subclass must be NEW */
+
+ if(parse_iax2_ies(&ies, fh->ie_data, ies_len))
+ return false; /* Invalid "full" frame received */
+
+ if(!ies.callee)
+ return false; /* According to the SPEC, a NEW MUST have a
+ * callee (Called Number) */
+
+ if(!strlen(ies.callee))
+ return false; /* According to the SPEC, a NEW MUST have a
+ * callee (Called Number) */
+
+ if(!ies.caller) {
+ ies.caller = "WITHELD";
+ } else {
+ if(!strlen(ies.caller)) {
+ ies.caller = "WITHELD";
+ }
+ }
+
+ /* Statistically this is most likely a NEW IAX2 frame. */
+
+ info->m_senderIp = ipHeader->ip_src;
+ info->m_receiverIp = ipHeader->ip_dest;
+ info->m_caller = CStdString(ies.caller);
+ info->m_callee = CStdString(ies.callee);
+ info->m_callNo = IntToString(ntohs(fh->scallno) & ~0x8000);
+
+ /* Report the packet */
+ Iax2SessionsSingleton::instance()->ReportIax2New(info);
+
+ LOG4CXX_INFO(s_packetLog, "Processed IAX2 NEW frame");
+
+ return true;
+}
+
+bool TryIax2Accept(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader,
+ UdpHeaderStruct* udpHeader, u_char* udpPayload)
+{
+ struct Iax2FullHeader *fh = (struct Iax2FullHeader *)udpPayload;
+ struct iax2_ies ies;
+ int ies_len = 0, udp_act_payload_len = 0;
+ Iax2AcceptInfoRef info(new Iax2AcceptInfo());
+
+ memset(&ies, 0, sizeof(ies));
+ udp_act_payload_len = (ntohs(udpHeader->len)-sizeof(UdpHeaderStruct));
+ if(udp_act_payload_len < sizeof(*fh))
+ return false; /* Frame too small */
+
+ if(!(ntohs(fh->scallno) & 0x8000))
+ return false; /* Not a full frame */
+
+ if(fh->type != IAX2_FRAME_IAX)
+ return false; /* Frame type must be IAX */
+
+ if(get_uncompressed_subclass(fh->c_sub) != IAX2_COMMAND_ACCEPT)
+ return false; /* Subclass must be ACCEPT */
+
+ ies_len = ((u_char*)ipHeader+ntohs(ipHeader->ip_len))-(udpPayload+sizeof(*fh));
+
+ /* In this case, this just serves to test the integrity of the
+ * Information Elements */
+ if(parse_iax2_ies(&ies, fh->ie_data, ies_len))
+ return false; /* Invalid "full" frame received */
+
+ if(!ies.format)
+ return false; /* According to the SPEC, ACCEPT must have
+ * a format specified */
+
+ /* We have an ACCEPT */
+
+ info->m_senderIp = ipHeader->ip_src;
+ info->m_receiverIp = ipHeader->ip_dest;
+ info->m_sender_callno = IntToString(ntohs(fh->scallno) & ~0x8000);
+ info->m_receiver_callno = IntToString(ntohs(fh->dcallno) & ~0x8000);
+
+ Iax2SessionsSingleton::instance()->ReportIax2Accept(info);
+
+ LOG4CXX_INFO(s_packetLog, "Processed IAX2 ACCEPT frame");
+
+ return true;
+}
+
+bool TryIax2Authreq(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader,
+ UdpHeaderStruct* udpHeader, u_char* udpPayload)
+{
+ struct Iax2FullHeader *fh = (struct Iax2FullHeader *)udpPayload;
+ struct iax2_ies ies;
+ int ies_len = 0, udp_act_payload_len = 0;
+ Iax2AuthreqInfoRef info(new Iax2AuthreqInfo());
+
+ memset(&ies, 0, sizeof(ies));
+ udp_act_payload_len = (ntohs(udpHeader->len)-sizeof(UdpHeaderStruct));
+ if(udp_act_payload_len < sizeof(*fh))
+ return false; /* Frame too small */
+
+ if(!(ntohs(fh->scallno) & 0x8000))
+ return false; /* Not a full frame */
+
+ if(fh->type != IAX2_FRAME_IAX)
+ return false; /* Frame type must be IAX */
+
+ if(get_uncompressed_subclass(fh->c_sub) != IAX2_COMMAND_AUTHREQ)
+ return false; /* Subclass must be AUTHREQ */
+
+ ies_len = ((u_char*)ipHeader+ntohs(ipHeader->ip_len))-(udpPayload+sizeof(*fh));
+
+ /* In this case, this just serves to test the integrity of the
+ * Information Elements */
+ if(parse_iax2_ies(&ies, fh->ie_data, ies_len))
+ return false; /* Invalid "full" frame received */
+
+
+ if(!ies.username)
+ return false; /* According to the spec AUTHREQ must have
+ * a user name. Can it be empty? */
+
+ /*
+ if(!strlen(ies.username))
+ return false; * According to the spec AUTHREQ must have
+ * a user name. *
+ */
+
+ if(!ies.authmethods)
+ return false; /* According to the spec AUTHREQ must have
+ * AUTHMETHODS */
+
+ if(!ies.challenge)
+ return false; /* According to the spec, AUTHREQ must have
+ * a CHALLENGE string. Can it be empty? */
+
+
+ /* We have an AUTHREQ */
+ info->m_senderIp = ipHeader->ip_src;
+ info->m_receiverIp = ipHeader->ip_dest;
+ info->m_sender_callno = IntToString(ntohs(fh->scallno) & ~0x8000);
+ info->m_receiver_callno = IntToString(ntohs(fh->dcallno) & ~0x8000);
+
+ /* Report the packet */
+ Iax2SessionsSingleton::instance()->ReportIax2Authreq(info);
+
+ LOG4CXX_INFO(s_packetLog, "Processed IAX2 AUTHREQ frame");
+
+ return true;
+}
+
+/* HANGUP via IAX frame */
+bool TryIax2Hangup(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader,
+ UdpHeaderStruct* udpHeader, u_char* udpPayload)
+{
+ struct Iax2FullHeader *fh = (struct Iax2FullHeader *)udpPayload;
+ struct iax2_ies ies;
+ int ies_len = 0, udp_act_payload_len = 0;
+ Iax2HangupInfoRef info(new Iax2HangupInfo());
+
+ memset(&ies, 0, sizeof(ies));
+ udp_act_payload_len = (ntohs(udpHeader->len)-sizeof(UdpHeaderStruct));
+ if(udp_act_payload_len < sizeof(*fh))
+ return false; /* Frame too small */
+
+ if(!(ntohs(fh->scallno) & 0x8000))
+ return false; /* Not a full frame */
+
+ if(fh->type != IAX2_FRAME_IAX)
+ return false; /* Frame type must be IAX */
+
+ if(get_uncompressed_subclass(fh->c_sub) != IAX2_COMMAND_HANGUP)
+ return false; /* Subclass must be HANGUP */
+
+ ies_len = ((u_char*)ipHeader+ntohs(ipHeader->ip_len))-(udpPayload+sizeof(*fh));
+
+ /* In this case, this just serves to test the integrity of the
+ * Information Elements */
+ if(parse_iax2_ies(&ies, fh->ie_data, ies_len))
+ return false; /* Invalid "full" frame received */
+
+ /* We have a HANGUP */
+
+ info->m_senderIp = ipHeader->ip_src;
+ info->m_receiverIp = ipHeader->ip_dest;
+ info->m_sender_callno = IntToString(ntohs(fh->scallno) & ~0x8000);
+ info->m_receiver_callno = IntToString(ntohs(fh->dcallno) & ~0x8000);
+
+ /* Report the packet */
+ Iax2SessionsSingleton::instance()->ReportIax2Hangup(info);
+
+ LOG4CXX_INFO(s_packetLog, "Processed IAX2 HANGUP frame");
+
+ return true;
+}
+
+/* HANGUP via CONTROL frame */
+bool TryIax2ControlHangup(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader,
+ UdpHeaderStruct* udpHeader, u_char* udpPayload)
+{
+ struct Iax2FullHeader *fh = (struct Iax2FullHeader *)udpPayload;
+ Iax2HangupInfoRef info(new Iax2HangupInfo());
+ int udp_act_payload_len = 0;
+
+ udp_act_payload_len = (ntohs(udpHeader->len)-sizeof(UdpHeaderStruct));
+ if(udp_act_payload_len < sizeof(*fh))
+ return false; /* Frame too small */
+
+ if(!(ntohs(fh->scallno) & 0x8000))
+ return false; /* Not a full frame */
+
+ if(fh->type != IAX2_FRAME_CONTROL)
+ return false; /* Frame type must be CONTROL */
+
+ if(get_uncompressed_subclass(fh->c_sub) != IAX2_CONTROL_HANGUP)
+ return false; /* Subclass must be HANGUP */
+
+ /* We have a HANGUP */
+
+ info->m_senderIp = ipHeader->ip_src;
+ info->m_receiverIp = ipHeader->ip_dest;
+ info->m_sender_callno = IntToString(ntohs(fh->scallno) & ~0x8000);
+ info->m_receiver_callno = IntToString(ntohs(fh->dcallno) & ~0x8000);
+
+ /* Report the packet */
+ Iax2SessionsSingleton::instance()->ReportIax2Hangup(info);
+
+ LOG4CXX_INFO(s_packetLog, "Processed IAX2 CONTROL HANGUP frame");
+
+ return true;
+}
+
+bool TryIax2Reject(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader,
+ UdpHeaderStruct* udpHeader, u_char* udpPayload)
+{
+ struct Iax2FullHeader *fh = (struct Iax2FullHeader *)udpPayload;
+ struct iax2_ies ies;
+ int ies_len = 0, udp_act_payload_len = 0;
+ Iax2HangupInfoRef info(new Iax2HangupInfo());
+
+ memset(&ies, 0, sizeof(ies));
+ udp_act_payload_len = (ntohs(udpHeader->len)-sizeof(UdpHeaderStruct));
+ if(udp_act_payload_len < sizeof(*fh))
+ return false; /* Frame too small */
+
+ if(!(ntohs(fh->scallno) & 0x8000))
+ return false; /* Not a full frame */
+
+ if(fh->type != IAX2_FRAME_IAX)
+ return false; /* Frame type must be IAX */
+
+ if(get_uncompressed_subclass(fh->c_sub) != IAX2_COMMAND_REJECT)
+ return false; /* Subclass must be REJECT */
+
+ ies_len = ((u_char*)ipHeader+ntohs(ipHeader->ip_len))-(udpPayload+sizeof(*fh));
+
+ /* In this case, this just serves to test the integrity of the
+ * Information Elements */
+ if(parse_iax2_ies(&ies, fh->ie_data, ies_len))
+ return false; /* Invalid "full" frame received */
+
+ /* We have a REJECT */
+
+ info->m_senderIp = ipHeader->ip_src;
+ info->m_receiverIp = ipHeader->ip_dest;
+ info->m_sender_callno = IntToString(ntohs(fh->scallno) & ~0x8000);
+ info->m_receiver_callno = IntToString(ntohs(fh->dcallno) & ~0x8000);
+
+ /* Report the packet */
+ Iax2SessionsSingleton::instance()->ReportIax2Hangup(info);
+
+ LOG4CXX_INFO(s_packetLog, "Processed IAX2 REJECT frame");
+
+ return true;
+}
+
+bool TryIax2FullVoiceFrame(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader,
+ UdpHeaderStruct* udpHeader, u_char* udpPayload)
+{
+ struct Iax2FullHeader *fh = (struct Iax2FullHeader *)udpPayload;
+ int data_len = 0, codec = 0, pt = 0, udp_act_payload_len = 0;
+ Iax2PacketInfoRef info(new Iax2PacketInfo());
+
+ udp_act_payload_len = (ntohs(udpHeader->len)-sizeof(UdpHeaderStruct));
+ if(udp_act_payload_len < sizeof(*fh))
+ return false; /* Frame too small */
+
+ if(!(ntohs(fh->scallno) & 0x8000))
+ return false; /* Not a full frame */
+
+ if(fh->type != IAX2_FRAME_VOICE)
+ return false; /* Frame type must be VOICE */
+
+ codec = get_uncompressed_subclass(fh->c_sub);
+ if((pt = iax2_codec_to_rtp_payloadtype(codec)) < 0) {
+ CStdString logmsg;
+
+ logmsg.Format("Invalid payload type %d received for "
+ "IAX_FRAME_VOICE, IAX2 codec %d", pt, codec);
+ LOG4CXX_INFO(s_packetLog, logmsg);
+ return false; /* Invalid codec type received */
+ }
+
+ data_len = ((u_char*)ipHeader+ntohs(ipHeader->ip_len))-(udpPayload+sizeof(*fh));
+ if(data_len == 0)
+ return false; /* Empty packet? */
+
+ /* We have a full VOICE frame */
+
+ info->m_sourceIp = ipHeader->ip_src;
+ info->m_destIp = ipHeader->ip_dest;
+ info->m_sourcecallno = (ntohs(fh->scallno) & ~0x8000);
+ info->m_destcallno = (ntohs(fh->dcallno) & ~0x8000);
+ info->m_payloadSize = data_len;
+ info->m_payload = udpPayload+sizeof(*fh);
+ info->m_payloadType = pt;
+ info->m_timestamp = ntohl(fh->ts);
+ info->m_arrivalTimestamp = time(NULL);
+ info->m_frame_type = IAX2_FRAME_FULL;
+
+ Iax2SessionsSingleton::instance()->ReportIax2Packet(info);
+
+ CStdString logmsg;
+ logmsg.Format("Processed IAX2 FULL VOICE fram, pt %d", pt);
+ LOG4CXX_INFO(s_packetLog, logmsg);
+
+ return true;
+}
+
+bool TryIax2MetaTrunkFrame(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader,
+ UdpHeaderStruct* udpHeader, u_char* udpPayload)
+{
+ struct Iax2MetaTrunkHeader *mh = (struct Iax2MetaTrunkHeader *)udpPayload;
+ struct Iax2MetaTrunkEntry *supermini = NULL;
+ struct Iax2MetaTrunkEntryTs *mini = NULL;
+ int content_type = 0; /* 0 means mini frames, 1 means super mini (no timestampes) */
+ int frame_ts = 0; /* Timestamp of frame */
+ int data_len = 0;
+ int entries = 0, udp_act_payload_len = 0;
+ Iax2PacketInfoRef info(new Iax2PacketInfo());
+
+ udp_act_payload_len = (ntohs(udpHeader->len)-sizeof(UdpHeaderStruct));
+ if(udp_act_payload_len < sizeof(*mh))
+ return false; /* Frame too small */
+
+ if(mh->meta != 0)
+ return false; /* Must be zero */
+
+ if(mh->metacmd & 0x80)
+ return false; /* 'V' bit must be set to zero */
+
+ if(mh->metacmd != 1)
+ return false; /* metacmd must be 1 */
+
+ /* Get the length of the information apart from the
+ * meta trunk header */
+ data_len = ((u_char*)ipHeader+ntohs(ipHeader->ip_len))-(udpPayload+sizeof(*mh));
+ if(data_len == 0)
+ return false; /* Empty packet? */
+
+ /* Step over the meta trunk header */
+ udpPayload += sizeof(*mh);
+
+ /* Determine whether the trunk contents have their own
+ * timestamps or not */
+ if(mh->cmddata & 0x01)
+ content_type = 1;
+ else
+ content_type = 0;
+
+ if(content_type) {
+ /* Have timestamps */
+
+ while(data_len) {
+ if(data_len < sizeof(*mini))
+ break;
+
+ mini = (struct Iax2MetaTrunkEntryTs *)udpPayload;
+
+ if(data_len < sizeof(*mini)+ntohs(mini->len))
+ break;
+
+ info->m_sourceIp = ipHeader->ip_src;
+ info->m_destIp = ipHeader->ip_dest;
+ info->m_sourcecallno = (ntohs(mini->mini.scallno) & ~0x8000);
+ info->m_destcallno = 0;
+ info->m_payloadSize = ntohs(mini->len);
+ info->m_payload = udpPayload+sizeof(*mini);
+ info->m_payloadType = 0;
+ info->m_timestamp = ntohs(mini->mini.ts);
+ info->m_arrivalTimestamp = time(NULL);
+ info->m_frame_type = IAX2_FRAME_MINI;
+
+ Iax2SessionsSingleton::instance()->ReportIax2Packet(info);
+ entries += 1;
+
+ udpPayload += sizeof(*mini)+ntohs(mini->len);
+ data_len -= sizeof(*mini)+ntohs(mini->len);
+ }
+ } else {
+ /* Have no timestamps */
+ while(data_len) {
+ if(data_len < sizeof(*supermini))
+ break;
+
+ supermini = (struct Iax2MetaTrunkEntry *)udpPayload;
+
+ if(data_len < sizeof(*supermini)+ntohs(supermini->len))
+ break;
+
+ info->m_sourceIp = ipHeader->ip_src;
+ info->m_destIp = ipHeader->ip_dest;
+ info->m_sourcecallno = (ntohs(supermini->scallno) & ~0x8000);
+ info->m_destcallno = 0;
+ info->m_payloadSize = ntohs(supermini->len);
+ info->m_payload = udpPayload+sizeof(*supermini);
+ info->m_payloadType = 0;
+ info->m_timestamp = 0;
+ info->m_arrivalTimestamp = time(NULL);
+ info->m_frame_type = IAX2_FRAME_MINI;
+
+ Iax2SessionsSingleton::instance()->ReportIax2Packet(info);
+ entries += 1;
+
+ udpPayload += sizeof(*supermini)+ntohs(supermini->len);
+ data_len -= sizeof(*supermini)+ntohs(supermini->len);
+ }
+ }
+
+
+ if(entries > 0) {
+ CStdString logmsg;
+
+ logmsg.Format("Processed IAX2 Meta Trunk packet with %d entries", entries);
+ LOG4CXX_DEBUG(s_packetLog, logmsg);
+ return true;
+ }
+
+ return false; /* No valid entries in this so-called meta trunk frame */
+}
+
+bool TryIax2MiniVoiceFrame(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader,
+ UdpHeaderStruct* udpHeader, u_char* udpPayload)
+{
+ struct Iax2MiniHeader *mini = (struct Iax2MiniHeader *)udpPayload;
+ int data_len = 0, udp_act_payload_len = 0;
+ Iax2PacketInfoRef info(new Iax2PacketInfo());
+
+ udp_act_payload_len = (ntohs(udpHeader->len)-sizeof(UdpHeaderStruct));
+ if(udp_act_payload_len < sizeof(*mini))
+ return false; /* Frame too small */
+
+ if((ntohs(mini->scallno) & 0x8000))
+ return false; /* Not a Mini frame */
+
+ data_len = ((u_char*)ipHeader+ntohs(ipHeader->ip_len))-(udpPayload+sizeof(*mini));
+ if(data_len == 0)
+ return false; /* Empty packet? */
+
+ info->m_sourceIp = ipHeader->ip_src;
+ info->m_destIp = ipHeader->ip_dest;
+ info->m_sourcecallno = (ntohs(mini->scallno) & ~0x8000);
+ info->m_destcallno = 0;
+ info->m_payloadSize = data_len;
+ info->m_payload = udpPayload+sizeof(*mini);
+ info->m_payloadType = 0;
+ info->m_timestamp = ntohl(mini->ts);
+ info->m_arrivalTimestamp = time(NULL);
+ info->m_frame_type = IAX2_FRAME_MINI;
+
+ Iax2SessionsSingleton::instance()->ReportIax2Packet(info);
+
+ //LOG4CXX_INFO(s_packetLog, "Processed IAX2 Mini Voice packet");
+
+ return true;
+}
+
bool TryRtp(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader, UdpHeaderStruct* udpHeader, u_char* udpPayload)
{
bool result = false;
RtpHeaderStruct* rtpHeader = (RtpHeaderStruct*)udpPayload;
+ /* Ensure that the UDP payload is at least sizeof(RtpHeaderStruct) */
+ if(ntohs(udpHeader->len) < sizeof(RtpHeaderStruct))
+ return false;
+
if (rtpHeader->version == 2)
{
u_short source = ntohs(udpHeader->source);
@@ -268,6 +1023,13 @@ bool TryRtp(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader, UdpH
bool TrySipBye(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader, UdpHeaderStruct* udpHeader, u_char* udpPayload)
{
bool result = false;
+ int udp_act_payload_len = (ntohs(udpHeader->len)-sizeof(UdpHeaderStruct));
+
+ /* Judgine from the memcmp() below, we need the UDP payload
+ * length to be at least 3 bytes. -- Gerald */
+ if(udp_act_payload_len < 3)
+ return false; /* Frame too small */
+
if (memcmp("BYE", (void*)udpPayload, 3) == 0)
{
result = true;
@@ -292,6 +1054,13 @@ bool TrySipInvite(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader
{
bool result = false;
bool drop = false;
+ int udp_act_payload_len = (ntohs(udpHeader->len)-sizeof(UdpHeaderStruct));
+
+ /* Judgine from the memcmp() below, we need the UDP payload
+ * length to be at least 3 bytes. -- Gerald */
+ if(udp_act_payload_len < 6)
+ return false; /* Frame too small */
+
if (memcmp("INVITE", (void*)udpPayload, 6) == 0)
{
result = true;
@@ -655,21 +1424,66 @@ void HandlePacket(u_char *param, const struct pcap_pkthdr *header, const u_char
{
UdpHeaderStruct* udpHeader = (UdpHeaderStruct*)((char *)ipHeader + ipHeaderLength);
- if( ntohs(udpHeader->source) > 1024 && ntohs(udpHeader->dest) > 1024 )
- {
+ if(ntohs(udpHeader->source) > 1024 && ntohs(udpHeader->dest) > 1024) {
+ bool detectedUsefulPacket = false;
u_char* udpPayload = (u_char *)udpHeader + sizeof(UdpHeaderStruct);
- MutexSentinel mutexSentinel(s_mutex); // serialize access for competing pcap threads
+ MutexSentinel mutexSentinel(s_mutex); // serialize access for competing pcap threads
+
+ detectedUsefulPacket = TryIax2New(ethernetHeader, ipHeader, udpHeader, udpPayload);
+
+ if(!detectedUsefulPacket) {
+ detectedUsefulPacket = TryIax2Accept(ethernetHeader, ipHeader, udpHeader,
+ udpPayload);
+ }
+
+ if(!detectedUsefulPacket) {
+ detectedUsefulPacket = TryIax2Authreq(ethernetHeader, ipHeader, udpHeader,
+ udpPayload);
+ }
+
+ if(!detectedUsefulPacket) {
+ detectedUsefulPacket = TryIax2Hangup(ethernetHeader, ipHeader, udpHeader,
+ udpPayload);
+ }
+
+ if(!detectedUsefulPacket) {
+ detectedUsefulPacket = TryIax2ControlHangup(ethernetHeader, ipHeader, udpHeader,
+ udpPayload);
+ }
+
+ if(!detectedUsefulPacket) {
+ detectedUsefulPacket = TryIax2Reject(ethernetHeader, ipHeader, udpHeader,
+ udpPayload);
+ }
+
+ if(!detectedUsefulPacket) {
+ detectedUsefulPacket = TryIax2FullVoiceFrame(ethernetHeader, ipHeader,
+ udpHeader, udpPayload);
+ }
+
+ if(!detectedUsefulPacket) {
+ detectedUsefulPacket = TryIax2MetaTrunkFrame(ethernetHeader, ipHeader,
+ udpHeader, udpPayload);
+ }
+
+ if(!detectedUsefulPacket) {
+ detectedUsefulPacket = TryIax2MiniVoiceFrame(ethernetHeader, ipHeader,
+ udpHeader, udpPayload);
+ }
- bool detectedUsefulPacket = TryRtp(ethernetHeader, ipHeader, udpHeader, udpPayload);
+ if(!detectedUsefulPacket) {
+ detectedUsefulPacket = TryRtp(ethernetHeader, ipHeader, udpHeader, udpPayload);
+ }
- if(!detectedUsefulPacket)
- {
- detectedUsefulPacket= TrySipInvite(ethernetHeader, ipHeader, udpHeader, udpPayload);
+ if(!detectedUsefulPacket) {
+ detectedUsefulPacket= TrySipInvite(ethernetHeader, ipHeader, udpHeader,
+ udpPayload);
}
- if(!detectedUsefulPacket)
- {
- detectedUsefulPacket = TrySipBye(ethernetHeader, ipHeader, udpHeader, udpPayload);
+
+ if(!detectedUsefulPacket) {
+ detectedUsefulPacket = TrySipBye(ethernetHeader, ipHeader, udpHeader,
+ udpPayload);
}
}
}
@@ -711,6 +1525,7 @@ void HandlePacket(u_char *param, const struct pcap_pkthdr *header, const u_char
MutexSentinel mutexSentinel(s_mutex); // serialize access for competing pcap threads
s_lastHooveringTime = now;
RtpSessionsSingleton::instance()->Hoover(now);
+ Iax2SessionsSingleton::instance()->Hoover(now);
}
}
diff --git a/orkaudio/audiocaptureplugins/voip/VoIp.dsp b/orkaudio/audiocaptureplugins/voip/VoIp.dsp
index cc39490..655dde1 100644
--- a/orkaudio/audiocaptureplugins/voip/VoIp.dsp
+++ b/orkaudio/audiocaptureplugins/voip/VoIp.dsp
@@ -124,6 +124,14 @@ SOURCE=.\Rtp.h
# End Source File
# Begin Source File
+SOURCE=.\Iax2Session.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Iax2Session.h
+# End Source File
+# Begin Source File
+
SOURCE=.\RtpSession.cpp
# End Source File
# Begin Source File