1 /*
2 * This file is part of the Sofia-SIP package
3 *
4 * Copyright (C) 2005 Nokia Corporation.
5 *
6 * Contact: Pekka Pessi <pekka.pessi@nokia.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2.1 of
11 * the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301 USA
22 *
23 */
24
25 /**@defgroup su_uniqueid GloballyUniqueIDs
26 *
27 * Globally unique IDs and random integers.
28 *
29 * GloballyUniqueID or #su_guid_t is a 128-bit identifier based on current
30 * time and MAC address of the node generating the ID. A new ID is generated
31 * each time su_guid_generate() is called. Please note that such IDs are @b
32 * not unique if multiple processes are run on the same node.
33 *
34 * Use su_guid_sprintf() to convert #su_guid_t to printable format.
35 *
36 * The random integers can be generated with functions
37 * - su_randint(),
38 * - su_randmem(), or
39 * - su_random().
40 */
41
42 /**@ingroup su_uniqueid
43 *
44 * @CFILE su_uniqueid.c Construct a GloballyUniqueID as per H.225.0 v2.
45 *
46 * @author Pekka Pessi <pessi@research.nokia.com>
47 *
48 * @date Created: Tue Apr 15 06:31:41 1997 pessi
49 */
50
51 #include "config.h"
52
53 #if defined(_WIN32)
54 int _getpid(void);
55 #define getpid _getpid
56 #endif
57
58 #include <string.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <time.h>
62
63 #if HAVE_SYS_TIME_H
64 #include <sys/time.h>
65 #endif
66
67 #if HAVE_UNISTD_H
68 #include <sys/types.h>
69 #include <unistd.h>
70 #endif
71
72 #include "sofia-sip/su.h"
73 #include "sofia-sip/su_time.h"
74 #include "sofia-sip/su_uniqueid.h"
75
76 /* For random number generator */
77 static FILE *urandom;
78
79 union state {
80 uint64_t u64;
81 };
82
83 #if SU_HAVE_PTHREADS
84
85 #include <pthread.h>
86
87 #if __sun
88 #undef PTHREAD_ONCE_INIT
89 #define PTHREAD_ONCE_INIT {{ 0, 0, 0, PTHREAD_ONCE_NOTDONE }}
90 #endif
91
92 static pthread_once_t once = PTHREAD_ONCE_INIT;
93 static int done_once = 0;
94 static pthread_key_t state_key;
95
96 static void
init_once(void)97 init_once(void)
98 {
99 if (done_once)
100 return;
101
102 pthread_key_create(&state_key, free);
103 #if HAVE_DEV_URANDOM
104 urandom = fopen("/dev/urandom", "rb");
105 #endif /* HAVE_DEV_URANDOM */
106 done_once = 1;
107 }
108
109 #else
110 static int initialized;
111 #endif
112
113 static union state *
get_state(void)114 get_state(void)
115 {
116 static union state state0[1];
117 union state *retval;
118
119 #if SU_HAVE_PTHREADS
120
121 pthread_once(&once, init_once);
122
123 if (urandom)
124 return NULL;
125
126 retval = pthread_getspecific(state_key);
127 if (retval) {
128 return retval;
129 }
130
131 retval = calloc(1, sizeof *retval);
132 if (retval != NULL)
133 pthread_setspecific(state_key, retval);
134 else
135 retval = state0;
136
137 #else /* !SU_HAVE_PTHREADS */
138
139 if (urandom == NULL) {
140 #if HAVE_DEV_URANDOM
141 urandom = fopen("/dev/urandom", "rb");
142 #endif /* HAVE_DEV_URANDOM */
143 }
144
145 if (urandom)
146 return NULL;
147
148 retval = state0;
149
150 if (initialized)
151 return retval;
152 #endif
153
154 {
155 uint32_t seed[32];
156 int i;
157 union {
158 uint32_t u32;
159 pthread_t tid;
160 } tid32 = { 0 };
161
162 tid32.tid = pthread_self();
163
164 memset(seed, 0, sizeof seed); /* Make valgrind happy */
165
166 for (i = 0; i < 32; i += 2) {
167 #if HAVE_CLOCK_GETTIME
168 struct timespec ts;
169 (void)clock_gettime(CLOCK_REALTIME, &ts);
170 seed[i] ^= ts.tv_sec; seed[i + 1] ^= ts.tv_nsec;
171 #else
172 su_time_t now;
173 su_time(&now);
174 seed[i] ^= now.tv_sec; seed[i + 1] ^= now.tv_sec;
175 #endif
176 }
177
178 seed[0] ^= getuid();
179 seed[1] ^= getpid();
180 seed[2] ^= tid32.u32;
181 seed[3] ^= (uint32_t)(intptr_t)retval;
182
183 for (i = 0; i < 32; i+= 4) {
184 retval->u64 += ((uint64_t)seed[i] << 32) | seed[i + 1];
185 retval->u64 *= ((uint64_t)seed[i + 3] << 32) | seed[i + 2];
186 }
187
188 retval->u64 += (uint64_t)su_nanotime(NULL);
189 }
190
191 return retval;
192 }
193
194 #if !defined(WIN32) && !defined(WIN64)
195 void sofia_su_uniqueid_destructor(void)
196 __attribute__((destructor));
197 #endif
198
199 void
sofia_su_uniqueid_destructor(void)200 sofia_su_uniqueid_destructor(void)
201 {
202 #if HAVE_DEV_URANDOM
203 if (urandom) {
204 fclose(urandom);
205 urandom=NULL;
206 }
207 #endif /* HAVE_DEV_URANDOM */
208
209 #if SU_HAVE_PTHREADS
210 if (done_once) {
211 pthread_key_delete(state_key);
212 done_once = 0;
213 }
214 #endif
215 }
216
217 #if HAVE_GETIFADDRS
218 #include <ifaddrs.h>
219 #if HAVE_NETPACKET_PACKET_H
220 #define HAVE_SOCKADDR_LL 1
221 #include <netpacket/packet.h>
222 #include <net/if_arp.h>
223 #endif
224 #endif
225
226 #define SIZEOF_NODE 6
227 static
init_node(uint8_t node[SIZEOF_NODE])228 void init_node(uint8_t node[SIZEOF_NODE])
229 {
230 #if HAVE_GETIFADDRS && HAVE_SOCKADDR_LL
231 struct ifaddrs *ifa, *results;
232
233 if (getifaddrs(&results) == 0) {
234 for (ifa = results; ifa; ifa = ifa->ifa_next) {
235 #if HAVE_SOCKADDR_LL
236 struct sockaddr_ll const *sll = (void *)ifa->ifa_addr;
237
238 if (sll == NULL || sll->sll_family != AF_PACKET)
239 continue;
240 switch (sll->sll_hatype) {
241 case ARPHRD_ETHER:
242 case ARPHRD_EETHER:
243 case ARPHRD_IEEE802:
244 break;
245 default:
246 continue;
247 }
248
249 memcpy(node, sll->sll_addr, SIZEOF_NODE);
250
251 break;
252 #endif
253 }
254
255 freeifaddrs(results);
256
257 if (ifa)
258 return; /* Success */
259 }
260 #endif
261
262 su_randmem(node, SIZEOF_NODE);
263 node[0] |= 1; /* "multicast" address */
264 }
265
266 static unsigned char node[SIZEOF_NODE];
267
su_node_identifier(void * address,size_t addrlen)268 size_t su_node_identifier(void *address, size_t addrlen)
269 {
270 if (addrlen > SIZEOF_NODE)
271 addrlen = SIZEOF_NODE;
272
273 su_guid_generate(NULL);
274 memcpy(address, node, addrlen);
275
276 return addrlen;
277 }
278
su_guid_generate(su_guid_t * v)279 void su_guid_generate(su_guid_t *v)
280 {
281 /* Constants */
282 static const unsigned version = 1; /* Current version */
283 static const unsigned reserved = 128; /* DCE variant */
284 #define granularity (10000000UL)
285 static const uint64_t mask60 = SU_U64_C(0xfffFFFFffffFFFF);
286 #define MAGIC (16384)
287
288 /* 100-nanosecond intervals between 15 October 1582 and 1 January 1900 */
289 static const uint64_t ntp_epoch =
290 (uint64_t)(141427) * (24 * 60 * 60L) * granularity;
291
292 static uint64_t timestamp0 = 0;
293 static unsigned clock_sequence = MAGIC;
294
295 #if SU_HAVE_PTHREADS
296 static pthread_mutex_t update = PTHREAD_MUTEX_INITIALIZER;
297 #endif
298
299 uint64_t tl = su_ntp_now();
300 uint64_t hi = su_ntp_hi(tl), lo = su_ntp_lo(tl);
301
302 lo *= granularity;
303 hi *= granularity;
304
305 tl = hi + (lo >> 32) + ntp_epoch;
306
307 #ifdef TESTING
308 printf("timestamp %08x-%08x\n", (unsigned)(tl >>32), (unsigned)tl);
309 #endif
310
311 tl &= mask60;
312 if (tl == 0) tl++;
313
314 #if SU_HAVE_PTHREADS
315 pthread_mutex_lock(&update);
316 #endif
317
318 if (timestamp0 == 0) {
319 clock_sequence = su_randint(0, MAGIC - 1);
320 init_node(node);
321 }
322 else if (tl <= timestamp0) {
323 clock_sequence = (clock_sequence + 1) & (MAGIC - 1);
324 }
325
326 timestamp0 = tl;
327
328 #if SU_HAVE_PTHREADS
329 pthread_mutex_unlock(&update);
330 #endif
331
332 if (v) {
333 v->s.time_high_and_version =
334 htons((unsigned short)(((tl >> 48) & 0x0fff) | (version << 12)));
335 v->s.time_mid = htons((unsigned short)((tl >> 32) & 0xffff));
336 v->s.time_low = htonl((unsigned long)(tl & 0xffffffffUL));
337 v->s.clock_seq_low = clock_sequence & 0xff;
338 v->s.clock_seq_hi_and_reserved = (clock_sequence >> 8) | reserved;
339 memcpy(v->s.node, node, sizeof(v->s.node));
340 }
341 }
342
343 /*
344 * Human-readable form of GloballyUniqueID
345 */
su_guid_sprintf(char * buf,size_t len,su_guid_t const * v)346 isize_t su_guid_sprintf(char* buf, size_t len, su_guid_t const *v)
347 {
348 char mybuf[su_guid_strlen + 1];
349 sprintf(mybuf, "%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
350 (unsigned long)ntohl(v->s.time_low),
351 ntohs(v->s.time_mid),
352 ntohs(v->s.time_high_and_version),
353 v->s.clock_seq_low,
354 v->s.clock_seq_hi_and_reserved,
355 v->s.node[0], v->s.node[1], v->s.node[2],
356 v->s.node[3], v->s.node[4], v->s.node[5]);
357 memcpy(buf, mybuf, len > sizeof(mybuf) ? sizeof(mybuf) : len);
358 return su_guid_strlen;
359 }
360
su_random64(void)361 uint64_t su_random64(void)
362 {
363 union state *state = get_state();
364
365 if (state) {
366 /* Simple rand64 from AoCP */
367 return state->u64 = state->u64 * 0X5851F42D4C957F2DULL + 1ULL;
368 }
369 else {
370 uint64_t retval;
371 size_t len = fread(&retval, 1, sizeof retval, urandom); (void)len;
372 return retval;
373 }
374 }
375
su_randmem(void * mem,size_t siz)376 void *su_randmem(void *mem, size_t siz)
377 {
378 union state *state = get_state();
379
380 if (state) {
381 size_t i;
382 uint64_t r64;
383 uint32_t r32;
384
385 for (i = 0; i < siz; i += 4) {
386 /* Simple rand64 from AoCP */
387 state->u64 = r64 = state->u64 * 0X5851F42D4C957F2DULL + 1ULL;
388 r32 = (uint32_t) (r64 >> 32) ^ (uint32_t)r64;
389 if (siz - i >= 4)
390 memcpy((char *)mem + i, &r32, 4);
391 else
392 memcpy((char *)mem + i, &r32, siz - i);
393 }
394 }
395 else {
396 size_t len = fread(mem, 1, siz, urandom); (void)len;
397 }
398
399 return mem;
400 }
401
402 /**
403 * Generate random integer in range [lb, ub] (inclusive)
404 */
su_randint(int lb,int ub)405 int su_randint(int lb, int ub)
406 {
407 uint64_t rnd;
408 unsigned modulo = (unsigned)(ub - lb + 1);
409
410 if (modulo != 0) {
411 do {
412 rnd = su_random64();
413 } while (rnd / modulo == 0xffffFFFFffffFFFFULL / modulo);
414
415 rnd %= modulo;
416 }
417 else {
418 rnd = su_random64();
419 }
420
421 return (int)rnd + lb;
422 }
423
424 /** Get random 32-bit unsigned number.
425 *
426 */
su_random(void)427 uint32_t su_random(void)
428 {
429 return (uint32_t)(su_random64() >> 16);
430 }
431