diff options
-rw-r--r-- | orkaudio/audiocaptureplugins/voip/Makefile.am | 2 | ||||
-rw-r--r-- | orkaudio/audiocaptureplugins/voip/SipTcp.cpp | 237 | ||||
-rw-r--r-- | orkaudio/audiocaptureplugins/voip/SipTcp.h | 69 | ||||
-rw-r--r-- | orkaudio/audiocaptureplugins/voip/VoIp.cpp | 164 |
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"); |