Windows NT 4.0 source code leak
 
 
 
 
 
 

922 lines
24 KiB

/*++
Copyright (c) 1994 Microsoft Corporation
Module Name:
adspclnt.c
Abstract:
Appletalk Data Stram Protocol (ADSP) connection-oriented full
duplex transport interface for RPC.
Author:
Mario Goertzel (mariogo) 23-Oct-1994
Revision History:
--*/
#include "sysinc.h"
#include "rpc.h"
#include "rpcdcep.h"
#include "rpctran.h"
#include "rpcerrp.h"
#include <AppleTalk.h>
#include <Adsp.h>
#include <Devices.h>
typedef struct
{
unsigned long MyA5; // My application's A5
unsigned short ccbRefNum; // Reference number for the CCB
TPCCB pCcb; // Ptr to connections' connection control block
Ptr pBuffers; // Send, receive and attn buffers
} CONNECTION, *PCONNECTION;
#define MAXIMUM_FRAGMENT 4096
#define QUEUE_SIZE (MAXIMUM_FRAGMENT + 256)
#define NETADDR_LEN (MAX_COMPUTERNAME_LENGTH + 1)
#define HOSTNAME_LEN (MAX_COMPUTERNAME_LENGTH)
#define ADDRESS_FAMILY AF_APPLETALK
#define PROTOCOL ATPROTO_ADSP
#define SOCKET_TYPE SOCKET_STREAM
#define MAX_HOSTNAME_LEN 32
#define OBJECTTYPE_PREFIX "DceDspRpc "
#define OBJECT_PREFIX_LEN (sizeof(OBJECTTYPE_PREFIX) - 1)
#define DEFAULTZONE "*"
#define ENDPOINT_MAPPER_EP "Endpoint Mapper"
#define ByteSwapLong(Value) \
Value = ( (((Value) & 0xFF000000) >> 24) \
| (((Value) & 0x00FF0000) >> 8) \
| (((Value) & 0x0000FF00) << 8) \
| (((Value) & 0x000000FF) << 24))
#define ByteSwapShort(Value) \
Value = ( (((Value) & 0x00FF) << 8) \
| (((Value) & 0xFF00) >> 8))
extern MACYIELDCALLBACK g_pfnCallback ;
/*
Global variables.
These are really initialized in ClientOpen().
BUGBUG MAC DLL: Need to do something different is multiple
processes can call ClientOpen and open driver.
*/
int driverInitialized = 0; // 1 - dspDriverReference valid.
short dspDriverReference; // DSP driver handle (ref num)
/*
Following Macros and structs are needed for Tower Stuff
*/
#pragma pack(1)
#define TRANSPORTID 0x16
#define TRANSPORTHOSTID 0x18
#define TOWERFLOORS 5
#define PROTSEQ "ncacn_at_dsp"
typedef struct _FLOOR_234 {
unsigned short ProtocolIdByteCount;
unsigned char FloorId;
unsigned short AddressByteCount;
unsigned char Data[2];
} FLOOR_234, PAPI * PFLOOR_234;
#define NEXTFLOOR(t,x) (t)((unsigned char PAPI *)x +((t)x)->ProtocolIdByteCount\
+ ((t)x)->AddressByteCount\
+ sizeof(((t)x)->ProtocolIdByteCount)\
+ sizeof(((t)x)->AddressByteCount))
/*
End of Tower Stuff!
*/
#pragma pack()
/*
Helper functions
*/
void __inline
SetupRemoteEntity(
OUT char *remoteName,
IN RPC_CHAR *address,
IN RPC_CHAR *endpoint
)
/*++
Routine Description:
Produces an appletale 'entityName' based on the servers name,
endpoint and optional zone.
entityName is of the format:
1 byte obj len bytes 1 b type len 1 b zone len
[obj len][ object string w/o null ][type len][ type string][zone len][ zone string]
Arguments:
remoteName - Upon return this contains the packed array
of sized strings representing the servers name.
address - 'sz' string containing the name and (optionally)
the zone of the server.
endpoint - The endpoint that the server is listening to.
Return Value:
n/a
--*/
{
RPC_CHAR *zone;
RPC_CHAR *temp = NULL ;
// Find zone name, if any.
zone = strchr(address, '@');
if (zone)
{
// Server address is of form "MyServer@MyZone"
temp = zone ;
*zone = '\0'; // Truncate "MyServer@MyZone" to "MyServer"
zone++; // zone is now "MyZone"
}
if (zone == 0 ||
*zone == '\0')
{
// Either no zone ("MyServer") or empty zone ("MyServer@"),
// use "*" which means the local zone. Does NOT mean ALL zones!)
zone = "*";
}
// Servers name is the object
*remoteName = RpcpStringLength(address);
RpcpMemoryCopy(remoteName + 1, address, *remoteName);
remoteName += (1 + *remoteName);
if (temp)
{
*temp = '@' ;
}
// Type name is OBJECTTYPE_PREFIX<endpoint>
*remoteName = OBJECT_PREFIX_LEN + RpcpStringLength(endpoint);
RpcpMemoryCopy(remoteName + 1, OBJECTTYPE_PREFIX, OBJECT_PREFIX_LEN);
RpcpMemoryCopy(remoteName + 1 + OBJECT_PREFIX_LEN, endpoint, RpcpStringLength(endpoint));
remoteName += (1 + *remoteName);
// Zone name
*remoteName = RpcpStringLength(zone);
RpcpMemoryCopy(remoteName + 1, zone, *remoteName);
return;
}
/*
Transport interfaces
*/
RPC_STATUS RPC_ENTRY
ClientOpen (
IN PCONNECTION pConn,
IN RPC_CHAR * NetworkAddress,
IN RPC_CHAR * Endpoint,
IN RPC_CHAR * NetworkOptions,
IN RPC_CHAR * TransportAddress,
IN RPC_CHAR * RpcProtocolSequence,
IN unsigned int Timeout
)
// Open a client connection
{
OSErr status;
DSPParamBlock pb; // Parameter block for DSP functions
volatile MPPParamBlock mppPb; // Parameter block for MPP functions (nbpPb)
volatile NBPparms *nbpPb; // Set to NBPparms field of MPPParamBlock
TPCCB pDspCcb; // Pointer to DSP connection control block
unsigned short ccbRef; // CCB reference number (handle)
char remoteName[99];// Appletalk name for server
AddrBlock remoteAddress;// Address of remote connection
char nbpBuf[104]; // Room for one name + address + pad
Ptr dspQ; // Data queue for the connection.
RPC_CHAR *zoneName; // Name of the zone to look in.
Str32 realEndpoint; // "DCE RPC ADSP-" + Endpoint
if (NetworkAddress == 0 || *NetworkAddress == '0')
{
// Local servers not supported on Mac
return(RPC_S_SERVER_UNAVAILABLE);
}
ASSERT(Endpoint && *Endpoint);
if (strlen(Endpoint) > 20)
{
return(RPC_S_INVALID_ENDPOINT_FORMAT);
}
if (driverInitialized <= 0)
{
// BUGBUG MAC DLL work: If multiple processes can call this at
// once we need a better solution for this.
if (!IsMPPOpen())
{
status = MPPOpen();
if (status != noErr)
{
#ifdef DEBUGRPC
PrintToDebugger("MPPOpen() failed: %d\n", status);
#endif
return(RPC_S_OUT_OF_RESOURCES);
}
}
status = opendriver(".DSP", &dspDriverReference);
if (status != noErr)
{
#ifdef DEBUGRPC
PrintToDebugger(".DSP Open failed: %d\n", status);
#endif
return(RPC_S_OUT_OF_RESOURCES);
}
driverInitialized = 1;
}
pb.ioCRefNum = dspDriverReference; // Drivers ref used to dispatch trap
pb.ioCompletion = 0; // ClientOpen completely sync.
nbpPb = &mppPb.NBP;
nbpPb->ioRefNum = mppRefNum;
nbpPb->ioCompletion = 0;
//
// Drivers initalized okay, now lookup the servers address with NBP.
//
// Could support x.z[y] style address, too.
// (w) net-^ ^ ^-socket (b)
// |-node (b)
SetupRemoteEntity(remoteName, NetworkAddress, Endpoint);
// Setup nbp parameter block to lookup the name.
nbpPb->csCode = lookupName;
nbpPb->interval = 0x4; // 4x8 ticks = ~500ms
ASSERT(Timeout <= 10);
nbpPb->count = 2 + Timeout; // Retry for 2 to 12 times based on Timeout
#if _MSC_VER >= 1000
nbpPb->nbpPtrs.entityPtr = (Ptr) &remoteName; // Name put together above
#else
nbpPb->NBPPtrs.entityPtr = (Ptr) &remoteName; // Name put together above
#endif
nbpPb->parm.verifyFlag = 0;
nbpPb->parm.Lookup.retBuffPtr = nbpBuf;
nbpPb->parm.Lookup.retBuffSize = 104;
nbpPb->parm.Lookup.maxToGet = 1;
nbpPb->parm.Lookup.numGotten = 0;
// Retry in the case of infinite timeout. Normal timeouts are
// controled but nbpPb->count value.
do {
status = PLookupName(&mppPb, FALSE);
}
while ( Timeout == RPC_C_BINDING_INFINITE_TIMEOUT
&& status != noErr
&& nbpPb->parm.Lookup.numGotten == 0 );
if ( status != noErr
|| nbpPb->parm.Lookup.numGotten == 0)
{
return(RPC_S_SERVER_UNAVAILABLE);
}
// Found atleast one name, we only try the first one.
status =
NBPExtract(nbpBuf,
nbpPb->parm.Lookup.numGotten,
1,
(EntityName *)&remoteName,
&remoteAddress);
ASSERT(status == noErr);
#ifdef DEBUGRPC
PrintToDebugger("Open found :%s[%s] with address %4x.%02x.%02x\n",
NetworkAddress, Endpoint,
remoteAddress.aNet, remoteAddress.aNode, remoteAddress.aSocket);
#endif
// Allocate memory and initialize the local end of the connection.
pDspCcb = (TPCCB)NewPtr(sizeof(TRCCB));
if (pDspCcb == 0)
{
return(RPC_S_OUT_OF_MEMORY);
}
dspQ = NewPtr( 2*QUEUE_SIZE + attnBufSize);
if (dspQ == 0)
{
DisposePtr((Ptr)pDspCcb);
return(RPC_S_OUT_OF_MEMORY);
}
// Initialize DSP driver
pb.csCode = dspInit;
pb.u.initParams.ccbPtr = pDspCcb;
pb.u.initParams.sendQSize = QUEUE_SIZE;
pb.u.initParams.recvQSize = QUEUE_SIZE;
pb.u.initParams.sendQueue = dspQ;
pb.u.initParams.recvQueue = dspQ + QUEUE_SIZE;
pb.u.initParams.attnPtr = dspQ + 2*QUEUE_SIZE;
pb.u.initParams.localSocket = 0; // Make appletalk choose local socket.
pb.u.initParams.userRoutine = 0; // Client shouldn't get connection events.
status = PBControl((ParmBlkPtr)&pb, FALSE);
if (status != noErr)
{
#ifdef DEBUGRPC
PrintToDebugger("dspInit failed %d\n", status);
#endif
DisposePtr((Ptr)pDspCcb);
DisposePtr(dspQ);
return(RPC_S_OUT_OF_MEMORY);
}
// Note: We may want to call the dspOptions trap here to set checksum,
// blocking factor, or such.
//
// Connect to remote address
//
pb.csCode = dspOpen;
// ccbRefNum set from before.
pb.u.openParams.remoteCID = 0; // Please choose this for me.
pb.u.openParams.remoteAddress = remoteAddress; // 4 byte copy.
pb.u.openParams.filterAddress = remoteAddress; // 4 byte copy.
pb.u.openParams.ocMode = ocRequest; // Open a connection to remoteAddr
pb.u.openParams.ocInterval = 0x3; // 3*(1/6s) = 1/2s
pb.u.openParams.ocMaximum = 1 + Timeout; // 1 to 11 retries.
if (Timeout == RPC_C_BINDING_INFINITE_TIMEOUT)
pb.u.openParams.ocMaximum = 0xff; // means retry forever.
status = PBControl((ParmBlkPtr)&pb, FALSE);
if (status != noErr)
{
#ifdef DEBUGRPC
PrintToDebugger("Open failed %d\n", status);
#endif
pb.csCode = dspRemove;
// ccRefNum already set
pb.u.closeParams.abort = 1; // throw away any data
status =
PBControl((ParmBlkPtr)&pb, FALSE);
ASSERT(status == noErr);
DisposePtr((Ptr)pDspCcb);
DisposePtr(dspQ);
return(RPC_S_SERVER_UNAVAILABLE);
}
// Save new connection the runtime's Connection object.
pConn->pCcb = pDspCcb;
pConn->ccbRefNum = pb.ccbRefNum;
pConn->MyA5 = SetCurrentA5();
pConn->pBuffers = dspQ;
return (RPC_S_OK);
}
RPC_STATUS RPC_ENTRY
ClientClose (
IN PCONNECTION pConn
)
// Close a client connection
{
OSErr status;
DSPParamBlock pb; // Parameter block for DSP functions
ASSERT(driverInitialized == 1);
pb.ioCRefNum = dspDriverReference;
pb.csCode = dspRemove;
pb.ioCompletion = 0;
pb.ccbRefNum = pConn->ccbRefNum;
pb.u.closeParams.abort = 1; // throw away any remaining data without sending
status = PBControl((ParmBlkPtr)&pb, FALSE);
if (status != noErr)
{
ASSERT(status == errRefNum)
#ifdef DEBUGRPC
PrintToDebugger("Closed bad connection\n");
ASSERT(0);
#endif
// Nobody really cares if they close a bad connection except
// when debugging a problem, right?
}
DisposePtr(pConn->pBuffers);
DisposePtr((Ptr)pConn->pCcb);
return(RPC_S_OK);
}
RPC_STATUS RPC_ENTRY
ClientSend (
IN PCONNECTION pConn,
IN void PAPI * Buffer,
IN unsigned int BufferLength
)
// Send a message to a connection.
{
OSErr status;
DSPParamBlock pb; // Parameter block for DSP functions
ASSERT(driverInitialized == 1);
pb.ioCRefNum = dspDriverReference;
pb.ioCompletion = 0; // sync sends because runtime may change
// contents of Buffer after we return.
pb.csCode = dspWrite;
pb.ccbRefNum = pConn->ccbRefNum;
pb.u.ioParams.reqCount = BufferLength;
pb.u.ioParams.dataPtr = Buffer;
pb.u.ioParams.eom = 1; // Each fragment is a message.
pb.u.ioParams.flush = 1; // Send it now, silly!
//
// Note: If we add shutdowns to the NT server we'll need to
// check for incoming data here if it has been sufficiently
// long since the last send. See the NT TCP/IP ClientSend() fn.
//
status =
PBControl((ParmBlkPtr)&pb, FALSE);
if (status != noErr)
{
// Errors as listed in Inside Mac: Networking
// errState -1278 Connection not open.
// errAborted -1279 Request aborted by dspRemove or dspClose
// errRefNum -1280 Bad ccbRefNum
//
// Only errState is expected.
ASSERT(status == errState);
ClientClose(pConn) ;
return(RPC_P_SEND_FAILED);
}
// I don't know how or why this wouldn't be true. If it isn't
// we'll probably need to loop around the send until the whole
// thing is sent.
ASSERT(pb.u.ioParams.actCount == BufferLength);
return(RPC_S_OK);
}
RPC_TRANS_STATUS RPC_ENTRY
ClientRecv (
IN PCONNECTION pConn,
IN OUT void PAPI * PAPI * Buffer,
IN OUT unsigned int PAPI * BufferLength
)
// Read a message from a connection.
{
RPC_STATUS RpcStatus;
OSErr status;
volatile DSPParamBlock pb; // Parameter block for DSP functions
int totalBytes = 0;
ASSERT(driverInitialized == 1);
if (*Buffer == 0)
{
*BufferLength = 512; // Min cached buffer size
RpcStatus = I_RpcTransClientReallocBuffer(pConn,
Buffer,
0,
*BufferLength);
if (RpcStatus != RPC_S_OK)
{
return(RPC_S_OUT_OF_MEMORY);
}
}
pb.ioCRefNum = dspDriverReference;
pb.ioCompletion = 0; // sync reads here
pb.csCode = dspRead;
pb.ccbRefNum = pConn->ccbRefNum;
do
{
pb.u.ioParams.dataPtr = (char *)*Buffer + totalBytes;
pb.u.ioParams.reqCount = *BufferLength - totalBytes;
ASSERT(pb.u.ioParams.reqCount);
status =
PBControl((ParmBlkPtr)&pb, FALSE);
if ( status != noErr
|| ( (pb.u.ioParams.eom == 0)
&& (pb.u.ioParams.actCount == 0) ) )
{
// If both eom and actCount are 0 then the connection is closed.
// Errors as listed in Inside Mac: Networking
// errFwdReset -1275 Read terminated by forward reset
// errState -1278 State isn't open, closing or closed
// errAborted -1279 Request aborted by dspRemove or dspClose
// errRefNum -1280 Bad ccbRefNum
//
// Only noErr and errState are expected. (maybe errFwdReset too?)
ASSERT( status == noErr
|| status == errState);
ClientClose(pConn) ;
return(RPC_P_RECEIVE_FAILED);
}
ASSERT(pb.u.ioParams.actCount || pb.u.ioParams.eom);
totalBytes += pb.u.ioParams.actCount;
if ( !pb.u.ioParams.eom
&& totalBytes == *BufferLength)
{
ASSERT(*BufferLength < I_RpcTransClientMaxFrag(pConn));
// Need a larger fragment
*BufferLength = I_RpcTransClientMaxFrag(pConn);
RpcStatus = I_RpcTransClientReallocBuffer(pConn,
Buffer,
totalBytes,
*BufferLength);
if (RpcStatus != RPC_S_OK)
{
return(RPC_S_OUT_OF_MEMORY);
}
}
}
while ( !pb.u.ioParams.eom );
*BufferLength = totalBytes;
return(RPC_S_OK);
}
#pragma pack(1)
RPC_STATUS RPC_ENTRY
ClientTowerConstruct(
IN char PAPI * Endpoint,
IN char PAPI * NetworkAddress,
OUT UNALIGNED short PAPI * Floors,
OUT UNALIGNED unsigned long PAPI * ByteCount,
OUT unsigned char PAPI * UNALIGNED PAPI * Tower,
IN char PAPI * Protseq
)
{
unsigned long TowerSize;
UNALIGNED PFLOOR_234 Floor, Floor1;
*Floors = TOWERFLOORS;
TowerSize = ((Endpoint == NULL) || (*Endpoint == '\0')) ?
2 : strlen(Endpoint) + 1;
TowerSize += ((NetworkAddress== NULL) || (*NetworkAddress== '\0')) ?
2 : strlen(NetworkAddress) + 1;
TowerSize += 2*sizeof(FLOOR_234) - 4;
if ((*Tower = (unsigned char PAPI*)I_RpcAllocate(*ByteCount = TowerSize))
== NULL)
{
return (RPC_S_OUT_OF_MEMORY);
}
Floor = (PFLOOR_234) *Tower;
Floor->ProtocolIdByteCount = 1;
Floor->FloorId = (unsigned char)(TRANSPORTID & 0xFF);
if ((Endpoint) && (*Endpoint))
{
memcpy((char PAPI *)&Floor->Data[0], Endpoint,
(Floor->AddressByteCount = strlen(Endpoint)+1));
}
else
{
Floor->AddressByteCount = 2;
Floor->Data[0] = 0;
}
//Onto the next floor
Floor1 = NEXTFLOOR(PFLOOR_234, Floor);
ByteSwapShort(Floor->AddressByteCount) ;
ByteSwapShort(Floor->ProtocolIdByteCount) ;
Floor1->ProtocolIdByteCount = 1;
Floor1->FloorId = (unsigned char)(TRANSPORTHOSTID & 0x0F);
if ((NetworkAddress) && (*NetworkAddress))
{
memcpy((char PAPI *)&Floor1->Data[0], NetworkAddress,
(Floor1->AddressByteCount = strlen(NetworkAddress) + 1));
}
else
{
Floor1->AddressByteCount = 2;
Floor1->Data[0] = 0;
}
ByteSwapShort(Floor1->AddressByteCount) ;
ByteSwapShort(Floor1->ProtocolIdByteCount) ;
return(RPC_S_OK);
}
RPC_STATUS RPC_ENTRY
ClientTowerExplode(
IN unsigned char PAPI * Tower,
OUT char PAPI * UNALIGNED PAPI * Protseq,
OUT char PAPI * UNALIGNED PAPI * Endpoint,
OUT char PAPI * UNALIGNED PAPI * NetworkAddress
)
{
UNALIGNED PFLOOR_234 Floor = (PFLOOR_234) Tower;
RPC_STATUS Status = RPC_S_OK;
if (Protseq != NULL)
{
*Protseq = I_RpcAllocate(strlen(PROTSEQ) + 1);
if (*Protseq == NULL)
{
Status = RPC_S_OUT_OF_MEMORY;
}
else
{
memcpy(*Protseq, PROTSEQ, strlen(PROTSEQ) + 1);
}
}
if ((Endpoint == NULL) || (Status != RPC_S_OK))
{
return (Status);
}
*Endpoint = I_RpcAllocate(Floor->AddressByteCount);
if (*Endpoint == NULL)
{
Status = RPC_S_OUT_OF_MEMORY;
if (Protseq != NULL)
I_RpcFree(*Protseq);
}
else
{
memcpy(*Endpoint, (char PAPI *)&Floor->Data[0], Floor->AddressByteCount);
}
return(Status);
}
#pragma pack()
// BUGBUG: Mac DLL: must have per process
Boolean RecvPending = 0;
RPC_TRANS_STATUS RPC_ENTRY
ClientAsyncRecv (
IN PCONNECTION pConn,
IN OUT void PAPI * PAPI * Buffer,
IN OUT unsigned int PAPI * BufferLength
)
// Read a message from a connection.
{
RPC_STATUS RpcStatus;
OSErr status;
volatile DSPParamBlock pb; // Parameter block for DSP functions
int totalBytes = 0;
if(RecvPending)
return(RPC_P_RECEIVE_FAILED);
ASSERT(driverInitialized == 1);
if (*Buffer == 0)
{
*BufferLength = 512; // Min cached buffer size
RpcStatus = I_RpcTransClientReallocBuffer(pConn,
Buffer,
0,
*BufferLength);
if (RpcStatus != RPC_S_OK)
{
return(RPC_S_OUT_OF_MEMORY);
}
}
pb.ioCRefNum = dspDriverReference;
pb.ioCompletion = 0;
pb.csCode = dspRead;
pb.ccbRefNum = pConn->ccbRefNum;
do
{
pb.u.ioParams.dataPtr = (char *)*Buffer + totalBytes;
pb.u.ioParams.reqCount = *BufferLength - totalBytes;
ASSERT(pb.u.ioParams.reqCount);
RecvPending = 1 ;
pb.ioResult = 1 ;
status =
PBControl((ParmBlkPtr)&pb, TRUE);
if ( status != noErr )
{
// If both eom and actCount are 0 then the connection is closed.
// Errors as listed in Inside Mac: Networking
// errFwdReset -1275 Read terminated by forward reset
// errState -1278 State isn't open, closing or closed
// errAborted -1279 Request aborted by dspRemove or dspClose
// errRefNum -1280 Bad ccbRefNum
//
// Only noErr and errState are expected. (maybe errFwdReset too?)
ASSERT( status == noErr
|| status == errState);
RecvPending = 0 ;
ClientClose(pConn) ;
return(RPC_P_RECEIVE_FAILED);
}
// the yeild function must return only when pb.ioResult != 1
while(pb.ioResult == 1)
{
if(g_pfnCallback)
(*g_pfnCallback)(&(pb.ioResult)) ;
}
if(pb.ioResult != noErr || ((pb.u.ioParams.eom == 0)
&& (pb.u.ioParams.actCount == 0)))
{
RecvPending = 0 ;
ClientClose(pConn) ;
return(RPC_P_RECEIVE_FAILED);
}
totalBytes += pb.u.ioParams.actCount;
if ( !pb.u.ioParams.eom
&& totalBytes == *BufferLength)
{
ASSERT(*BufferLength < I_RpcTransClientMaxFrag(pConn));
// Need a larger fragment
*BufferLength = I_RpcTransClientMaxFrag(pConn);
RpcStatus = I_RpcTransClientReallocBuffer(pConn,
Buffer,
totalBytes,
*BufferLength);
if (RpcStatus != RPC_S_OK)
{
RecvPending = 0 ;
return(RPC_S_OUT_OF_MEMORY);
}
}
}
while ( !pb.u.ioParams.eom );
*BufferLength = totalBytes;
RecvPending = 0 ;
return(RPC_S_OK);
}
RPC_STATUS RPC_ENTRY
ClientSendReceive (
IN PCONNECTION CConnection,
IN void PAPI * SendBuffer,
IN unsigned int SendBufferLength,
IN OUT void PAPI * PAPI * ReceiveBuffer,
IN OUT unsigned int PAPI * ReceiveBufferLength
)
{
RPC_STATUS status ;
if((status = ClientSend(CConnection, SendBuffer, SendBufferLength)) != RPC_S_OK)
return status ;
return ClientAsyncRecv(CConnection, ReceiveBuffer, ReceiveBufferLength) ;
}
RPC_CLIENT_TRANSPORT_INFO ClientAdspTransInfo =
{
RPC_TRANSPORT_INTERFACE_VERSION,
TRANSPORTID,
ClientTowerConstruct,
ClientTowerExplode,
4096,
sizeof (CONNECTION),
ClientOpen,
ClientClose,
ClientSend,
ClientRecv,
ClientSendReceive,
0,
0,
0
};
RPC_CLIENT_TRANSPORT_INFO PAPI * RPC_ENTRY ClientAdspTransportLoad (
IN RPC_CHAR PAPI * RpcProtocolSequence
)
// Loadable transport initialization function
{
return(&ClientAdspTransInfo);
}