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.
 
 
 
 
 
 

3256 lines
73 KiB

/*++
Copyright (C) Microsoft Corporation, 1991 - 1999
Module Name:
worker.c
Abstract:
This file contains the real stuff for the EP Mapper.
Author:
Bharat Shah (barat) 17-2-92
Revision History:
06-03-97 gopalp Added code to cleanup stale EP Mapper entries.
--*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sysinc.h>
#include <wincrypt.h>
#include <rpc.h>
#include <rpcndr.h>
#include "epmp.h"
#include "eptypes.h"
#include "local.h"
#include "twrproto.h"
#include <winsock2.h>
#define StringCompareA lstrcmpiA
#define StringLengthA lstrlenA
#define EP_S_DUPLICATE_ENTRY 0x16c9a0d8
RPC_IF_ID LocalNullUuid = {0};
UUID MgmtIf = {
0xafa8bd80,
0x7d8a,
0x11c9,
{0xbe, 0xf4, 0x08, 0x00, 0x2b, 0x10, 0x29, 0x89}
};
const int IP_ADDR_OFFSET = 0x4b;
void
PatchTower(
IN OUT twr_t *Tower,
IN int address)
{
int UNALIGNED *pIPAddr;
pIPAddr = (int UNALIGNED *) (((char *) Tower) + IP_ADDR_OFFSET);
//
// Patch the tower
//
*pIPAddr = address;
}
//
// Forward definitions
//
USHORT
GetProtseqIdAnsi(
PSTR Protseq
);
RPC_STATUS
DelayedUseProtseq(
USHORT id
);
VOID
CompleteDelayedUseProtseqs(
void
);
void
DeletePSEP(
PIFOBJNode Node,
char * Protseq,
char * Endpoint
);
void
PurgeOldEntries(
PIFOBJNode Node,
PPSEPNode List,
BOOL StrictMatch
);
RPC_STATUS
MatchPSAndEP (
PPSEPNode Node,
void *Pseq,
void * Endpoint,
unsigned long Version
);
PPSEPNode
FindPSEP (
register PPSEPNode List,
char * Pseq,
char * Endpoint,
unsigned long Version,
PFNPointer2 Compare
);
PIFOBJNode
FindIFOBJNode(
register PIFOBJNode List,
UUID * Obj,
UUID * IF,
unsigned long Version,
unsigned long Inq,
unsigned long VersOpts,
PFNPointer Compare
);
PIENTRY
MatchByKey(
register PIENTRY pList,
unsigned long key
)
/*++
Routine Description:
This routine Seqrches the Link-list of IF-OBJ nodes based on
key supplied.
Arguments:
List - The Linked list [head] - to be searched
key - The Id
Return Value:
Returns a pointer to the matching IFObj node in the list or returns NULL.
--*/
{
CheckInSem();
for (; pList && pList->Id < key; pList = pList->Next)
{
; // Empty body
}
return(pList);
}
RPC_STATUS RPC_ENTRY
GetForwardEp(
UUID *IfId,
RPC_VERSION * IFVersion,
UUID *Object,
unsigned char * Protseq,
void * * EpString
)
/*++
Routine Description:
Server rutime has received a pkt destined for a dynamically
declared endpoint. Epmapper must return the servers endpoint
to enable the runtime to correctly forward the pkt.
Arguments:
IF - Server Interface UUID
IFVersion - Version of the Interface
Obj - UUID of the Object
Protseq - Ptotocol sequence the interface is using.
EpString - Place to store the endpoint structure.
Return Value:
Returns a pointer to a string containing the server's endpoint.
RPC_S_OUT_OF_MEMORY
EPT_S_NOT_REGISTERED
---*/
{
PIFOBJNode pNode;
PPSEPNode pPSEPNode;
unsigned short len;
char * String;
PFNPointer Match;
unsigned long InqType;
unsigned long Version = VERSION(IFVersion->MajorVersion,
IFVersion->MinorVersion);
if (memcmp((char *)IfId, (char *)&MgmtIf, sizeof(UUID)) == 0)
{
InqType = RPC_C_EP_MATCH_BY_OBJ;
Match = SearchIFObjNode;
}
else
{
InqType = 0;
Match = WildCardMatch;
}
*EpString = 0;
EnterSem();
pNode = IFObjList;
if (pNode == 0)
{
LeaveSem();
return(EPT_S_NOT_REGISTERED);
}
while (pNode != 0)
{
pNode = FindIFOBJNode(
pNode,
Object,
IfId,
Version,
InqType,
0,
Match
);
if (pNode == 0)
{
LeaveSem();
return(EPT_S_NOT_REGISTERED);
}
pPSEPNode = pNode->PSEPlist;
pPSEPNode = FindPSEP(
pPSEPNode,
Protseq,
NULL,
0L,
MatchPSAndEP
);
if (pPSEPNode == 0)
{
pNode = pNode->Next;
if (pNode == 0)
{
LeaveSem();
return(EPT_S_NOT_REGISTERED);
}
continue;
}
// We now have a PSEPNode. We just ought to return the first one!
// Use I_RpcAllocate To Allocate because runtime will free this!
String = I_RpcAllocate( len = (strlen(pPSEPNode->EP) + 1) );
if (String == 0)
{
LeaveSem();
return(RPC_S_OUT_OF_MEMORY);
}
// Do we really need memset()?. memcpy set the bytes...
memset(String, 0, len);
memcpy(String, pPSEPNode->EP, len);
*EpString = String;
LeaveSem();
return(RPC_S_OK);
} // while loop
// we never go through here.
return(EPT_S_NOT_REGISTERED);
}
RPC_STATUS
SearchIFObjNode(
PIFOBJNode pNode,
UUID *Object,
UUID *IfUuid,
unsigned long Version,
unsigned long InqType,
unsigned long VersOption
)
/*++
Routine Description:
This routine Seqrches the Link-list of IF-OBJ nodes based on
Obj, IFUuid, IFVersion, Inqtype [Ignore OBJ, IgnoreIF, etc],
and VersOption [Identical Ver, Compatible Vers. etc]
Arguments:
List - The Linked list head - to be searched
Obj - UUID of the Object
IF - Interface UUID
Version - Version of the Interface
InqType - Type of Inquiry [Filter options based on IF/Obj/Both
VersOpts - Filter options based on Version
Return Value:
Returns a pointer to the matching IFObj node in the list or returns NULL.
--*/
{
switch (InqType)
{
default:
case RPC_C_EP_ALL_ELTS:
return 0;
case RPC_C_EP_MATCH_BY_BOTH:
if (memcmp(
(char *)&pNode->ObjUuid,
(char *)Object,
sizeof(UUID))
)
return(1);
// Intentionally Fall through ..
case RPC_C_EP_MATCH_BY_IF:
return(!(
(
!memcmp(
(char *)&pNode->IFUuid,
(char *)IfUuid,
sizeof(UUID)
)
)
&&
(
( (VersOption == RPC_C_VERS_UPTO)
&& pNode->IFVersion <= Version)
|| ( (VersOption == RPC_C_VERS_COMPATIBLE)
&& ((pNode->IFVersion & 0xFFFF0000) ==
(Version & 0xFFFF0000))
&& (pNode->IFVersion >= Version)
)
|| ( (VersOption == RPC_C_VERS_EXACT)
&& (pNode->IFVersion == Version)
)
|| (VersOption == RPC_C_VERS_ALL)
|| ( (VersOption == RPC_C_VERS_MAJOR_ONLY)
&& ((pNode->IFVersion & 0xFFFF0000L)
== (Version & 0xFFFF0000L))
)
|| ( (VersOption ==
I_RPC_C_VERS_UPTO_AND_COMPATIBLE)
&& ((pNode->IFVersion & 0xFFFF0000L)
== (Version & 0xFFFF0000L))
&& (pNode->IFVersion <= Version)
)
)
)
); // return(
case RPC_C_EP_MATCH_BY_OBJ:
return(
memcmp(
(char *)&pNode->ObjUuid,
(char *)Object,
sizeof(UUID)
)
);
} // switch
}
PIFOBJNode
FindIFOBJNode(
register PIFOBJNode List,
UUID * Obj,
UUID * IF,
unsigned long Version,
unsigned long Inq,
unsigned long VersOpts,
PFNPointer Compare
)
/*++
Routine Description:
This routine Seqrches the Link-list of IF-OBJ nodes based on
Obj and IF specified.
Arguments:
List - The Linked list head - to be searched
Obj - UUID of the Object
IF - Interface UUID
Version - Version of the Interface
Inq - Type of Inquiry [Filter based on IF/OB/Both]
VersOpt - Filter based on version [<=, >=, == etc]
Compare() - A pointer to function used for searching.
WildCardMatch or ExactMatch.
Return Value:
Returns a pointer to the matching IFObj node in the list or returns NULL.
--*/
{
CheckInSem();
for (; (List !=NULL) && (*Compare)(List, Obj, IF, Version, Inq, VersOpts);
List = List->Next)
{
; // Empty body
}
return (List);
}
PPSEPNode
FindPSEP (
register PPSEPNode List,
char * Pseq,
char * Endpoint,
unsigned long Version,
PFNPointer2 Compare
)
/*++
Routine Description:
This routine Seqrches the Link-list of PSEP nodes based on
Protocol sequence and Endpoint specified.
Arguments:
List - The Linked list head - to be searched
Pseq - Protocol sequence string specified
Endpoint - Endpoint string specified
Version - Version of the Interface
Compare() - A pointer to function used for searching.
Return Value:
Returns a pointer to the matching PSEP node in the list or returns NULL.
--*/
{
CheckInSem();
for (; List && (*Compare)(List, Pseq, Endpoint, Version); List = List->Next)
{
; // Empty body
}
return (List);
if (Version); // May need this if we overload FindNode and collapse
// FindPSEP and FindIFOBJ
}
RPC_STATUS
ExactMatch(
PIFOBJNode Node,
UUID *Obj,
UUID *IF,
unsigned long Version,
unsigned long InqType,
unsigned long VersOptions
)
/*++
Routine Description:
This routine compares a Node in the IFOBJList to [Obj, IF, Version] triple
and returns 0 if there is an exact match else returns 1
Arguments:
Node - An IFOBJ node
Obj - UUID of the Object
IF - Interface UUID
Version - Version of the Interface
Return Value:
Returns 0 if there is an exact match; 1 otherwise
--*/
{
return(( memcmp(&Node->ObjUuid, Obj,sizeof(UUID))
|| memcmp(&Node->IFUuid, IF, sizeof(UUID))
|| (Node->IFVersion != Version)));
}
RPC_STATUS
WildCardMatch (
PIFOBJNode Node,
UUID *Obj,
UUID *IF,
unsigned long Version,
unsigned long InqType,
unsigned long VersOptions
)
/*++
Routine Description:
This routine compares a Node in the IFOBJList to [Obj, IF, Version] triple
and returns 0 if there is an exact match or if registered IF-Obj node
has a NULL Obj UUid and version of the registed IF_Obj is >= that
supplied
Arguments:
Node - An IFOBJ node
Obj - UUID of the Object
IF - Interface UUID
Version - Version of the Interface
Return Value:
Returns 0 if there is a wild card match ; 1 otherwise
--*/
{
if ( (!memcmp(&Node->IFUuid, IF, sizeof(UUID)))
&& ((Node->IFVersion & 0xFFFF0000L) == (Version & 0xFFFF0000L))
&& (Node->IFVersion >= Version)
&& ((!memcmp(&Node->ObjUuid, Obj, sizeof(UUID))) ||
(IsNullUuid(&Node->ObjUuid)) ) )
{
return(0);
}
return(1);
}
RPC_STATUS
MatchPSAndEP (
PPSEPNode Node,
void *Pseq,
void * Endpoint,
unsigned long Version
)
/*++
Routine Description:
This routine Matches A Node on PSEP list with given Protseq and Endpoint
If Pseq is given pseqs are matched, if Endpoint is given Endpoints
are matched, if neither is given returns true, if both are given
both are matched.
Arguments:
Node - A PSEP node on PSEP list.
Pseq - Protocol Sequence string
Endpoint - Endpoint string
Return Value:
Returns 0 if Matched successfully, 1 otherwise.
--*/
{
return ( (Pseq && RpcpStringCompareA(Node->Protseq, Pseq))
|| (Endpoint && RpcpStringCompareA(Node->EP, Endpoint)) );
}
void
PurgeOldEntries(
PIFOBJNode Node,
PPSEPNode List,
BOOL StrictMatch
)
{
PPSEPNode Tmp, DeleteMe;
char * Endpoint = 0;
CheckInSem();
Tmp = Node->PSEPlist;
while (Tmp != 0)
{
if (StrictMatch == TRUE)
Endpoint = Tmp->EP;
if (DeleteMe = FindPSEP(List, Tmp->Protseq, Endpoint, 0L, MatchPSAndEP))
{
DeleteMe = Tmp;
Tmp = Tmp->Next;
UnLinkFromPSEPList(&Node->PSEPlist, DeleteMe);
DeleteMe->Signature = FREE;
FreeMem(DeleteMe);
}
else
{
Tmp = Tmp->Next;
}
}
}
RPC_STATUS
IsNullUuid (
UUID * Uuid
)
/*++
Routine Description:
This routine checks if a UUID is Nil
Arguments:
Uuid - UUID to be tested
Return Value:
Returns 1 if it is a Nil UUID
0 otherwise.
--*/
{
unsigned long PAPI * Vector;
Vector = (unsigned long PAPI *) Uuid;
if ( (Vector[0] == 0)
&& (Vector[1] == 0)
&& (Vector[2] == 0)
&& (Vector[3] == 0))
return(1);
return(0);
}
twr_p_t
NewTower(
twr_p_t Tower
)
/*++
Routine Description:
This routine returns a New, Duplicated tower
Arguments:
Tower - The tower that needs to be duplicated.
Return Value:
Retunes a pointer to a new tower if successful else returns
NULL
--*/
{
unsigned short len;
twr_p_t NewTower;
len = (unsigned short)(sizeof(Tower->tower_length) + Tower->tower_length);
if ((NewTower = MIDL_user_allocate(len)) != NULL)
{
memcpy((char *)NewTower, (char *)Tower, len);
}
return(NewTower);
}
const unsigned long EPLookupHandleSignature = 0xFAFAFAFA;
PSAVEDCONTEXT
GetNewContext(
unsigned long Type
)
/*++
Routine Description
++*/
{
PSAVEDCONTEXT Context;
if ( ((Context = AllocMem(sizeof(SAVEDCONTEXT))) == 0) )
return 0;
memset(Context, 0, sizeof(SAVEDCONTEXT));
Context->Cb = sizeof(SAVEDCONTEXT);
Context->Type = Type;
Context->Signature = EPLookupHandleSignature;
EnLinkContext(Context);
return(Context);
}
const unsigned int EPMapSignature = 0xCBBCCBBC;
const unsigned int EPLookupSignature = 0xABBAABBA;
RPC_STATUS
AddToSavedContext(
PSAVEDCONTEXT Context,
PIFOBJNode Node,
PPSEPNode Psep,
unsigned long Calltype,
BOOL fPatchTower,
int PatchTowerAddress
)
{
void * NewNode;
PSAVEDTOWER SavedTower;
PSAVED_EPT SavedEndpoint;
unsigned long Size;
unsigned long TowerSize;
ASSERT(Calltype == Context->Type);
switch (Calltype)
{
case EP_MAP:
Size = sizeof(SAVEDTOWER) ;
if ((NewNode = AllocMem(Size)) == 0)
return(RPC_S_OUT_OF_MEMORY);
SavedTower = (PSAVEDTOWER) NewNode;
memset(SavedTower, 0, Size);
SavedTower->Cb = Size;
SavedTower->Signature = EPMapSignature;
SavedTower->Tower = NewTower(Psep->Tower);
if (SavedTower->Tower == 0)
{
FreeMem(NewNode);
return(RPC_S_OUT_OF_MEMORY);
}
if (fPatchTower)
{
PatchTower(SavedTower->Tower, PatchTowerAddress);
}
break;
case EP_LOOKUP:
Size = sizeof(SAVED_EPT) + strlen(Node->Annotation) + 1;
if ((NewNode = AllocMem(Size)) == 0)
return(RPC_S_OUT_OF_MEMORY);
SavedEndpoint = (PSAVED_EPT) NewNode;
memset(SavedEndpoint, 0, Size);
SavedEndpoint->Cb = Size;
SavedEndpoint->Signature = EPLookupSignature;
SavedEndpoint->Tower = NewTower(Psep->Tower);
SavedEndpoint->Annotation = (char *)NewNode +
sizeof(SAVED_EPT);
memcpy( (char *) &SavedEndpoint->Object,
(char *)&Node->ObjUuid,
sizeof(UUID)
);
strcpy(SavedEndpoint->Annotation, Node->Annotation);
if (SavedEndpoint->Tower == 0)
{
FreeMem(NewNode);
return(RPC_S_OUT_OF_MEMORY);
}
if (fPatchTower)
{
PatchTower(SavedEndpoint->Tower, PatchTowerAddress);
}
break;
default:
ASSERT(!"Unknown lookup type\n");
return(RPC_S_INTERNAL_ERROR);
break;
}
Link((PIENTRY *)(&Context->List), NewNode);
return(RPC_S_OK);
}
RPC_STATUS
GetEntriesFromSavedContext(
PSAVEDCONTEXT Context,
char * Buffer,
unsigned long Requested,
unsigned long *Returned
)
{
PIENTRY SavedEntry = (PIENTRY)Context->List;
PIENTRY TmpEntry;
unsigned long Type = Context->Type;
while ( (*Returned < Requested) && (SavedEntry != 0) )
{
switch (Type)
{
case EP_MAP:
((I_Tower *)Buffer)->Tower = ((PSAVEDTOWER)SavedEntry)->Tower;
Buffer = Buffer + sizeof(I_Tower);
break;
case EP_LOOKUP:
((ept_entry_t *)Buffer)->tower = ((PSAVED_EPT)SavedEntry)->Tower;
strcpy(((ept_entry_t *)Buffer)->annotation,
((PSAVED_EPT)SavedEntry)->Annotation);
memcpy(Buffer,(char *)&((PSAVED_EPT)SavedEntry)->Object,
sizeof(UUID));
Buffer = Buffer + sizeof(ept_entry_t);
break;
default:
ASSERT(!"Unknown Inquiry Type");
break;
}
(*Returned)++;
TmpEntry = SavedEntry;
SavedEntry = SavedEntry->Next;
UnLink((PIENTRY *)&Context->List, TmpEntry);
FreeMem(TmpEntry);
}
return(RPC_S_OK);
}
RPC_STATUS
GetEntries(
UUID *ObjUuid,
UUID *IFUuid,
unsigned long Version,
char * Pseq,
ept_lookup_handle_t *key,
char * Buffer,
unsigned long Calltype,
unsigned long Requested,
unsigned long *Returned,
unsigned long InqType,
unsigned long VersOptions,
PFNPointer Match
)
/*++
Routine Description:
This is a generic routine for retreiving a series [as spec. by Requested]
of Towers (in case of Map) or ept_entry_t's in case of Lookup.
Arguments:
ObjUuid - Object Uuid
IfUuid - Interface Uuid
Version - InterfaceVersion [hi ushort = VerMajor, lo ushort VerMinor]
Protseq - An ascii string specifying the protocol seq.
key - A resume key - If NULL, search is started from the beginning
if non-null, represents an encoding from where the epmapper
supposed to start searching. It is an opaque value as far
as the client is concerned.
Buffer - A buffer of entries returned
Calltype - A flag to indicate whether Ep_entries or string bindings
are to be returned.
Requested - Max no. of entries requested
Returned - Actual no of entroes returned
Return Value:
RPC_S_OUT_OF_MEMORY
RPC_S_OK
EP_S_NOT_REGISTERED
--*/
{
PIFOBJNode pNode=NULL, pList = IFObjList;
unsigned long err=0, fResumeNodeFound=0;
PPSEPNode pPSEPNode;
char * buffer = Buffer;
PSAVEDCONTEXT Context = (PSAVEDCONTEXT) *key;
ept_lookup_handle_t LOOKUP_FINISHED = (ept_lookup_handle_t) LongToPtr(0xffffffff);
int UNALIGNED *pIPAddr;
BOOL fPatchTower;
int PatchTowerAddress;
SOCKADDR_STORAGE SockAddr;
ULONG BufferSize;
int FormatType;
RPC_STATUS RpcStatus;
*Returned = 0;
EnterSem();
if (*key)
{
if (*key == LOOKUP_FINISHED)
{
*key = 0;
LeaveSem();
return(EP_S_NOT_REGISTERED);
}
if (Context->Signature != EPLookupHandleSignature)
{
LeaveSem();
return EP_S_CANT_PERFORM_OP;
}
err = GetEntriesFromSavedContext(Context, Buffer, Requested, Returned);
if (Context->List == 0)
{
UnLink((PIENTRY *)&GlobalContextList, (PIENTRY)Context);
FreeMem(Context);
// Setting the Key To FFFFFFFFL is a hack for down level
// Version 1.0 Ep Clients who never expected getting a key 0
// and Success!
if (Requested <= 1)
*key = LOOKUP_FINISHED;
else
*key = 0L;
LeaveSem();
return(err);
}
LeaveSem();
return(err);
}
*key = 0;
while ((!err))
{
if ((pNode = FindIFOBJNode(
pList,
ObjUuid,
IFUuid,
Version,
InqType,
VersOptions,
Match)) == 0)
{
break;
}
pPSEPNode = pNode->PSEPlist;
while (pPSEPNode != 0)
{
if ((pPSEPNode = FindPSEP(pPSEPNode, Pseq, NULL, 0L,
MatchPSAndEP)) == 0)
break;
fPatchTower = FALSE;
if (StringCompareA(pPSEPNode->Protseq, "ncacn_ip_tcp") == 0
|| StringCompareA(pPSEPNode->Protseq, "ncadg_ip_udp") == 0
|| StringCompareA(pPSEPNode->Protseq, "ncacn_http") == 0)
{
pIPAddr = (int UNALIGNED *) ((char *) pPSEPNode->Tower + IP_ADDR_OFFSET);
if (*pIPAddr == 0)
{
BufferSize = sizeof(SockAddr);
RpcStatus = I_RpcServerInqLocalConnAddress(NULL,
&SockAddr,
&BufferSize,
&FormatType);
if (RpcStatus == RPC_S_OK)
{
// IPv6 towers don't exist yet - they are not defined
// by DCE. Do patching for IPv4 only
if (FormatType == RPC_P_ADDR_FORMAT_TCP_IPV4)
{
PatchTowerAddress = ((SOCKADDR_IN *)&SockAddr)->sin_addr.S_un.S_addr;
fPatchTower = TRUE;
}
}
}
}
if (*Returned < Requested)
{
err = PackDataIntoBuffer(&buffer, pNode, pPSEPNode, Calltype, fPatchTower, PatchTowerAddress);
if (err == RPC_S_OK)
{
(*Returned)++;
}
else
{
ASSERT(err == RPC_S_OUT_OF_MEMORY);
break;
}
}
else
{
if (Context == 0)
{
*key = (ept_lookup_handle_t) (Context = GetNewContext(Calltype));
if (Context == 0)
{
err = RPC_S_OUT_OF_MEMORY;
break;
}
}
AddToSavedContext(Context, pNode, pPSEPNode, Calltype, fPatchTower, PatchTowerAddress);
}
pPSEPNode = pPSEPNode->Next;
} // while - over PSEPList
pList = pNode->Next;
} // while - over IFOBJList
LeaveSem();
if ((*Returned == 0) && Requested && (!err))
{
err = EP_S_NOT_REGISTERED;
}
if ((*Returned <= Requested) && (Context == 0))
{
if (Requested <= 1)
*key = LOOKUP_FINISHED;
else
*key = 0L;
}
return(err);
}
RPC_STATUS
PackDataIntoBuffer(
char * * Buffer,
PIFOBJNode Node,
PPSEPNode PSEP,
unsigned long Type,
BOOL fPatchTower,
int PatchTowerAddress
)
/*++
Routine Description:
This routine copies 1 entry [Either a Tower or ept_entry]
in the Buffer, increments buffer appropriately.
Arguments:
BindingHandle - An explicit binding handle to the EP.
Node - IFOBJNode
PSEP - PSEPNode
Type - Type of entry to be copied
PatchTower - if TRUE, the newly created tower needs to be patched. If
FALSE, the tower doesn't need to be patched
PatchTowerAddress - an IPv4 representation of the address to be put
in the tower. The IPv4 address must be in network byte order
Return Value:
RPC_S_OK or RPC_S_* for error
--*/
{
I_Tower * Twr;
ept_entry_t *p;
switch (Type)
{
case EP_MAP:
Twr = (I_Tower *)(* Buffer);
Twr->Tower = NewTower(PSEP->Tower);
if (Twr->Tower == 0)
{
return(RPC_S_OUT_OF_MEMORY);
}
if (fPatchTower)
PatchTower(Twr->Tower, PatchTowerAddress);
*Buffer += sizeof(I_Tower);
break;
case EP_LOOKUP:
p = (ept_entry_t *)(*Buffer);
p->tower = NewTower(PSEP->Tower);
if (p->tower == 0)
{
return(RPC_S_OUT_OF_MEMORY);
}
if (fPatchTower)
PatchTower(p->tower, PatchTowerAddress);
memcpy( *Buffer, (char *)&Node->ObjUuid, sizeof(UUID) );
strcpy(p->annotation, Node->Annotation);
*Buffer += sizeof(ept_entry_t);
break;
default:
ASSERT(!"Unknown type");
break;
}
return(RPC_S_OK);
}
void
ept_cleanup_handle_t_rundown(
ept_cleanup_handle_t hEpCleanup
)
/*++
Routine Description:
This routine cleans up the entries registered by the process
associated with this context handle hEpCleanup.
Arguments:
hEpCleanup - The context handle for which the rundown is
being done.
Return Value:
None.
--*/
{
PIFOBJNode NodesListToDelete = NULL;
PIFOBJNode pIterator, DeleteMe, pPreviousNode;
PPSEPNode pTempPSEP, pDeletePSEP;
PEP_CLEANUP ProcessCtxt = (PEP_CLEANUP) hEpCleanup;
#ifdef DBG_DETAIL
PIFOBJNode pTemp, pLast;
#endif // DBG_DETAIL
if (ProcessCtxt == NULL)
{
return;
}
EnterSem();
ASSERT(IFObjList);
ASSERT(cTotalEpEntries > 0);
ASSERT(ProcessCtxt->EntryList);
ASSERT(ProcessCtxt->cEntries > 0);
ASSERT(ProcessCtxt->EntryList->OwnerOfList == ProcessCtxt);
ASSERT_PROCESS_CONTEXT_LIST_COUNT(ProcessCtxt, ProcessCtxt->cEntries);
#ifdef DBG_DETAIL
DbgPrint("RPCSS: Entered Cleanup Rundown for [%p] with (%d) entries\n",
hEpCleanup, ProcessCtxt->cEntries);
DbgPrint("RPCSS: Dump of IFOBJList\n");
pTemp = IFObjList;
pLast = IFObjList;
while (pTemp)
{
DbgPrint("RPCSS: \t\t[%p]\n", pTemp);
pLast = pTemp;
pTemp = pTemp->Next;
}
DbgPrint("RPCSS: --------------------\n");
while (pLast)
{
DbgPrint("RPCSS: \t\t\t[%p]\n", pLast);
pLast = pLast->Prev;
}
#endif // DBG_DETAIL
// Save the previous Node.
pPreviousNode = ProcessCtxt->EntryList->Prev;
pIterator = ProcessCtxt->EntryList;
while ((pIterator != NULL) && (pIterator->OwnerOfList == ProcessCtxt))
{
ProcessCtxt->cEntries--;
cTotalEpEntries--;
#ifdef DBG_DETAIL
DbgPrint("RPCSS: cTotalEpEntries-- [%p] (%d) - Cleanup\n", hEpCleanup, cTotalEpEntries);
#endif // DBG_DETAIL
DeleteMe = pIterator;
pIterator = pIterator->Next;
// Add to a list that will be deleted later.
DeleteMe->Next = NodesListToDelete;
}
ASSERT(ProcessCtxt->cEntries == 0);
//
// Adjust the links
//
if (pPreviousNode)
{
// Adjust forward link
pPreviousNode->Next = pIterator;
}
else
{
ASSERT(ProcessCtxt->EntryList == IFObjList);
}
if (pIterator)
{
// Adjust backward link
pIterator->Prev = pPreviousNode;
}
//
// Empty the EP Mapper table, if necessary.
//
if (ProcessCtxt->EntryList == IFObjList)
{
if (pIterator)
{
ASSERT(cTotalEpEntries > 0);
// New Head for Ep Mapper list
IFObjList = pIterator;
}
else
{
ASSERT(cTotalEpEntries == 0);
// Memory for this node is already freed in the while loop above.
IFObjList = NULL;
}
}
else
{
ASSERT(cTotalEpEntries > 0);
}
LeaveSem();
//
// Free entities outside the lock.
//
FreeMem(ProcessCtxt);
while (NodesListToDelete != NULL)
{
DeleteMe = NodesListToDelete;
NodesListToDelete = NodesListToDelete->Next;
// Delete the PSEP list.
pTempPSEP = DeleteMe->PSEPlist;
while (pTempPSEP != NULL)
{
pDeletePSEP = pTempPSEP;
pTempPSEP = pTempPSEP->Next;
FreeMem(pDeletePSEP);
}
FreeMem(DeleteMe);
}
}
void
ept_insert(
handle_t h,
unsigned32 NumEntries,
ept_entry_t Entries[],
unsigned long Replace,
error_status *Status
)
/*++
Routine Description:
This function is no longer supported by EpMapper. RPC Runtime does not
call this function anymore. And, no one else should be...
--*/
{
ASSERT(Status);
*Status = EPT_S_CANT_PERFORM_OP;
}
void
ept_insert_ex(
IN handle_t h,
IN OUT ept_cleanup_handle_t *hEpCleanup,
IN unsigned32 NumEntries,
IN ept_entry_t Entries[],
IN unsigned long Replace,
OUT error_status *Status
)
/*++
Routine Description:
This is the exposed rpc interface routine that adds a series of
endpoints to the Endpoint Mapper database.
Arguments:
h - An explicit binding handle to the EP.
hEpCleanup - A context handle used to purge the Endpoint Mapper
database of stale entries.
NumEntries - Number of Entries to be added.
Entries - An array of ept_entry_t entries.
Replace - TRUE => Replace existing entries.
FALSE=> Just add.
Return Value:
RPC_S_OK - The endpoint was successfully deleted.
RPC_S_OUT_OF_MEMORY - There is no memory to perform the op.
EPT_S_CANT_PERFORM - Invalid entry.
--*/
{
ept_entry_t * Ep;
unsigned short i, j;
unsigned int TransType = 0x0;
unsigned long err = 0;
unsigned long Version;
unsigned char protseqid;
char *Protseq, *Endpoint;
RPC_IF_ID IfId;
PPSEPNode List = 0;
PPSEPNode pPSEPNode, TmpPsep, pTempPSEP, pDeletePSEP;
unsigned long cb;
twr_t * Tower;
BOOL bIFNodeFound = FALSE;
PIFOBJNode NodesListToDelete = NULL;
PIFOBJNode Node, NewNode, DeleteMe = NULL;
UUID * Object;
char * Annotation;
RPC_STATUS Err;
SECURITY_DESCRIPTOR SecurityDescriptor, * PSecurityDesc;
BOOL Bool;
//
// First, make sure the call is via LRPC.
//
err = I_RpcBindingInqTransportType(h, &TransType);
ASSERT(err == RPC_S_OK);
if (TransType != TRANSPORT_TYPE_LPC)
{
*Status = RPC_S_ACCESS_DENIED;
return;
}
//
// Create a temporary PSEP list from the Tower entries.
//
for (Ep = &Entries[0], i = 0; i < NumEntries; Ep++,i++)
{
err = TowerExplode(
Ep->tower,
&IfId,
NULL,
&Protseq,
&Endpoint,
0
);
if (err == RPC_S_OUT_OF_MEMORY)
break;
if (err)
{
err = RPC_S_OK;
continue;
}
Object = &Ep->object;
Annotation = (char *)&Ep->annotation;
Tower = Ep->tower;
cb = sizeof(PSEPNode) +
strlen(Protseq) +
strlen(Endpoint) +
2 + // for the 2 null terminators
Tower->tower_length +
sizeof(Tower->tower_length) +
4; // We need to align tower on DWORD
if ( (pPSEPNode = AllocMem(cb)) == NULL )
{
err = RPC_S_OUT_OF_MEMORY;
break;
}
// Mark this protseq to start listening if needed.
protseqid = (unsigned char) GetProtseqIdAnsi(Protseq);
DelayedUseProtseq(protseqid);
//
// Add a node to the temporary PSEP list
//
memset(pPSEPNode, 0, cb);
pPSEPNode->Signature = PSEPSIGN;
pPSEPNode->Cb = cb;
//strcpy(pPSEPNode->Protseq= ((char *) (pPSEPNode+1)), Protseq);
// Protseq
pPSEPNode->Protseq = (char *) (pPSEPNode + 1); // What is the +1 for?
strcpy(pPSEPNode->Protseq, Protseq);
// Endpoint
pPSEPNode->EP = pPSEPNode->Protseq + strlen(pPSEPNode->Protseq) + 1;
strcpy(pPSEPNode->EP, Endpoint);
// Tower. We add necessary pad so that Tower is aligned to a DWORD.
pPSEPNode->Tower = (twr_t PAPI *)(pPSEPNode->EP +
strlen(pPSEPNode->EP) + 1);
(char PAPI*)(pPSEPNode->Tower) += 4 - ((ULONG_PTR)
(pPSEPNode->Tower) & 3);
memcpy((char PAPI *)pPSEPNode->Tower,
Tower,
Tower->tower_length + sizeof(Tower->tower_length)
);
// Finally, add.
EnterSem();
EnLinkOnPSEPList(&List, pPSEPNode);
LeaveSem();
I_RpcFree(Protseq);
I_RpcFree(Endpoint);
}
if ((err == RPC_S_OUT_OF_MEMORY) || (List == 0))
{
*Status = err;
return;
}
CompleteDelayedUseProtseqs();
Version = VERSION(IfId.VersMajor, IfId.VersMinor);
//
// Find if a compatible Endpoint Mapper entry is already present.
//
if (*hEpCleanup != NULL)
{
//
// The requesting process has previously registered entries
// with the Endpoint Mapper.
//
ASSERT_PROCESS_CONTEXT_LIST_COUNT((PEP_CLEANUP)*hEpCleanup, ((PEP_CLEANUP)*hEpCleanup)->cEntries);
ASSERT(((PEP_CLEANUP)*hEpCleanup)->MagicVal == CLEANUP_MAGIC_VALUE);
ASSERT(((PEP_CLEANUP)*hEpCleanup)->cEntries != 0);
if ( (((PEP_CLEANUP)*hEpCleanup)->MagicVal != CLEANUP_MAGIC_VALUE)
|| (((PEP_CLEANUP)*hEpCleanup)->cEntries == 0))
{
*Status = EPT_S_CANT_PERFORM_OP;
return;
}
EnterSem();
if (Replace == TRUE) // Common case
{
//
// If we find a compatible entry, we just replace its PSEP list
// with the temporary list that we just created.
//
Node = ((PEP_CLEANUP)*hEpCleanup)->EntryList;
while (Node != 0)
{
Node = FindIFOBJNode(
Node,
Object,
&IfId.Uuid,
Version,
RPC_C_EP_MATCH_BY_BOTH,
I_RPC_C_VERS_UPTO_AND_COMPATIBLE,
SearchIFObjNode
);
if ((Node == 0) || (Node->OwnerOfList != *hEpCleanup))
break;
// Matching Endpoint Mapper entry found.
PurgeOldEntries(Node, List, FALSE);
if (Node->IFVersion == Version)
{
bIFNodeFound = TRUE;
// Seek to the end of Tmp and then Link
TmpPsep = List;
while (TmpPsep->Next != 0)
TmpPsep = TmpPsep->Next;
TmpPsep->Next = Node->PSEPlist;
Node->PSEPlist = List;
}
if (Node->PSEPlist == 0)
{
DeleteMe = Node;
Node = Node->Next;
err = UnLinkFromIFOBJList((PEP_CLEANUP)*hEpCleanup, DeleteMe);
ASSERT(err == RPC_S_OK);
// Add to a list that will be deleted later...
DeleteMe->Next = NodesListToDelete;
NodesListToDelete = DeleteMe;
DeleteMe->Signature = FREE;
}
else
{
Node = Node->Next;
}
} // while loop
}
else // (Replace != TRUE)
{
//
// If we find an entry with an exact match, we append
// the temporary PSEP list to the entry's PSEP list.
//
Node = ((PEP_CLEANUP)*hEpCleanup)->EntryList;
NewNode = FindIFOBJNode(
Node,
Object,
&IfId.Uuid,
Version,
0,
0,
ExactMatch
);
if (NewNode && (NewNode->OwnerOfList == *hEpCleanup))
{
bIFNodeFound = TRUE;
PurgeOldEntries(NewNode, List, TRUE);
// Seek to the end of Tmp and then Link
TmpPsep = List;
while (TmpPsep->Next != 0)
TmpPsep = TmpPsep->Next;
TmpPsep->Next = NewNode->PSEPlist;
NewNode->PSEPlist = List;
}
} // if (Replace == TRUE)
LeaveSem();
} // if (*hpCleanup != NULL)
//
// Free the list outside the lock.
//
while (NodesListToDelete != NULL)
{
DeleteMe = NodesListToDelete;
NodesListToDelete = NodesListToDelete->Next;
// Delete the PSEP list.
pTempPSEP = DeleteMe->PSEPlist;
while (pTempPSEP != NULL)
{
pDeletePSEP = pTempPSEP;
pTempPSEP = pTempPSEP->Next;
FreeMem(pDeletePSEP);
}
FreeMem(DeleteMe);
}
if (bIFNodeFound == FALSE)
{
//
// One of the following is TRUE:
// a. The process is registering with EP Mapper for the first time.
// b. No compatible Ep entry was found.
//
//
// Allocate a new EP Mapper entry.
//
cb = sizeof(IFOBJNode);
cb += strlen(Annotation) + 1;
if ((NewNode = AllocMem(cb)) == NULL)
{
*Status = RPC_S_OUT_OF_MEMORY;
return;
}
//
// Fill-in the new entry
//
memset(NewNode, 0, cb);
NewNode->Cb = cb;
NewNode->Signature = IFOBJSIGN;
NewNode->IFVersion = Version;
memcpy((char *)&NewNode->ObjUuid, (char *)Object, sizeof(UUID));
memcpy((char *)&NewNode->IFUuid, (char *)&IfId.Uuid, sizeof(UUID));
strcpy((NewNode->Annotation=(char *)(NewNode+1)), Annotation);
if (IsNullUuid(Object))
NewNode->IFOBJid = MAKEGLOBALIFOBJID(MAXIFOBJID);
else
NewNode->IFOBJid = MAKEGLOBALIFOBJID(GlobalIFOBJid--);
//
// Create a new context for this process, if necessary
//
if (*hEpCleanup == NULL)
{
*hEpCleanup = AllocMem(sizeof(EP_CLEANUP));
if (*hEpCleanup == NULL)
{
LeaveSem();
FreeMem(NewNode);
*Status = RPC_S_OUT_OF_MEMORY;
return;
}
memset(*hEpCleanup, 0x0, sizeof(EP_CLEANUP));
((PEP_CLEANUP)*hEpCleanup)->MagicVal = CLEANUP_MAGIC_VALUE;
}
//
// Insert the new entry into the EP Mapper table.
//
EnterSem();
err = EnLinkOnIFOBJList((PEP_CLEANUP)*hEpCleanup, NewNode);
ASSERT(err == RPC_S_OK);
NewNode->PSEPlist = List;
LeaveSem();
}
*Status = err;
}
void
ept_delete(
handle_t h,
unsigned32 NumEntries,
ept_entry_t Entries[],
error_status *Status
)
/*++
Routine Description:
This function is no longer supported by EpMapper. RPC Runtime does not
call this function anymore. And, no one else should be...
--*/
{
ASSERT(Status);
*Status = EPT_S_CANT_PERFORM_OP;
}
RPC_STATUS
ept_delete_ex_helper(
IN ept_cleanup_handle_t hEpCleanup,
IN UUID *Object,
IN UUID *Interface,
IN unsigned long IFVersion,
IN char PAPI * Protseq,
IN char PAPI * Endpoint
)
/*++
Routine Description:
This routine deletes an Endpoint registered with the EP Mapper
Arguments:
hEpCleanup - A context handle used to purge the Endpoint Mapper
database of stale entries.
Object - Object Uuid.
Interface - If Uuid
IFVersion - Version of the IF [Hi ushort=Major, Lo ushort=Minor]
Protseq - Protocol Sequence
Endpoint - Endpoint string
Notes:
a. This routine has to be called by holding a mutex.
Return Value:
RPC_S_OK - The endpoint was successfully deleted
EPT_S_NOT_REGISTERED - No matching entries were found
--*/
{
PIFOBJNode pNode;
PPSEPNode pPSEPNode = NULL;
unsigned long cb, err = 0;
PEP_T p;
PEP_CLEANUP ProcessCtx;
if (!Protseq || !Endpoint)
{
return(EPT_S_NOT_REGISTERED);
}
CheckInSem();
ProcessCtx = (PEP_CLEANUP)hEpCleanup;
if (ProcessCtx->EntryList == NULL)
return EPT_S_NOT_REGISTERED;
pNode = FindIFOBJNode(
ProcessCtx->EntryList,
Object,
Interface,
IFVersion,
0L,
0L,
ExactMatch
);
if ((pNode != NULL) && (pNode->PSEPlist != NULL))
{
pPSEPNode = FindPSEP(
pNode->PSEPlist,
Protseq,
Endpoint,
0L,
MatchPSAndEP
);
}
if (pPSEPNode != NULL)
{
UnLinkFromPSEPList(&pNode->PSEPlist, pPSEPNode);
if (pNode->PSEPlist == NULL)
{
err = UnLinkFromIFOBJList((PEP_CLEANUP)hEpCleanup, pNode);
// ASSERT(err == RPC_S_OK);
if (err != RPC_S_OK)
{
// Restore the PSEPList
EnLinkOnPSEPList(&pNode->PSEPlist, pPSEPNode);
return err;
}
pNode->Signature = FREE;
FreeMem(pNode);
}
pPSEPNode->Signature = FREE;
FreeMem(pPSEPNode);
}
else
{
err = EPT_S_NOT_REGISTERED;
}
return(err);
}
void
ept_delete_ex(
IN handle_t h,
IN OUT ept_cleanup_handle_t *hEpCleanup,
IN unsigned32 NumEntries,
IN ept_entry_t Entries[],
OUT error_status *Status
)
/*++
Routine Description:
This routine deletes the specified Endpoints
Arguments:
BindingHandle - An explicit binding handle to the EP.
NumEntries - #of entries in the Bunffer that need to be deleted.
Entries[] - Buffer of #NumEntries of ept_entry_t structures
Return Value:
RPC_S_OK - The endpoint was successfully deleted
EPT_S_NOT_REGISTERED - No matching entries were found
--*/
{
ept_entry_t * Ep;
unsigned short i;
unsigned int TransType = 0x0;
RPC_STATUS err;
RPC_STATUS DeleteStatus;
unsigned long Version;
char *Protseq, *Endpoint;
RPC_IF_ID IfId;
RPC_TRANSFER_SYNTAX XferId;
//
// First, make sure the call is via LRPC.
//
err = I_RpcBindingInqTransportType(h, &TransType);
ASSERT(err == RPC_S_OK);
if (TransType != TRANSPORT_TYPE_LPC)
{
*Status = RPC_S_ACCESS_DENIED;
return;
}
if ( !( (*hEpCleanup)
&& (((PEP_CLEANUP)*hEpCleanup)->MagicVal == CLEANUP_MAGIC_VALUE)
&& (((PEP_CLEANUP)*hEpCleanup)->cEntries != 0)
)
)
{
//
// Cannot ASSERT here. This is possible. (ep1-26, ep2-3)
//
//ASSERT(*hEpCleanup);
//ASSERT(((PEP_CLEANUP)*hEpCleanup)->MagicVal == CLEANUP_MAGIC_VALUE);
//ASSERT(((PEP_CLEANUP)*hEpCleanup)->cEntries != 0);
*Status = EPT_S_CANT_PERFORM_OP;
return;
}
*Status = EPT_S_NOT_REGISTERED;
DeleteStatus = RPC_S_OK;
for (Ep = &Entries[0], i = 0; i < NumEntries; Ep++,i++)
{
err = TowerExplode(
Ep->tower,
&IfId,
&XferId,
&Protseq,
&Endpoint,
0
);
if (err == RPC_S_OUT_OF_MEMORY)
{
*Status = RPC_S_OUT_OF_MEMORY;
break;
}
if (err)
{
continue;
}
Version = VERSION(IfId.VersMajor, IfId.VersMinor);
EnterSem();
//
// NOTE:
//
// If even one call to ept_delete_ex_helper() fails, we want to return
// failure from ept_delete_ex(). This is different from the past where
// if one call succeeded, then the function returned success.
//
err = ept_delete_ex_helper(
*hEpCleanup,
&Ep->object,
&IfId.Uuid,
Version,
Protseq,
Endpoint
);
if (err)
{
// Save the last failure status.
DeleteStatus = err;
}
if (((PEP_CLEANUP)*hEpCleanup)->cEntries == 0)
{
//
// No entry left in this process's list. Time to zero out this
// process's context handle.
//
//ASSERT(((PEP_CLEANUP)*hEpCleanup)->EntryList == NULL);
FreeMem(*hEpCleanup);
*hEpCleanup = NULL;
}
LeaveSem();
if (Protseq)
I_RpcFree(Protseq);
if (Endpoint)
I_RpcFree(Endpoint);
}
if (err)
{
// RPC_S_OUT_OF_MEMORY OR the last call to
// ept_delete_ex_helper() failed.
*Status = err;
}
else
{
// RPC_S_OK OR one of the calls to ept_delete_ex_helper() (but
// not the last one) failed.
*Status = DeleteStatus;
}
}
void
ept_lookup(
handle_t hEpMapper,
unsigned32 InquiryType,
UUID * Object,
RPC_IF_ID * Ifid,
unsigned32 VersOptions,
ept_lookup_handle_t *LookupHandle,
unsigned32 MaxRequested,
unsigned32 *NumEntries,
ept_entry_t Entries[],
error_status *Status
)
/*++
Routine Description:
This routine returns upto MaxRequested, ept_entry(s) currently
registered with the Endpoint mapper based on the
Obj, Interface, Protocol sequence and filters VersOptions and
InqType
Arguments:
BindingHandle - An explicit binding handle to the EP.
InquiryType - Search Filter [Seach based on IF, Obj or Both]
Obj - Object Uuid. specified by the client
ObjInterface - Interface Uuid spec. by the client.
InId - The If Specification [IF Uuid+IfVersion]
VersOpts- Search Filter based on Versions [Versins <, >, ==]
MapHandle - A resume key - If NULL, search is started from the beginning
if non-null, represents an encoding from where the epmapper is
is supposed to start searching. It is an opaque value as far as the
as far as the client is concerned.
MaxRequested - Max number of entries requested by the client.
Returned - The actual number of entries returned by the mapper.
Entries - Buffer of ept_entries returned.
Return Value:
RPC_S_OUT_OF_MEMORY
RPC_S_OK - At least one matching entry is being returned.
EP_S_NOT_REGISTERED - No matching entries were found
EPT_S_CANT_PERFORM_OP - MaxRequested value exceed ep_max_lookup_results
--*/
{
unsigned long Version;
if (Ifid == NULL)
{
Ifid = &LocalNullUuid;
}
if (Object == NULL)
{
Object = (UUID *) &LocalNullUuid;
}
switch (VersOptions)
{
case RPC_C_VERS_ALL:
Version = 0;
break;
case RPC_C_VERS_COMPATIBLE:
case RPC_C_VERS_EXACT:
case RPC_C_VERS_UPTO:
Version = VERSION(Ifid->VersMajor, Ifid->VersMinor);
break;
case RPC_C_VERS_MAJOR_ONLY:
Version = VERSION(Ifid->VersMajor, 0);
break;
default:
break;
}
*Status = GetEntries(
Object,
&Ifid->Uuid,
Version,
NULL,
LookupHandle,
(char *)Entries,
EP_LOOKUP,
MaxRequested,
NumEntries,
InquiryType,
VersOptions,
SearchIFObjNode
);
}
void
ept_map(
handle_t h,
UUID *Obj OPTIONAL,
twr_p_t MapTower,
ept_lookup_handle_t *MapHandle,
unsigned32 MaxTowers,
unsigned32 *NumTowers,
twr_p_t *ITowers,
error_status *Status
)
/*++
Routine Description:
This routine returns a fully-resolved string binding, for a given
Obj, Interface, and Protocol sequence if an appropriate entry is
found. Else returns EP_S_NOT_REGISTERED.
Arguments:
BindingHandle - An explicit binding handle to the EP.
Obj - Object Uuid. specified by the client
ObjInterface - Interface Uuid spec. by the client.
Interfacever - InterfaceVersion [hi ushort = VerMajor, lo ushort VerMinor]
Protseq - An ascii string specifying the protocol seq.
MapHandle - A resume key - If NULL, search is started from the beginning
if non-null, represents an encoding from where the epmapper is
supposed to start searching. It is an opaque value as far as the
client is concerned.
Binding - The fully resolved string binding returned if the call is
successful.
Return Value:
RPC_S_OUT_OF_MEMORY
RPC_S_OK
EP_S_NOT_REGISTERED
--*/
{
RPC_IF_ID Ifid;
RPC_TRANSFER_SYNTAX Xferid;
char *Protseq;
unsigned long Version;
char * String = 0;
if (Obj == 0)
{
Obj = (UUID *) &LocalNullUuid;
}
*Status = TowerExplode(
MapTower,
&Ifid,
&Xferid,
&Protseq,
NULL,
0
);
if (*Status)
{
*NumTowers = 0;
return;
}
Version = VERSION(Ifid.VersMajor,Ifid.VersMinor);
if (memcmp((char *)&Ifid.Uuid, (char *)&MgmtIf, sizeof(UUID)) == 0)
{
if ((Obj == 0) || IsNullUuid(Obj))
{
*NumTowers = 0;
*Status = RPC_S_BINDING_INCOMPLETE;
}
else
{
*Status = GetEntries(
Obj,
&Ifid.Uuid,
Version,
Protseq,
MapHandle,
(char *)ITowers,
EP_MAP,
MaxTowers,
NumTowers,
RPC_C_EP_MATCH_BY_OBJ,
RPC_C_VERS_ALL,
SearchIFObjNode
);
}
}
else
{
*Status = GetEntries(
Obj,
&Ifid.Uuid,
Version,
Protseq,
MapHandle,
(char *)ITowers,
EP_MAP,
MaxTowers,
NumTowers,
0L,
0L,
WildCardMatch
);
}
if (Protseq)
I_RpcFree(Protseq);
}
void
ept_inq_object(
handle_t BindingHandle,
UUID *Object,
error_status *status
)
/*++
Routine Description:
Not supported
Arguments:
BindingHandle - An explicit binding handle to the EP.
Object _ No idea whose UUID this is.
Return Value:
EPT_S_CANT_PERFORM_OP
--*/
{
ASSERT(*status);
*status = EPT_S_CANT_PERFORM_OP;
}
void
DeletePSEP(
PIFOBJNode Node,
char * Protseq,
char * Endpoint
)
{
PSEPNode *Psep, *Tmp;
if (Node == 0)
return;
Psep = Node->PSEPlist;
while (Psep != 0)
{
Psep = FindPSEP(
Psep,
Protseq,
Endpoint,
0L,
MatchPSAndEP
);
if (Psep != 0)
{
Tmp = Psep;
Psep = Psep->Next;
UnLinkFromPSEPList(&Node->PSEPlist, Tmp);
Tmp->Signature = FREE;
FreeMem(Tmp);
}
}
}
void
ept_mgmt_delete(
handle_t BindingHandle,
boolean32 ObjectSpecd,
UUID * Object,
twr_p_t Tower,
error_status *Error
)
/*++
Routine Description:
Not supported
Arguments:
BindingHandle - An explicit binding handle to the EP.
Object _ ObjUUid
Tower - Tower specifying the Endpoints to be deleted.
Return Value:
EPT_S_CANT_PERFORM_OP
--*/
{
ASSERT(*Error);
*Error = EP_S_CANT_PERFORM_OP;
}
void ept_lookup_handle_t_rundown (ept_lookup_handle_t h)
{
PSAVEDCONTEXT Context = (PSAVEDCONTEXT) h;
PIENTRY Entry;
unsigned long Type;
PIENTRY Tmp;
twr_t * Tower;
ASSERT (Context != 0);
if ( (PtrToUlong(Context)) == 0xFFFFFFFF)
return;
Type = Context->Type;
EnterSem();
Entry = (PIENTRY)Context->List;
while (Entry != 0)
{
switch (Type)
{
case EP_MAP:
Tower = ((PSAVEDTOWER)Entry)->Tower;
break;
case EP_LOOKUP:
Tower = ((PSAVED_EPT)Entry)->Tower;
break;
default:
ASSERT(!"Unknown Inquiry Type");
break;
}
MIDL_user_free(Tower);
Tmp = Entry;
Entry = Entry->Next;
FreeMem(Tmp);
}
// Now free The Context
UnLink((PIENTRY *)&GlobalContextList, (PIENTRY)Context);
LeaveSem();
FreeMem(Context);
}
void
ept_lookup_handle_free(
handle_t h,
ept_lookup_handle_t * ept_context_handle,
error_status * status
)
{
if ( (ept_context_handle != 0) && (*ept_context_handle != 0))
{
ept_lookup_handle_t_rundown( *ept_context_handle );
*ept_context_handle = 0;
}
*status = 0;
}
#define MAX(x,y) ((x) < (y)) ? (y) : (x)
#define MIN(x,y) ((x) > (y)) ? (y) : (x)
#ifdef DEBUGRPC
#define DEBUG_MIN(x,y) MIN((x),(y))
#else
#define DEBUG_MIN(x,y) MAX((x),(y))
#endif
error_status_t
OpenEndpointMapper(
IN handle_t hServer,
OUT HPROCESS *pProcessHandle
)
{
PROCESS *pProcess = MIDL_user_allocate(sizeof(PROCESS));
if (!pProcess)
{
*pProcessHandle = 0;
return(RPC_S_OUT_OF_MEMORY);
}
pProcess->MagicVal = PROCESS_MAGIC_VALUE;
pProcess->pPorts = 0;
*pProcessHandle = (PVOID)pProcess;
return(RPC_S_OK);
}
//
// Port Management stuff
//
//
// Port Management Globals
//
const RPC_CHAR *PortConfigKey = RPC_CONST_STRING("Software\\Microsoft\\Rpc\\Internet");
const RPC_CHAR *DefaultPortType = RPC_CONST_STRING("UseInternetPorts");
const RPC_CHAR *ExplictPortType = RPC_CONST_STRING("PortsInternetAvailable");
const RPC_CHAR *PortRanges = RPC_CONST_STRING("Ports");
CRITICAL_SECTION PortLock;
BOOL fValidConfiguration = FALSE;
BOOL fPortRestrictions = FALSE;
PORT_TYPE SystemDefaultPortType = 0;
IP_PORT *pFreeInternetPorts = 0;
IP_PORT *pFreeIntranetPorts = 0;
PORT_RANGE *InternetPorts = 0;
PORT_RANGE *IntranetPorts = 0;
//
// Port management APIs
//
RPC_STATUS
InitializeIpPortManager(
void
)
{
HKEY hkey;
RPC_STATUS status;
DWORD size, type, value;
RPC_CHAR *pstr;
PORT_RANGE *pSet;
PORT_RANGE *pLast;
PORT_RANGE *pCurrent;
PORT_RANGE *pComplement;
PORT_RANGE *pNew;
LONG min, max;
InitializeCriticalSectionAndSpinCount(&PortLock, PREALLOCATE_EVENT_MASK);
status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
PortConfigKey,
0,
KEY_READ,
&hkey);
if (status != RPC_S_OK)
{
if (status != ERROR_FILE_NOT_FOUND)
{
#if DBG
PrintToDebugger("RPCSS: Unable to open port config key: %d\n", status);
#endif
}
ASSERT(status == ERROR_FILE_NOT_FOUND);
fValidConfiguration = TRUE;
return(RPC_S_OK);
}
size = sizeof(value);
status = RegQueryValueEx(hkey,
DefaultPortType,
0,
&type,
(PBYTE)&value,
&size);
if ( status != RPC_S_OK
|| type != REG_SZ
|| ( *(RPC_CHAR *)&value != 'Y'
&& *(RPC_CHAR *)&value != 'y'
&& *(RPC_CHAR *)&value != 'N'
&& *(RPC_CHAR *)&value != 'n') )
{
RegCloseKey(hkey);
ASSERT(fValidConfiguration == FALSE);
return(RPC_S_OK);
}
if ( *(RPC_CHAR *)&value == 'Y'
|| *(RPC_CHAR *)&value == 'y')
{
SystemDefaultPortType = PORT_INTERNET;
}
else
{
SystemDefaultPortType = PORT_INTRANET;
}
size = sizeof(value);
status = RegQueryValueEx(hkey,
ExplictPortType,
0,
&type,
(PBYTE)&value,
&size);
if ( status != RPC_S_OK
|| type != REG_SZ
|| ( *(RPC_CHAR *)&value != 'Y'
&& *(RPC_CHAR *)&value != 'y'
&& *(RPC_CHAR *)&value != 'N'
&& *(RPC_CHAR *)&value != 'n') )
{
RegCloseKey(hkey);
ASSERT(fValidConfiguration == FALSE);
return(RPC_S_OK);
}
if ( *(RPC_CHAR *)&value == 'Y'
|| *(RPC_CHAR *)&value == 'y')
{
value = PORT_INTERNET;
}
else
{
value = PORT_INTRANET;
}
size = DEBUG_MIN(1, 100);
do
{
ASSERT(size);
pstr = alloca(size);
ASSERT(pstr);
status = RegQueryValueEx(hkey,
PortRanges,
0,
&type,
(PBYTE)pstr,
&size);
}
while (status == ERROR_MORE_DATA);
RegCloseKey(hkey);
if ( status != RPC_S_OK
|| type != REG_MULTI_SZ)
{
ASSERT(fValidConfiguration == FALSE);
return(RPC_S_OK);
}
//
// The user is going to specify a range of ports in the registery
// with a flag indicating if these ports are internet or intranet.
//
// ie, 500-550
// 560
// 559
// 2000-2048
// 2029-2049
//
// Note that order (in the REG_MULTI_SZ) and overlapping sets
// are ok. We must handle creating a port range list for this
// array and for the complement BUT NOT INCLUDING <=1024 by default.
//
// completment set to above is:
//
// 1025-1999
// 2050-32767
//
#define MIN_PORT 1025 // Only important for complement sets.
#define MAX_PORT 65535
pSet = 0;
pLast = 0;
while(*pstr)
{
RPC_CHAR *t;
#ifdef UNICODE
min = wcstol(pstr, &t, 10);
#else
min = strtol(pstr, &t, 10);
#endif
if (min > MAX_PORT || min < 0)
{
status = RPC_S_INVALID_ARG;
break;
}
if ( *t != 0
#ifdef UNICODE
&& *t != L'-')
#else
&& *t != '-')
#endif
{
status = RPC_S_INVALID_ARG;
break;
}
if (*t == 0)
{
max = min;
}
else
{
#ifdef UNICODE
max = wcstol(t + 1, &t, 10);
#else
min = strtol(t + 1, &t, 10);
#endif
if (max > MAX_PORT || max < 0 || max < min)
{
status = RPC_S_INVALID_ARG;
break;
}
}
ASSERT(min <= max);
// Ok, got some ports, allocate a structure for them..
pNew = MIDL_user_allocate(sizeof(PORT_RANGE));
if (0 == pNew)
{
status = RPC_S_OUT_OF_MEMORY;
break;
}
pNew->pNext = 0;
pNew->Min = (unsigned short) min;
pNew->Max = (unsigned short) max;
// We can to maintain the set of ranges in order. As we insert
// we'll fix any ranges which overlap.
pCurrent = pSet;
pLast = 0;
for (;;)
{
if (0 == pSet)
{
pSet = pNew;
break;
}
if ( pNew->Min <= (pCurrent->Max + 1)
&& pNew->Max >= (pCurrent->Min - 1) )
{
// The ranges overlap or touch. We'll merge them now..
pCurrent->Min = MIN(pNew->Min, pCurrent->Min);
pCurrent->Max = MAX(pCurrent->Max, pNew->Max);
MIDL_user_free(pNew);
// Since the new larger range may overlap another existing
// range we just insert the larger range as if it was new...
pNew = pCurrent;
// Take current out of the list.
if (pLast)
{
pLast->pNext = pCurrent->pNext;
}
if (pSet == pNew)
{
pSet = pSet->pNext;
}
// Restart
pCurrent = pSet;
pLast = 0;
continue;
}
if (pNew->Min < pCurrent->Min)
{
// Found the spot
if (pLast)
{
pLast->pNext = pNew;
pNew->pNext = pCurrent;
}
else
{
ASSERT(pCurrent == pSet);
pNew->pNext = pCurrent;
pSet = pNew;
}
break;
}
// Continue the search
pLast = pCurrent;
pCurrent = pCurrent->pNext;
if (0 == pCurrent)
{
// Reached the end of the list, insert it here.
pLast->pNext = pNew;
ASSERT(pNew->pNext == 0);
break;
}
}
ASSERT(pSet);
// Advance to the next string of the final null.
pstr = RpcpCharacter(pstr, 0) + 1;
}
if (pSet == 0)
{
status = RPC_S_INVALID_ARG;
}
if (value == PORT_INTERNET)
{
InternetPorts = pSet;
}
else
{
IntranetPorts = pSet;
}
if (status == RPC_S_OK)
{
// We've constructed the set of ports in the registry,
// now we need to compute the complement set.
pComplement = 0;
pCurrent = 0;
min = MIN_PORT;
while(pSet)
{
if (min < pSet->Min)
{
max = pSet->Min - 1;
ASSERT(max >= min);
pNew = MIDL_user_allocate(sizeof(PORT_RANGE));
if (0 == pNew)
{
status = RPC_S_OUT_OF_MEMORY;
break;
}
pNew->pNext = 0;
pNew->Min = (unsigned short) min;
pNew->Max = (unsigned short) max;
if (pComplement == 0)
{
pComplement = pCurrent = pNew;
}
else
{
ASSERT(pCurrent);
pCurrent->pNext = pNew;
pCurrent = pNew;
}
}
min = MAX(MIN_PORT, pSet->Max + 1);
pSet = pSet->pNext;
}
if (status == RPC_S_OK && min < MAX_PORT)
{
// Final port in orginal set less then max, allocate final
// range for the set complement.
pNew = MIDL_user_allocate(sizeof(PORT_RANGE));
if (0 != pNew)
{
pNew->Min = (unsigned short) min;
pNew->Max = MAX_PORT;
pNew->pNext = 0;
if (pCurrent)
{
pCurrent->pNext = pNew;
}
else
{
ASSERT(min == MIN_PORT);
pComplement = pNew;
}
}
else
{
status = RPC_S_OUT_OF_MEMORY;
}
}
// Even if we failed assign the pointer, it's either
// null or needs to be freed.
if (value == PORT_INTERNET)
{
ASSERT(IntranetPorts == 0);
IntranetPorts = pComplement;
}
else
{
ASSERT(InternetPorts == 0);
InternetPorts = pComplement;
}
}
if (status != RPC_S_OK)
{
ASSERT(fValidConfiguration == FALSE);
while(InternetPorts)
{
PORT_RANGE *pT = InternetPorts;
InternetPorts = InternetPorts->pNext;
MIDL_user_free(pT);
}
while(IntranetPorts)
{
PORT_RANGE *pT = IntranetPorts;
IntranetPorts = IntranetPorts->pNext;
MIDL_user_free(pT);
}
return(RPC_S_OK);
}
fValidConfiguration = TRUE;
fPortRestrictions = TRUE;
return(RPC_S_OK);
}
BOOL
AllocatePort(
OUT IP_PORT **ppPort,
IN OUT IP_PORT **ppPortFreeList,
IN PORT_RANGE *pPortList
)
/*++
Routine Description:
Allocates a port object for a specific process. It first tries
to use any ports in the free list. If there's nothing in the
port this then it tries to find a free port in the PortList
which is one of the sets computed during startup.
Arguments:
ppPort - Will contain the allocated port object if successful.
ppPortFreeList - Pointer to the head of the free list associated
with this type of port. Maybe modified during this call.
pPortList - Port ranges associated with this type of port.
Return Value:
TRUE - Port allocated
FALSE - Port not allocated
--*/
{
IP_PORT *pPort = 0;
// First see if there is free port to reuse.
if (*ppPortFreeList)
{
EnterCriticalSection(&PortLock);
if (*ppPortFreeList)
{
pPort = *ppPortFreeList;
*ppPortFreeList = pPort->pNext;
pPort->pNext = 0;
}
LeaveCriticalSection(&PortLock);
}
if (pPort == 0)
{
// No port in the free list, try to allocate one
// Assume we'll find a free port..
pPort = MIDL_user_allocate(sizeof(IP_PORT));
if (0 != pPort)
{
pPort->pNext = 0;
EnterCriticalSection(&PortLock);
while ( pPortList
&& pPortList->Min > pPortList->Max)
{
pPortList = pPortList->pNext;
}
if (pPortList)
{
ASSERT(pPortList->Min <= pPortList->Max);
pPort->Port = pPortList->Min;
pPortList->Min++;
// We could remove empty ranges from the list.
}
LeaveCriticalSection(&PortLock);
if (0 == pPortList)
{
MIDL_user_free(pPort);
pPort = 0;
#ifdef DEBUGRPC
DbgPrint("RPC: Out of reserved ports\n");
#endif
}
}
}
// REVIEW: Post SUR we should look at adding events for
// allocation and failure to allocate IP ports
*ppPort = pPort;
return(pPort != 0);
}
error_status_t
AllocateReservedIPPort(
IN HPROCESS hProcess,
IN PORT_TYPE PortType,
OUT long *pAllocationStatus,
OUT unsigned short *pAllocatedPort
)
/*++
Routine Description:
Remote manager for RPC runtime to call locally to allocate
a local port. The call and process parameters must be valid
and called only locally. Based on the PortType paramet a
IP port maybe allocated for the calling process. The
allocationstatus contains the result of the port allocation
step.
Arguments:
hProcess - Valid process context handle allocated with
a call to OpenEndpointMapper.
PortType - One of
PORT_INTERNET
PORT_INTRANET
PORT_DEFAULT
Used to determine which port range to allocate from.
pAllocationStatus -
RPC_S_OK - successfully allocated a port.
RPC_S_OUT_OF_RESOURES - no ports available.
pAllocatePort - If allocation status is RPC_S_OK then
this contains the value of the port allocated.
If zero it means that there are no port restrictions
and any port maybe used.
Return Value:
RPC_S_OK
RPC_S_INVALID_ARG - configuration error or PortType out of range.
RPC_S_ACCESS_ DENIED - not called locally.
--*/
{
PROCESS *pProcess = (PROCESS *)hProcess;
IP_PORT *pPort;
UINT type;
BOOL b;
*pAllocatedPort = 0;
*pAllocationStatus = RPC_S_OK;
ASSERT(pProcess);
if (!fValidConfiguration)
{
return(RPC_S_INVALID_ARG);
}
if ( (I_RpcBindingInqTransportType(0, &type) != RPC_S_OK)
|| (type != TRANSPORT_TYPE_LPC)
|| (0 == pProcess)
|| (pProcess->MagicVal != PROCESS_MAGIC_VALUE ) )
{
return(RPC_S_ACCESS_DENIED);
}
if (PortType > PORT_DEFAULT || PortType < PORT_INTERNET)
{
return(RPC_S_INVALID_ARG);
}
if (fPortRestrictions == FALSE)
{
// No port restrictions on this machine, just use zero.
// This is the common case.
ASSERT(*pAllocatedPort == 0);
ASSERT(*pAllocationStatus == 0);
return(RPC_S_OK);
}
// Need to actually allocate a unique port for this process.
if (PortType == PORT_DEFAULT)
{
// Allocate using default policy
PortType = SystemDefaultPortType;
}
ASSERT(PortType == PORT_INTERNET || PortType == PORT_INTRANET);
pPort = 0;
if (PortType == PORT_INTERNET)
{
b = AllocatePort(&pPort,
&pFreeInternetPorts,
InternetPorts
);
}
else
{
b = AllocatePort(&pPort,
&pFreeIntranetPorts,
IntranetPorts);
}
if (!b)
{
ASSERT(pPort == 0);
// REVIEW: Do we want a unique error code if no ports
// are available?
*pAllocationStatus = RPC_S_OUT_OF_RESOURCES;
return(RPC_S_OK);
}
ASSERT(pPort);
ASSERT(pPort->pNext == 0);
pPort->Type = (unsigned short) PortType;
pPort->pNext = pProcess->pPorts;
pProcess->pPorts = pPort;
*pAllocatedPort = pPort->Port;
ASSERT(*pAllocationStatus == RPC_S_OK);
return(RPC_S_OK);
}
void
HPROCESS_rundown(
HPROCESS hProcess
)
{
PROCESS *pProcess = (PROCESS *)hProcess;
IP_PORT *pCurrent;
IP_PORT *pSave;
ASSERT(pProcess);
ASSERT(pProcess->MagicVal == PROCESS_MAGIC_VALUE);
pCurrent = pProcess->pPorts;
if (pCurrent)
{
EnterCriticalSection(&PortLock);
do
{
pSave = pCurrent->pNext;
if (pCurrent->Type == PORT_INTERNET)
{
pCurrent->pNext = pFreeInternetPorts;
pFreeInternetPorts = pCurrent;
}
else
{
ASSERT(pCurrent->Type == PORT_INTRANET);
pCurrent->pNext = pFreeIntranetPorts;
pFreeIntranetPorts = pCurrent;
}
pCurrent = pSave;
}
while(pCurrent);
LeaveCriticalSection(&PortLock);
}
MIDL_user_free(pProcess);
return;
}