/*++ Copyright (c) 1987-1991 Microsoft Corporation Module Name: vrremote.c Abstract: This module contains a routine VrRemoteApi which is a 16-bit only version of RxRemoteApi from the net\rpcxlate project. This routine supports remoted lanman APIs from a Virtual Dos Machine. This routine does not have to convert 32-16-32, but rather receives 16-bit data and sends a 16-bit transaction packet either to a down-level server or an NT-level server which must be running XactSrv to respond to this request. This routine and the support routines in vrremutl.c were lifted from the lanman project Note: since this is 32-bit code which deals with 16-bit data in a few places, 32-bit data items should be used where possible and only use 16-bit items where unavoidable Contents of this file: VrRemoteApi VrTransaction (VrpGetStructureSize) (VrpGetArrayLength) (VrpGetFieldSize) (VrpConvertReceiveBuffer) (VrpConvertVdmPointer) (VrpPackSendBuffer) Author: Richard L Firth (rfirth) 24-Oct-1991 Environment: Flat 32-bit, user space Revision History: 21-Oct-1991 rfirth Created --*/ #include #include // ASSERT, DbgPrint #include #include #include // x86 virtual machine definitions #include #include // common Vr stuff #include #include #include // NetWkstaGetInfo #include // NetApiBufferFree #include // REM_MAX_PARMS #include // FETCHWORD #include // prototypes #include #include #include // RxpTransactSmb #include // API_W numbers #include #include // // Global data. // unsigned short remapi_err_flag; // // code // NET_API_STATUS VrTransaction( IN LPSTR ServerName, IN LPBYTE SendParmBuffer, IN DWORD SendParmBufLen, IN LPBYTE SendDataBuffer, IN DWORD SendDataBufLen, OUT LPBYTE ReceiveParmBuffer, IN DWORD ReceiveParmBufLen, IN LPBYTE ReceiveDataBuffer, IN OUT LPDWORD ReceiveDataBufLen, IN BOOL NullSessionFlag ) /*++ Routine Description: Sends a transaction request to a server and receives a response Arguments: ServerName - to send request to SendParmBuffer - send parameters SendParmBufLen - length of send parameters SendDataBuffer - send data SendDataBufLen - length of send data ReceiveParmBuffer - receive parameter buffer ReceiveParmBufLen - length of receive parameter buffer ReceiveDataBuffer - where to receive data ReceiveDataBufLen - length of data buffer NullSessionFlag - set if we are to use a null session Return Value: NET_API_STATUS Success - NERR_Success Failure - --*/ { NET_API_STATUS status; status = RxpTransactSmb(ServerName, // // BUGBUG - transport name? // NULL, SendParmBuffer, SendParmBufLen, SendDataBuffer, SendDataBufLen, ReceiveParmBuffer, ReceiveParmBufLen, ReceiveDataBuffer, ReceiveDataBufLen, NullSessionFlag ); if (status == NERR_Success) { } return status; } NET_API_STATUS VrRemoteApi( IN DWORD ApiNumber, IN LPBYTE ServerNamePointer, IN LPSTR ParameterDescriptor, IN LPSTR DataDescriptor, IN LPSTR AuxDescriptor OPTIONAL, IN BOOL NullSessionFlag ) /*++ Routine Description: This routine creates and sends a 16-bit transaction SMB containing the parameters and data required for a remoted function call. Any received data is copied back into the caller's data space as 16-bit data. This function is being called on behalf of a VDM process which in turn is running as a virtual Intel 286 which means: * little endian * pointers are 32-bits : * stack is 16-bits wide and EXPANDS DOWN This routine is called as a result of the NetIRemoteAPI function being called in the VDM. This is an internal function and so the descriptor parameters are trusted. However, if the original (16-bit) caller gave us a bad buffer address or length then the results will be unpredictable. The original API which called NetIRemoteAPI was a pascal calling convention routine so if its parameter list was: FAR PASCAL NetRoutine(server_name, buffer_pointer, buffer_length, &bytes_read, &total); the stack would look like this: (note: all pointers are far) +----------------+ stack pointer => | ip | routine was called far +----------------+ | cs | +----------------+ | &total | Offset +----------------+ | &total | Segment +----------------+ | &bytes_read | Offset +----------------+ | &bytes_read | Segment +----------------+ | buffer_length | +----------------+ | buffer_pointer | Offset +----------------+ | buffer_pointer | Segment +----------------+ | server_name | Offset +----------------+ | server_name | Segment +----------------+ Assumes: BYTE is an 8-bit quantity WORD is a 16-bit quantity DWORD is a 32-bit quantity LPSTR is a 32-bit flat pointer to an 8-bit quantity Arguments: ApiNumber - Function number of the API required ServerNamePointer - Flat 32-bit pointer to address of 32-bit segmented far pointer to ASCIZ server name in Dos image. Immediately prior to this is a pascal calling convention stack of 16-bit caller parameters (see above). The server name identifies the server at which the API is to be executed ParameterDescriptor - Flat 32-bit pointer to ASCIZ string which describes caller parameters DataDescriptor - Flat 32-bit pointer to ASCIZ string which describes data structure in caller buffer (if any) or structure of data returned from server AuxDescriptor - Flat 32-bit pointer to ASCIZ string which describes auxiliary data structures in send buffer (if any) or structure of aux data returned from server NullSessionFlag - TRUE if we are to use a NULL session Return Value: NET_API_STATUS Success - 0 Failure - NERR_InternalError Return this when we have a bad descriptor character or we blow an internal limit. Basically if we return this its safe to assume the DOS box handed us some garbage (typically a descriptor string got trashed etc) --*/ { // // redefine our parameter identifiers as old-code identifiers // #define api_num ApiNumber #define servername_ptr ServerNamePointer #define parm_str ParameterDescriptor #define data_str DataDescriptor #define aux_str AuxDescriptor // // define a macro to perform the buffer checking and length and pointer // manipulation. Either quits the routine and returns ERROR_INVALID_PARAMETER // or updates parm_len and parm_pos to indicate the next available positions // and makes this_parm_pos available as the current position to write into // #define CHECK_PARAMETERS(len) \ { \ parm_len += len; \ if (parm_len > sizeof(parm_buf)) { \ return ERROR_INVALID_PARAMETER; \ } \ this_parm_pos = parm_pos; \ parm_pos += len; \ } // // 32-bit flat pointers and buffers // BYTE parm_buf[REM_MAX_PARMS]; // Parameter buffer BYTE computerName[CNLEN+1]; LPBYTE parm_pos; // Pointer into parm_buf LPBYTE this_parm_pos; // next place to write in parm_buf LPBYTE parm_ptr; // Ponter to stack parms LPSTR l_parm; // Used to index parm_str LPSTR l_data; // Used to index data_str LPSTR l_aux; // Used to index aux_str LPBYTE rcv_data_ptr; // Pointer to callers rcv buf LPBYTE send_data_ptr; // Ptr to send buffer to use LPBYTE wkstaInfo; LPBYTE serverName; // // lengths - 32-bit variables (even though actual lengths are quite small) // DWORD parm_len; // Length of send parameters DWORD ret_parm_len; // Length of expected parms DWORD rcv_data_length; // Length of callers rcv buf DWORD send_data_length; // Length of callers send buf DWORD parm_num; // Callers value for parm_num DWORD struct_size; // Size of fixed data struct DWORD aux_size; // Size of aux data struct DWORD num_struct; // Loop count for ptr fixup // // 16-bit quantities - only used when converting received 16-bit data in // caller's receive buffer // WORD ReceiveBufferSelector; WORD ReceiveBufferOffset; WORD converter; // For pointer fixups // // various flags // BOOL rcv_dl_flag; // Expect return data flag BOOL send_dl_flag; // Send data buffer flag BOOL rcv_dp_flag; // rcv buf ptr present flag BOOL send_dp_flag; // send buf ptr present flag BOOL parm_num_flag; // API has a parm_num BOOL alloc_flag; // // misc. variables // DWORD aux_pos; // aux structure expected DWORD no_aux_check; // check flag int len; // General purpose length API_RET_TYPE status; // Return status from remote UNICODE_STRING uString; ANSI_STRING aString; LPWSTR uncName; NTSTATUS ntstatus; // // Clear the internal error flag // remapi_err_flag = 0; // // Set found parameter flags to FALSE and ponters to NULL // rcv_dl_flag = FALSE; send_dl_flag = FALSE; rcv_dp_flag = FALSE; alloc_flag = FALSE; send_dp_flag = FALSE; parm_num_flag = FALSE; rcv_data_length = 0; send_data_length= 0; parm_num = 0; rcv_data_ptr = NULL; send_data_ptr = NULL; // // Set up parm_ptr to point to first of the callers parmeters // parm_ptr = servername_ptr; parm_pos = parm_buf; ret_parm_len = 2 * sizeof(WORD); /* Allow for return status & offset */ // // parse parameter descriptor/build parameter buffer for transaction // and get interesting information from 16-bit parameters // When finished, the parameter buffer looks like this: // // [] // // Remember: DOS only deals with ASCII characters // *((LPWORD)parm_pos)++ = (WORD)ApiNumber; parm_len = sizeof(WORD); len = strlen(ParameterDescriptor) + 1; parm_len += len; if (parm_len > sizeof(parm_buf)) { return NERR_InternalError; } l_parm = parm_pos; RtlCopyMemory(parm_pos, ParameterDescriptor, len); parm_pos += len; len = strlen(DataDescriptor) + 1; parm_len += len; if (parm_len > sizeof(parm_buf)) { return NERR_InternalError; } l_data = parm_pos; RtlCopyMemory(parm_pos, DataDescriptor, len); parm_pos += len; // // parse the parameter descriptor strings. Remember interesting things such // as pointers to buffers, buffer lengths, etc. // for (; *l_parm != '\0'; l_parm++) { switch(*l_parm) { case REM_WORD: CHECK_PARAMETERS(sizeof(WORD)); parm_ptr -= sizeof(WORD); SmbMoveUshort((LPWORD)this_parm_pos, (LPWORD)parm_ptr); break; case REM_ASCIZ: { LPSTR pstring; // // the parameter is a pointer to a string. Read the string // pointer from the caller's stack then check the string proper. // If the pointer is NULL, change the parameter descriptor sent // in the SMB to indicate the pointer was NULL at this end // parm_ptr -= sizeof(LPSTR); pstring = LPSTR_FROM_POINTER(parm_ptr); if (pstring == NULL) { *(l_parm) = REM_NULL_PTR; break; } len = strlen(pstring) + 1; CHECK_PARAMETERS(len); RtlCopyMemory(this_parm_pos, pstring, len); } break; case REM_BYTE_PTR: case REM_WORD_PTR: case REM_DWORD_PTR: { LPBYTE pointer; parm_ptr -= sizeof(LPBYTE); pointer = LPBYTE_FROM_POINTER(parm_ptr); if (pointer == NULL) { *(l_parm) = REM_NULL_PTR; /* Indicate null pointer */ break; } len = VrpGetArrayLength(l_parm, &l_parm); CHECK_PARAMETERS(len); RtlCopyMemory(this_parm_pos, pointer, len); } break; case REM_RCV_WORD_PTR: case REM_RCV_BYTE_PTR: case REM_RCV_DWORD_PTR: { LPBYTE pointer; parm_ptr -= sizeof(LPBYTE*); pointer = LPBYTE_FROM_POINTER(parm_ptr); // // Added this test for a NULL pointer to allow for // a reserved field (currently MBN) to be a recv // pointer. - ERICPE 7/19/89 // if (pointer == NULL) { *(l_parm) = REM_NULL_PTR; break; } ret_parm_len += VrpGetArrayLength(l_parm, &l_parm); if (ret_parm_len > sizeof(parm_buf)) { ASSERT(FALSE); return NERR_InternalError; } } break; case REM_DWORD: CHECK_PARAMETERS(sizeof(DWORD)); parm_ptr -= sizeof(DWORD); SmbMoveUlong((LPDWORD)this_parm_pos, (LPDWORD)parm_ptr); break; case REM_RCV_BUF_LEN: CHECK_PARAMETERS(sizeof(WORD)); parm_ptr -= sizeof(WORD); SmbMoveUshort((LPWORD)this_parm_pos, (LPWORD)parm_ptr); rcv_data_length = (DWORD)SmbGetUshort((LPWORD)parm_ptr); rcv_dl_flag = TRUE; #ifdef VR_DIAGNOSE DbgPrint("VrRemoteApi: rcv_data_length=%x\n", rcv_data_length); #endif break; case REM_RCV_BUF_PTR: parm_ptr -= sizeof(LPBYTE); ReceiveBufferOffset = GET_OFFSET(parm_ptr); ReceiveBufferSelector = GET_SELECTOR(parm_ptr); rcv_data_ptr = LPBYTE_FROM_POINTER(parm_ptr); rcv_dp_flag = TRUE; #ifdef VR_DIAGNOSE DbgPrint("VrRemoteApi: Off=%x, Sel=%x, data_ptr=%x\n", ReceiveBufferOffset, ReceiveBufferSelector, rcv_data_ptr); #endif break; case REM_SEND_BUF_PTR: parm_ptr -= sizeof(LPBYTE); send_data_ptr = LPBYTE_FROM_POINTER(parm_ptr); send_dp_flag = TRUE; break; case REM_SEND_BUF_LEN: parm_ptr -= sizeof(WORD); send_data_length = (DWORD)SmbGetUshort((LPWORD)parm_ptr); send_dl_flag = TRUE; break; case REM_ENTRIES_READ: ret_parm_len += sizeof(WORD); if (ret_parm_len > sizeof(parm_buf)) { ASSERT(FALSE); return NERR_InternalError; } parm_ptr -= sizeof(LPBYTE); break; case REM_PARMNUM: CHECK_PARAMETERS(sizeof(WORD)); parm_ptr -= sizeof(WORD); parm_num = (DWORD)SmbGetUshort((LPWORD)parm_ptr); SmbMoveUshort((LPWORD)this_parm_pos, (LPWORD)parm_ptr); parm_num_flag = TRUE; break; case REM_FILL_BYTES: // // This is a rare type but is needed to ensure that the // send paramteres are at least as large as the return // parameters so that buffer management can be simplified // on the server. // len = VrpGetArrayLength(l_parm, &l_parm); CHECK_PARAMETERS(len); break; default: /* Could be a digit from NULL send array */ break; } } // // The parameter buffer now contains ; // api_num - word // parm_str - asciz, (NULL c,i,f,z identifiers replaced with Z. // data_str - asciz // parameters - as identified by parm_str. // // // For the receive buffer there is no data to set up for the call // but there might have been an REM_AUX_COUNT descriptor in data_str // which requires the aux_str to be copied onto the end of the // parameter buffer. // if (rcv_dp_flag || send_dp_flag) { // // Find the length of the fixed length portion of the data // buffer. // struct_size = VrpGetStructureSize(l_data, &aux_pos); if (aux_pos != -1) { l_aux = aux_str; len = strlen(l_aux) + 1; /* Length of aux descriptor */ CHECK_PARAMETERS(len); RtlCopyMemory(this_parm_pos, aux_str, len); aux_size = VrpGetStructureSize(l_aux, &no_aux_check); if (no_aux_check != -1) { /* Error if N in aux_str */ ASSERT(FALSE); return NERR_InternalError; } } } // // For a send buffer the data pointed to in the fixed structure // must be copied into the send buffer. Any pointers which already // point in the send buffer are NULLed as it is illegal to use // the buffer for the send data, it is our transport buffer. // NOTE - if parmnum was specified the buffer contains only that // element of the structure so no length checking is needed at this // side. A parmnum for a pointer type means that the data is at the // start of the buffer so there is no copying to be done. // if (send_dp_flag) { // // Only process buffer if no parm_num and this is not a block send // (no data structure) or an asciz concatenation send // if ((parm_num == 0) && (*l_data != REM_DATA_BLOCK)) { status = VrpPackSendBuffer( &send_data_ptr, &send_data_length, &alloc_flag, data_str, aux_str, struct_size, aux_pos, aux_size, parm_num_flag, FALSE ); if (status != 0) { return status; } } } // // Check for an internal error prior to issuing the transaction // if (remapi_err_flag != 0) { if (alloc_flag) { LocalFree(send_data_ptr); } return NERR_InternalError; } // // get the server name. If it is NULL then we are faking a local API call // by making a remote call to XactSrv on this machine. Fill in our computer // name // serverName = LPSTR_FROM_POINTER(servername_ptr); //////////////////////////////////////////////////////////////////////////////// //// is this actually required any longer? if (serverName == NULL) { status = NetWkstaGetInfo(NULL, 100, &wkstaInfo); if (status) { if (alloc_flag) { LocalFree(send_data_ptr); } return status; } else { computerName[0] = computerName[1] = '\\'; // // BUGBUG - Unicode - ASCII conversion here // strcpy(computerName+2, ((LPWKSTA_INFO_100)wkstaInfo)->wki100_computername); NetApiBufferFree(wkstaInfo); serverName = computerName; #ifdef VR_DIAGNOSE DbgPrint("VrRemoteApi: computername is %s\n", serverName); #endif } } //////////////////////////////////////////////////////////////////////////////// // // The parameter buffers and data buffers are now set up for // sending to the API worker so call transact to send them. // RtlInitAnsiString(&aString, serverName); ntstatus = RtlAnsiStringToUnicodeString(&uString, &aString, (BOOLEAN)TRUE); if (!NT_SUCCESS(ntstatus)) { #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrRemoteApi: Unexpected situation: RtlAnsiStringToUnicodeString returns %x\n", ntstatus); } #endif return ERROR_NOT_ENOUGH_MEMORY; } uncName = uString.Buffer; #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrpTransactVdm: UncName=%ws\n", uncName); } #endif status = RxpTransactSmb((LPTSTR)uncName, // // BUGBUG - transport name? // NULL, parm_buf, // Send parm buffer parm_len, // Send parm length send_data_ptr, // Send data buffer send_data_length, // Send data length parm_buf, // Rcv prm buffer ret_parm_len, // Rcv parm length rcv_data_ptr, // Rcv data buffer &rcv_data_length, // Rcv data length NullSessionFlag ); RtlFreeUnicodeString(&uString); if (status) { #ifdef VR_DIAGNOSE DbgPrint("Error: VrRemoteApi: RxpTransactSmb returns %d(%x)\n", status, status); #endif switch (status) { case NERR_BufTooSmall: /* No data returned from API worker */ rcv_data_length = 0; break; case ERROR_MORE_DATA: /* Just a warning for the caller */ break; case NERR_TooMuchData: /* Just a warning for the caller */ break; default: rcv_data_length = 0; break; } } /* 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 parmeters. * The return parameter buffer contains; * status, (unsigned short) * converter, (unsigned short) * ... - fields described by rcv ptr types in parm_str */ parm_pos = parm_buf + sizeof(WORD); converter = (WORD)SmbGetUshort((LPWORD)parm_pos); parm_pos += sizeof(WORD); // // Set up parm_ptr to point to first of the callers parmeters // parm_ptr = servername_ptr; // // set default value of num_struct to 1, if data, 0 if no data // num_struct = (DWORD)((*data_str == '\0') ? 0 : 1); for (; *parm_str != '\0'; parm_str++) { switch (*parm_str) { case REM_RCV_WORD_PTR: case REM_RCV_BYTE_PTR: case REM_RCV_DWORD_PTR: { LPBYTE ptr; parm_ptr -= sizeof(LPBYTE*); ptr = LPBYTE_FROM_POINTER(parm_ptr); // // 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. len will be garbage in this // case, so don't update parm_pos either. All we // use VrpGetArrayLength for is to update parm_str if // the parameter was NULL. // if (ptr != NULL) { len = VrpGetArrayLength(parm_str, &parm_str); RtlCopyMemory(ptr, parm_pos, len); // // 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 ((api_num == API_WPrintQGetInfo) && (status == NERR_Success) && (*parm_str == REM_RCV_WORD_PTR) && (*(LPWORD)ptr == 0)) { rcv_data_length = 0; status = ERROR_INVALID_LEVEL; } // // END OF GROSS HACK // parm_pos += len; } } break; case REM_ENTRIES_READ: { LPWORD wptr; parm_ptr -= sizeof(LPWORD*); wptr = (LPWORD)POINTER_FROM_POINTER(parm_ptr); num_struct = (DWORD)SmbGetUshort((LPWORD)parm_pos); SmbPutUshort((LPWORD)wptr, (WORD)num_struct); parm_pos += sizeof(WORD); } break; case REM_FILL_BYTES: // // Special case, this was not really an input parameter // so parm_ptr does not get changed. However, the parm_str // pointer must be advanced past the descriptor field so // use get VrpGetArrayLength to do this but ignore the // return length. // VrpGetArrayLength(parm_str, &parm_str); break; default: // // If the descriptor was not a rcv pointer type then step // over the parmeter pointer. // parm_ptr -= VrpGetFieldSize(parm_str, &parm_str); } } // // Now convert all pointer fields in the receive buffer to local // pointers. // if (rcv_dp_flag && (rcv_data_length != 0)) { VrpConvertReceiveBuffer( rcv_data_ptr, // lp ReceiveBufferSelector, // word ReceiveBufferOffset, // word converter, // word num_struct, // dword data_str, // lp aux_str // lp ); } if (alloc_flag) { LocalFree(send_data_ptr); } if (remapi_err_flag != 0) { return NERR_InternalError; } return status; } DWORD VrpGetStructureSize( IN LPSTR Descriptor, IN LPDWORD AuxOffset ) /*++ Routine Description: Calculates the length of the fixed portion of a structure, based on the descriptor for that structure Arguments: Descriptor - pointer to ASCIZ data descriptor string AuxOffset - pointer to returned dword which is relative position in the data descriptor where a REM_AUX_NUM descriptor was found This will be set to -1 if no aux descriptor found Return Value: Length in bytes of structure described by Descriptor --*/ { DWORD length; char c; *AuxOffset = (DWORD)(-1); for (length = 0; (c = *Descriptor) != '\0'; Descriptor++) { if (c == REM_AUX_NUM) { *AuxOffset = length; length += sizeof(WORD); } else { length += VrpGetFieldSize(Descriptor, &Descriptor); } } return length; } DWORD VrpGetArrayLength( IN LPSTR Descriptor, IN LPSTR* pDescriptor ) /*++ Routine Description: Calculates the length of an array described by an element of a descriptor string and update the descriptor string pointer to point to the last char in the element of the descriptor string. Arguments: Descriptor - pointer to ASCIZ descriptor string pDescriptor - pointer to address of Descriptor Return Value: Length in bytes of array described by Descriptor --*/ { DWORD num_elements; DWORD element_length; // // First set length of an element in the array // switch (*Descriptor) { case REM_WORD: case REM_WORD_PTR: case REM_RCV_WORD_PTR: element_length = sizeof(WORD); break; case REM_DWORD: case REM_DWORD_PTR: case REM_RCV_DWORD_PTR: element_length = sizeof(DWORD); break; case REM_BYTE: case REM_BYTE_PTR: case REM_RCV_BYTE_PTR: case REM_FILL_BYTES: element_length = sizeof(BYTE); break; // // Warning: following fixes a bug in which "b21" type // combinations in parmeter string will be // handled correctly when pointer to such "bit map" // in the struct is NULL. These two dumbos could // interfere so we force a success return. // case REM_ASCIZ: case REM_SEND_LENBUF: case REM_NULL_PTR: return 0; default: remapi_err_flag = NERR_InternalError; ASSERT(FALSE); return 0; } // // Now get numeber of elements in the array // for (num_elements = 0, Descriptor++; (*Descriptor <= '9') && (*Descriptor >= '0'); Descriptor++, (*pDescriptor)++) { num_elements = (WORD)((10 * num_elements) + ((WORD)*Descriptor - (WORD)'0')); } return (num_elements == 0) ? element_length : element_length * num_elements; } DWORD VrpGetFieldSize( IN LPSTR Descriptor, IN LPSTR* pDescriptor ) /*++ Routine Description: Calculates the length of an field described by an element of a descriptor string and update the descriptor string pointer to point to the last char in the element of the descriptor string. Arguments: Descriptor - pointer to the descriptor string pDescriptor - pointer to the address of the descriptor. On exit this points to the last character in the descriptor just parsed Return Value: Length in bytes of the field parsed --*/ { char c; c = *Descriptor; if (IS_POINTER(c) || (c == REM_NULL_PTR)) { /* All pointers same size */ while (*(++Descriptor) <= '9' && *Descriptor >= '0') { (*pDescriptor)++; /* Move ptr to end of field size */ } return sizeof(LPSTR); } // // Here if descriptor was not a pointer type so have to find the field // length specifically // switch (c) { case REM_WORD: case REM_BYTE: case REM_DWORD: return VrpGetArrayLength(Descriptor, pDescriptor); case REM_AUX_NUM: case REM_PARMNUM: case REM_RCV_BUF_LEN: case REM_SEND_BUF_LEN: return sizeof(WORD); case REM_DATA_BLOCK: case REM_IGNORE: return 0; /* No structure for this */ case REM_DATE_TIME: return sizeof(DWORD); default: remapi_err_flag = NERR_InternalError; #ifdef VR_DIAGNOSE DbgPrint("VrpGetFieldSize: offending descriptor is '%c'\n", c); #endif ASSERT(FALSE); return 0; } } VOID VrpConvertReceiveBuffer( IN LPBYTE ReceiveBuffer, IN WORD BufferSelector, IN WORD BufferOffset, IN WORD ConverterWord, IN DWORD NumberStructs, IN LPSTR DataDescriptor, IN LPSTR AuxDescriptor ) /*++ Routine Description: All pointers in the receive buffer are returned from the API worker as pointers into the buffer position given to the API on the API worker's station. In order to convert them into local pointers the segment of each pointer must be set to the segment of the rcv buffer and the offset must be set to; offset of rcv buffer + offset of pointer - converter word. This routine steps through the receive buffer and calls VrpConvertVdmPointer to perform the above pointer conversions. Arguments: ReceiveBuffer - 32-bit flat pointer to 16-bit DOS buffer BufferSelector - 16-bit selector of Dos receive buffer BufferOffset - 16-bit offset of Dos receive buffer ConverterWord - From API worker NumberStructs - Entries read parm (or 1 for GetInfo) DataDescriptor - String for data format AuxDescriptor - string for aux format Return Value: None. --*/ { LPSTR l_data; LPSTR l_aux; DWORD num_aux; DWORD i, j; char c; for (i = 0; i < NumberStructs; i++) { // // convert all pointers in next primary; if we hit a aux word count // remember number of secondary structures // for (l_data = DataDescriptor, num_aux = 0; c = *l_data; l_data++) { if (c == REM_AUX_NUM) { num_aux = (DWORD)*(ULPWORD)ReceiveBuffer; } if (IS_POINTER(c)) { VrpConvertVdmPointer( (ULPWORD)ReceiveBuffer, BufferSelector, BufferOffset, ConverterWord ); } ReceiveBuffer += VrpGetFieldSize(l_data, &l_data); } // // convert any pointers in any returned secondary (aux) structures // for (j = 0; j < num_aux; j++) { for (l_aux = AuxDescriptor; c = *l_aux; l_aux++) { if (IS_POINTER(c)) { VrpConvertVdmPointer( (ULPWORD)ReceiveBuffer, BufferSelector, BufferOffset, ConverterWord ); } ReceiveBuffer += VrpGetFieldSize(l_aux, &l_aux); } } } } VOID VrpConvertVdmPointer( IN ULPWORD TargetPointer, IN WORD BufferSegment, IN WORD BufferOffset, IN WORD ConverterWord ) /*++ Routine Description: All pointers in the receive buffer are returned from the API worker as pointers into the buffer position given to to the API on the API worker's station. In order to convert them into local pointers the segment of each pointer must be set to the segment of the rcv buffer and the offset must be set to; offset of rcv buffer + offset of pointer - converter word. The pointer is not converted if it is NULL Arguments: TargetPointer - 32-bit flat pointer to segmented Dos pointer to convert BufferSegment - 16-bit selector/segment of target buffer in DOS image BufferOffset - 16-bit offset within BufferSegment where buffer starts ConverterWord - 16-bit offset converter word from API worker on server Return Value: None. --*/ { WORD offset; if (*((UCHAR * UNALIGNED *)TargetPointer) != NULL) { SET_SELECTOR(TargetPointer, BufferSegment); offset = GET_OFFSET(TargetPointer) - ConverterWord; SET_OFFSET(TargetPointer, BufferOffset + offset); } } NET_API_STATUS VrpPackSendBuffer( IN OUT LPBYTE* SendBufferPtr, IN OUT LPDWORD SendBufLenPtr, OUT LPBOOL SendBufferAllocated, IN OUT LPSTR DataDescriptor, IN LPSTR AuxDescriptor, IN DWORD StructureSize, IN DWORD AuxOffset, IN DWORD AuxSize, IN BOOL SetInfoFlag, IN BOOL OkToModifyDescriptor ) /*++ Routine Description: For a send buffer the data pointed to in the fixed structure must be copied into the send buffer. Any pointers which already point in the send buffer are NULLed ( or errored if the call is not a SetInfo type) as it is illegal to use the buffer for the send data, it is our transport buffer Note that if the caller's (VDM) buffer is large enough, the variable data will be copied there. Eg. if the caller is doing a NetUseAdd which has a 26 byte fixed structure (use_info_1) and they placed that structure in a 1K buffer, the remote name will be copied into their own buffer at offset 26. The data pointed to is in 16-bit little-endian format; any pointers are segmented 16:16 pointers combined in the (thankfully) imitable intel way to result in a 20-bit linear (virtual) address If this function fails, the caller's buffer pointer and length will not have altered. If it succeeds however, *SendBufferPtr and *SendBufLenPtr may be different to the values passed, depending on whether *SendBufferAllocated is TRUE Arguments: SendBufferPtr - pointer to pointer to caller's 16-bit send buffer. We may be able to satisfy the send from this buffer if the data is simple (ie no structures to send). If we have to send structured data then we may have to allocate a new buffer in this routine because we need to move all of the caller's data into one buffer and (s)he may not have allocated enough space to hold everything. Additionally, we cannot assume that we can write the caller's data into their own buffer! SendBufLenPtr - pointer to the length of the allocated buffer. If we allocate a buffer in this routine, this length will alter SendBufferAllocated - pointer to a flag which will get set (TRUE) if we do actually allocate a buffer in this routine DataDescriptor - pointer to ASCIZ string which describes the primary data structure in the buffer. This may be updated if NULL pointers are found where a REM_ASCIZ descriptor designates a string pointer AuxDescriptor - pointer to ASCIZ string which describes the secondary data structure in the buffer StructureSize - the size (in bytes) of the fixed portion of the primary data structure AuxOffset - offset to the REM_AUX_NUM descriptor ('N') within the data descriptor, or -1 if there isn't one AuxSize - size in bytes of the fixed part of the secondary data structure, if any SetInfoFlag - indication of whether the API was a SetInfo call OkToModifyDescriptor- TRUE if we can modify REM_ASCIZ descriptor chars to REM_NULL_PTR in DataDescriptor, if a NULL pointer is found in the structure. Used by VrNet routines which are not calling VrRemoteApi Return Value: NET_API_STATUS Success - NERR_Success Failure - ERROR_NOT_ENOUGH_MEMORY NERR_BufTooSmall --*/ { LPBYTE struct_ptr; LPBYTE c_send_buf; LPBYTE send_ptr; DWORD c_send_len; DWORD buf_length; DWORD to_send_len; DWORD num_aux; LPSTR data_ptr; LPSTR l_dsc; LPSTR l_str; BOOL alloc_flag = FALSE; DWORD num_struct; DWORD len; UCHAR c; DWORD numberOfStructureTypes; DWORD i, j; LPBYTE ptr; // // Make local copies of the original start and length of the caller's // buffer as the originals may change if malloc is used but they // will still be needed for the F_RANGE check. // struct_ptr = c_send_buf = send_ptr = *SendBufferPtr; c_send_len = buf_length = *SendBufLenPtr; if ((buf_length < StructureSize) || (AuxOffset == StructureSize)) { return NERR_BufTooSmall; } // // if the offset to the REM_AUX_NUM descriptor is not -1 then we have // associated secondary structures with this primary. The actual number // is embedded in the primary structure. Retrieve it // if (AuxOffset != -1) { num_aux = (DWORD)SmbGetUshort((LPWORD)(send_ptr + AuxOffset)); to_send_len = StructureSize + (num_aux * AuxSize); if (buf_length < to_send_len) { return NERR_BufTooSmall; } numberOfStructureTypes = 2; } else { to_send_len = StructureSize; num_aux = AuxSize = 0; numberOfStructureTypes = 1; } // // Set up the data pointer to point past fixed length structures // data_ptr = send_ptr + to_send_len; // // Any data pointed to by pointers in the data or aux structures // must now be copied into the buffer. Start with the primary data // structure. // l_str = DataDescriptor; num_struct = 1; /* Only one primary structure allowed */ for (i = 0; i < numberOfStructureTypes; l_str = AuxDescriptor, num_struct = num_aux, i++) { for (j = 0 , l_dsc = l_str; j < num_struct; j++, l_dsc = l_str) { for (; (c = *l_dsc) != '\0'; l_dsc++) { if (IS_POINTER(c)) { ptr = LPBYTE_FROM_POINTER(struct_ptr); if (ptr == NULL) { if ((*l_dsc == REM_ASCIZ) && OkToModifyDescriptor) { #ifdef VR_DIAGNOSE DbgPrint("VrpPackSendBuffer: modifying descriptor to REM_NULL_PTR\n"); #endif *l_dsc = REM_NULL_PTR; } struct_ptr += sizeof(LPBYTE); VrpGetArrayLength(l_dsc, &l_dsc); } else { // // If the pointer is NULL or points inside the // original send buffer ( may have been reallocated) // then NULL it as it is not a field being set OR // return an error for a non SetInfo type call as // it is illegal to have a pointer into the // transport buffer. // if (RANGE_F(ptr, c_send_buf, c_send_len)) { if (SetInfoFlag) { SmbPutUlong((LPDWORD)struct_ptr, 0L); VrpGetArrayLength(l_dsc, &l_dsc); struct_ptr += sizeof(LPSTR); } else { return ERROR_INVALID_PARAMETER; } } else { switch (c) { case REM_ASCIZ: len = strlen(ptr) + 1; break; case REM_SEND_LENBUF: len = *(LPWORD)ptr; break; default: len = VrpGetArrayLength(l_dsc, &l_dsc); } // // There is data to be copied into the send // buffer so check that it will fit. // to_send_len += len; if (to_send_len > buf_length) { buf_length = to_send_len + BUF_INC; if (!alloc_flag) { // // Need new buffer // send_ptr = (LPBYTE)LocalAlloc(LMEM_FIXED, buf_length); if (send_ptr == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } alloc_flag = TRUE; // // Got new buffer, so copy old buffer // RtlCopyMemory(send_ptr, c_send_buf, to_send_len - len); struct_ptr = send_ptr + (struct_ptr - c_send_buf); data_ptr = send_ptr + (data_ptr - c_send_buf); } else { LPBYTE newPtr; newPtr = (LPBYTE)LocalReAlloc(send_ptr, buf_length, LMEM_MOVEABLE); if (newPtr == NULL) { LocalFree(send_ptr); return ERROR_NOT_ENOUGH_MEMORY; } else if (newPtr != send_ptr) { // // fix up the pointers // data_ptr = newPtr + (data_ptr - send_ptr); struct_ptr = newPtr + (struct_ptr - send_ptr); send_ptr = newPtr; } } } // // There is room for new data in buffer so copy // it and and update the struct and data ptrs // RtlCopyMemory(data_ptr, ptr, len); data_ptr += len; struct_ptr += sizeof(LPBYTE); } } } else { // // If the descriptor was not a pointer type then step // over the corresponding data field. // struct_ptr += VrpGetFieldSize(l_dsc, &l_dsc); } } } } *SendBufferPtr = send_ptr; // // Note that this is potentially incorrect: we are actually returning the // size of the structure + dynamic data to be sent, which is probably not // the same as the size of the buffer we (re)allocated. This is how it is // done in Lanman, so we'll do the same thing until it breaks // *SendBufLenPtr = to_send_len; *SendBufferAllocated = alloc_flag; return NERR_Success; }