/* * internal functions for uuid library * * written by nathan wagner and placed in the public domain */ #include #include #include #include #include #include "hash.h" #include "pduuid.h" #ifdef __linux__ #include #include #include #include #include #include #endif #ifdef __APPLE__ #include #include #include #include #endif #ifdef WIN32 #include #include #endif #ifdef __CYGWIN__ #include #include #endif #if 0 static void pnode(uint64_t node) { unsigned int bytes[6]; int i; for (i=0;i<6;i++) { bytes[5-i] = node & 0xff; node >>= 8; } fprintf(stderr, "%02x", bytes[0]); for (i=1;i<6;i++) { fprintf(stderr, ":%02x", bytes[i]); } fprintf(stderr, "\n"); } #endif static void set_time_low(pd_uuid_t *u, uint32_t low) { u->data[0] = (low >> 24) & 0xff; u->data[1] = (low >> 16) & 0xff; u->data[2] = (low >> 8) & 0xff; u->data[3] = low & 0xff; } static void set_time_mid(pd_uuid_t *u, uint16_t mid) { u->data[4] = (mid >> 8) & 0xff; u->data[5] = mid & 0xff; } static void set_time_hi(pd_uuid_t *u, uint16_t hi) { u->data[7] = hi & 0xff; u->data[6] &= 0xf0; u->data[6] |= (hi >> 8) & 0xf; } static void set_version(pd_uuid_t *u, uint8_t version) { u->data[6] &= 0xf; u->data[6] |= (version & 0xf) << 4; } static void set_clock_seq_low(pd_uuid_t *u, uint8_t low) { u->data[9] = low & 0xff; } static void set_clock_seq_hi(pd_uuid_t *u, uint8_t hi) { u->data[8] &= 0xc0; u->data[8] |= hi & 0x3f; } static void set_reserved(pd_uuid_t *u, uint8_t reserved) { u->data[8] &= 0x3f; u->data[8] |= (reserved & 0x3) << 6; } #if 0 static void set_node(pd_uuid_t *u, uint8_t data[6]) { u->data[10] = data[0]; u->data[11] = data[1]; u->data[12] = data[2]; u->data[13] = data[3]; u->data[14] = data[4]; u->data[15] = data[5]; } #endif static void set_node64(pd_uuid_t *u, uint64_t node) { u->data[10] = (node >> 40) & 0xff; u->data[11] = (node >> 32) & 0xff; u->data[12] = (node >> 24) & 0xff; u->data[13] = (node >> 16) & 0xff; u->data[14] = (node >> 8) & 0xff; u->data[15] = (node >> 0) & 0xff; } static void set_timestamp(pd_uuid_t *u, uint64_t ts) { set_time_low(u, ts & 0xffffffff); set_time_mid(u, (ts >> 32) & 0xffff); set_time_hi(u, (ts >> 48) & 0xfff); } static void set_clockseq(pd_uuid_t *u, uint16_t seq) { set_clock_seq_low(u, seq & 0xff); set_clock_seq_hi(u, (seq >> 8) & 0xff); } static void format_uuid(pd_uuid_t *u, struct pd_uuid_state *s, int version) { set_timestamp(u, s->timestamp); set_version(u, version); set_clockseq(u, s->clock_sequence); set_reserved(u, 0x2); set_node64(u, s->node); } static int obtain_global_lock(void *data) { /* no-op */ return 0; } static int release_global_lock(void *data) { /* no-op */ return 0; } #define GREGORIAN 122192928000000000ULL static uint64_t current_time(void) { uint64_t now; struct timeval tv; static int seq = 0; /* TODO is this BSD specific? */ gettimeofday(&tv, 0); now = (tv.tv_sec * 10000000ULL + tv.tv_usec * 10ULL + seq++ % 10) + GREGORIAN; return now; } static void random_bytes(void *buf, size_t n) { unsigned char *s = buf; int i = 0; while (i < n) { i += pd_uuid_rng_get_bytes(s+i, n-i); } } static uint64_t random_mc_mac(struct pd_uuid_state *s) { uint64_t node = 0; if (s && s->random_bytes) { s->random_bytes(&node, sizeof node, s->rng_state); } else { random_bytes(&node, sizeof node); } node |= 0x800000000000ULL; /* rfc 4.5 */ return node; } /* * TODO would probably make more sense to use a destination array * rather than returning an integer */ static uint64_t current_node(struct pd_uuid_state *st) { uint64_t node = 0; #ifdef __linux__ struct ifconf conf; struct ifreq *req; struct ifreq interfaces[4]; int s, i; unsigned char *data; s = socket(AF_INET, SOCK_DGRAM, 0); if (s != -1) { conf.ifc_len = sizeof interfaces; conf.ifc_req = interfaces; if (ioctl(s, SIOCGIFCONF, &conf) == 0) { for (i=0; i < 4; i++) { req = &conf.ifc_req[i]; if (ioctl(s, SIOCGIFFLAGS, req) != 0) { continue; } if (req->ifr_flags & IFF_LOOPBACK) { continue; } if (req->ifr_flags & IFF_NOARP) { continue; } if (ioctl(s, SIOCGIFHWADDR, req) == 0) { data = (unsigned char *)req->ifr_hwaddr.sa_data; node = data[0]; node = node << 8; node += data[1]; node = node << 8; node += data[2]; node = node << 8; node += data[3]; node = node << 8; node += data[4]; node = node << 8; node += data[5]; close(s); return node; } else { fprintf(stderr, "ioctl to read address failed: %d\n", errno); } } } close(s); } #endif /* this is any BSD based system i think */ #ifdef __APPLE__ struct ifaddrs *addrs; struct ifaddrs *a; struct sockaddr_dl *dl; unsigned char *data; if (getifaddrs(&addrs) == 0) { for (a = addrs; a; a = a->ifa_next) { if (a->ifa_addr && a->ifa_addr->sa_family == AF_LINK) { dl = (struct sockaddr_dl *)a->ifa_addr; data = (unsigned char *)(dl->sdl_data + dl->sdl_nlen); if (dl->sdl_alen != 6) continue; node = data[0]; node = node << 8; node += data[1]; node = node << 8; node += data[2]; node = node << 8; node += data[3]; node = node << 8; node += data[4]; node = node << 8; node += data[5]; freeifaddrs(addrs); return node; } } freeifaddrs(addrs); } #endif #if defined(WIN32) || defined(__CYGWIN__) IP_ADAPTER_INFO addrs[4]; DWORD size; int rc; unsigned char *data; size = sizeof addrs; rc = GetAdaptersInfo(addrs, &size); if (rc == ERROR_SUCCESS) { PIP_ADAPTER_INFO info; for (info = addrs; info; info = info->Next) { if (info->Type != MIB_IF_TYPE_ETHERNET) { continue; } data = info->Address; node = data[0]; node = node << 8; node += data[1]; node = node << 8; node += data[2]; node = node << 8; node += data[3]; node = node << 8; node += data[4]; node = node << 8; node += data[5]; } return node; } #endif /* if we get here, use a random multicast address */ node = random_mc_mac(st); return node; } static uint16_t random_clock_sequence(struct pd_uuid_state *s) { uint16_t seq; if (s && s->random_bytes) { s->random_bytes(&seq, sizeof seq, s->rng_state); } else { random_bytes(&seq, sizeof seq); } return seq; } static int read_state(struct pd_uuid_state *s) { uint64_t node; s->available = 0; node = current_node(s); if (!s->available || s->node != node) { s->clock_sequence = random_clock_sequence(s); } s->node = node; return 0; } static int save_state(struct pd_uuid_state *s) { /* no op */ return 0; } static unsigned long get_bytes(void *buf, unsigned long n, void *state) { unsigned char *s = buf; int i = 0; while (i < n) { i += pd_uuid_rng_get_bytes(s+i, n-i); } return i; } int pd_uuid_init(struct pd_uuid_state *s, int flags) { if (!s) return 0; s->get_lock = obtain_global_lock; s->release_lock = release_global_lock; s->lock_data = 0; if (flags & 0x1) { s->read_state = 0; s->save_state = 0; s->node = current_node(s); } else { s->read_state = read_state; s->save_state = 0; } s->random_bytes = get_bytes; s->rng_state = 0; s->available = 0; return 1; } int pd_uuid_init_state(struct pd_uuid_state *s) { if (!s) return 0; s->get_lock = obtain_global_lock; s->release_lock = release_global_lock; s->lock_data = 0; s->read_state = read_state; s->save_state = save_state; s->random_bytes = get_bytes; s->rng_state = 0; s->node = 0; s->available = 0; return 1; } static int pd_uuid_make_v1_any(struct pd_uuid_state *s, pd_uuid_t *uuid, int rmac); int pd_uuid_make_v1(struct pd_uuid_state *s, pd_uuid_t *uuid) { return pd_uuid_make_v1_any(s, uuid, 0); } int pd_uuid_make_v1mc(struct pd_uuid_state *s, pd_uuid_t *uuid) { return pd_uuid_make_v1_any(s, uuid, 1); } static int pd_uuid_make_v1_any(struct pd_uuid_state *s, pd_uuid_t *uuid, int rmac) { struct pd_uuid_state ls; uint64_t now; uint64_t node; int err; if (!s) { s = &ls; pd_uuid_init_state(s); } if (s->get_lock) { if ((err = s->get_lock(s->lock_data)) != 0) { /* TODO set uuid to nil ? */ /* be cute and have an "error" uuid? */ return 0; } } if (s->read_state) { if ((err = s->read_state(s)) != 0) { return 0; } } now = current_time(); if (rmac) { node = random_mc_mac(s); } else { node = current_node(s); } if (!s->available || s->node != node) { s->clock_sequence = random_clock_sequence(s); } s->node = node; if (s->available && s->timestamp > now) { s->clock_sequence++; } else { s->timestamp = now; } if (s->save_state) { if ((err = s->save_state(s)) != 0) { return 0; } } if (s->release_lock) { if ((err = s->release_lock(s->lock_data)) != 0) { /* TODO set uuid to nil ? */ /* be cute and have an "error" uuid? */ return 0; } } format_uuid(uuid, s, 1); return 1; } int pd_uuid_make_v4(struct pd_uuid_state *s, pd_uuid_t *uuid) { random_bytes(uuid, sizeof *uuid); set_version(uuid, 4); set_reserved(uuid, 0x2); return 1; } int pd_set_uuid_hash(struct pd_uuid *s, void *hash, int version) { int i; unsigned char *h = hash; for (i=0;i<16;i++) { s->data[i] = h[i]; } set_version(s, version); set_reserved(s, 0x2); return 1; } int pd_uuid_make_v3(struct pd_uuid_state *s, pd_uuid_t *uuid, pd_uuid_t *ns, void *data, int len) { hash_state hs; unsigned char hash[16]; md5_init(&hs); md5_process(&hs, ns->data, 16); md5_process(&hs, data, len); md5_done(&hs, hash); pd_set_uuid_hash(uuid, hash, 3); return 1; } int pd_uuid_make_v5(struct pd_uuid_state *s, pd_uuid_t *uuid, pd_uuid_t *ns, void *data, int len) { hash_state hs; unsigned char hash[20]; sha1_init(&hs); sha1_process(&hs, ns->data, sizeof *ns); sha1_process(&hs, data, len); sha1_done(&hs, hash); pd_set_uuid_hash(uuid, hash, 5); return 1; } /* * s must point to enough space, i.e. at least 37 bytes. * this is constrained enough that sprintf could * probably be avoided */ char *pd_uuid_get_string(pd_uuid_t *uuid, char *s) { char *r; int i; r = s; for (i=0;i<16;i++) { r += sprintf(r, "%.2x", (int)uuid->data[i]); if (i == 3 || i == 5 || i == 7 || i == 9) { *r++ = '-'; *r = 0; } } return s; } /* * might be faster to sscanf in four bytes at a time * and using htonl() * A nybble loop might be faster yet. */ int pd_uuid_set_string(pd_uuid_t *uuid, char *s) { unsigned int byte; int i; for (i=0;i<16;i++) { if (*s == 0) return 0; if (*s == '-') s++; if (sscanf(s, "%02x", &byte) != 1) { return 0; } s += 2; uuid->data[i] = byte & 0xff; } return 1; } /* pre-defined namespace uuids */ pd_uuid_t pd_uuid_ns_dns = { { 0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 } }; pd_uuid_t pd_uuid_ns_url = { { 0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 } }; pd_uuid_t pd_uuid_ns_oid = { { 0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 } }; pd_uuid_t pd_uuid_ns_x500 = { { 0x6b, 0xa7, 0xb8, 0x14, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 } }; int pd_uuid_copy(pd_uuid_t *src, pd_uuid_t *dst) { if (src && dst) { memcpy(dst->data, src->data, 16); return 1; } return 0; } int pd_uuid_cmp(pd_uuid_t *a, pd_uuid_t *b) { if (a && b) { return memcmp(a, b, 16); } return 1; }