1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/* 18 * This attempts to generate V1 UUIDs according to the Internet Draft 19 * located at http://www.webdav.org/specs/draft-leach-uuids-guids-01.txt 20 */ 21#include "apr.h" 22#include "apr_uuid.h" 23#include "apr_md5.h" 24#include "apr_general.h" 25#include "apr_portable.h" 26 27 28#if APR_HAVE_UNISTD_H 29#include <unistd.h> /* for getpid, gethostname */ 30#endif 31#if APR_HAVE_STDLIB_H 32#include <stdlib.h> /* for rand, srand */ 33#endif 34 35 36#if APR_HAVE_STRING_H 37#include <string.h> 38#endif 39#if APR_HAVE_STRINGS_H 40#include <strings.h> 41#endif 42#if APR_HAVE_NETDB_H 43#include <netdb.h> 44#endif 45#if APR_HAVE_SYS_TIME_H 46#include <sys/time.h> /* for gettimeofday */ 47#endif 48 49#define NODE_LENGTH 6 50 51static int uuid_state_seqnum; 52static unsigned char uuid_state_node[NODE_LENGTH] = { 0 }; 53 54 55static void get_random_info(unsigned char node[NODE_LENGTH]) 56{ 57#if APR_HAS_RANDOM 58 59 (void) apr_generate_random_bytes(node, NODE_LENGTH); 60 61#else 62 63 unsigned char seed[APR_MD5_DIGESTSIZE]; 64 apr_md5_ctx_t c; 65 66 /* ### probably should revise some of this to be a bit more portable */ 67 68 /* Leach & Salz use Linux-specific struct sysinfo; 69 * replace with pid/tid for portability (in the spirit of mod_unique_id) */ 70 struct { 71 /* Add thread id here, if applicable, when we get to pthread or apr */ 72 pid_t pid; 73#ifdef NETWARE 74 apr_uint64_t t; 75#else 76 struct timeval t; 77#endif 78 char hostname[257]; 79 80 } r; 81 82 apr_md5_init(&c); 83#ifdef NETWARE 84 r.pid = NXThreadGetId(); 85 NXGetTime(NX_SINCE_BOOT, NX_USECONDS, &(r.t)); 86#else 87 r.pid = getpid(); 88 gettimeofday(&r.t, (struct timezone *)0); 89#endif 90 gethostname(r.hostname, 256); 91 apr_md5_update(&c, (const unsigned char *)&r, sizeof(r)); 92 apr_md5_final(seed, &c); 93 94 memcpy(node, seed, NODE_LENGTH); /* use a subset of the seed bytes */ 95#endif 96} 97 98/* This implementation generates a random node ID instead of a 99 system-dependent call to get IEEE node ID. This is also more secure: 100 we aren't passing out our MAC address. 101*/ 102static void get_pseudo_node_identifier(unsigned char *node) 103{ 104 get_random_info(node); 105 node[0] |= 0x01; /* this designates a random multicast node ID */ 106} 107 108static void get_system_time(apr_uint64_t *uuid_time) 109{ 110 /* ### fix this call to be more portable? */ 111 *uuid_time = apr_time_now(); 112 113 /* Offset between UUID formatted times and Unix formatted times. 114 UUID UTC base time is October 15, 1582. 115 Unix base time is January 1, 1970. */ 116 *uuid_time = (*uuid_time * 10) + APR_TIME_C(0x01B21DD213814000); 117} 118 119/* true_random -- generate a crypto-quality random number. */ 120static int true_random(void) 121{ 122 apr_uint64_t time_now; 123 124#if APR_HAS_RANDOM 125 unsigned char buf[2]; 126 127 if (apr_generate_random_bytes(buf, 2) == APR_SUCCESS) { 128 return (buf[0] << 8) | buf[1]; 129 } 130#endif 131 132 /* crap. this isn't crypto quality, but it will be Good Enough */ 133 134 time_now = apr_time_now(); 135 srand((unsigned int)(((time_now >> 32) ^ time_now) & 0xffffffff)); 136 137 return rand() & 0x0FFFF; 138} 139 140static void init_state(void) 141{ 142 uuid_state_seqnum = true_random(); 143 get_pseudo_node_identifier(uuid_state_node); 144} 145 146static void get_current_time(apr_uint64_t *timestamp) 147{ 148 /* ### this needs to be made thread-safe! */ 149 150 apr_uint64_t time_now; 151 static apr_uint64_t time_last = 0; 152 static apr_uint64_t fudge = 0; 153 154 get_system_time(&time_now); 155 156 /* if clock reading changed since last UUID generated... */ 157 if (time_last != time_now) { 158 /* The clock reading has changed since the last UUID was generated. 159 Reset the fudge factor. if we are generating them too fast, then 160 the fudge may need to be reset to something greater than zero. */ 161 if (time_last + fudge > time_now) 162 fudge = time_last + fudge - time_now + 1; 163 else 164 fudge = 0; 165 time_last = time_now; 166 } 167 else { 168 /* We generated two really fast. Bump the fudge factor. */ 169 ++fudge; 170 } 171 172 *timestamp = time_now + fudge; 173} 174 175APU_DECLARE(void) apr_uuid_get(apr_uuid_t *uuid) 176{ 177 apr_uint64_t timestamp; 178 unsigned char *d = uuid->data; 179 180#if APR_HAS_OS_UUID 181 if (apr_os_uuid_get(d) == APR_SUCCESS) { 182 return; 183 } 184#endif /* !APR_HAS_OS_UUID */ 185 186 if (!uuid_state_node[0]) 187 init_state(); 188 189 get_current_time(×tamp); 190 191 /* time_low, uint32 */ 192 d[3] = (unsigned char)timestamp; 193 d[2] = (unsigned char)(timestamp >> 8); 194 d[1] = (unsigned char)(timestamp >> 16); 195 d[0] = (unsigned char)(timestamp >> 24); 196 /* time_mid, uint16 */ 197 d[5] = (unsigned char)(timestamp >> 32); 198 d[4] = (unsigned char)(timestamp >> 40); 199 /* time_hi_and_version, uint16 */ 200 d[7] = (unsigned char)(timestamp >> 48); 201 d[6] = (unsigned char)(((timestamp >> 56) & 0x0F) | 0x10); 202 /* clock_seq_hi_and_reserved, uint8 */ 203 d[8] = (unsigned char)(((uuid_state_seqnum >> 8) & 0x3F) | 0x80); 204 /* clock_seq_low, uint8 */ 205 d[9] = (unsigned char)uuid_state_seqnum; 206 /* node, byte[6] */ 207 memcpy(&d[10], uuid_state_node, NODE_LENGTH); 208} 209