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