diff options
author | Dwayne Boone <dwayne@zeroc.com> | 2005-07-05 11:09:55 +0000 |
---|---|---|
committer | Dwayne Boone <dwayne@zeroc.com> | 2005-07-05 11:09:55 +0000 |
commit | 9b8cc712d4a41d71840416776bc94ee8485bb9b3 (patch) | |
tree | 7d467fdd6a66bc2b5878d82070d45adbd5c20414 /cppe/src/IceE/UUID.cpp | |
parent | cleaning the cache method out of ReferenceFactory (diff) | |
download | ice-9b8cc712d4a41d71840416776bc94ee8485bb9b3.tar.bz2 ice-9b8cc712d4a41d71840416776bc94ee8485bb9b3.tar.xz ice-9b8cc712d4a41d71840416776bc94ee8485bb9b3.zip |
Changed Ice to IceE EVERYWHERE!!!
Diffstat (limited to 'cppe/src/IceE/UUID.cpp')
-rw-r--r-- | cppe/src/IceE/UUID.cpp | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/cppe/src/IceE/UUID.cpp b/cppe/src/IceE/UUID.cpp new file mode 100644 index 00000000000..439c310bfba --- /dev/null +++ b/cppe/src/IceE/UUID.cpp @@ -0,0 +1,275 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2005 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICEE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#include <IceE/UUID.h> + +// On Windows, we use Windows's RPC UUID generator. +// +// Under WinCE we use the Cryptography functions to generate random +// numbers which should be suitable for a "version 4" UUID. +// +// On other platforms, we use a high quality random number generator +// (/dev/random) to generate "version 4" UUIDs, as described in +// http://www.ietf.org/internet-drafts/draft-mealling-uuid-urn-00.txt +// + + +#if defined(_WIN32_WCE) +# include <wincrypt.h> +#elif defined(_WIN32) +# include <rpc.h> +#else +# include <IceE/StaticMutex.h> +# include <sys/types.h> +# include <sys/stat.h> +# include <fcntl.h> +# include <unistd.h> +#endif + +using namespace std; + +// Helper char to hex functions +// +inline void halfByteToHex(unsigned char hb, char*& hexBuffer) +{ + if(hb < 10) + { + *hexBuffer++ = '0' + hb; + } + else + { + *hexBuffer++ = 'A' + (hb - 10); + } +} + +inline void bytesToHex(unsigned char* bytes, size_t len, char*& hexBuffer) +{ + for(size_t i = 0; i < len; i++) + { + halfByteToHex((bytes[i] & 0xF0) >> 4, hexBuffer); + halfByteToHex((bytes[i] & 0x0F), hexBuffer); + } +} + + +IceE::UUIDGenerationException::UUIDGenerationException(const char* file, int line) : + Exception(file, line) +{ +} + +const char* IceE::UUIDGenerationException::_name = "IceE::UUIDGenerationException"; + +#ifndef _WIN32 +// +// Unfortunately on Linux (at least up to 2.6.9), concurrent access to /dev/urandom +// can return the same value. Search for "Concurrent access to /dev/urandom" in the +// linux-kernel mailing list archive for additional details. +// Since /dev/urandom on other platforms is usually a port from Linux, this problem +// could be widespread. Therefore, instead of using 122 random bits that could be +// duplicated, we replace the last 15 bits of all "random" UUIDs by the last 15 bits +// of the process id, and in each process, we serialize access to /dev/urandom using +// a static mutex. +// + +static IceE::StaticMutex staticMutex = ICEE_STATIC_MUTEX_INITIALIZER; +static int fd = -1; +static char myPid[2]; + +namespace +{ + +// +// Close fd at exit +// +class UUIDCleanup +{ +public: + + ~UUIDCleanup() + { + IceE::StaticMutex::Lock lock(staticMutex); + if(fd != -1) + { + close(fd); + fd = -1; + } + } +}; +static UUIDCleanup uuidCleanup; +} + + +#endif + +const string +IceE::UUIDGenerationException::ice_name() const +{ + return _name; +} + +IceE::Exception* +IceE::UUIDGenerationException::ice_clone() const +{ + return new UUIDGenerationException(*this); +} + +void +IceE::UUIDGenerationException::ice_throw() const +{ + throw *this; +} + + +string +IceE::generateUUID() +{ +#if defined(_WIN32 ) && !defined(_WIN32_WCE) + + UUID uuid; + UuidCreate(&uuid); + + unsigned char* str; + + UuidToString(&uuid, &str); + + string result = reinterpret_cast<char*>(str); + + RpcStringFree(&str); + return result; + +#else + + struct UUID + { + unsigned char timeLow[4]; + unsigned char timeMid[2]; + unsigned char timeHighAndVersion[2]; + unsigned char clockSeqHiAndReserved; + unsigned char clockSeqLow; + unsigned char node[6]; + }; + UUID uuid; + + assert(sizeof(UUID) == 16); + + char* buffer = reinterpret_cast<char*>(&uuid); + int reads = 0; + size_t index = 0; + +#ifdef _WIN32_WCE + // + // TODO: I suspect we should cache this provider. + // + HCRYPTPROV prov; + if(!CryptAcquireContext(&prov, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + { + throw UUIDGenerationException(__FILE__, __LINE__); + } + + memset(buffer, 0, 16); + if(!CryptGenRandom(prov, 16, (unsigned char*)buffer)) + { + bool rc = CryptReleaseContext(prov, 0); + assert(rc); + throw UUIDGenerationException(__FILE__, __LINE__); + } + + if(!CryptReleaseContext(prov, 0)) + { + throw UUIDGenerationException(__FILE__, __LINE__); + } +#else + { + // + // Serialize access to /dev/urandom; see comment above. + // + IceE::StaticMutex::Lock lock(staticMutex); + if(fd == -1) + { + fd = open("/dev/urandom", O_RDONLY); + if (fd == -1) + { + assert(0); + throw UUIDGenerationException(__FILE__, __LINE__); + } + + // + // Initialize myPid as well + // + pid_t pid = getpid(); + myPid[0] = (pid >> 8) & 0x7F; + myPid[1] = pid & 0xFF; + } + + + // + // Limit the number of attempts to 20 reads to avoid + // a potential "for ever" loop + // + while(reads <= 20 && index != sizeof(UUID)) + { + ssize_t bytesRead = read(fd, buffer + index, sizeof(UUID) - index); + + if(bytesRead == -1 && errno != EINTR) + { + int err = errno; + fprintf(stderr, "Reading /dev/urandom returned %s\n", strerror(err)); + assert(0); + throw UUIDGenerationException(__FILE__, __LINE__); + } + else + { + index += bytesRead; + reads++; + } + } + } + + if (index != sizeof(UUID)) + { + assert(0); + throw UUIDGenerationException(__FILE__, __LINE__); + } + + // + // Replace the end of the node by myPid (15 bits) + // + uuid.node[4] = (uuid.node[4] & 0x80) | myPid[0]; + uuid.node[5] = myPid[1]; +#endif + + // + // Adjust the bits that say "version 4" UUID + // + uuid.timeHighAndVersion[0] &= 0x0F; + uuid.timeHighAndVersion[0] |= (4 << 4); + uuid.clockSeqHiAndReserved &= 0x3F; + uuid.clockSeqHiAndReserved |= 0x80; + + // + // Convert to a UUID string + // + char uuidString[16 * 2 + 4 + 1]; // 16 bytes, 4 '-' and a final '\0' + char* uuidIndex = uuidString; + bytesToHex(uuid.timeLow, sizeof(uuid.timeLow), uuidIndex); + *uuidIndex++ = '-'; + bytesToHex(uuid.timeMid, sizeof(uuid.timeMid), uuidIndex); + *uuidIndex++ = '-'; + bytesToHex(uuid.timeHighAndVersion, sizeof(uuid.timeHighAndVersion), uuidIndex); + *uuidIndex++ = '-'; + bytesToHex(&uuid.clockSeqHiAndReserved, sizeof(uuid.clockSeqHiAndReserved), uuidIndex); + bytesToHex(&uuid.clockSeqLow, sizeof(uuid.clockSeqLow), uuidIndex); + *uuidIndex++ = '-'; + bytesToHex(uuid.node, sizeof(uuid.node), uuidIndex); + *uuidIndex = '\0'; + + return uuidString; + +#endif +} |