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

1005 lines
25 KiB
C++

/*++
Copyright (c) 1999-2000 Microsoft Corporation
File Name:
server.c
Abstract:
This file contains code which implements rpc server start and stop.
Author: Ting Cai
Created: 07/10/1999
--*/
#include <pch.h>
#include "ssdp.h"
#include "status.h"
#include "ssdpfunc.h"
#include "ssdptypes.h"
#include "ssdpnetwork.h"
#include "ncbase.h"
#include "event.h"
#include "ncinet.h"
#include "eventsrv.h"
#include "iphlpapi.h"
#include "announce.h"
#include "notify.h"
#include "search.h"
#include "cache.h"
#include "ReceiveData.h"
#include "InterfaceList.h"
#include "ReceiveData.h"
#include "InterfaceHelper.h"
#define SSDP_MSG_MAX_THROTTLE_SIZE 16384
/*********************************************************************/
/* Global vars for debugging */
/*********************************************************************/
// To-Do: Auto-restart the ssdpsrv.exe.
// Updated through interlocked exchange
static LONG bRegisteredIf = 0;
// static long bRegisteredEp = 0;
LONG bShutdown = 0;
HANDLE ShutDownEvent = NULL;
HWND hWnd = NULL;
SOCKET g_socketTcp;
HANDLE g_hAddrChange = NULL;
OVERLAPPED g_ovAddrChange = {0};
HANDLE g_hEventAddrChange = NULL;
HANDLE g_hAddrChangeWait = NULL;
static const CHAR c_szWindowClassName[] = "SSDP Server Window";
HWND SsdpCreateWindow();
LRESULT CALLBACK SsdpWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
VOID RunMessageLoop();
VOID NTAPI InterfaceChange(PVOID pvContext, BOOLEAN fFlag)
{
DWORD dwStatus;
HWND hwnd = (HWND)pvContext;
TraceTag(ttidSsdpNetwork, "InterfaceChange called!!!!!!!");
ResetNetworkList(hwnd);
dwStatus = NotifyAddrChange(&g_hAddrChange, &g_ovAddrChange);
if (dwStatus != ERROR_SUCCESS && dwStatus != ERROR_IO_PENDING)
{
TraceTag(ttidSsdpNetwork, "NotifyAddrChange returned %d",
dwStatus);
}
}
BOOL FRegisterAddrChange(HWND hwnd)
{
DWORD dwStatus;
BOOL fResult = FALSE;
TraceTag(ttidSsdpNetwork, "RegisterAddrChange() entered...");
g_hEventAddrChange = CreateEvent(NULL, FALSE, FALSE, NULL);
if (g_hEventAddrChange)
{
if (RegisterWaitForSingleObject(&g_hAddrChangeWait, g_hEventAddrChange,
InterfaceChange, (LPVOID)hwnd, INFINITE, 0))
{
TraceTag(ttidSsdpNetwork, "RegisterWaitForSingleObject() "
"succeeded...");
g_ovAddrChange.hEvent = g_hEventAddrChange;
dwStatus = NotifyAddrChange(&g_hAddrChange, &g_ovAddrChange);
if (dwStatus != ERROR_SUCCESS && dwStatus != ERROR_IO_PENDING)
{
TraceTag(ttidSsdpNetwork, "NotifyAddrChange returned %d",
dwStatus);
}
else
{
fResult = TRUE;
TraceTag(ttidSsdpNetwork, "NotifyAddrChange succeeded",
dwStatus);
}
}
}
return fResult;
}
VOID UnregisterAddrChange()
{
if (g_hAddrChangeWait)
{
UnregisterWait(g_hAddrChangeWait);
}
if (g_hEventAddrChange)
{
CloseHandle(g_hEventAddrChange);
}
}
INT SsdpMain(SERVICE_STATUS_HANDLE ssHandle, LPSERVICE_STATUS pStatus)
{
TraceTag(ttidSsdpRpcIf, "SsdpMain - Enter");
unsigned long hThread;
#ifdef DBG
InitializeDebugging();
#endif
// Initialize data structures
HRESULT hr = S_OK;
InitializeListNetwork();
InitializeListOpenConn();
hr = CTimerQueue::Instance().HrInitialize();
if(FAILED(hr))
{
TraceHr(ttidSsdpRpcIf, FAL, hr, FALSE, "SsdpMain - CTimerQueue::Instance().HrInitialize failed");
goto cleanup;
}
hr = CInterfaceHelper::Instance().HrInitialize();
if(FAILED(hr))
{
goto cleanup;
}
hr = CUPnPInterfaceList::Instance().HrInitialize();
if(FAILED(hr))
{
goto cleanup;
TraceHr(ttidSsdpRpcIf, FAL, hr, FALSE, "SsdpMain - CUPnPInterfaceList::Instance().HrInitialize failed");
}
// SSDP socket initialization
if (SocketInit() != 0)
{
TraceTag(ttidError, "SsdpMain - SocketInit failed");
goto cleanup;
}
g_socketTcp = CreateHttpSocket();
if (g_socketTcp == INVALID_SOCKET)
{
TraceTag(ttidError, "SsdpMain - CreateHttpSocket failed");
// Should we continue without eventing?
goto cleanup;
}
if (RpcServerStart() != 0)
{
TraceTag(ttidError, "SsdpMain - RpcServerStart failed");
goto cleanup;
}
hr = CReceiveDataManager::Instance().HrInitialize();
if(FAILED(hr))
{
TraceHr(ttidSsdpRpcIf, FAL, hr, FALSE, "SsdpMain - CReceiveDataManager::Instance().HrInitialize failed");
RpcServerStop();
goto cleanup;
}
// Initializes Max Cache Entries
hr = CSsdpCacheEntryManager::Instance().HrInitialize();
if(FAILED(hr))
{
TraceHr(ttidSsdpRpcIf, FAL, hr, FALSE, "SsdpMain - CSsdpCacheEntryManager::Instance().HrInitialize failed");
RpcServerStop();
goto cleanup;
}
hWnd = SsdpCreateWindow();
if (hWnd == NULL)
{
TraceTag(ttidError, "SsdpMain - SsdpCreateWindow failed");
RpcServerStop();
goto cleanup;
}
if (ListenOnAllNetworks(hWnd) != 0)
{
TraceTag(ttidError, "SsdpMain - ListenOnAllNetworks failed");
RpcServerStop();
goto cleanup;
}
if (StartHttpServer(g_socketTcp, hWnd, SM_TCP) != 0)
{
TraceTag(ttidError, "SsdpMain - StartHttpServer failed");
RpcServerStop();
goto cleanup;
}
if (!FRegisterAddrChange(hWnd))
{
TraceTag(ttidError, "SsdpMain - FRegisterAddrChange failed");
RpcServerStop();
goto cleanup;
}
TraceTag(ttidSsdpRpcIf, "SsdpMain - Performed initialization");
pStatus->dwCurrentState = SERVICE_RUNNING;
if (SetServiceStatus(ssHandle, pStatus) == FALSE)
{
TraceTag(ttidError, "SsdpMain - SetServiceStatus failed");
RpcServerStop();
goto cleanup;
}
TraceTag(ttidSsdpRpcIf, "SSDPSRV service is now started");
RunMessageLoop();
TraceTag(ttidSsdpRpcIf, "SsdpMain - Doing shutdown");
RpcServerStop();
UnregisterAddrChange();
TraceTag(ttidSsdpRpcIf, "Waiting for the shut down event.");
if (ShutDownEvent)
{
WaitForSingleObject(ShutDownEvent,INFINITE);
}
TraceTag(ttidSsdpRpcIf, "Shut down event signaled.");
cleanup:
TraceTag(ttidSsdpRpcIf, "SsdpMain - Doing cleanup");
CReceiveDataManager::Instance().HrShutdown();
CleanupHttpSocket();
CleanupListOpenConn();
CSsdpCacheEntryManager::Instance().HrShutdown();
CTimerQueue::Instance().HrShutdown(INVALID_HANDLE_VALUE);
CUPnPInterfaceList::Instance().HrShutdown();
CInterfaceHelper::Instance().HrShutdown();
CleanupListNetwork(FALSE);
SocketFinish();
TraceTag(ttidSsdpRpcIf, "Finished shutdown cleanup.");
#ifdef DBG
// CloseLogFileHandle(fileLog);
UnInitializeDebugging();
#endif // DBG
if (ShutDownEvent)
{
CloseHandle(ShutDownEvent);
ShutDownEvent = NULL;
}
#ifdef NEVER
if (g_hInetSess)
{
InternetCloseHandle(g_hInetSess);
}
#endif
if (hWnd)
{
DestroyWindow(hWnd);
hWnd = NULL;
}
UnregisterClass(c_szWindowClassName, NULL);
return 0;
}
/*********************************************************************/
/* MIDL allocate and free */
/*********************************************************************/
VOID __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
return(malloc(len));
}
VOID __RPC_USER midl_user_free(VOID __RPC_FAR * ptr)
{
free(ptr);
}
BOOL
IsAuthenticatedUser()
{
BOOL fAuthenticated = FALSE;
DWORD dwSidSize = SECURITY_MAX_SID_SIZE;
SID* pSidAuthenticated = (SID*)midl_user_allocate(dwSidSize);
if (NULL == pSidAuthenticated)
{
goto Cleanup;
}
//
// create SID for the authenticated users
//
if (!CreateWellKnownSid(WinAuthenticatedUserSid,
NULL, // not a domain sid
pSidAuthenticated,
&dwSidSize))
{
// check the error for debug builds, normally we don't care about the error
// because all we can do is to fail the call :)
#ifdef DBG
HRESULT hr = (HRESULT)GetLastError();
#endif
goto Cleanup;
}
//
// check whether current client token has this sid
//
if (!CheckTokenMembership(NULL, //current token
pSidAuthenticated, // sid for the authenticated user
&fAuthenticated))
{
// check the error for debug builds, normally we don't care about the error
// because all we can do is to fail the call :)
#ifdef DBG
HRESULT hr = (HRESULT)GetLastError();
#endif
// just to be on the safe side (as we don't know that CheckTokenMembership
// does not modify fAuthenticated in case of error)
fAuthenticated = FALSE;
goto Cleanup;
}
Cleanup:
if (pSidAuthenticated)
midl_user_free(pSidAuthenticated);
return fAuthenticated;
}
BOOL
IsAnonymousUser()
{
BOOL fAnonymous = FALSE;
DWORD dwSidSize = SECURITY_MAX_SID_SIZE;
SID* pSidAnonymous= (SID*)midl_user_allocate(dwSidSize);
if (NULL == pSidAnonymous)
{
goto Cleanup;
}
//
// create SID for the authenticated users
//
if (!CreateWellKnownSid(WinAnonymousSid,
NULL, // not a domain sid
pSidAnonymous,
&dwSidSize))
{
// check the error for debug builds, normally we don't care about the error
// because all we can do is to fail the call :)
#ifdef DBG
HRESULT hr = (HRESULT)GetLastError();
#endif
goto Cleanup;
}
//
// check whether current client token has this sid
//
if (!CheckTokenMembership(NULL, //current token
pSidAnonymous, // sid for the authenticated user
&fAnonymous))
{
// check the error for debug builds, normally we don't care about the error
// because all we can do is to fail the call :)
#ifdef DBG
HRESULT hr = (HRESULT)GetLastError();
#endif
// just to be on the safe side (as we don't know that CheckTokenMembership
// does not modify fAnonymous in case of error)
fAnonymous = FALSE;
goto Cleanup;
}
Cleanup:
if (pSidAnonymous)
midl_user_free(pSidAnonymous);
return fAnonymous;
}
RPC_STATUS RPC_ENTRY
RpcSecurityCallback( RPC_IF_HANDLE* handle, void* pCtx)
{
RPC_STATUS status = RPC_S_OK;
UINT uiTransportType = 0;
ULONG ulAuthLevel = 0;
ULONG ulAuthSvc = 0;
BOOL fImpersonated = FALSE;
//
// check to make sure incoming call comes through LRPC
//
status = I_RpcBindingInqTransportType(NULL, // current caller
&uiTransportType);
if (status)
goto Cleanup;
if (TRANSPORT_TYPE_LPC != uiTransportType)
{
status = RPC_S_ACCESS_DENIED;
goto Cleanup;
}
//
// retrieve client's authentication information
//
status = RpcBindingInqAuthClient(NULL, // current call
NULL, // don't care about authz handle
NULL, // don't need client' principal name, as he is local
&ulAuthLevel,
&ulAuthSvc,
NULL);
if (status)
goto Cleanup;
//
// we require packet privacy (encryption and signing). with lrpc
// it is set by default, but still, the check is simple
//
if (!(RPC_C_AUTHN_LEVEL_PKT_PRIVACY & ulAuthLevel))
{
status = RPC_S_ACCESS_DENIED;
goto Cleanup;
}
//
// client used some kind of authentication service
//
if (RPC_C_AUTHN_NONE == ulAuthSvc)
{
status = RPC_S_ACCESS_DENIED;
goto Cleanup;
}
//
// impersonate, in order to check caller's token
//
status = RpcImpersonateClient(NULL);
if (status)
goto Cleanup;
fImpersonated = TRUE;
//
// check whether this is an authenticated user (client)
//
if (!IsAuthenticatedUser())
{
status = RPC_S_ACCESS_DENIED;
goto Cleanup;
}
//
// do a separate check for the anonymous user
// (even though i am not sure whether it is applicable for LRPC)
//
if (IsAnonymousUser())
{
status = RPC_S_ACCESS_DENIED;
goto Cleanup;
}
Cleanup:
if (fImpersonated)
RpcRevertToSelf();
//
// this routine is expected to return either success (RPC_S_OK) or
// RPC_S_ACCESS_DENIED, so all other erros should be masked
//
if (status != RPC_S_OK)
status = RPC_S_ACCESS_DENIED;
return status;
}
INT RpcServerStart()
{
RPC_STATUS status = RPC_S_OK;
BOOL fRegisteredRpc = FALSE;
RPC_BINDING_VECTOR* pBindingVector = NULL;
//
// analyze all existing networks available.
//
status = GetNetworks();
if (status)
goto Error;
//
// create the event, to be used to synchronize shutdown
//
ShutDownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (ShutDownEvent == NULL)
{
TraceTag(ttidSsdpRpcInit, "Failed to create shut down event (%d)",
GetLastError());
goto Error;
}
//
// now, start the RPC interface
//
//
// Use LPC protocol sequence
//
status = RpcServerUseProtseq((unsigned char*)"ncalrpc",
RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
NULL);
if (status)
goto Error;
//
// use NTLM or kerberos
//
status = RpcServerRegisterAuthInfo(NULL,
RPC_C_AUTHN_GSS_NEGOTIATE,
NULL,
NULL);
if (status)
goto Error;
//
// register our interface,
// RPC_IF_ALLOW_SECURE_ONLY - to allow only users with authentication level higher than
// RPC_C_AUTHN_LEVEL_NONE, even though it is
// unnecessary, as it is superceeded by the security callback
// RPC_IF_AUTOLISTEN - start listening on the interface as soon as interface is registred and
// and stops listening as soon as interface is unregistered
//
status = RpcServerRegisterIfEx(_ssdpsrv_v1_0_s_ifspec,
NULL, // MgrTypeUuid
NULL, // MgrEpv; null means use default
RPC_IF_ALLOW_SECURE_ONLY | RPC_IF_AUTOLISTEN,
RPC_C_LISTEN_MAX_CALLS_DEFAULT,
(RPC_IF_CALLBACK_FN*)RpcSecurityCallback);
if (status)
goto Error;
//
// get the list of available bindings
//
status = RpcServerInqBindings(&pBindingVector);
if (status)
goto Error;
//
// register all the endpoints with the map database
//
status = RpcEpRegister(_ssdpsrv_v1_0_s_ifspec,
pBindingVector,
NULL,
NULL); // no annotation
if (status)
goto Error;
// if we reached this point, everything must have registered successfully
// so we should mark that
fRegisteredRpc = TRUE;
TraceTag(ttidSsdpRpcInit, "RPC server is started");
Cleanup:
if (pBindingVector)
RpcBindingVectorFree(&pBindingVector);
return status;
Error:
TraceTag(ttidSsdpRpcInit, "StartRpcServer failed (%x)", status);
//
// don't cleanup everything, as all this globl stuff, like ShutdownEvent
// is expected to be cleaned from the SsdpMain.
// Rpc is the only stuff that needs to be cleaned up
//
if (fRegisteredRpc)
{
RpcServerStop();
}
goto Cleanup;
}
// Unregister RPC interface, endpoint and close the file if necessary.
INT RpcServerStop()
{
RPC_STATUS status = RPC_S_OK;
status = RpcServerUnregisterIfEx(_ssdpsrv_v1_0_s_ifspec,
NULL, // MgrTypeUuid
1); // call rundown now
TraceTag(ttidSsdpRpcStop, "Leaving RpcServerStop");
return status;
}
VOID ProcessSsdpRequest(PSSDP_REQUEST pSsdpRequest, RECEIVE_DATA *pData)
{
// Ensure that the socket is in the network list before attempting to
// get its name
//
if (pData->fIsTcpSocket || FReferenceSocket(pData->socket))
{
sockaddr_in addr;
int nSize = sizeof(addr);
getsockname(pData->socket, reinterpret_cast<sockaddr*>(&addr), &nSize);
CInterfaceHelper::Instance().HrResolveAddress(addr.sin_addr.S_un.S_addr,
pSsdpRequest->guidInterface);
if (!pData->fIsTcpSocket)
{
UnreferenceSocket(pData->socket);
}
}
else
{
FreeSsdpRequest(pSsdpRequest);
return;
}
if (!pData->fIsTcpSocket && !pData->fMCast && pSsdpRequest->Method != SSDP_M_SEARCH)
{
FreeSsdpRequest(pSsdpRequest);
return;
}
if (pSsdpRequest->Method == SSDP_M_SEARCH)
{
if (0 == lstrcmpA(pSsdpRequest->RequestUri, "*"))
{
TraceTag(ttidSsdpSocket, "Searching for ST (%s)",
pSsdpRequest->Headers[SSDP_ST]);
CSsdpServiceManager::Instance().HrAddSearchResponse(pSsdpRequest,
&pData->socket,
&pData->RemoteSocket);
}
else
{
TraceTag(ttidSsdpSocket, "Not searching for ST, since URI != '*' (URI='%s')",
pSsdpRequest->RequestUri);
}
}
else if (pSsdpRequest->Method == SSDP_NOTIFY)
{
TraceTag(ttidSsdpSocket, "Receive notification of type (%s)",
pSsdpRequest->Headers[SSDP_NT]);
if (!lstrcmpi(pSsdpRequest->Headers[SSDP_NTS], "upnp:propchange"))
{
if(pSsdpRequest->Headers[GENA_SID])
{
TraceTag(ttidEvents, "ProcessSsdpRequest - upnp:propchange - SID:%s", pSsdpRequest->Headers[GENA_SID]);
}
CSsdpNotifyRequestManager::Instance().HrCheckListNotifyForEvent(pSsdpRequest);
if (pData->fIsTcpSocket || FReferenceSocket(pData->socket))
{
SocketSend(OKResponseHeader, pData->socket, NULL);
if (!pData->fIsTcpSocket)
{
UnreferenceSocket(pData->socket);
}
}
}
else if (!lstrcmpi(pSsdpRequest->Headers[SSDP_NTS], "ssdp:alive") ||
!lstrcmpi(pSsdpRequest->Headers[SSDP_NTS], "ssdp:byebye"))
{
BOOL IsSubscribed;
// preserve source address if possible.
// hack here where we use szSID to hold address
// this should not be used for alive normally.
if (pSsdpRequest->Headers[GENA_SID] == NULL)
{
char* pszIp = GetSourceAddress(pData->RemoteSocket);
pSsdpRequest->Headers[GENA_SID] = (CHAR *) midl_user_allocate(
sizeof(CHAR) * (strlen(pszIp) + 1));
if (pSsdpRequest->Headers[GENA_SID])
{
strcpy(pSsdpRequest->Headers[GENA_SID], pszIp);
}
}
// We only cache notification that clients has subscribed.
IsSubscribed = CSsdpNotifyRequestManager::Instance().FIsAliveOrByebyeInListNotify(pSsdpRequest);
CSsdpCacheEntryManager::Instance().HrUpdateCacheList(pSsdpRequest, IsSubscribed);
}
else
{
// unrecognized NTS type
}
// SsdpMessage fields are freed when clean up cache entry.
// FreeSsdpRequest(pSsdpRequest);
}
else
{
TraceTag(ttidSsdpSocket, "Unrecognized SSDP request.");
}
FreeSsdpRequest(pSsdpRequest);
}
VOID RunMessageLoop()
{
MSG msg;
while (GetMessage(&msg, 0, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
TraceTag(ttidSsdpRpcInit, "Message loop is done");
}
HWND SsdpCreateWindow()
{
WNDCLASS WndClass;
HWND hwnd;
//
// Register the window class.
//
WndClass.style = 0;
WndClass.lpfnWndProc = SsdpWindowProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = NULL;
WndClass.hIcon = NULL;
WndClass.hCursor = NULL;
WndClass.hbrBackground = NULL;
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = c_szWindowClassName;
if (!RegisterClass(&WndClass))
{
TraceTag(ttidSsdpRpcInit, "RegisterClassEx failed.");
return NULL;
}
//
// Create the window.
//
hwnd = CreateWindow(
c_szWindowClassName,
"",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
NULL,
NULL
);
if (hwnd == NULL)
{
TraceTag(ttidSsdpRpcInit, "CreateWindow failed.");
return NULL;
}
TraceTag(ttidSsdpRpcInit, "Created window %x", hwnd);
ShowWindow(hwnd, SW_HIDE);
UpdateWindow(hwnd);
return hwnd;
}
LRESULT CALLBACK SsdpWindowProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam
)
/*++
Routine Description:
Window message dispatch procedure for our hidden window.
Arguments:
hwnd - The target window handle.
msg - The current message.
wParam - WPARAM value.
lParam - LPARAM value.
Return Value:
LRESULT - The result of the message.
--*/
{
LRESULT Result = 0;
INT EventCode;
INT ErrorCode;
SOCKET Socket;
CHAR * szData;
CHAR * szTcpData;
SOCKADDR_IN RemoteSocket;
DWORD cbBuffSize = 0;
BOOL bMCast;
switch (msg)
{
case SM_SSDP:
Socket = (SOCKET) wParam;
EventCode = WSAGETSELECTEVENT(lParam);
ErrorCode = WSAGETSELECTERROR(lParam);
switch (EventCode)
{
case FD_READ:
if (FReferenceSocket(Socket))
{
cbBuffSize = 0;
if (SocketReceive(Socket, &szData, &cbBuffSize, &RemoteSocket, TRUE, &bMCast) == TRUE)
{
if(cbBuffSize <= SSDP_MSG_MAX_THROTTLE_SIZE )
{
CReceiveDataManager::Instance().HrAddData(
szData,
Socket,
bMCast,
reinterpret_cast<SOCKADDR_IN*>(&RemoteSocket));
}
else
{
// Typical SSDP MSG is less than 1K. If its greater than 16K we suspect a Buffer flood attack.
free(szData);
TraceTag(ttidSsdpRpcInit, "Received SSDP Msg more than 16k");
}
}
UnreferenceSocket(Socket);
}
break;
}
break;
case SM_TCP:
Socket = (SOCKET) wParam;
EventCode = WSAGETSELECTEVENT(lParam);
ErrorCode = WSAGETSELECTERROR(lParam);
switch (EventCode)
{
case FD_READ:
DWORD cbBuffer;
if (SocketReceive(Socket, &szTcpData, &cbBuffer,
&RemoteSocket, FALSE, &bMCast) == TRUE)
{
RECEIVE_DATA * pData = NULL;
pData = (RECEIVE_DATA *)malloc(sizeof(RECEIVE_DATA));
if (pData)
{
CopyMemory(&pData->RemoteSocket, &RemoteSocket,
sizeof(SOCKADDR_IN));
pData->socket = Socket;
pData->szBuffer = szTcpData;
pData->cbBuffer = cbBuffer;
pData->fIsTcpSocket = TRUE;
pData->fMCast = FALSE;
QueueUserWorkItem(LookupListOpenConn, pData, 0);
}
else
{
TraceError("Couldn't allocate sufficient memory!",
E_OUTOFMEMORY);
}
}
break;
case FD_ACCEPT:
TraceTag(ttidSsdpRpcInit, "Ready to accept connection");
HandleAccept(Socket);
break;
case FD_CLOSE:
// To-Do: Do I need to call recv to make sure all available data are read?
TraceTag(ttidSsdpRpcInit, "Closing socket %d", Socket);
closesocket(Socket);
RemoveOpenConn(Socket);
}
break;
case WM_QUERYENDSESSION:
TraceTag(ttidSsdpRpcInit, "Received WM_QUERYENDSESSION message");
if (!(lParam & ENDSESSION_LOGOFF))
{
TraceTag(ttidSsdpRpcInit, "System is shutting down");
}
Result = TRUE;
break;
default:
//
// Pass it through.
//
Result = DefWindowProc( hwnd, msg, wParam, lParam );
break;
}
return Result;
}