summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGerald Begumisa <ben_g@users.sourceforge.net>2007-04-28 22:13:29 +0000
committerGerald Begumisa <ben_g@users.sourceforge.net>2007-04-28 22:13:29 +0000
commitdbbc010093d9ddda3fd4372580aeb9a60c089ce1 (patch)
tree6c34f7312ac353f848f9070a449e018f1986bee5
parent2c370d203163951283f80bd229cc81bf533fb089 (diff)
Support for SIP over TCP added
git-svn-id: https://oreka.svn.sourceforge.net/svnroot/oreka/trunk@438 09dcff7a-b715-0410-9601-b79a96267cd0
-rw-r--r--orkaudio/audiocaptureplugins/voip/Makefile.am2
-rw-r--r--orkaudio/audiocaptureplugins/voip/SipTcp.cpp237
-rw-r--r--orkaudio/audiocaptureplugins/voip/SipTcp.h69
-rw-r--r--orkaudio/audiocaptureplugins/voip/VoIp.cpp164
4 files changed, 470 insertions, 2 deletions
diff --git a/orkaudio/audiocaptureplugins/voip/Makefile.am b/orkaudio/audiocaptureplugins/voip/Makefile.am
index 5ea43a8..a415ade 100644
--- a/orkaudio/audiocaptureplugins/voip/Makefile.am
+++ b/orkaudio/audiocaptureplugins/voip/Makefile.am
@@ -1,7 +1,7 @@
METASOURCES = AUTO
lib_LTLIBRARIES = libvoip.la
libvoip_la_SOURCES = VoIpConfig.cpp VoIp.cpp Rtp.cpp Iax2Session.cpp RtpSession.cpp \
- AudioCapturePluginCommon.cpp PacketHeaderDefs.cpp
+ SipTcp.cpp AudioCapturePluginCommon.cpp PacketHeaderDefs.cpp
libvoip_la_LDFLAGS = -module
AM_CPPFLAGS = -D_REENTRANT
libvoip_la_LIBADD = -lACE -lxerces-c -llog4cxx -lorkbase -lpcap
diff --git a/orkaudio/audiocaptureplugins/voip/SipTcp.cpp b/orkaudio/audiocaptureplugins/voip/SipTcp.cpp
new file mode 100644
index 0000000..0cea0f5
--- /dev/null
+++ b/orkaudio/audiocaptureplugins/voip/SipTcp.cpp
@@ -0,0 +1,237 @@
+#include <list>
+#include "ace/OS_NS_unistd.h"
+#include "ace/OS_NS_string.h"
+#include "ace/OS_NS_strings.h"
+#include "ace/OS_NS_dirent.h"
+#include "ace/Singleton.h"
+#include "ace/Min_Max.h"
+#include "ace/OS_NS_arpa_inet.h"
+#include "ace/OS_NS_ctype.h"
+#include "ace/Thread_Manager.h"
+#include "ace/Thread_Mutex.h"
+#include "ace/Thread_Semaphore.h"
+#include "AudioCapturePlugin.h"
+#include "AudioCapturePluginCommon.h"
+#include "Utils.h"
+#include "VoIpConfig.h"
+#include "pcap.h"
+#include "PacketHeaderDefs.h"
+#include "Rtp.h"
+#include "RtpSession.h"
+#include "Iax2Session.h"
+#include "SipTcp.h"
+#include "boost/shared_ptr.hpp"
+#include "StdString.h"
+#include "SipTcp.h"
+
+
+SafeBuffer::SafeBuffer()
+{
+ m_pBuffer = NULL;
+ m_size = 0;
+}
+
+SafeBuffer::~SafeBuffer()
+{
+ if(m_size) {
+ free(m_pBuffer);
+ m_size = 0;
+ }
+
+ m_pBuffer = NULL;
+}
+
+int SafeBuffer::Size()
+{
+ return m_size;
+}
+
+void SafeBuffer::Store(u_char *buf, int len)
+{
+ if(m_size) {
+ free(m_pBuffer);
+ m_size = 0;
+ m_pBuffer = NULL;
+ }
+
+ m_pBuffer = (u_char *)calloc(len+1, 1);
+ m_size = len;
+
+ if(!m_pBuffer) {
+ char tmp[80];
+ snprintf(tmp, sizeof(tmp), "%d", len);
+
+ CStdString numBytes = CStdString(tmp);
+ throw("SafeBuffer::Store could not malloc a buffer of size:" + numBytes);
+ }
+
+ memcpy(m_pBuffer, buf, len);
+}
+
+void SafeBuffer::Add(u_char *buf, int len)
+{
+ u_char *newBuf = NULL;
+
+ if(!m_size) {
+ Store(buf, len);
+ return;
+ }
+
+ newBuf = (u_char*)realloc(m_pBuffer, m_size+len+1);
+ if(!newBuf) {
+ char tmp[80];
+ snprintf(tmp, sizeof(tmp), "%d", len+m_size);
+
+ CStdString numBytes = CStdString(tmp);
+ throw("SafeBuffer::Add failed to realloc buffer to " + numBytes);
+ }
+
+ m_pBuffer = newBuf;
+ memcpy(m_pBuffer+m_size, buf, len);
+ m_size += len;
+ *(m_pBuffer+m_size) = 0;
+}
+
+u_char *SafeBuffer::GetBuffer()
+{
+ return m_pBuffer;
+}
+
+// ============================================================
+
+static 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(ACE_OS::strncasecmp(toFind, ptr, strlen(toFind)) == 0)
+ {
+ return (ptr+strlen(toFind));
+ }
+ }
+ return NULL;
+}
+
+static 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;
+}
+
+static void memToHex(unsigned char* input, size_t len, CStdString&output)
+{
+ char byteAsHex[10];
+ for(int i=0; i<len; i++)
+ {
+ sprintf(byteAsHex, "%.2x", input[i]);
+ output += byteAsHex;
+ }
+}
+
+SipTcpStream::SipTcpStream()
+{
+ m_expectingSeqNo = 0;
+ m_senderIp.s_addr = 0;
+ m_receiverIp.s_addr = 0;
+ m_senderPort = 0;
+ m_receiverPort = 0;
+ m_entryTime = time(NULL);
+ m_sipRequest = SafeBufferRef(new SafeBuffer());
+}
+
+SipTcpStream::~SipTcpStream()
+{
+}
+
+void SipTcpStream::ToString(CStdString& string)
+{
+ char senderIp[16], receiverIp[16];
+ CStdString expSeq, lastSeq;
+
+ memToHex((unsigned char *)&m_expectingSeqNo, sizeof(m_expectingSeqNo), expSeq);
+ memToHex((unsigned char *)&m_lastSeqNo, sizeof(m_lastSeqNo), lastSeq);
+
+ 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-port:%d receiver-port:%d entry-time:%d expecting-seq-no:%s total-bytes:%d last-seqno:%s [[[%s]]]", senderIp, receiverIp, m_senderPort, m_receiverPort, m_entryTime, expSeq, m_sipRequest->Size(), lastSeq, m_sipRequest->GetBuffer());
+ string.Format("sender:%s receiver:%s sender-port:%d receiver-port:%d entry-time:%d expecting-seq-no:%s total-bytes:%d last-seqno:%s", senderIp, receiverIp, m_senderPort, m_receiverPort, m_entryTime, expSeq, m_sipRequest->Size(), lastSeq);
+}
+
+void SipTcpStream::AddTcpPacket(u_char *pBuffer, int packetLen)
+{
+ m_sipRequest->Add(pBuffer, packetLen);
+}
+
+/*
+ * How we know the SIP request is complete: Small excerpt from
+ * RFC3261
+ *
+ * ---8<---
+ * ...
+ * 20.14 Content-Length
+ *
+ * The Content-Length header field indicates the size of the message-
+ * body, in decimal number of octets, sent to the recipient.
+ * Applications SHOULD use this field to indicate the size of the
+ * message-body to be transferred, regardless of the media type of the
+ * entity. If a stream-based protocol (such as TCP) is used as
+ * transport, the header field MUST be used.
+ *
+ * The size of the message-body does not include the CRLF separating
+ * header fields and body. Any Content-Length greater than or equal to
+ * zero is a valid value. If no body is present in a message, then the
+ * Content-Length header field value MUST be set to zero.
+ * ...
+ * --->8---
+ *
+ */
+bool SipTcpStream::SipRequestIsComplete()
+{
+ if(!m_sipRequest->Size())
+ return false;
+
+ char *pBufStart = (char*)m_sipRequest->GetBuffer();
+ char *pBufEnd = pBufStart+m_sipRequest->Size();
+ char *contentLengthHeader = ACE_OS::strstr(pBufStart, "Content-Length: ");
+ char *contentLength = memFindAfter("Content-Length: ", pBufStart, pBufEnd);
+ int cLength = 0;
+
+ if(!contentLength || !contentLengthHeader)
+ return false;
+
+ char *eol = memFindEOL(contentLengthHeader, pBufEnd);
+ if(eol == contentLengthHeader)
+ return false;
+
+ cLength = ACE_OS::atoi(contentLength);
+ if(!cLength)
+ return true;
+
+ /* Step over newlines */
+ while(*eol && (*eol == '\r' || *eol == '\n'))
+ eol++;
+
+ if(!*eol)
+ return false;
+
+ if(strlen(eol) == cLength)
+ return true;
+}
+
+SafeBufferRef SipTcpStream::GetCompleteSipRequest()
+{
+ SafeBufferRef buf(new SafeBuffer());
+
+ buf->Store(m_sipRequest->GetBuffer(), m_sipRequest->Size());
+
+ return buf;
+}
+
diff --git a/orkaudio/audiocaptureplugins/voip/SipTcp.h b/orkaudio/audiocaptureplugins/voip/SipTcp.h
new file mode 100644
index 0000000..bd02d96
--- /dev/null
+++ b/orkaudio/audiocaptureplugins/voip/SipTcp.h
@@ -0,0 +1,69 @@
+/*
+ * 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 _SIPTCP_H__
+#define _SIPTCP_H__ 1
+
+#include <log4cxx/logger.h>
+#include <list>
+#include "SipTcp.h"
+#include <map>
+#include "ace/Singleton.h"
+#include "PacketHeaderDefs.h"
+
+using namespace log4cxx;
+
+class SafeBuffer
+{
+public:
+ SafeBuffer();
+ ~SafeBuffer();
+
+ void Store(u_char *buf, int len);
+ void Add(u_char *buf, int len);
+ u_char *GetBuffer();
+ int Size();
+
+private:
+ u_char *m_pBuffer;
+ int m_size;
+};
+typedef boost::shared_ptr<SafeBuffer> SafeBufferRef;
+
+// ============================================================
+
+class SipTcpStream
+{
+public:
+ SipTcpStream();
+ ~SipTcpStream();
+ void ToString(CStdString& string);
+ void AddTcpPacket(u_char *pBuffer, int packetLen);
+ bool SipRequestIsComplete();
+ SafeBufferRef GetCompleteSipRequest();
+
+ struct in_addr m_senderIp;
+ struct in_addr m_receiverIp;
+ int m_senderPort;
+ int m_receiverPort;
+ unsigned long int m_expectingSeqNo;
+ unsigned long int m_lastSeqNo;
+
+ int m_entryTime;
+private:
+ SafeBufferRef m_sipRequest;
+};
+typedef boost::shared_ptr<SipTcpStream> SipTcpStreamRef;
+
+#endif
+
diff --git a/orkaudio/audiocaptureplugins/voip/VoIp.cpp b/orkaudio/audiocaptureplugins/voip/VoIp.cpp
index 6e7c8fb..9f34247 100644
--- a/orkaudio/audiocaptureplugins/voip/VoIp.cpp
+++ b/orkaudio/audiocaptureplugins/voip/VoIp.cpp
@@ -38,6 +38,7 @@
#include "Rtp.h"
#include "RtpSession.h"
#include "Iax2Session.h"
+#include "SipTcp.h"
extern AudioChunkCallBackFunction g_audioChunkCallBack;
extern CaptureEventCallBackFunction g_captureEventCallBack;
@@ -49,6 +50,7 @@ static LoggerPtr s_packetLog;
static LoggerPtr s_packetStatsLog;
static LoggerPtr s_rtpPacketLog;
static LoggerPtr s_sipPacketLog;
+static LoggerPtr s_sipTcpPacketLog;
static LoggerPtr s_skinnyPacketLog;
static LoggerPtr s_sipExtractionLog;
static LoggerPtr s_voipPluginLog;
@@ -65,6 +67,7 @@ static unsigned int s_numPackets;
static unsigned int s_numPacketsPerSecond;
static unsigned int s_minPacketsPerSecond;
static unsigned int s_maxPacketsPerSecond;
+static std::list<SipTcpStreamRef> s_SipTcpStreams;
VoIpConfigTopObjectRef g_VoIpConfigTopObjectRef;
#define DLLCONFIG g_VoIpConfigTopObjectRef.get()->m_config
@@ -1073,6 +1076,158 @@ bool TrySipBye(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader, U
return result;
}
+bool TrySipInvite(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader, UdpHeaderStruct* udpHeader, u_char* udpPayload);
+bool TrySipBye(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader, UdpHeaderStruct* udpHeader, u_char* udpPayload);
+
+static bool SipByeTcpToUdp(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader,TcpHeaderStruct* tcpHeader, u_char *pBuffer, int bLength)
+{
+ UdpHeaderStruct udpHeader;
+
+ udpHeader.source = tcpHeader->source;
+ udpHeader.dest = tcpHeader->dest;
+ udpHeader.len = htons(bLength+sizeof(UdpHeaderStruct));
+
+ return TrySipBye(ethernetHeader, ipHeader, &udpHeader, pBuffer);
+}
+
+static bool SipInviteTcpToUdp(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader, TcpHeaderStruct* tcpHeader, u_char *pBuffer, int bLength)
+{
+ UdpHeaderStruct udpHeader;
+
+ udpHeader.source = tcpHeader->source;
+ udpHeader.dest = tcpHeader->dest;
+ udpHeader.len = htons(bLength+sizeof(UdpHeaderStruct));
+
+ return TrySipInvite(ethernetHeader, ipHeader, &udpHeader, pBuffer);
+}
+
+bool TrySipTcp(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader, TcpHeaderStruct* tcpHeader)
+{
+ int tcpLengthPayloadLength = 0;
+ bool result = false;
+ std::list<SipTcpStreamRef> toErase;
+
+ if(ntohs(tcpHeader->source) != 5060 && ntohs(tcpHeader->dest) != 5060)
+ return false;
+
+ u_char* startTcpPayload = (u_char*)tcpHeader + TCP_HEADER_LENGTH;
+ tcpLengthPayloadLength = ((u_char*)ipHeader+ntohs(ipHeader->ip_len)) - startTcpPayload;
+
+ if(tcpLengthPayloadLength < 6)
+ {
+ return false;
+ }
+
+ if((memcmp("INVITE", (void*)startTcpPayload, 6) == 0) ||
+ (memcmp("BYE", (void*)startTcpPayload, 3) == 0))
+ {
+ SipTcpStreamRef tcpstream(new SipTcpStream());
+ int exists = 0;
+
+ tcpstream->m_senderIp = ipHeader->ip_src;
+ tcpstream->m_receiverIp = ipHeader->ip_dest;
+ tcpstream->m_senderPort = ntohs(tcpHeader->source);
+ tcpstream->m_receiverPort = ntohs(tcpHeader->dest);
+ tcpstream->m_expectingSeqNo = ntohl(tcpHeader->seq)+tcpLengthPayloadLength;
+ tcpstream->m_lastSeqNo = ntohl(tcpHeader->seq);
+ tcpstream->AddTcpPacket(startTcpPayload, tcpLengthPayloadLength);
+
+ // Ensure this is not a duplicate
+ for(std::list<SipTcpStreamRef>::iterator it = s_SipTcpStreams.begin(); it != s_SipTcpStreams.end(); it++)
+ {
+ SipTcpStreamRef tcpStreamList = *it;
+
+ if(((unsigned int)(tcpstream->m_senderIp.s_addr) == (unsigned int)(tcpStreamList->m_senderIp.s_addr)) &&
+ ((unsigned int)(tcpstream->m_receiverIp.s_addr) == (unsigned int)(tcpStreamList->m_receiverIp.s_addr)) &&
+ (tcpstream->m_senderPort == tcpStreamList->m_senderPort) &&
+ (tcpstream->m_receiverPort == tcpStreamList->m_receiverPort) &&
+ (tcpstream->m_expectingSeqNo == tcpStreamList->m_expectingSeqNo) &&
+ (tcpstream->m_lastSeqNo == tcpStreamList->m_lastSeqNo))
+ {
+ exists = 1;
+ break;
+ }
+ }
+
+ if(exists == 1) {
+ CStdString logMsg;
+
+ logMsg.Format("Dropped duplicate TCP packet");
+ LOG4CXX_INFO(s_sipTcpPacketLog, logMsg);
+ return true;
+ }
+
+ if(tcpstream->SipRequestIsComplete()) {
+ /* Hmm.. Lucky us */
+ SafeBufferRef buffer = tcpstream->GetCompleteSipRequest();
+ CStdString tcpStream;
+
+ tcpstream->ToString(tcpStream);
+ LOG4CXX_INFO(s_sipTcpPacketLog, "Obtained complete TCP Stream: " + tcpStream);
+
+ if(memcmp("INVITE", (void*)buffer->GetBuffer(), 6) == 0)
+ return SipInviteTcpToUdp(ethernetHeader, ipHeader, tcpHeader, buffer->GetBuffer(), buffer->Size());
+
+ return SipByeTcpToUdp(ethernetHeader, ipHeader, tcpHeader, buffer->GetBuffer(), buffer->Size());
+ }
+
+ s_SipTcpStreams.push_back(tcpstream);
+
+ CStdString tcpStream;
+
+ tcpstream->ToString(tcpStream);
+ LOG4CXX_INFO(s_sipTcpPacketLog, "Obtained incomplete TCP Stream: " + tcpStream);
+
+ return true;
+ }
+
+ for(std::list<SipTcpStreamRef>::iterator it = s_SipTcpStreams.begin(); it != s_SipTcpStreams.end(); it++)
+ {
+ SipTcpStreamRef tcpstream = *it;
+ int found = 0;
+
+ if(((unsigned int)(tcpstream->m_senderIp.s_addr) == (unsigned int)(ipHeader->ip_src.s_addr)) &&
+ ((unsigned int)(tcpstream->m_receiverIp.s_addr) == (unsigned int)(ipHeader->ip_dest.s_addr)) &&
+ (tcpstream->m_senderPort == ntohs(tcpHeader->source)) &&
+ (tcpstream->m_receiverPort == ntohs(tcpHeader->dest)) &&
+ (tcpstream->m_expectingSeqNo == ntohl(tcpHeader->seq)) && !found)
+ {
+ result = true;
+ found = 1;
+
+ tcpstream->AddTcpPacket(startTcpPayload, tcpLengthPayloadLength);
+
+ if(tcpstream->SipRequestIsComplete()) {
+ SafeBufferRef buffer = tcpstream->GetCompleteSipRequest();
+ CStdString tcpStream;
+
+ tcpstream->ToString(tcpStream);
+ LOG4CXX_INFO(s_sipTcpPacketLog, "TCP Stream updated to completion: " + tcpStream);
+
+ if(memcmp("INVITE", (void*)buffer->GetBuffer(), 6) == 0) {
+ SipInviteTcpToUdp(ethernetHeader, ipHeader, tcpHeader, buffer->GetBuffer(), buffer->Size());
+ } else {
+ SipByeTcpToUdp(ethernetHeader, ipHeader, tcpHeader, buffer->GetBuffer(), buffer->Size());
+ }
+
+ toErase.push_back(tcpstream);
+ }
+ } else {
+ if((time(NULL) - tcpstream->m_entryTime) >= 60)
+ toErase.push_back(tcpstream);
+ }
+ }
+
+ for(std::list<SipTcpStreamRef>::iterator it = toErase.begin(); it != toErase.end(); it++)
+ {
+ SipTcpStreamRef tcpstream = *it;
+
+ s_SipTcpStreams.remove(tcpstream);
+ }
+
+ return result;
+}
+
bool TrySipInvite(EthernetHeaderStruct* ethernetHeader, IpHeaderStruct* ipHeader, UdpHeaderStruct* udpHeader, u_char* udpPayload)
{
bool result = false;
@@ -1539,7 +1694,12 @@ void HandlePacket(u_char *param, const struct pcap_pkthdr *header, const u_char
else if(ipHeader->ip_p == IPPROTO_TCP)
{
TcpHeaderStruct* tcpHeader = (TcpHeaderStruct*)((char *)ipHeader + ipHeaderLength);
-
+ CStdString tcpSeq;
+
+ memToHex((unsigned char *)&tcpHeader->seq, 4, tcpSeq);
+
+ TrySipTcp(ethernetHeader, ipHeader, tcpHeader);
+
if(ntohs(tcpHeader->source) == SKINNY_CTRL_PORT || ntohs(tcpHeader->dest) == SKINNY_CTRL_PORT)
{
u_char* startTcpPayload = (u_char*)tcpHeader + TCP_HEADER_LENGTH;
@@ -1836,11 +1996,13 @@ void VoIp::OpenDevices()
void VoIp::Initialize()
{
m_pcapHandles.clear();
+ s_SipTcpStreams.clear();
s_packetLog = Logger::getLogger("packet");
s_packetStatsLog = Logger::getLogger("packet.pcapstats");
s_rtpPacketLog = Logger::getLogger("packet.rtp");
s_sipPacketLog = Logger::getLogger("packet.sip");
+ s_sipTcpPacketLog = Logger::getLogger("packet.tcpsip");
s_skinnyPacketLog = Logger::getLogger("packet.skinny");
s_sipExtractionLog = Logger::getLogger("sipextraction");