windows-nt/Source/XPSP1/NT/net/upnp/ssdp/common/ssdpparser/parser.cpp
2020-09-26 16:20:57 +08:00

1236 lines
32 KiB
C++

/*++
Copyright (c) 1999 - 2000 Microsoft Corporation
Module Name:
parser.c
Abstract:
This module encode and decode ssdp notify and search messages.
Author:
Ting Cai (tingcai) creation-date-07-25-99
--*/
/* Note:
Use midl memory routines for SSDP_REQUEST
*/
#include <pch.h>
#pragma hdrstop
#include "ssdpparserp.h"
#include "ssdpapi.h"
#include "limits.h"
#include "ncstring.h"
#define HTTP_VERSION "HTTP/1.1"
#define END_HEADERS_SIZE 3
const char * szEndOfHeaders = "\n\r\n";
#define HEADER_END_SIZE 4
const char * szHeaderEnd = "\r\n\r\n";
BOOL IsStrDigits(LPSTR pszStr);
VOID strtrim(CHAR ** pszStr);
static const CHAR c_szMaxAge[] = "max-age";
static const DWORD c_cchMaxAge = celems(c_szMaxAge) - 1;
// Keep in sync with SSDP_METHOD in ssdp.idl
CHAR *SsdpMethodStr[] =
{
"NOTIFY",
"M-SEARCH",
"SUBSCRIBE",
"UNSUBSCRIBE",
"INVALID",
};
// Keep in sync with SSDP_HEADER in ssdp.idl
CHAR *SsdpHeaderStr[] =
{
"Host",
"NT",
"NTS",
"ST",
"Man",
"MX",
"Location",
"AL",
"USN",
"Cache-Control",
"Callback",
"Timeout",
"Scope",
"SID",
"SEQ",
"Content-Length",
"Content-Type",
"Server",
"Ext",
};
/*
NOTIFY * HTTP/1.1
Host: reservedSSDPmulticastaddress
NT: blenderassociation:blender
NTS: ssdp:alive
USN: someunique:idscheme3
AL: <blender:ixl><http://foo/bar>
Cache-Control: max-age = 7393
To-do: HTTP headers are all ascii, the API may accept unicode, but convert to ascii before
calling the rpc interface. The server keeps ascii version only.
To-do: Support arbitrary content.
*/
// Compose an SSDP alive message from the a SSDP Message structure, caller should
// free the memory when done.
BOOL ComposeSsdpResponse(const SSDP_REQUEST *Source, CHAR **pszBytes)
{
CHAR ResponseHeader[40] = "HTTP/1.1 200 OK";
INT iLength = 0;
INT iNumOfBytes = 0;
CHAR * szBytes;
INT i;
iLength = strlen(ResponseHeader) + strlen(CRLF);
if (Source->Headers[SSDP_NT] != NULL)
{
iLength += strlen(SsdpHeaderStr[SSDP_ST]) +
strlen(COLON) +
strlen(Source->Headers[SSDP_NT]) +
strlen(CRLF);
}
if (Source->Headers[SSDP_LOCATION] != NULL)
{
iLength += strlen(SsdpHeaderStr[SSDP_LOCATION]) +
strlen(COLON) +
strlen(Source->Headers[SSDP_LOCATION]) +
strlen(CRLF);
}
if (Source->Headers[SSDP_AL] != NULL)
{
iLength += strlen(SsdpHeaderStr[SSDP_AL]) +
strlen(COLON) +
strlen(Source->Headers[SSDP_AL]) +
strlen(CRLF);
}
if (Source->Headers[SSDP_USN] != NULL)
{
iLength += strlen(SsdpHeaderStr[SSDP_USN]) +
strlen(COLON) +
strlen(Source->Headers[SSDP_USN]) +
strlen(CRLF);
}
if (Source->Headers[SSDP_CACHECONTROL] != NULL)
{
iLength += strlen(SsdpHeaderStr[SSDP_CACHECONTROL]) +
strlen(COLON) +
strlen(Source->Headers[SSDP_CACHECONTROL]) +
strlen(CRLF);
}
if (Source->Headers[GENA_SID] != NULL)
{
iLength += strlen(SsdpHeaderStr[GENA_SID]) +
strlen(COLON) +
strlen(Source->Headers[GENA_SID]) +
strlen(CRLF);
}
if (Source->Headers[GENA_TIMEOUT] != NULL)
{
iLength += strlen(SsdpHeaderStr[GENA_TIMEOUT]) +
strlen(COLON) +
strlen(Source->Headers[GENA_TIMEOUT]) +
strlen(CRLF);
}
if (Source->Headers[SSDP_SERVER] != NULL)
{
iLength += strlen(SsdpHeaderStr[SSDP_SERVER]) +
strlen(COLON) +
strlen(Source->Headers[SSDP_SERVER]) +
strlen(CRLF);
}
if (Source->Headers[SSDP_EXT] != NULL)
{
iLength += strlen(SsdpHeaderStr[SSDP_EXT]) +
strlen(COLON) +
strlen(Source->Headers[SSDP_EXT]) +
strlen(CRLF);
}
iLength += strlen(CRLF);
szBytes = (CHAR *) midl_user_allocate(sizeof(CHAR) * iLength + 1);
if (szBytes == NULL)
{
TraceTag(ttidSsdpParser, "Faled to allocate memory for the ssdp message.");
return FALSE;
}
iNumOfBytes += wsprintf(szBytes + iNumOfBytes, "%s%s", ResponseHeader, CRLF);
if (Source->Headers[SSDP_NT] != NULL)
{
iNumOfBytes += wsprintf(szBytes + iNumOfBytes, "%s%s%s%s",
SsdpHeaderStr[SSDP_ST], COLON,
Source->Headers[SSDP_NT],CRLF);
}
if (Source->Headers[SSDP_USN] != NULL)
{
iNumOfBytes += wsprintf(szBytes + iNumOfBytes, "%s%s%s%s",
SsdpHeaderStr[SSDP_USN], COLON,
Source->Headers[SSDP_USN],CRLF);
}
if (Source->Headers[SSDP_LOCATION] != NULL)
{
iNumOfBytes += wsprintf(szBytes + iNumOfBytes, "%s%s%s%s",
SsdpHeaderStr[SSDP_LOCATION], COLON,
Source->Headers[SSDP_LOCATION],CRLF);
}
if (Source->Headers[SSDP_AL] != NULL)
{
iNumOfBytes += wsprintf(szBytes + iNumOfBytes, "%s%s%s%s",
SsdpHeaderStr[SSDP_AL], COLON,
Source->Headers[SSDP_AL],CRLF);
}
if (Source->Headers[SSDP_CACHECONTROL] != NULL)
{
iNumOfBytes += wsprintf(szBytes + iNumOfBytes, "%s%s%s%s",
SsdpHeaderStr[SSDP_CACHECONTROL], COLON,
Source->Headers[SSDP_CACHECONTROL],CRLF);
}
if (Source->Headers[GENA_SID] != NULL)
{
iNumOfBytes += wsprintf(szBytes + iNumOfBytes, "%s%s%s%s",
SsdpHeaderStr[GENA_SID], COLON,
Source->Headers[GENA_SID],CRLF);
}
if (Source->Headers[GENA_TIMEOUT] != NULL)
{
iNumOfBytes += wsprintf(szBytes + iNumOfBytes, "%s%s%s%s",
SsdpHeaderStr[GENA_TIMEOUT], COLON,
Source->Headers[GENA_TIMEOUT],CRLF);
}
if (Source->Headers[SSDP_SERVER] != NULL)
{
iNumOfBytes += wsprintf(szBytes + iNumOfBytes, "%s%s%s%s",
SsdpHeaderStr[SSDP_SERVER], COLON,
Source->Headers[SSDP_SERVER],CRLF);
}
if (Source->Headers[SSDP_EXT] != NULL)
{
iNumOfBytes += wsprintf(szBytes + iNumOfBytes, "%s%s%s%s",
SsdpHeaderStr[SSDP_EXT], COLON,
Source->Headers[SSDP_EXT],CRLF);
}
iNumOfBytes += wsprintf(szBytes + iNumOfBytes, "%s",CRLF);
*pszBytes = szBytes;
return TRUE;
}
// To-do: optimize, cache does not care about the request/response line
BOOL ComposeSsdpRequest(const SSDP_REQUEST *Source, CHAR **pszBytes)
{
INT iLength = 0;
INT iNumOfBytes = 0;
CHAR iLifeTime[10];
CHAR * szBytes;
INT i;
if (Source->Method != SSDP_INVALID && Source->RequestUri != NULL)
{
iLength += strlen(SsdpMethodStr[Source->Method]) + strlen(SP) +
strlen(Source->RequestUri) + strlen(SP) +
strlen(HTTP_VERSION) + strlen(CRLF);
}
for (i = 0; i < NUM_OF_HEADERS; i++)
{
if (Source->Headers[i] != NULL)
{
iLength += strlen(SsdpHeaderStr[i]) + strlen(COLON) +
strlen(Source->Headers[i]) + strlen(CRLF);
}
}
iLength += strlen(CRLF);
szBytes = (CHAR *) malloc(sizeof(CHAR) * iLength + 1);
if (szBytes == NULL)
{
TraceTag(ttidSsdpParser, "Faled to allocate memory for the ssdp message.");
return FALSE;
}
if (Source->Method != SSDP_INVALID && Source->RequestUri != NULL)
{
iNumOfBytes += wsprintf(szBytes + iNumOfBytes, "%s%s%s%s%s%s",
SsdpMethodStr[Source->Method], SP,
Source->RequestUri, SP, HTTP_VERSION, CRLF);
}
for (i = 0; i < NUM_OF_HEADERS; i++)
{
if (Source->Headers[i] != NULL)
{
iNumOfBytes += wsprintf(szBytes + iNumOfBytes, "%s%s%s%s",
SsdpHeaderStr[i], COLON,
Source->Headers[i],CRLF);
}
}
iNumOfBytes += wsprintf(szBytes + iNumOfBytes, "%s",CRLF);
*pszBytes = szBytes;
return TRUE;
}
BOOL FReplaceTokenInLocation(LPCSTR szIn, LPSTR szReplace, LPSTR *pszOut)
{
CHAR * pch;
Assert(pszOut);
*pszOut = NULL;
if (pch = strstr(szIn, c_szReplaceGuid))
{
LPSTR szOut;
DWORD cbOut;
cbOut = (lstrlen(szIn) - c_cchReplaceGuid + lstrlen(szReplace) + 1) *
sizeof(CHAR);
szOut = (LPSTR)malloc(cbOut);
if (szOut)
{
lstrcpyn(szOut, szIn, (int)(pch - szIn + 1));
lstrcat(szOut, szReplace);
pch += c_cchReplaceGuid;
lstrcat(szOut, pch);
*pszOut = szOut;
}
else
{
return FALSE;
}
}
return TRUE;
}
// Pre-Conditions:
// Result->Headers[CONTENT_LENGTH] contains only digits.
// pContent points to the first char "\r\n\r\n".
// Return szValue:
// returns FALSE if there not enough content.
BOOL ParseContent(const char *pContent, DWORD cbContent, SSDP_REQUEST *Result)
{
if(!Result->Headers[CONTENT_LENGTH])
{
return TRUE;
}
DWORD dwContentLength = strtoul(Result->Headers[CONTENT_LENGTH], NULL, 10);
if (dwContentLength == 0)
{
// If it can't be conver to a number or it is 0.
TraceTag(ttidSsdpParser, "Content-Length is 0.");
return TRUE;
}
else if ((cbContent == (DWORD)(-1)) || (cbContent == dwContentLength))
{
Result->Content = (CHAR*) midl_user_allocate(dwContentLength + 1);
if (Result->Content == NULL)
{
TraceTag(ttidSsdpParser, "Failed to allocate memory for Content");
return FALSE;
}
else
{
lstrcpyn(Result->Content,pContent, dwContentLength + 1);
return TRUE;
}
}
return FALSE;
}
CHAR * ParseRequestLine(CHAR * szMessage, SSDP_REQUEST *Result)
{
CHAR *token;
INT i;
Assert(NULL != szMessage);
// Make sure we don't go past the end of the buffer
CHAR * pchEnd = szMessage + lstrlen(szMessage);
// Get the HTTP method
token = strtok(szMessage," \t\n");
if (token == NULL)
{
TraceTag(ttidSsdpParser, "Parser could not locate the seperator, "
"space, tab or eol");
return NULL;
}
for (i = 0; i < NUM_OF_METHODS; i++)
{
if (_stricmp(SsdpMethodStr[i],token) == 0)
{
Result->Method = (SSDP_METHOD)i;
break;
}
}
if (i == NUM_OF_METHODS)
{
TraceTag(ttidSsdpParser, "Parser could not find method . "
"Received %s", token);
return NULL;
}
// Get the Request-URI
token = strtok(NULL," ");
if (token == NULL)
{
TraceTag(ttidSsdpParser, "Parser could not find the url in the "
"message.");
return NULL;
}
// Ingore the name space parsing for now, get the string after the last '/'.
Result->RequestUri = (CHAR*) midl_user_allocate(strlen(token) + 1);
if (Result->RequestUri == NULL)
{
TraceTag(ttidSsdpParser, "Parser could not allocate memory for url.");
return NULL;
}
// Record the service.
strcpy(Result->RequestUri, token);
// Get the version number
token = strtok(NULL," \t\r");
// To-Do: Record the version number when necessary.
if (token == NULL)
{
TraceTag(ttidSsdpParser, "Failed to get the version in the request "
"header.");
FreeSsdpRequest(Result);
return NULL;
}
if (_stricmp(token, "HTTP/1.1") != 0)
{
TraceTag(ttidSsdpParser, "The version specified in the request "
"message is not HTTP/1.1");
FreeSsdpRequest(Result);
return NULL;
}
// point to the rest of the header data, after the current token
token += lstrlen(token) + 1;
if (token >= pchEnd)
{
TraceTag(ttidSsdpParser, "no data following HTTP/1.1 in the request");
FreeSsdpRequest(Result);
return NULL;
}
return token;
}
BOOL VerifySsdpHeaders(SSDP_REQUEST *Result)
{
if (Result->Method == SSDP_M_SEARCH)
{
if (Result->Headers[SSDP_ST] == NULL ||
Result->Headers[SSDP_MX] == NULL ||
*Result->Headers[SSDP_MX] == L'\0' ||
*Result->Headers[SSDP_ST] == L'\0')
{
TraceTag(ttidSsdpParser, "ST and MX header should not be NULL for M-SEARCH");
return FALSE;
}
if (Result->Headers[SSDP_MAN] == NULL || lstrcmpi(Result->Headers[SSDP_MAN], "\"ssdp:discover\""))
{
TraceTag(ttidSsdpParser, "MAN header for M-SEARCH must be \"ssdp:discover\"");
return FALSE;
}
if (Result->Headers[SSDP_HOST] == NULL || lstrcmpi(Result->Headers[SSDP_HOST], SSDP_ADDR_PORT))
{
TraceTag(ttidSsdpParser, "HOST must be 239.255.255.250:1900"
"for n M-SEARCH message.");
return FALSE;
}
}
if (Result->Headers[SSDP_MX] != NULL &&
IsStrDigits(Result->Headers[SSDP_MX]) == FALSE)
{
TraceTag(ttidSsdpParser, "MX header should be all digits");
return FALSE;
}
if (Result->Method == SSDP_NOTIFY)
{
if (Result->Headers[SSDP_NT] == NULL ||
Result->Headers[SSDP_NTS] == NULL)
{
TraceTag(ttidSsdpParser, "NT and NTS headers should not be NULL for a NOTIFY message.");
return FALSE;
}
// Assume NOTIFY other than upnp:propchange requires USN header.
// Currently, ssdp:alive and ssdp:byebye
if (lstrcmpi(Result->Headers[SSDP_NTS], "upnp:propchange"))
{
if (Result->Headers[SSDP_USN] == NULL)
{
TraceTag(ttidSsdpParser, "USN headers should not be NULL for "
"a NOTIFY message.");
return FALSE;
}
if (!lstrcmpi(Result->Headers[SSDP_NTS], "ssdp:alive"))
{
// ssdp:alive messages must have a location header
if (Result->Headers[SSDP_LOCATION] == NULL)
{
TraceTag(ttidSsdpParser, "LOCATION header can not be NULL "
"for a NOTIFY message.");
return FALSE;
}
// ssdp:alive messages must have a SERVER header
if (Result->Headers[SSDP_SERVER] == NULL)
{
TraceTag(ttidSsdpParser, "SERVER header can not be NULL "
"for a NOTIFY message.");
return FALSE;
}
if (Result->Headers[SSDP_HOST] == NULL || lstrcmpi(Result->Headers[SSDP_HOST], SSDP_ADDR_PORT))
{
TraceTag(ttidSsdpParser, "HOST must be 239.255.255.250:1900"
"for a NOTIFY message.");
return FALSE;
}
}
else if (!lstrcmpi(Result->Headers[SSDP_NTS], "ssdp:byebye"))
{
if (Result->Headers[SSDP_HOST] == NULL || lstrcmpi(Result->Headers[SSDP_HOST], SSDP_ADDR_PORT))
{
TraceTag(ttidSsdpParser, "HOST must be 239.255.255.250:1900"
"for a NOTIFY message.");
return FALSE;
}
}
}
}
if (Result->Headers[CONTENT_LENGTH] != NULL &&
IsStrDigits(Result->Headers[CONTENT_LENGTH]) == FALSE )
{
TraceTag(ttidSsdpParser, "Content length header should be all digits");
return FALSE;
}
return TRUE;
}
BOOL HasContentBody(PSSDP_REQUEST Result)
{
return (Result->Headers[CONTENT_LENGTH] != NULL);
}
BOOL ParseSsdpRequest(CHAR * szMessage, PSSDP_REQUEST Result)
{
CHAR *szHeaders;
szHeaders = ParseRequestLine(szMessage, Result);
if (szHeaders == NULL)
{
return FALSE;
}
char *pContent = ParseHeaders(szHeaders, Result);
if ( pContent == NULL)
{
return FALSE;
}
else
{
if (VerifySsdpHeaders(Result) == FALSE)
{
return FALSE;
}
// Headers are OK.
if (Result->Headers[CONTENT_LENGTH] != NULL)
{
// To-Do: Maybe we can share the this routine with those
// in ProcessTcpReceiveData();
// In that case, we need catch the return szValue of ParseContent
// and probably return a more meaningful error code.
ParseContent(pContent, (DWORD)(-1), Result);
}
return TRUE;
}
}
// Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
// Returns a pointer to the first CHAR after the status line
// Returns NULL if fail to parse status line
BOOL ParseSsdpResponse(CHAR *szMessage, SSDP_REQUEST *Result)
{
CHAR *token;
CHAR *szHeaders;
Assert(NULL != szMessage);
// Make sure we don't go past the end of the buffer
CHAR * pchEnd = szMessage + lstrlen(szMessage);
// get the version
token = strtok(szMessage," \t\n");
if (token == NULL)
{
TraceTag(ttidSsdpParser, "Response: Parser could not locate the "
"seperator, space, tab or eol");
return FALSE;
}
if (_stricmp(token, "HTTP/1.1") != 0)
{
TraceTag(ttidSsdpParser, "The version specified in the response "
"message is not HTTP/1.1");
return FALSE;
}
// get the response code
token = strtok(NULL," ");
if (token == NULL)
{
TraceTag(ttidSsdpParser, "Parser could not find the url in the "
"message.");
return FALSE;
}
if (_stricmp(token, "200") != 0)
{
TraceTag(ttidSsdpParser, "The response code in the response message "
"is not HTTP/1.1");
return FALSE;
}
// get the response message, no need for now.
token = strtok(NULL," \t\r");
if (token == NULL)
{
TraceTag(ttidSsdpParser, "Failed to get the version in the request "
"header.");
return FALSE;
}
szHeaders = token + strlen(token) + 1;
if (szHeaders >= pchEnd)
{
TraceTag(ttidSsdpParser, "no data following HTTP/1.1 200 in the request");
return FALSE;
}
char *pContent = ParseHeaders(szHeaders, Result);
if (pContent == NULL || Result->Headers[SSDP_USN] == NULL)
{
return FALSE;
} else
{
if (Result->Headers[CONTENT_LENGTH] != NULL)
{
ParseContent(pContent, (DWORD)(-1), Result);
}
return TRUE;
}
}
BOOL ParseCacheControlHeader(SSDP_REQUEST *Result)
{
if (Result->Headers[SSDP_CACHECONTROL])
{
// Further parse the cache-control header
//
CHAR * szValue = strstr(Result->Headers[SSDP_CACHECONTROL], c_szMaxAge);
if (szValue)
{
CHAR * szTemp = szValue + c_cchMaxAge;
strtrim(&szTemp);
if (*szTemp != '=')
{
TraceTag(ttidSsdpParser, "Invalid max-age directive"
" in cache-control header.");
return FALSE;
}
else
{
szTemp++;
memcpy(Result->Headers[SSDP_CACHECONTROL], c_szMaxAge, c_cchMaxAge);
Result->Headers[SSDP_CACHECONTROL][c_cchMaxAge] = '=';
strtrim(&szTemp);
szValue = szTemp;
while (isdigit(*szTemp))
{
szTemp++;
}
if (szTemp == szValue)
{
// no digits found
TraceTag(ttidSsdpParser, "Invalid max-age directive"
" in cache-control header.");
return FALSE;
}
memcpy(Result->Headers[SSDP_CACHECONTROL] + c_cchMaxAge+1,
szValue, (size_t)(szTemp - szValue));
// Nul term the string so the cache-control
// header should now be "max-age=398733" and
// nothing more or less
size_t cch = c_cchMaxAge + 1 + (szTemp - szValue);
Result->Headers[SSDP_CACHECONTROL][cch] = 0;
}
}
else
{
TraceTag(ttidSsdpParser, "Cache-control header"
"did not include max-age directive.");
return TRUE;
}
}
return TRUE;
}
char * ParseHeaders(CHAR *szMessage, SSDP_REQUEST *Result)
{
CHAR *token;
INT i = NUM_OF_HEADERS;
INT iPrevHeader = NUM_OF_HEADERS;
Assert(NULL != szMessage);
if (NULL == strstr(szMessage, szHeaderEnd))
{
TraceTag(ttidSsdpParser, "Invalid header - does not end with double CRLF");
return NULL;
}
// Get the next header
token = strtok(szMessage, "\r\n");
while (token != NULL)
{
CHAR * pHeaderSep; // points to the ':' that seperates the header and its content.
CHAR * pHeaderCopy;
CHAR * pBeyondTokenEnd;
pBeyondTokenEnd = token + strlen(token) + 1;
if ((*token == ' ' || *token == '\t') && iPrevHeader != NUM_OF_HEADERS)
{
// this is a continuation of the last line
strtrim(&token);
if (Result->Headers[iPrevHeader])
{
// we already have this header, so add this data to it.
CHAR* szTemp = (CHAR*)midl_user_allocate(
sizeof(CHAR) * (strlen(token) + strlen(Result->Headers[iPrevHeader]) + 3));
if (szTemp)
{
strcpy(szTemp, Result->Headers[iPrevHeader]);
strcat(szTemp, " ");
strcat(szTemp, token);
midl_user_free(Result->Headers[iPrevHeader]);
Result->Headers[iPrevHeader] = szTemp;
}
else
{
TraceTag(ttidSsdpParser, "Failed to allocate memory "
"for token %s", token);
FreeSsdpRequest(Result);
return NULL;
}
}
else
{
TraceTag(ttidSsdpParser, "Memory not already allocated for header %s",
SsdpHeaderStr[iPrevHeader]);
FreeSsdpRequest(Result);
return NULL;
}
}
else
{
pHeaderSep = strchr( token, ':' );
if (pHeaderSep == NULL)
{
TraceTag(ttidSsdpParser, "Token %s does not have a ':', ignored.",
token);
}
else
{
*pHeaderSep = '\0';
strtrim(&token);
// determine which header we are looking at
for (i = 0; i < NUM_OF_HEADERS; i++)
{
if (_stricmp(SsdpHeaderStr[i],token) == 0)
break;
}
if (i < NUM_OF_HEADERS)
{
CHAR *szValue;
szValue = pHeaderSep + 1;
strtrim(&szValue);
if (Result->Headers[i])
{
// we already have this header, so add this data to it.
CHAR* szTemp = (CHAR*)midl_user_allocate(
sizeof(CHAR) * (strlen(szValue) + strlen(Result->Headers[i]) + 3));
if (szTemp)
{
strcpy(szTemp, Result->Headers[i]);
strcat(szTemp, ", ");
strcat(szTemp, szValue);
midl_user_free(Result->Headers[i]);
Result->Headers[i] = szTemp;
}
else
{
midl_user_free(Result->Headers[i]);
Result->Headers[i] = NULL;
}
}
else
{
Result->Headers[i] = (CHAR *) midl_user_allocate(
sizeof(CHAR) * (strlen(szValue) + 1));
if (Result->Headers[i])
{
strcpy(Result->Headers[i], szValue);
}
}
if (Result->Headers[i] == NULL)
{
TraceTag(ttidSsdpParser, "Failed to allocate memory "
"for szValue %s",szValue);
FreeSsdpRequest(Result);
return NULL;
}
}
else
{
// Ignore not recognized header
TraceTag(ttidSsdpParser, "Token %s does not match any SSDP "
"headers",token);
}
}
}
// Get the next header
if (!strncmp(pBeyondTokenEnd, szEndOfHeaders, END_HEADERS_SIZE))
{
// no more headers. parse what we have
if (!ParseCacheControlHeader(Result))
{
FreeSsdpRequest(Result);
return NULL;
}
return (pBeyondTokenEnd + END_HEADERS_SIZE);
}
else
{
token = strtok(NULL, "\r\n");
iPrevHeader = i;
}
}
// We should always have a "\r\n\r\n" in any legal message.
TraceTag(ttidSsdpParser, "Received message does not contain \\r\\n\\r\\n. Ignored. ");
FreeSsdpRequest(Result);
return NULL;
}
BOOL InitializeSsdpRequest(SSDP_REQUEST *pRequest)
{
INT i;
pRequest->Method = SSDP_INVALID;
pRequest->RequestUri = NULL;
for (i = 0; i < NUM_OF_HEADERS; i++)
{
pRequest->Headers[i] = NULL;
}
pRequest->ContentType = NULL;
pRequest->Content = NULL;
return TRUE;
}
VOID FreeSsdpRequest(SSDP_REQUEST *pSsdpRequest)
{
INT i = 0;
if (pSsdpRequest->Content != NULL)
{
midl_user_free(pSsdpRequest->Content);
pSsdpRequest->Content = NULL;
}
if (pSsdpRequest->ContentType != NULL)
{
midl_user_free(pSsdpRequest->ContentType);
pSsdpRequest->ContentType = NULL;
}
if (pSsdpRequest->RequestUri != NULL)
{
midl_user_free(pSsdpRequest->RequestUri);
pSsdpRequest->RequestUri = NULL;
}
for (i = 0; i < NUM_OF_HEADERS; i++)
{
if (pSsdpRequest->Headers[i] != NULL)
{
midl_user_free(pSsdpRequest->Headers[i]);
pSsdpRequest->Headers[i] = NULL;
}
}
}
// Get rid of leading or trailing white space or tab.
VOID PrintSsdpRequest(const SSDP_REQUEST *pssdpRequest)
{
INT i;
for (i = 0; i < NUM_OF_HEADERS; i++)
{
if (pssdpRequest->Headers[i] == NULL)
{
TraceTag(ttidSsdpParser, "%s = (NULL) ",SsdpHeaderStr[i],
pssdpRequest->Headers[i]);
}
else
{
TraceTag(ttidSsdpParser, "%s = (%s) ",SsdpHeaderStr[i],
pssdpRequest->Headers[i]);
}
}
}
// Assume szValue does not have beginning or trailing spaces.
INT GetMaxAgeFromCacheControl(const CHAR *szValue)
{
CHAR * pEqual;
_int64 Temp;
if (szValue == NULL)
{
return -1;
}
pEqual = strchr(szValue, '=');
if (pEqual == NULL)
{
return -1;
}
Temp = _atoi64(pEqual+1);
if (Temp > UINT_MAX / 1000)
{
TraceTag(ttidSsdpAnnounce, "Life time exceeded the UINT limit. "
"Set to limit");
Temp = UINT_MAX;
}
return (UINT)Temp;
}
BOOL ConvertToByebyeNotify(PSSDP_REQUEST pSsdpRequest)
{
CHAR * szTemp = "ssdp:byebye";
free(pSsdpRequest->Headers[SSDP_NTS]);
pSsdpRequest->Headers[SSDP_NTS] = NULL;
pSsdpRequest->Headers[SSDP_NTS] = SzaDupSza(szTemp);
if (pSsdpRequest->Headers[SSDP_NTS] == NULL)
{
return FALSE;
}
else
{
return TRUE;
}
}
BOOL ConvertToAliveNotify(PSSDP_REQUEST pSsdpRequest)
{
CHAR * szTemp = "ssdp:alive";
Assert(pSsdpRequest->Headers[SSDP_ST] != NULL);
Assert(pSsdpRequest->Headers[SSDP_NTS] == NULL);
pSsdpRequest->Headers[SSDP_NTS] = SzaDupSza(szTemp);
if (pSsdpRequest->Headers[SSDP_NTS] == NULL)
{
return FALSE;
}
else
{
pSsdpRequest->Headers[SSDP_NT] = pSsdpRequest->Headers[SSDP_ST];
pSsdpRequest->Headers[SSDP_ST] = NULL;
return TRUE;
}
}
BOOL CompareSsdpRequest(const SSDP_REQUEST * pRequestA, const SSDP_REQUEST * pRequestB)
{
INT i;
for (i = 0; i < NUM_OF_HEADERS; i++)
{
if ((pRequestA->Headers[i] == NULL &&
pRequestB->Headers[i]!= NULL) ||
(pRequestA->Headers[i] != NULL &&
pRequestB->Headers[i]== NULL))
{
return FALSE;
}
else if (pRequestA->Headers[i] != NULL &&
pRequestB->Headers[i] != NULL)
{
if (strcmp(pRequestA->Headers[i], pRequestB->Headers[i]) != 0)
{
TraceTag(ttidSsdpParser, "Different headers index %d",i);
return FALSE;
}
}
}
Assert(pRequestA->Content == NULL && pRequestB->Content == NULL);
Assert(pRequestA->ContentType == NULL && pRequestB->ContentType == NULL);
// We ignore Request URI, as they should always be * for alive and byebye.
return TRUE;
}
// Deep copy
BOOL CopySsdpRequest(PSSDP_REQUEST Destination, const SSDP_REQUEST * Source)
{
INT i;
ZeroMemory(Destination, sizeof(*Destination));
Destination->Method = Source->Method ;
for (i = 0; i < NUM_OF_HEADERS; i++)
{
if (Source->Headers[i] != NULL)
{
Destination->Headers[i] = (CHAR *) midl_user_allocate(
strlen(Source->Headers[i]) + 1);
if (Destination->Headers[i] == NULL)
{
goto cleanup;
}
else
{
strcpy(Destination->Headers[i], Source->Headers[i]);
}
}
}
if (Source->Content != NULL)
{
Destination->Content = (CHAR *) midl_user_allocate(
strlen(Source->Content) + 1);
if (Destination->Content == NULL)
{
goto cleanup;
}
else
{
strcpy(Destination->Content, Source->Content);
}
}
if (Source->ContentType != NULL)
{
Destination->ContentType = (CHAR *) midl_user_allocate(
strlen(Source->ContentType) + 1);
if (Destination->ContentType == NULL)
{
goto cleanup;
}
else
{
strcpy(Destination->ContentType, Source->ContentType);
}
}
if (Source->RequestUri != NULL)
{
Destination->RequestUri = (CHAR *) midl_user_allocate(
strlen(Source->RequestUri) + 1);
if (Destination->RequestUri == NULL)
{
goto cleanup;
}
else
{
strcpy(Destination->RequestUri, Source->RequestUri);
}
}
Destination->guidInterface = Source->guidInterface;
return TRUE;
cleanup:
FreeSsdpRequest(Destination);
return FALSE;
}
BOOL IsStrDigits(LPSTR pszStr)
{
int i = 0;
while (pszStr[i] != '\0')
{
if (isdigit(pszStr[i++]) == 0)
{
return FALSE;
}
}
return TRUE;
}
VOID strtrim(CHAR ** pszStr)
{
CHAR *end;
CHAR *begin;
// Empty string. Nothing to do.
//
if (!(**pszStr))
{
return;
}
begin = *pszStr;
end = begin + strlen(*pszStr) - 1;
while (*begin == ' ' || *begin == '\t')
{
begin++;
}
*pszStr = begin;
while ((end > begin) && (*end == ' ' || *end == '\t'))
{
end--;
}
*(end+1) = '\0';
}
CHAR* IsHeadersComplete(const CHAR *szHeaders)
{
return strstr(szHeaders, szHeaderEnd);
}