/*++ Copyright (C) Microsoft Corporation, 1992 - 1999 Module Name: dceuuid.cxx Abstract: This module contains the entry points for routines dealing with UUIDs. In particular, UuidCreate lives here. Author: Michael Montague (mikemon) 16-Jan-1992 Revision History: Dave Steckler (davidst) 31-Mar-1992 If NT, remote call to UuidGetValues. Mario Goertzel (mariogo) 1-May-1994 Added the rest of the DCE UUID APIs Mario Goertzel (mariogo) 18-May-1994 Changed algorithm and implementation. No longer based on RPC. Platform specific functions in uuidsup.cxx (win32) and dos\uuid16 (dos/win16). --*/ #include #include #include #include #include // Contain a cached block of uuids to reduce the // average cost of creating a uuid. UUID_CACHED_VALUES_STRUCT UuidCachedValues; #define CACHE_VALID 1 #define CACHE_LOCAL_ONLY 2 // -> CACHE_VALID static unsigned char UuidCacheValid = CACHE_LOCAL_ONLY; RPC_STATUS RPC_ENTRY I_UuidCreate( OUT UUID PAPI * Uuid ) /*++ Historically this function was used for cheap sometimes unique uuid's for context handles and such. Now it's just a wrapper for UuidCreate. --*/ { RPC_STATUS Status = UuidCreateSequential (Uuid); if (Status == RPC_S_UUID_LOCAL_ONLY) return(RPC_S_OK); return(Status); } #define RC4_REKEY_PARAM (500000) extern void *g_rc4SafeCtx; RPC_STATUS GenerateRandomNumber(unsigned char *Buffer, int BufferSize) { unsigned int KeyEntry; unsigned int KeyBytesUsed = 0; rc4_safe_select(g_rc4SafeCtx, &KeyEntry, &KeyBytesUsed); if (KeyBytesUsed >= RC4_REKEY_PARAM) { BYTE newSeed[256]; if (!RtlGenRandom (newSeed, sizeof(newSeed))) { return RPC_S_OUT_OF_MEMORY; } rc4_safe_key(g_rc4SafeCtx, KeyEntry, sizeof(newSeed), newSeed); } // the rc4_safe fucntion is thread safe rc4_safe(g_rc4SafeCtx, KeyEntry, BufferSize, Buffer); return RPC_S_OK; } RPC_STATUS RPC_ENTRY UuidCreate ( OUT UUID PAPI * Uuid ) { RPC_STATUS RpcStatus; RPC_UUID_GENERATE PAPI * RpcUuid = (RPC_UUID_GENERATE PAPI *) Uuid; RpcStatus = GenerateRandomNumber((unsigned char *)Uuid, 16); if (RpcStatus != RPC_S_OK) return RpcStatus; // Overwriting some bits of the uuid RpcUuid->TimeHiAndVersion = (RpcUuid->TimeHiAndVersion & RPC_UUID_TIME_HIGH_MASK) | RPC_RAND_UUID_VERSION; RpcUuid->ClockSeqHiAndReserved = (RpcUuid->ClockSeqHiAndReserved & RPC_UUID_CLOCK_SEQ_HI_MASK) | RPC_UUID_RESERVED; return RPC_S_OK; } #define MAX_CACHED_UUID_TIME 10000 // 10 seconds RPC_STATUS RPC_ENTRY UuidCreateSequential ( OUT UUID PAPI * Uuid ) /*++ Routine Description: This routine will create a new UUID (or GUID) which is unique in time and space. We will try to guarantee that the UUID (or GUID) we generate is unique in time and space. This means that this routine may fail if we can not generate one which we can guarantee is unique in time and space. Arguments: Uuid - Returns the generated UUID (or GUID). Return Value: RPC_S_OK - The operation completed successfully. RPC_S_UUID_NO_ADDRESS - We were unable to obtain the ethernet or token ring address for this machine. RPC_S_UUID_LOCAL_ONLY - On NT & Chicago if we can't get a network address. This is a warning to the user, the UUID is still valid, it just may not be unique on other machines. RPC_S_OUT_OF_MEMORY - Returned as needed. --*/ { RPC_UUID_GENERATE PAPI * RpcUuid = (RPC_UUID_GENERATE PAPI *) Uuid; RPC_STATUS Status = RPC_S_OK; static DWORD LastTickCount = 0; InitializeIfNecessary(); if (GetTickCount()-LastTickCount > MAX_CACHED_UUID_TIME) { UuidCachedValues.AllocatedCount = 0; LastTickCount = GetTickCount(); } ULARGE_INTEGER Time; long Delta; for(;;) { Time.QuadPart = UuidCachedValues.Time.QuadPart; // Copy the static info into the UUID. We can't do this later // because the clock sequence could be updated by another thread. *(unsigned long *)&RpcUuid->ClockSeqHiAndReserved = *(unsigned long *)&UuidCachedValues.ClockSeqHiAndReserved; *(unsigned long *)&RpcUuid->NodeId[2] = *(unsigned long *)&UuidCachedValues.NodeId[2]; Delta = InterlockedDecrement(&UuidCachedValues.AllocatedCount); if (Time.QuadPart != UuidCachedValues.Time.QuadPart) { // If our captured time doesn't match the cache then another // thread already took the lock and updated the cache. We'll // just loop and try again. continue; } if (Delta >= 0) { break; } // // Allocate block of Uuids. // Status = UuidGetValues( &UuidCachedValues ); if (Status == RPC_S_OK) { UuidCacheValid = CACHE_VALID; } else { UuidCacheValid = CACHE_LOCAL_ONLY; } if (Status != RPC_S_OK && Status != RPC_S_UUID_LOCAL_ONLY) { #ifdef DEBUGRPC if (Status != RPC_S_OUT_OF_MEMORY) PrintToDebugger("RPC: UuidGetValues returned or raised: %x\n", Status); #endif ASSERT( (Status == RPC_S_OUT_OF_MEMORY) ); return Status; } // Loop } Time.QuadPart -= Delta; RpcUuid->TimeLow = (unsigned long) Time.LowPart; RpcUuid->TimeMid = (unsigned short) (Time.HighPart & 0x0000FFFF); RpcUuid->TimeHiAndVersion = (unsigned short) (( (unsigned short)(Time.HighPart >> 16) & RPC_UUID_TIME_HIGH_MASK) | RPC_UUID_VERSION); ASSERT( Status == RPC_S_OK || Status == RPC_S_UUID_LOCAL_ONLY); if (UuidCacheValid == CACHE_LOCAL_ONLY) { return RPC_S_UUID_LOCAL_ONLY; } return(Status); } RPC_STATUS RPC_ENTRY UuidToString ( IN UUID PAPI * Uuid, OUT unsigned short PAPI * PAPI * StringUuid ) /*++ Routine Description: This routine converts a UUID into its string representation. Arguments: Uuid - Supplies the UUID to be converted into string representation. StringUuid - Returns the string representation of the UUID. The runtime will allocate the string. The caller is responsible for freeing the string using RpcStringFree. Return Value: RPC_S_OK - We successfully converted the UUID into its string representation. RPC_S_OUT_OF_MEMORY - Insufficient memory is available to allocate a string. --*/ { RPC_CHAR PAPI * String; InitializeIfNecessary(); // The string representation of a UUID is always 36 character long, // and we need one more for the terminating zero. *StringUuid = (RPC_CHAR PAPI *) RpcpFarAllocate(sizeof(RPC_CHAR) * 37); if ( *StringUuid == 0 ) { return(RPC_S_OUT_OF_MEMORY); } String = ((RPC_UUID PAPI *) Uuid)->ConvertToString(*StringUuid); *String = 0; return(RPC_S_OK); } RPC_STATUS RPC_ENTRY UuidFromString ( IN unsigned short PAPI * StringUuid OPTIONAL, OUT UUID PAPI * Uuid ) /*++ Routine Description: We convert a UUID from its string representation into the binary representation. Arguments: StringUuid - Optionally supplies the string representation of the UUID; if the string is not supplied, then the Uuid is set to the NIL UUID. Uuid - Returns the binary representation of the UUID. Return Value: RPC_S_OK - The string representation was successfully converted into the binary representation. RPC_S_INVALID_STRING_UUID - The supplied string UUID is not correct. --*/ { RPC_UUID RpcUuid; if ( StringUuid == 0 ) { ((RPC_UUID PAPI *) Uuid)->SetToNullUuid(); return(RPC_S_OK); } if ( RpcUuid.ConvertFromString(StringUuid) != 0) { return(RPC_S_INVALID_STRING_UUID); } ((RPC_UUID PAPI *) Uuid)->CopyUuid(&RpcUuid); return(RPC_S_OK); } signed int RPC_ENTRY UuidCompare ( IN UUID __RPC_FAR * Uuid1, IN UUID __RPC_FAR * Uuid2, OUT RPC_STATUS __RPC_FAR * Status ) /*++ Routine Description: The supplied uuids are compared and their order is determined. Arguments: Uuid1, Uuid2 - Supplies the uuids to be compared. A value of NULL can be supplied to indicate the nil uuid. Status - The status of the function. Currently always RPC_S_OK. Return Value: Returns the result of the comparison. Negative one (-1) will be returned if Uuid1 precedes Uuid2 in order, zero will be returned if Uuid1 is equal to Uuid2, and positive one (1) will be returned if Uuid1 follows Uuid2 in order. A nil uuid is the first uuid in order. Note: The algorithm for comparing uuids is specified by the DCE RPC Architecture. --*/ { int Uuid1Nil, Uuid2Nil; RPC_STATUS RpcStatus; Uuid1Nil = UuidIsNil(Uuid1, &RpcStatus); ASSERT(RpcStatus == RPC_S_OK); Uuid2Nil = UuidIsNil(Uuid2, &RpcStatus); ASSERT(RpcStatus == RPC_S_OK); *Status = RPC_S_OK; if ( Uuid1Nil != 0 ) { // Uuid1 is the nil uuid. if ( Uuid2Nil != 0 ) { // Uuid2 is the nil uuid. return(0); } else { return(-1); } } else if ( Uuid2Nil != 0 ) { // Uuid2 is the nil uuid. return(1); } else { if ( Uuid1->Data1 == Uuid2->Data1 ) { if ( Uuid1->Data2 == Uuid2->Data2 ) { if ( Uuid1->Data3 == Uuid2->Data3 ) { int compare = RpcpMemoryCompare(&Uuid1->Data4[0], &Uuid2->Data4[0], 8); if (compare > 0) { return(1); } else if (compare < 0 ) { return(-1); } return(0); } else if ( Uuid1->Data3 > Uuid2->Data3 ) { return(1); } else { return(-1); } } else if ( Uuid1->Data2 > Uuid2->Data2 ) { return(1); } else { return(-1); } } else if ( Uuid1->Data1 > Uuid2->Data1 ) { return(1); } else { return(-1); } } ASSERT(!"This is not reached"); return(1); } RPC_STATUS RPC_ENTRY UuidCreateNil ( OUT UUID __RPC_FAR * NilUuid ) /*++ Arguments: NilUuid - Returns a nil uuid. --*/ { ((RPC_UUID __RPC_FAR *)NilUuid)->SetToNullUuid(); return(RPC_S_OK); } int RPC_ENTRY UuidEqual ( IN UUID __RPC_FAR * Uuid1, IN UUID __RPC_FAR * Uuid2, OUT RPC_STATUS __RPC_FAR * Status ) /*++ Routine Description: This routine is used to determine if two uuids are equal. Arguments: Uuid1, Uuid2 - Supplies the uuids to compared for equality. A value of NULL can be supplied to indicate the nil uuid. Status - Will always be set to RPC_S_OK. Return Value: Returns non-zero if Uuid1 equals Uuid2; otherwise, zero will be returned. --*/ { *Status = RPC_S_OK; if (Uuid1 == 0) { if ( (Uuid2 == 0) || ((RPC_UUID __RPC_FAR *)Uuid2)->IsNullUuid()) { return 1; } return 0; } if (Uuid2 == 0) { if (((RPC_UUID __RPC_FAR *)Uuid1)->IsNullUuid()) { return 1; } return 0; } return( ((RPC_UUID __RPC_FAR *)Uuid1)->MatchUuid( (RPC_UUID __RPC_FAR *)Uuid2) == 0 ); } unsigned short RPC_ENTRY UuidHash ( IN UUID __RPC_FAR * Uuid, OUT RPC_STATUS __RPC_FAR * Status ) /*++ Routine Description: An application will use this routine to create a hash value for a uuid. Arguments: Uuid - Supplies the uuid for which we want to create a hash value. A value of NULL can be supplied to indicate the nil uuid. Status - Will always be set to RPC_S_OK. Return Value: Returns the hash value. --*/ { *Status = RPC_S_OK; if ( Uuid == 0 ) { return(0); } return( ((RPC_UUID __RPC_FAR *)Uuid)->HashUuid() ); } int RPC_ENTRY UuidIsNil ( IN UUID __RPC_FAR * Uuid, OUT RPC_STATUS __RPC_FAR * Status ) /*++ Routine Description: We will determine if the supplied uuid is the nil uuid or not. Arguments: Uuid - Supplies the uuid to check. A value of NULL indicates the nil uuid. Status - This will always be RPC_S_OK. Return Value: Returns non-zero if the supplied uuid is the nil uuid; otherwise, zero will be returned. --*/ { *Status = RPC_S_OK; if ( Uuid == 0 ) { return(1); } return ( ((RPC_UUID __RPC_FAR *) Uuid)->IsNullUuid() ); }