michael@0: /* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #if defined(XP_WIN) michael@0: #include michael@0: #include michael@0: #elif defined(XP_MACOSX) michael@0: #include michael@0: #else michael@0: #include michael@0: #include "prrng.h" michael@0: #endif michael@0: michael@0: #include "nsUUIDGenerator.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsUUIDGenerator, nsIUUIDGenerator) michael@0: michael@0: nsUUIDGenerator::nsUUIDGenerator() michael@0: : mLock("nsUUIDGenerator.mLock") michael@0: { michael@0: } michael@0: michael@0: nsUUIDGenerator::~nsUUIDGenerator() michael@0: { michael@0: } michael@0: michael@0: nsresult michael@0: nsUUIDGenerator::Init() michael@0: { michael@0: // We're a service, so we're guaranteed that Init() is not going michael@0: // to be reentered while we're inside Init(). michael@0: michael@0: #if !defined(XP_WIN) && !defined(XP_MACOSX) && !defined(ANDROID) michael@0: /* initialize random number generator using NSPR random noise */ michael@0: unsigned int seed; michael@0: michael@0: size_t bytes = 0; michael@0: while (bytes < sizeof(seed)) { michael@0: size_t nbytes = PR_GetRandomNoise(((unsigned char *)&seed)+bytes, michael@0: sizeof(seed)-bytes); michael@0: if (nbytes == 0) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: bytes += nbytes; michael@0: } michael@0: michael@0: /* Initialize a new RNG state, and immediately switch michael@0: * back to the previous one -- we want to use mState michael@0: * only for our own calls to random(). michael@0: */ michael@0: mSavedState = initstate(seed, mState, sizeof(mState)); michael@0: setstate(mSavedState); michael@0: michael@0: mRBytes = 4; michael@0: #ifdef RAND_MAX michael@0: if ((unsigned long) RAND_MAX < (unsigned long)0xffffffff) michael@0: mRBytes = 3; michael@0: if ((unsigned long) RAND_MAX < (unsigned long)0x00ffffff) michael@0: mRBytes = 2; michael@0: if ((unsigned long) RAND_MAX < (unsigned long)0x0000ffff) michael@0: mRBytes = 1; michael@0: if ((unsigned long) RAND_MAX < (unsigned long)0x000000ff) michael@0: return NS_ERROR_FAILURE; michael@0: #endif michael@0: michael@0: #endif /* non XP_WIN and non XP_MACOSX */ michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsUUIDGenerator::GenerateUUID(nsID** ret) michael@0: { michael@0: nsID *id = static_cast(NS_Alloc(sizeof(nsID))); michael@0: if (id == nullptr) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: nsresult rv = GenerateUUIDInPlace(id); michael@0: if (NS_FAILED(rv)) { michael@0: NS_Free(id); michael@0: return rv; michael@0: } michael@0: michael@0: *ret = id; michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsUUIDGenerator::GenerateUUIDInPlace(nsID* id) michael@0: { michael@0: // The various code in this method is probably not threadsafe, so lock michael@0: // across the whole method. michael@0: MutexAutoLock lock(mLock); michael@0: michael@0: #if defined(XP_WIN) michael@0: HRESULT hr = CoCreateGuid((GUID*)id); michael@0: if (FAILED(hr)) michael@0: return NS_ERROR_FAILURE; michael@0: #elif defined(XP_MACOSX) michael@0: CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault); michael@0: if (!uuid) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: CFUUIDBytes bytes = CFUUIDGetUUIDBytes(uuid); michael@0: memcpy(id, &bytes, sizeof(nsID)); michael@0: michael@0: CFRelease(uuid); michael@0: #else /* not windows or OS X; generate randomness using random(). */ michael@0: /* XXX we should be saving the return of setstate here and switching michael@0: * back to it; instead, we use the value returned when we called michael@0: * initstate, since older glibc's have broken setstate() return values michael@0: */ michael@0: #ifndef ANDROID michael@0: setstate(mState); michael@0: #endif michael@0: michael@0: size_t bytesLeft = sizeof(nsID); michael@0: while (bytesLeft > 0) { michael@0: #ifdef ANDROID michael@0: long rval = arc4random(); michael@0: const size_t mRBytes = 4; michael@0: #else michael@0: long rval = random(); michael@0: #endif michael@0: michael@0: michael@0: uint8_t *src = (uint8_t*)&rval; michael@0: // We want to grab the mRBytes least significant bytes of rval, since michael@0: // mRBytes less than sizeof(rval) means the high bytes are 0. michael@0: #ifdef IS_BIG_ENDIAN michael@0: src += sizeof(rval) - mRBytes; michael@0: #endif michael@0: uint8_t *dst = ((uint8_t*) id) + (sizeof(nsID) - bytesLeft); michael@0: size_t toWrite = (bytesLeft < mRBytes ? bytesLeft : mRBytes); michael@0: for (size_t i = 0; i < toWrite; i++) michael@0: dst[i] = src[i]; michael@0: michael@0: bytesLeft -= toWrite; michael@0: } michael@0: michael@0: /* Put in the version */ michael@0: id->m2 &= 0x0fff; michael@0: id->m2 |= 0x4000; michael@0: michael@0: /* Put in the variant */ michael@0: id->m3[0] &= 0x3f; michael@0: id->m3[0] |= 0x80; michael@0: michael@0: #ifndef ANDROID michael@0: /* Restore the previous RNG state */ michael@0: setstate(mSavedState); michael@0: #endif michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: }