#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; }