]> pd.if.org Git - uuid/blob - internal.c
Initial commit
[uuid] / internal.c
1 /*
2  * internal functions for uuid library
3  *
4  * written by nathan wagner and placed in the public domain
5  */
6 #include <stdint.h>
7 #include <sys/time.h>
8 #include <stdio.h>
9
10 #include "hash.h"
11
12 #include "pduuid.h"
13
14 #ifdef __linux__
15 #include <sys/ioctl.h>
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <netinet/in.h>
19 #include <linux/if.h>
20 #endif
21
22 #ifdef __APPLE__
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <net/if_dl.h>
26 #include <ifaddrs.h>
27 #endif
28
29 #ifdef WIN32
30 #include <windows.h>
31 #include <iphlpapi.h>
32 #endif
33
34 #ifdef __CYGWIN__
35 #include <windows.h>
36 #include <iphlpapi.h>
37 #endif
38
39 static void set_time_low(pd_uuid_t *u, uint32_t low) {
40         u->data[0] = (low >> 24) & 0xff;
41         u->data[1] = (low >> 16) & 0xff;
42         u->data[2] = (low >> 8) & 0xff;
43         u->data[3] = low & 0xff;
44 }
45
46 static void set_time_mid(pd_uuid_t *u, uint16_t mid) {
47         u->data[4] = (mid >> 8) & 0xff;
48         u->data[5] = mid & 0xff;
49 }
50
51 static void set_time_hi(pd_uuid_t *u, uint16_t hi) {
52         u->data[7] = hi & 0xff;
53         u->data[6] &= 0xf0;
54         u->data[6] |= (hi >> 8) & 0xf;
55 }
56
57 static void set_version(pd_uuid_t *u, uint8_t version) {
58         u->data[6] &= 0xf;
59         u->data[6] |= (version & 0xf) << 4;
60 }
61
62 static void set_clock_seq_low(pd_uuid_t *u, uint8_t low) {
63         u->data[9] = low & 0xff;
64 }
65
66 static void set_clock_seq_hi(pd_uuid_t *u, uint8_t hi) {
67         u->data[8] &= 0xc0;
68         u->data[8] |= hi & 0x3f;
69 }
70
71 static void set_reserved(pd_uuid_t *u, uint8_t reserved) {
72         u->data[8] &= 0x3f;
73         u->data[8] |= (reserved & 0x3) << 6;
74 }
75
76 #if 0
77 static void set_node(pd_uuid_t *u, uint8_t data[6]) {
78         u->data[10] = data[0];
79         u->data[11] = data[1];
80         u->data[12] = data[2];
81         u->data[13] = data[3];
82         u->data[14] = data[4];
83         u->data[15] = data[5];
84 }
85 #endif
86
87 static void set_node64(pd_uuid_t *u, uint64_t node) {
88         u->data[10] = (node >> 40) & 0xff;
89         u->data[11] = (node >> 32) & 0xff;
90         u->data[12] = (node >> 24) & 0xff;
91         u->data[13] = (node >> 16) & 0xff;
92         u->data[14] = (node >> 8) & 0xff;
93         u->data[15] = (node >> 0) & 0xff;
94 }
95
96 static void set_timestamp(pd_uuid_t *u, uint64_t ts) {
97         set_time_low(u, ts & 0xffffffff);
98         set_time_mid(u, (ts >> 32) & 0xffff);
99         set_time_hi(u, (ts >> 48) & 0xfff);
100 }
101
102 static void set_clockseq(pd_uuid_t *u, uint16_t seq) {
103         set_clock_seq_low(u, seq & 0xff);
104         set_clock_seq_hi(u, (seq >> 8) & 0xff);
105 }
106
107 static void format_uuid(pd_uuid_t *u, struct pd_uuid_state *s, int version) {
108         set_timestamp(u, s->timestamp);
109         set_version(u, version);
110         set_clockseq(u, s->clock_sequence);
111         set_reserved(u, 0x2);
112         set_node64(u, s->node);
113 }
114
115 static int obtain_global_lock(void *data) {
116         /* no-op */
117         return 0;
118 }
119
120 static int release_global_lock(void *data) {
121         /* no-op */
122         return 0;
123 }
124
125 #define GREGORIAN 122192928000000000ULL
126 static uint64_t current_time(void) {
127         uint64_t now;
128         struct timeval tv;
129         
130         /* TODO is this BSD specific? */
131         gettimeofday(&tv, 0);
132
133         now = (tv.tv_sec * 10000000ULL + tv.tv_usec * 10ULL) + GREGORIAN;
134         return now;
135 }
136
137 static void random_bytes(void *buf, size_t n) {
138         unsigned char *s = buf;
139         int i = 0;
140
141         while (i < n) {
142                 i += pd_uuid_rng_get_bytes(s+i, n-i);
143         }
144 }
145
146 static uint64_t random_mc_mac(struct pd_uuid_state  *s) {
147         uint64_t node = 0;
148
149         if (s && s->random_bytes) {
150                 s->random_bytes(&node, sizeof node, s->rng_state);
151         } else {
152                 random_bytes(&node, sizeof node);
153         }
154         node |= 0x800000000000ULL; /* rfc 4.5 */
155         return node;
156 }
157
158 /*
159  * TODO would probably make more sense to use a destination array
160  * rather than returning an integer
161  */
162 static uint64_t current_node(struct pd_uuid_state *st) {
163         uint64_t node = 0;
164
165 #ifdef __linux__
166         struct ifconf   conf;
167         struct ifreq    *req;
168         struct ifreq    interfaces[4];
169         int s, i;
170         unsigned char *data;
171
172         s = socket(AF_INET, SOCK_DGRAM, 0);
173         if (s != -1) {
174                 conf.ifc_len = sizeof interfaces;
175                 conf.ifc_req = interfaces;
176                 if (ioctl(s, SIOCGIFCONF, &conf) == 0)  {
177                         for (i=0; i < 4; i++) {
178                                 req = &conf.ifc_req[i];
179                                 if (ioctl(s, SIOCGIFFLAGS, req) != 0) {
180                                         continue;
181                                 }
182                                 if (req->ifr_flags & IFF_LOOPBACK) {
183                                         continue;
184                                 }
185                                 if (req->ifr_flags & IFF_NOARP) {
186                                         continue;
187                                 }
188                                 if (ioctl(s, SIOCGIFHWADDR, req) == 0) {
189                                         int j;
190                                         data = (unsigned char *)req->ifr_hwaddr.sa_data;
191                                         node = data[0];
192                                         node = node << 8; node += data[1];
193                                         node = node << 8; node += data[2];
194                                         node = node << 8; node += data[3];
195                                         node = node << 8; node += data[4];
196                                         node = node << 8; node += data[5];
197                                         return node;
198                                 }
199                         }
200                 }
201         }
202 #endif
203
204         /* this is any BSD based system i think */
205 #ifdef __APPLE__
206         struct ifaddrs *addrs;
207         struct ifaddrs *a;
208         struct sockaddr_dl *dl;
209         unsigned char *data;
210         if (getifaddrs(&addrs) == 0) {
211                 for (a = addrs; a; a = a->ifa_next) {
212
213                         if (a->ifa_addr && a->ifa_addr->sa_family == AF_LINK) {
214                                 dl = (struct sockaddr_dl *)a->ifa_addr;
215                                 data = (unsigned char *)(dl->sdl_data + dl->sdl_nlen);
216                                 if (dl->sdl_alen != 6) continue;
217
218                                 node = data[0];
219                                 node = node << 8; node += data[1];
220                                 node = node << 8; node += data[2];
221                                 node = node << 8; node += data[3];
222                                 node = node << 8; node += data[4];
223                                 node = node << 8; node += data[5];
224                                 freeifaddrs(addrs);
225                                 return node;
226                         }
227                 }
228                 freeifaddrs(addrs);
229         }
230 #endif
231
232 #if defined(WIN32) || defined(__CYGWIN__)
233         IP_ADAPTER_INFO addrs[4];
234         DWORD size;
235         int rc;
236         unsigned char *data;
237
238         size = sizeof addrs;
239         rc = GetAdaptersInfo(addrs, &size);
240         if (rc == ERROR_SUCCESS) {
241                 PIP_ADAPTER_INFO info;
242                 for (info = addrs; info; info = info->Next) {
243                         if (info->Type != MIB_IF_TYPE_ETHERNET) {
244                                 continue;
245                         }
246                         data = info->Address;
247                         node = data[0];
248                         node = node << 8; node += data[1];
249                         node = node << 8; node += data[2];
250                         node = node << 8; node += data[3];
251                         node = node << 8; node += data[4];
252                         node = node << 8; node += data[5];
253                 }
254                 return node;
255         }
256 #endif
257
258         /* if we get here, use a random multicast address */
259         node = random_mc_mac(st);
260         return node;
261 }
262
263 static uint16_t random_clock_sequence(struct pd_uuid_state *s) {
264         uint16_t seq;
265
266         if (s && s->random_bytes) {
267                 s->random_bytes(&seq, sizeof seq, s->rng_state);
268         } else {
269                 random_bytes(&seq, sizeof seq);
270         }
271         return seq;
272 }
273
274 static int read_state(struct pd_uuid_state *s) {
275         s->available = 0;
276         s->node = 0LL;
277         s->clock_sequence = 0;
278         s->timestamp = 0LL;
279
280         return 0;
281 }
282
283 static int save_state(struct pd_uuid_state *s) {
284         /* no op */
285         return 0;
286 }
287
288 static unsigned long get_bytes(void *buf, unsigned long n, void *state) {
289         unsigned char *s = buf;
290         int i = 0;
291
292         while (i < n) {
293                 i += pd_uuid_rng_get_bytes(s+i, n-i);
294         }
295         return i;
296 }
297
298 int pd_uuid_init_state(struct pd_uuid_state *s) {
299         if (!s) return 0;
300
301         s->get_lock = obtain_global_lock;
302         s->release_lock = release_global_lock;
303         s->lock_data = 0;
304
305         s->read_state = read_state;
306         s->save_state = save_state;
307
308         s->random_bytes = get_bytes;
309         s->rng_state = 0;
310
311         s->available = 0;
312
313         return 1;
314 }
315
316 int pd_uuid_make_v1(struct pd_uuid_state *s, pd_uuid_t *uuid) {
317         struct pd_uuid_state    ls;
318         uint64_t        now;
319         uint64_t        node;
320         int err;
321
322         if (!s) {
323                 s = &ls;
324                 pd_uuid_init_state(s);
325         }
326
327         if (s->get_lock) {
328                 if ((err = s->get_lock(s->lock_data)) != 0) {
329                         /* TODO set uuid to nil ? */
330                         /* be cute and have an "error" uuid? */
331                         return 0;
332                 }
333         }
334
335         if (s->read_state) {
336                 if ((err = s->read_state(s)) != 0) {
337                         return 0;
338                 }
339         }
340
341         now = current_time();
342         node = current_node(s);
343
344         if (!s->available || s->node != node) {
345                 s->clock_sequence = random_clock_sequence(s);
346         }
347         s->node = node;
348
349         if (s->available && s->timestamp > now) {
350                 s->clock_sequence++;
351         } else {
352                 s->timestamp = now;
353         }
354
355         if (s->save_state) {
356                 if ((err = s->save_state(s)) != 0) {
357                         return 0;
358                 }
359         }
360
361         if (s->release_lock) {
362                 if ((err = s->release_lock(s->lock_data)) != 0) {
363                         /* TODO set uuid to nil ? */
364                         /* be cute and have an "error" uuid? */
365                         return 0;
366                 }
367         }
368
369         format_uuid(uuid, s, 1);
370
371         return 1;
372 }
373
374 int pd_uuid_make_v1mc(struct pd_uuid_state *ps, pd_uuid_t *uuid) {
375         struct pd_uuid_state    s;
376         uint64_t        now;
377         uint64_t        node;
378
379         obtain_global_lock(0);
380         read_state(&s);
381         now = current_time();
382         node = random_mc_mac(&s);
383         if (!s.available) {
384                 s.clock_sequence = random_clock_sequence(&s);
385         }
386
387         if (s.available && s.timestamp > now) {
388                 s.clock_sequence++;
389         } else {
390                 s.timestamp = now;
391         }
392
393         save_state(&s);
394         release_global_lock(0);
395         s.node = node;
396
397         format_uuid(uuid, &s, 1);
398
399         return 1;
400 }
401
402 int pd_uuid_make_v4(struct pd_uuid_state *s, pd_uuid_t *uuid) {
403         random_bytes(uuid, sizeof *uuid);
404         set_version(uuid, 4);
405         set_reserved(uuid, 0x2);
406         return 1;
407 }
408
409 int pd_set_uuid_hash(struct pd_uuid *s, void *hash, int version) {
410         int i;
411         unsigned char *h = hash;
412
413         for (i=0;i<16;i++) {
414                 s->data[i] = h[i];
415         }
416         set_version(s, version);
417         set_reserved(s, 0x2);
418         return 1;
419 }
420
421 int pd_uuid_make_v3(struct pd_uuid_state *s, pd_uuid_t *uuid, pd_uuid_t *ns, void *data, int len) {
422         hash_state hs;
423         unsigned char hash[16];
424
425         md5_init(&hs);
426         md5_process(&hs, ns->data, 16);
427         md5_process(&hs, data, len);
428         md5_done(&hs, hash);
429         pd_set_uuid_hash(uuid, hash, 3);
430         return 1;
431 }
432
433 int pd_uuid_make_v5(struct pd_uuid_state *s, pd_uuid_t *uuid, pd_uuid_t *ns, void *data, int len) {
434         hash_state hs;
435         unsigned char hash[20];
436
437         sha1_init(&hs);
438         sha1_process(&hs, ns->data, sizeof *ns);
439         sha1_process(&hs, data, len);
440         sha1_done(&hs, hash);
441         pd_set_uuid_hash(uuid, hash, 5);
442         return 1;
443 }
444
445 int pd_uuid_set_string(pd_uuid_t *uuid, char *s) {
446         unsigned int byte;
447         int i;
448
449         for (i=0;i<16;i++) {
450                 if (*s == 0) return 0;
451                 if (*s == '-') s++;
452
453                 if (sscanf(s, "%02x", &byte) != 1) {
454                         return 0;
455                 }
456                 s += 2;
457                 uuid->data[i] = byte & 0xff;
458         }
459         return 1;
460 }