diff options
Diffstat (limited to 'cpp/src/IceUtil/UUID.cpp')
-rw-r--r-- | cpp/src/IceUtil/UUID.cpp | 126 |
1 files changed, 117 insertions, 9 deletions
diff --git a/cpp/src/IceUtil/UUID.cpp b/cpp/src/IceUtil/UUID.cpp index 70932f32eb6..db3070a8e9e 100644 --- a/cpp/src/IceUtil/UUID.cpp +++ b/cpp/src/IceUtil/UUID.cpp @@ -15,17 +15,71 @@ #include <IceUtil/UUID.h> #include <IceUtil/Unicode.h> +// On Windows, we use Windows's RPC UUID generator. +// 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 + +#include <assert.h> + #ifdef _WIN32 # include <rpc.h> #else -extern "C" // uuid/uuid.h seems to miss extern "C" declarations. -{ -# include <uuid/uuid.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, int len, char*& hexBuffer) +{ + for(int i = 0; i < len; i++) + { + halfByteToHex((bytes[i] & 0xF0) >> 4, hexBuffer); + halfByteToHex((bytes[i] & 0x0F), hexBuffer); + } +} + + +IceUtil::UUIDGenerationException::UUIDGenerationException(const char* file, int line) : + Exception(file, line) +{} + +string +IceUtil::UUIDGenerationException::ice_name() const +{ + return "IceUtil::UUIDGenerationException"; +} + +IceUtil::Exception* +IceUtil::UUIDGenerationException::ice_clone() const +{ + return new UUIDGenerationException(*this); +} + +void +IceUtil::UUIDGenerationException::ice_throw() const +{ + throw *this; +} + + string IceUtil::generateUUID() { @@ -55,13 +109,67 @@ IceUtil::generateUUID() #else - uuid_t uuid; - uuid_generate(uuid); + // On Linux, /dev/random, even when used in blocking mode, sometimes + // fails or returns fewer bytes. + // Maybe we should use a combination of version 4 UUIDs (with /dev/random), + // and version 1 UUIDs (MAC address + time), when /dev/random is exhausted? + + int fd = open("/dev/urandom", O_RDONLY); + + if (fd == -1) + { + assert(0); + throw UUIDGenerationException(__FILE__, __LINE__); + } + + 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); + + ssize_t bytesRead = read(fd, &uuid, sizeof(UUID)); + + if (bytesRead != sizeof(UUID)) + { + assert(0); + close(fd); + throw UUIDGenerationException(__FILE__, __LINE__); + } + + close(fd); + + // Adjust the bits that say "version 4" UUID + // + uuid.timeHighAndVersion[0] &= 0x0F; + uuid.timeHighAndVersion[0] |= (3 << 4); + uuid.clockSeqHiAndReserved &= 0x3F; + uuid.clockSeqHiAndReserved |= 0x80; - char str[37]; - uuid_unparse(uuid, str); + // 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 str; + return uuidString; #endif } |