/*++ Copyright (c) 1989 Microsoft Corporation Module Name: dllutil.c Abstract: This module contains utility procedures for the Windows Client DLL Author: Steve Wood (stevewo) 8-Oct-1990 Revision History: --*/ #pragma warning(disable:4201) // nameless struct/union #include "csrdll.h" NTSTATUS CsrClientCallServer ( IN OUT PCSR_API_MSG m, IN OUT PCSR_CAPTURE_HEADER CaptureBuffer OPTIONAL, IN CSR_API_NUMBER ApiNumber, IN ULONG ArgLength ) /*++ Routine Description: This function sends an API request to the Windows Emulation Subsystem Server and waits for a reply. Arguments: m - Pointer to the API request message to send. CaptureBuffer - Optional pointer to a capture buffer located in the Port Memory section that contains additional data being sent to the server. Since Port Memory is also visible to the server, no data needs to be copied, but pointers to locations within the capture buffer need to be converted into pointers valid in the server's process context, since the server's view of the Port Memory is not at the same virtual address as the client's view. ApiNumber - Small integer that is the number of the API being called. ArgLength - Length, in bytes, of the argument portion located at the end of the request message. Used to calculate the length of the request message. Return Value: Status Code from either client or server --*/ { NTSTATUS Status; PULONG_PTR PointerOffsets; ULONG CountPointers; ULONG_PTR Pointer; // // Initialize the header of the message. // if ((LONG)ArgLength < 0) { ArgLength = (ULONG)(-(LONG)ArgLength); m->h.u2.s2.Type = 0; } else { m->h.u2.ZeroInit = 0; } ArgLength |= (ArgLength << 16); ArgLength += ((sizeof( CSR_API_MSG ) - sizeof( m->u )) << 16) | (FIELD_OFFSET( CSR_API_MSG, u ) - sizeof( m->h )); m->h.u1.Length = ArgLength; m->CaptureBuffer = NULL; m->ApiNumber = ApiNumber; // // if the caller is within the server process, do the API call directly // and skip the capture buffer fixups and LPC call. // if (CsrServerProcess == FALSE) { // // If the CaptureBuffer argument is present, then there is data located // in the Port Memory section that is being passed to the server. All // Port Memory pointers need to be converted so they are valid in the // Server's view of the Port Memory. // if (ARGUMENT_PRESENT (CaptureBuffer)) { // // Store a pointer to the capture buffer in the message that is // valid in the server process's context. // m->CaptureBuffer = (PCSR_CAPTURE_HEADER) ((PCHAR)CaptureBuffer + CsrPortMemoryRemoteDelta); // // Mark the fact that we are done allocating space from the end of // the capture buffer. // CaptureBuffer->FreeSpace = NULL; // // Loop over all of the pointers to Port Memory within the message // itself and convert them into server pointers. Also, convert // the pointers to pointers into offsets. // PointerOffsets = CaptureBuffer->MessagePointerOffsets; CountPointers = CaptureBuffer->CountMessagePointers; while (CountPointers--) { Pointer = *PointerOffsets++; if (Pointer != 0) { *(PULONG_PTR)Pointer += CsrPortMemoryRemoteDelta; PointerOffsets[ -1 ] = Pointer - (ULONG_PTR)m; } } } // // Send the request to the server and wait for a reply. // Status = NtRequestWaitReplyPort (CsrPortHandle, (PPORT_MESSAGE)m, (PPORT_MESSAGE)m); // // If the CaptureBuffer argument is present then reverse what we did // to the pointers above so that the client side code can use them // again. // if (ARGUMENT_PRESENT (CaptureBuffer)) { // // Convert the capture buffer pointer back to a client pointer. // m->CaptureBuffer = (PCSR_CAPTURE_HEADER) ((PCHAR)m->CaptureBuffer - CsrPortMemoryRemoteDelta); // // Loop over all of the pointers to Port Memory within the message // itself and convert them into client pointers. Also, convert // the offset pointers to pointers into back into pointers // PointerOffsets = CaptureBuffer->MessagePointerOffsets; CountPointers = CaptureBuffer->CountMessagePointers; while (CountPointers--) { Pointer = *PointerOffsets++; if (Pointer != 0) { Pointer += (ULONG_PTR)m; PointerOffsets[ -1 ] = Pointer; *(PULONG_PTR)Pointer -= CsrPortMemoryRemoteDelta; } } } // // Check for failed status and do something. // if (!NT_SUCCESS (Status)) { #if DBG if (Status != STATUS_PORT_DISCONNECTED && Status != STATUS_INVALID_HANDLE) { DbgPrint( "CSRDLL: NtRequestWaitReplyPort failed - Status == %X\n", Status); } #endif m->ReturnValue = Status; } } else { m->h.ClientId = NtCurrentTeb()->ClientId; Status = (CsrServerApiRoutine) ((PCSR_API_MSG)m, (PCSR_API_MSG)m); // // Check for failed status and do something. // if (!NT_SUCCESS( Status )) { #if DBG DbgPrint( "CSRDLL: Server side client call failed - Status == %X\n", Status); #endif m->ReturnValue = Status; } } // // The value of this function is whatever the server function returned. // return m->ReturnValue; } HANDLE CsrGetProcessId ( VOID ) /*++ Routine Description: This function gets the process ID of the CSR process (for the session) Arguments: None Return Value: Process ID of CSR --*/ { return CsrProcessId; } PCSR_CAPTURE_HEADER CsrAllocateCaptureBuffer ( IN ULONG CountMessagePointers, IN ULONG Size ) /*++ Routine Description: This function allocates a buffer from the Port Memory section for use by the client in capture arguments into Port Memory. In addition to specifying the size of the data that needs to be captured, the caller needs to specify how many pointers to captured data will be passed. Pointers can be located in either the request message itself, and/or the capture buffer. Arguments: CountMessagePointers - Number of pointers within the request message that will point to locations within the allocated capture buffer. Size - Total size of the data that will be captured into the capture buffer. Return Value: A pointer to the capture buffer header. --*/ { ULONG CountPointers, SizePointers; PCSR_CAPTURE_HEADER CaptureBuffer; ULONG RemainingSize; // // Calculate the total number of pointers that will be passed // CountPointers = CountMessagePointers; // // Calculate the total size of the capture buffer. This includes the // header, the array of pointer offsets and the data length. We round // the data length to a 32-bit boundary, assuming that each pointer // points to data whose length is not aligned on a 32-bit boundary. // RemainingSize = (MAXLONG & ~0x3) - FIELD_OFFSET(CSR_CAPTURE_HEADER, MessagePointerOffsets); // // Bail early if too big. // if ((Size >= RemainingSize) || (CountPointers > (MAXLONG/sizeof(PVOID))) ) { return NULL; } RemainingSize -= Size; SizePointers = CountPointers * sizeof(PVOID); if (SizePointers >= RemainingSize) { return NULL; } RemainingSize -= SizePointers; if ((CountPointers+1) >= (RemainingSize/3) ) { return NULL; } Size += FIELD_OFFSET(CSR_CAPTURE_HEADER, MessagePointerOffsets) + SizePointers; Size = (Size + (3 * (CountPointers+1))) & ~3; // // Allocate the capture buffer from the Port Memory Heap. // CaptureBuffer = RtlAllocateHeap (CsrPortHeap, MAKE_CSRPORT_TAG( CAPTURE_TAG ), Size); if (CaptureBuffer == NULL) { // // FIX, FIX - need to attempt the receive lost reply messages to // to see if they contain CaptureBuffer pointers that can be freed. // return NULL; } // // Initialize the capture buffer header // CaptureBuffer->Length = Size; CaptureBuffer->CountMessagePointers = 0; // // If there are pointers being passed then initialize the arrays of // pointer offsets to zero. In either case set the free space pointer // in the capture buffer header to point to the first 32-bit aligned // location after the header, the arrays of pointer offsets are considered // part of the header. // RtlZeroMemory (CaptureBuffer->MessagePointerOffsets, CountPointers * sizeof (ULONG_PTR)); CaptureBuffer->FreeSpace = (PCHAR) (CaptureBuffer->MessagePointerOffsets + CountPointers); // // Return the address of the capture buffer. // return CaptureBuffer; } VOID CsrFreeCaptureBuffer ( IN PCSR_CAPTURE_HEADER CaptureBuffer ) /*++ Routine Description: This function frees a capture buffer allocated by CsrAllocateCaptureBuffer. Arguments: CaptureBuffer - Pointer to a capture buffer allocated by CsrAllocateCaptureBuffer. Return Value: None. --*/ { // // Free the capture buffer back to the Port Memory heap. // RtlFreeHeap (CsrPortHeap, 0, CaptureBuffer); } ULONG CsrAllocateMessagePointer ( IN OUT PCSR_CAPTURE_HEADER CaptureBuffer, IN ULONG Length, OUT PVOID *Pointer ) /*++ Routine Description: This function allocates space from the capture buffer along with a pointer to point to it. The pointer is presumed to be located in the request message structure. Arguments: CaptureBuffer - Pointer to a capture buffer allocated by CsrAllocateCaptureBuffer. Length - Size of data being allocated from the capture buffer. Pointer - Address of the pointer within the request message that is to point to the space allocated out of the capture buffer. Return Value: The actual length of the buffer allocated, after it has been rounded up to a multiple of 4. --*/ { if (Length == 0) { *Pointer = NULL; Pointer = NULL; } else { // // Set the returned pointer value to point to the next free byte in // the capture buffer. // *Pointer = CaptureBuffer->FreeSpace; // // Round the length up to a multiple of 4 // if (Length >= MAXLONG) { // // Bail early if too big // return 0; } Length = (Length + 3) & ~3; // // Update the free space pointer to point to the next available byte // in the capture buffer. // CaptureBuffer->FreeSpace += Length; } // // Remember the location of this pointer so that CsrClientCallServer can // convert it into a server pointer prior to sending the request to // the server. // CaptureBuffer->MessagePointerOffsets[ CaptureBuffer->CountMessagePointers++ ] = (ULONG_PTR)Pointer; // // Return the actual length allocated. // return Length; } VOID CsrCaptureMessageBuffer ( IN OUT PCSR_CAPTURE_HEADER CaptureBuffer, IN PVOID Buffer OPTIONAL, IN ULONG Length, OUT PVOID *CapturedBuffer ) /*++ Routine Description: This function captures a buffer of bytes in an API request message. Arguments: CaptureBuffer - Pointer to a capture buffer allocated by CsrAllocateCaptureBuffer. Buffer - Optional pointer to the buffer. If this parameter is not present, then no data is copied into capture buffer. Length - Length of the buffer. CapturedBuffer - Pointer to the field in the message that will be filled in to point to the capture buffer. Return Value: None. --*/ { // // Set the length fields of the captured string structure and allocated // the Length for the string from the capture buffer. // CsrAllocateMessagePointer (CaptureBuffer, Length, CapturedBuffer); // // If Buffer parameter is not present or the length of the data is zero, // return. // if (!ARGUMENT_PRESENT( Buffer ) || (Length == 0)) { return; } // // Copy the buffer data to the capture area. // RtlMoveMemory (*CapturedBuffer, Buffer, Length); return; } VOID CsrCaptureMessageString ( IN OUT PCSR_CAPTURE_HEADER CaptureBuffer, IN PCSTR String OPTIONAL, IN ULONG Length, IN ULONG MaximumLength, OUT PSTRING CapturedString ) /*++ Routine Description: This function captures an ASCII string into a counted string data structure located in an API request message. Arguments: CaptureBuffer - Pointer to a capture buffer allocated by CsrAllocateCaptureBuffer. String - Optional pointer to the ASCII string. If this parameter is not present, then the counted string data structure is set to the null string. Length - Length of the ASCII string, ignored if String is NULL. MaximumLength - Maximum length of the string. Different for null terminated strings, where Length does not include the null and MaximumLength does. This is always how much space is allocated from the capture buffer. CaptureString - Pointer to the counted string data structure that will be filled in to point to the captured ASCII string. Return Value: None. --*/ { ASSERT(CapturedString != NULL); // // If String parameter is not present, then set the captured string // to be the null string and return. // if (!ARGUMENT_PRESENT( String )) { CapturedString->Length = 0; CapturedString->MaximumLength = (USHORT)MaximumLength; CsrAllocateMessagePointer( CaptureBuffer, MaximumLength, (PVOID *)&CapturedString->Buffer ); // // Make it NULL terminated if there is any room. // if (MaximumLength != 0) { CapturedString->Buffer[0] = 0; } return; } // // Set the length fields of the captured string structure and allocated // the MaximumLength for the string from the capture buffer. // CapturedString->Length = (USHORT)Length; CapturedString->MaximumLength = (USHORT) CsrAllocateMessagePointer( CaptureBuffer, MaximumLength, (PVOID *)&CapturedString->Buffer ); // // If the Length of the ASCII string is non-zero then move it to the // capture area. // if (Length != 0) { RtlMoveMemory (CapturedString->Buffer, String, MaximumLength ); } if (CapturedString->Length < CapturedString->MaximumLength) { CapturedString->Buffer[ CapturedString->Length ] = '\0'; } return; } VOID CsrCaptureMessageUnicodeStringInPlace ( IN OUT PCSR_CAPTURE_HEADER CaptureBuffer, IN OUT PUNICODE_STRING String ) /*++ Routine Description: This function captures an ASCII string into a counted string data structure located in an API request message. Arguments: CaptureBuffer - Pointer to a capture buffer allocated by CsrAllocateCaptureBuffer. String - Optional pointer to the Unicode string. If this parameter is not present, then the counted string data structure is set to the null string. Length - Length of the Unicode string in bytes, ignored if String is NULL. MaximumLength - Maximum length of the string. Different for null terminated strings, where Length does not include the null and MaximumLength does. This is always how much space is allocated from the capture buffer. CaptureString - Pointer to the counted string data structure that will be filled in to point to the captured Unicode string. Return Value: None, but if you don't trust the String parameter, use a __try block. --*/ { ASSERT(String != NULL); CsrCaptureMessageString (CaptureBuffer, (PCSTR)String->Buffer, String->Length, String->MaximumLength, (PSTRING)String); // test > before substraction due to unsignedness if (String->MaximumLength > String->Length) { if ((String->MaximumLength - String->Length) >= sizeof(WCHAR)) { String->Buffer[ String->Length / sizeof(WCHAR) ] = 0; } } } NTSTATUS CsrCaptureMessageMultiUnicodeStringsInPlace ( IN OUT PCSR_CAPTURE_HEADER* InOutCaptureBuffer, IN ULONG NumberOfStringsToCapture, IN const PUNICODE_STRING* StringsToCapture ) /*++ Routine Description: Capture multiple unicode strings. If the CaptureBuffer hasn't been allocated yet (passed as NULL), first allocate it. Arguments: CaptureBuffer - Pointer to a capture buffer allocated by CsrAllocateCaptureBuffer, or NULL, in which case we call CsrAllocateCaptureBuffer for you; this is the case if you are only capturing these strings and nothing else. NumberOfStringsToCapture - StringsToCapture - Return Value: NTSTATUS --*/ { ULONG Length = 0; ULONG i = 0; PCSR_CAPTURE_HEADER CaptureBuffer = NULL; if (InOutCaptureBuffer == NULL) { return STATUS_INVALID_PARAMETER; } CaptureBuffer = *InOutCaptureBuffer; if (CaptureBuffer == NULL) { Length = 0; for (i = 0 ; i != NumberOfStringsToCapture ; ++i) { if (StringsToCapture[i] != NULL) { Length += StringsToCapture[i]->MaximumLength; } } CaptureBuffer = CsrAllocateCaptureBuffer(NumberOfStringsToCapture, Length); if (CaptureBuffer == NULL) { return STATUS_NO_MEMORY; } *InOutCaptureBuffer = CaptureBuffer; } for (i = 0 ; i != NumberOfStringsToCapture ; i += 1) { if (StringsToCapture[i] != NULL) { CsrCaptureMessageUnicodeStringInPlace (CaptureBuffer, StringsToCapture[i]); } } return STATUS_SUCCESS; } PLARGE_INTEGER CsrCaptureTimeout ( IN ULONG MilliSeconds, OUT PLARGE_INTEGER Timeout ) { if (MilliSeconds == -1) { return NULL; } Timeout->QuadPart = Int32x32To64( MilliSeconds, -10000 ); return (PLARGE_INTEGER)Timeout; } VOID CsrProbeForWrite ( IN PVOID Address, IN ULONG Length, IN ULONG Alignment ) /*++ Routine Description: This function probes a structure for read accessibility. If the structure is not accessible, then an exception is raised. Arguments: Address - Supplies a pointer to the structure to be probed. Length - Supplies the length of the structure. Alignment - Supplies the required alignment of the structure expressed as the number of bytes in the primitive datatype (e.g., 1 for char, 2 for short, 4 for long, and 8 for quad). Return Value: None. --*/ { CHAR Temp; volatile CHAR *StartAddress; volatile CHAR *EndAddress; // // If the structure has zero length, then do not probe the structure for // write accessibility or alignment. // if (Length != 0) { // // If the structure is not properly aligned, then raise a data // misalignment exception. // ASSERT((Alignment == 1) || (Alignment == 2) || (Alignment == 4) || (Alignment == 8)); StartAddress = (volatile CHAR *)Address; if (((ULONG_PTR)StartAddress & (Alignment - 1)) != 0) { RtlRaiseStatus(STATUS_DATATYPE_MISALIGNMENT); } else { Temp = *StartAddress; *StartAddress = Temp; EndAddress = StartAddress + Length - 1; Temp = *EndAddress; *EndAddress = Temp; } } } VOID CsrProbeForRead ( IN PVOID Address, IN ULONG Length, IN ULONG Alignment ) /*++ Routine Description: This function probes a structure for read accessibility. If the structure is not accessible, then an exception is raised. Arguments: Address - Supplies a pointer to the structure to be probed. Length - Supplies the length of the structure. Alignment - Supplies the required alignment of the structure expressed as the number of bytes in the primitive datatype (e.g., 1 for char, 2 for short, 4 for long, and 8 for quad). Return Value: None. --*/ { CHAR Temp; volatile CHAR *StartAddress; volatile CHAR *EndAddress; // // If the structure has zero length, then do not probe the structure for // read accessibility or alignment. // if (Length != 0) { // // If the structure is not properly aligned, then raise a data // misalignment exception. // ASSERT((Alignment == 1) || (Alignment == 2) || (Alignment == 4) || (Alignment == 8)); StartAddress = (volatile CHAR *)Address; if (((ULONG_PTR)StartAddress & (Alignment - 1)) != 0) { RtlRaiseStatus(STATUS_DATATYPE_MISALIGNMENT); } else { Temp = *StartAddress; EndAddress = StartAddress + Length - 1; Temp = *EndAddress; } } }