/*
 * gen_uuid.c --- generate a DCE-compatible uuid
 *
 * Copyright (C) 1996, 1997, 1998, 1999 Theodore Ts'o.
 *
 * %Begin-Header%
 * This file may be redistributed under the terms of the GNU 
 * Library General Public License.
 * %End-Header%
 */

/* 

   This is the real version of gen_uuid.c for SRB.  Because it based
   on code released under the GNU Library General Public License, we
   are distributing it separately from the rest of the SRB.  There is
   a dummy version distributed with the SRB that works, but only
   poorly.  So if you are interested in using SRB GUID/UUID
   functionality, you will need to replace the dummy
   src/lib/gen_uuid.c with this one, and then rebuild.


   This is a slightly modified version of Theodore Ts'o's code for
   inclusion with the SRB sources.  This is also based on a C++
   wrapper written by Tim Barrass for BaBar
   (http://www.slac.stanford.edu/BFROOT/dist/releases/14.1.0/BbrUUID/),
   converted back to C.  See comments noted with 'SRB'.

   On Linux (zuri), it successfully opens /dev/urandom, so the whole
   output UUID is random values, i.e., the preferred mode.

   On Solaris (miner), urandom and random are not available so it
   calls uuid_generate_time to use more time-based values.  In this
   case, it tries to get the IP address, but HAVE_NET_IF_H is not
   defined so it instead gets a random value (once).  If called in
   rapid succession (see the test main), the value will be only
   slightly different, but the microsecond clock is often different
   and if not, the the clock_seq logic seems to work fine preventing
   repeats.

   On AIX (tf004i), it operates like on Solaris, producing the less
   random, but adequate, value.

   On the OS X (my Mac), it works like it does on Linux, producing
   the most random values.m

   Wayne Schroeder, December 2003 */

/*
 * Force inclusion of SVID stuff since we need it if we're compiling in
 * gcc-wall wall mode
 */
#define _SVID_SOURCE


/* For SRB, I believe we can just define INT32 and UINT32 as below: */
typedef int	INT32;			/* >= 32 bits */
typedef unsigned int	UINT32;		/* >= 32 bits */


/*#ifdef HAVE_UNISTD_H */
#include <unistd.h>
/*#endif */
/*#ifdef HAVE_STDLIB_H */
#include <stdlib.h>
/*#endif */
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#ifdef HAVE_SYS_SOCKIO_H
#include <sys/sockio.h>
#endif
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif

/* Originally, this was in uuid.h, but for SRB it is all in one source
file so it seems simplier to paste and modify it here: */

struct uuid {
	UINT32	time_low;
	UINT32	time_mid;
        UINT32	time_hi_and_version;
	UINT32	clock_seq;
	UINT32	node;
};

/*
For SRB, I belive UINT32 is OK for all of these on the machines we support,
but this is the original:

struct uuid {
	__u32	time_low;
	__u16	time_mid;
	__u16	time_hi_and_version;
	__u16	clock_seq;
	__u8	node[6];
};
*/

typedef unsigned char uuid_t[16];

/* End of uuid.h code (#include "uuid.h") */



#ifdef HAVE_SRANDOM
#define srand(x) 	srandom(x)
#define rand() 		random()
#endif

/* uuid_pack and uuid_unpack moved into this source file for SRB */
void uuid_unpack(const uuid_t in, struct uuid *uu)
{
  	const  unsigned char *ptr = in;

	INT32		tmp;

	tmp = *ptr++;
	tmp = (tmp << 8) | *ptr++;
	tmp = (tmp << 8) | *ptr++;
	tmp = (tmp << 8) | *ptr++;
	uu->time_low = tmp;

	tmp = *ptr++;
	tmp = (tmp << 8) | *ptr++;
	uu->time_mid = tmp;
	
	tmp = *ptr++;
	tmp = (tmp << 8) | *ptr++;
	uu->time_hi_and_version = tmp;

	tmp = *ptr++;
	tmp = (tmp << 8) | *ptr++;
	uu->clock_seq = tmp;

	memcpy((unsigned char *)&uu->node, ptr, 6);
}

/* uuid_pack and uuid_unpack moved into this source file for SRB */
void uuid_pack(const struct uuid *uu, uuid_t ptr)
{
	UINT32	tmp;
	unsigned char	*out = ptr;

	tmp = uu->time_low;
	out[3] = (unsigned char) tmp;
	tmp >>= 8;
	out[2] = (unsigned char) tmp;
	tmp >>= 8;
	out[1] = (unsigned char) tmp;
	tmp >>= 8;
	out[0] = (unsigned char) tmp;
	
	tmp = uu->time_mid;
	out[5] = (unsigned char) tmp;
	tmp >>= 8;
	out[4] = (unsigned char) tmp;

	tmp = uu->time_hi_and_version;
	out[7] = (unsigned char) tmp;
	tmp >>= 8;
	out[6] = (unsigned char) tmp;

	tmp = uu->clock_seq;
	out[9] = (unsigned char) tmp;
	tmp >>= 8;
	out[8] = (unsigned char) tmp;

	/* changed from "memcpy(out+10, uu->node, 6);" for SRB: */
	memcpy(out+10, (unsigned char *)&uu->node, 6);
}


static int get_random_fd(void)
{
	struct timeval	tv;
	static int	fd = -2;                  
	int		i;

	if (fd == -2) {
		gettimeofday(&tv, 0);
		fd = open("/dev/urandom", O_RDONLY);
		if (fd == -1)
			fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
		srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
	}
	/* Crank the random number generator a few times */
	gettimeofday(&tv, 0);
	for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
		rand();
	return fd;
}


/*
 * Generate a series of random bytes.  Use /dev/urandom if possible,
 * and if not, use srandom/random.
 */
static void get_random_bytes(void *buf, int nbytes)
{
	int i, fd = get_random_fd();
	int lose_counter = 0;
	char *cp = (char *) buf;

	if (fd >= 0) {
		while (nbytes > 0) {
			i = read(fd, cp, nbytes);
			if (i <= 0) {
				if (lose_counter++ > 16)
					break;
				continue;
			}
			nbytes -= i;
			cp += i;
			lose_counter = 0;
		}
	}

	/* XXX put something better here if no /dev/random! */
	for (i = 0; i < nbytes; i++)
		*cp++ = rand() & 0xFF;
	return;
}

/*
 * Get the ethernet hardware address, if we can find it...
 */
static int get_node_id(unsigned char *node_id)
{
#ifdef HAVE_NET_IF_H
	int 		sd;
	struct ifreq 	ifr, *ifrp;
	struct ifconf 	ifc;
	char buf[1024];
	int		n, i;
	unsigned char 	*a;
	
/*
 * BSD 4.4 defines the size of an ifreq to be
 * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
 * However, under earlier systems, sa_len isn't present, so the size is 
 * just sizeof(struct ifreq)
 */
#ifdef HAVE_SA_LEN
#ifndef max
#define max(a,b) ((a) > (b) ? (a) : (b))
#endif
#define ifreq_size(i) max(sizeof(struct ifreq),\
     sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
#else
#define ifreq_size(i) sizeof(struct ifreq)
#endif /* HAVE_SA_LEN*/

	sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
	if (sd < 0) {
		return -1;
	}
	memset(buf, 0, sizeof(buf));
	ifc.ifc_len = sizeof(buf);
	ifc.ifc_buf = buf;
	if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0) {
		close(sd);
		return -1;
	}
	n = ifc.ifc_len;
	for (i = 0; i < n; i+= ifreq_size(*ifr) ) {
		ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i);
		strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ);
#ifdef SIOCGIFHWADDR
		if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0)
			continue;
		a = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
#else
#ifdef SIOCGENADDR
		if (ioctl(sd, SIOCGENADDR, &ifr) < 0)
			continue;
		a = (unsigned char *) ifr.ifr_enaddr;
#else
		/*
		 * XXX we don't have a way of getting the hardware
		 * address
		 */
		close(sd);
		return 0;
#endif /* SIOCGENADDR */
#endif /* SIOCGIFHWADDR */
		if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5])
			continue;
		if (node_id) {
			memcpy(node_id, a, 6);
			close(sd);
			return 1;
		}
	}
	close(sd);
#endif
	return 0;
}

/* Assume that the gettimeofday() has microsecond granularity */
#define MAX_ADJUSTMENT 10

/* SRB change.  Original was static int get_clock(UINT32 *clock_high,
   UINT32 *clock_low, __u16 *ret_clock_seq) but I believe 32 bits is
   OK for ret_clock_seq too */
static int get_clock(UINT32 *clock_high, UINT32 *clock_low, UINT32 *ret_clock_seq)
{
	static int			adjustment = 0;
	static struct timeval		last = {0, 0};

/* Original: "static __u16 clock_seq;", but for SRB: */
	static UINT32			clock_seq;

	struct timeval 			tv;
	unsigned long long		clock_reg;
	
try_again:
	gettimeofday(&tv, 0);
	if ((last.tv_sec == 0) && (last.tv_usec == 0)) {
		get_random_bytes(&clock_seq, sizeof(clock_seq));
		clock_seq &= 0x1FFF;
		last = tv;
		last.tv_sec--;
	}
	if ((tv.tv_sec < last.tv_sec) ||
	    ((tv.tv_sec == last.tv_sec) &&
	     (tv.tv_usec < last.tv_usec))) {
		clock_seq = (clock_seq+1) & 0x1FFF;
		adjustment = 0;
		last = tv;
	} else if ((tv.tv_sec == last.tv_sec) &&
	    (tv.tv_usec == last.tv_usec)) {
		if (adjustment >= MAX_ADJUSTMENT)
			goto try_again;
		adjustment++;
	} else {
		adjustment = 0;
		last = tv;
	}
		
	clock_reg = tv.tv_usec*10 + adjustment;
	clock_reg += ((unsigned long long) tv.tv_sec)*10000000;
	clock_reg += (((unsigned long long) 0x01B21DD2) << 32) + 0x13814000;

	*clock_high = clock_reg >> 32;
	*clock_low = clock_reg;
	*ret_clock_seq = clock_seq;
	return 0;
}


void uuid_generate_time(uuid_t out)
{
	static unsigned char node_id[6];
	static int has_init = 0;
	struct uuid uu;
	UINT32	clock_mid;

	if (!has_init) {
		if (get_node_id(node_id) <= 0) {
			get_random_bytes(node_id, 6);
			/*
			 * Set multicast bit, to prevent conflicts
			 * with IEEE 802 addresses obtained from
			 * network cards
			 */
			node_id[0] |= 0x80;
		}
		has_init = 1;
	}
	get_clock(&clock_mid, &uu.time_low, &uu.clock_seq);
	uu.clock_seq |= 0x8000;

	/* Original was "uu.time_mid = (__u16) clock_mid;", for SRB: */
	uu.time_mid = clock_mid;

	uu.time_hi_and_version = (clock_mid >> 16) | 0x1000;

	/* Original was "memcpy(uu.node, node_id, 6);", for SRB: */
	memcpy((char *)&uu.node, node_id, 6);
	uuid_pack(&uu, out);
}

void uuid_generate_random(uuid_t out)
{
	uuid_t	buf;
	struct uuid uu;

	get_random_bytes(buf, sizeof(buf));
	uuid_unpack(buf, &uu);

	uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000;
	uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) | 0x4000;
	uuid_pack(&uu, out);
}

/*
 * This is the generic front-end to uuid_generate_random and
 * uuid_generate_time.  It uses uuid_generate_random only if
 * /dev/urandom is available, since otherwise we won't have
 * high-quality randomness.
 */
void uuid_generate(uuid_t out)
{
        get_random_fd();
	if (get_random_fd() >= 0)
	  uuid_generate_random(out);
	else
	  uuid_generate_time(out);
}

/* The rest is code added for SRB: */

#define MDAS_SUCCESS  0
/* Get a UUID and print it as a hex string */
int getGuidBySRB(char *result)
{
     uuid_t out;
     uuid_generate(out);
     sprintf(result, "%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x",
        out[0], out[1], out[2], out[3], out[4], out[5], out[6], out[7],
      out[8], out[9], out[10], out[11], out[12], out[13], out[14],out[15]);
     return(MDAS_SUCCESS);
}


/* A main that can be used for testing, normally defined out */
#if 0
main()
{
  char result1[40];
  char result2[40];
  char result3[40];
  char result4[40];
  getGuidBySRB(result1);
  getGuidBySRB(result2);
  getGuidBySRB(result3);
  printf("uuid=%s\n",result1);
  printf("uuid=%s\n",result2);
  printf("uuid=%s\n",result3);
  sleep(1);
  getGuidBySRB(result4);
  printf("uuid=%s\n",result4);
}
#endif
