/*++ Microsoft Windows NT RPC Name Service Copyright (c) 1995 Microsoft Corporation Module Name: utils.cxx Abstract: This module contains definitions of miscellaneous utility functions. Author: Satish Thatte (SatishT) 09/20/95 Created all the code below except where otherwise indicated. --*/ #include #include #include #include #include BOOL hasWhacksInMachineName( STRING_T szName ) /*++ Routine Description: Check if machine name has "\\" at the beginning. Returns: TRUE if "\\" present, FALSE otherwise. --*/ { if ((*szName == L'\\') && (*(szName+1) == L'\\')) return TRUE; else return FALSE; } unsigned long CurrentTime ( ) /*++ Routine Description: Return the current time in seconds. Returns: The time. --*/ { ULONG time; // QUERY the current time; and return the time since service start in seconds. time = GetCurrentTime(); return((time-StartTime)/1000); } BOOL IsStillCurrent( ULONG ulCacheTime, ULONG ulTolerance ) /*++ Routine Description: Determines if a cache creation time is still "current" based on a tolerance level for staleness. All time values are in seconds. Arguments: ulCacheTime - cache creation time ulTolerance - staleness tolerance Returns: TRUE if current, FALSE otherwise --*/ { ULONG ulCurrent = CurrentTime(); DBGOUT(UTIL, "IsStillCurrent: ulCacheTime = " << ulCacheTime << WNL); DBGOUT(UTIL, "IsStillCurrent: ulTolerance = " << ulTolerance << WNL); DBGOUT(UTIL, "IsStillCurrent: ulCurrent = " << ulCurrent << "\n\n"); /* the next statement is basically a kludge to get around the fact that rpc_c_ns_default_exp_age is -1 which, as a ULONG is 0xffffffff so that ulTolerance + CACHE_GRACE wraps around to CACHE_GRACE - 1 for the default tolerance! */ if (ulTolerance + CACHE_GRACE < CACHE_GRACE) // ulTolerance wrap around return TRUE; if ( (ulCurrent - ulCacheTime) > (ulTolerance + CACHE_GRACE) || ((long)ulCurrent - (long)ulCacheTime) < 0 // ulCurrent wrap around ) return FALSE; else { DBGOUT(UTIL, "IsStillCurrent: YES!\n\n"); return TRUE; } } void AssertHeap() /*++ Routine Description: Checks the state of the current process heap, or of a single block in the heap if a non-null block address is provided. --*/ { BOOL valid = HeapValidate( hHeapHandle, 0, // default: serialize heap access NULL // default: validate the entire heap ); if (!valid) Raise(NSI_S_HEAP_TRASHED); } RPC_BINDING_HANDLE MakeDClocTolocHandle( STRING_T pszDCName ) /*++ Routine Description: Create an RPC binding handle for a locator on the given server Arguments: pszDCName - name of the server machine Returns: A binding handle. --*/ { RPC_STATUS Status; STRING_T pszTempBinding; STRING_T pszNetworkAddr = catenate(TEXT("\\\\"),pszDCName); Status = RpcStringBindingCompose( NULL, TEXT("ncacn_np"), pszNetworkAddr, TEXT("\\pipe\\locator"), NULL, &pszTempBinding ); if (Status != RPC_S_OK) Raise(NSI_S_DC_BINDING_FAILURE); RPC_BINDING_HANDLE hResult; Status = RpcBindingFromStringBinding( pszTempBinding, &hResult ); if (Status != RPC_S_OK) Raise(NSI_S_DC_BINDING_FAILURE); UINT Timeout; Status = RpcMgmtInqComTimeout( // BUGBUG: does this have any effect? hResult, &Timeout ); delete pszNetworkAddr; RpcStringFree(&pszTempBinding); return hResult; } STRING_T catenate( STRING_T pszPrefix, STRING_T pszSuffix ) /*++ Routine Description: Concatenate the two given strings into a new string Arguments: pszPrefix - prefix of result pszSuffix - suffix of result Returns: A newly allocated string. --*/ { long prefixLen = wcslen(pszPrefix); long suffixLen = wcslen(pszSuffix); STRING_T pszResult = new WCHAR[(prefixLen+suffixLen+1)*sizeof(WCHAR)]; wcscpy(pszResult,pszPrefix); wcscpy(pszResult+prefixLen,pszSuffix); return pszResult; } NSI_SERVER_BINDING_VECTOR_T * BVTtoSBVT( NSI_BINDING_VECTOR_T * pbvt ) /*++ Routine Description: This function reformulates a NSI_BINDING_VECTOR_T as a NSI_SERVER_BINDING_VECTOR_T but does not make copies of the string bindings in the process. --*/ { NSI_SERVER_BINDING_VECTOR_T * psbvt = (NSI_SERVER_BINDING_VECTOR_T *) new char [ sizeof(UNSIGNED32) + sizeof(NSI_STRING_BINDING_T)*(pbvt->count) ]; psbvt->count = pbvt->count; for (UNSIGNED32 i = 0; i < pbvt->count; i++) { psbvt->string[i] = pbvt->binding[i].string; } return psbvt; } STRING_T makeBindingStringWithObject( STRING_T binding, STRING_T object ) /*++ Routine Description: Take an existing binding string and replace the object part before returning. The string returned is allocated by RPC and must be freed by calling RpcStringFree unless it is an out parameter for an RPC call (or a part of such a returned value) in which case it is freed by the RPC runtime. --*/ { RPC_STATUS Status; /* variables to receive binding decomposition */ STRING_T ProtSeq; STRING_T EndPoint; STRING_T NetworkAddr; STRING_T NetworkOptions; Status = RpcStringBindingParse( binding, NULL, // don't need current Object ID &ProtSeq, &NetworkAddr, &EndPoint, // Don't need endpoint &NetworkOptions); if (Status != RPC_S_OK) // corrupted entry? Raise(NSI_S_INVALID_STRING_BINDING); // The following allocates and creates a new string // BUGBUG: for now we keep the endpoint to be compatible with // the old locator, but the endpoint really should be removed STRING_T tempBinding = NULL; Status = RpcStringBindingCompose( object, ProtSeq, NetworkAddr, EndPoint, NetworkOptions, &tempBinding ); if (Status != RPC_S_OK) // must be out of memory Raise(NSI_S_OUT_OF_MEMORY); RpcStringFree(&ProtSeq); RpcStringFree(&EndPoint); RpcStringFree(&NetworkAddr); RpcStringFree(&NetworkOptions); return tempBinding; } void I_NSI_NS_HANDLE_T_rundown( IN NSI_NS_HANDLE_T InqContext ) /*++ Routine Description: Cleanup after an a lookup operation. Internal version. Arguments: InqContext - Context to cleanup --*/ { CContextHandle *pHandle = (CContextHandle *) InqContext; __try { pHandle->rundown(); } __finally { delete pHandle; } } void NSI_NS_HANDLE_T_done( /* [out][in] */ NSI_NS_HANDLE_T __RPC_FAR *inq_context, /* [out] */ UNSIGNED16 __RPC_FAR *status) /*++ Routine Description: A generalized version of the "done" operation for NS context handles. Arguments: inq_context - context handle the client is done with status - status is returned here --*/ { *status = NSI_S_OK; if (*inq_context) __try { I_NSI_NS_HANDLE_T_rundown(*inq_context); } __except (EXCEPTION_EXECUTE_HANDLER) { *status = (UNSIGNED16) GetExceptionCode(); } *inq_context = NULL; } void __RPC_API NSI_NS_HANDLE_T_rundown( IN NSI_NS_HANDLE_T InqContext ) /*++ Routine Description: Cleanup after an aborted lookup operation. This is the one called directly by RPC runtime when needed. Arguments: InqContext - Context to cleanup --*/ { DBGOUT(MEM1,"RPC Called Rundown\n\n"); I_NSI_NS_HANDLE_T_rundown(InqContext); } RPC_BINDING_HANDLE ConnectToMasterLocator( ULONG& Status ) /*++ Routine Description: Contains the logic for connecting to a master locator, whether in a workgroup or domain. Tries to use the cached list of masters in the locator object, and if there are none then in a workgroup environment tries a broadcast for master locators in the workgroup. When a name for a machine with a master is found, the locator is pinged to ensure that it is actually alive. Returns: A binding handle to a live master locator, if any. NULL otherwise. --*/ { RPC_BINDING_HANDLE hCurrentMaster = NULL; int fHandleWorked = FALSE; TGSLString *pMasters = myRpcLocator->getMasters(); if ((pMasters->size() == 0) && (myRpcLocator->IsInWorkgroup())) { myRpcLocator->TryBroadcastingForMasterLocator(); pMasters = myRpcLocator->getMasters(); } TGSLStringIter iterDCs(*pMasters); CStringW * pPrimaryDCName = myRpcLocator->getPDC(); RpcImpersonateClient(0); for (CStringW * pSWcurrentDC = pPrimaryDCName ? pPrimaryDCName : iterDCs.next(); pSWcurrentDC != NULL; pSWcurrentDC = iterDCs.next() ) { hCurrentMaster = MakeDClocTolocHandle(*pSWcurrentDC); RpcTryExcept { CLIENT_I_nsi_ping_locator( hCurrentMaster, &Status ); } RpcExcept (EXCEPTION_EXECUTE_HANDLER) { RpcBindingFree(&hCurrentMaster); hCurrentMaster = NULL; Status = RpcExceptionCode(); if (Status == RPC_S_ACCESS_DENIED) Status = NSI_S_NO_NS_PRIVILEGE; /* Unnecessary to set it here if (Status == RPC_S_SERVER_TOO_BUSY) Status = NSI_S_NAME_SERVICE_UNAVAILABLE; */ } RpcEndExcept; if (Status != RPC_S_OK) continue; fHandleWorked = TRUE; break; } RpcRevertToSelf(); if (!fHandleWorked) { hCurrentMaster = NULL; if (Status != NSI_S_NO_NS_PRIVILEGE) Status = NSI_S_NAME_SERVICE_UNAVAILABLE; } return hCurrentMaster; } NSI_UUID_VECTOR_T * getVector(CObjectInqHandle *pInqHandle) /*++ Routine Description: Given a handle for object lookup, extract all the objects in it and format them into a standard object vector. This function is always used in creating ObjectInqHandles Arguments: pInqHandle - a context handle for object inquiry Returns: A standard object vector --*/ { // we have no idea how many we will have, so we use a simple // linked list to gather the UUIDs before setting up the vector int fDone = FALSE; CSimpleLinkList GuidList; while (!fDone) { __try { GUID * pGUID = pInqHandle->next(); if (pGUID) GuidList.insert(pGUID); else fDone = TRUE; } __except(EXCEPTION_EXECUTE_HANDLER) { fDone = TRUE; } } ULONG ulSize = GuidList.size(); NSI_UUID_VECTOR_T *pUuidVector = (NSI_UUID_VECTOR_T *) midl_user_allocate( sizeof(UNSIGNED32) + sizeof(GUID*) * ulSize ); pUuidVector->count = ulSize; for (ULONG i = 0; i < ulSize; i++) pUuidVector->uuid[i] = (GUID*) GuidList.pop(); return pUuidVector; } void *new_handler(size_t) /* this is just in case we use the standard "new" operation, which we don't */ { Raise(NSI_S_OUT_OF_MEMORY); return NULL; // just to keep the compiler happy, never used } void parseEntryName( CONST_STRING_T fullName, CStringW * &pswDomainName, CStringW * &pswEntryName ) /*++ Routine Description: Parses a given entry name into its domain and entry name parts. Deals with both global and relative (local) entry name syntax. Arguments: fullName - the name to parse pswDomainName - the domain name (if the input was a global name) is returned here. For a local name, NULL is returned. pswEntryName - the relative entry name is returned here. Remarks: The returned values are newly allocated string objects. --*/ { if (memcmp(RelativePrefix, fullName, RelativePrefixLength * sizeof(WCHAR)) == 0) { pswDomainName = NULL; pswEntryName = new CStringW(fullName + RelativePrefixLength); } else if (memcmp(GlobalPrefix, fullName, GlobalPrefixLength * sizeof(WCHAR)) == 0) { // make a copy starting after the prefix STRING_T domainBegin = CStringW::copyString(fullName + GlobalPrefixLength); for (STRING_T psz = domainBegin; (*psz != NSnameDelimiter) && (*psz != WStringTerminator); psz++); if (*psz == WStringTerminator) { // no entry name at all delete [] domainBegin; Raise(NSI_S_INCOMPLETE_NAME); } else { *psz = 0; // the NULL character pswDomainName = new CStringW(domainBegin); pswEntryName = new CStringW(psz+1); delete [] domainBegin; } } else // neither relative nor global prefix found Raise(NSI_S_UNSUPPORTED_NAME_SYNTAX); } STRING_T makeGlobalName( const STRING_T szDomainName, const STRING_T szEntryName ) /*++ Routine Description: Formats a global entry name from domain and relative name parts. Arguments: szDomainName - the domain name pswEntryName - the relative entry name Returns A newly allocated global name string --*/ { STRING_T psz1, psz2; psz1 = catenate(TEXT("/"),szEntryName); psz2 = catenate(szDomainName,psz1); delete [] psz1; psz1 = catenate(GlobalPrefix,psz2); delete [] psz2; return psz1; } int IsNormalCode( IN ULONG StatusCode ) /*++ Routine Description: A test to see if a status code is a normal status to return or signifies some error of purely internal interest. Arguments: StatusCode - the code to test Returns TRUE if normal, FALSE if internal error (such as failure to connect to master locator). --*/ { return (StatusCode < NSI_S_STATUS_MAX) && (StatusCode != NSI_S_NAME_SERVICE_UNAVAILABLE) && (StatusCode != NSI_S_NO_NS_PRIVILEGE) && (StatusCode != NSI_S_OUT_OF_MEMORY) && (StatusCode != NSI_S_NO_MASTER_LOCATOR); } void StripObjectsFrom( NSI_BINDING_VECTOR_P_T * BindingVectorOut ) /*++ Routine Description: The vector of binding handles normally returned from a lookup handle contains binding strings with objects (if objects are present). However, this is not usable in locator-to-locator communication because the old (Steve Zeck's) locator expects objectless strings and simply attaches objects in front of the strings it gets from a master. We therefore strip the objects from these strings when they are meant to be returned to another locator. This also gives us an opportunity to eliminate duplicates, which is important because an object inquiry must be done via RPC for every binding string returned to the inquiring locator as a result of the weakness of the current locator-to-locator interface. Arguments: StatusCode - the code to test Returns TRUE if normal, FALSE if internal error (such as failure to connect to master locator). --*/ { if (!*BindingVectorOut) return; NSI_BINDING_VECTOR_P_T pbvtGivenVector = *BindingVectorOut; TCSafeSkipList SSLwinnow; for (ULONG i = 0; i < pbvtGivenVector->count; i++) { STRING_T pszOld = pbvtGivenVector->binding[i].string; STRING_T pszNew = makeBindingStringWithObject(pszOld,NULL); pbvtGivenVector->binding[i].string = CStringW::copyMIDLstring(pszNew); midl_user_free(pszOld); RpcStringFree(&pszNew); CNSBinding *pNSB = new CNSBinding(pbvtGivenVector->binding[i]); if (Duplicate == SSLwinnow.insert(pNSB)) delete pNSB; } ULONG ulBVcount = SSLwinnow.size(); NSI_BINDING_VECTOR_P_T pbvtNewVector = (NSI_BINDING_VECTOR_T *) midl_user_allocate(sizeof(UNSIGNED32) + sizeof(NSI_BINDING_T)*ulBVcount); pbvtNewVector->count = ulBVcount; TCSafeSkipListIterator nsbIter(SSLwinnow); CNSBinding* pNSB = nsbIter.next(); for (ULONG j=0; j < ulBVcount; j++) { pNSB->copyBinding(pbvtNewVector->binding[j]); pNSB = nsbIter.next(); } CBVWrapper destructor(pbvtGivenVector); // wrap to release destructor.rundown(); // run down the pbvtGivenVector SSLwinnow.wipeOut(); // release CNSBinding objects *BindingVectorOut = pbvtNewVector; } unsigned RandomBit( unsigned long *pState ) /*++ Routine Description: Generates a "random" bit. Using the MASK32 polynomial this function will have a period of exactly 2^32-1. A more complete description of this algorithm can be found in "Numerical Recipes In C: The Art of Scientific Computation" Never generates a sequence of the same value longer more then 31. This doesn't affect the odds much since a more general algorithm would generate such a sequence only every 2^31*(32/2) sequences. Arguments: pState - Pointer to a dword of State. This should be initalized to a random value (GetTickCount()) before the first call. It should be unmodified between future calls. Must not be zero. Return Value: 0 or 1 Author: MarioGo --*/ { #define B1 (1) #define B2 (1<<1) #define B3 (1<<2) #define B4 (1<<3) #define B5 (1<<4) #define B6 (1<<5) #define B7 (1<<6) #define B32 (1<<31) // Selected polynomial's from table in Numerical Recipes In C. #define MASK30 (B1 + B4 + B6) // for 30,6,4,1,0 polynomial #define MASK32 (B1 + B2 + B3 + B5 + B7) // for 32,7,5,3,2,1,0 polynomial if (*pState & B32) { *pState = ((*pState ^ MASK32) << 1) | 1; return(1); } *pState <<= 1; return(0); }