613 lines
17 KiB
C++
613 lines
17 KiB
C++
//+---------------------------------------------------------------------------
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1999.
|
|
//
|
|
// File: E V E N T S R V . C P P
|
|
//
|
|
// Contents: UPnP GENA server.
|
|
//
|
|
// Notes:
|
|
//
|
|
// Author: Ting Cai Dec. 1999
|
|
//
|
|
// Email: tingcai@microsoft.com
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include <pch.h>
|
|
#pragma hdrstop
|
|
|
|
#include <winsock2.h>
|
|
#include "wininet.h"
|
|
#include "eventsrv.h"
|
|
#include "ssdpfunc.h"
|
|
#include "ssdptypes.h"
|
|
#include "ssdpnetwork.h"
|
|
#include "ncbase.h"
|
|
#define LISTEN_BACKLOG 5
|
|
|
|
|
|
VOID ProcessSsdpRequest(PSSDP_REQUEST pSsdpRequest, RECEIVE_DATA *pData);
|
|
|
|
static LIST_ENTRY g_listOpenConn;
|
|
static CRITICAL_SECTION g_csListOpenConn;
|
|
|
|
static int g_cOpenConnections;
|
|
static long g_cMaxOpenConnections = 150;
|
|
|
|
static const long c_cMaxOpenDefault = 150; // default maximum
|
|
static const long c_cMaxOpenMin = 5; // absolute minimum
|
|
static const long c_cMaxOpenMax = 1500; // absolute maximum
|
|
|
|
static long cQueuedAccepts = 0;
|
|
|
|
|
|
static SOCKET HttpSocket;
|
|
LONG bCreated = 0;
|
|
|
|
VOID InitializeListOpenConn()
|
|
{
|
|
|
|
HKEY hkey;
|
|
DWORD dwMaxConns = c_cMaxOpenDefault;
|
|
|
|
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
"SYSTEM\\CurrentControlSet\\Services"
|
|
"\\SSDPSRV\\Parameters", 0,
|
|
KEY_READ, &hkey))
|
|
{
|
|
DWORD cbSize = sizeof(DWORD);
|
|
|
|
// ignore failure. In that case, we'll use default
|
|
(VOID) RegQueryValueEx(hkey, "MaxEventConnects", NULL, NULL, (BYTE *)&dwMaxConns, &cbSize);
|
|
|
|
RegCloseKey(hkey);
|
|
}
|
|
|
|
dwMaxConns = max(dwMaxConns, c_cMaxOpenMin);
|
|
dwMaxConns = min(dwMaxConns, c_cMaxOpenMax);
|
|
g_cMaxOpenConnections = dwMaxConns;
|
|
|
|
InitializeCriticalSection(&g_csListOpenConn);
|
|
EnterCriticalSection(&g_csListOpenConn);
|
|
InitializeListHead(&g_listOpenConn);
|
|
g_cOpenConnections = 0;
|
|
LeaveCriticalSection(&g_csListOpenConn);
|
|
|
|
TraceTag(ttidEventServer, "Initializing Max Connections %d ", g_cOpenConnections);
|
|
}
|
|
|
|
VOID FreeOpenConnection(POPEN_TCP_CONN pOpenConn)
|
|
{
|
|
Assert(OPEN_TCP_CONN_SIGNATURE == (pOpenConn->iType));
|
|
|
|
FreeSsdpRequest(&pOpenConn->ssdpRequest);
|
|
free(pOpenConn->szData);
|
|
pOpenConn->szData = NULL;
|
|
pOpenConn->state = CONNECTION_INIT;
|
|
pOpenConn->cbData = 0;
|
|
pOpenConn->cbHeaders = 0;
|
|
}
|
|
|
|
VOID CloseOpenConnection(SOCKET socketPeer)
|
|
{
|
|
closesocket(socketPeer);
|
|
RemoveOpenConn(socketPeer);
|
|
|
|
}
|
|
POPEN_TCP_CONN CreateOpenConnection(SOCKET socketPeer)
|
|
{
|
|
POPEN_TCP_CONN pOpenConn = (POPEN_TCP_CONN) malloc(sizeof(OPEN_TCP_CONN));
|
|
|
|
if (pOpenConn == NULL)
|
|
{
|
|
TraceTag(ttidEventServer, "Couldn't allocate memory for %d", socketPeer);
|
|
return NULL;
|
|
}
|
|
pOpenConn->iType = OPEN_TCP_CONN_SIGNATURE;
|
|
pOpenConn->socketPeer = socketPeer;
|
|
pOpenConn->szData = NULL;
|
|
pOpenConn->state = CONNECTION_INIT;
|
|
pOpenConn->cbData = 0;
|
|
pOpenConn->cbHeaders = 0;
|
|
|
|
InitializeSsdpRequest(&pOpenConn->ssdpRequest);
|
|
|
|
return pOpenConn;
|
|
}
|
|
|
|
VOID AddToListOpenConn(POPEN_TCP_CONN pOpenConn)
|
|
{
|
|
EnterCriticalSection(&g_csListOpenConn);
|
|
InsertHeadList(&g_listOpenConn, &(pOpenConn->linkage));
|
|
g_cOpenConnections++;
|
|
LeaveCriticalSection(&g_csListOpenConn);
|
|
TraceTag(ttidEventServer, "AddToListOpenConn - Connections %d ", g_cOpenConnections);
|
|
}
|
|
|
|
VOID CleanupListOpenConn()
|
|
{
|
|
PLIST_ENTRY p;
|
|
PLIST_ENTRY pListHead = &g_listOpenConn;
|
|
|
|
TraceTag(ttidEventServer, "----- Cleanup Open Connection List -----");
|
|
|
|
EnterCriticalSection(&g_csListOpenConn);
|
|
for (p = pListHead->Flink; p != pListHead;)
|
|
{
|
|
|
|
POPEN_TCP_CONN pOpenConn;
|
|
|
|
pOpenConn = CONTAINING_RECORD (p, OPEN_TCP_CONN, linkage);
|
|
|
|
p = p->Flink;
|
|
|
|
TraceTag(ttidEventServer, "Removing Open Conn %x -----", pOpenConn);
|
|
|
|
RemoveEntryList(&(pOpenConn->linkage));
|
|
g_cOpenConnections--;
|
|
TraceTag(ttidEventServer, "CleanupListOpenConn - Connections %d ", g_cOpenConnections);
|
|
closesocket(pOpenConn->socketPeer);
|
|
|
|
FreeOpenConnection(pOpenConn);
|
|
|
|
// just to be sure we don't use this again
|
|
pOpenConn->iType = -1;
|
|
|
|
free(pOpenConn);
|
|
}
|
|
|
|
LeaveCriticalSection(&g_csListOpenConn);
|
|
DeleteCriticalSection(&g_csListOpenConn);
|
|
|
|
TraceTag(ttidEventServer, "----- Finished Cleanup Open Connection List -----");
|
|
}
|
|
|
|
// Pre-condition: WSAStartup was successful.
|
|
// Post-Condtion: HttpSocket is created. GetNetworks can proceed.
|
|
|
|
SOCKET CreateHttpSocket()
|
|
{
|
|
SOCKADDR_IN sockaddrLocal;
|
|
int iRet;
|
|
|
|
HttpSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
if (HttpSocket == INVALID_SOCKET)
|
|
{
|
|
TraceTag(ttidEventServer, "Failed to create http socket. Error code (%d).", GetLastError());
|
|
return INVALID_SOCKET;
|
|
}
|
|
|
|
// Bind
|
|
|
|
sockaddrLocal.sin_family = AF_INET;
|
|
sockaddrLocal.sin_addr.s_addr = INADDR_ANY;
|
|
sockaddrLocal.sin_port = htons(EVENT_PORT);
|
|
|
|
iRet = bind(HttpSocket, (struct sockaddr *)&sockaddrLocal, sizeof(sockaddrLocal));
|
|
if (iRet == SOCKET_ERROR)
|
|
{
|
|
TraceTag(ttidEventServer, "Failed to bind http socket. Error code (%d).", GetLastError());
|
|
closesocket(HttpSocket);
|
|
HttpSocket = INVALID_SOCKET;
|
|
return INVALID_SOCKET;
|
|
}
|
|
|
|
InterlockedIncrement(&bCreated);
|
|
|
|
return HttpSocket;
|
|
}
|
|
|
|
INT StartHttpServer(SOCKET HttpSocket, HWND hWnd, u_int wMsg)
|
|
{
|
|
INT iRet;
|
|
|
|
iRet = listen(HttpSocket, LISTEN_BACKLOG);
|
|
if (iRet == SOCKET_ERROR)
|
|
{
|
|
iRet = GetLastError();
|
|
closesocket(HttpSocket);
|
|
HttpSocket = INVALID_SOCKET;
|
|
TraceTag(ttidEventServer, "Failed to listen on http socket. Error code (%d).", iRet);
|
|
return iRet;
|
|
}
|
|
|
|
iRet = WSAAsyncSelect(HttpSocket, hWnd, wMsg, FD_ACCEPT | FD_CONNECT | FD_READ | FD_CLOSE);
|
|
|
|
if (iRet == SOCKET_ERROR)
|
|
{
|
|
iRet = GetLastError();
|
|
closesocket(HttpSocket);
|
|
HttpSocket = INVALID_SOCKET;
|
|
TraceTag(ttidEventServer, "----- select failed with error code %d -----", iRet);
|
|
return iRet;
|
|
}
|
|
else
|
|
{
|
|
TraceTag(ttidEventServer, "Ready to accept tcp connections.");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
VOID CleanupHttpSocket()
|
|
{
|
|
if (InterlockedExchange(&bCreated, bCreated) != 0)
|
|
{
|
|
if (HttpSocket != INVALID_SOCKET)
|
|
{
|
|
closesocket(HttpSocket);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID DoAccept(SOCKET socket)
|
|
{
|
|
SOCKADDR_IN sockaddrFrom;
|
|
SOCKET socketPeer;
|
|
int iLen;
|
|
POPEN_TCP_CONN pOpenTcpConn;
|
|
|
|
iLen = sizeof(SOCKADDR_IN);
|
|
|
|
Assert(socket == HttpSocket);
|
|
|
|
// AcceptEx
|
|
socketPeer = accept(socket, (LPSOCKADDR)&sockaddrFrom, &iLen);
|
|
TraceTag(ttidEventServer, "DoAccept - Before Adding to List Connections %d ", g_cOpenConnections);
|
|
if (socketPeer == SOCKET_ERROR)
|
|
{
|
|
TraceTag(ttidEventServer, "----- accept failed with error code %d -----", GetLastError());
|
|
return;
|
|
}
|
|
|
|
pOpenTcpConn = CreateOpenConnection(socketPeer);
|
|
|
|
if (pOpenTcpConn)
|
|
{
|
|
AddToListOpenConn(pOpenTcpConn);
|
|
}
|
|
else
|
|
{
|
|
TraceError("Couldn't add new connection. Out of memory!",
|
|
E_OUTOFMEMORY);
|
|
}
|
|
}
|
|
|
|
VOID DelayAccept()
|
|
{
|
|
InterlockedIncrement(&cQueuedAccepts);
|
|
TraceTag(ttidEventServer, "----- DelayAccept %d -----", cQueuedAccepts);
|
|
}
|
|
VOID DoDelayedAccept()
|
|
{
|
|
InterlockedDecrement(&cQueuedAccepts);
|
|
TraceTag(ttidEventServer, "----- DoDelayedAccept %d -----", cQueuedAccepts);
|
|
|
|
DoAccept(HttpSocket);
|
|
}
|
|
|
|
|
|
VOID HandleAccept(SOCKET socket)
|
|
{
|
|
if ((g_cOpenConnections > g_cMaxOpenConnections) && (socket == HttpSocket))
|
|
{
|
|
DelayAccept();
|
|
}
|
|
else
|
|
{
|
|
DoAccept(socket);
|
|
}
|
|
}
|
|
|
|
|
|
// Pre-Condition:
|
|
// The cs for open connection list is held
|
|
|
|
BOOL FProcessTcpReceiveBuffer(POPEN_TCP_CONN pOpenConn, RECEIVE_DATA *pData)
|
|
{
|
|
Assert(pOpenConn);
|
|
Assert(pData);
|
|
Assert(pData->szBuffer);
|
|
Assert(OPEN_TCP_CONN_SIGNATURE == (pOpenConn->iType));
|
|
|
|
int iLen = 1;
|
|
CHAR *szBuf = NULL;
|
|
CHAR *pCurrent;
|
|
CHAR *szHeaders;
|
|
BOOL fNeedToLeave = TRUE;
|
|
|
|
TraceTag(ttidEventServer, "Partying on pOpenConn 0x%08X, pData->szBuffer='%s'", pOpenConn, pData->szBuffer);
|
|
|
|
|
|
if ( pOpenConn->cbData > MAX_EVENT_BUF_THROTTLE_SIZE ) {
|
|
|
|
pOpenConn->state = CONNECTION_ERROR_FORCED_CLOSE;
|
|
|
|
SocketSendErrorResponse(pData->socket, HTTP_STATUS_BAD_REQUEST);
|
|
// Gracefully shutdown. Open Conn will be removed in FD_CLOSe
|
|
shutdown(pData->socket, SD_SEND);
|
|
TraceTag(ttidEventServer, "FProcessTcpReceiveBuffer - Exceeds MAX_EVENT_BUF_THROTTLE_SIZE");
|
|
|
|
// Try to tear down the connection..
|
|
}
|
|
else {
|
|
// Accumulate data
|
|
iLen += strlen(pData->szBuffer);
|
|
|
|
if (pOpenConn->szData != NULL)
|
|
{
|
|
iLen += strlen(pOpenConn->szData);
|
|
}
|
|
|
|
szBuf = (CHAR *) malloc(iLen * sizeof(CHAR));
|
|
|
|
if (!szBuf)
|
|
{
|
|
TraceError("FProcessTcpReceiveBuffer", E_OUTOFMEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
szBuf[0] = '\0';
|
|
|
|
if (pOpenConn->szData)
|
|
{
|
|
strcpy(szBuf, pOpenConn->szData);
|
|
free(pOpenConn->szData);
|
|
}
|
|
strcat(szBuf, pData->szBuffer);
|
|
|
|
pOpenConn->cbData += pData->cbBuffer;
|
|
|
|
pOpenConn->szData = szBuf;
|
|
|
|
}
|
|
TraceTag(ttidEventServer, "FProcessTcpReceiveBuffer - Buff Recv %d",pOpenConn->cbData);
|
|
switch (pOpenConn->state)
|
|
{
|
|
case CONNECTION_INIT:
|
|
pCurrent = IsHeadersComplete(pOpenConn->szData);
|
|
if((pCurrent == NULL) && pOpenConn->cbData > MAX_EVENT_NOTIFY_HEADER_THROTTLE_SIZE )
|
|
{
|
|
pOpenConn->state = CONNECTION_ERROR_FORCED_CLOSE;
|
|
|
|
SocketSendErrorResponse(pData->socket, HTTP_STATUS_BAD_REQUEST);
|
|
// Gracefully shutdown. Open Conn will be removed in FD_CLOSe
|
|
shutdown(pData->socket, SD_SEND);
|
|
TraceTag(ttidEventServer, "FProcessTcpReceiveBuffer - Exceeds MAX_EVENT_NOTIFY_HEADER_THROTTLE_SIZE");
|
|
|
|
// Try to tear down the connection..
|
|
}
|
|
if ( pCurrent != NULL)
|
|
{
|
|
pOpenConn->cbHeaders = (DWORD)(pCurrent - (pOpenConn->szData)) + 4;
|
|
|
|
szHeaders = ParseRequestLine(pOpenConn->szData, &(pOpenConn->ssdpRequest));
|
|
if ((szHeaders != NULL) && ( pOpenConn->ssdpRequest.Method == SSDP_NOTIFY ))
|
|
{
|
|
CHAR *szContent;
|
|
|
|
szContent = ParseHeaders(szHeaders, &(pOpenConn->ssdpRequest));
|
|
if (szContent == NULL)
|
|
{
|
|
TraceTag(ttidEventServer, "ParseHeaders returned NULL for socket %d",
|
|
pOpenConn->socketPeer);
|
|
|
|
// We've reached a terminal error. Since there might be
|
|
// other received data for this connection already in the
|
|
// queue, transition to this error state, so that we know
|
|
// not to process any more data.
|
|
pOpenConn->state = CONNECTION_ERROR_CLOSING;
|
|
|
|
FreeOpenConnection(pOpenConn);
|
|
|
|
SocketSendErrorResponse(pData->socket, HTTP_STATUS_BAD_REQUEST);
|
|
// Gracefully shutdown. Open Conn will be removed in FD_CLOSe
|
|
shutdown(pData->socket, SD_SEND);
|
|
}
|
|
else
|
|
{
|
|
if (VerifySsdpHeaders(&(pOpenConn->ssdpRequest)) == FALSE)
|
|
{
|
|
TraceTag(ttidEventServer, "Verified headers returned false for %d",
|
|
pOpenConn->socketPeer);
|
|
|
|
pOpenConn->state = CONNECTION_ERROR_CLOSING;
|
|
|
|
SocketSendErrorResponse(pData->socket, HTTP_STATUS_BAD_REQUEST);
|
|
// Gracefully shutdown. Open Conn will be removed in FD_CLOSe
|
|
shutdown(pData->socket, SD_SEND);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// else
|
|
|
|
if (ParseContent(szContent,
|
|
(pOpenConn->cbData - pOpenConn->cbHeaders),
|
|
&(pOpenConn->ssdpRequest)) == TRUE)
|
|
{
|
|
PSSDP_REQUEST pRequest;
|
|
|
|
pRequest = (PSSDP_REQUEST) malloc(sizeof(SSDP_REQUEST));
|
|
|
|
if (pRequest != NULL &&
|
|
CopySsdpRequest(pRequest, &(pOpenConn->ssdpRequest)) != FALSE)
|
|
{
|
|
fNeedToLeave = FALSE;
|
|
FreeOpenConnection(pOpenConn);
|
|
LeaveCriticalSection(&g_csListOpenConn);
|
|
ProcessSsdpRequest(pRequest, pData);
|
|
free(pRequest);
|
|
}
|
|
else
|
|
{
|
|
FreeOpenConnection(pOpenConn);
|
|
if (pRequest)
|
|
{
|
|
FreeSsdpRequest(pRequest);
|
|
free(pRequest);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// HTTP request not complete
|
|
|
|
CHAR *szTemp;
|
|
|
|
szTemp = SzaDupSza(szContent);
|
|
free(pOpenConn->szData);
|
|
pOpenConn->szData = szTemp;
|
|
|
|
pOpenConn->state = CONNECTION_HEADERS_READY;
|
|
|
|
// Done for now, wait for more data
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pOpenConn->state = CONNECTION_ERROR_FORCED_CLOSE;
|
|
SocketSendErrorResponse(pData->socket, HTTP_STATUS_BAD_REQUEST);
|
|
|
|
// Gracefully shutdown. Open Conn will be removed in FD_CLOSe
|
|
shutdown(pData->socket, SD_SEND);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CONNECTION_HEADERS_READY:
|
|
if (ParseContent(pOpenConn->szData,
|
|
(pOpenConn->cbData - pOpenConn->cbHeaders),
|
|
&(pOpenConn->ssdpRequest)) == TRUE)
|
|
{
|
|
SSDP_REQUEST ssdpRequest;
|
|
|
|
if (CopySsdpRequest(&ssdpRequest, &(pOpenConn->ssdpRequest)) != FALSE)
|
|
{
|
|
fNeedToLeave = FALSE;
|
|
FreeOpenConnection(pOpenConn);
|
|
LeaveCriticalSection(&g_csListOpenConn);
|
|
ProcessSsdpRequest(&ssdpRequest, pData);
|
|
}
|
|
else
|
|
{
|
|
FreeOpenConnection(pOpenConn);
|
|
FreeSsdpRequest(&ssdpRequest);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TraceTag(ttidEventServer, "ParseContent failed!");
|
|
}
|
|
break;
|
|
|
|
case CONNECTION_ERROR_CLOSING:
|
|
// we've already failed to process this socket but it hasn't yet
|
|
// been closed. Don't do anything here.
|
|
//
|
|
TraceTag(ttidEventServer,
|
|
"FProcessTcpReceiveBuffer: "
|
|
"connection closing from error, ignoring pData->szBuffer");
|
|
break;
|
|
}
|
|
|
|
TraceTag(ttidEventServer, "Done partying on pOpenConn 0x%08X", pOpenConn);
|
|
|
|
return fNeedToLeave;
|
|
}
|
|
|
|
DWORD LookupListOpenConn(LPVOID pvData)
|
|
{
|
|
PLIST_ENTRY p;
|
|
PLIST_ENTRY pListHead = &g_listOpenConn;
|
|
BOOL fLeave = TRUE;
|
|
BOOL fCloseConn = FALSE;
|
|
RECEIVE_DATA * pData = (RECEIVE_DATA *)pvData;
|
|
|
|
Assert(pData);
|
|
|
|
TraceTag(ttidEventServer, "----- Search Open Connections List -----");
|
|
|
|
AssertSz(pData->szBuffer != NULL, "SocketReceive should have allocated the buffer");
|
|
|
|
EnterCriticalSection(&g_csListOpenConn);
|
|
for (p = pListHead->Flink; p != pListHead;)
|
|
{
|
|
POPEN_TCP_CONN pOpenConn;
|
|
|
|
pOpenConn = CONTAINING_RECORD (p, OPEN_TCP_CONN, linkage);
|
|
|
|
p = p->Flink;
|
|
|
|
if (pOpenConn->socketPeer == pData->socket)
|
|
{
|
|
fLeave = FProcessTcpReceiveBuffer(pOpenConn, pData);
|
|
fCloseConn = (pOpenConn->state == CONNECTION_ERROR_FORCED_CLOSE)?TRUE:FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fLeave)
|
|
{
|
|
LeaveCriticalSection(&g_csListOpenConn);
|
|
}
|
|
|
|
if(fCloseConn)
|
|
{
|
|
CloseOpenConnection(pData->socket);
|
|
}
|
|
|
|
|
|
free(pData->szBuffer);
|
|
free(pData);
|
|
|
|
return 0;
|
|
}
|
|
|
|
VOID RemoveOpenConn(SOCKET socket)
|
|
{
|
|
PLIST_ENTRY p;
|
|
PLIST_ENTRY pListHead = &g_listOpenConn;
|
|
int cFound = 0;
|
|
|
|
TraceTag(ttidEventServer, "----- Search Open Connections List to remove -----");
|
|
|
|
EnterCriticalSection(&g_csListOpenConn);
|
|
for (p = pListHead->Flink; p != pListHead;)
|
|
{
|
|
|
|
POPEN_TCP_CONN pOpenConn;
|
|
|
|
pOpenConn = CONTAINING_RECORD (p, OPEN_TCP_CONN, linkage);
|
|
|
|
p = p->Flink;
|
|
|
|
if (pOpenConn->socketPeer == socket)
|
|
{
|
|
RemoveEntryList(&pOpenConn->linkage);
|
|
g_cOpenConnections--;
|
|
|
|
FreeOpenConnection(pOpenConn);
|
|
free(pOpenConn);
|
|
|
|
cFound++;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&g_csListOpenConn);
|
|
TraceTag(ttidEventServer, "RemoveOpenConn - Found %d",cFound);
|
|
while (cFound > 0)
|
|
{
|
|
if (InterlockedExchange(&cQueuedAccepts, cQueuedAccepts) > 0)
|
|
{
|
|
DoDelayedAccept();
|
|
}
|
|
cFound--;
|
|
}
|
|
|
|
TraceTag(ttidEventServer, "RemoveOpenConn - Num of Connections %d",g_cOpenConnections);
|
|
|
|
}
|
|
|