/*++ Copyright (C) Microsoft Corporation, 1991 - 1999 Module Name: epclnt.cxx Abstract: This file contains the client runtime entry points into the endpoint mapper dll. Author: Michael Montague (mikemon) 06-Jan-1992 Revision History: --*/ #include #include #include #include #include #include #include #include #include #include extern RPC_STATUS __RPC_FAR MapFromNcaStatusCode ( IN unsigned long NcaStatus ); const static ProtseqEndpointPair EpMapperTable[] = { L"ncacn_np", L"\\pipe\\epmapper", NMP, L"ncalrpc", L"epmapper", LRPC, L"ncacn_ip_tcp", L"135", TCP, L"ncadg_ip_udp", L"135", UDP, #ifdef NETBIOS_ON L"ncacn_nb_nb", L"135", NBF, L"ncacn_nb_tcp", L"135", NBT, L"ncacn_nb_ipx", L"135", NBI, #endif #ifdef SPX_ON L"ncacn_spx", L"34280", SPX, #endif #ifdef IPX_ON L"ncadg_ipx", L"34280", IPX, #endif #ifdef APPLETALK_ON L"ncacn_at_dsp", L"Endpoint Mapper", DSP, #endif #ifdef NCADG_MQ_ON L"ncadg_mq", L"EpMapper", MSMQ, #endif L"ncacn_http", L"593", HTTP }; const int LrpcEpMapperEntry = 1; // the entry in the table that contains LRPC unsigned long PartialRetries = 0; unsigned long ReallyTooBusy = 0; typedef struct _EP_LOOKUP_DATA { unsigned int NumberOfEndpoints; unsigned int MaximumEndpoints; unsigned int CurrentEndpoint; RPC_CHAR * PAPI * Endpoints; } EP_LOOKUP_DATA; RPC_STATUS RPC_ENTRY EpResolveEndpoint ( IN UUID PAPI * ObjectUuid, OPTIONAL IN RPC_SYNTAX_IDENTIFIER PAPI * IfId, IN RPC_SYNTAX_IDENTIFIER PAPI * XferId, IN RPC_CHAR PAPI * RpcProtocolSequence, IN RPC_CHAR PAPI * NetworkAddress, IN RPC_CHAR PAPI * Options, IN OUT void PAPI * PAPI * EpLookupHandle, IN unsigned ConnTimeout, IN ULONG CallTimeout, IN CLIENT_AUTH_INFO *AuthInfo, OPTIONAL OUT RPC_CHAR * PAPI * Endpoint ) /*++ Routine Description: The runtime will call this routine to resolve an endpoint. Arguments: ObjectUuid - Optional specifies the object uuid in the binding for which we are trying to resolve an endpoint. Ifid - Pointer to the Syntax Identifier for the Interface Xferid - Pointer to the Syntax Identifier for the Transfer Syntax. RpcProtocolSequence - Supplies the rpc protocol sequence contained in the binding. NetworkAddress - Supplies the network address. This will be used to contact the endpoint mapper on that machine in order to resolve the endpoint. EpLookupHandle - Supplies the current version of the lookup handle for this iteration through the endpoint mapper. A new value for the lookup handle will be returned. If RPC_S_NO_ENDPOINT_FOUND is returned, this parameter will be set to its initial value, zero. ConnTimeout - the connection timeout CallTimeout - the call timeout AutInfo - any auth info that needs to be used during the resolution process Endpoint - Returns the endpoint resolved by the endpoint mapper on the machine specified by the network address argument. The storage for the endpoint must have been allocated using the runtime API I_RpcAllocate. Return Value: RPC_S_OK - The endpoint was successfully resolved. EP_S_NOT_REGISTERED - There are no more endpoints to be found for the specified combination of interface, network address, and lookup handle. RPC_S_OUT_OF_MEMORY - Insufficient memory is available to allow resolution of the endpoint. EP_S_CANT_PERFORM_OP - The operation failed due to misc. error e.g. unable to bind to the EpMapper. --*/ { RPC_BINDING_HANDLE MapperHandle = 0; RPC_STATUS RpcStatus; twr_p_t InputTower = 0; twr_p_t OutputTower[4]; unsigned long Returned; error_status ErrorStatus; ept_lookup_handle_t ContextHandle = 0; EP_LOOKUP_DATA PAPI * EpLookupData = (EP_LOOKUP_DATA PAPI *) *EpLookupHandle; unsigned long RetryCount, i; unsigned char PAPI * EPoint; PSID SidToUse; int NormalizeRetryCount; BOOL SidFound; CHeapAnsi AnsiNWAddr, AnsiOptions; CStackAnsi AnsiProtseq; CNlUnicode nlEndpoint; // We have already taken all of the endpoints from the endpoint mapper; // now we just return them back to the runtime one at a time. ReturnEndpointFromList: ASSERT(*Endpoint == 0); if ( EpLookupData != 0 ) { // When we reach the end of the list of endpoints, return an error, // and set things up so that we will start at the beginning again. if ( EpLookupData->CurrentEndpoint == EpLookupData->NumberOfEndpoints ) { RpcpErrorAddRecord(EEInfoGCRuntime, EPT_S_NOT_REGISTERED, EEInfoDLEpResolveEndpoint10, RpcProtocolSequence, NetworkAddress, IfId->SyntaxGUID.Data1, (ULONG)((EpLookupData->CurrentEndpoint << 16) | EpLookupData->NumberOfEndpoints)); EpLookupData->CurrentEndpoint = 0; return(EPT_S_NOT_REGISTERED); } *Endpoint = DuplicateString( EpLookupData->Endpoints[EpLookupData->CurrentEndpoint]); EpLookupData->CurrentEndpoint += 1; if ( *Endpoint == 0 ) { return(RPC_S_OUT_OF_MEMORY); } return(RPC_S_OK); } // Otherwise, we need to take the list of endpoints from the endpoint // mapper. EpLookupData = (EP_LOOKUP_DATA PAPI *) RpcpFarAllocate( sizeof(EP_LOOKUP_DATA)); if ( EpLookupData == 0 ) { return(RPC_S_OUT_OF_MEMORY); } EpLookupData->MaximumEndpoints = 64; EpLookupData->Endpoints = (RPC_CHAR * PAPI *) RpcpFarAllocate( sizeof(RPC_CHAR PAPI *) * EpLookupData->MaximumEndpoints); if ( EpLookupData->Endpoints == 0 ) { RpcStatus = RPC_S_OUT_OF_MEMORY; goto CleanupAndReturn; } EpLookupData->NumberOfEndpoints = 0; EpLookupData->CurrentEndpoint = 0; RpcStatus = AnsiNWAddr.Attach(NetworkAddress); if ( RpcStatus != RPC_S_OK ) { ASSERT( RpcStatus == RPC_S_OUT_OF_MEMORY ); goto CleanupAndReturn; } i = 1+RpcpStringLength(RpcProtocolSequence); *(AnsiProtseq.GetPAnsiString()) = (char *)_alloca(i); RpcStatus = AnsiProtseq.Attach(RpcProtocolSequence, i, i * 2); if ( RpcStatus != RPC_S_OK ) { ASSERT( RpcStatus == RPC_S_OUT_OF_MEMORY ); goto CleanupAndReturn; } RpcStatus = AnsiOptions.Attach(Options); if ( RpcStatus != RPC_S_OK ) { ASSERT( RpcStatus == RPC_S_OUT_OF_MEMORY ); goto CleanupAndReturn; } RpcStatus = BindToEpMapper(&MapperHandle, NetworkAddress, RpcProtocolSequence, Options, ConnTimeout, CallTimeout, AuthInfo ); if ( RpcStatus != RPC_S_OK ) { MapperHandle = 0; goto CleanupAndReturn; } RpcStatus = TowerConstruct((RPC_IF_ID PAPI *) IfId, (RPC_TRANSFER_SYNTAX PAPI *) XferId, (char PAPI *) AnsiProtseq, NULL, (char PAPI *) AnsiNWAddr, &InputTower); if ( RpcStatus != RPC_S_OK ) { goto CleanupAndReturn; } NormalizeRetryCount = 0; SidFound = FALSE; for (RetryCount = 0;;) { OutputTower[0] = 0; OutputTower[1] = 0; OutputTower[2] = 0; OutputTower[3] = 0; if (AuthInfo && (AuthInfo->Capabilities & RPC_C_QOS_CAPABILITIES_LOCAL_MA_HINT) && (SidFound == FALSE)) { // use ept_map_auth if we have the local hint and the SID. ASSERT(AuthInfo->ServerSid); RpcStatus = NormalizeAccountSid(AuthInfo->ServerSid, &SidToUse, &NormalizeRetryCount); if (RpcStatus != RPC_S_OK) { goto CleanupAndReturn; } } else SidToUse = NULL; RpcTryExcept { if (SidToUse) { ept_map_auth (MapperHandle, ObjectUuid, InputTower, (PISID)SidToUse, &ContextHandle, 4L, &Returned, &OutputTower[0], &ErrorStatus); } else { ept_map(MapperHandle, ObjectUuid, InputTower, &ContextHandle, 4L, &Returned, &OutputTower[0], &ErrorStatus); } } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { ErrorStatus = RpcExceptionCode(); } RpcEndExcept if ((NormalizeRetryCount != 0) && (ErrorStatus == EP_S_NOT_REGISTERED)) { ASSERT(AuthInfo); ASSERT(AuthInfo->ServerSid); ASSERT(AuthInfo->Capabilities & RPC_C_QOS_CAPABILITIES_LOCAL_MA_HINT); continue; } if ( ErrorStatus == RPC_S_SERVER_TOO_BUSY) { // RPC_S_SERVER_TOO_BUSY can happen only if we successfully // established connection. This means the SID was good. SidFound = TRUE; if (RetryCount < 3) { RetryCount ++; PartialRetries++; continue; } else { ReallyTooBusy++; } } if ( ErrorStatus != 0 ) { // For DCE interop the endpoint mapper returns DCE errors on the // wire. We need to map some of them to the MS RPC ones. // Remove this SPN from our cache in case it is no longer valid, to be safe we will // do this for any error code returned from the ep mapper if (AuthInfo && (AuthInfo->Capabilities & RPC_C_QOS_CAPABILITIES_LOCAL_MA_HINT)) { (void) RemoveFromSIDCache(AuthInfo->ServerPrincipalName); } switch (ErrorStatus) { case EP_S_CANT_PERFORM_OP : RpcStatus = EPT_S_CANT_PERFORM_OP; break; case EP_S_NOT_REGISTERED : RpcpErrorAddRecord(EEInfoGCRuntime, EPT_S_NOT_REGISTERED, EEInfoDLEpResolveEndpoint20, RpcProtocolSequence, NetworkAddress, IfId->SyntaxGUID.Data1, (ULONGLONG)MapperHandle); RpcStatus = EPT_S_NOT_REGISTERED; break; default : RpcStatus = MapFromNcaStatusCode(ErrorStatus); break; } break; } // we completed one call successfully. Remember it so that we don't // normalize the sid again SidFound = TRUE; // If the received buffer is truncated we may receive no output towers with // a non-NULL ContextHandle. This should not happen without corruption. CORRUPTION_ASSERT( ((Returned != 0) || (ContextHandle == 0)) && (Returned <= 4) ); for (i = 0; i < Returned; i++) { if (OutputTower[i] == 0) { ASSERT(!"OutputTower[] contains a NULL element.\n"); RpcStatus = RPC_S_OUT_OF_MEMORY ; break; } RpcStatus = TowerExplode( OutputTower[i], NULL, NULL, NULL, (char PAPI * PAPI *) &EPoint, NULL ); if ( RpcStatus != RPC_S_OK ) { break; } RpcStatus = nlEndpoint.Attach((char *)EPoint); *Endpoint = nlEndpoint; I_RpcFree(EPoint); if ( *Endpoint == 0 ) { RpcStatus = RPC_S_OUT_OF_MEMORY; break; } EpLookupData->Endpoints[EpLookupData->NumberOfEndpoints] = *Endpoint; // We copied the endpoint into the list. Erase its value so that we do not // return a bogus string in case we fail later, for example why exploding the // next tower. The endpoint is a member of DceBinding and should remain NULL // on failure. *Endpoint = 0; EpLookupData->NumberOfEndpoints += 1; if ( EpLookupData->NumberOfEndpoints == EpLookupData->MaximumEndpoints ) { goto OutsideTheLoop; } }//..for Loop over parse all towers/eps in this loop for (i = 0; i < Returned; i++) { MIDL_user_free(OutputTower[i]); } if ( (ContextHandle == 0) || (RpcStatus != RPC_S_OK) ) { break; } } //..for loop over get all endpoints OutsideTheLoop: ASSERT( InputTower != 0 ); I_RpcFree(InputTower); CleanupAndReturn: if ( MapperHandle != 0 ) { RPC_STATUS Status = RpcBindingFree(&MapperHandle); ASSERT( Status == RPC_S_OK ); MapperHandle = 0; } if ( ContextHandle != 0 ) { RpcSsDestroyClientContext(&ContextHandle); ContextHandle = 0; } if ( ( RpcStatus == EPT_S_NOT_REGISTERED ) || ( RpcStatus == RPC_S_OK ) ) { if ( EpLookupData->NumberOfEndpoints != 0 ) { *EpLookupHandle = EpLookupData; goto ReturnEndpointFromList; } RpcStatus = EPT_S_NOT_REGISTERED; } if ( EpLookupData != 0 ) { if ( EpLookupData->Endpoints != 0 ) { while ( EpLookupData->NumberOfEndpoints > 0 ) { EpLookupData->NumberOfEndpoints -= 1; delete EpLookupData->Endpoints[EpLookupData->NumberOfEndpoints]; } RpcpFarFree(EpLookupData->Endpoints); } RpcpFarFree(EpLookupData); } return(RpcStatus); } RPC_STATUS RPC_ENTRY EpGetEpmapperEndpoint( IN OUT RPC_CHAR * PAPI * Endpoint, IN RPC_CHAR PAPI * Protseq ) /*+ Routine Description: Returns the Endpoint mappers well known endpoint for a given protocol sequence. Arguments: Endpoint - Place to store the epmappers well known endpoint. Protsq - Protocol sequence the client wishes to use. Return Value: RPC_S_OK - Found the protocol sequence in the epmapper table and am returning the associated well known endpoint. EPT_S_CANT_PERFORM_OP - Protocol sequence not found. --*/ { RPC_STATUS Status = EPT_S_CANT_PERFORM_OP; RPC_STATUS rc; unsigned int i, Count; if (Protseq != NULL) { Count = sizeof(EpMapperTable)/sizeof(EpMapperTable[0]); for (i = 0; i < Count; i++) { //Search for the protocol sequence client is using. if ( RpcpStringCompare(Protseq, EpMapperTable[i].Protseq) ) { //Not yet found. continue; } else { //Found a match. Grab the epmapper known endpoint. *Endpoint = DuplicateString(EpMapperTable[i].Endpoint); if (*Endpoint == NULL) return RPC_S_OUT_OF_MEMORY; else { Status = RPC_S_OK; break; } } } //for }//if return(Status); } RPC_STATUS RPC_ENTRY BindToEpMapper( OUT RPC_BINDING_HANDLE PAPI * MapperHandle, IN RPC_CHAR * NWAddress OPTIONAL, IN RPC_CHAR * Protseq OPTIONAL, IN RPC_CHAR * Options OPTIONAL, IN unsigned ConnTimeout, IN ULONG CallTimeout, IN CLIENT_AUTH_INFO *AuthInfo OPTIONAL ) /*+ Routine Description: This helper routine is used to by RpcEpRegister[NoReplace], RpcEpUnRegister and EpResolveEndpoint(epclnt.cxx) to bind to the EpMapper. If a Protseq is supplied, that particular protseq is tried, otherwise lrpc is used to connect to the local epmapper. If a NW Address is specified EpMapper at that host is contacted, else local Endpoint mapper is used. Arguments: MapperHandle- Returns binding handle to the Endpoint mapper NWAddress - NW address of the Endpoint mapper to bind to. Ignored if protseq is NULL. Protseq - Protocol sequence the client wishes to use. NULL indicates a call to the local endpoint mapper. ConnTimeout - the connection timeout CallTimeout - the call timeout AuthInfo - authentication information for the resolution Return Value: RPC_S_OK - The ansi string was successfully converted into a unicode string. RPC_S_OUT_OF_MEMORY - Insufficient memory is available for the unicode string. EP_S_CANT_PERFORM_OP - The binding was unsuccessful, possibly because the protocol sequence is not supported. --*/ { RPC_STATUS Status = EPT_S_CANT_PERFORM_OP; RPC_CHAR * BindingString = NULL; unsigned int i, Count; BOOL IsLrpc = FALSE; RPC_SECURITY_QOS_V3 RpcSecurityQos; ASSERT(EpMapperTable[LrpcEpMapperEntry].ProtocolId == LRPC); Count = sizeof(EpMapperTable)/sizeof(EpMapperTable[0]); // If Protseq == NULL use lrpc. if (NULL == Protseq) { ASSERT(NWAddress == 0); BindingString = RPC_STRING_LITERAL("ncalrpc:[epmapper]"); IsLrpc = TRUE; } else { for (i = 0; i < Count; i++) { if (RpcpStringCompare(Protseq, EpMapperTable[i].Protseq) ) continue; // Found it. // skip the optimization if AuthInfo isn't present if (ARGUMENT_PRESENT(AuthInfo)) { // convert local named pipes to use LRPC if necessary if ( (EpMapperTable[i].ProtocolId == NMP) && ( (NWAddress == NULL) || (AuthInfo->Capabilities & RPC_C_QOS_CAPABILITIES_LOCAL_MA_HINT) ) ) { // move directly to the endpoint mapper entry for LRPC i = LrpcEpMapperEntry; } } IsLrpc = (EpMapperTable[i].ProtocolId == LRPC); Status = RpcStringBindingCompose( NULL, EpMapperTable[i].Protseq, NWAddress, EpMapperTable[i].Endpoint, (EpMapperTable[i].ProtocolId == HTTP ? Options : 0), &BindingString); break; } } if (BindingString) { Status = RpcBindingFromStringBinding(BindingString, MapperHandle); } if (BindingString != NULL && Protseq != NULL) { RpcStringFree(&BindingString); } if (Status != RPC_S_OK) { return(EPT_S_CANT_PERFORM_OP); } if (AuthInfo && (RpcpStringNCompare(Protseq, RPC_STRING_LITERAL("ncacn_"), 6) == 0)) { Status = SetAuthInformation (*MapperHandle, AuthInfo); if (Status != RPC_S_OK) return Status; } else if (IsLrpc) { // mutually authenticate the endpoint mapper for LRPC RpcSecurityQos.Version = RPC_C_SECURITY_QOS_VERSION_3; RpcSecurityQos.ImpersonationType = RPC_C_IMP_LEVEL_IMPERSONATE; RpcSecurityQos.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC; RpcSecurityQos.Capabilities = RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH; RpcSecurityQos.AdditionalSecurityInfoType = 0; RpcSecurityQos.u.HttpCredentials = NULL; RpcSecurityQos.Sid = (PVOID)&LocalSystem; Status = RpcBindingSetAuthInfoEx (*MapperHandle, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_WINNT, NULL, // AuthIdentity NULL, // AuthzSvc (RPC_SECURITY_QOS *)&RpcSecurityQos ); if (Status != RPC_S_OK) { RpcBindingFree(MapperHandle); return(EPT_S_CANT_PERFORM_OP); } } Status = RpcMgmtSetComTimeout(*MapperHandle, ConnTimeout); if (Status != RPC_S_OK) { RpcBindingFree(MapperHandle); return(EPT_S_CANT_PERFORM_OP); } Status = RpcBindingSetOption(*MapperHandle, RPC_C_OPT_CALL_TIMEOUT, CallTimeout); if (Status != RPC_S_OK) { RpcBindingFree(MapperHandle); return(Status); } return(RPC_S_OK); } void RPC_ENTRY EpFreeLookupHandle ( IN void PAPI * EpLookupHandle ) { EP_LOOKUP_DATA PAPI * EpLookupData = (EP_LOOKUP_DATA PAPI *) EpLookupHandle; if ( EpLookupData->Endpoints != 0 ) { while ( EpLookupData->NumberOfEndpoints > 0 ) { EpLookupData->NumberOfEndpoints -= 1; delete EpLookupData->Endpoints[EpLookupData->NumberOfEndpoints]; } RpcpFarFree(EpLookupData->Endpoints); } RpcpFarFree(EpLookupData); } HPROCESS hRpcssContext = 0; RPC_STATUS RPC_ENTRY I_RpcServerAllocateIpPort( IN DWORD Flags, OUT USHORT *pPort ) { USHORT Port; RPC_STATUS status; RPC_BINDING_HANDLE hServer; PORT_TYPE type; RPC_SECURITY_QOS_V3 RpcSecurityQos; *pPort = 0; // Figure out what sort of port to allocate if (Flags & RPC_C_USE_INTERNET_PORT) { type = PORT_INTERNET; Flags &= ~RPC_C_USE_INTERNET_PORT; } else if (Flags & RPC_C_USE_INTRANET_PORT) { type = PORT_INTRANET; Flags &= ~RPC_C_USE_INTRANET_PORT; } else { type = PORT_DEFAULT; } // Setup process context in the endpoint mapper if needed. if (hRpcssContext == 0) { HPROCESS hProcess; status = RpcBindingFromStringBinding(RPC_STRING_LITERAL("ncalrpc:[epmapper]"), &hServer); if (status != RPC_S_OK) { return(status); } // mutually authenticate the endpoint mapper RpcSecurityQos.Version = RPC_C_SECURITY_QOS_VERSION_3; RpcSecurityQos.ImpersonationType = RPC_C_IMP_LEVEL_IMPERSONATE; RpcSecurityQos.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC; RpcSecurityQos.Capabilities = RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH; RpcSecurityQos.AdditionalSecurityInfoType = 0; RpcSecurityQos.u.HttpCredentials = NULL; RpcSecurityQos.Sid = (PVOID)&LocalSystem; status = RpcBindingSetAuthInfoEx (hServer, (RPC_CHAR *)&LocalSystem, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_WINNT, NULL, // AuthIdentity NULL, // AuthzSvc (RPC_SECURITY_QOS *)&RpcSecurityQos ); if (status != RPC_S_OK) { return(status); } hProcess = 0; status = OpenEndpointMapper(hServer, &hProcess); RPC_STATUS statust = RpcBindingFree(&hServer); ASSERT(statust == RPC_S_OK); if (status != RPC_S_OK) { return(status); } GlobalMutexRequest(); if (hRpcssContext != 0) { ASSERT(hRpcssContext != hProcess); RpcSmDestroyClientContext(&hProcess); } else { hRpcssContext = hProcess; } GlobalMutexClear(); } // Actually allocate a port. RPC_STATUS allocstatus; status = AllocateReservedIPPort(hRpcssContext, type, &allocstatus, pPort); if (status != RPC_S_OK) { ASSERT(*pPort == 0); return(status); } return(allocstatus); } // Very special code for our older (NT 3.1 Era) servers. // // These server send out unique pointers which will confuse our // stubs while unmarshalling. Here we go through and fixup the // node id's to look like full pointers. // // This code can be removed when support for NT 3.1 era servers // is no longer required. extern "C" void FixupForUniquePointerServers(PRPC_MESSAGE pMessage) { int CurrentPointer = 3; unsigned int NumberOfPointers; unsigned int i; unsigned long __RPC_FAR *pBuffer; pBuffer = (unsigned long __RPC_FAR *) pMessage->Buffer; // The output buffer looks like: // [ out-context handle (20b) ] // [ count (4b) ] // [ max (4b) ] // [ min (4b) ] // [ length (4b) ] // should be the same as count // [ pointer node (count of them) ] ASSERT(pBuffer[5] == pBuffer[8]); NumberOfPointers = pBuffer[5]; ASSERT(pMessage->BufferLength >= 4 * 9 + 4 * NumberOfPointers); for(i = 0; i < NumberOfPointers; i++) { if (pBuffer[9 + i] != 0) { pBuffer[9 + i] = CurrentPointer; CurrentPointer++; } } return; } #if defined(WIN) || defined(WIN32) void __RPC_FAR * __RPC_API MIDL_user_allocate( size_t Size ) /*++ Routine Description: MIDL generated stubs need this routine. Arguments: Size - Supplies the length of the memory to allocate in bytes. Return Value: The buffer allocated will be returned, if there is sufficient memory, otherwise, zero will be returned. --*/ { void PAPI * pvBuf; pvBuf = I_RpcAllocate(Size); return(pvBuf); } void __RPC_API MIDL_user_free ( void __RPC_FAR *Buf ) { I_RpcFree(Buf); } #endif