1#include "EXTERN.h"
2#include "perl.h"
3#include "XSUB.h"
4#include "UUID.h"
5
6static  perl_uuid_t NameSpace_DNS = { /* 6ba7b810-9dad-11d1-80b4-00c04fd430c8 */
7   0x6ba7b810,
8   0x9dad,
9   0x11d1,
10   0x80, 0xb4, { 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }
11};
12
13static  perl_uuid_t NameSpace_URL = { /* 6ba7b811-9dad-11d1-80b4-00c04fd430c8 */
14   0x6ba7b811,
15   0x9dad,
16   0x11d1,
17   0x80, 0xb4, { 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }
18};
19
20static  perl_uuid_t NameSpace_OID = { /* 6ba7b812-9dad-11d1-80b4-00c04fd430c8 */
21   0x6ba7b812,
22   0x9dad,
23   0x11d1,
24   0x80, 0xb4, { 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }
25};
26
27perl_uuid_t NameSpace_X500 = { /* 6ba7b814-9dad-11d1-80b4-00c04fd430c8 */
28   0x6ba7b814,
29   0x9dad,
30   0x11d1,
31   0x80, 0xb4, { 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }
32};
33
34static void format_uuid_v1(
35   perl_uuid_t     *uuid,
36   unsigned16  clock_seq,
37   perl_uuid_time_t timestamp,
38   uuid_node_t node
39) {
40   uuid->time_low = (unsigned long)(timestamp & 0xFFFFFFFF);
41   uuid->time_mid = (unsigned short)((timestamp >> 32) & 0xFFFF);
42   uuid->time_hi_and_version = (unsigned short)((timestamp >> 48) &
43      0x0FFF);
44
45   uuid->time_hi_and_version |= (1 << 12);
46   uuid->clock_seq_low = clock_seq & 0xFF;
47   uuid->clock_seq_hi_and_reserved = (clock_seq & 0x3F00) >> 8;
48   uuid->clock_seq_hi_and_reserved |= 0x80;
49   memcpy(&uuid->node, &node, sizeof uuid->node);
50}
51
52static void get_current_time(perl_uuid_time_t * timestamp) {
53   perl_uuid_time_t        time_now;
54   static perl_uuid_time_t time_last;
55   static unsigned16  uuids_this_tick;
56   static int         inited = 0;
57
58   if (!inited) {
59      get_system_time(&time_last);
60      uuids_this_tick = UUIDS_PER_TICK;
61      inited = 1;
62   };
63   while (1) {
64      get_system_time(&time_now);
65
66      if (time_last != time_now) {
67         uuids_this_tick = 0;
68         time_last = time_now;
69         break;
70      };
71      if (uuids_this_tick < UUIDS_PER_TICK) {
72         uuids_this_tick++;
73         break;
74      };
75   };
76   *timestamp = time_now + uuids_this_tick;
77}
78
79static unsigned16 true_random(void) {
80   static int  inited = 0;
81   perl_uuid_time_t time_now;
82
83   if (!inited) {
84      get_system_time(&time_now);
85      time_now = time_now/UUIDS_PER_TICK;
86      srand((unsigned int)(((time_now >> 32) ^ time_now)&0xffffffff));
87      inited = 1;
88    };
89    return (rand());
90}
91
92static void format_uuid_v3(
93   perl_uuid_t        *uuid,
94   unsigned char  hash[16]
95) {
96   memcpy(uuid, hash, sizeof(perl_uuid_t));
97
98   uuid->time_low            = ntohl(uuid->time_low);
99   uuid->time_mid            = ntohs(uuid->time_mid);
100   uuid->time_hi_and_version = ntohs(uuid->time_hi_and_version);
101
102   uuid->time_hi_and_version &= 0x0FFF;
103   uuid->time_hi_and_version |= (3 << 12);
104   uuid->clock_seq_hi_and_reserved &= 0x3F;
105   uuid->clock_seq_hi_and_reserved |= 0x80;
106}
107
108static void get_system_time(perl_uuid_time_t *perl_uuid_time) {
109#if defined __cygwin__ || defined __MINGW32__ || defined WIN32
110   /* ULARGE_INTEGER time; */
111   LARGE_INTEGER time;
112
113   /* use QeryPerformanceCounter for +ms resolution - as per Paul Stodghill
114   GetSystemTimeAsFileTime((FILETIME *)&time); */
115   QueryPerformanceCounter(&time);
116   time.QuadPart +=
117      (unsigned __int64) (1000*1000*10) *
118      (unsigned __int64) (60 * 60 * 24) *
119      (unsigned __int64) (17+30+31+365*18+5);
120
121   *perl_uuid_time = time.QuadPart;
122#else
123   struct timeval tp;
124
125   gettimeofday(&tp, (struct timezone *)0);
126   *perl_uuid_time = (tp.tv_sec * I64(10000000)) + (tp.tv_usec * I64(10)) +
127      I64(0x01B21DD213814000);
128#endif
129}
130
131static void get_random_info(unsigned char seed[16]) {
132   MD5_CTX c;
133#if defined __cygwin__ || defined __MINGW32__ || defined __MSWin32__
134   typedef struct {
135      MEMORYSTATUS  m;
136      SYSTEM_INFO   s;
137      FILETIME      t;
138      LARGE_INTEGER pc;
139      DWORD         tc;
140      DWORD         l;
141      char          hostname[MAX_COMPUTERNAME_LENGTH + 1];
142   } randomness;
143#else
144   typedef struct {
145      long           hostid;
146      struct timeval t;
147      char           hostname[257];
148   } randomness;
149#endif
150   randomness r;
151
152   MD5Init(&c);
153
154#if defined __cygwin__ || defined __MINGW32__ || defined __MSWin32__
155   GlobalMemoryStatus(&r.m);
156   GetSystemInfo(&r.s);
157   GetSystemTimeAsFileTime(&r.t);
158   QueryPerformanceCounter(&r.pc);
159   r.tc = GetTickCount();
160   r.l = MAX_COMPUTERNAME_LENGTH + 1;
161   GetComputerName(r.hostname, &r.l );
162#else
163   r.hostid = gethostid();
164   gettimeofday(&r.t, (struct timezone *)0);
165   gethostname(r.hostname, 256);
166#endif
167
168   MD5Update(&c, (unsigned char*)&r, sizeof(randomness));
169   MD5Final(seed, &c);
170}
171
172SV* make_ret(const perl_uuid_t u, int type) {
173   char           buf[BUFSIZ];
174   unsigned char *from, *to;
175   STRLEN         len;
176   int            i;
177
178   memset(buf, 0x00, BUFSIZ);
179   switch(type) {
180   case F_BIN:
181      memcpy(buf, (void*)&u, sizeof(perl_uuid_t));
182      len = sizeof(perl_uuid_t);
183      break;
184   case F_STR:
185      sprintf(buf, "%8.8X-%4.4X-%4.4X-%2.2X%2.2X-", (unsigned int)u.time_low, u.time_mid,
186	 u.time_hi_and_version, u.clock_seq_hi_and_reserved, u.clock_seq_low);
187      for(i = 0; i < 6; i++ )
188	 sprintf(buf+strlen(buf), "%2.2X", u.node[i]);
189      len = strlen(buf);
190      break;
191   case F_HEX:
192      sprintf(buf, "0x%8.8X%4.4X%4.4X%2.2X%2.2X", (unsigned int)u.time_low, u.time_mid,
193	 u.time_hi_and_version, u.clock_seq_hi_and_reserved, u.clock_seq_low);
194      for(i = 0; i < 6; i++ )
195	 sprintf(buf+strlen(buf), "%2.2X", u.node[i]);
196      len = strlen(buf);
197      break;
198   case F_B64:
199      for(from = (unsigned char*)&u, to = (unsigned char*)buf, i = sizeof(u);
200	  i > 0; i -= 3, from += 3) {
201         *to++ = base64[from[0]>>2];
202         switch(i) {
203	 case 1:
204	    *to++ = base64[(from[0]&0x03)<<4];
205	    *to++ = '=';
206	    *to++ = '=';
207	     break;
208         case 2:
209	    *to++ = base64[((from[0]&0x03)<<4) | ((from[1]&0xF0)>>4)];
210	    *to++ = base64[(from[1]&0x0F)<<2];
211	    *to++ = '=';
212	     break;
213         default:
214	    *to++ = base64[((from[0]&0x03)<<4) | ((from[1]&0xF0)>>4)];
215	    *to++ = base64[((from[1]&0x0F)<<2) | ((from[2]&0xC0)>>6)];
216	    *to++ = base64[(from[2]&0x3F)];
217         }
218      }
219      len = strlen(buf);
220      break;
221   default:
222      croak("invalid type: %d\n", type);
223      break;
224   }
225   return sv_2mortal(newSVpv(buf,len));
226}
227
228MODULE = Data::UUID		PACKAGE = Data::UUID
229
230PROTOTYPES: DISABLE
231
232void
233constant(sv,arg)
234PREINIT:
235   STRLEN  len;
236   char   *pv;
237INPUT:
238   SV   *sv
239   char *s = SvPV(sv, len);
240PPCODE:
241   pv = 0; len = sizeof(perl_uuid_t);
242   if (strEQ(s,"NameSpace_DNS"))
243      pv = (char*)&NameSpace_DNS;
244   if (strEQ(s,"NameSpace_URL"))
245      pv = (char*)&NameSpace_URL;
246   if (strEQ(s,"NameSpace_X500"))
247      pv = (char*)&NameSpace_X500;
248   if (strEQ(s,"NameSpace_OID"))
249      pv = (char*)&NameSpace_OID;
250   ST(0) = sv_2mortal(newSVpv(pv, len));
251   XSRETURN(1);
252
253uuid_context_t*
254new(class)
255   char *class;
256PREINIT:
257   FILE          *fd;
258   unsigned char  seed[16];
259   perl_uuid_time_t    timestamp;
260   mode_t         mask;
261CODE:
262   Newz(0,RETVAL,1,uuid_context_t);
263   if ((fd = fopen(UUID_STATE_NV_STORE, "rb"))) {
264      fread(&(RETVAL->state), sizeof(uuid_state_t), 1, fd);
265      fclose(fd);
266      get_current_time(&timestamp);
267      RETVAL->next_save = timestamp;
268   }
269   if ((fd = fopen(UUID_NODEID_NV_STORE, "rb"))) {
270      pid_t *hate = (pid_t *) &(RETVAL->nodeid);
271      fread(&(RETVAL->nodeid), sizeof(uuid_node_t), 1, fd );
272      fclose(fd);
273
274      *hate += getpid();
275   } else {
276      get_random_info(seed);
277      seed[0] |= 0x80;
278      memcpy(&(RETVAL->nodeid), seed, sizeof(uuid_node_t));
279      mask = umask(_DEFAULT_UMASK);
280      if ((fd = fopen(UUID_NODEID_NV_STORE, "wb"))) {
281         fwrite(&(RETVAL->nodeid), sizeof(uuid_node_t), 1, fd);
282         fclose(fd);
283      };
284      umask(mask);
285   }
286   errno = 0;
287OUTPUT:
288   RETVAL
289
290void
291create(self)
292   uuid_context_t *self;
293ALIAS:
294   Data::UUID::create_bin = F_BIN
295   Data::UUID::create_str = F_STR
296   Data::UUID::create_hex = F_HEX
297   Data::UUID::create_b64 = F_B64
298PREINIT:
299   perl_uuid_time_t  timestamp;
300   unsigned16   clockseq;
301   perl_uuid_t       uuid;
302   FILE        *fd;
303   mode_t       mask;
304PPCODE:
305   clockseq = self->state.cs;
306   get_current_time(&timestamp);
307   if ( self->state.ts == I64(0) ||
308      memcmp(&(self->nodeid), &(self->state.node), sizeof(uuid_node_t)))
309      clockseq = true_random();
310   else if (timestamp <= self->state.ts)
311      clockseq++;
312
313   format_uuid_v1(&uuid, clockseq, timestamp, self->nodeid);
314   self->state.node = self->nodeid;
315   self->state.ts   = timestamp;
316   self->state.cs   = clockseq;
317   if (timestamp > self->next_save ) {
318      mask = umask(_DEFAULT_UMASK);
319      if((fd = fopen(UUID_STATE_NV_STORE, "wb"))) {
320	 LOCK(fd);
321         fwrite(&(self->state), sizeof(uuid_state_t), 1, fd);
322	 UNLOCK(fd);
323         fclose(fd);
324      }
325      umask(mask);
326      self->next_save = timestamp + (10 * 10 * 1000 * 1000);
327   }
328   ST(0) = make_ret(uuid, ix);
329   XSRETURN(1);
330
331void
332create_from_name(self,nsid,name)
333   uuid_context_t *self;
334   perl_uuid_t         *nsid;
335   char           *name;
336ALIAS:
337   Data::UUID::create_from_name_bin = F_BIN
338   Data::UUID::create_from_name_str = F_STR
339   Data::UUID::create_from_name_hex = F_HEX
340   Data::UUID::create_from_name_b64 = F_B64
341PREINIT:
342   MD5_CTX       c;
343   unsigned char hash[16];
344   perl_uuid_t        net_nsid;
345   perl_uuid_t        uuid;
346PPCODE:
347   net_nsid = *nsid;
348   net_nsid.time_low            = htonl(net_nsid.time_low);
349   net_nsid.time_mid            = htons(net_nsid.time_mid);
350   net_nsid.time_hi_and_version = htons(net_nsid.time_hi_and_version);
351
352   MD5Init(&c);
353   MD5Update(&c, (unsigned char*)&net_nsid, sizeof(perl_uuid_t));
354   MD5Update(&c, (unsigned char*)name, strlen(name));
355   MD5Final(hash, &c);
356
357   format_uuid_v3(&uuid, hash);
358   ST(0) = make_ret(uuid, ix);
359   XSRETURN(1);
360
361int
362compare(self,u1,u2)
363   uuid_context_t *self;
364   perl_uuid_t         *u1;
365   perl_uuid_t         *u2;
366PREINIT:
367   int i;
368CODE:
369   RETVAL = 0;
370   CHECK(u1->time_low, u2->time_low);
371   CHECK(u1->time_mid, u2->time_mid);
372   CHECK(u1->time_hi_and_version, u2->time_hi_and_version);
373   CHECK(u1->clock_seq_hi_and_reserved, u2->clock_seq_hi_and_reserved);
374   CHECK(u1->clock_seq_low, u2->clock_seq_low);
375   for (i = 0; i < 6; i++) {
376      if (u1->node[i] < u2->node[i])
377         RETVAL = -1;
378      if (u1->node[i] > u2->node[i])
379         RETVAL =  1;
380   }
381OUTPUT:
382   RETVAL
383
384void
385to_string(self,uuid)
386   uuid_context_t *self;
387   perl_uuid_t         *uuid;
388ALIAS:
389   Data::UUID::to_hexstring = F_HEX
390   Data::UUID::to_b64string = F_B64
391PPCODE:
392   ST(0) = make_ret(*uuid, ix ? ix : F_STR);
393   XSRETURN(1);
394
395void
396from_string(self,str)
397   uuid_context_t *self;
398   char           *str;
399ALIAS:
400   Data::UUID::from_hexstring = F_HEX
401   Data::UUID::from_b64string = F_B64
402PREINIT:
403   perl_uuid_t         uuid;
404   char          *from, *to;
405   int            i, c;
406   unsigned char  buf[4];
407PPCODE:
408   switch(ix) {
409   case F_BIN:
410   case F_STR:
411   case F_HEX:
412      from = str;
413      memset(&uuid, 0x00, sizeof(perl_uuid_t));
414      if ( from[0] == '0' && from[1] == 'x' )
415         from += 2;
416      for (i = 0; i < sizeof(perl_uuid_t); i++) {
417         if (*from == '-')
418	    from++;
419         if (sscanf(from, "%2x", &c) != 1)
420	    croak("from_string(%s) failed...\n", str);
421         ((unsigned char*)&uuid)[i] = (unsigned char)c;
422         from += 2;
423      }
424      uuid.time_low            = ntohl(uuid.time_low);
425      uuid.time_mid            = ntohs(uuid.time_mid);
426      uuid.time_hi_and_version = ntohs(uuid.time_hi_and_version);
427      break;
428   case F_B64:
429      from = str; to = (char*)&uuid;
430      while(from < (str + strlen(str))) {
431	 i = 0; memset(buf, 254, 4);
432	 do {
433	    c = index64[(int)*from++];
434	    if (c != 255) buf[i++] = (unsigned char)c;
435	    if (from == (str + strlen(str)))
436	       break;
437         } while (i < 4);
438
439	 if (buf[0] == 254 || buf[1] == 254)
440	    break;
441         *to++ = (buf[0] << 2) | ((buf[1] & 0x30) >> 4);
442
443	 if (buf[2] == 254) break;
444	 *to++ = ((buf[1] & 0x0F) << 4) | ((buf[2] & 0x3C) >> 2);
445
446	 if (buf[3] == 254) break;
447	 *to++ = ((buf[2] & 0x03) << 6) | buf[3];
448      }
449      break;
450   default:
451      croak("invalid type %d\n", ix);
452      break;
453   }
454   ST(0) = make_ret(uuid, F_BIN);
455   XSRETURN(1);
456
457void
458DESTROY(self)
459   uuid_context_t *self;
460PREINIT:
461   FILE           *fd;
462CODE:
463   if ((fd = fopen(UUID_STATE_NV_STORE, "wb"))) {
464      LOCK(fd);
465      fwrite(&(self->state), sizeof(uuid_state_t), 1, fd);
466      UNLOCK(fd);
467      fclose(fd);
468   };
469   Safefree(self);
470