wireshark/packaging/u3/tools/uuid.c

312 lines
9.7 KiB
C

/* uuid.c
* UUID generation
*
* $Id$
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
** Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc.
** Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. &
** Digital Equipment Corporation, Maynard, Mass.
** Copyright (c) 1998 Microsoft.
** To anyone who acknowledges that this file is provided "AS IS"
** without any express or implied warranty: permission to use, copy,
** modify, and distribute this file for any purpose is hereby
** granted without fee, provided that the above copyright notices and
** this notice appears in all source code copies, and that none of
** the names of Open Software Foundation, Inc., Hewlett-Packard
** Company, Microsoft, or Digital Equipment Corporation be used in
** advertising or publicity pertaining to distribution of the software
** without specific, written prior permission. Neither Open Software
** Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital
** Equipment Corporation makes any representations about the
** suitability of this software for any purpose.
*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "sysdep.h"
#include "uuid.h"
/* various forward declarations */
static int read_state(unsigned16 *clockseq, uuid_time_t *timestamp,
uuid_node_t *node);
static void write_state(unsigned16 clockseq, uuid_time_t timestamp,
uuid_node_t node);
static void format_uuid_v1(uuid_t *uuid, unsigned16 clockseq,
uuid_time_t timestamp, uuid_node_t node);
static void format_uuid_v3or5(uuid_t *uuid, unsigned char hash[16],
int v);
static void get_current_time(uuid_time_t *timestamp);
static unsigned16 true_random(void);
/* uuid_create -- generator a UUID */
int uuid_create(uuid_t *uuid)
{
uuid_time_t timestamp, last_time;
unsigned16 clockseq;
uuid_node_t node;
uuid_node_t last_node;
int f;
/* acquire system-wide lock so we're alone */
LOCK;
/* get time, node ID, saved state from non-volatile storage */
get_current_time(&timestamp);
get_ieee_node_identifier(&node);
f = read_state(&clockseq, &last_time, &last_node);
/* if no NV state, or if clock went backwards, or node ID
changed (e.g., new network card) change clockseq */
if (!f || memcmp(&node, &last_node, sizeof node))
clockseq = true_random();
else if (timestamp < last_time)
clockseq++;
/* save the state for next time */
write_state(clockseq, timestamp, node);
UNLOCK;
/* stuff fields into the UUID */
format_uuid_v1(uuid, clockseq, timestamp, node);
return 1;
}
/* format_uuid_v1 -- make a UUID from the timestamp, clockseq,
and node ID */
void format_uuid_v1(uuid_t* uuid, unsigned16 clock_seq,
uuid_time_t timestamp, uuid_node_t node)
{
/* Construct a version 1 uuid with the information we've gathered
plus a few constants. */
uuid->time_low = (unsigned long)(timestamp & 0xFFFFFFFF);
uuid->time_mid = (unsigned short)((timestamp >> 32) & 0xFFFF);
uuid->time_hi_and_version =
(unsigned short)((timestamp >> 48) & 0x0FFF);
uuid->time_hi_and_version |= (1 << 12);
uuid->clock_seq_low = clock_seq & 0xFF;
uuid->clock_seq_hi_and_reserved = (clock_seq & 0x3F00) >> 8;
uuid->clock_seq_hi_and_reserved |= 0x80;
memcpy(&uuid->node, &node, sizeof uuid->node);
}
/* data type for UUID generator persistent state */
typedef struct {
uuid_time_t ts; /* saved timestamp */
uuid_node_t node; /* saved node ID */
unsigned16 cs; /* saved clock sequence */
} uuid_state;
static uuid_state st;
/* read_state -- read UUID generator state from non-volatile store */
int read_state(unsigned16 *clockseq, uuid_time_t *timestamp,
uuid_node_t *node)
{
static int inited = 0;
FILE *fp;
/* only need to read state once per boot */
if (!inited) {
fp = fopen("state", "rb");
if (fp == NULL)
return 0;
fread(&st, sizeof st, 1, fp);
fclose(fp);
inited = 1;
}
*clockseq = st.cs;
*timestamp = st.ts;
*node = st.node;
return 1;
}
/* write_state -- save UUID generator state back to non-volatile
storage */
void write_state(unsigned16 clockseq, uuid_time_t timestamp,
uuid_node_t node)
{
static int inited = 0;
static uuid_time_t next_save;
FILE* fp;
if (!inited) {
next_save = timestamp;
inited = 1;
}
/* always save state to volatile shared state */
st.cs = clockseq;
st.ts = timestamp;
st.node = node;
if (timestamp >= next_save) {
fp = fopen("state", "wb");
fwrite(&st, sizeof st, 1, fp);
fclose(fp);
/* schedule next save for 10 seconds from now */
next_save = timestamp + (10 * 10 * 1000 * 1000);
}
}
/* get-current_time -- get time as 60-bit 100ns ticks since UUID epoch.
Compensate for the fact that real clock resolution is
less than 100ns. */
void get_current_time(uuid_time_t *timestamp)
{
static int inited = 0;
static uuid_time_t time_last;
static unsigned16 uuids_this_tick;
uuid_time_t time_now;
if (!inited) {
get_system_time(&time_now);
uuids_this_tick = UUIDS_PER_TICK;
inited = 1;
}
for ( ; ; ) {
get_system_time(&time_now);
/* if clock reading changed since last UUID generated, */
if (time_last != time_now) {
/* reset count of uuids gen'd with this clock reading */
uuids_this_tick = 0;
time_last = time_now;
break;
}
if (uuids_this_tick < UUIDS_PER_TICK) {
uuids_this_tick++;
break;
}
/* going too fast for our clock; spin */
}
/* add the count of uuids to low order bits of the clock reading */
*timestamp = time_now + uuids_this_tick;
}
/* true_random -- generate a crypto-quality random number.
**This sample doesn't do that.** */
static unsigned16 true_random(void)
{
static int inited = 0;
uuid_time_t time_now;
if (!inited) {
get_system_time(&time_now);
time_now = time_now / UUIDS_PER_TICK;
srand((unsigned int)
(((time_now >> 32) ^ time_now) & 0xffffffff));
inited = 1;
}
return rand();
}
/* uuid_create_md5_from_name -- create a version 3 (MD5) UUID using a
"name" from a "name space" */
void uuid_create_md5_from_name(uuid_t *uuid, uuid_t nsid, void *name,
int namelen)
{
MD5_CTX c;
unsigned char hash[16];
uuid_t net_nsid;
/* put name space ID in network byte order so it hashes the same
no matter what endian machine we're on */
net_nsid = nsid;
net_nsid.time_low = htonl(net_nsid.time_low);
net_nsid.time_mid = htons(net_nsid.time_mid);
net_nsid.time_hi_and_version = htons(net_nsid.time_hi_and_version);
MD5Init(&c);
MD5Update(&c, &net_nsid, sizeof net_nsid);
MD5Update(&c, name, namelen);
MD5Final(hash, &c);
/* the hash is in network byte order at this point */
format_uuid_v3or5(uuid, hash, 3);
}
void uuid_create_sha1_from_name(uuid_t *uuid, uuid_t nsid, void *name,
int namelen)
{
SHA_CTX c;
unsigned char hash[20];
uuid_t net_nsid;
/* put name space ID in network byte order so it hashes the same
no matter what endian machine we're on */
net_nsid = nsid;
net_nsid.time_low = htonl(net_nsid.time_low);
net_nsid.time_mid = htons(net_nsid.time_mid);
net_nsid.time_hi_and_version = htons(net_nsid.time_hi_and_version);
SHA1_Init(&c);
SHA1_Update(&c, &net_nsid, sizeof net_nsid);
SHA1_Update(&c, name, namelen);
SHA1_Final(hash, &c);
/* the hash is in network byte order at this point */
format_uuid_v3or5(uuid, hash, 5);
}
/* format_uuid_v3or5 -- make a UUID from a (pseudo)random 128-bit
number */
void format_uuid_v3or5(uuid_t *uuid, unsigned char hash[16], int v)
{
/* convert UUID to local byte order */
memcpy(uuid, hash, sizeof *uuid);
uuid->time_low = ntohl(uuid->time_low);
uuid->time_mid = ntohs(uuid->time_mid);
uuid->time_hi_and_version = ntohs(uuid->time_hi_and_version);
/* put in the variant and version bits */
uuid->time_hi_and_version &= 0x0FFF;
uuid->time_hi_and_version |= (v << 12);
uuid->clock_seq_hi_and_reserved &= 0x3F;
uuid->clock_seq_hi_and_reserved |= 0x80;
}
/* uuid_compare -- Compare two UUID's "lexically" and return */
#define CHECK(f1, f2) if (f1 != f2) return f1 < f2 ? -1 : 1;
int uuid_compare(uuid_t *u1, uuid_t *u2)
{
int i;
CHECK(u1->time_low, u2->time_low);
CHECK(u1->time_mid, u2->time_mid);
CHECK(u1->time_hi_and_version, u2->time_hi_and_version);
CHECK(u1->clock_seq_hi_and_reserved, u2->clock_seq_hi_and_reserved);
CHECK(u1->clock_seq_low, u2->clock_seq_low)
for (i = 0; i < 6; i++) {
if (u1->node[i] < u2->node[i])
return -1;
if (u1->node[i] > u2->node[i])
return 1;
}
return 0;
}
#undef CHECK