Platform-Independent Guid Generation in C++

Platform-independent GUID generation in C++?

If you can afford to use Boost, then there is a UUID library that should do the trick. It's very straightforward to use - check the documentation and this answer.

How to generate a GUID in C?

You can either use or look at the code of Boost.Uuid :

http://www.boost.org/doc/libs/1_47_0/libs/uuid/index.html

It is a C++ library, but still, you can find inside how the code author retrieved the Uuid on multiple systems. Last time I checked (january 2010), I found at least the following implementations for Windows and Linux/Solaris (this info could be outdated):

UUID/GUID on Linux/Solaris

Open a file to /dev/urandom and read enough bytes (16) to make up a GUID/UUID.

UUID/GUID on Windows

Use the following WinAPI functions

  • CryptAcquireContext to acquire a random context
  • CryptReleaseContext to release the acquired random context
  • CryptGenRandom to generate enough bytes to make up a GUID/UUID

Other implementations

The Wikipedia page on GUID/UUID has a list of alternative implementations you could use/study:

https://en.wikipedia.org/wiki/UUID#Implementations

About your conditions

There is a GUID/UUID type which is always random (the version 4), meaning that to be compatible with other GUID/UUID semantics, you should respect that.

Now, you want the GUID/UUID to be ordered in time. The only way to do that without weakening the GUID/UUID randomness would be to prefix the 16-byte GUID/UUID with an unsigned integer (which would make your identifier data 20-bytes, or more, depending on your integer). Just generate a GUID/UUID, and increase the integer.

Simple Generation of GUID in C

CoCreateGuid is standard for all forms of Windows. Linux standard is libuuid, which is standard on all Linux versions and should be lightweight. I don't know of any library that will work for both Windows and Unix. I think that an #if branch for Windows and Linux is actually appropriate here.

Example of UUID generation using Boost in C++

A basic example:

#include <boost/uuid/uuid.hpp>            // uuid class
#include <boost/uuid/uuid_generators.hpp> // generators
#include <boost/uuid/uuid_io.hpp> // streaming operators etc.

int main() {
boost::uuids::uuid uuid = boost::uuids::random_generator()();
std::cout << uuid << std::endl;
}

Example output:

7feb24af-fc38-44de-bc38-04defc3804de

How to obtain (almost) unique system identifier in a cross platform way?

To generate a mostly unique machine id, you can get a few serial numbers from various pieces of hardware on the system. Most processors will have a CPU serial number, the hard disks each have a number, and each network card will have a unique MAC address.

You can get these and build a fingerprint for the machine. You might want to allow some of these numbers to change before declaring it a new machine. ( e.g. if the 2 out of three are the same, then the machine is the same ). So you can deal somewhat gracefully from having a component upgraded.

I've clipped some code from one of my projects that gets these numbers.

Windows:

#include "machine_id.h"   

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <intrin.h>
#include <iphlpapi.h>
#ifndef _MSC_VER
#include <cpuid.h>
#else
#include <intrin.h>
#endif

// we just need this for purposes of unique machine id. So any one or two mac's is
// fine.
u16 hashMacAddress( PIP_ADAPTER_INFO info )
{
u16 hash = 0;
for ( u32 i = 0; i < info->AddressLength; i++ )
{
hash += ( info->Address[i] << (( i & 1 ) * 8 ));
}
return hash;
}

void getMacHash( u16& mac1, u16& mac2 )
{
IP_ADAPTER_INFO AdapterInfo[32];
DWORD dwBufLen = sizeof( AdapterInfo );

DWORD dwStatus = GetAdaptersInfo( AdapterInfo, &dwBufLen );
if ( dwStatus != ERROR_SUCCESS )
return; // no adapters.

PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo;
mac1 = hashMacAddress( pAdapterInfo );
if ( pAdapterInfo->Next )
mac2 = hashMacAddress( pAdapterInfo->Next );

// sort the mac addresses. We don't want to invalidate
// both macs if they just change order.
if ( mac1 > mac2 )
{
u16 tmp = mac2;
mac2 = mac1;
mac1 = tmp;
}
}

u16 getVolumeHash()
{
DWORD serialNum = 0;

// Determine if this volume uses an NTFS file system.
GetVolumeInformation( "c:\\", NULL, 0, &serialNum, NULL, NULL, NULL, 0 );
u16 hash = (u16)(( serialNum + ( serialNum >> 16 )) & 0xFFFF );

return hash;
}

u16 getCpuHash()
{
int cpuinfo[4] = { 0, 0, 0, 0 };
__cpuid( cpuinfo, 0 );
u16 hash = 0;
u16* ptr = (u16*)(&cpuinfo[0]);
for ( u32 i = 0; i < 8; i++ )
hash += ptr[i];

return hash;
}

const char* getMachineName()
{
static char computerName[1024];
DWORD size = 1024;
GetComputerName( computerName, &size );
return &(computerName[0]);
}

Linux and OsX:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <sys/utsname.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <assert.h>

#ifdef DARWIN
#include <net/if_dl.h>
#include <ifaddrs.h>
#include <net/if_types.h>
#else //!DARWIN
// #include <linux/if.h>
// #include <linux/sockios.h>
#endif //!DARWIN

const char* getMachineName()
{
static struct utsname u;

if ( uname( &u ) < 0 )
{
assert(0);
return "unknown";
}

return u.nodename;
}

//---------------------------------get MAC addresses ------------------------------------unsigned short-unsigned short----------
// we just need this for purposes of unique machine id. So any one or two mac's is fine.
unsigned short hashMacAddress( unsigned char* mac )
{
unsigned short hash = 0;

for ( unsigned int i = 0; i < 6; i++ )
{
hash += ( mac[i] << (( i & 1 ) * 8 ));
}
return hash;
}

void getMacHash( unsigned short& mac1, unsigned short& mac2 )
{
mac1 = 0;
mac2 = 0;

#ifdef DARWIN

struct ifaddrs* ifaphead;
if ( getifaddrs( &ifaphead ) != 0 )
return;

// iterate over the net interfaces
bool foundMac1 = false;
struct ifaddrs* ifap;
for ( ifap = ifaphead; ifap; ifap = ifap->ifa_next )
{
struct sockaddr_dl* sdl = (struct sockaddr_dl*)ifap->ifa_addr;
if ( sdl && ( sdl->sdl_family == AF_LINK ) && ( sdl->sdl_type == IFT_ETHER ))
{
if ( !foundMac1 )
{
foundMac1 = true;
mac1 = hashMacAddress( (unsigned char*)(LLADDR(sdl))); //sdl->sdl_data) + sdl->sdl_nlen) );
} else {
mac2 = hashMacAddress( (unsigned char*)(LLADDR(sdl))); //sdl->sdl_data) + sdl->sdl_nlen) );
break;
}
}
}

freeifaddrs( ifaphead );

#else // !DARWIN

int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP );
if ( sock < 0 ) return;

// enumerate all IP addresses of the system
struct ifconf conf;
char ifconfbuf[ 128 * sizeof(struct ifreq) ];
memset( ifconfbuf, 0, sizeof( ifconfbuf ));
conf.ifc_buf = ifconfbuf;
conf.ifc_len = sizeof( ifconfbuf );
if ( ioctl( sock, SIOCGIFCONF, &conf ))
{
assert(0);
return;
}

// get MAC address
bool foundMac1 = false;
struct ifreq* ifr;
for ( ifr = conf.ifc_req; (char*)ifr < (char*)conf.ifc_req + conf.ifc_len; ifr++ )
{
if ( ifr->ifr_addr.sa_data == (ifr+1)->ifr_addr.sa_data )
continue; // duplicate, skip it

if ( ioctl( sock, SIOCGIFFLAGS, ifr ))
continue; // failed to get flags, skip it
if ( ioctl( sock, SIOCGIFHWADDR, ifr ) == 0 )
{
if ( !foundMac1 )
{
foundMac1 = true;
mac1 = hashMacAddress( (unsigned char*)&(ifr->ifr_addr.sa_data));
} else {
mac2 = hashMacAddress( (unsigned char*)&(ifr->ifr_addr.sa_data));
break;
}
}
}

close( sock );

#endif // !DARWIN

// sort the mac addresses. We don't want to invalidate
// both macs if they just change order.
if ( mac1 > mac2 )
{
unsigned short tmp = mac2;
mac2 = mac1;
mac1 = tmp;
}
}

unsigned short getVolumeHash()
{
// we don't have a 'volume serial number' like on windows. Lets hash the system name instead.
unsigned char* sysname = (unsigned char*)getMachineName();
unsigned short hash = 0;

for ( unsigned int i = 0; sysname[i]; i++ )
hash += ( sysname[i] << (( i & 1 ) * 8 ));

return hash;
}

#ifdef DARWIN
#include <mach-o/arch.h>
unsigned short getCpuHash()
{
const NXArchInfo* info = NXGetLocalArchInfo();
unsigned short val = 0;
val += (unsigned short)info->cputype;
val += (unsigned short)info->cpusubtype;
return val;
}

#else // !DARWIN

static void getCpuid( unsigned int* p, unsigned int ax )
{
__asm __volatile
( "movl %%ebx, %%esi\n\t"
"cpuid\n\t"
"xchgl %%ebx, %%esi"
: "=a" (p[0]), "=S" (p[1]),
"=c" (p[2]), "=d" (p[3])
: "0" (ax)
);
}

unsigned short getCpuHash()
{
unsigned int cpuinfo[4] = { 0, 0, 0, 0 };
getCpuid( cpuinfo, 0 );
unsigned short hash = 0;
unsigned int* ptr = (&cpuinfo[0]);
for ( unsigned int i = 0; i < 4; i++ )
hash += (ptr[i] & 0xFFFF) + ( ptr[i] >> 16 );

return hash;
}
#endif // !DARWIN

int main()
{

printf("Machine: %s\n", getMachineName());
printf("CPU: %d\n", getCpuHash());
printf("Volume: %d\n", getVolumeHash());
return 0;
}


Related Topics



Leave a reply



Submit