1/* MiniDLNA project 2 * 3 * http://sourceforge.net/projects/minidlna/ 4 * 5 * Much of this code and ideas for this code have been taken 6 * from Helge Deller's proposed Linux kernel patch (which 7 * apparently never made it upstream), and some from Busybox. 8 * 9 * MiniDLNA media server 10 * Copyright (C) 2009 Justin Maggard 11 * 12 * This file is part of MiniDLNA. 13 * 14 * MiniDLNA is free software; you can redistribute it and/or modify 15 * it under the terms of the GNU General Public License version 2 as 16 * published by the Free Software Foundation. 17 * 18 * MiniDLNA is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU General Public License for more details. 22 * 23 * You should have received a copy of the GNU General Public License 24 * along with MiniDLNA. If not, see <http://www.gnu.org/licenses/>. 25 */ 26#include "config.h" 27#include <stdio.h> 28#include <stdlib.h> 29#include <time.h> 30#include <fcntl.h> 31#include <unistd.h> 32#include <string.h> 33#include <sys/ioctl.h> 34#include <sys/time.h> 35#include <errno.h> 36#if HAVE_MACH_MACH_TIME_H 37#include <mach/mach_time.h> 38#elif HAVE_CLOCK_GETTIME_SYSCALL 39#include <sys/syscall.h> 40#endif 41 42#include "uuid.h" 43#include "getifaddr.h" 44#include "log.h" 45 46static uint32_t clock_seq; 47static const uint32_t clock_seq_max = 0x3fff; /* 14 bits */ 48static int clock_seq_initialized; 49 50#ifndef CLOCK_MONOTONIC 51#define CLOCK_MONOTONIC CLOCK_REALTIME 52#endif 53 54unsigned long long 55monotonic_us(void) 56{ 57 struct timespec ts; 58 59#if HAVE_CLOCK_GETTIME 60 clock_gettime(CLOCK_MONOTONIC, &ts); 61#elif HAVE_CLOCK_GETTIME_SYSCALL 62 syscall(__NR_clock_gettime, CLOCK_MONOTONIC, &ts); 63#elif HAVE_MACH_MACH_TIME_H 64 return mach_absolute_time(); 65#else 66 struct timeval tv; 67 gettimeofday(&tv, 0); 68 TIMEVAL_TO_TIMESPEC(&tv, &ts); 69#endif 70 return ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000; 71} 72 73int 74read_bootid_node(unsigned char *buf, size_t size) 75{ 76 FILE *boot_id; 77 78 if(size != 6) 79 return -1; 80 81 boot_id = fopen("/proc/sys/kernel/random/boot_id", "r"); 82 if(!boot_id) 83 return -1; 84 if((fseek(boot_id, 24, SEEK_SET) < 0) || 85 (fscanf(boot_id, "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", 86 &buf[0], &buf[1], &buf[2], &buf[3], &buf[4], &buf[5]) != 6)) 87 { 88 fclose(boot_id); 89 return -1; 90 } 91 92 fclose(boot_id); 93 return 0; 94} 95 96static void 97read_random_bytes(unsigned char *buf, size_t size) 98{ 99 int i; 100 pid_t pid; 101 102 i = open("/dev/urandom", O_RDONLY); 103 if(i >= 0) 104 { 105 if (read(i, buf, size) == -1) 106 DPRINTF(E_MAXDEBUG, L_GENERAL, "Failed to read random bytes\n"); 107 close(i); 108 } 109 /* Paranoia. /dev/urandom may be missing. 110 * rand() is guaranteed to generate at least [0, 2^15) range, 111 * but lowest bits in some libc are not so "random". */ 112 srand(monotonic_us()); 113 pid = getpid(); 114 while(1) 115 { 116 for(i = 0; i < size; i++) 117 buf[i] ^= rand() >> 5; 118 if(pid == 0) 119 break; 120 srand(pid); 121 pid = 0; 122 } 123} 124 125void 126init_clockseq(void) 127{ 128 unsigned char buf[4]; 129 130 read_random_bytes(buf, 4); 131 memcpy(&clock_seq, &buf, sizeof(clock_seq)); 132 clock_seq &= clock_seq_max; 133 clock_seq_initialized = 1; 134} 135 136int 137generate_uuid(unsigned char uuid_out[16]) 138{ 139 static uint64_t last_time_all; 140 static unsigned int clock_seq_started; 141 static char last_node[6] = { 0, 0, 0, 0, 0, 0 }; 142 143 struct timespec ts; 144 uint64_t time_all; 145 int inc_clock_seq = 0; 146 147 unsigned char mac[6]; 148 int mac_error; 149 150 memset(&mac, '\0', sizeof(mac)); 151 /* Get the spatially unique node identifier */ 152 153 mac_error = getsyshwaddr((char *)mac, sizeof(mac)); 154 155 if(!mac_error) 156 { 157 memcpy(&uuid_out[10], mac, ETH_ALEN); 158 } 159 else 160 { 161 /* use bootid's nodeID if no network interface found */ 162 DPRINTF(E_INFO, L_HTTP, "Could not find MAC. Use bootid's nodeID.\n"); 163 if( read_bootid_node(&uuid_out[10], 6) != 0) 164 { 165 DPRINTF(E_INFO, L_HTTP, "bootid node not successfully read.\n"); 166 read_random_bytes(&uuid_out[10], 6); 167 } 168 } 169 170 if(memcmp(last_node, uuid_out+10, 6) != 0) 171 { 172 inc_clock_seq = 1; 173 memcpy(last_node, uuid_out+10, 6); 174 } 175 176 /* Determine 60-bit timestamp value. For UUID version 1, this is 177 * represented by Coordinated Universal Time (UTC) as a count of 100- 178 * nanosecond intervals since 00:00:00.00, 15 October 1582 (the date of 179 * Gregorian reform to the Christian calendar). 180 */ 181#if HAVE_CLOCK_GETTIME 182 clock_gettime(CLOCK_REALTIME, &ts); 183#elif HAVE_CLOCK_GETTIME_SYSCALL 184 syscall(__NR_clock_gettime, CLOCK_REALTIME, &ts); 185#else 186 struct timeval tv; 187 gettimeofday(&tv, 0); 188 TIMEVAL_TO_TIMESPEC(&tv, &ts); 189#endif 190 time_all = ((uint64_t)ts.tv_sec) * (NSEC_PER_SEC / 100); 191 time_all += ts.tv_nsec / 100; 192 193 /* add offset from Gregorian Calendar to Jan 1 1970 */ 194 time_all += 12219292800000ULL * (NSEC_PER_MSEC / 100); 195 time_all &= 0x0fffffffffffffffULL; /* limit to 60 bits */ 196 197 /* Determine clock sequence (max. 14 bit) */ 198 if(!clock_seq_initialized) 199 { 200 init_clockseq(); 201 clock_seq_started = clock_seq; 202 } 203 else 204 { 205 if(inc_clock_seq || time_all <= last_time_all) 206 { 207 clock_seq = (clock_seq + 1) & clock_seq_max; 208 if(clock_seq == clock_seq_started) 209 { 210 clock_seq = (clock_seq - 1) & clock_seq_max; 211 } 212 } 213 else 214 clock_seq_started = clock_seq; 215 } 216 last_time_all = time_all; 217 218 /* Fill in timestamp and clock_seq values */ 219 uuid_out[3] = (uint8_t)time_all; 220 uuid_out[2] = (uint8_t)(time_all >> 8); 221 uuid_out[1] = (uint8_t)(time_all >> 16); 222 uuid_out[0] = (uint8_t)(time_all >> 24); 223 uuid_out[5] = (uint8_t)(time_all >> 32); 224 uuid_out[4] = (uint8_t)(time_all >> 40); 225 uuid_out[7] = (uint8_t)(time_all >> 48); 226 uuid_out[6] = (uint8_t)(time_all >> 56); 227 228 uuid_out[8] = clock_seq >> 8; 229 uuid_out[9] = clock_seq & 0xff; 230 231 /* Set UUID version to 1 --- time-based generation */ 232 uuid_out[6] = (uuid_out[6] & 0x0F) | 0x10; 233 /* Set the UUID variant to DCE */ 234 uuid_out[8] = (uuid_out[8] & 0x3F) | 0x80; 235 236 return 0; 237} 238 239/* Places a null-terminated 37-byte time-based UUID string in the buffer pointer to by buf. 240 * A large enough buffer must already be allocated. */ 241int 242get_uuid_string(char *buf) 243{ 244 unsigned char uuid[16]; 245 246 if( generate_uuid(uuid) != 0 ) 247 return -1; 248 249 sprintf(buf, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", 250 uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], uuid[8], 251 uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); 252 buf[36] = '\0'; 253 254 return 0; 255} 256