1#include "EXTERN.h" 2#include "perl.h" 3#include "XSUB.h" 4#include "UUID.h" 5 6#if defined __BEOS__ || defined __HAIKU__ 7# undef bool 8# include <OS.h> 9#endif 10 11#ifdef USE_ITHREADS 12# define DU_THREADSAFE 1 13#else 14# define DU_THREADSAFE 0 15#endif 16 17#if DU_THREADSAFE 18 19# define pPTBL pTHX 20# define pPTBL_ pTHX_ 21# define aPTBL aTHX 22# define aPTBL_ aTHX_ 23 24# define PTABLE_VAL_FREE(V) ((void) (V)) 25 26# include "ptable.h" 27 28# define ptable_store(T, K, V) ptable_store(aTHX_ (T), (K), (V)) 29 30static ptable *instances; 31static perl_mutex instances_mutex; 32 33static void inc(pTHX_ ptable_ent *ent, void *ud) { 34 UV count = PTR2UV(ent->val); 35 PERL_UNUSED_VAR(ud); 36 ptable_store(instances, ent->key, (void *)++count); 37} 38 39#endif 40 41static perl_uuid_t NameSpace_DNS = { /* 6ba7b810-9dad-11d1-80b4-00c04fd430c8 */ 42 0x6ba7b810, 43 0x9dad, 44 0x11d1, 45 0x80, 0xb4, { 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 } 46}; 47 48static perl_uuid_t NameSpace_URL = { /* 6ba7b811-9dad-11d1-80b4-00c04fd430c8 */ 49 0x6ba7b811, 50 0x9dad, 51 0x11d1, 52 0x80, 0xb4, { 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 } 53}; 54 55static perl_uuid_t NameSpace_OID = { /* 6ba7b812-9dad-11d1-80b4-00c04fd430c8 */ 56 0x6ba7b812, 57 0x9dad, 58 0x11d1, 59 0x80, 0xb4, { 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 } 60}; 61 62static perl_uuid_t NameSpace_X500 = { /* 6ba7b814-9dad-11d1-80b4-00c04fd430c8 */ 63 0x6ba7b814, 64 0x9dad, 65 0x11d1, 66 0x80, 0xb4, { 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 } 67}; 68 69static void format_uuid_v1( 70 perl_uuid_t *uuid, 71 unsigned16 clock_seq, 72 perl_uuid_time_t timestamp, 73 uuid_node_t node 74) { 75 uuid->time_low = (unsigned long)(timestamp & 0xFFFFFFFF); 76 uuid->time_mid = (unsigned short)((timestamp >> 32) & 0xFFFF); 77 uuid->time_hi_and_version = (unsigned short)((timestamp >> 48) & 78 0x0FFF); 79 80 uuid->time_hi_and_version |= (1 << 12); 81 uuid->clock_seq_low = clock_seq & 0xFF; 82 uuid->clock_seq_hi_and_reserved = (clock_seq & 0x3F00) >> 8; 83 uuid->clock_seq_hi_and_reserved |= 0x80; 84 memcpy(&uuid->node, &node, sizeof uuid->node); 85} 86 87static void get_current_time(perl_uuid_time_t * timestamp) { 88 perl_uuid_time_t time_now; 89 static perl_uuid_time_t time_last; 90 static unsigned16 uuids_this_tick; 91 static int inited = 0; 92 93 if (!inited) { 94 get_system_time(&time_last); 95 uuids_this_tick = UUIDS_PER_TICK; 96 inited = 1; 97 }; 98 while (1) { 99 get_system_time(&time_now); 100 101 if (time_last != time_now) { 102 uuids_this_tick = 0; 103 time_last = time_now; 104 break; 105 }; 106 if (uuids_this_tick < UUIDS_PER_TICK) { 107 uuids_this_tick++; 108 break; 109 }; 110 }; 111 *timestamp = time_now + uuids_this_tick; 112} 113 114static unsigned16 true_random(void) { 115 static int inited = 0; 116 perl_uuid_time_t time_now; 117 118 if (!inited) { 119 get_system_time(&time_now); 120 time_now = time_now/UUIDS_PER_TICK; 121 srand((unsigned int)(((time_now >> 32) ^ time_now)&0xffffffff)); 122 inited = 1; 123 }; 124 return (rand()); 125} 126 127static void format_uuid_v3( 128 perl_uuid_t *uuid, 129 unsigned char hash[16] 130) { 131 memcpy(uuid, hash, sizeof(perl_uuid_t)); 132 133 uuid->time_low = ntohl(uuid->time_low); 134 uuid->time_mid = ntohs(uuid->time_mid); 135 uuid->time_hi_and_version = ntohs(uuid->time_hi_and_version); 136 137 uuid->time_hi_and_version &= 0x0FFF; 138 uuid->time_hi_and_version |= (3 << 12); 139 uuid->clock_seq_hi_and_reserved &= 0x3F; 140 uuid->clock_seq_hi_and_reserved |= 0x80; 141} 142 143static void get_system_time(perl_uuid_time_t *perl_uuid_time) { 144#if defined __cygwin__ || defined __MINGW32__ || defined WIN32 145 /* ULARGE_INTEGER time; */ 146 LARGE_INTEGER time; 147 148 /* use QeryPerformanceCounter for +ms resolution - as per Paul Stodghill 149 GetSystemTimeAsFileTime((FILETIME *)&time); */ 150 QueryPerformanceCounter(&time); 151 time.QuadPart += 152 (unsigned __int64) (1000*1000*10) * 153 (unsigned __int64) (60 * 60 * 24) * 154 (unsigned __int64) (17+30+31+365*18+5); 155 156 *perl_uuid_time = time.QuadPart; 157#else 158 struct timeval tp; 159 160 gettimeofday(&tp, (struct timezone *)0); 161 *perl_uuid_time = (tp.tv_sec * I64(10000000)) + (tp.tv_usec * I64(10)) + 162 I64(0x01B21DD213814000); 163#endif 164} 165 166static void get_random_info(unsigned char seed[16]) { 167 SV* ctx; 168#if defined __cygwin__ || defined __MINGW32__ || defined __MSWin32__ 169 typedef struct { 170 MEMORYSTATUS m; 171 SYSTEM_INFO s; 172 FILETIME t; 173 LARGE_INTEGER pc; 174 DWORD tc; 175 DWORD l; 176 char hostname[MAX_COMPUTERNAME_LENGTH + 1]; 177 } randomness; 178#else 179 typedef struct { 180#if defined __BEOS__ || defined __HAIKU__ 181 system_info sys_info; 182#else 183 long hostid; 184#endif 185 struct timeval t; 186 char hostname[257]; 187 } randomness; 188#endif 189 randomness r; 190 191#if defined __cygwin__ || defined __MINGW32__ || defined __MSWin32__ 192 GlobalMemoryStatus(&r.m); 193 GetSystemInfo(&r.s); 194 GetSystemTimeAsFileTime(&r.t); 195 QueryPerformanceCounter(&r.pc); 196 r.tc = GetTickCount(); 197 r.l = MAX_COMPUTERNAME_LENGTH + 1; 198 GetComputerName(r.hostname, &r.l ); 199#else 200# if defined __BEOS__ || defined __HAIKU__ 201 get_system_info(&r.sys_info); 202# else 203 r.hostid = gethostid(); 204# endif 205 gettimeofday(&r.t, (struct timezone *)0); 206 gethostname(r.hostname, 256); 207#endif 208 209 ctx = MD5Init(); 210 MD5Update(ctx, sv_2mortal(newSVpv((char*)&r, sizeof(randomness)))); 211 MD5Final(seed, ctx); 212} 213 214static SV* make_ret(const perl_uuid_t u, int type) { 215 char buf[BUFSIZ]; 216 const unsigned char *from; 217 unsigned char *to; 218 STRLEN len; 219 int i; 220 221 memset(buf, 0x00, BUFSIZ); 222 switch(type) { 223 case F_BIN: 224 memcpy(buf, &u, sizeof(perl_uuid_t)); 225 len = sizeof(perl_uuid_t); 226 break; 227 case F_STR: 228 sprintf(buf, "%8.8X-%4.4X-%4.4X-%2.2X%2.2X-", (unsigned int)u.time_low, u.time_mid, 229 u.time_hi_and_version, u.clock_seq_hi_and_reserved, u.clock_seq_low); 230 for(i = 0; i < 6; i++ ) 231 sprintf(buf+strlen(buf), "%2.2X", u.node[i]); 232 len = strlen(buf); 233 break; 234 case F_HEX: 235 sprintf(buf, "0x%8.8X%4.4X%4.4X%2.2X%2.2X", (unsigned int)u.time_low, u.time_mid, 236 u.time_hi_and_version, u.clock_seq_hi_and_reserved, u.clock_seq_low); 237 for(i = 0; i < 6; i++ ) 238 sprintf(buf+strlen(buf), "%2.2X", u.node[i]); 239 len = strlen(buf); 240 break; 241 case F_B64: 242 for(from = (const unsigned char*)&u, to = (unsigned char*)buf, i = sizeof(u); i > 0; i -= 3, from += 3) { 243 *to++ = base64[from[0]>>2]; 244 switch(i) { 245 case 1: 246 *to++ = base64[(from[0]&0x03)<<4]; 247 *to++ = '='; 248 *to++ = '='; 249 break; 250 case 2: 251 *to++ = base64[((from[0]&0x03)<<4) | ((from[1]&0xF0)>>4)]; 252 *to++ = base64[(from[1]&0x0F)<<2]; 253 *to++ = '='; 254 break; 255 default: 256 *to++ = base64[((from[0]&0x03)<<4) | ((from[1]&0xF0)>>4)]; 257 *to++ = base64[((from[1]&0x0F)<<2) | ((from[2]&0xC0)>>6)]; 258 *to++ = base64[(from[2]&0x3F)]; 259 } 260 } 261 len = strlen(buf); 262 break; 263 default: 264 croak("invalid type: %d\n", type); 265 break; 266 } 267 return sv_2mortal(newSVpv(buf,len)); 268} 269 270static SV* MD5Init() { 271 SV* res; 272 int rcount; 273 274 dSP; 275 276 ENTER; SAVETMPS; 277 278 PUSHMARK(SP); 279 XPUSHs(sv_2mortal(newSVpv("Digest::MD5", 0))); 280 PUTBACK; 281 282 rcount = call_method("new", G_SCALAR); 283 SPAGAIN; 284 285 if ( rcount != 1 ) 286 croak("couldn't construct new Digest::MD5 object"); 287 288 res = newSVsv(POPs); 289 290 PUTBACK; 291 FREETMPS; 292 LEAVE; 293 294 return res; 295}; 296 297static void MD5Update( SV* ctx, SV* data ) { 298 dSP; 299 ENTER; SAVETMPS; 300 301 PUSHMARK(SP); 302 XPUSHs(ctx); 303 XPUSHs(data); 304 PUTBACK; 305 306 call_method("add", G_DISCARD); 307 SPAGAIN; 308 309 PUTBACK; 310 FREETMPS; 311 LEAVE; 312}; 313 314static void MD5Final( unsigned char hash[16], SV* ctx ) { 315 int rcount; 316 char* tmp; 317 STRLEN len; 318 SV* retval; 319 dSP; 320 321 ENTER; SAVETMPS; 322 323 PUSHMARK(SP); 324 XPUSHs(sv_2mortal(ctx)); 325 PUTBACK; 326 327 rcount = call_method("digest", G_SCALAR); 328 SPAGAIN; 329 330 if ( rcount != 1 ) 331 croak("Digest::MD5->digest hasn't returned a scalar"); 332 333 retval = POPs; 334 tmp = SvPV(retval, len); 335 if ( len != 16 ) 336 croak("Digest::MD5->digest returned not 16 bytes"); 337 338 memcpy(hash, tmp, len); 339 340 PUTBACK; 341 FREETMPS; 342 LEAVE; 343}; 344 345MODULE = Data::UUID PACKAGE = Data::UUID 346 347PROTOTYPES: DISABLE 348 349uuid_context_t* 350new(class) 351PREINIT: 352 FILE *fd; 353 unsigned char seed[16]; 354 perl_uuid_time_t timestamp; 355 mode_t mask; 356 UV one = 1; 357CODE: 358 RETVAL = (uuid_context_t *)PerlMemShared_malloc(sizeof(uuid_context_t)); 359 if ((fd = fopen(UUID_STATE_NV_STORE, "rb"))) { 360 fread(&(RETVAL->state), sizeof(uuid_state_t), 1, fd); 361 fclose(fd); 362 get_current_time(×tamp); 363 RETVAL->next_save = timestamp; 364 } 365 if ((fd = fopen(UUID_NODEID_NV_STORE, "rb"))) { 366 pid_t *hate = (pid_t *) &(RETVAL->nodeid); 367 fread(&(RETVAL->nodeid), sizeof(uuid_node_t), 1, fd ); 368 fclose(fd); 369 370 *hate += getpid(); 371 } else { 372 get_random_info(seed); 373 seed[0] |= 0x80; 374 memcpy(&(RETVAL->nodeid), seed, sizeof(uuid_node_t)); 375 mask = umask(_DEFAULT_UMASK); 376 if ((fd = fopen(UUID_NODEID_NV_STORE, "wb"))) { 377 fwrite(&(RETVAL->nodeid), sizeof(uuid_node_t), 1, fd); 378 fclose(fd); 379 }; 380 umask(mask); 381 } 382 errno = 0; 383#if DU_THREADSAFE 384 MUTEX_LOCK(&instances_mutex); 385 ptable_store(instances, RETVAL, INT2PTR(void *, one)); 386 MUTEX_UNLOCK(&instances_mutex); 387#endif 388OUTPUT: 389 RETVAL 390 391void 392create(self) 393 uuid_context_t *self; 394ALIAS: 395 Data::UUID::create_bin = F_BIN 396 Data::UUID::create_str = F_STR 397 Data::UUID::create_hex = F_HEX 398 Data::UUID::create_b64 = F_B64 399PREINIT: 400 perl_uuid_time_t timestamp; 401 unsigned16 clockseq; 402 perl_uuid_t uuid; 403 FILE *fd; 404 mode_t mask; 405PPCODE: 406 clockseq = self->state.cs; 407 get_current_time(×tamp); 408 if ( self->state.ts == I64(0) || 409 memcmp(&(self->nodeid), &(self->state.node), sizeof(uuid_node_t))) 410 clockseq = true_random(); 411 else if (timestamp <= self->state.ts) 412 clockseq++; 413 414 format_uuid_v1(&uuid, clockseq, timestamp, self->nodeid); 415 self->state.node = self->nodeid; 416 self->state.ts = timestamp; 417 self->state.cs = clockseq; 418 if (timestamp > self->next_save ) { 419 mask = umask(_DEFAULT_UMASK); 420 if((fd = fopen(UUID_STATE_NV_STORE, "wb"))) { 421 LOCK(fd); 422 fwrite(&(self->state), sizeof(uuid_state_t), 1, fd); 423 UNLOCK(fd); 424 fclose(fd); 425 } 426 umask(mask); 427 self->next_save = timestamp + (10 * 10 * 1000 * 1000); 428 } 429 ST(0) = make_ret(uuid, ix); 430 XSRETURN(1); 431 432void 433create_from_name(self,nsid,name) 434 uuid_context_t *self; 435 perl_uuid_t *nsid; 436 SV *name; 437ALIAS: 438 Data::UUID::create_from_name_bin = F_BIN 439 Data::UUID::create_from_name_str = F_STR 440 Data::UUID::create_from_name_hex = F_HEX 441 Data::UUID::create_from_name_b64 = F_B64 442PREINIT: 443 SV *ctx; 444 unsigned char hash[16]; 445 perl_uuid_t net_nsid; 446 perl_uuid_t uuid; 447PPCODE: 448 net_nsid = *nsid; 449 net_nsid.time_low = htonl(net_nsid.time_low); 450 net_nsid.time_mid = htons(net_nsid.time_mid); 451 net_nsid.time_hi_and_version = htons(net_nsid.time_hi_and_version); 452 453 ctx = MD5Init(); 454 MD5Update(ctx, newSVpv((char*)&net_nsid, sizeof(perl_uuid_t))); 455 MD5Update(ctx, name); 456 MD5Final(hash, ctx); 457 458 format_uuid_v3(&uuid, hash); 459 ST(0) = make_ret(uuid, ix); 460 XSRETURN(1); 461 462int 463compare(self,u1,u2) 464 uuid_context_t *self; 465 perl_uuid_t *u1; 466 perl_uuid_t *u2; 467PREINIT: 468 int i; 469CODE: 470 RETVAL = 0; 471 CHECK(u1->time_low, u2->time_low); 472 CHECK(u1->time_mid, u2->time_mid); 473 CHECK(u1->time_hi_and_version, u2->time_hi_and_version); 474 CHECK(u1->clock_seq_hi_and_reserved, u2->clock_seq_hi_and_reserved); 475 CHECK(u1->clock_seq_low, u2->clock_seq_low); 476 for (i = 0; i < 6; i++) { 477 if (u1->node[i] < u2->node[i]) 478 RETVAL = -1; 479 if (u1->node[i] > u2->node[i]) 480 RETVAL = 1; 481 } 482OUTPUT: 483 RETVAL 484 485void 486to_string(self,uuid) 487 uuid_context_t *self; 488 perl_uuid_t *uuid; 489ALIAS: 490 Data::UUID::to_hexstring = F_HEX 491 Data::UUID::to_b64string = F_B64 492PPCODE: 493 ST(0) = make_ret(*uuid, ix ? ix : F_STR); 494 XSRETURN(1); 495 496void 497from_string(self,str) 498 uuid_context_t *self; 499 char *str; 500ALIAS: 501 Data::UUID::from_hexstring = F_HEX 502 Data::UUID::from_b64string = F_B64 503PREINIT: 504 perl_uuid_t uuid; 505 char *from, *to; 506 int c; 507 unsigned int i; 508 unsigned char buf[4]; 509PPCODE: 510 switch(ix) { 511 case F_BIN: 512 case F_STR: 513 case F_HEX: 514 from = str; 515 memset(&uuid, 0x00, sizeof(perl_uuid_t)); 516 if ( from[0] == '0' && from[1] == 'x' ) 517 from += 2; 518 for (i = 0; i < sizeof(perl_uuid_t); i++) { 519 if (*from == '-') 520 from++; 521 if (sscanf(from, "%2x", &c) != 1) 522 croak("from_string(%s) failed...\n", str); 523 ((unsigned char*)&uuid)[i] = (unsigned char)c; 524 from += 2; 525 } 526 uuid.time_low = ntohl(uuid.time_low); 527 uuid.time_mid = ntohs(uuid.time_mid); 528 uuid.time_hi_and_version = ntohs(uuid.time_hi_and_version); 529 break; 530 case F_B64: 531 from = str; to = (char*)&uuid; 532 while(from < (str + strlen(str))) { 533 i = 0; memset(buf, 254, 4); 534 do { 535 c = index64[(int)*from++]; 536 if (c != 255) buf[i++] = (unsigned char)c; 537 if (from == (str + strlen(str))) 538 break; 539 } while (i < 4); 540 541 if (buf[0] == 254 || buf[1] == 254) 542 break; 543 *to++ = (buf[0] << 2) | ((buf[1] & 0x30) >> 4); 544 545 if (buf[2] == 254) break; 546 *to++ = ((buf[1] & 0x0F) << 4) | ((buf[2] & 0x3C) >> 2); 547 548 if (buf[3] == 254) break; 549 *to++ = ((buf[2] & 0x03) << 6) | buf[3]; 550 } 551 break; 552 default: 553 croak("invalid type %d\n", ix); 554 break; 555 } 556 ST(0) = make_ret(uuid, F_BIN); 557 XSRETURN(1); 558 559#if DU_THREADSAFE 560 561void 562CLONE(klass) 563CODE: 564 MUTEX_LOCK(&instances_mutex); 565 ptable_walk(instances, inc, instances); 566 MUTEX_UNLOCK(&instances_mutex); 567 568#endif 569 570void 571DESTROY(self) 572 uuid_context_t *self; 573PREINIT: 574#if DU_THREADSAFE 575 UV count; 576#endif 577 FILE *fd; 578CODE: 579#if DU_THREADSAFE 580 MUTEX_LOCK(&instances_mutex); 581 count = PTR2UV(ptable_fetch(instances, self)); 582 count--; 583 ptable_store(instances, self, (void *)count); 584 MUTEX_UNLOCK(&instances_mutex); 585 if (count == 0) { 586#endif 587 if ((fd = fopen(UUID_STATE_NV_STORE, "wb"))) { 588 LOCK(fd); 589 fwrite(&(self->state), sizeof(uuid_state_t), 1, fd); 590 UNLOCK(fd); 591 fclose(fd); 592 }; 593 PerlMemShared_free(self); 594#if DU_THREADSAFE 595 } 596#endif 597 598BOOT: 599{ 600 HV *stash = gv_stashpv("Data::UUID", 0); 601 STRLEN len = sizeof(perl_uuid_t); 602#if DU_THREADSAFE 603 instances = ptable_new(); 604 MUTEX_INIT(&instances_mutex); 605#endif 606 newCONSTSUB(stash, "NameSpace_DNS", newSVpv((char *)&NameSpace_DNS, len)); 607 newCONSTSUB(stash, "NameSpace_URL", newSVpv((char *)&NameSpace_URL, len)); 608 newCONSTSUB(stash, "NameSpace_OID", newSVpv((char *)&NameSpace_OID, len)); 609 newCONSTSUB(stash, "NameSpace_X500", newSVpv((char *)&NameSpace_X500, len)); 610} 611