1/* MiniDLNA project
2 *
3 * http://sourceforge.net/projects/minidlna/
4 * (c) 2009 Justin Maggard
5 * This software is subject to the conditions detailed
6 * in the LICENCE file provided within the distribution
7 *
8 * Much of this code and ideas for this code have been taken
9 * from Helge Deller's proposed Linux kernel patch (which
10 * apparently never made it upstream), and some from Busybox.
11 */
12#include <stdio.h>
13#include <stdlib.h>
14#include <time.h>
15#include <fcntl.h>
16#include <unistd.h>
17#include <sys/syscall.h>
18#include <string.h>
19#include <net/if.h>
20#include <sys/ioctl.h>
21#include <sys/time.h>
22#include <errno.h>
23
24#include "getifaddr.h"
25#include "log.h"
26
27#define ETH_ALEN 6
28#define NSEC_PER_SEC 1000000000L
29#define NSEC_PER_MSEC 1000000L
30
31static u_int32_t clock_seq;
32static const u_int32_t clock_seq_max = 0x3fff; /* 14 bits */
33static int clock_seq_initialized;
34
35unsigned long long
36monotonic_us(void)
37{
38	struct timespec ts;
39
40	syscall(__NR_clock_gettime, CLOCK_MONOTONIC, &ts);
41	return ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000;
42}
43
44int
45read_bootid_node(unsigned char *buf, size_t size)
46{
47	FILE *boot_id;
48
49	if(size != 6)
50		return -1;
51
52	boot_id = fopen("/proc/sys/kernel/random/boot_id", "r");
53	if(!boot_id)
54		return -1;
55	if((fseek(boot_id, 24, SEEK_SET) < 0) ||
56	   (fscanf(boot_id, "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
57		   &buf[0], &buf[1], &buf[2], &buf[3], &buf[4], &buf[5]) != 6))
58	{
59		fclose(boot_id);
60		return -1;
61	}
62
63	fclose(boot_id);
64	return 0;
65}
66
67static void
68read_random_bytes(unsigned char *buf, size_t size)
69{
70	int i;
71	pid_t pid;
72
73	i = open("/dev/urandom", O_RDONLY);
74	if(i >= 0)
75	{
76		read(i, buf, size);
77		close(i);
78	}
79	/* Paranoia. /dev/urandom may be missing.
80	 * rand() is guaranteed to generate at least [0, 2^15) range,
81	 * but lowest bits in some libc are not so "random".  */
82	srand(monotonic_us());
83	pid = getpid();
84	while(1)
85	{
86		for(i = 0; i < size; i++)
87			buf[i] ^= rand() >> 5;
88		if(pid == 0)
89			break;
90		srand(pid);
91		pid = 0;
92	}
93}
94
95void
96init_clockseq(void)
97{
98	unsigned char buf[4];
99
100	read_random_bytes(buf, 4);
101	memcpy(&clock_seq, &buf, sizeof(clock_seq));
102	clock_seq &= clock_seq_max;
103	clock_seq_initialized = 1;
104}
105
106int
107generate_uuid(unsigned char uuid_out[16])
108{
109	static u_int64_t last_time_all;
110	static unsigned int clock_seq_started;
111	static char last_node[6] = { 0, 0, 0, 0, 0, 0 };
112
113	struct timespec ts;
114	u_int64_t time_all;
115	int inc_clock_seq = 0;
116
117	unsigned char mac[6];
118	int mac_error;
119
120	memset(&mac, '\0', sizeof(mac));
121	/* Get the spatially unique node identifier */
122
123	mac_error = getsyshwaddr((char *)mac, sizeof(mac));
124
125	if(!mac_error)
126	{
127		memcpy(&uuid_out[10], mac, ETH_ALEN);
128	}
129	else
130	{
131		/* use bootid's nodeID if no network interface found */
132		DPRINTF(E_INFO, L_HTTP, "Could not find MAC.  Use bootid's nodeID.\n");
133		if( read_bootid_node(&uuid_out[10], 6) != 0)
134		{
135			DPRINTF(E_INFO, L_HTTP, "bootid node not successfully read.\n");
136			read_random_bytes(&uuid_out[10], 6);
137		}
138	}
139
140	if(memcmp(last_node, uuid_out+10, 6) != 0)
141	{
142		inc_clock_seq = 1;
143		memcpy(last_node, uuid_out+10, 6);
144	}
145
146	/* Determine 60-bit timestamp value. For UUID version 1, this is
147	 * represented by Coordinated Universal Time (UTC) as a count of 100-
148	 * nanosecond intervals since 00:00:00.00, 15 October 1582 (the date of
149	 * Gregorian reform to the Christian calendar).
150	 */
151	syscall(__NR_clock_gettime, CLOCK_REALTIME, &ts);
152	time_all = ((u_int64_t)ts.tv_sec) * (NSEC_PER_SEC / 100);
153	time_all += ts.tv_nsec / 100;
154
155	/* add offset from Gregorian Calendar to Jan 1 1970 */
156	time_all += 12219292800000ULL * (NSEC_PER_MSEC / 100);
157	time_all &= 0x0fffffffffffffffULL; /* limit to 60 bits */
158
159	/* Determine clock sequence (max. 14 bit) */
160	if(!clock_seq_initialized)
161	{
162		init_clockseq();
163		clock_seq_started = clock_seq;
164	}
165	else
166	{
167		if(inc_clock_seq || time_all <= last_time_all)
168		{
169			clock_seq = (clock_seq + 1) & clock_seq_max;
170			if(clock_seq == clock_seq_started)
171			{
172				clock_seq = (clock_seq - 1) & clock_seq_max;
173			}
174		}
175		else
176			clock_seq_started = clock_seq;
177	}
178	last_time_all = time_all;
179
180	/* Fill in timestamp and clock_seq values */
181	uuid_out[3] = (u_int8_t)time_all;
182	uuid_out[2] = (u_int8_t)(time_all >> 8);
183	uuid_out[1] = (u_int8_t)(time_all >> 16);
184	uuid_out[0] = (u_int8_t)(time_all >> 24);
185	uuid_out[5] = (u_int8_t)(time_all >> 32);
186	uuid_out[4] = (u_int8_t)(time_all >> 40);
187	uuid_out[7] = (u_int8_t)(time_all >> 48);
188	uuid_out[6] = (u_int8_t)(time_all >> 56);
189
190	uuid_out[8] = clock_seq >> 8;
191	uuid_out[9] = clock_seq & 0xff;
192
193	/* Set UUID version to 1 --- time-based generation */
194	uuid_out[6] = (uuid_out[6] & 0x0F) | 0x10;
195	/* Set the UUID variant to DCE */
196	uuid_out[8] = (uuid_out[8] & 0x3F) | 0x80;
197
198	return 0;
199}
200
201/* Places a null-terminated 37-byte time-based UUID string in the buffer pointer to by buf.
202 * A large enough buffer must already be allocated. */
203int
204get_uuid_string(char *buf)
205{
206	unsigned char uuid[16];
207
208	if( generate_uuid(uuid) != 0 )
209		return -1;
210
211	sprintf(buf, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
212	        uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], uuid[8],
213	        uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
214	buf[36] = '\0';
215
216	return 0;
217}
218