write audit log call variant which accepts json objects; old/new value logging for...
[public/netxms.git] / src / libnetxms / uuid.cpp
1 /*
2 ** libuuid integrated into NetXMS project
3 ** Copyright (C) 1996, 1997 Theodore Ts'o.
4 ** Integrated into NetXMS by Victor Kirhenshtein
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU Lesser General Public License as published
8 ** by the Free Software Foundation; either version 3 of the License, or
9 ** (at your option) any later version.
10 **
11 ** This program is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ** GNU General Public License for more details.
15 **
16 ** You should have received a copy of the GNU Lesser General Public License
17 ** along with this program; if not, write to the Free Software
18 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 **
20 ** File: uuid.cpp
21 **
22 **/
23
24 #include "libnetxms.h"
25 #include <uuid.h>
26
27 #if HAVE_SYS_IOCTL_H
28 #include <sys/ioctl.h>
29 #endif
30
31 #if HAVE_SYS_SOCKIO_H
32 #include <sys/sockio.h>
33 #endif
34
35 #if HAVE_NET_IF_H
36 #include <net/if.h>
37 #endif
38
39 #if HAVE_NET_IF_ARP_H
40 #include <net/if_arp.h>
41 #endif
42
43 #if HAVE_NET_IF_DL_H
44 #include <net/if_dl.h>
45 #endif
46
47 #if HAVE_SYS_UTSNAME_H
48 #include <sys/utsname.h>
49 #endif
50
51 #ifdef HAVE_SRANDOM
52 #define srand(x) srandom(x)
53 #define rand() random()
54 #endif
55
56 /**
57 * NULL GUID
58 */
59 const uuid uuid::NULL_UUID = uuid();
60
61 /*
62 * Offset between 15-Oct-1582 and 1-Jan-70
63 */
64 #define TIME_OFFSET_HIGH 0x01B21DD2
65 #define TIME_OFFSET_LOW 0x13814000
66
67 /**
68 * Unpacked UUID structure
69 */
70 struct __uuid
71 {
72 DWORD time_low;
73 WORD time_mid;
74 WORD time_hi_and_version;
75 WORD clock_seq;
76 BYTE node[6];
77 };
78
79 /**
80 * Internal routine for packing UUID's
81 */
82 static void uuid_pack(struct __uuid *uu, uuid_t ptr)
83 {
84 unsigned int tmp;
85 unsigned char *out = ptr;
86
87 tmp = uu->time_low;
88 out[3] = (unsigned char) tmp;
89 tmp >>= 8;
90 out[2] = (unsigned char) tmp;
91 tmp >>= 8;
92 out[1] = (unsigned char) tmp;
93 tmp >>= 8;
94 out[0] = (unsigned char) tmp;
95
96 tmp = uu->time_mid;
97 out[5] = (unsigned char) tmp;
98 tmp >>= 8;
99 out[4] = (unsigned char) tmp;
100
101 tmp = uu->time_hi_and_version;
102 out[7] = (unsigned char) tmp;
103 tmp >>= 8;
104 out[6] = (unsigned char) tmp;
105
106 tmp = uu->clock_seq;
107 out[9] = (unsigned char) tmp;
108 tmp >>= 8;
109 out[8] = (unsigned char) tmp;
110
111 memcpy(out+10, uu->node, 6);
112 }
113
114 /**
115 * Internal routine for unpacking UUID
116 */
117 static void uuid_unpack(const uuid_t in, struct __uuid *uu)
118 {
119 const unsigned char *ptr = in;
120 unsigned int tmp;
121
122 tmp = *ptr++;
123 tmp = (tmp << 8) | *ptr++;
124 tmp = (tmp << 8) | *ptr++;
125 tmp = (tmp << 8) | *ptr++;
126 uu->time_low = tmp;
127
128 tmp = *ptr++;
129 tmp = (tmp << 8) | *ptr++;
130 uu->time_mid = tmp;
131
132 tmp = *ptr++;
133 tmp = (tmp << 8) | *ptr++;
134 uu->time_hi_and_version = tmp;
135
136 tmp = *ptr++;
137 tmp = (tmp << 8) | *ptr++;
138 uu->clock_seq = tmp;
139
140 memcpy(uu->node, ptr, 6);
141 }
142
143 /**
144 * Clear a UUID
145 */
146 void LIBNETXMS_EXPORTABLE _uuid_clear(uuid_t uu)
147 {
148 memset(uu, 0, 16);
149 }
150
151 #define UUCMP(u1,u2) if (u1 != u2) return((u1 < u2) ? -1 : 1);
152
153 /**
154 * compare whether or not two UUID's are the same
155 *
156 * Returns 1/-1 if the two UUID's are different, and 0 if they are the same.
157 */
158 int LIBNETXMS_EXPORTABLE _uuid_compare(const uuid_t uu1, const uuid_t uu2)
159 {
160 struct __uuid uuid1, uuid2;
161
162 uuid_unpack(uu1, &uuid1);
163 uuid_unpack(uu2, &uuid2);
164
165 UUCMP(uuid1.time_low, uuid2.time_low);
166 UUCMP(uuid1.time_mid, uuid2.time_mid);
167 UUCMP(uuid1.time_hi_and_version, uuid2.time_hi_and_version);
168 UUCMP(uuid1.clock_seq, uuid2.clock_seq);
169 return memcmp(uuid1.node, uuid2.node, 6);
170 }
171
172 /**
173 * isnull.c --- Check whether or not the UUID is null
174 * Returns true if the uuid is the NULL uuid
175 */
176 bool LIBNETXMS_EXPORTABLE _uuid_is_null(const uuid_t uu)
177 {
178 const unsigned char *cp;
179 int i;
180
181 for (i=0, cp = uu; i < 16; i++)
182 if (*cp++)
183 return false;
184 return true;
185 }
186
187 /**
188 * Parse UUID
189 */
190 int LIBNETXMS_EXPORTABLE _uuid_parse(const TCHAR *in, uuid_t uu)
191 {
192 struct __uuid uuid;
193 int i;
194 const TCHAR *cp;
195 TCHAR buf[3];
196
197 if (_tcslen(in) != 36)
198 return -1;
199 for (i=0, cp = in; i <= 36; i++,cp++) {
200 if ((i == 8) || (i == 13) || (i == 18) ||
201 (i == 23))
202 if (*cp == _T('-'))
203 continue;
204 if (i == 36)
205 if (*cp == 0)
206 continue;
207 if (!_istxdigit(*cp))
208 return -1;
209 }
210 uuid.time_low = _tcstoul(in, NULL, 16);
211 uuid.time_mid = (WORD)_tcstoul(in + 9, NULL, 16);
212 uuid.time_hi_and_version = (WORD)_tcstoul(in + 14, NULL, 16);
213 uuid.clock_seq = (WORD)_tcstoul(in + 19, NULL, 16);
214 cp = in + 24;
215 buf[2] = 0;
216 for(i = 0; i < 6; i++)
217 {
218 buf[0] = *cp++;
219 buf[1] = *cp++;
220 uuid.node[i] = (BYTE)_tcstoul(buf, NULL, 16);
221 }
222
223 uuid_pack(&uuid, uu);
224 return 0;
225 }
226
227 /**
228 * Convert packed UUID to string
229 */
230 TCHAR LIBNETXMS_EXPORTABLE *_uuid_to_string(const uuid_t uu, TCHAR *out)
231 {
232 struct __uuid uuid;
233
234 uuid_unpack(uu, &uuid);
235 _sntprintf(out, 64,
236 _T("%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x"),
237 uuid.time_low, uuid.time_mid, uuid.time_hi_and_version,
238 uuid.clock_seq >> 8, uuid.clock_seq & 0xFF,
239 uuid.node[0], uuid.node[1], uuid.node[2],
240 uuid.node[3], uuid.node[4], uuid.node[5]);
241 return out;
242 }
243
244 #ifdef UNICODE
245
246 /**
247 * Convert packed UUID to string (non-UNICODE version)
248 */
249 char LIBNETXMS_EXPORTABLE *_uuid_to_stringA(const uuid_t uu, char *out)
250 {
251 struct __uuid uuid;
252
253 uuid_unpack(uu, &uuid);
254 snprintf(out, 64,
255 "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
256 uuid.time_low, uuid.time_mid, uuid.time_hi_and_version,
257 uuid.clock_seq >> 8, uuid.clock_seq & 0xFF,
258 uuid.node[0], uuid.node[1], uuid.node[2],
259 uuid.node[3], uuid.node[4], uuid.node[5]);
260 return out;
261 }
262
263 #endif
264
265 #ifndef _WIN32
266
267 static int get_random_fd()
268 {
269 int fd = -2;
270 int i;
271
272 if (fd == -2)
273 {
274 fd = open("/dev/urandom", O_RDONLY);
275 if (fd == -1)
276 fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
277 srand((getpid() << 16) ^ getuid() ^ time(0));
278 }
279 /* Crank the random number generator a few times */
280 for (i = time(0) & 0x1F; i > 0; i--)
281 rand();
282 return fd;
283 }
284
285 #endif
286
287 /*
288 * Generate a series of random bytes. Use /dev/urandom if possible,
289 * and if not, use srandom/random.
290 */
291 static void get_random_bytes(void *buf, int nbytes)
292 {
293 int i;
294 char *cp = (char *)buf;
295 #ifndef _WIN32
296 int fd = get_random_fd();
297 int lose_counter = 0;
298
299 if (fd >= 0)
300 {
301 while (nbytes > 0)
302 {
303 i = read(fd, cp, nbytes);
304 if ((i < 0) &&
305 ((errno == EINTR) || (errno == EAGAIN)))
306 continue;
307 if (i <= 0) {
308 if (lose_counter++ == 8)
309 break;
310 continue;
311 }
312 nbytes -= i;
313 cp += i;
314 lose_counter = 0;
315 }
316 close(fd);
317 }
318 #endif
319 if (nbytes == 0)
320 return;
321
322 /* FIXME: put something better here if no /dev/random! */
323 srand((unsigned int)time(NULL) ^ GetCurrentProcessId());
324 for (i = 0; i < nbytes; i++)
325 *cp++ = rand() & 0xFF;
326 return;
327 }
328
329 /*
330 * Get the ethernet hardware address or other available node ID
331 */
332 static int get_node_id(unsigned char *node_id)
333 {
334 #if defined(_AIX)
335
336 struct utsname info;
337 if (uname(&info) == -1)
338 return -1;
339
340 memset(node_id, 0, 6);
341 StrToBinA(info.machine, node_id, 6);
342 return 0;
343
344 #elif defined(HAVE_NET_IF_H) && !defined(sun) && !defined(__sun) && (defined(SIOCGIFHWADDR) || defined(SIOCGENADDR))
345
346 int sd;
347 struct ifreq ifr, *ifrp;
348 struct ifconf ifc;
349 char buf[1024];
350 int n, i;
351 unsigned char *a;
352
353 /*
354 * BSD 4.4 defines the size of an ifreq to be
355 * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
356 * However, under earlier systems, sa_len isn't present, so the size is
357 * just sizeof(struct ifreq)
358 */
359 #ifdef HAVE_SA_LEN
360 #ifndef max
361 #define max(a,b) ((a) > (b) ? (a) : (b))
362 #endif
363 #define ifreq_size(i) max(sizeof(struct ifreq),\
364 sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
365 #else
366 #define ifreq_size(i) sizeof(struct ifreq)
367 #endif /* HAVE_SA_LEN*/
368
369 #ifdef AF_INET6
370 sd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
371 if (sd == INVALID_SOCKET)
372 #endif
373 {
374 sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
375 if (sd == INVALID_SOCKET)
376 {
377 return -1;
378 }
379 }
380 memset(buf, 0, sizeof(buf));
381 ifc.ifc_len = sizeof(buf);
382 ifc.ifc_buf = buf;
383 if (ioctl(sd, SIOCGIFCONF, (char *)&ifc) < 0)
384 {
385 close(sd);
386 return -1;
387 }
388 n = ifc.ifc_len;
389 for (i = 0; i < n; i+= ifreq_size(*ifr) ) {
390 ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i);
391 strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ);
392 #if defined(SIOCGIFHWADDR)
393 if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0)
394 continue;
395 a = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
396 #elif defined(SIOCGENADDR)
397 if (ioctl(sd, SIOCGENADDR, &ifr) < 0)
398 continue;
399 a = (unsigned char *) ifr.ifr_enaddr;
400 #else
401 // we don't have a way of getting the hardware address
402 a = (unsigned char *)"\x00\x00\x00\x00\x00\x00";
403 close(sd);
404 return 0;
405 #endif /* SIOCGENADDR / SIOCGIFHWADDR */
406 if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5])
407 continue;
408 if (node_id)
409 {
410 memcpy(node_id, a, 6);
411 close(sd);
412 return 1;
413 }
414 }
415 close(sd);
416 #endif
417 return 0;
418 }
419
420 /* Assume that the gettimeofday() has microsecond granularity */
421 #define MAX_ADJUSTMENT 10
422
423 static int get_clock(DWORD *clock_high, DWORD *clock_low, WORD *ret_clock_seq)
424 {
425 static int adjustment = 0;
426 static struct timeval last = {0, 0};
427 static unsigned short clock_seq;
428 struct timeval tv;
429 QWORD clock_reg;
430 #ifdef _WIN32
431 FILETIME ft;
432 ULARGE_INTEGER li;
433 unsigned __int64 t;
434 #endif
435
436 try_again:
437 #ifdef _WIN32
438 GetSystemTimeAsFileTime(&ft);
439 li.LowPart = ft.dwLowDateTime;
440 li.HighPart = ft.dwHighDateTime;
441 t = li.QuadPart; // In 100-nanosecond intervals
442 t /= 10; // To microseconds
443 tv.tv_sec = (long)(t / 1000000);
444 tv.tv_usec = (long)(t % 1000000);
445 #else
446 gettimeofday(&tv, NULL);
447 #endif
448 if ((last.tv_sec == 0) && (last.tv_usec == 0))
449 {
450 get_random_bytes(&clock_seq, sizeof(clock_seq));
451 clock_seq &= 0x1FFF;
452 last = tv;
453 last.tv_sec--;
454 }
455 if ((tv.tv_sec < last.tv_sec) ||
456 ((tv.tv_sec == last.tv_sec) &&
457 (tv.tv_usec < last.tv_usec)))
458 {
459 clock_seq = (clock_seq + 1) & 0x1FFF;
460 adjustment = 0;
461 last = tv;
462 }
463 else if ((tv.tv_sec == last.tv_sec) &&
464 (tv.tv_usec == last.tv_usec))
465 {
466 if (adjustment >= MAX_ADJUSTMENT)
467 goto try_again;
468 adjustment++;
469 }
470 else
471 {
472 adjustment = 0;
473 last = tv;
474 }
475
476 clock_reg = tv.tv_usec * 10 + adjustment;
477 clock_reg += ((QWORD)tv.tv_sec) * 10000000;
478 clock_reg += (((QWORD)0x01B21DD2) << 32) + 0x13814000;
479
480 *clock_high = (DWORD)(clock_reg >> 32);
481 *clock_low = (DWORD)clock_reg;
482 *ret_clock_seq = clock_seq;
483 return 0;
484 }
485
486 static void uuid_generate_time(uuid_t out)
487 {
488 static unsigned char node_id[6];
489 static int has_init = 0;
490 struct __uuid uu;
491 DWORD clock_mid;
492
493 if (!has_init)
494 {
495 if (get_node_id(node_id) <= 0)
496 {
497 get_random_bytes(node_id, 6);
498 /*
499 * Set multicast bit, to prevent conflicts
500 * with IEEE 802 addresses obtained from
501 * network cards
502 */
503 node_id[0] |= 0x80;
504 }
505 has_init = 1;
506 }
507 get_clock(&clock_mid, &uu.time_low, &uu.clock_seq);
508 uu.clock_seq |= 0x8000;
509 uu.time_mid = (WORD)clock_mid;
510 uu.time_hi_and_version = (WORD)((clock_mid >> 16) | 0x1000);
511 memcpy(uu.node, node_id, 6);
512 uuid_pack(&uu, out);
513 }
514
515 static void uuid_generate_random(uuid_t out)
516 {
517 uuid_t buf;
518 struct __uuid uu;
519
520 get_random_bytes(buf, sizeof(buf));
521 uuid_unpack(buf, &uu);
522
523 uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000;
524 uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) | 0x4000;
525 uuid_pack(&uu, out);
526 }
527
528 /*
529 * This is the generic front-end to uuid_generate_random and
530 * uuid_generate_time. It uses uuid_generate_random only if
531 * /dev/urandom is available, since otherwise we won't have
532 * high-quality randomness.
533 */
534 void LIBNETXMS_EXPORTABLE _uuid_generate(uuid_t out)
535 {
536 #ifdef _WIN32
537 UUID uuid;
538
539 UuidCreate(&uuid);
540 memcpy(out, &uuid, UUID_LENGTH);
541 #else
542 int fd;
543
544 fd = get_random_fd();
545 if (fd >= 0)
546 {
547 close(fd);
548 uuid_generate_random(out);
549 }
550 else
551 {
552 uuid_generate_time(out);
553 }
554 #endif
555 }