Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

1531 lines
48 KiB

/*++
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 <nt.h>
#include <ntrtl.h> // ASSERT, DbgPrint
#include <nturtl.h>
#include <windows.h>
#include <softpc.h> // x86 virtual machine definitions
#include <vrdlctab.h>
#include <vdmredir.h> // common Vr stuff
#include <lmcons.h>
#include <lmerr.h>
#include <lmwksta.h> // NetWkstaGetInfo
#include <lmapibuf.h> // NetApiBufferFree
#include <apiworke.h> // REM_MAX_PARMS
#include <mvdm.h> // FETCHWORD
#include <vrremote.h> // prototypes
#include <remtypes.h>
#include <smbgtpt.h>
#include <rxp.h> // RxpTransactSmb
#include <apinums.h> // API_W numbers
#include <string.h>
#include <vrdebug.h>
//
// 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 <segment|selector>:<offset>
* 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:
//
// <api_num><parm_desc><data_desc><parms>[<aux_desc>]
//
// 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,
(LPSTR)((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;
}