/*++ Copyright (c) 1987-1993 Microsoft Corporation Module Name: ConvBlk.c Abstract: This module contains RxpConvertBlock, a support routine for RxRemoteApi. Author: John Rogers (JohnRo) 01-Apr-1991 (Created portable LanMan (NT) version from LanMan 2.0) Environment: Portable to any flat, 32-bit environment. (Uses Win32 typedefs.) Requires ANSI C extensions: slash-slash comments, long external names. Revision History: 01-Apr-1991 JohnRo Converted from LanMan 2.x sources. 03-May-1991 JohnRo Handle enum (array) correctly. Get 32-bit data desc and convert to receive buffer with it. Handle receive word correctly. Lots of cleanup to comments. RcvDataPointer and RcvDataPresent are redundant. Fixed receive buffer length problem. Added (quiet) debug output. Reduced recompile hits from header files. 09-May-1991 JohnRo Made changes to reflect CliffV's code review. 11-May-1991 JohnRo Convert pointers, and then tell convert single entry that input pointers are OK. Also, let's treat Converter as DWORD locally. Force SmbGetUshort to get a word instead of a single byte. 14-May-1991 JohnRo Pass 2 aux descriptors to RxpConvertBlock. Added debug print of NumStruct as it changes. Use FORMAT_LPVOID instead of FORMAT_POINTER (max portability). 15-May-1991 JohnRo Added various "native" flags. 17-May-1991 JohnRo Handle array of aux structs. 20-May-1991 JohnRo Make data descriptors OPTIONAL for RxpConvertBlock. 11-Jun-1991 rfirth Added extra parameter: SmbRcvByteLen which specifies the amount of bytes in SmbRcvBuffer 14-Jun-1991 JohnRo Got rid of extraneous debug hex dump of buffer at end. Use NetpDbgReasonable(). 15-Jul-1991 JohnRo Changed RxpConvertDataStructures to allow ERROR_MORE_DATA, e.g. for print APIs. Also got rid of a few unreferenced local variables. 17-Jul-1991 JohnRo Extracted RxpDebug.h from Rxp.h. 01-Aug-1991 RFirth Removed #if 0 block and variables which were put into convdata.c (RxpConvertDataStructures) 19-Aug-1991 rfirth Added Flags parameter and support for ALLOCATE_RESPONSE flag 26-Aug-1991 JohnRo Minor changes suggested by PC-LINT. 20-Sep-1991 JohnRo Downlevel NetService APIs. (Make sure *RcvDataBuffer gets set if ALLOCATE_RESPONSE is passed and *SmbRcvByteLen==0.) 21-Nov-1991 JohnRo Removed NT dependencies to reduce recompiles. 04-Nov-1992 JohnRo RAID 9355: Event viewer: won't focus on LM UNIX machine. (Added REM_DATA_BLOCK support for error log return data.) Use PREFIX_ equates. 04-May-1993 JohnRo RAID 6167: avoid access violation or assert with WFW print server. Use NetpKdPrint() where possible. 18-May-1993 JohnRo DosPrintQGetInfoW underestimates number of bytes needed. --*/ // These must be included first: #include // IN, OUT, DWORD, LPBYTE, etc. #include // NET_API_STATUS. // These may be included in any order: #include // ALIGN_WORST #include // API_ equates. #include // CHAR_BIT. #include // NetapipBufferAllocate, NetApiBufferFree #include // NERR_ and ERROR_ equates. #include // NetpAssert(), NetpDbg routines, FORMAT_ equates. #include // NetpMoveMemory(), etc. #include // PREFIX_ equates. #include // REM_BYTE, etc. #include // LPDESC, RapConvertSingleEntry(), etc. #include // Flags parameter definitions #include // My prototype. #include // IF_DEBUG(). #define DESC_IS_UNSTRUCTURED( descPtr ) \ ( ( (descPtr)==NULL) || ( (*(descPtr)) == REM_DATA_BLOCK ) ) NET_API_STATUS RxpConvertBlock( IN DWORD ApiNumber, IN LPBYTE ResponseBlockPointer, IN LPDESC ParmDescriptorString, IN LPDESC DataDescriptor16 OPTIONAL, IN LPDESC DataDescriptor32 OPTIONAL, IN LPDESC AuxDesc16 OPTIONAL, IN LPDESC AuxDesc32 OPTIONAL, IN va_list *FirstArgumentPointer, // rest of API's arguments IN LPBYTE SmbRcvBuffer OPTIONAL, IN DWORD SmbRcvByteLen, OUT LPBYTE RcvDataBuffer OPTIONAL, IN DWORD RcvDataLength, IN DWORD Flags ) /*++ Routine Description: RxpConvertBlock translates the remote response (of a remoted API) into the local equivalent. This involves converting the response (which is in the form of 16-bit data in the transaction response buffer) to local data formats, and setting them in the argument list. Arguments: ApiNumber - Function number of the API required. ResponseBlockPointer - Points to the transaction SMB response block. ParmDescriptorString - A pointer to a ASCIIZ string describing the API call parameters (other than server name). DataDescriptor16 - A pointer to a ASCIIZ string describing the structure of the data in the call, i.e. the return data structure for a Enum or GetInfo call. This string is used for adjusting pointers to data in the local buffers after transfer across the net. If there is no structure involved in the call then DataDescriptor16 must be a null pointer. DataDescriptor32 - An optional pointer to a ASCIIZ string describing the 32-bit structure of the return data structure. AuxDesc16, AuxDesc32 - Will be NULL in most cases unless a REM_AUX_COUNT descriptor char is present in DataDescriptor16 in which case the aux descriptors define a secondary data format as DataDescriptor16 defines the primary. FirstArgumentPointer - Points to the va_list (variable arguments list) containing the API arguments (after the server name). The caller must call va_start and va_end. SmbRcvBuffer - Optionally points to 16-bit format receive data buffer. SmbRcvByteLen - the number of bytes contained in SmbRcvBuffer (if not NULL) RcvDataBuffer - Points to the data area for the received data. For instance, this may be a server info structure from NetServerGetInfo. This pointer will be NULL for many APIs. If (Flags & ALLOCATE_RESPONSE) then this pointer actually points to the pointer to the eventual buffer. We allocate a buffer in this routine and set *RcvDataBuffer to it. If we fail to get the buffer then *RcvDataBuffer will be set to NULL RcvDataLength - Length of the data area that RcvDataBuffer points to. If (Flags & ALLOCATE_RESPONSE) then this value will be the size that the caller of RxRemoteApi originally decided that the down-level server should use and incidentally was the original size of SmbRcvBuffer Flags - bit-mapped flags word. Currently defined flags are: NO_PERMISSION_REQUIRED - used by RxpTransactSmb to determine whether a NULL session may be used ALLOCATE_RESPONSE - used by this routine to allocate the final 32-bit response data buffer based on the size of the SMB data received, multiplied by the RAP_CONVERSION_FACTOR Return Value: NET_API_STATUS - return value from remote API. --*/ { DWORD Converter; // For pointer fixups. LPBYTE CurrentBlockPointer; LPDWORD EntriesReadPtr = NULL; DWORD NumStruct; // Loop count for ptr fixup. va_list ParmPtr; LPBYTE pDataBuffer = NULL; // pointer to returned data NET_API_STATUS Status; // Return status from remote. DWORD TempLength; // General purpose length. #if DBG // // Code in this file depends on 16-bit words; the Remote Admin Protocol // demands it. // NetpAssert( ( (sizeof(WORD)) * CHAR_BIT) == 16); if (DataDescriptor16 != NULL) { NetpAssert(DataDescriptor32 != NULL); } else { NetpAssert(DataDescriptor32 == NULL); } if (AuxDesc16 != NULL) { NetpAssert(AuxDesc32 != NULL); } else { NetpAssert(AuxDesc32 == NULL); } #endif ParmPtr = *FirstArgumentPointer; // The API call was successful. Now translate the return buffers // into the local API format. // // First copy any data from the return parameter buffer into the // fields pointed to by the original call parameters. // The return parameter buffer contains; // Status, (16 bits) // Converter, (16 bits) // ... fields described by rcv ptr types in // ParmDescriptorString CurrentBlockPointer = ResponseBlockPointer; Status = (NET_API_STATUS) SmbGetUshort( (LPWORD) CurrentBlockPointer ); CurrentBlockPointer += sizeof(WORD); Converter = ((DWORD) SmbGetUshort( (LPWORD) CurrentBlockPointer )) & 0xffff; IF_DEBUG(CONVBLK) { NetpKdPrint(( PREFIX_NETAPI "RxpConvertBlock: Converter=" FORMAT_HEX_DWORD ".\n", Converter )); } CurrentBlockPointer += sizeof(WORD); // Set default value of NumStruct to 1, if data, 0 if no data. if ( (DataDescriptor16 != NULL) && (*DataDescriptor16 != '\0') ) { NumStruct = 1; } else { NumStruct = 0; } for( ; *ParmDescriptorString != '\0'; ParmDescriptorString++) { IF_DEBUG(CONVBLK) { NetpKdPrint(( PREFIX_NETAPI "RxpConvertBlock: *parm='" FORMAT_LPDESC_CHAR "', ParmPtr is:\n", *ParmDescriptorString )); NetpDbgHexDump((LPVOID) & ParmPtr, sizeof(va_list)); } switch( *ParmDescriptorString) { case REM_WORD : // Word in old APIs (DWORD in 32-bit). case REM_DWORD : // DWord. (void) va_arg(ParmPtr, DWORD); // Step over this arg. break; case REM_ASCIZ : (void) va_arg(ParmPtr, LPSTR); // Step over this arg. break; case REM_BYTE_PTR : (void) va_arg(ParmPtr, LPBYTE); // Step over this arg. (void) RapArrayLength( ParmDescriptorString, &ParmDescriptorString, Response); break; case REM_WORD_PTR : // (WORD *) in old APIs. case REM_DWORD_PTR : // (DWORD *) (void) va_arg(ParmPtr, LPDWORD); // Step over this arg. break; case REM_RCV_WORD_PTR : // pointer to rcv word(s) (DWORD in 32-bit) { LPDWORD Temp; DWORD ArrayCount; Temp = va_arg(ParmPtr, LPDWORD); ++ParmDescriptorString; // point to first (possible) digit... ArrayCount = RapDescArrayLength( ParmDescriptorString); // updated past last. --ParmDescriptorString; // point back at last digit for loop. IF_DEBUG(CONVBLK) { NetpKdPrint(( PREFIX_NETAPI "RxpConvertBlock: rcv.word.ptr, temp=" FORMAT_LPVOID ", ArrayCount=" FORMAT_DWORD ".\n", (LPVOID) Temp, ArrayCount )); } // if the rcv buffer given to us by the user is NULL, // (one currently can be - it is an MBZ parameter for // now in the log read apis...), don't attempt to // copy anything. TempLength will be garbage in this // case, so don't update CurrentBlockPointer either. All we // use RapArrayLength for is to update ParmDescriptorString if // the parameter was NULL. if ( Temp == NULL ) { ; /* NULLBODY */ } else { // Copy one or more words (expanding to DWORDS as we go). DWORD WordsLeft = ArrayCount; do { DWORD Data; // Convert byte order if necessary, and expand. Data = (DWORD) SmbGetUshort( (LPWORD) CurrentBlockPointer ); *Temp = Data; Temp += sizeof(DWORD); --WordsLeft; } while (WordsLeft > 0); // This gross hack is to fix the problem that a // down level spooler (Lan Server 1.2) // do not perform level checking // on the w functions of the api(s): // DosPrintQGetInfo // and thus can return NERR_Success // and bytesavail == 0. This combination // is technically illegal, and results in // us attempting to unpack a buffer full of // garbage. The following code detects this // condition and resets the amount of returned // data to zero so we do not attempt to unpack // the buffer. Since we know the reason for the // mistake at the server end is that we passed // them a new level, we return ERROR_INVALID_LEVEL // in this case. // ERICPE, 5/16/90. if ((ApiNumber == API_WPrintQGetInfo) && (Status == NERR_Success) && (*(LPWORD)CurrentBlockPointer == 0) && (*ParmDescriptorString == REM_RCV_WORD_PTR)) { Status = ERROR_INVALID_LEVEL; goto ErrorExit; } // END OF GROSS HACK CurrentBlockPointer += (ArrayCount * sizeof(WORD)); } break; } case REM_RCV_BYTE_PTR : // pointer to rcv byte(s) { LPBYTE Temp; Temp = va_arg(ParmPtr, LPBYTE); TempLength = RapArrayLength( ParmDescriptorString, &ParmDescriptorString, Response); // if the rcv buffer given to us by the user is NULL, // (one currently can be - it is an MBZ parameter for // now in the log read apis...), don't attempt to // copy anything. TempLength will be garbage in this // case, so don't update CurrentBlockPointer either. All we // use RapArrayLength for is to update ParmDescriptorString if // the parameter was NULL. if ( Temp != NULL ) { NetpMoveMemory( Temp, // dest CurrentBlockPointer, // src TempLength); // len CurrentBlockPointer += TempLength; } } break; case REM_RCV_DWORD_PTR : // pointer to rcv Dword(s) { LPDWORD Temp; Temp = va_arg(ParmPtr, LPDWORD); TempLength = RapArrayLength( ParmDescriptorString, &ParmDescriptorString, Response); // if the rcv buffer given to us by the user is NULL, // (one currently can be - it is an MBZ parameter for // now in the log read apis...), don't attempt to // copy anything. TempLength will be garbage in this // case, so don't update CurrentBlockPointer either. All we // use RapArrayLength for is to update ParmDescriptorString if // the parameter was NULL. if ( Temp == NULL ) { ; /* NULLBODY */ } else { NetpMoveMemory( Temp, // dest CurrentBlockPointer, // src TempLength); // len CurrentBlockPointer += TempLength; } } break; case REM_SEND_BUF_PTR : (void) va_arg(ParmPtr, LPVOID); // Step over arg. break; case REM_SEND_BUF_LEN : (void) va_arg(ParmPtr, DWORD); // Step over (32-bit) buf len. break; case REM_RCV_BUF_PTR : (void) va_arg(ParmPtr, LPVOID); break; case REM_RCV_BUF_LEN : (void) va_arg(ParmPtr, DWORD); // Step over (32-bit) buf len. break; case REM_PARMNUM : (void) va_arg(ParmPtr, DWORD); // Step over (32-bit) parm num. break; case REM_ENTRIES_READ : // Used for NumStruct { EntriesReadPtr = va_arg(ParmPtr, LPDWORD); NumStruct = (DWORD) SmbGetUshort((LPWORD) CurrentBlockPointer); if (RapValueWouldBeTruncated(NumStruct)) { Status = ERROR_INVALID_PARAMETER; return Status; } IF_DEBUG(CONVBLK) { NetpKdPrint(( PREFIX_NETAPI "RxpConvertBlock: NumStruct is now " FORMAT_DWORD ".\n", NumStruct )); } // Assume all entries will fit; we'll correct this later if not. *EntriesReadPtr = NumStruct; CurrentBlockPointer += sizeof(WORD); break; } case REM_FILL_BYTES : // Special case, this was not really an input parameter so ParmPtr // does not get changed. However, the ParmDescriptorString // pointer must be advanced past the descriptor field so // use get RapArrayLength to do this but ignore the // return length. (void) RapArrayLength( ParmDescriptorString, &ParmDescriptorString, Response); break; case REM_AUX_NUM : // Can't have aux in parm desc. case REM_BYTE : // Can't push a byte, so this is bogus? case REM_DATA_BLOCK : // Not in parm desc. case REM_DATE_TIME : // Never used case REM_NULL_PTR : // Never used case REM_SEND_LENBUF : // Never used default : NetpBreakPoint(); Status = NERR_InternalError; goto ErrorExit; } // switch } // for // // If no data was returned from the server, then there's no point in // continuing. Return early with the status code as returned from the // remoted function // if (!SmbRcvByteLen) { if (Flags & ALLOCATE_RESPONSE) { // // We failed to allocate the buffer; this in turn will cause // RxRemoteApi to fail, in which event, the calling function // (ie RxNetXxxx) may try to free the buffer allocated on its // behalf (ie the buffer we just failed to get). Ensure that // the caller doesn't try to free an invalid pointer by setting // the returned pointer to NULL // NetpAssert(RcvDataBuffer); // address of the callers buffer pointer *(LPBYTE*)RcvDataBuffer = NULL; } return Status; } // // If the caller of RxRemoteApi requested that we allocate the final data // buffer on their behalf, then allocate it here. We use as the size // criterion // // (RAP_CONVERSION_FACTOR + 1/RAP_CONVERSION_FRACTION) * SmbRcvByteLen // // since this has the size of 16-bit data actually received. // // RAP_CONVERSION_FACTOR is 2 since that's the ratio for the size of // WCHAR to CHAR and of DWORD to WORD. However, Lanman data structures // typically represents text as zero terminated array of CHAR within the // returned structure. The array itself is of maximum size. However, // the typical native representation has a 4-byte pointer to a zero // terminated WCHAR. A factor of 2 wouldn't account for the 4-byte pointer. // Assuming the smallest lanman array size is 13 bytes (e.g., NNLEN+1), an // additional factor of 4/13 (about 1/3) is needed. So, // RAP_CONVERSION_FRACTION is 3. // // Round the size to a multiple of the alignment for the system to allow // data to be packed at the trailing end of the buffer. // // NOTE: Since the original API caller expects to use NetApiBufferFree to // get rid of this buffer, we must use NetapipBufferAllocate // if (Flags & ALLOCATE_RESPONSE) { NET_API_STATUS ConvertStatus; NetpAssert(RcvDataBuffer); // address of the callers buffer pointer NetpAssert(SmbRcvByteLen); // size of the data received RcvDataLength = SmbRcvByteLen * RAP_CONVERSION_FACTOR; RcvDataLength += (SmbRcvByteLen + RAP_CONVERSION_FRACTION - 1) / RAP_CONVERSION_FRACTION; RcvDataLength = ROUND_UP_COUNT( RcvDataLength, ALIGN_WORST ); if (ConvertStatus = NetapipBufferAllocate(RcvDataLength, (PVOID *) &pDataBuffer)) { NetpKdPrint(( PREFIX_NETAPI "Error: RxpConvertBlock cannot allocate memory (" "error " FORMAT_API_STATUS ").\n", ConvertStatus )); Status = ConvertStatus; goto ErrorExit; } NetpAssert( pDataBuffer != NULL ); IF_DEBUG(CONVBLK) { NetpKdPrint(( PREFIX_NETAPI "RxpConvertBlock: allocated " FORMAT_DWORD " byte buffer " "at " FORMAT_LPVOID " for caller\n", RcvDataLength, (LPVOID) pDataBuffer )); } *(LPBYTE*)RcvDataBuffer = pDataBuffer; } else { pDataBuffer = RcvDataBuffer; } ////////////////////////////////////////////////////////////////////////// // // // NB: From here on down, RcvDataBuffer should not be used to point to // // the received data buffer. Use pDataBuffer. The reason is that // // RcvDataBuffer is ambiguous - it may point to a buffer or it may // // point to a pointer to a buffer. pDataBuffer will always point // // to a buffer // // // ////////////////////////////////////////////////////////////////////////// // // Done doing arguments, so now we can do the receive buffer. // if ((pDataBuffer != NULL) && (RcvDataLength != 0)) { // DWORD BytesRequired = 0; LPBYTE EntryPtr16 = SmbRcvBuffer; // LPBYTE EntryPtr32 = pDataBuffer; DWORD NumAuxStructs; // LPBYTE StringAreaEnd = pDataBuffer + RcvDataLength; // BOOL auxiliaries = AuxDesc32 != NULL; // // MOD 06/06/91 RLF // // NetpAssert( DataDescriptor16 != NULL ); // // MOD 06/06/91 RLF // IF_DEBUG(CONVBLK) { NetpKdPrint(( PREFIX_NETAPI "RxpConvertBlock: " "SMB rcv buff (before rcv buff conv) (partial):\n" )); NetpDbgHexDump(SmbRcvBuffer, NetpDbgReasonable(RcvDataLength)); } // Now convert all pointer fields in the receive buffer to local // pointers. // // MOD 06/06/91 RLF // // If we have a receive data buffer, we may or may not be receiving // structured data. If DataDescriptor16 is NULL assume unstructured data // else work out sizes of structs etc. // // When I say "unstructured data", is it okay to treat this as an array of // bytes? What about ASCIZ-UC translation? My guess is that at this level // unstructured data is bytes and any higher-level software which knows // the format of received/expected data can process it // if ( !DESC_IS_UNSTRUCTURED( DataDescriptor16 ) ) { NET_API_STATUS ConvertStatus; DWORD NumCopied = 0; // // MOD 06/06/91 RLF // ConvertStatus = RxpReceiveBufferConvert( SmbRcvBuffer, // buffer (updated in place) RcvDataLength, // buffer size in bytes Converter, NumStruct, DataDescriptor16, AuxDesc16, & NumAuxStructs); if (ConvertStatus != NERR_Success) { Status = ConvertStatus; goto ErrorExit; } ConvertStatus = RxpConvertDataStructures( DataDescriptor16, // going from 16-bit data DataDescriptor32, // to 32-bit AuxDesc16, // same applies for aux data AuxDesc32, EntryPtr16, // where the 16-bit data is pDataBuffer, // where the 32-bit data goes RcvDataLength, // size of the output buffer (bytes) NumStruct, // number of primaries &NumCopied, // number of primaries copied. Both, // parameter to RapConvertSingleEntry RapToNative // convert 16-bit data to 32-bit ); if (ConvertStatus != NERR_Success) { // // This only happens when (1) the API allows the application // to specify the buffer size and (2) the size is too small. // As part of the "switch" above, we've already set the // "pcbNeeded" (pointer to count of bytes needed). Actually, // that value assumes that the RAP and native sizes are the // same. It's up to RxRemoteApi's caller to correct that, // if it's even feasible. // NetpAssert( ConvertStatus == ERROR_MORE_DATA ); if (EntriesReadPtr != NULL) { // Some APIs, like DosPrintQEnum, have entries read value. NetpAssert( NumCopied <= NumStruct ); *EntriesReadPtr = NumCopied; } else { // APIs like DosPrintQGetInfo does not have entries read. // There isn't much we can do for them. } Status = ConvertStatus; // Continue, treating this as "normal" status... } else { NetpAssert( NumCopied == NumStruct ); } IF_DEBUG(CONVBLK) { NetpKdPrint(( PREFIX_NETAPI "RxpConvertBlock: rcv buff (after CSE) (partial):\n" )); NetpDbgHexDump(pDataBuffer, NetpDbgReasonable(RcvDataLength)); } } else { // // There is no 16-bit data descriptor. We take this to mean that // the data is unstructured - typically a string will be returned // An example? Why, I_NetNameCanonicalize of course! // #if DBG NetpAssert(RcvDataLength >= SmbRcvByteLen); #endif // // Ascii-Unicode conversion is the responsibility of the caller // NetpMoveMemory(pDataBuffer, SmbRcvBuffer, SmbRcvByteLen); } } // if pDataBuffer && RcvDataLength IF_DEBUG(CONVBLK) { NetpKdPrint(( PREFIX_NETAPI "RxpConvertBlock: returning (normal) status=" FORMAT_API_STATUS "\n", Status )); } return(Status); ErrorExit: NetpAssert( Status != NO_ERROR ); if (Flags & ALLOCATE_RESPONSE) { // // If we already allocated a buffer on behalf of the caller of // RxRemoteApi, then free it up. // if (pDataBuffer != NULL) { (VOID) NetApiBufferFree(pDataBuffer); } // // We failed to allocate the buffer; this in turn will cause // RxRemoteApi to fail, in which event, the calling function // (ie RxNetXxxx) may try to free the buffer allocated on its // behalf (ie the buffer we just failed to get). Ensure that // the caller doesn't try to free an invalid pointer by setting // the returned pointer to NULL. // NetpAssert( RcvDataBuffer != NULL ); *(LPBYTE*)RcvDataBuffer = NULL; } NetpKdPrint(( PREFIX_NETAPI "RxpConvertBlock: returning error status=" FORMAT_API_STATUS "\n", Status )); return (Status); } // RxpConvertBlock