Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2222 lines
63 KiB

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
faxsvc.c
Abstract:
This module contains the service specific code.
Author:
Wesley Witt (wesw) 16-Jan-1996
Revision History:
--*/
#include "faxsvc.h"
#pragma hdrstop
#include <ExpDef.h>
#include <ErrorRep.h>
class CComBSTR;
#define SERVICE_DEBUG_LOG_FILE _T("FXSSVCDebugLogFile.txt")
static SERVICE_STATUS gs_FaxServiceStatus;
static SERVICE_STATUS_HANDLE gs_FaxServiceStatusHandle;
static LPCWSTR gs_lpcwstrUnhandledExceptionSourceName; // Points to the friendly name of the last
// FSP / R.Ext which caused an unhandled
// exception in a direct function call.
// Used for event logging
// in FaxUnhandledExceptionFilter().
static EXCEPTION_SOURCE_TYPE gs_UnhandledExceptionSource; // Specifies the source of an unhandled
// exception.
// Used for event logging
// in FaxUnhandledExceptionFilter().
static DWORD gs_dwUnhandledExceptionCode; // Holds the exception code of the last
// unhandled exception in a direct function call
// to an FSP / R.Ext
// Used for event logging
// in FaxUnhandledExceptionFilter().
static BOOL gs_bUseDefaultFaultHandlingPolicy; // Read from the registry during service
// startup. If non-zero, FaxUnhandledExceptionFilter
// behaves as if it did not exist.
HANDLE g_hServiceShutDownEvent; // This event is set after the service got g_hSCMServiceShutDownEvent from SCM and signals the various threads to terminate!
HANDLE g_hSCMServiceShutDownEvent; // This event is set when SCM tells the service to STOP!
HANDLE g_hServiceIsDownSemaphore; // This semaphore is used to synchronize TapiWorkerThread() JobQueueThread() and EndFaxSvc()
SERVICE_TABLE_ENTRY ServiceDispatchTable[] = {
{ FAX_SERVICE_NAME, FaxServiceMain },
{ NULL, NULL }
};
static
BOOL
InitializeFaxLibrariesGlobals(
VOID
)
/*++
Routine Description:
Initialize Fax libraries globals.
Becuase the process is not always terminated when the service is stopped,
We must not have any staticly initialized global variables.
Initialize all fax libraries global variables before starting the service
Arguments:
None.
Return Value:
BOOL
--*/
{
BOOL bRet = TRUE;
DEBUG_FUNCTION_NAME(TEXT("InitializeFaxLibraries"));
if (!FXSEVENTInitialize())
{
DebugPrintEx(DEBUG_ERR,
TEXT("FXSEVENTInitialize failed"));
bRet = FALSE;
}
if (!FXSTIFFInitialize())
{
DebugPrintEx(DEBUG_ERR,
TEXT("FXSTIFFInitialize failed"));
bRet = FALSE;
}
return bRet;
}
static
VOID
FreeFaxLibrariesGlobals(
VOID
)
/*++
Routine Description:
Frees Fax libraries globals.
Arguments:
None.
Return Value:
BOOL
--*/
{
FXSEVENTFree();
HeapCleanup();
return;
}
static
BOOL
InitializeServiceGlobals(
VOID
)
/*++
Routine Description:
Initialize service globals.
Becuase the process is not always terminated when the service is stopped,
We must not have any staticly initialized global variables.
Initialize all service global variables before starting the service
Arguments:
None.
Return Value:
BOOL
--*/
{
DWORD Index;
DWORD ec = ERROR_SUCCESS;
DEBUG_FUNCTION_NAME(TEXT("InitializeServiceGlobals"));
//
// Initialize static allocated globals
//
g_pFaxPerfCounters = NULL;
#ifdef DBG
g_hCritSecLogFile = INVALID_HANDLE_VALUE;
#endif
gs_lpcwstrUnhandledExceptionSourceName = NULL;
gs_UnhandledExceptionSource = EXCEPTION_SOURCE_UNKNOWN;
gs_dwUnhandledExceptionCode = 0;
gs_bUseDefaultFaultHandlingPolicy = FALSE;
ZeroMemory (&g_ReceiptsConfig, sizeof(g_ReceiptsConfig)); // Global receipts configuration
g_ReceiptsConfig.dwSizeOfStruct = sizeof(g_ReceiptsConfig);
ZeroMemory (g_ArchivesConfig, sizeof(g_ArchivesConfig)); // Global archives configuration
g_ArchivesConfig[0].dwSizeOfStruct = sizeof(g_ArchivesConfig[0]);
g_ArchivesConfig[1].dwSizeOfStruct = sizeof(g_ArchivesConfig[1]);
ZeroMemory (&g_ActivityLoggingConfig, sizeof(g_ActivityLoggingConfig)); // Global activity logging configuration
g_ActivityLoggingConfig.dwSizeOfStruct = sizeof(g_ActivityLoggingConfig);
ZeroMemory (&g_ServerActivity, sizeof(g_ServerActivity)); // Global Fax Service Activity
g_ServerActivity.dwSizeOfStruct = sizeof(FAX_SERVER_ACTIVITY);
ZeroMemory (g_wszFaxQueueDir, sizeof(g_wszFaxQueueDir));
g_hDispatchEventsCompPort = NULL; // Dispatch Events completion port
g_hSendEventsCompPort = NULL; // Send Events completion port = NULL; // Events completion port
g_dwlClientID = 0; // Client ID
ZeroMemory (g_FaxQuotaWarn, sizeof(g_FaxQuotaWarn));
g_hArchiveQuotaWarningEvent = NULL;
g_dwConnectionCount = 0; // Represents the number of active rpc connections.
g_hInboxActivityLogFile = INVALID_HANDLE_VALUE;
g_hOutboxActivityLogFile = INVALID_HANDLE_VALUE;
g_fLogStringTableInit = FALSE; // activity logging string table
g_dwQueueCount = 0; // Count of jobs (both parent and non-parent) in the queue. Protected by g_CsQueue
g_hJobQueueEvent = NULL;
g_dwQueueState = 0;
g_ScanQueueAfterTimeout = FALSE; // The JobQueueThread checks this if waked up after JOB_QUEUE_TIMEOUT.
// If it is TRUE - g_hQueueTimer or g_hJobQueueEvent were not set - Scan the queue.
g_dwReceiveDevicesCount = 0; // Count of devices that are receive-enabled. Protected by g_CsLine.
g_bDelaySuicideAttempt = FALSE; // If TRUE, the service waits
// before checking if it can commit suicide.
// Initially FALSE, can be set to true if the service is launched
// with SERVICE_DELAY_SUICIDE command line parameter.
g_bServiceCanSuicide = TRUE;
g_dwCountRoutingMethods = 0;
g_pLineCountryList = NULL;
g_dwLastUniqueId = 0;
g_bServiceIsDown = FALSE; // This is set to TRUE by FaxEndSvc()
g_TapiCompletionPort = NULL;
g_hLineApp = NULL;
g_pAdaptiveFileBuffer = NULL;
gs_FaxServiceStatus.dwServiceType = SERVICE_WIN32;
gs_FaxServiceStatus.dwCurrentState = SERVICE_START_PENDING;
gs_FaxServiceStatus.dwWin32ExitCode = 0;
gs_FaxServiceStatus.dwServiceSpecificExitCode = 0;
gs_FaxServiceStatus.dwCheckPoint = 0;
gs_FaxServiceStatus.dwWaitHint = 0;
g_hRPCListeningThread = NULL;
g_lServiceThreadsCount = 0;
g_hThreadCountEvent = NULL;
g_dwAllowRemote = 0; // By default, do not allow remote calls if the printer is not shared.
for (Index = 0; Index < gc_dwCountInboxTable; Index++)
{
g_InboxTable[Index].String = NULL;
}
for (Index = 0; Index < gc_dwCountOutboxTable; Index++)
{
g_OutboxTable[Index].String = NULL;
}
for (Index = 0; Index < gc_dwCountServiceStringTable; Index++)
{
g_ServiceStringTable[Index].String = NULL;
}
g_StatusCompletionPortHandle = NULL;
g_pFaxSD = NULL;
g_dwDeviceCount = 0;
g_dwDeviceEnabledCount = 0;
g_dwDeviceEnabledLimit = GetDeviceLimit();
g_hFaxPerfCountersMap = 0;
g_hTapiWorkerThread = NULL;
g_hJobQueueThread = NULL;
g_dwRecipientsLimit = 0;
g_hResource = GetResInstance(NULL);
if(!g_hResource)
{
ec = GetLastError();
goto Error;
}
//
// Create an event to signal service shutdown from service to various threads
//
g_hServiceShutDownEvent = CreateEvent(
NULL, // SD
TRUE, // reset type - Manual
FALSE, // initial state - Not signaled. The event is signaled when the service gets g_hSCMServiceShutDownEvent
NULL // object name
);
if (NULL == g_hServiceShutDownEvent)
{
ec = GetLastError();
DebugPrintEx(DEBUG_ERR,
TEXT("CreateEvent (g_hServiceShutDownEvent) failed (ec: %ld)"),
ec);
goto Error;
}
//
// Create an event used by SCM to signal service shutdown
//
g_hSCMServiceShutDownEvent = CreateEvent(
NULL, // SD
TRUE, // reset type - Manual
FALSE, // initial state - Not signaled. The event is signaled when the service gets SERVICE_CONTROL_STOP or SERVICE_CONTROL_SHUTDOWN.
NULL // object name
);
if (NULL == g_hSCMServiceShutDownEvent)
{
ec = GetLastError();
DebugPrintEx(DEBUG_ERR,
TEXT("CreateEvent (g_hSCMServiceShutDownEvent) failed (ec: %ld)"),
ec);
goto Error;
}
//
// Create a semaphore to syncronize TapiWorkerThread() JobQueueThread() and EndFaxSvc()
//
g_hServiceIsDownSemaphore = CreateSemaphore(
NULL, // SD
0, // initial count - Not signaled
2, // maximum count - JobQueueThread and TapiWorkerThread
NULL // object name
);
if (NULL == g_hServiceIsDownSemaphore)
{
ec = GetLastError();
DebugPrintEx(DEBUG_ERR,
TEXT("CreateSemaphore (g_hServiceIsDownSemaphore) failed (ec: %ld)"),
ec);
goto Error;
}
//
// Try to init some global critical sections
//
#ifdef DBG
if (!g_CsCritSecList.Initialize())
{
ec = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("CFaxCriticalSection::Initialize failed: err = %d"),
ec);
goto Error;
}
#endif
if (!g_CsConfig.Initialize() ||
!g_CsInboundActivityLogging.Initialize() ||
!g_CsOutboundActivityLogging.Initialize() ||
!g_CsJob.Initialize() ||
!g_CsQueue.Initialize() ||
!g_CsPerfCounters.Initialize() ||
!g_CsSecurity.Initialize() ||
!g_csUniqueQueueFile.Initialize() ||
!g_CsLine.Initialize() ||
!g_CsRouting.Initialize() ||
!g_CsServiceThreads.Initialize() ||
!g_CsHandleTable.Initialize() ||
!g_CsActivity.Initialize() ||
!g_CsClients.Initialize())
{
ec = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("CFaxCriticalSection::Initialize failed: err = %d"),
ec);
goto Error;
}
//
// Initialize service linked lists
//
InitializeListHead( &g_DeviceProvidersListHead );
InitializeListHead( &g_HandleTableListHead );
InitializeListHead( &g_JobListHead );
InitializeListHead( &g_QueueListHead );
InitializeListHead( &g_QueueListHead);
InitializeListHead( &g_lstRoutingExtensions );
InitializeListHead( &g_lstRoutingMethods );
#if DBG
InitializeListHead( &g_CritSecListHead );
#endif
InitializeListHead( &g_TapiLinesListHead );
InitializeListHead( &g_RemovedTapiLinesListHead );
//
// Initialize dynamic allocated global classes
//
g_pClientsMap = NULL;
g_pNotificationMap = NULL;
g_pTAPIDevicesIdsMap = NULL;
g_pGroupsMap = NULL;
g_pRulesMap = NULL;
g_pClientsMap = new (std::nothrow) CClientsMap;
if (NULL == g_pClientsMap)
{
ec = ERROR_NOT_ENOUGH_MEMORY;
goto Error;
}
g_pNotificationMap = new (std::nothrow) CNotificationMap;
if (NULL == g_pNotificationMap)
{
ec = ERROR_NOT_ENOUGH_MEMORY;
goto Error;
}
g_pTAPIDevicesIdsMap = new (std::nothrow) CMapDeviceId;
if (NULL == g_pTAPIDevicesIdsMap)
{
ec = ERROR_NOT_ENOUGH_MEMORY;
goto Error;
}
g_pGroupsMap = new (std::nothrow) COutboundRoutingGroupsMap;
if (NULL == g_pGroupsMap)
{
ec = ERROR_NOT_ENOUGH_MEMORY;
goto Error;
}
g_pRulesMap = new (std::nothrow) COutboundRulesMap;
if (NULL == g_pRulesMap)
{
ec = ERROR_NOT_ENOUGH_MEMORY;
goto Error;
}
return TRUE;
Error:
Assert(ec != ERROR_SUCCESS);
SetLastError (ec);
return FALSE;
}
LONG
HandleFaxExtensionFault (
EXCEPTION_SOURCE_TYPE ExSrc,
LPCWSTR lpcswstrExtFriendlyName,
DWORD dwCode
)
/*++
Routine Description:
This function handles all exceptions thrown as a result from direct calls into fax extensions (FSPs and routing extensions).
All it does is store the friendly name of the FSP/R.Ext and the exception code and re-throws the exception.
It should be used as an exception filter in the __except keyword.
The FaxUnhandledExceptionFilter() function uses that store information to log an event message.
Arguments:
ExSrc - [in] The source of the exception (FSP / R.Ext)
lpcswstrExtFriendlyName - [in] Extension friendly name
dwCode - [in] Exception code
Return Value:
Exception handling code
--*/
{
DEBUG_FUNCTION_NAME(TEXT("HandleFaxExtensionFault"));
Assert (lpcswstrExtFriendlyName);
Assert (ExSrc > EXCEPTION_SOURCE_UNKNOWN);
Assert (ExSrc <= EXCEPTION_SOURCE_ROUTING_EXT);
gs_lpcwstrUnhandledExceptionSourceName = lpcswstrExtFriendlyName;
gs_UnhandledExceptionSource = ExSrc;
gs_dwUnhandledExceptionCode = dwCode;
return EXCEPTION_CONTINUE_SEARCH;
} // HandleFaxExtensionFault
LONG FaxUnhandledExceptionFilter(
_EXCEPTION_POINTERS *pExceptionInfo
)
/*++
Routine Description:
This function serves as the catch-all exception handler for the entire process.
When an unhandled exception is thrown, this function will be called and:
1. Increase unhandled exceptions count
2. For first unhandled exception only
2.1. Generate a Dr. Watson report
2.2. Write an event log entry
2.3. Attempt to shut down all FSPs
3. Terminate the process
Arguments:
pExceptionInfo - Pointer to exception information
Return Value:
Exception handling code
Remarks:
This function gets called by kernel32!UnhandledExceptionFilterEx.
kernel32!UnhandledExceptionFilterEx doesn't call this function if a debugger is attached
while a unhandled exception occurs. Instead, it returns EXCEPTION_CONTINUE_SEARCH which let's
the debugger handle the 2nd chance exception.
If this function returns EXCEPTION_EXECUTE_HANDLER, kernel32!UnhandledExceptionFilterEx does nothing and
kernel32!BaseThreadStart calls kernel32!ExitProcess.
This basically means:
- No GP fault UI
- No Dr. Watson report (direct or queued)
- No support for AeDebug in the registry for attaching a debugger to a crashing process
if this function EXCEPTION_CONTINUE_SEARCH, kernel32!UnhandledExceptionFilterEx acts normally.
This basically means:
- GP fault UI (which might be disabled by calling SetErrorMode(), which we never do)
- A Dr. Watson report is generated
- AeDebug support is enabled
- If none of the above is used, kernel32!BaseThreadStart calls kernel32!ExitProcess
--*/
{
static volatile long lFaultCount = 0;
DEBUG_FUNCTION_NAME(TEXT("FaxUnhandledExceptionFilter"));
DebugPrintEx (
DEBUG_MSG,
TEXT("Unhandled exception from %s (code %d)."),
gs_lpcwstrUnhandledExceptionSourceName,
gs_dwUnhandledExceptionCode);
if (STATUS_BREAKPOINT == pExceptionInfo->ExceptionRecord->ExceptionCode)
{
//
// Debug break exception caught here.
// Make sure the user sees the normal system behavior.
//
DebugPrintEx (DEBUG_MSG, TEXT("Debug break exception caught."));
return EXCEPTION_CONTINUE_SEARCH;
}
if (1 == InterlockedIncrement (&lFaultCount))
{
if (gs_bUseDefaultFaultHandlingPolicy)
{
//
// User chose (in the registry) to disable the SCM revival feature by using the
// system's default fault handling policy.
//
DebugPrintEx (DEBUG_MSG, TEXT("UseDefaultFaultHandlingPolicy is set. Exception is ignored to be handled by the system."));
return EXCEPTION_CONTINUE_SEARCH;
}
// First unhandled exception caught here.
// Try to nicely shutdown the FSPs.
// Start by generating a Dr. Watson report.
//
EFaultRepRetVal ret = ReportFault (pExceptionInfo, 0);
if (frrvOk != ret &&
frrvOkHeadless != ret &&
frrvOkQueued != ret &&
frrvOkManifest != ret)
{
//
// Error generating a Dr. Watson report
//
DebugPrintEx (
DEBUG_MSG,
TEXT("ReportFault failed with %ld."),
ret);
}
//
// Log a fault event entry
//
switch (gs_UnhandledExceptionSource)
{
case EXCEPTION_SOURCE_UNKNOWN:
FaxLog(FAXLOG_CATEGORY_UNKNOWN,
FAXLOG_LEVEL_MIN,
1,
MSG_FAX_GENERAL_FAULT,
DWORD2HEX(gs_dwUnhandledExceptionCode)
);
break;
case EXCEPTION_SOURCE_FSP:
FaxLog(FAXLOG_CATEGORY_UNKNOWN,
FAXLOG_LEVEL_MIN,
2,
MSG_FAX_FSP_GENERAL_FAULT,
gs_lpcwstrUnhandledExceptionSourceName,
DWORD2HEX(gs_dwUnhandledExceptionCode)
);
break;
case EXCEPTION_SOURCE_ROUTING_EXT:
FaxLog(FAXLOG_CATEGORY_UNKNOWN,
FAXLOG_LEVEL_MIN,
2,
MSG_FAX_ROUTING_EXT_GENERAL_FAULT,
gs_lpcwstrUnhandledExceptionSourceName,
DWORD2HEX(gs_dwUnhandledExceptionCode)
);
break;
default:
ASSERT_FALSE;
break;
}
//
// Attempt to gracefully stop the FSPs.
// This is crucial for releasing the H/W to the correct state before our process gets killed.
//
StopAllInProgressJobs();
StopFaxServiceProviders();
//
// Ask kernel32!BaseThreadStart to call kernel32!ExitProcess
//
return EXCEPTION_EXECUTE_HANDLER;
}
else
{
//
// Fault caught while shutting down.
//
DebugPrintEx (
DEBUG_MSG,
TEXT("Unhandled exception number %d from %s (code %d) ignored."),
lFaultCount,
gs_lpcwstrUnhandledExceptionSourceName,
gs_dwUnhandledExceptionCode);
//
// Ask kernel32!BaseThreadStart to call kernel32!ExitProcess
//
return EXCEPTION_EXECUTE_HANDLER;
}
} // FaxUnhandledExceptionFilter
#ifdef __cplusplus
extern "C"
#endif
int
WINAPI
#ifdef UNICODE
wWinMain(
#else
WinMain(
#endif
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nShowCmd
);
int
WINAPI
#ifdef UNICODE
wWinMain(
#else
WinMain(
#endif
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nShowCmd
)
/*++
Routine Description:
Main entry point for the TIFF image viewer.
Arguments:
hInstance - Instance handle
hPrevInstance - Not used
lpCmdLine - Command line arguments
nShowCmd - How to show the window
Return Value:
Return code, zero for success.
--*/
{
DWORD ec = ERROR_SUCCESS;
DEBUG_FUNCTION_NAME(TEXT("WinMain"));
OPEN_DEBUG_FILE (SERVICE_DEBUG_LOG_FILE);
if (!StartServiceCtrlDispatcher( ServiceDispatchTable))
{
ec = GetLastError();
DebugPrintEx (
DEBUG_ERR,
TEXT("StartServiceCtrlDispatcher error =%d"),
ec);
}
CLOSE_DEBUG_FILE;
return ec;
}
static
VOID
FreeServiceGlobals (
VOID
)
{
DWORD Index;
DEBUG_FUNCTION_NAME(TEXT("FreeServiceGlobals"));
//
// Delete all global critical sections
//
g_CsHandleTable.SafeDelete();
#ifdef DBG
g_CsCritSecList.SafeDelete();
#endif
g_CsConfig.SafeDelete();
g_CsInboundActivityLogging.SafeDelete();
g_CsOutboundActivityLogging.SafeDelete();
g_CsJob.SafeDelete();
g_CsQueue.SafeDelete();
g_CsPerfCounters.SafeDelete();
g_CsSecurity.SafeDelete();
g_csUniqueQueueFile.SafeDelete();
g_CsLine.SafeDelete();
g_CsRouting.SafeDelete();
g_CsActivity.SafeDelete();
g_CsClients.SafeDelete();
g_CsServiceThreads.SafeDelete();
if (g_pClientsMap)
{
delete g_pClientsMap;
g_pClientsMap = NULL;
}
if (g_pNotificationMap)
{
delete g_pNotificationMap;
g_pNotificationMap = NULL;
}
if (g_pTAPIDevicesIdsMap)
{
delete g_pTAPIDevicesIdsMap;
g_pTAPIDevicesIdsMap = NULL;
}
if (g_pGroupsMap)
{
delete g_pGroupsMap;
g_pGroupsMap = NULL;
}
if (g_pRulesMap)
{
delete g_pRulesMap;
g_pRulesMap = NULL;
}
//
// Close global Handles and free globaly allocated memory
//
if (NULL != g_pFaxSD)
{
if (!DestroyPrivateObjectSecurity (&g_pFaxSD))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("DestroyPrivateObjectSecurity() failed. (ec: %ld)"),
GetLastError());
}
g_pFaxSD = NULL;
}
if (INVALID_HANDLE_VALUE != g_hOutboxActivityLogFile)
{
if (!CloseHandle (g_hOutboxActivityLogFile))
{
DebugPrintEx(DEBUG_ERR,
TEXT("CloseHandle failed (ec: %ld)"),
GetLastError());
}
g_hOutboxActivityLogFile = INVALID_HANDLE_VALUE;
}
if (INVALID_HANDLE_VALUE != g_hInboxActivityLogFile)
{
if (!CloseHandle (g_hInboxActivityLogFile))
{
DebugPrintEx(DEBUG_ERR,
TEXT("CloseHandle failed (ec: %ld)"),
GetLastError());
}
g_hInboxActivityLogFile = INVALID_HANDLE_VALUE;
}
#if DBG
if (INVALID_HANDLE_VALUE != g_hCritSecLogFile)
{
CloseHandle(g_hCritSecLogFile);
g_hCritSecLogFile = INVALID_HANDLE_VALUE;
}
#endif
if (NULL != g_hSendEventsCompPort)
{
if (!CloseHandle (g_hSendEventsCompPort))
{
DebugPrintEx(DEBUG_ERR,
TEXT("CloseHandle failed (ec: %ld)"),
GetLastError());
}
g_hSendEventsCompPort = NULL;
}
if (NULL != g_hDispatchEventsCompPort)
{
if (!CloseHandle (g_hDispatchEventsCompPort))
{
DebugPrintEx(DEBUG_ERR,
TEXT("CloseHandle failed (ec: %ld)"),
GetLastError());
}
g_hDispatchEventsCompPort = NULL;
}
if (NULL != g_hArchiveQuotaWarningEvent)
{
if (!CloseHandle(g_hArchiveQuotaWarningEvent))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to close archive config event handle - quota warnings [handle = %p] (ec=0x%08x)."),
g_hArchiveQuotaWarningEvent,
GetLastError());
}
g_hArchiveQuotaWarningEvent = NULL;
}
for (Index = 0; Index < gc_dwCountInboxTable; Index++)
{
MemFree(g_InboxTable[Index].String);
g_InboxTable[Index].String = NULL;
}
for (Index = 0; Index < gc_dwCountOutboxTable; Index++)
{
MemFree(g_OutboxTable[Index].String);
g_OutboxTable[Index].String = NULL;
}
for (Index = 0; Index < gc_dwCountServiceStringTable; Index++)
{
MemFree(g_ServiceStringTable[Index].String);
g_ServiceStringTable[Index].String = NULL;
}
if (NULL != g_StatusCompletionPortHandle)
{
if (!CloseHandle(g_StatusCompletionPortHandle))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CloseHandle Failed (ec: %ld"),
GetLastError());
}
g_StatusCompletionPortHandle = NULL;
}
if (NULL != g_pFaxPerfCounters)
{
if (!UnmapViewOfFile(g_pFaxPerfCounters))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("UnmapViewOfFile Failed (ec: %ld"),
GetLastError());
}
g_pFaxPerfCounters = NULL;
}
if (NULL != g_hFaxPerfCountersMap)
{
if (!CloseHandle(g_hFaxPerfCountersMap))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CloseHandle Failed (ec: %ld"),
GetLastError());
}
g_hFaxPerfCountersMap = NULL;
}
if (NULL != g_hLineApp)
{
LONG Rslt = lineShutdown(g_hLineApp);
if (Rslt)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("lineShutdown() failed (ec: %ld)"),
Rslt);
}
g_hLineApp = NULL;
}
if (NULL != g_hThreadCountEvent)
{
if (!CloseHandle(g_hThreadCountEvent))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CloseHandle for g_hThreadCountEvent Failed (ec: %ld"),
GetLastError());
}
g_hThreadCountEvent = NULL;
}
if (NULL != g_hServiceShutDownEvent)
{
if (!CloseHandle(g_hServiceShutDownEvent))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CloseHandle for g_hServiceShutDownEvent Failed (ec: %ld"),
GetLastError());
}
g_hServiceShutDownEvent = NULL;
}
if (NULL != g_hSCMServiceShutDownEvent)
{
if (!CloseHandle(g_hSCMServiceShutDownEvent))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CloseHandle for g_hSCMServiceShutDownEvent Failed (ec: %ld"),
GetLastError());
}
g_hSCMServiceShutDownEvent = NULL;
}
if (NULL != g_hServiceIsDownSemaphore)
{
if (!CloseHandle(g_hServiceIsDownSemaphore))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CloseHandle for g_hServiceIsDownSemaphore Failed (ec: %ld"),
GetLastError());
}
g_hServiceIsDownSemaphore = NULL;
}
if (NULL != g_hTapiWorkerThread)
{
if (!CloseHandle(g_hTapiWorkerThread))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CloseHandle for g_hTapiWorkerThread Failed (ec: %ld"),
GetLastError());
}
g_hTapiWorkerThread = NULL;
}
if (NULL != g_hJobQueueThread)
{
if (!CloseHandle(g_hJobQueueThread))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CloseHandle for g_hJobQueueThread Failed (ec: %ld"),
GetLastError());
}
g_hJobQueueThread = NULL;
}
MemFree(g_pAdaptiveFileBuffer);
g_pAdaptiveFileBuffer = NULL;
MemFree(g_pLineCountryList);
g_pLineCountryList = NULL;
FreeRecieptsConfiguration( &g_ReceiptsConfig, FALSE);
//
// free g_ArchivesConfig strings memory
//
MemFree(g_ArchivesConfig[0].lpcstrFolder);
MemFree(g_ArchivesConfig[1].lpcstrFolder);
//
// free g_ActivityLoggingConfig strings memory
//
MemFree(g_ActivityLoggingConfig.lptstrDBPath);
FreeResInstance();
Assert ((ULONG_PTR)g_HandleTableListHead.Flink == (ULONG_PTR)&g_HandleTableListHead);
Assert ((ULONG_PTR)g_DeviceProvidersListHead.Flink == (ULONG_PTR)&g_DeviceProvidersListHead);
Assert ((ULONG_PTR)g_JobListHead.Flink == (ULONG_PTR)&g_JobListHead);
Assert ((ULONG_PTR)g_QueueListHead.Flink == (ULONG_PTR)&g_QueueListHead);
Assert ((ULONG_PTR)g_lstRoutingMethods .Flink == (ULONG_PTR)&g_lstRoutingMethods );
Assert ((ULONG_PTR)g_lstRoutingExtensions .Flink == (ULONG_PTR)&g_lstRoutingExtensions );
#if DBG
Assert ((ULONG_PTR)g_CritSecListHead .Flink == (ULONG_PTR)&g_CritSecListHead );
#endif
Assert ((ULONG_PTR)g_TapiLinesListHead .Flink == (ULONG_PTR)&g_TapiLinesListHead );
Assert ((ULONG_PTR)g_RemovedTapiLinesListHead .Flink == (ULONG_PTR)&g_RemovedTapiLinesListHead );
return;
}
VOID
EncryptReceiptsPassword(
VOID
)
/*++
Routine Description:
Checks if the receipts password is stored encrypted. If it is not encrypted, it encrypts the password.
Unattended fax installation stores this password as clear text in the registry.
Arguments:
None.
Return Value:
None.
--*/
{
DEBUG_FUNCTION_NAME(TEXT("EncryptReceiptsPassword"));
FAX_ENUM_DATA_ENCRYPTION DataEncrypted;
HKEY hKey;
LPWSTR lpwstrPassword = NULL;
BOOL bDeletePassword = FALSE;
hKey = OpenRegistryKey(HKEY_LOCAL_MACHINE, REGKEY_FAX_RECEIPTS, FALSE, KEY_READ | KEY_WRITE);
if (NULL == hKey)
{
DebugPrintEx(DEBUG_ERR,
TEXT("OpenRegistryKey failed (ec: %ld)"),
GetLastError());
return;
}
lpwstrPassword = GetRegistrySecureString(hKey, REGVAL_RECEIPTS_PASSWORD, NULL, TRUE, &DataEncrypted);
if (FAX_DATA_ENCRYPTED == DataEncrypted)
{
//
// We are done!
//
goto exit;
}
else if(FAX_NO_DATA == DataEncrypted)
{
//
// We do not know if the data is encrypted or not.
// Delete the password
//
bDeletePassword = TRUE;
}
else
{
Assert (FAX_DATA_NOT_ENCRYPTED == DataEncrypted);
//
// Data is not encrypted, store it encrypted now.
//
if (!SetRegistrySecureString(
hKey,
REGVAL_RECEIPTS_PASSWORD,
lpwstrPassword ? lpwstrPassword : EMPTY_STRING,
TRUE // Optionally non-encrypted
))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("SetRegistrySecureString failed : %ld"),
GetLastError());
bDeletePassword = TRUE;
}
}
if (TRUE == bDeletePassword)
{
//
// We do not want to leave clear text password in the registry
//
LONG lResult;
lResult = RegDeleteValue( hKey, REGVAL_RECEIPTS_PASSWORD);
if (ERROR_SUCCESS != lResult)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("RegDeleteValue failed : %ld"),
lResult);
}
}
exit:
RegCloseKey(hKey);
if (lpwstrPassword)
{
SecureZeroMemory(lpwstrPassword, wcslen(lpwstrPassword)*sizeof(WCHAR));
MemFree(lpwstrPassword);
}
return;
}
VOID
FaxServiceMain(
DWORD argc,
LPTSTR *argv
)
/*++
Routine Description:
This is the service main that is called by the
service controller.
Arguments:
argc - argument count
argv - argument array
Return Value:
None.
--*/
{
OPEN_DEBUG_FILE(SERVICE_DEBUG_LOG_FILE);
DEBUG_FUNCTION_NAME(TEXT("FaxServiceMain"));
DWORD dwRet;
HKEY hStartupKey = NULL; // Key to signal clients the service is running
HKEY hFaxRoot = NULL; // Key to root of fax prarmeters in the registry
#if DBG
for (int i = 1; i < argc; i++)
{
if (0 == _tcsicmp(argv[i], TEXT("/d")))
{
//
// We are in debug mode. - attach the debugger
//
DebugPrintEx(DEBUG_MSG,
TEXT("Entring debug mode..."));
DebugBreak();
}
}
#endif // #if DBG
//
// First of all make sure receipts password is stored encrypted
// Unattended installation stores the password as clear text.
//
EncryptReceiptsPassword();
//
// Becuase the process is not always terminated when the service is stopped,
// We must not have any staticly initialized global variables.
// Initialize all service global variables before starting the service
//
if (!InitializeServiceGlobals())
{
DebugPrintEx(DEBUG_ERR,
TEXT("InitializeServiceGlobals failed (ec: %ld)"),
GetLastError());
goto Exit;
}
if (!InitializeFaxLibrariesGlobals())
{
DebugPrintEx(DEBUG_ERR,
TEXT("InitializeFaxLibrariesGlobals failed"));
goto Exit;
}
hFaxRoot = OpenRegistryKey (HKEY_LOCAL_MACHINE,
REGKEY_FAXSERVER,
TRUE, // Create key if needed
KEY_READ);
if (hFaxRoot)
{
GetRegistryDwordEx (hFaxRoot, REGVAL_USE_DEFAULT_FAULT_HANDLING_POLICY, (LPDWORD)&gs_bUseDefaultFaultHandlingPolicy);
RegCloseKey (hFaxRoot);
}
//
// This will prevent the system exception dialog from showing up.
// This is required so we can support the SCM service-recovery feature
// to auto-recovery from unhandled exceptions.
//
SetUnhandledExceptionFilter (FaxUnhandledExceptionFilter);
for (int i = 1; i < argc; i++)
{
if (0 == _tcsicmp(argv[i], SERVICE_ALWAYS_RUNS))
{
//
// Service must never suicide
//
g_bServiceCanSuicide = FALSE;
DebugPrintEx(DEBUG_MSG,
TEXT("Command line detected. Service will not suicide"));
}
else if (0 == _tcsicmp(argv[i], SERVICE_DELAY_SUICIDE))
{
//
// Service should delay suicide
//
g_bDelaySuicideAttempt = TRUE;
DebugPrintEx(DEBUG_MSG,
TEXT("Command line detected. Service will delay suicide attempts"));
}
}
gs_FaxServiceStatusHandle = RegisterServiceCtrlHandler(
FAX_SERVICE_NAME,
FaxServiceCtrlHandler
);
if (!gs_FaxServiceStatusHandle)
{
DebugPrintEx(DEBUG_ERR,
TEXT("RegisterServiceCtrlHandler failed %d"),
GetLastError());
goto Exit;
}
//
// Open the HKLM\Software\Microsoft\Fax\Client\ServiceStartup key
// to tell clients the service is up.
//
hStartupKey = OpenRegistryKey (HKEY_LOCAL_MACHINE,
REGKEY_FAX_SERVICESTARTUP,
TRUE, // Create key if needed
KEY_READ | KEY_WRITE);
if (!hStartupKey)
{
DebugPrintEx(DEBUG_ERR,
TEXT("Can't open reg key (ec = %ld)"),
GetLastError() );
goto Exit;
}
//
// Initialize service
//
dwRet = ServiceStart();
if (ERROR_SUCCESS != dwRet)
{
//
// The service failed to start correctly
//
DebugPrintEx(DEBUG_ERR,
TEXT("The service failed to start correctly (dwRet = %ld)"), dwRet );
}
else
{
//
// mark the service in the running state
//
DebugPrintEx(
DEBUG_MSG,
TEXT("Reporting SERVICE_RUNNING to the SCM"));
ReportServiceStatus( SERVICE_RUNNING, 0, 0 );
//
// Notify all clients (on local machine) that the server is up and running.
// For security reasons, we do that by writing to the registry
// (HKLM\Software\Microsoft\Fax\Client\ServiceStartup\RPCReady).
// The client modules listen to registry changes in that key.
//
if (!SetRegistryDword (hStartupKey,
REGVAL_FAX_RPC_READY,
GetRegistryDword (hStartupKey, REGVAL_FAX_RPC_READY) + 1))
{
DebugPrintEx(DEBUG_ERR,
TEXT("Can't open reg key (ec = %ld)"),
GetLastError() );
}
//
// Wait for service shutdown event from SCM
//
DebugPrintEx(DEBUG_MSG,
TEXT("Waiting for service shutdown event"));
DWORD dwWaitRes = WaitForSingleObject (g_hSCMServiceShutDownEvent ,INFINITE);
if (WAIT_OBJECT_0 != dwWaitRes)
{
DebugPrintEx(DEBUG_ERR,
TEXT("WaitForSingleObject failed (ec = %ld)"), GetLastError() );
}
}
//
// Close service
//
if (ERROR_SUCCESS != dwRet)
{
EndFaxSvc(FAXLOG_LEVEL_MIN);
}
else
{
EndFaxSvc(FAXLOG_LEVEL_MAX);
}
Exit:
if (hStartupKey)
{
RegCloseKey (hStartupKey);
}
FreeServiceGlobals();
FreeFaxLibrariesGlobals();
ReportServiceStatus( SERVICE_STOPPED, 0 , 0);
return;
} // FaxServiceMain
BOOL SetServiceIsDownFlag(VOID)
{
/*++
Routine Description:
Sets the g_bServiceIsDown to true.
Done by a separate thread while reporting SERVICE_STOP_PENDING to SCM.
Arguments:
None.
Return Value:
TRUE on success.
FALSE on failure - use GetLastError() to get standard win32 error code.
--*/
HANDLE hThread = NULL;
DWORD ec = ERROR_SUCCESS;
DEBUG_FUNCTION_NAME(TEXT("SetServiceIsDownFlag"));
//
// Call SetServiceIsDownFlagThread() to set the g_bServiceIsDown flag
//
hThread = CreateThread( NULL,
0,
(LPTHREAD_START_ROUTINE) SetServiceIsDownFlagThread,
NULL,
0,
NULL
);
if (NULL == hThread)
{
ec = GetLastError();
DebugPrintEx( DEBUG_ERR,
_T("Failed to create SetServiceIsDownFlagThread (ec: %ld)."),
ec);
goto Exit;
}
if (!WaitAndReportForThreadToTerminate(hThread,TEXT("Waiting for SetServiceIsDownFlagThread to terminate.")))
{
ec = GetLastError();
DebugPrintEx( DEBUG_ERR,
_T("Failed to WaitAndReportForThreadToTerminate (ec: %ld)."),
ec);
goto Exit;
}
Assert(ec == ERROR_SUCCESS);
Exit:
if (NULL != hThread)
{
if (!CloseHandle(hThread))
{
DebugPrintEx( DEBUG_ERR,
_T("CloseHandle Failed (ec: %ld)."),
GetLastError());
}
}
if (ERROR_SUCCESS != ec)
{
SetLastError(ec);
}
return (ERROR_SUCCESS == ec);
}
DWORD SetServiceIsDownFlagThread(
LPVOID pvUnused
)
/*++
Routine Description:
Sets the g_bServiceIsDown to true.
Done by a separate thread in order for FaxServiceCtrlHandler to return immediately.
Arguments:
None.
Return Value:
Return code. Return zero for success.
--*/
{
DWORD dwWaitCount = 2; // We wait for TapiWorkerThread() and JobQueueThread()
DEBUG_FUNCTION_NAME(TEXT("SetServiceIsDownFlagThread"));
//
// First set the global flag that the service is going down
//
g_bServiceIsDown = TRUE;
//
// Update dwWaitCount to the correct value
//
if (NULL == g_hTapiWorkerThread)
{
DebugPrintEx(
DEBUG_WRN,
TEXT("TapiWorkerThread was not created. Do not wait for an event from TapiWorkerThread()"));
dwWaitCount -= 1;
}
if (NULL == g_hJobQueueThread)
{
DebugPrintEx(
DEBUG_WRN,
TEXT("g_hJobQueueThread was not created. Do not wait for an event from g_hJobQueueThread()"));
dwWaitCount -= 1;
}
//
// Wake up TapiWorkerThread and JobQueueThread, so they can read g_bServiceIsDown
//
if (NULL != g_hJobQueueEvent)
{
if (!SetEvent( g_hJobQueueEvent ))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Failed to set g_hJobQueueEvent. (ec: %ld)"),
GetLastError());
}
}
if (NULL != g_TapiCompletionPort)
{
if (!PostQueuedCompletionStatus( g_TapiCompletionPort,
0,
FAXDEV_EVENT_KEY,
(LPOVERLAPPED) NULL))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("PostQueuedCompletionStatus failed (FAXDEV_EVENT_KEY - TapiWorkerThread). (ec: %ld)"),
GetLastError());
}
}
DebugPrintEx(
DEBUG_MSG,
TEXT("SetServiceIsDownFlagThread waits for %d Thread(s)"),
dwWaitCount);
//
// Wait for a signal from TapiWorkerThread and JobQueueThread that the global flag g_bServiceIsDown was read by them
//
for (DWORD i = 0; i < dwWaitCount; i++)
{
DWORD dwWaitRes = WaitForSingleObject (g_hServiceIsDownSemaphore ,INFINITE);
if (WAIT_OBJECT_0 != dwWaitRes)
{
DebugPrintEx(DEBUG_ERR,
TEXT("WaitForSingleObject failed (ec = %ld)"), GetLastError() );
}
}
return (ERROR_SUCCESS);
}
VOID
FaxServiceCtrlHandler(
DWORD Opcode
)
/*++
Routine Description:
This is the FAX service control dispatch function.
Arguments:
Opcode - requested control code
Return Value:
None.
--*/
{
DEBUG_FUNCTION_NAME(TEXT("FaxServiceCtrlHandler"));
switch(Opcode)
{
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
ReportServiceStatus( SERVICE_STOP_PENDING, 0, 2000 );
if (!SetEvent(g_hSCMServiceShutDownEvent))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("SetEvent failed (g_hSCMServiceShutDownEvent) (ec = %ld)"),
GetLastError());
}
return;
default:
DebugPrintEx(
DEBUG_WRN,
TEXT("Unrecognized opcode %ld"),
Opcode);
break;
}
ReportServiceStatus( 0, 0, 0 );
return;
}
BOOL
ReportServiceStatus(
DWORD CurrentState,
DWORD Win32ExitCode,
DWORD WaitHint
)
/*++
Routine Description:
This function updates the service control manager's status information for the FAX service.
Arguments:
CurrentState - Indicates the current state of the service
Win32ExitCode - Specifies a Win32 error code that the service uses to
report an error that occurs when it is starting or stopping.
WaitHint - Specifies an estimate of the amount of time, in milliseconds,
that the service expects a pending start, stop, or continue
operation to take before the service makes its next call to the
SetServiceStatus function with either an incremented dwCheckPoint
value or a change in dwCurrentState.
Return Value:
BOOL
--*/
{
BOOL rVal;
if (CurrentState == SERVICE_START_PENDING)
{
gs_FaxServiceStatus.dwControlsAccepted = 0;
}
else
{
gs_FaxServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
}
if (CurrentState)
{
gs_FaxServiceStatus.dwCurrentState = CurrentState;
}
gs_FaxServiceStatus.dwWin32ExitCode = Win32ExitCode;
gs_FaxServiceStatus.dwWaitHint = WaitHint;
++(gs_FaxServiceStatus.dwCheckPoint);
//
// Report the status of the service to the service control manager.
//
rVal = SetServiceStatus( gs_FaxServiceStatusHandle, &gs_FaxServiceStatus );
if (!rVal)
{
DebugPrint(( TEXT("SetServiceStatus() failed: ec=%d"), GetLastError() ));
}
return rVal;
}
DWORD
RpcBindToFaxClient(
IN LPCWSTR ServerName,
IN LPCWSTR Endpoint,
OUT RPC_BINDING_HANDLE *pBindingHandle
)
/*++
Routine Description:
Binds to the Fax server to the Client RPC server if possible.
Arguments:
ServerName - Name of client RPC server to bind with.
Endpoint - Name of interface to bind with.
pBindingHandle - Location where binding handle is to be placed
Return Value:
STATUS_SUCCESS - The binding has been successfully completed.
STATUS_INVALID_COMPUTER_NAME - The ServerName syntax is invalid.
STATUS_NO_MEMORY - There is not sufficient memory available to the
caller to perform the binding.
--*/
{
RPC_STATUS RpcStatus;
LPWSTR StringBinding;
WCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
LPWSTR NewServerName = LOCAL_HOST_ADDRESS;
DWORD bufLen = MAX_COMPUTERNAME_LENGTH + 1;
DEBUG_FUNCTION_NAME(TEXT("RpcBindToFaxClient"));
*pBindingHandle = NULL;
if (ServerName != NULL)
{
if (GetComputerNameW(ComputerName,&bufLen))
{
if ((_wcsicmp(ComputerName,ServerName) == 0) ||
((ServerName[0] == '\\') &&
(ServerName[1] == '\\') &&
(_wcsicmp(ComputerName,&(ServerName[2]))==0)))
{
//
// We are binding to local machine - using LOCAL_HOST_ADDRESS (defined as _T("127.0.0.1")).
// Using this format we can help the RPC server to detect local calls.
//
NewServerName = LOCAL_HOST_ADDRESS;
}
else
{
NewServerName = (LPWSTR)ServerName;
}
}
else
{
DWORD ec = GetLastError();
DebugPrintEx(
DEBUG_ERR,
TEXT("GetComputerNameW failed (ec = %lu)"),
ec);
return ec;
}
}
RpcStatus = RpcStringBindingComposeW(0,
const_cast<LPTSTR>(RPC_PROT_SEQ_TCP_IP),
NewServerName,
(LPWSTR)Endpoint,
(LPWSTR)L"",
&StringBinding);
if ( RpcStatus != RPC_S_OK )
{
DebugPrintEx(
DEBUG_ERR,
TEXT("RpcStringBindingComposeW failed (ec = %ld)"),
RpcStatus);
return( STATUS_NO_MEMORY );
}
RpcStatus = RpcBindingFromStringBindingW(StringBinding, pBindingHandle);
RpcStringFreeW(&StringBinding);
if ( RpcStatus != RPC_S_OK )
{
DebugPrintEx(
DEBUG_ERR,
TEXT("RpcBindingFromStringBindingW failed (ec = %ld)"),
RpcStatus);
*pBindingHandle = NULL;
if ( (RpcStatus == RPC_S_INVALID_ENDPOINT_FORMAT)
|| (RpcStatus == RPC_S_INVALID_NET_ADDR) )
{
return( ERROR_INVALID_COMPUTERNAME );
}
return(STATUS_NO_MEMORY);
}
//
// Ask for the highest level of privacy (autnetication + encryption)
//
RPC_SECURITY_QOS rpcSecurityQOS = { RPC_C_SECURITY_QOS_VERSION,
RPC_C_QOS_CAPABILITIES_DEFAULT,
RPC_C_QOS_IDENTITY_STATIC,
RPC_C_IMP_LEVEL_IDENTIFY // Server can obtain information about
// client security identifiers and privileges,
// but cannot impersonate the client.
};
RpcStatus = RpcBindingSetAuthInfoEx (
*pBindingHandle, // RPC binding handle
TEXT(""), // Server principal name - ignored for RPC_C_AUTHN_WINNT
RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // Authentication level - fullest
// Authenticates, verifies, and privacy-encrypts the arguments passed
// to every remote call.
RPC_C_AUTHN_WINNT, // Authentication service (NTLMSSP)
NULL, // Authentication identity - use currently logged on user
0, // Unused when Authentication service == RPC_C_AUTHN_WINNT
&rpcSecurityQOS); // Defines the security quality-of-service
if (RPC_S_OK != RpcStatus)
{
//
// Couldn't set RPC authentication mode
//
DebugPrintEx(
DEBUG_ERR,
TEXT("RpcBindingSetAuthInfoEx (RPC_C_AUTHN_LEVEL_PKT_PRIVACY) failed. (ec: %ld)"),
RpcStatus);
RpcBindingFree (pBindingHandle);
*pBindingHandle = NULL;
return RpcStatus;
}
//
// Set time out on RPC calls to 30 sec, to avoid denial of service by malicious user.
//
RpcStatus = RpcBindingSetOption(
*pBindingHandle,
RPC_C_OPT_CALL_TIMEOUT,
30*1000);
if (RPC_S_OK != RpcStatus)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("RpcBindingSetOption failed (ec = %ld)"),
RpcStatus);
RpcBindingFree (pBindingHandle);
*pBindingHandle = NULL;
return RpcStatus;
}
return(ERROR_SUCCESS);
}
RPC_STATUS RPC_ENTRY FaxServerSecurityCallBack(
IN RPC_IF_HANDLE idIF,
IN void *ctx
)
/*++
Routine Description:
Security callback function is automatically called when
any RPC server function is called. (usually, once per client - but in some cases,
the RPC run time may call the security-callback function more than
once per client-per interface)
o The call-back will deny access for clients with authentication level below RPC_C_AUTHN_LEVEL_PRIVACY.
Arguments:
idIF - UUID and version of the interface.
ctx - Pointer to an RPC_IF_ID server binding handle representing the client.
Return Value:
The callback function should return RPC_S_OK if the client is allowed to call methods in this interface.
Any other return code will cause the client to receive the exception RPC_S_ACCESS_DENIED.
--*/
{
RPC_AUTHZ_HANDLE hPrivs;
DWORD dwAuthn;
BOOL fLocal;
BOOL fPrinterShared;
DWORD dwRes;
RPC_STATUS status;
RPC_STATUS rpcStatRet = ERROR_ACCESS_DENIED;
LPWSTR lpwstrProtSeq = NULL;
DEBUG_FUNCTION_NAME(TEXT("FaxServerSecurityCallBack"));
//
// Query the client's protseq
//
status = GetRpcStringBindingInfo(ctx,
NULL,
&lpwstrProtSeq);
if (status != RPC_S_OK)
{
DebugPrintEx(DEBUG_ERR,
TEXT("RpcBindingServerFromClient failed - (ec: %lu)"),
status);
goto error;
}
if (_tcsicmp(lpwstrProtSeq, RPC_PROT_SEQ_NP))
{
DebugPrintEx(DEBUG_ERR,
TEXT("Client not using named pipes protSeq.")
);
goto error;
}
//
// Query the client's authentication level
//
status = RpcBindingInqAuthClient(
ctx,
&hPrivs,
NULL,
&dwAuthn,
NULL,
NULL);
if (status != RPC_S_OK)
{
DebugPrintEx(DEBUG_ERR,
TEXT("RpcBindingInqAuthClient returned: 0x%x"),
status);
goto error;
}
//
// Now check the authentication level.
// We require at least packet-level privacy (RPC_C_AUTHN_LEVEL_PKT_PRIVACY) authentication.
//
if (dwAuthn < RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
{
DebugPrintEx(DEBUG_ERR,
TEXT("Attempt by client to use weak authentication. - 0x%x"),
dwAuthn);
goto error;
}
if (0 == g_dwAllowRemote)
{
//
// The administrator did not set the registry to always allow remote calls
// If the printer is not shared, block remote connections
//
dwRes = IsLocalFaxPrinterShared(&fPrinterShared);
if (ERROR_SUCCESS != dwRes)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("IsLocalFaxPrinterShared failed. - %ld"),
dwRes);
goto error;
}
if (FALSE == fPrinterShared)
{
status = IsLocalRPCConnectionNP(&fLocal);
if (RPC_S_OK != status)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("IsLocalRPCConnectionNP failed. - %ld"),
status);
goto error;
}
if (FALSE == fLocal)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("Printer is not shared, and a remote connection is done"));
goto error;
}
}
}
rpcStatRet = RPC_S_OK;
error:
if(NULL != lpwstrProtSeq)
{
MemFree(lpwstrProtSeq);
}
return rpcStatRet;
} // FaxServerSecurityCallBack
RPC_STATUS
AddFaxRpcInterface(
IN LPWSTR InterfaceName,
IN RPC_IF_HANDLE InterfaceSpecification
)
/*++
Routine Description:
Starts an RPC Server, adds the address (or port/pipe), and adds the
interface (dispatch table).
Arguments:
InterfaceName - points to the name of the interface.
InterfaceSpecification - Supplies the interface handle for the
interface which we wish to add.
Return Value:
NT_SUCCESS - Indicates the server was successfully started.
STATUS_NO_MEMORY - An attempt to allocate memory has failed.
Other - Status values that may be returned by:
RpcServerRegisterIfEx()
RpcServerUseProtseqEp()
, or any RPC error codes, or any windows error codes that
can be returned by LocalAlloc.
--*/
{
RPC_STATUS RpcStatus;
LPWSTR Endpoint = NULL;
DEBUG_FUNCTION_NAME(TEXT("AddFaxRpcInterface"));
// We need to concatenate \pipe\ to the front of the interface name.
Endpoint = (LPWSTR)LocalAlloc(LMEM_FIXED, sizeof(NT_PIPE_PREFIX) + WCSSIZE(InterfaceName));
if (Endpoint == 0)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("LocalAlloc failed"));
return(STATUS_NO_MEMORY);
}
wcscpy(Endpoint, NT_PIPE_PREFIX);
wcscat(Endpoint,InterfaceName);
RpcStatus = RpcServerUseProtseqEpW(const_cast<LPTSTR>(RPC_PROT_SEQ_NP), RPC_C_PROTSEQ_MAX_REQS_DEFAULT, Endpoint, NULL);
if (RpcStatus != RPC_S_OK)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("RpcServerUseProtseqEpW failed (ec = %ld)"),
RpcStatus);
goto CleanExit;
}
RpcStatus = RpcServerRegisterIfEx(InterfaceSpecification,
0,
0,
RPC_IF_ALLOW_SECURE_ONLY, // Limits connections to clients that use an authorization level higher than RPC_C_AUTHN_LEVEL_NONE
RPC_C_LISTEN_MAX_CALLS_DEFAULT, // Relieves the RPC run-time environment from enforcing an unnecessary restriction
FaxServerSecurityCallBack);
if (RpcStatus != RPC_S_OK)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("RpcServerRegisterIf failed (ec = %ld)"),
RpcStatus);
}
CleanExit:
if ( Endpoint != NULL )
{
LocalFree(Endpoint);
}
return RpcStatus;
}
RPC_STATUS
StartFaxRpcServer(
IN LPWSTR InterfaceName,
IN RPC_IF_HANDLE InterfaceSpecification
)
/*++
Routine Description:
Starts an RPC Server, adds the address (or port/pipe), and adds the
interface (dispatch table).
Arguments:
InterfaceName - points to the name of the interface.
InterfaceSpecification - Supplies the interface handle for the
interface which we wish to add.
Return Value:
NT_SUCCESS - Indicates the server was successfully started.
STATUS_NO_MEMORY - An attempt to allocate memory has failed.
Other - Status values that may be returned by:
RpcServerRegisterIf()
RpcServerUseProtseqEp()
, or any RPC error codes, or any windows error codes that
can be returned by LocalAlloc.
--*/
{
RPC_STATUS RpcStatus = RPC_S_OK;
DEBUG_FUNCTION_NAME(TEXT("StartFaxRpcServer"));
RpcStatus = AddFaxRpcInterface( InterfaceName,
InterfaceSpecification );
if ( RpcStatus != RPC_S_OK )
{
DebugPrintEx(
DEBUG_ERR,
TEXT("AddFaxRpcInterface failed (ec = %ld)"),
RpcStatus);
return RpcStatus;
}
if (FALSE == IsDesktopSKU())
{
//
// We are not running on DesktopSKU, so remote connection is enabled. RPC data can be passed on the wire
// We use NTLM authentication for privacy level RPC calls
//
RpcStatus = RpcServerRegisterAuthInfo (
RPC_SERVER_PRINCIPAL_NAME, // Igonred by RPC_C_AUTHN_WINNT
RPC_C_AUTHN_WINNT, // NTLM SPP authenticator
NULL, // Ignored when using RPC_C_AUTHN_WINNT
NULL); // Ignored when using RPC_C_AUTHN_WINNT
if (RpcStatus != RPC_S_OK)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("RpcServerRegisterAuthInfo() failed (ec: %ld)"),
RpcStatus);
RPC_STATUS RpcStatus2 = RpcServerUnregisterIf(InterfaceSpecification, 0, 1);
if (RpcStatus2 != RPC_S_OK)
{
//
// failed to unregister interface. don't propagate error
//
DebugPrintEx(
DEBUG_ERR,
TEXT("RpcServerUnregisterIf() failed (ec: %ld)"),
RpcStatus2);
}
return RpcStatus;
}
}
RpcStatus = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT , TRUE); // Do not wait
if ( RpcStatus != RPC_S_OK )
{
DebugPrintEx(
DEBUG_ERR,
TEXT("RpcServerListen failed (ec = %ld)"),
RpcStatus);
RPC_STATUS RpcStatus2 = RpcServerUnregisterIf(InterfaceSpecification, 0, 1);
if (RpcStatus2 != RPC_S_OK)
{
//
// failed to unregister interface. don't propagate error
//
DebugPrintEx(
DEBUG_ERR,
TEXT("RpcServerUnregisterIf() failed (ec: %ld)"),
RpcStatus2);
}
return RpcStatus;
}
return RpcStatus;
}
DWORD
StopFaxRpcServer(
VOID
)
/*++
Routine Description:
Stops the service RPC server.
Arguments:
Return Value:
--*/
{
RPC_STATUS RpcStatus = RPC_S_OK;
DEBUG_FUNCTION_NAME(TEXT("StopFaxRpcServer"));
DWORD dwRet = ERROR_SUCCESS;
RpcStatus = RpcMgmtStopServerListening(NULL);
if (RPC_S_OK != RpcStatus)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("RpcMgmtStopServerListening failed. (ec: %ld)"),
RpcStatus);
dwRet = RpcStatus;
}
//
// Wait for the RPC listening thread to return.
// The thread returns only when all RPC calls are terminated
//
if (NULL != g_hRPCListeningThread)
{
if (!WaitAndReportForThreadToTerminate( g_hRPCListeningThread,
TEXT("Waiting for RPC listning thread to terminate.")) )
{
DebugPrintEx(
DEBUG_ERR,
_T("WaitAndReportForThreadToTerminate failed (ec: %ld)"),
GetLastError());
}
if (!CloseHandle(g_hRPCListeningThread))
{
DebugPrintEx(
DEBUG_ERR,
TEXT("CloseHandle for g_hRPCListeningThread Failed (ec: %ld"),
GetLastError());
}
g_hRPCListeningThread = NULL;
}
RpcStatus = RpcServerUnregisterIfEx(
fax_ServerIfHandle, // Specifies the interface to remove from the registry
NULL, // remove the interface specified in the IfSpec parameter for all previously registered type UUIDs from the registry.
FALSE); // RPC run time will not call the rundown routines.
if (RPC_S_OK != RpcStatus)
{
DebugPrintEx(
DEBUG_ERR,
TEXT("RpcServerUnregisterIfEx failed. (ec: %ld)"),
RpcStatus);
}
return ((ERROR_SUCCESS == dwRet) ? RpcStatus : dwRet);
}