|
|
//+---------------------------------------------------------------------------
//
// 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: [email protected]
//
//----------------------------------------------------------------------------
#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);
}
|