/*++ 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 extern RPC_STATUS __RPC_FAR MapFromNcaStatusCode ( IN unsigned long NcaStatus ); static ProtseqEndpointPair EpMapperTable[] = { "ncacn_np", "\\pipe\\epmapper", "ncalrpc", "epmapper", "ncacn_ip_tcp", "135", "ncadg_ip_udp", "135", #ifdef NETBIOS_ON "ncacn_nb_nb", "135", "ncacn_nb_tcp", "135", "ncacn_nb_ipx", "135", #endif #ifdef SPX_ON "ncacn_spx", "34280", #endif #ifdef IPX_ON "ncadg_ipx", "34280", #endif #ifdef APPLETALK_ON "ncacn_at_dsp", "Endpoint Mapper", #endif #ifdef NCADG_MQ_ON "ncadg_mq", "EpMapper", #endif "ncacn_http", "593" }; 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; 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; CHeapAnsi AnsiNWAddr, AnsiOptions; CStackAnsi AnsiProtseq; CNlUnicode nlEndpoint; ASSERT(*Endpoint == 0); // 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: 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 ) { RpcpFarFree(EpLookupData); EpLookupData = 0; return(RPC_S_OUT_OF_MEMORY); } EpLookupData->NumberOfEndpoints = 0; EpLookupData->CurrentEndpoint = 0; RpcStatus = AnsiNWAddr.Attach(NetworkAddress); if ( RpcStatus != RPC_S_OK ) { ASSERT( RpcStatus == RPC_S_OUT_OF_MEMORY ); RpcpFarFree(EpLookupData); EpLookupData = 0; return(RpcStatus); } 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 ); RpcpFarFree(EpLookupData); EpLookupData = 0; return(RpcStatus); } RpcStatus = AnsiOptions.Attach(Options); if ( RpcStatus != RPC_S_OK ) { ASSERT( RpcStatus == RPC_S_OUT_OF_MEMORY ); RpcpFarFree(EpLookupData); EpLookupData = 0; return(RpcStatus); } 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; } for (RetryCount = 0;;) { OutputTower[0] = 0; OutputTower[1] = 0; OutputTower[2] = 0; OutputTower[3] = 0; RpcTryExcept { ept_map(MapperHandle, ObjectUuid, InputTower, &ContextHandle, 4L, &Returned, &OutputTower[0], &ErrorStatus); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { ErrorStatus = RpcExceptionCode(); } RpcEndExcept if ( ErrorStatus == RPC_S_SERVER_TOO_BUSY) { 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. 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; } ASSERT( ((Returned != 0) || (ContextHandle == 0)) && (Returned <= 4) ); for (i = 0; i < Returned; i++) { if (OutputTower[i] == 0) { return RPC_S_OUT_OF_MEMORY ; } 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; 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 ); } if ( ContextHandle != 0 ) { RpcSsDestroyClientContext(&ContextHandle); } 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; CStackAnsi AnsiProtseq; CNlUnicode nlEndpoint; unsigned char PAPI * Epoint; if (Protseq != NULL) { #ifdef UNICODE //Must convert the protocol sequence into an ansi string from unicode i = 1 + RpcpStringLength(Protseq); *(AnsiProtseq.GetPAnsiString()) = (char *)_alloca(i); rc = AnsiProtseq.Attach(Protseq, i, i * 2); if ( rc != RPC_S_OK ) { ASSERT( rc == RPC_S_OUT_OF_MEMORY ); return(Status); } #else AnsiProtseq = Protseq; #endif Count = sizeof(EpMapperTable)/sizeof(EpMapperTable[0]); for (i = 0; i < Count; i++) { //Search for the protocol sequence client is using. if ( RpcpStringCompareA((char PAPI *)AnsiProtseq, (char PAPI *)EpMapperTable[i].Protseq) ) { //Not yet found. continue; } else { //Found a match. Grab the epmappers known endpoint. Epoint = (unsigned char __RPC_FAR *)(EpMapperTable[i].Endpoint); rc = nlEndpoint.Attach((char *)Epoint); *Endpoint = nlEndpoint; 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 fHttp = FALSE; Count = sizeof(EpMapperTable)/sizeof(EpMapperTable[0]); // If Protseq == NULL use lrpc. if (NULL == Protseq) { ASSERT(NWAddress == 0); BindingString = RPC_STRING_LITERAL("ncalrpc:[epmapper]"); } else { char * AnsiProtseq; AnsiProtseq = (char *) _alloca(1+RpcpStringLength(Protseq)); if (!AnsiProtseq) { return RPC_S_OUT_OF_MEMORY; } SimpleUnicodeToAnsi(Protseq, AnsiProtseq); if (Options) { fHttp = (0 == RpcpStringCompare(Protseq, RPC_STRING_LITERAL("ncacn_http"))); } for (i = 0; i < Count; i++) { if (RpcpStringCompareA(AnsiProtseq, (char PAPI *) EpMapperTable[i].Protseq) ) continue; // Found it. RPC_CHAR * Endpoint; Endpoint = (RPC_CHAR *) _alloca(sizeof(RPC_CHAR) * (1+strlen(EpMapperTable[i].Endpoint))); if (!Endpoint) { return RPC_S_OUT_OF_MEMORY; } SimpleAnsiToPlatform(EpMapperTable[i].Endpoint, Endpoint); Status = RpcStringBindingCompose( NULL, Protseq, NWAddress, Endpoint, (fHttp ? 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; } Status = RpcMgmtSetComTimeout(*MapperHandle, ConnTimeout); if (Status != RPC_S_OK) { return(EPT_S_CANT_PERFORM_OP); } 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; *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); } 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