Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #if defined(XP_WIN)
8 : #include <windows.h>
9 : #include <objbase.h>
10 : #elif defined(XP_MACOSX)
11 : #include <CoreFoundation/CoreFoundation.h>
12 : #else
13 : #include <stdlib.h>
14 : #include "prrng.h"
15 : #endif
16 :
17 : #include "nsUUIDGenerator.h"
18 :
19 : #ifdef ANDROID
20 : extern "C" NS_EXPORT void arc4random_buf(void*, size_t);
21 : #endif
22 :
23 : using namespace mozilla;
24 :
25 1219 : NS_IMPL_ISUPPORTS(nsUUIDGenerator, nsIUUIDGenerator)
26 :
27 3 : nsUUIDGenerator::nsUUIDGenerator()
28 3 : : mLock("nsUUIDGenerator.mLock")
29 : {
30 3 : }
31 :
32 0 : nsUUIDGenerator::~nsUUIDGenerator()
33 : {
34 0 : }
35 :
36 : nsresult
37 3 : nsUUIDGenerator::Init()
38 : {
39 : // We're a service, so we're guaranteed that Init() is not going
40 : // to be reentered while we're inside Init().
41 :
42 : #if !defined(XP_WIN) && !defined(XP_MACOSX) && !defined(HAVE_ARC4RANDOM)
43 : /* initialize random number generator using NSPR random noise */
44 : unsigned int seed;
45 :
46 3 : size_t bytes = 0;
47 9 : while (bytes < sizeof(seed)) {
48 3 : size_t nbytes = PR_GetRandomNoise(((unsigned char*)&seed) + bytes,
49 3 : sizeof(seed) - bytes);
50 3 : if (nbytes == 0) {
51 0 : return NS_ERROR_FAILURE;
52 : }
53 3 : bytes += nbytes;
54 : }
55 :
56 : /* Initialize a new RNG state, and immediately switch
57 : * back to the previous one -- we want to use mState
58 : * only for our own calls to random().
59 : */
60 3 : mSavedState = initstate(seed, mState, sizeof(mState));
61 3 : setstate(mSavedState);
62 :
63 3 : mRBytes = 4;
64 : #ifdef RAND_MAX
65 : if ((unsigned long)RAND_MAX < 0xffffffffUL) {
66 3 : mRBytes = 3;
67 : }
68 : if ((unsigned long)RAND_MAX < 0x00ffffffUL) {
69 : mRBytes = 2;
70 : }
71 : if ((unsigned long)RAND_MAX < 0x0000ffffUL) {
72 : mRBytes = 1;
73 : }
74 : if ((unsigned long)RAND_MAX < 0x000000ffUL) {
75 : return NS_ERROR_FAILURE;
76 : }
77 : #endif
78 :
79 : #endif /* non XP_WIN and non XP_MACOSX and non ARC4RANDOM */
80 :
81 3 : return NS_OK;
82 : }
83 :
84 : NS_IMETHODIMP
85 5 : nsUUIDGenerator::GenerateUUID(nsID** aRet)
86 : {
87 5 : nsID* id = static_cast<nsID*>(moz_xmalloc(sizeof(nsID)));
88 5 : if (!id) {
89 0 : return NS_ERROR_OUT_OF_MEMORY;
90 : }
91 :
92 5 : nsresult rv = GenerateUUIDInPlace(id);
93 5 : if (NS_FAILED(rv)) {
94 0 : free(id);
95 0 : return rv;
96 : }
97 :
98 5 : *aRet = id;
99 5 : return rv;
100 : }
101 :
102 : NS_IMETHODIMP
103 151 : nsUUIDGenerator::GenerateUUIDInPlace(nsID* aId)
104 : {
105 : // The various code in this method is probably not threadsafe, so lock
106 : // across the whole method.
107 302 : MutexAutoLock lock(mLock);
108 :
109 : #if defined(XP_WIN)
110 : HRESULT hr = CoCreateGuid((GUID*)aId);
111 : if (FAILED(hr)) {
112 : return NS_ERROR_FAILURE;
113 : }
114 : #elif defined(XP_MACOSX)
115 : CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
116 : if (!uuid) {
117 : return NS_ERROR_FAILURE;
118 : }
119 :
120 : CFUUIDBytes bytes = CFUUIDGetUUIDBytes(uuid);
121 : memcpy(aId, &bytes, sizeof(nsID));
122 :
123 : CFRelease(uuid);
124 : #else /* not windows or OS X; generate randomness using random(). */
125 : /* XXX we should be saving the return of setstate here and switching
126 : * back to it; instead, we use the value returned when we called
127 : * initstate, since older glibc's have broken setstate() return values
128 : */
129 : #ifndef HAVE_ARC4RANDOM
130 151 : setstate(mState);
131 : #endif
132 :
133 : #ifdef HAVE_ARC4RANDOM_BUF
134 : arc4random_buf(aId, sizeof(nsID));
135 : #else /* HAVE_ARC4RANDOM_BUF */
136 151 : size_t bytesLeft = sizeof(nsID);
137 1963 : while (bytesLeft > 0) {
138 : #ifdef HAVE_ARC4RANDOM
139 : long rval = arc4random();
140 : const size_t mRBytes = 4;
141 : #else
142 906 : long rval = random();
143 : #endif
144 :
145 :
146 906 : uint8_t* src = (uint8_t*)&rval;
147 : // We want to grab the mRBytes least significant bytes of rval, since
148 : // mRBytes less than sizeof(rval) means the high bytes are 0.
149 : #ifdef IS_BIG_ENDIAN
150 : src += sizeof(rval) - mRBytes;
151 : #endif
152 906 : uint8_t* dst = ((uint8_t*)aId) + (sizeof(nsID) - bytesLeft);
153 906 : size_t toWrite = (bytesLeft < mRBytes ? bytesLeft : mRBytes);
154 3322 : for (size_t i = 0; i < toWrite; i++) {
155 2416 : dst[i] = src[i];
156 : }
157 :
158 906 : bytesLeft -= toWrite;
159 : }
160 : #endif /* HAVE_ARC4RANDOM_BUF */
161 :
162 : /* Put in the version */
163 151 : aId->m2 &= 0x0fff;
164 151 : aId->m2 |= 0x4000;
165 :
166 : /* Put in the variant */
167 151 : aId->m3[0] &= 0x3f;
168 151 : aId->m3[0] |= 0x80;
169 :
170 : #ifndef HAVE_ARC4RANDOM
171 : /* Restore the previous RNG state */
172 151 : setstate(mSavedState);
173 : #endif
174 : #endif
175 :
176 302 : return NS_OK;
177 : }
|