You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
610 lines
17 KiB
610 lines
17 KiB
#include "precomp.h"
|
|
|
|
SIP_MESSAGE::SIP_MESSAGE()
|
|
{
|
|
ZeroMemory( this, sizeof SIP_MESSAGE );
|
|
|
|
InitializeListHead(&m_HeaderList);
|
|
|
|
ParseState = SIP_PARSE_STATE_INIT;
|
|
}
|
|
|
|
|
|
SIP_MESSAGE::~SIP_MESSAGE()
|
|
{
|
|
if (CSeqMethodStr != NULL)
|
|
{
|
|
free(CSeqMethodStr);
|
|
}
|
|
|
|
FreeHeaderList();
|
|
}
|
|
|
|
|
|
void SIP_MESSAGE::Reset()
|
|
{
|
|
ParseState = SIP_PARSE_STATE_INIT;
|
|
BaseBuffer = NULL;
|
|
ContentLengthSpecified = FALSE;
|
|
MsgBody.Offset = 0;
|
|
MsgBody.Length = 0;
|
|
FreeHeaderList();
|
|
}
|
|
|
|
|
|
// Both do essentially the same thing.
|
|
#define InsertBeforeListElement(ListElement, NewElement) \
|
|
InsertTailList(ListElement, NewElement)
|
|
|
|
HRESULT SIP_MESSAGE::AddHeader(
|
|
IN OFFSET_STRING *pHeaderName,
|
|
IN SIP_HEADER_ENUM HeaderId,
|
|
IN OFFSET_STRING *pHeaderValue
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
|
|
SIP_HEADER_ENTRY *pNewHeaderEntry = new SIP_HEADER_ENTRY;
|
|
|
|
if (pNewHeaderEntry == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
pNewHeaderEntry->HeaderName = *pHeaderName;
|
|
pNewHeaderEntry->HeaderId = HeaderId;
|
|
pNewHeaderEntry->HeaderValue = *pHeaderValue;
|
|
|
|
LIST_ENTRY *pListEntry;
|
|
SIP_HEADER_ENTRY *pHeaderEntry;
|
|
|
|
pListEntry = m_HeaderList.Flink;
|
|
|
|
while (pListEntry != &m_HeaderList)
|
|
{
|
|
pHeaderEntry = CONTAINING_RECORD(pListEntry,
|
|
SIP_HEADER_ENTRY,
|
|
ListEntry);
|
|
// Do an unsigned comparsion so that unknown headers
|
|
// are pushed to the end.
|
|
if ((ULONG)HeaderId < (ULONG)pHeaderEntry->HeaderId)
|
|
{
|
|
break;
|
|
}
|
|
|
|
pListEntry = pListEntry->Flink;
|
|
}
|
|
|
|
// Insert before the tail or the element we found with a greater HeaderId
|
|
InsertBeforeListElement(pListEntry, &pNewHeaderEntry->ListEntry);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
VOID SIP_MESSAGE::FreeHeaderList()
|
|
{
|
|
LIST_ENTRY *pListEntry;
|
|
SIP_HEADER_ENTRY *pHeaderEntry;
|
|
|
|
while (!IsListEmpty(&m_HeaderList))
|
|
{
|
|
pListEntry = RemoveHeadList(&m_HeaderList);
|
|
|
|
pHeaderEntry = CONTAINING_RECORD(pListEntry,
|
|
SIP_HEADER_ENTRY,
|
|
ListEntry);
|
|
delete pHeaderEntry;
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT
|
|
SIP_MESSAGE::StoreCallId()
|
|
{
|
|
SIP_HEADER_ENTRY *pHeaderEntry;
|
|
ULONG NumHeaders;
|
|
HRESULT hr;
|
|
|
|
ENTER_FUNCTION("SIP_MESSAGE::StoreCallId");
|
|
|
|
hr = GetHeader(SIP_HEADER_CALL_ID, &pHeaderEntry, &NumHeaders);
|
|
if (hr != S_OK)
|
|
{
|
|
LOG((RTC_ERROR, "%s Couldn't find Call-Id header %x",
|
|
__fxName, hr));
|
|
return hr;
|
|
}
|
|
else if (NumHeaders != 1)
|
|
{
|
|
LOG((RTC_ERROR, "%s More than one Call-Id header in message",
|
|
__fxName));
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
CallId = pHeaderEntry->HeaderValue;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
SIP_MESSAGE::StoreCSeq()
|
|
{
|
|
HRESULT hr;
|
|
PSTR CSeqHeader;
|
|
ULONG CSeqHeaderLen;
|
|
ULONG BytesParsed = 0;
|
|
|
|
ENTER_FUNCTION("SIP_MESSAGE::StoreCSeq");
|
|
|
|
hr = GetSingleHeader(SIP_HEADER_CSEQ, &CSeqHeader, &CSeqHeaderLen);
|
|
if (hr != S_OK)
|
|
{
|
|
LOG((RTC_ERROR, "%s Couldn't find CSeq header %x",
|
|
__fxName, hr));
|
|
return hr;
|
|
}
|
|
|
|
|
|
hr = ParseCSeq(CSeqHeader, CSeqHeaderLen, &BytesParsed,
|
|
&CSeq, &CSeqMethodId, &CSeqMethodStr);
|
|
if (hr != S_OK)
|
|
{
|
|
LOG((RTC_ERROR, "%s Parsing CSeq header failed %x",
|
|
__fxName, hr));
|
|
return hr;
|
|
}
|
|
|
|
// check for unknown method id also that both the strings
|
|
// for the method are the same.
|
|
|
|
if (MsgType == SIP_MESSAGE_TYPE_REQUEST &&
|
|
Request.MethodId != CSeqMethodId)
|
|
{
|
|
LOG((RTC_ERROR, "%s Request Method Id doesn't match CSeq method Id",
|
|
__fxName));
|
|
return E_FAIL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// Also checks whether the Content-Type is "application/sdp"
|
|
HRESULT
|
|
SIP_MESSAGE::GetSDPBody(
|
|
OUT PSTR *pSDPBody,
|
|
OUT ULONG *pSDPBodyLen
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
PSTR ContentTypeHdrValue;
|
|
ULONG ContentTypeHdrValueLen;
|
|
|
|
ENTER_FUNCTION("SIP_MESSAGE::GetSDPBody");
|
|
|
|
if (MsgBody.Length == 0)
|
|
{
|
|
*pSDPBody = NULL;
|
|
*pSDPBodyLen = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
// We have Message Body. Check type.
|
|
|
|
hr = GetSingleHeader(SIP_HEADER_CONTENT_TYPE,
|
|
&ContentTypeHdrValue,
|
|
&ContentTypeHdrValueLen);
|
|
if (hr != S_OK)
|
|
{
|
|
LOG((RTC_ERROR, "%s - couldn't find Content-Type header",
|
|
__fxName));
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (IsContentTypeSdp(ContentTypeHdrValue, ContentTypeHdrValueLen))
|
|
{
|
|
*pSDPBody = MsgBody.GetString(BaseBuffer);
|
|
*pSDPBodyLen = MsgBody.Length;
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
LOG((RTC_ERROR, "%s - invalid Content-Type %.*s",
|
|
__fxName, ContentTypeHdrValueLen, ContentTypeHdrValue));
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Returns the number of headers if there are multiple headers.
|
|
// Should we store the headers in sorted order and do a binary
|
|
// search.
|
|
// All the headers with the same header name are stored consecutively.
|
|
|
|
HRESULT SIP_MESSAGE::GetHeader(
|
|
IN SIP_HEADER_ENUM HeaderId,
|
|
OUT SIP_HEADER_ENTRY **ppHeaderEntry,
|
|
OUT ULONG *pNumHeaders
|
|
)
|
|
{
|
|
LIST_ENTRY *pListEntry;
|
|
SIP_HEADER_ENTRY *pHeaderEntry;
|
|
|
|
*ppHeaderEntry = NULL;
|
|
*pNumHeaders = 0;
|
|
|
|
pListEntry = m_HeaderList.Flink;
|
|
|
|
while (pListEntry != &m_HeaderList)
|
|
{
|
|
pHeaderEntry = CONTAINING_RECORD(pListEntry,
|
|
SIP_HEADER_ENTRY,
|
|
ListEntry);
|
|
if (HeaderId == pHeaderEntry->HeaderId)
|
|
{
|
|
*ppHeaderEntry = pHeaderEntry;
|
|
while (pListEntry != &m_HeaderList &&
|
|
HeaderId == pHeaderEntry->HeaderId)
|
|
{
|
|
(*pNumHeaders)++;
|
|
pListEntry = pListEntry->Flink;
|
|
pHeaderEntry = CONTAINING_RECORD(pListEntry,
|
|
SIP_HEADER_ENTRY,
|
|
ListEntry);
|
|
}
|
|
return S_OK;
|
|
}
|
|
pListEntry = pListEntry->Flink;
|
|
}
|
|
|
|
return RTC_E_SIP_HEADER_NOT_PRESENT;
|
|
}
|
|
|
|
|
|
// Can be used to get headers such as From, To, CallId,
|
|
// which are guaranteed to have just one header (unlike Via, Contact)
|
|
HRESULT
|
|
SIP_MESSAGE::GetSingleHeader(
|
|
IN SIP_HEADER_ENUM HeaderId,
|
|
OUT PSTR *pHeaderValue,
|
|
OUT ULONG *pHeaderValueLen
|
|
)
|
|
{
|
|
SIP_HEADER_ENTRY *pHeaderEntry;
|
|
ULONG NumHeaders;
|
|
HRESULT hr;
|
|
|
|
ENTER_FUNCTION("SIP_MESSAGE::GetSingleHeader");
|
|
|
|
hr = GetHeader(HeaderId, &pHeaderEntry, &NumHeaders);
|
|
if (hr != S_OK)
|
|
{
|
|
return hr;
|
|
}
|
|
else if (NumHeaders != 1)
|
|
{
|
|
LOG((RTC_ERROR, "%s - more than one header : %d",
|
|
__fxName, NumHeaders));
|
|
return E_FAIL;
|
|
}
|
|
|
|
*pHeaderValueLen = pHeaderEntry->HeaderValue.Length;
|
|
*pHeaderValue = pHeaderEntry->HeaderValue.GetString(BaseBuffer);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// Same as GetSingleHeader() except that it could be used
|
|
// to get for headers such as Contact which could have multiple
|
|
// headers in a message. This function is called if we only care
|
|
// about processing the first header.
|
|
HRESULT
|
|
SIP_MESSAGE::GetFirstHeader(
|
|
IN SIP_HEADER_ENUM HeaderId,
|
|
OUT PSTR *pHeaderValue,
|
|
OUT ULONG *pHeaderValueLen
|
|
)
|
|
{
|
|
SIP_HEADER_ENTRY *pHeaderEntry;
|
|
ULONG NumHeaders;
|
|
HRESULT hr;
|
|
|
|
hr = GetHeader(HeaderId, &pHeaderEntry, &NumHeaders);
|
|
if (hr != S_OK)
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
*pHeaderValueLen = pHeaderEntry->HeaderValue.Length;
|
|
*pHeaderValue = pHeaderEntry->HeaderValue.GetString(BaseBuffer);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
SIP_MESSAGE::GetStoredMultipleHeaders(
|
|
IN SIP_HEADER_ENUM HeaderId,
|
|
OUT COUNTED_STRING **pStringArray,
|
|
OUT ULONG *pNumHeaders
|
|
)
|
|
{
|
|
LIST_ENTRY *pListEntry;
|
|
SIP_HEADER_ENTRY *pHeaderEntry;
|
|
ULONG NumHeaders = 0;
|
|
COUNTED_STRING *StringArray;
|
|
HRESULT hr;
|
|
ULONG i;
|
|
|
|
*pStringArray = NULL;
|
|
*pNumHeaders = 0;
|
|
|
|
hr = GetHeader(HeaderId, &pHeaderEntry, &NumHeaders);
|
|
if (hr != S_OK)
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
StringArray = (COUNTED_STRING *) malloc(NumHeaders * sizeof(COUNTED_STRING));
|
|
if (StringArray == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
ZeroMemory(StringArray, NumHeaders * sizeof(COUNTED_STRING));
|
|
|
|
pListEntry = (LIST_ENTRY *)
|
|
(pHeaderEntry + FIELD_OFFSET(SIP_HEADER_ENTRY, ListEntry));
|
|
|
|
for (i = 0; i < NumHeaders; i++)
|
|
{
|
|
PSTR HeaderValue;
|
|
ULONG HeaderLen;
|
|
|
|
pHeaderEntry = CONTAINING_RECORD(pListEntry,
|
|
SIP_HEADER_ENTRY,
|
|
ListEntry);
|
|
|
|
HeaderLen = pHeaderEntry->HeaderValue.Length;
|
|
HeaderValue = pHeaderEntry->HeaderValue.GetString(BaseBuffer);
|
|
StringArray[i].Buffer = (PSTR) malloc(HeaderLen + 1);
|
|
if (StringArray[i].Buffer == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto error;
|
|
}
|
|
|
|
strncpy(StringArray[i].Buffer, HeaderValue, HeaderLen);
|
|
StringArray[i].Buffer[HeaderLen] = '\0';
|
|
StringArray[i].Length = HeaderLen;
|
|
|
|
pListEntry = pListEntry->Flink;
|
|
}
|
|
|
|
*pNumHeaders = NumHeaders;
|
|
*pStringArray = StringArray;
|
|
return S_OK;
|
|
|
|
error:
|
|
if (StringArray != NULL)
|
|
{
|
|
for (i = 0; i < NumHeaders; i++)
|
|
{
|
|
if (StringArray[i].Buffer != NULL)
|
|
free(StringArray[i].Buffer);
|
|
}
|
|
free(StringArray);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
// The list should be sorted descending using QValue.
|
|
HRESULT
|
|
SIP_MESSAGE::InsertInContactHeaderList(
|
|
IN OUT LIST_ENTRY *pContactHeaderList,
|
|
IN CONTACT_HEADER *pNewContactHeader
|
|
)
|
|
{
|
|
LIST_ENTRY *pListEntry;
|
|
CONTACT_HEADER *pContactHeader;
|
|
|
|
pListEntry = pContactHeaderList->Flink;
|
|
|
|
while (pListEntry != pContactHeaderList)
|
|
{
|
|
pContactHeader = CONTAINING_RECORD(pListEntry,
|
|
CONTACT_HEADER,
|
|
m_ListEntry);
|
|
|
|
// Do an unsigned comparsion so that unknown headers
|
|
// are pushed to the end.
|
|
if (pNewContactHeader->m_QValue > pContactHeader->m_QValue)
|
|
{
|
|
break;
|
|
}
|
|
|
|
pListEntry = pListEntry->Flink;
|
|
}
|
|
|
|
// Insert before the tail or the element we found with a greater HeaderId
|
|
InsertBeforeListElement(pListEntry, &pNewContactHeader->m_ListEntry);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// pContactHeaderList is the list head. This routine will parse all
|
|
// the contact headers into CONTACT_HEADER structures
|
|
// (allocated using new) and will add those structures to this
|
|
// list. The caller delete the structures when cleaning up the list.
|
|
|
|
// This function assumes that InitializeListHead() has been called
|
|
// earlier on pContactHeaderList.
|
|
|
|
// The list should be sorted using QValue.
|
|
|
|
HRESULT
|
|
SIP_MESSAGE::ParseContactHeaders(
|
|
OUT LIST_ENTRY *pContactHeaderList
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
LIST_ENTRY *pListEntry;
|
|
SIP_HEADER_ENTRY *pHeaderEntry;
|
|
CONTACT_HEADER *pContactHeader;
|
|
ULONG NumHeaders = 0;
|
|
ULONG i;
|
|
ULONG BytesParsed;
|
|
PSTR HeaderValue;
|
|
ULONG HeaderLen;
|
|
|
|
ASSERT(IsListEmpty(pContactHeaderList));
|
|
|
|
ENTER_FUNCTION("SIP_MESSAGE::ParseContactHeaders");
|
|
|
|
hr = GetHeader(SIP_HEADER_CONTACT, &pHeaderEntry, &NumHeaders);
|
|
if (hr != S_OK)
|
|
{
|
|
LOG((RTC_ERROR, "%s Couldn't find Contact header in msg %x",
|
|
__fxName, hr));
|
|
return hr;
|
|
}
|
|
|
|
pListEntry = (LIST_ENTRY *)
|
|
(pHeaderEntry + FIELD_OFFSET(SIP_HEADER_ENTRY, ListEntry));
|
|
|
|
for (i = 0; i < NumHeaders; i++)
|
|
{
|
|
pHeaderEntry = CONTAINING_RECORD(pListEntry,
|
|
SIP_HEADER_ENTRY,
|
|
ListEntry);
|
|
|
|
HeaderLen = pHeaderEntry->HeaderValue.Length;
|
|
HeaderValue = pHeaderEntry->HeaderValue.GetString(BaseBuffer);
|
|
|
|
BytesParsed = 0;
|
|
|
|
while (BytesParsed < HeaderLen)
|
|
{
|
|
pContactHeader = new CONTACT_HEADER;
|
|
|
|
if (pContactHeader == NULL)
|
|
{
|
|
LOG((RTC_ERROR, "%s allocating pContactHeader failed",
|
|
__fxName));
|
|
FreeContactHeaderList(pContactHeaderList);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
ZeroMemory( pContactHeader, sizeof(CONTACT_HEADER) );
|
|
|
|
hr = ParseContactHeader(HeaderValue, HeaderLen, &BytesParsed,
|
|
pContactHeader);
|
|
if (hr != S_OK)
|
|
{
|
|
// If parsing a contact header fails we just skip it.
|
|
delete pContactHeader;
|
|
LOG((RTC_ERROR,
|
|
"%s ParseContactHeader failed: %x - skipping Contact header %.*s",
|
|
__fxName, hr, HeaderLen, HeaderValue));
|
|
break;
|
|
}
|
|
|
|
InsertInContactHeaderList(pContactHeaderList, pContactHeader);
|
|
}
|
|
|
|
pListEntry = pListEntry->Flink;
|
|
}
|
|
|
|
if (IsListEmpty(pContactHeaderList))
|
|
{
|
|
LOG((RTC_ERROR, "%s - no valid Contact headers returning hr: %x",
|
|
__fxName, hr));
|
|
return hr;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// pRecordRouteHeaderList is the list head. This routine will parse
|
|
// all the Record-Route headers into RECORD_ROUTE_HEADER structures
|
|
// (allocated using new) and will add those structures to this
|
|
// list. The caller delete the structures when cleaning up the list.
|
|
|
|
// This function assumes that InitializeListHead() has been
|
|
// called earlier on pRecordRouteHeaderList.
|
|
|
|
// The headers are in the order they appear in the message.
|
|
|
|
HRESULT
|
|
SIP_MESSAGE::ParseRecordRouteHeaders(
|
|
OUT LIST_ENTRY *pRecordRouteHeaderList
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
LIST_ENTRY *pListEntry;
|
|
SIP_HEADER_ENTRY *pHeaderEntry;
|
|
RECORD_ROUTE_HEADER *pRecordRouteHeader;
|
|
ULONG NumHeaders = 0;
|
|
ULONG i;
|
|
ULONG BytesParsed;
|
|
PSTR HeaderValue;
|
|
ULONG HeaderLen;
|
|
|
|
ENTER_FUNCTION("SIP_MESSAGE::ParseRecordRouteHeaders");
|
|
|
|
hr = GetHeader(SIP_HEADER_RECORD_ROUTE, &pHeaderEntry, &NumHeaders);
|
|
if (hr != S_OK)
|
|
{
|
|
LOG((RTC_TRACE, "%s Couldn't find Record-Route header in msg %x",
|
|
__fxName, hr));
|
|
return hr;
|
|
}
|
|
|
|
pListEntry = (LIST_ENTRY *)
|
|
(pHeaderEntry + FIELD_OFFSET(SIP_HEADER_ENTRY, ListEntry));
|
|
|
|
for (i = 0; i < NumHeaders; i++)
|
|
{
|
|
pHeaderEntry = CONTAINING_RECORD(pListEntry,
|
|
SIP_HEADER_ENTRY,
|
|
ListEntry);
|
|
|
|
HeaderLen = pHeaderEntry->HeaderValue.Length;
|
|
HeaderValue = pHeaderEntry->HeaderValue.GetString(BaseBuffer);
|
|
|
|
BytesParsed = 0;
|
|
|
|
while (BytesParsed < HeaderLen)
|
|
{
|
|
pRecordRouteHeader = new RECORD_ROUTE_HEADER();
|
|
if (pRecordRouteHeader == NULL)
|
|
{
|
|
LOG((RTC_ERROR, "%s allocating pContactHeader failed",
|
|
__fxName));
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
hr = ParseRecordRouteHeader(HeaderValue, HeaderLen, &BytesParsed,
|
|
pRecordRouteHeader);
|
|
if (hr != S_OK)
|
|
{
|
|
delete pRecordRouteHeader;
|
|
LOG((RTC_ERROR, "%s ParseRecordRouteHeader failed: %x",
|
|
__fxName, hr));
|
|
return hr;
|
|
}
|
|
|
|
InsertTailList(pRecordRouteHeaderList,
|
|
&pRecordRouteHeader->m_ListEntry);
|
|
}
|
|
|
|
pListEntry = pListEntry->Flink;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|