#
# Patch given file with DATA hunks and print to STDOUT.
#
# This patch:
#   - Fixes missing ifreq.ifr_hwaddr on at least some versions of
#       Solaris.
#       osname=solaris, osvers=2.11, archname=i86pc-solaris-64
#       uname='sunos ouzel 5.11 omnios-r151034-0d278a0cc5 i86pc i386 i86pc '
#   - Explicitly casts some places for g++.
#   - Include Windows.h if exists, and stdint.h and typedef mode_t.
#   - Include gettimeofday() if Windows.h exists.
#   - Add cast to silence warning.
#   - Define getpid() for native Win32.
#   - Includes for native Win32.
#   - Stops reseeding RNG after first pass on Win32.
#   - Adds comments re thread safety.
#   - Add srand()/rand() replacement.
#   - Roll adjustment overflow to clock_seq.
#   - Make get_clock() thread locals into global statics.
#   - Handle missing Win32 getuid().
#   - Use lstat to catch symlink race.
#   - Make state file path configurable.
#   - Fixup includes.
#   - Add note on random number generation.
#   - Count random bytes read so far.
#   - Return without calling rand() on complete /dev/urandom read.
#   - Add editor modeline.
#   - Move to uuconfig.h.
#   - Add method to set forced random mac.
#   - Add method to force random MAC on each request.
#   - Remove jrand stuff.
#
use strict;
use warnings;
use Text::Patch 'patch';

my $in = $ARGV[0];

my ($txt, $patch);
{
    local $/;
    open my $fh, '<', $in or die "open: $in: $!";
    $txt   = <$fh>;
    $patch = <DATA>;
}

#print $txt,"\n";
#exit;

my $out = patch( $txt, $patch, STYLE => 'Unified' );

print $out;
exit 0;

__END__
--- usrcP/uuid/gen_uuid.c	Sun Jan 14 20:07:26 2024
+++ ulib/uuid/gen_uuid.c	Wed Jan 17 17:37:54 2024
@@ -39,66 +39,29 @@
 #define _SVID_SOURCE
 #define _DEFAULT_SOURCE	  /* since glibc 2.20 _SVID_SOURCE is deprecated */
 
-#include "config.h"
+#include "uuconfig.h"
+#include "uuid/uuidP.h"
+#include "uuid/uuidd.h"
+#include "uuid/uuid_types.h"
 
-#include <stdio.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
+/*
+  #if defined(__linux__) && defined(HAVE_SYS_SYSCALL_H)
+  #include <sys/syscall.h>
+  #endif
+*/
+
+#ifdef USE_WIN32_NATIVE
+#define getpid() _getpid()
+#define ftruncate(a,b) _chsize(a,b)
+typedef myuint32_t mode_t;
 #endif
-#ifdef HAVE_STDLIB_H
-#include <stdlib.h>
-#endif
-#include <string.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <sys/types.h>
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-#ifdef HAVE_SYS_WAIT_H
-#include <sys/wait.h>
-#endif
-#include <sys/stat.h>
-#ifdef HAVE_SYS_FILE_H
-#include <sys/file.h>
-#endif
-#ifdef HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-#ifdef HAVE_SYS_RANDOM_H
-#include <sys/random.h>
-#endif
-#ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
-#ifdef HAVE_SYS_UN_H
-#include <sys/un.h>
-#endif
-#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
-#ifdef HAVE_NET_IF_DL_H
-#include <net/if_dl.h>
-#endif
-#if defined(__linux__) && defined(HAVE_SYS_SYSCALL_H)
-#include <sys/syscall.h>
-#endif
-#ifdef HAVE_SYS_RESOURCE_H
-#include <sys/resource.h>
-#endif
 
-#include "uuidP.h"
-#include "uuidd.h"
-
 #ifdef HAVE_SRANDOM
 #define srand(x) 	srandom(x)
 #define rand() 		random()
+#else
+#define srand(x) xo_srand()
+#define rand()   xo_rand()
 #endif
 
 #ifdef TLS
@@ -108,13 +71,97 @@
 #endif
 
 #if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48)
-#define DO_JRAND_MIX
+#define DONT_DO_JRAND_MIX
 THREAD_LOCAL unsigned short jrand_seed[3];
 #endif
 
-static int myget_random_fd(void)
+#ifdef USE_WIN32_NATIVE
+int gettimeofday(struct timeval * tp, struct timezone * tzp)
 {
+  // Note: SYSTEMTIME has 1ms resolution.
+  //       FILETIME has 100nsec resolution.
+  //       struct timeval has 1usec resolution.
+  //       struct timespec has 1nsec resolution.
+  //
+  // Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's
+  // This magic number is the number of 100 nanosecond intervals since January 1, 1601 (UTC)
+  // until 00:00:00 January 1, 1970
+  //
+  static const myuint64_t EPOCH = ((myuint64_t) 116444736000000000ULL);
+
+  //SYSTEMTIME  system_time;
+  FILETIME    file_time;
+  myuint64_t  time;
+
+  //GetSystemTime( &system_time );
+  //SystemTimeToFileTime( &system_time, &file_time );
+  GetSystemTimeAsFileTime( &file_time );
+  time =  ((myuint64_t)file_time.dwLowDateTime )      ;
+  time += ((myuint64_t)file_time.dwHighDateTime) << 32;
+
+  tp->tv_sec  = (long) ((time - EPOCH) / 10000000L);
+  //tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
+  tp->tv_usec = (long) (((time - EPOCH) % 10000000L) / 10);
+  return 0;
+}
+#endif
+
+/* based on xoshiro256++
+ * https://prng.di.unimi.it/xoshiro256plusplus.c
+*/
+static myuint64_t xo_s[4];
+static inline uint64_t xo_rotl(const uint64_t x, int k) {
+  return (x << k) | (x >> (64 - k));
+}
+myuint64_t xo_rand(void) {
+  const myuint64_t result = xo_rotl(xo_s[0] + xo_s[3], 23) + xo_s[0];
+  const myuint64_t t = xo_s[1] << 17;
+
+  xo_s[2] ^= xo_s[0];
+  xo_s[3] ^= xo_s[1];
+  xo_s[1] ^= xo_s[2];
+  xo_s[0] ^= xo_s[3];
+
+  xo_s[2] ^= t;
+
+  xo_s[3] = xo_rotl(xo_s[3], 45);
+
+  return result;
+}
+
+/* based on splitmix64
+ * https://xorshift.di.unimi.it/splitmix64.c
+*/
+void xo_srand(void)
+{
+  struct timeval  tv;
+  myuint64_t      x, y;
+  myuint32_t      seed;
+  myint32_t       i, j;
+
+  gettimeofday(&tv, NULL);
+  seed = ~tv.tv_sec ^ ~tv.tv_usec ^ ~getpid();
+  x    = (myuint64_t)seed << 32 ^ ~seed;
+
+  for (i=0; i<4; ++i) {
+    gettimeofday(&tv, NULL);
+    for (j = (tv.tv_sec ^ tv.tv_usec) & 0x7F ; j>=0 ; --j)
+      y = (x += 0x9e3779b97f4a7c15);
+      y = (y ^ (y >> 30)) * 0xbf58476d1ce4e5b9;
+      y = (y ^ (y >> 27)) * 0x94d049bb133111eb;
+      xo_s[i] = y ^ (y >> 31);
+    }
+
+  gettimeofday(&tv, NULL);
+  for (i = (tv.tv_sec ^ tv.tv_usec) & 0x7F ; i>=0 ; --i)
+    x = xo_rand();
+}
+
+static int myget_random_fd()
+{
 	struct timeval	tv;
+  /* This static is problematic if
+	   unserialized access permitted. (JRM) */
 	static int	fd = -2;
 	int		i;
 
@@ -129,18 +176,26 @@
 			if (i >= 0)
 				fcntl(fd, F_SETFD, i | FD_CLOEXEC);
 		}
-#endif
 		srand(((unsigned)getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
+#else
+		fd = -1;
+		/* No easy substitute for getuid(). (JRM) */
+		/* Note: PerlProc_getuid() always returns 0 on Win32. (JRM) */
+		srand(((unsigned)getpid() << 16) ^ 0xdeadbeef ^ tv.tv_sec ^ tv.tv_usec);
+#endif
+    xo_srand();
 #ifdef DO_JRAND_MIX
 		jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF);
 		jrand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF);
 		jrand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16;
 #endif
 	}
+
 	/* Crank the random number generator a few times */
 	gettimeofday(&tv, 0);
 	for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
-		rand();
+		xo_rand();
+
 	return fd;
 }
 
@@ -148,58 +203,88 @@
 /*
  * Generate a series of random bytes.  Use /dev/urandom if possible,
  * and if not, use srandom/random.
+ *
+ * (JRM)
+ * https://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/
+ *
+ * If all else fails, use xoshiro256++. (JRM)
  */
 static void myget_random_bytes(void *buf, int nbytes)
 {
-	int i, n = nbytes, fd;
-	int lose_counter = 0;
-	unsigned char *cp = buf;
+  myuint64_t r;
+  int i = 0, n = nbytes, fd, rv;
+  int lose_counter = 0;
+  unsigned char *cp = (unsigned char*)buf;
 
 #ifdef HAVE_GETRANDOM
-	i = getrandom(buf, nbytes, 0);
-	if (i == nbytes)
-		return;
+  i = getrandom(buf, nbytes, 0);
+  if (i == nbytes)
+    return;
+  n -= i;
 #endif
 #ifdef HAVE_GETENTROPY
-	if (getentropy(buf, nbytes) == 0)
-		return;
+  if (getentropy(buf, nbytes) == 0)
+    return;
 #endif
 
-	fd = myget_random_fd();
-	if (fd >= 0) {
-		while (n > 0) {
-			i = read(fd, cp, n);
-			if (i <= 0) {
-				if (lose_counter++ > 16)
-					break;
-				continue;
-			}
-			n -= i;
-			cp += i;
-			lose_counter = 0;
-		}
-	}
+  if ((fd = myget_random_fd()) >= 0) {
+    while (n > 0) {
+      rv = read(fd, cp, n);
+      if (rv <= 0) {
+        if (lose_counter++ > 16)
+          break;
+        continue;
+      }
+      i += rv;
+      n -= rv;
+      cp += rv;
+      lose_counter = 0;
+    }
+    if (i == nbytes)
+      return;
+  }
 
-	/*
-	 * We do this all the time, but this is the only source of
-	 * randomness if /dev/random/urandom is out to lunch.
-	 */
-	for (cp = buf, i = 0; i < nbytes; i++)
-		*cp++ ^= (rand() >> 7) & 0xFF;
+  /*
+   * We do this all the time, but this is the only source of
+   * randomness if /dev/random/urandom is out to lunch.
+   */
+  for (cp = (unsigned char*)buf ; i <= nbytes-8 ; i+=8) {
+    r = xo_rand();
+    *cp++ ^= r       & 0xFF;
+    *cp++ ^= r >> 8  & 0xFF;
+    *cp++ ^= r >> 16 & 0xFF;
+    *cp++ ^= r >> 24 & 0xFF;
+    *cp++ ^= r >> 32 & 0xFF;
+    *cp++ ^= r >> 40 & 0xFF;
+    *cp++ ^= r >> 48 & 0xFF;
+    *cp++ ^= r >> 56 & 0xFF;
+  }
+  for ( ; i < nbytes ; i++) {
+    r = xo_rand();
+    *cp++ ^= r       & 0xFF; if (++i >= nbytes) break;
+    *cp++ ^= r >> 8  & 0xFF; if (++i >= nbytes) break;
+    *cp++ ^= r >> 16 & 0xFF; if (++i >= nbytes) break;
+    *cp++ ^= r >> 24 & 0xFF; if (++i >= nbytes) break;
+    *cp++ ^= r >> 32 & 0xFF; if (++i >= nbytes) break;
+    *cp++ ^= r >> 40 & 0xFF; if (++i >= nbytes) break;
+    *cp++ ^= r >> 48 & 0xFF; if (++i >= nbytes) break;
+    *cp++ ^= r >> 56 & 0xFF; if (++i >= nbytes) break;
+  }
+
 #ifdef DO_JRAND_MIX
-	{
-		unsigned short tmp_seed[3];
+  {
+    unsigned short tmp_seed[3];
 
-		memcpy(tmp_seed, jrand_seed, sizeof(tmp_seed));
-		jrand_seed[2] = jrand_seed[2] ^ syscall(__NR_gettid);
-		for (cp = buf, i = 0; i < nbytes; i++)
-			*cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF;
-		memcpy(jrand_seed, tmp_seed,
-		       sizeof(jrand_seed) - sizeof(unsigned short));
-	}
+    memcpy(tmp_seed, jrand_seed, sizeof(tmp_seed));
+    jrand_seed[2] = jrand_seed[2] ^ syscall(__NR_gettid);
+    for (cp = (unsigned char*)buf, i = 0; i < nbytes; i++)
+      *cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF;
+    memcpy(jrand_seed, tmp_seed,
+           sizeof(jrand_seed) - sizeof(unsigned short));
+  }
 #endif
 
-	return;
+  return;
 }
 
 /*
@@ -254,11 +339,17 @@
 	for (i = 0; i < n; i+= ifreq_size(*ifrp) ) {
 		ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i);
 		strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ);
-#ifdef SIOCGIFHWADDR
+#if defined(SIOCGIFHWADDR) && ( defined(ifr_hwaddr) || defined(ifr_addr) )
 		if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0)
 			continue;
+#ifdef ifr_hwaddr
 		a = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
 #else
+#ifdef ifr_addr
+		a = (unsigned char *) &ifr.ifr_addr.sa_data;
+#endif /* ifr_addr */
+#endif /* ifr_hwaddr */
+#else
 #ifdef SIOCGENADDR
 		if (ioctl(sd, SIOCGENADDR, &ifr) < 0)
 			continue;
@@ -292,37 +383,61 @@
 	return 0;
 }
 
+static const char *statepath = NULL;
+
+void myuuid_init_statepath(const char *path)
+{
+  statepath = path;
+}
+
 /* Assume that the gettimeofday() has microsecond granularity */
 #define MAX_ADJUSTMENT 10
 
 static int myget_clock(myuint32_t *clock_high, myuint32_t *clock_low,
 		     myuint16_t *ret_clock_seq, int *num)
 {
-	THREAD_LOCAL int		adjustment = 0;
-	THREAD_LOCAL struct timeval	last = {0, 0};
-	THREAD_LOCAL int		state_fd = -2;
-	THREAD_LOCAL FILE		*state_f;
-	THREAD_LOCAL myuint16_t		clock_seq;
+  /* Abandoned THREAD_LOCAL on all of these. It caused threads to
+     appear to be independent generators. We are serialized at
+     library entrance anyway. (JRM) */
+	static int				adjustment = 0;
+	static struct timeval	last = {0, 0};
+	static int				state_fd = -2;
+	static FILE				*state_f;
+	static myuint16_t		clock_seq;
 	struct timeval 			tv;
 #ifndef _WIN32
 	struct flock			fl;
 #endif
-	myuint64_t			clock_reg;
-	mode_t				save_umask;
-	int				len;
+	struct stat				statbuf;
+	myuint64_t				clock_reg;
+	mode_t					save_umask;
+	int						len;
 
 	if (state_fd == -2) {
-		save_umask = umask(0);
-		state_fd = open("/var/lib/libuuid/clock.txt",
-				O_RDWR|O_CREAT, 0660);
-		(void) umask(save_umask);
-		if (state_fd >= 0) {
-			state_f = fdopen(state_fd, "r+");
-			if (!state_f) {
-				close(state_fd);
-				state_fd = -1;
+#ifdef HAVE_LSTAT
+		if ((lstat(statepath, &statbuf) == 0)
+			&& ((statbuf.st_mode & S_IFMT) == S_IFLNK))
+			state_fd = -1;
+		else {
+#endif
+			save_umask = umask(0);
+			state_fd = open(statepath, O_RDWR|O_CREAT, 0660);
+			(void) umask(save_umask);
+			if (state_fd >= 0) {
+#ifdef HAVE_LSTAT
+				state_f = NULL;
+				if ((lstat(statepath, &statbuf) == 0)
+					&& ((statbuf.st_mode & S_IFMT) != S_IFLNK))
+#endif
+					state_f = fdopen(state_fd, "r+");
+				if (!state_f) {
+					close(state_fd);
+					state_fd = -1;
+				}
 			}
+#ifdef HAVE_LSTAT
 		}
+#endif
 	}
 #ifndef _WIN32
 	fl.l_type = F_WRLCK;
@@ -340,6 +455,9 @@
 			break;
 		}
 	}
+#else
+  if (state_fd >= 0)
+    rewind(state_f);
 #endif
 	if (state_fd >= 0) {
 		unsigned int cl;
@@ -362,23 +480,27 @@
 		last.tv_sec--;
 	}
 
-try_again:
-	gettimeofday(&tv, 0);
-	if ((tv.tv_sec < last.tv_sec) ||
-	    ((tv.tv_sec == last.tv_sec) &&
-	     (tv.tv_usec < last.tv_usec))) {
-		clock_seq = (clock_seq+1) & 0x3FFF;
-		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;
-	}
+  /* Now rolling adjustment overflows into clock_seq
+     instead of trying again. (JRM) */
+  gettimeofday(&tv, 0);
+  if ((tv.tv_sec < last.tv_sec) || ((tv.tv_sec == last.tv_sec) && (tv.tv_usec < last.tv_usec))) {
+    clock_seq = (clock_seq+1) & 0x3FFF;
+    adjustment = 0;
+    last = tv;
+  }
+  else if ((tv.tv_sec == last.tv_sec) && (tv.tv_usec == last.tv_usec)) {
+    if (adjustment >= MAX_ADJUSTMENT) {
+      clock_seq = (clock_seq+1) & 0x3FFF;
+      adjustment = 0;
+    }
+    else {
+      adjustment++;
+    }
+  }
+  else {
+    adjustment = 0;
+    last = tv;
+  }
 
 	clock_reg = tv.tv_usec*10 + adjustment;
 	clock_reg += ((myuint64_t) tv.tv_sec)*10000000;
@@ -413,8 +535,9 @@
 #endif
 	}
 
+
 	*clock_high = clock_reg >> 32;
-	*clock_low = clock_reg;
+	*clock_low = (myuint32_t)clock_reg;
 	*ret_clock_seq = clock_seq;
 	return 0;
 }
@@ -494,6 +617,8 @@
 	struct sockaddr_un srv_addr;
 	struct stat st;
 	pid_t pid;
+	/* These statics are not audited since
+	   we dont build uuidd anyway. (JRM) */
 	static const char *uuidd_path = UUIDD_PATH;
 	static int access_ret = -2;
 	static int start_attempts = 0;
@@ -564,15 +689,65 @@
 #pragma GCC diagnostic pop
 #endif
 
+int myuuid_node_or_fail(unsigned char *dest) {
+	static int has_init = 0;
+  static int has_fail = 0;
+	static unsigned char node_id[6];
+
+	if (!has_init) {
+		if (myget_node_id(node_id) <= 0)
+      has_fail = 1;
+		has_init = 1;
+	}
+  if (has_fail) return 0;
+  memcpy(dest, node_id, 6);
+  return 1;
+}
+
+void myuuid_node_or_rand(unsigned char *dest) {
+	static int has_init = 0;
+	static unsigned char node_id[6];
+
+	if (!has_init) {
+		if (myget_node_id(node_id) <= 0) {
+			myget_random_bytes(node_id, 6);
+			/*
+			 * Set multicast bit, to prevent conflicts
+			 * with IEEE 802 addresses obtained from
+			 * network cards
+			 */
+			node_id[0] |= 0x01;
+		}
+		has_init = 1;
+	}
+	memcpy(dest, node_id, 6);
+}
+
+static int hide_mac    = 0;
+static int hide_always = 0;
+
+void myuuid_hide_mac(void) {
+  hide_mac = 1;
+}
+
+void myuuid_hide_always(void) {
+  hide_mac    = 1;
+  hide_always = 1;
+}
+
 void myuuid__generate_time(myuuid_t out, int *num)
 {
+	/* Another race. Worst case, if node_id cannot be found, threads may
+	   have different initial random node data. Once all race participants
+	   complete the course, all should be same. Only relevant in an
+	   initial thundering herd scenario. (JRM) */
 	static unsigned char node_id[6];
 	static int has_init = 0;
 	struct myuuid uu;
 	myuint32_t	clock_mid;
 
-	if (!has_init) {
-		if (myget_node_id(node_id) <= 0) {
+	if (!has_init || hide_always) {
+		if (hide_mac == 1 || myget_node_id(node_id) <= 0) {
 			myget_random_bytes(node_id, 6);
 			/*
 			 * Set multicast bit, to prevent conflicts
@@ -593,6 +768,7 @@
 
 void myuuid_generate_time(myuuid_t out)
 {
+#ifdef USE_UUIDD
 #ifdef TLS
 	THREAD_LOCAL int		num = 0;
 	THREAD_LOCAL struct myuuid	uu;
@@ -629,7 +805,8 @@
 #else
 	if (myget_uuid_via_daemon(UUIDD_OP_TIME_UUID, out, 0) == 0)
 		return;
-#endif
+#endif /* TLS */
+#endif /* USE_UUIDD */
 
 	myuuid__generate_time(out, 0);
 }
@@ -680,3 +857,4 @@
 	else
 		myuuid_generate_time(out);
 }
+/* ex:set sw=2 ts=2 itab=spaces: */

