// This file is part of the EasyParentalControl Project.
//
// EasyParentalControl is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// EasyParentalControl is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with EasyParentalControl. If not, see <http://www.gnu.org/licenses/>.
// Parts of this file is derived from the winsock lsp sample, that can be found in the Microsoft Windows SDK v7.0
#pragma unmanaged
#include "StdAfx.h"
#include "Types.h"
#include "GlobalVars.h"
#include "SockInfo.h"
//
// We must register a window class with a unique name. To do so we'll
// use PROVIDER_CLASS string to format one which includes the current
// process ID. This string will be formatted into a buffer of
// PROVIDER_CLASS_CHAR_LEN length.
//
#define PROVIDER_CLASS TEXT("Layered WS2 Provider 0x%08x")
#define PROVIDER_CLASS_CHAR_LEN 32
//
// When handing window messages it is possible we'll get a message for a
// socket not in our list of created sockets. This can occur on an accepted
// socket since some messages may be posted to the accepted socket before
// the SOCK_INFO structure is inserted into the socket list in WSPAccept.
// So if we don't find a SOCK_INFO structure, we'll sleep and try again
// (up to MAX_ASYNC_RETRIES).
//
#define MAX_ASYNC_RETRIES 7
#define ASYNC_TIMEOUT 500 // half a second
#pragma warning(disable:4127) // Disable conditional expression is constant warning
#pragma warning(disable:4706) // Disable assignment within conditional warning (message pump)
//
// Function Prototypes
//
// Creates the hidden window and is the message pump for it
static DWORD WINAPI
AsyncMsgHandler(
LPVOID lpParameter
);
// Handler function for our Winsock FD_* events which posts completions to upper layer
static LRESULT CALLBACK
AsyncWndProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
);
//
// Globals to this file
//
static HANDLE WorkerThreadHandle = NULL; // Dispatch thread for handing window messages
static HWND AsyncWindow = NULL; // Handle to the hidden window
static TCHAR AsyncProviderClassName[ PROVIDER_CLASS_CHAR_LEN ];
//
// Function: StopAsyncWindowManager
//
// Description:
// This function cleans up the subsystem that handles asynchronous
// window IO (e.g. WSAAsyncSelect). Basically, it destroys the the
// hidden window if its been created. Note that this function is
// only called from WSPCleanup when the DLL is about to be unloaded,
// and we've already entered the critical section (GlobalVars::gCriticalSection).
//
int
StopAsyncWindowManager(
)
{
int rc,
code;
if ( NULL != AsyncWindow )
{
// Post a quit message to the thread
PostMessage( AsyncWindow, WM_DESTROY, 0, 0 );
// Wait for the thread to cleanup and exit
rc = WaitForSingleObject( WorkerThreadHandle, 10000 );
if ( WAIT_TIMEOUT == rc )
{
dbgprint("StopAsyncWindowManager: Timed out waiting for async thread!");
goto cleanup;
}
// Retrieve the exit code and display a simple message
rc = GetExitCodeThread( WorkerThreadHandle, (LPDWORD) &code );
if ( 0 == rc )
dbgprint("StopAsyncWindowManager: Unable to retrieve thread exit code: %d",
GetLastError() );
else if ( 0 != code )
dbgprint("StopAsyncWindowManager: Async window thread exited abnormally!");
cleanup:
CloseHandle( WorkerThreadHandle );
WorkerThreadHandle = NULL;
}
return 0;
}
//
// Function: GetWorkerWindow
//
// Description:
// This returns a handle to our hidden window that acts as an
// intermediary between the apps window and winsock. If the window
// hasn't already been created, then create it.
//
HWND
GetWorkerWindow(
)
{
HANDLE ReadyEvent = NULL;
int rc;
EnterCriticalSection( &GlobalVars::gCriticalSection );
if ( NULL == WorkerThreadHandle )
{
// Create an event which the worker thread will signal when its ready
ReadyEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
if ( NULL == ReadyEvent )
{
dbgprint("GetWorkerWindow: CreateEvent failed: %d", GetLastError() );
goto cleanup;
}
// Create the asyn window message thread
WorkerThreadHandle = CreateThread(
NULL,
0,
AsyncMsgHandler,
(LPVOID)ReadyEvent,
0,
NULL
);
if ( NULL == WorkerThreadHandle )
{
dbgprint( "GetWorkerWindow: CreateThread failed: %d", GetLastError() );
goto cleanup;
}
// Wait for the window to become initialized
rc = WaitForSingleObject( ReadyEvent, INFINITE );
if ( ( WAIT_FAILED == rc ) || ( WAIT_TIMEOUT == rc ) )
dbgprint( "GetWorkerWindow: WaitForSingleObject failed: %d! (error = %d)",
rc, GetLastError() );
}
cleanup:
if ( NULL != ReadyEvent )
{
// Close the ready event
rc = CloseHandle( ReadyEvent );
if ( 0 == rc )
{
dbgprint("GetWorkerWindow: CloseHandle failed: %d", GetLastError() );
}
}
LeaveCriticalSection( &GlobalVars::gCriticalSection );
return AsyncWindow;
}
//
// Function: AsyncMsgHandler
//
// Description:
// This is the message pump for our hidden window.
//
static DWORD WINAPI
AsyncMsgHandler(
LPVOID lpParameter
)
{
MSG msg;
DWORD Ret;
HANDLE readyEvent;
HRESULT hr;
WNDCLASS wndclass;
// Event to signal when window is ready
readyEvent = (HANDLE) lpParameter;
hr = StringCchPrintf(
AsyncProviderClassName,
PROVIDER_CLASS_CHAR_LEN,
PROVIDER_CLASS,
GetCurrentProcessId()
);
if ( FAILED( hr ) )
{
dbgprint("AsyncMsgHandler: StringCchPrintf failed: %d", GetLastError());
goto cleanup;
}
dbgprint("AsyncMsgHandler: Class name is '%s'", AsyncProviderClassName );
memset( &wndclass, 0, sizeof( wndclass ) );
wndclass.lpfnWndProc = (WNDPROC)AsyncWndProc;
wndclass.hInstance = GlobalVars::gDllInstance;
wndclass.lpszClassName = AsyncProviderClassName;
if ( 0 == RegisterClass( &wndclass ) )
{
dbgprint("AsyncMsgHandle: RegisterClass failed: %d", GetLastError());
goto cleanup;
}
// Create a window.
AsyncWindow = CreateWindow(
AsyncProviderClassName,
TEXT("Layered Hidden Window"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
GlobalVars::gDllInstance,
NULL
);
if ( NULL == AsyncWindow )
{
dbgprint("AsyncMessageHandler: CreateWindow failed: %d", GetLastError() );
goto cleanup;
}
// Indicate the window is ready
SetEvent( readyEvent );
// Message pump
while ( ( Ret = GetMessage( &msg, NULL, 0, 0 ) ) )
{
if ( -1 == Ret )
{
dbgprint("AsyncMessageHandler: GetMessage returned -1, exiting loop");
break;
}
TranslateMessage( &msg );
DispatchMessage( &msg );
}
// Clean up the window and window class which were created in this thread
if ( NULL != AsyncWindow )
{
DestroyWindow( AsyncWindow );
AsyncWindow = NULL;
}
UnregisterClass( AsyncProviderClassName, GlobalVars::gDllInstance );
ExitThread(0);
cleanup:
SetEvent( readyEvent );
ExitThread( (DWORD) -1);
}
//
// Function: AsyncWndProc
//
// Description:
// This is the window proc for our hidden window. Once we receive a message
// on our hidden window we must translate our lower provider's socket handle
// to the app's socket handle and then complete the notification to the user.
//
static LRESULT CALLBACK
AsyncWndProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
SOCK_INFO *si = NULL;
int retries,
rc;
if ( WM_SOCKET == uMsg )
{
retries = 0;
//
// Given the lower provider's socket handle (the wParam), do a reverse lookup
// to find the socket context structure. It is possible we received a message
// for a socket not in our list of sockets. This usually occurs after WSPAccept
// completes and we immediately get a message for the accepted socket but the
// LSP code to insert the new context object hasn't executed yet. If this is
// the case wait on an event until a new context object is added (which
// signals the event). Note we'll wait only MAX_ASYNC_RETRIES times before
// bailing.
//
while ( retries < MAX_ASYNC_RETRIES )
{
dbgprint("hWnd 0x%p, uMsg 0x%x, WPARAM 0x%p, LPARAM 0x%p (retries %d)",
hWnd, uMsg, wParam, lParam, retries);
// Find the context for this lower provider socket handle
si = GetCallerSocket( NULL, wParam );
if ( NULL == si )
{
dbgprint("Unable to find socket context for 0x%p", wParam);
// Wait on an event which is signaled when context is added
rc = WaitForSingleObject( GlobalVars::gAddContextEvent, ASYNC_TIMEOUT );
if ( WAIT_FAILED == rc )
{
dbgprint("AsyncWndProc: WaitForSingleObject failed: %d",
GetLastError());
break;
}
else // timeout or success
{
// Reset the event if it was signaled
if ( WAIT_OBJECT_0 == rc )
ResetEvent( GlobalVars::gAddContextEvent );
// If signaled, hopefully next lookup will succeed
retries++;
continue;
}
}
GlobalVars::gMainUpCallTable.lpWPUPostMessage(
si->hWnd,
si->uMsg,
si->LayeredSocket,
lParam
);
return 0;
}
}
else if ( WM_DESTROY == uMsg )
{
// Post a quit message to exit our async message pump thread
PostQuitMessage( 0 );
return 0;
}
return DefWindowProc( hWnd, uMsg, wParam, lParam );
}