/*++ Copyright (c) 2000-2000 Microsoft Corporation Module Name: Init.c Abstract: This module implements Initialization routines the PGM Transport and other routines that are specific to the NT implementation of a driver. Author: Mohammad Shabbir Alam (MAlam) 3-30-2000 Revision History: --*/ #include "precomp.h" #include //******************* Pageable Routine Declarations **************** #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, InitPgm) #pragma alloc_text(PAGE, InitStaticPgmConfig) #pragma alloc_text(PAGE, InitDynamicPgmConfig) #pragma alloc_text(PAGE, PgmReadRegistryParameters) #pragma alloc_text(PAGE, AllocateInitialPgmStructures) #pragma alloc_text(PAGE, PgmCreateDevice) #pragma alloc_text(PAGE, PgmDereferenceDevice) #endif //******************* Pageable Routine Declarations **************** //---------------------------------------------------------------------------- NTSTATUS InitStaticPgmConfig( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: This routine initializes the static values used by Pgm Arguments: IN DriverObject - Pointer to driver object created by the system. IN RegistryPath - Pgm driver's registry location Return Value: NTSTATUS - Final status of the operation --*/ { NTSTATUS status; PAGED_CODE(); // // Initialize the Static Configuration data structure // PgmZeroMemory (&PgmStaticConfig, sizeof(tPGM_STATIC_CONFIG)); // // get the file system process since we need to know this for // allocating and freeing handles // PgmStaticConfig.FspProcess = PsGetCurrentProcess(); PgmStaticConfig.DriverObject = DriverObject; // save the driver object for event logging purposes // // save the registry path for later use (to read the registry) // PgmStaticConfig.RegistryPath.MaximumLength = (USHORT) RegistryPath->MaximumLength; if (PgmStaticConfig.RegistryPath.Buffer = PgmAllocMem (RegistryPath->MaximumLength, PGM_TAG('0'))) { RtlCopyUnicodeString(&PgmStaticConfig.RegistryPath, RegistryPath); } else { PgmLog (PGM_LOG_ERROR, DBG_INIT_PGM, "InitStaticPgmConfig", "INSUFFICIENT_RESOURCES <%d> bytes\n", PgmStaticConfig.RegistryPath.MaximumLength); return (STATUS_INSUFFICIENT_RESOURCES); } ExInitializeNPagedLookasideList(&PgmStaticConfig.TdiLookasideList, NULL, NULL, 0, sizeof (tTDI_SEND_CONTEXT), PGM_TAG('2'), TDI_LOOKASIDE_DEPTH); ExInitializeNPagedLookasideList(&PgmStaticConfig.DebugMessagesLookasideList, NULL, NULL, 0, (MAX_DEBUG_MESSAGE_LENGTH + 1), PGM_TAG('3'), DEBUG_MESSAGES_LOOKASIDE_DEPTH); status = FECInitGlobals (); if (!NT_SUCCESS (status)) { ExDeleteNPagedLookasideList (&PgmStaticConfig.DebugMessagesLookasideList); ExDeleteNPagedLookasideList (&PgmStaticConfig.TdiLookasideList); PgmFreeMem (PgmStaticConfig.RegistryPath.Buffer); } PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_INIT_PGM, "InitStaticPgmConfig", "FECInitGlobals returned <%x>\n", status); return (status); } //---------------------------------------------------------------------------- NTSTATUS InitDynamicPgmConfig( ) /*++ Routine Description: This routine initializes the dynamic values used by Pgm Arguments: Return Value: NTSTATUS - Final status of the operation --*/ { ULONG i; PAGED_CODE(); // // Initialize the Static Configuration data structure // PgmZeroMemory (&PgmDynamicConfig, sizeof(tPGM_DYNAMIC_CONFIG)); // // Initialize the list heads before doing anything else since // we can access them anytime later // InitializeListHead (&PgmDynamicConfig.SenderAddressHead); InitializeListHead (&PgmDynamicConfig.ReceiverAddressHead); InitializeListHead (&PgmDynamicConfig.CurrentReceivers); InitializeListHead (&PgmDynamicConfig.CleanedUpAddresses); InitializeListHead (&PgmDynamicConfig.ClosedConnections); InitializeListHead (&PgmDynamicConfig.ConnectionsCreated); InitializeListHead (&PgmDynamicConfig.CleanedUpConnections); InitializeListHead (&PgmDynamicConfig.LocalInterfacesList); InitializeListHead (&PgmDynamicConfig.WorkerQList); PgmDynamicConfig.ReceiversTimerTickCount = 1; // Init PgmDynamicConfig.SourcePort = (USHORT) GetRandomInteger (2000, 20000); #if DBG for (i=0; i\n", status); return (status); } // // Open the Pgm key. // RtlInitUnicodeString (&KeyName, ParametersString); InitializeObjectAttributes (&TmpObjectAttributes, &KeyName, // name OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, // attributes *pConfigHandle, // root NULL); // security descriptor status = ZwOpenKey (pParametersHandle, KEY_READ, &TmpObjectAttributes); if (!NT_SUCCESS(status)) { PgmLog (PGM_LOG_ERROR, DBG_INIT_PGM, "PgmOpenRegistryParameters", "ZwOpenKey returned <%x>\n", status); ZwClose(*pConfigHandle); } return (status); } //---------------------------------------------------------------------------- NTSTATUS ReadRegistryElement( IN HANDLE HandleToKey, IN PWSTR pwsValueName, OUT PUNICODE_STRING pucString ) /*++ Routine Description: This routine is will read a string value given by pwsValueName, under a given Key (which must be open) - given by HandleToKey. This routine allocates memory for the buffer in the returned pucString, so the caller must deallocate that. Arguments: pwsValueName- the name of the value to read (i.e. IPAddress) Return Value: pucString - the string returns the string read from the registry --*/ { ULONG BytesRead; NTSTATUS Status; UNICODE_STRING TempString; PKEY_VALUE_FULL_INFORMATION ReadValue = NULL; PAGED_CODE(); // // First, get the sizeof the string // RtlInitUnicodeString(&TempString, pwsValueName); // initilize the name of the value to read Status = ZwQueryValueKey (HandleToKey, &TempString, // string to retrieve KeyValueFullInformation, NULL, 0, &BytesRead); // get bytes to be read if (((!NT_SUCCESS (Status)) && (Status != STATUS_BUFFER_OVERFLOW) && (Status != STATUS_BUFFER_TOO_SMALL)) || (BytesRead == 0)) { return (STATUS_UNSUCCESSFUL); } if (ReadValue = (PKEY_VALUE_FULL_INFORMATION) PgmAllocMem (BytesRead, PGM_TAG('R'))) { Status = ZwQueryValueKey (HandleToKey, &TempString, // string to retrieve KeyValueFullInformation, (PVOID)ReadValue, // returned info BytesRead, &BytesRead); // # of bytes returned if ((NT_SUCCESS (Status)) && (ReadValue->DataLength)) { // move the read in data to the front of the buffer RtlMoveMemory ((PVOID) ReadValue, (((PUCHAR)ReadValue) + ReadValue->DataOffset), ReadValue->DataLength); RtlInitUnicodeString (pucString, (PWSTR) ReadValue); } else { PgmFreeMem (ReadValue); Status = STATUS_UNSUCCESSFUL; } } else { Status = STATUS_INSUFFICIENT_RESOURCES; } return(Status); } //---------------------------------------------------------------------------- NTSTATUS PgmReadRegistryParameters( IN PUNICODE_STRING RegistryPath, OUT tPGM_REGISTRY_CONFIG **ppPgmRegistryConfig ) /*++ Routine Description: This routine reads any required registry parameters Arguments: OUT ppPgmDynamic -- non-NULL only if we have any registry valuies to read Return Value: NTSTATUS - Final status of the operation --*/ { HANDLE PgmConfigHandle; HANDLE ParametersHandle; NTSTATUS status; tPGM_REGISTRY_CONFIG *pRegistryConfig; PAGED_CODE(); if (!(pRegistryConfig = PgmAllocMem (sizeof (tPGM_REGISTRY_CONFIG), PGM_TAG('0')))) { return (STATUS_INSUFFICIENT_RESOURCES); } status = PgmOpenRegistryParameters (RegistryPath, &PgmConfigHandle, &ParametersHandle); if (!NT_SUCCESS(status)) { PgmLog (PGM_LOG_ERROR, DBG_INIT_PGM, "PgmReadRegistryParameters", "ZwOpenKey returned <%x>\n", status); PgmFreeMem (pRegistryConfig); return STATUS_UNSUCCESSFUL; } // // zero out the Registry fields // PgmZeroMemory (pRegistryConfig, sizeof(tPGM_REGISTRY_CONFIG)); // // *************************************** // Now read all the registry needs we need // status = ReadRegistryElement (ParametersHandle, PARAM_SENDER_FILE_LOCATION, &pRegistryConfig->ucSenderFileLocation); if (NT_SUCCESS (status)) { pRegistryConfig->Flags |= PGM_REGISTRY_SENDER_FILE_SPECIFIED; } // // End of list of entries to be read // *************************************** // ZwClose(ParametersHandle); ZwClose(PgmConfigHandle); PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_INIT_PGM, "PgmReadRegistryParameters", "STATUS_SUCCESS\n"); *ppPgmRegistryConfig = pRegistryConfig; return (STATUS_SUCCESS); } //---------------------------------------------------------------------------- NTSTATUS AllocateInitialPgmStructures( ) /*++ Routine Description: This routine allocates any initial structures that may be required Arguments: Return Value: NTSTATUS - Final status of the operation --*/ { PAGED_CODE(); PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_INIT_PGM, "AllocateInitialPgmStructures", "STATUS_SUCCESS\n"); return (STATUS_SUCCESS); } //---------------------------------------------------------------------------- NTSTATUS PgmCreateDevice( ) /*++ Routine Description: This routine allocates the Pgm device for clients to call into the Pgm driver. Arguments: IN Return Value: NTSTATUS - Final status of the CreateDevice operation --*/ { NTSTATUS Status; tPGM_DEVICE *pPgmDevice = NULL; UNICODE_STRING ucPgmDeviceExportName; UNICODE_STRING ucProtocolNumber; WCHAR wcProtocolNumber[10]; USHORT PgmBindDeviceNameLength; PAGED_CODE(); RtlInitUnicodeString (&ucPgmDeviceExportName, WC_PGM_DEVICE_EXPORT_NAME); PgmBindDeviceNameLength = sizeof(DD_RAW_IP_DEVICE_NAME) + 10; Status = IoCreateDevice (PgmStaticConfig.DriverObject, // Driver Object sizeof(tPGM_DEVICE)+PgmBindDeviceNameLength, // Device Extension &ucPgmDeviceExportName, // Device Name FILE_DEVICE_NETWORK, // Device type 0x12 FILE_DEVICE_SECURE_OPEN, // Device Characteristics FALSE, // Exclusive &pPgmDeviceObject); if (!NT_SUCCESS (Status)) { PgmLog (PGM_LOG_ERROR, DBG_INIT_PGM, "PgmCreateDevice", "FAILed <%x> ExportDevice=%wZ\n", Status, &ucPgmDeviceExportName); pgPgmDevice = NULL; return Status; } pPgmDevice = (tPGM_DEVICE *) pPgmDeviceObject->DeviceExtension; // // zero out the DeviceExtension // PgmZeroMemory (pPgmDevice, sizeof(tPGM_DEVICE)+PgmBindDeviceNameLength); // put a verifier value into the structure so that we can check that // we are operating on the right data PgmInitLock (pPgmDevice, DEVICE_LOCK); pPgmDevice->Verify = PGM_VERIFY_DEVICE; PGM_REFERENCE_DEVICE (pPgmDevice, REF_DEV_CREATE, TRUE); pPgmDevice->pPgmDeviceObject = pPgmDeviceObject; // // Save the raw IP device name as a counted string. The device // name is followed by a path separator then the protocol number // of interest. // pPgmDevice->ucBindName.Buffer = (PWSTR) &pPgmDevice->BindNameBuffer; pPgmDevice->ucBindName.Length = 0; pPgmDevice->ucBindName.MaximumLength = PgmBindDeviceNameLength; RtlAppendUnicodeToString (&pPgmDevice->ucBindName, DD_RAW_IP_DEVICE_NAME); pPgmDevice->ucBindName.Buffer[pPgmDevice->ucBindName.Length / sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR; pPgmDevice->ucBindName.Length += sizeof(WCHAR); ucProtocolNumber.Buffer = wcProtocolNumber; ucProtocolNumber.MaximumLength = sizeof (wcProtocolNumber); RtlIntegerToUnicodeString ((ULONG) IPPROTO_RM, 10, &ucProtocolNumber); RtlAppendUnicodeStringToString (&pPgmDevice->ucBindName, &ucProtocolNumber); // // Initialize the event that will be used to signal the Device is ready to be deleted // KeInitializeEvent (&pPgmDevice->DeviceCleanedupEvent, NotificationEvent, FALSE); // // Now open a control channel on top of Ip // Status = PgmTdiOpenControl (pPgmDevice); if (!NT_SUCCESS (Status)) { PgmLog (PGM_LOG_ERROR, DBG_INIT_PGM, "PgmCreateDevice", "PgmTdiOpenControl FAILed <%x>\n", Status); IoDeleteDevice (pPgmDeviceObject); return (Status); } // increase the stack size of our device object, over that of the transport // so that clients create Irps large enough // to pass on to the transport below. // In theory, we should just add 1 here, to account for our presence in the // driver chain. // pPgmDeviceObject->StackSize = pPgmDevice->pControlDeviceObject->StackSize + 1; pPgmDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; pgPgmDevice = pPgmDevice; PgmLog (PGM_LOG_INFORM_STATUS, DBG_INIT_PGM, "PgmCreateDevice", "Status=<%x> ExportDevice=%wZ\n", Status, &ucPgmDeviceExportName); return (Status); } //---------------------------------------------------------------------------- VOID PgmDereferenceDevice( IN OUT tPGM_DEVICE **ppPgmDevice, IN ULONG RefContext ) /*++ Routine Description: This routine dereferences the RefCount on the Pgm device extension and deletes the device if the RefCount goes down to 0. Arguments: IN ppPgmDevice -- ptr to PgmDevice Extension IN RefContext -- the context for which this device extension was referenced earlier Return Value: NTSTATUS - Final status of the set event operation --*/ { tPGM_DEVICE *pPgmDevice = *ppPgmDevice; KAPC_STATE ApcState; BOOLEAN fAttached; PAGED_CODE(); ASSERT (PGM_VERIFY_HANDLE (pPgmDevice, PGM_VERIFY_DEVICE)); ASSERT (pPgmDevice->RefCount); // Check for too many derefs ASSERT (pPgmDevice->ReferenceContexts[RefContext]--); if (--pPgmDevice->RefCount) { return; } if (pPgmDevice->hControl) { // // This is only done at Load/Unload time, so we should // be currently in the System Process Context! // PgmAttachFsp (&ApcState, &fAttached, REF_FSP_DESTROY_DEVICE); ObDereferenceObject (pPgmDevice->pControlFileObject); ZwClose (pPgmDevice->hControl); pPgmDevice->pControlFileObject = NULL; pPgmDevice->hControl = NULL; PgmDetachFsp (&ApcState, &fAttached, REF_FSP_DESTROY_DEVICE); } PgmLog (PGM_LOG_INFORM_STATUS, DBG_INIT_PGM, "PgmDereferenceDevice", "Deleting pgPgmDevice=%x ...\n", pgPgmDevice); IoDeleteDevice (pPgmDevice->pPgmDeviceObject); *ppPgmDevice = NULL; return; } //---------------------------------------------------------------------------- NTSTATUS InitPgm( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: This routine is called at DriverEntry to initialize all the Pgm parameters Arguments: IN DriverObject - Pointer to driver object created by the system. IN RegistryPath - Pgm driver's registry location Return Value: NTSTATUS - Final status of the set event operation --*/ { NTSTATUS status; tPGM_REGISTRY_CONFIG *pPgmRegistry = NULL; PAGED_CODE(); status = InitStaticPgmConfig (DriverObject, RegistryPath); if (!NT_SUCCESS (status)) { PgmLog (PGM_LOG_ERROR, DBG_INIT_PGM, "InitPgm", "InitStaticPgmConfig returned <%x>\n", status); return (status); } //--------------------------------------------------------------------------------------- status = InitDynamicPgmConfig (); if (!NT_SUCCESS (status)) { PgmLog (PGM_LOG_ERROR, DBG_INIT_PGM, "InitPgm", "InitDynamicPgmConfig returned <%x>\n", status); CleanupInit (E_CLEANUP_STATIC_CONFIG); return (status); } //--------------------------------------------------------------------------------------- // // Read Registry configuration data // status = PgmReadRegistryParameters (RegistryPath, &pPgmRegistry); if (!NT_SUCCESS(status)) { // // There must have been some major problems with the registry read, so we will not load! // PgmLog (PGM_LOG_ERROR, DBG_INIT_PGM, "InitPgm", "FAILed to read registry, status = <%x>\n", status); CleanupInit (E_CLEANUP_DYNAMIC_CONFIG); return (status); } ASSERT (pPgmRegistry); pPgmRegistryConfig = pPgmRegistry; //--------------------------------------------------------------------------------------- // // Allocate the data structures we need at Init time // status = AllocateInitialPgmStructures (); if (!NT_SUCCESS(status)) { // // There must have been some major problems with the registry read, so we will not load! // PgmLog (PGM_LOG_ERROR, DBG_INIT_PGM, "InitPgm", "FAILed to allocate initial structures = <%x>\n", status); CleanupInit (E_CLEANUP_REGISTRY_PARAMETERS); return (status); } //--------------------------------------------------------------------------------------- // // Create the Pgm Device to be exported // status = PgmCreateDevice (); if (!NT_SUCCESS(status)) { // // There must have been some major problems with the registry read, so we will not load! // PgmLog (PGM_LOG_ERROR, DBG_INIT_PGM, "InitPgm", "FAILed to create PgmDevice, status=<%x>\n", status); CleanupInit (E_CLEANUP_STRUCTURES); return (status); } PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_INIT_PGM, "InitPgm", "SUCCEEDed!\n"); return (status); }