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(×tamp); 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(×tamp); 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