Add uuid_t and UUID primitive functions

This commit is contained in:
Andrew Bettison 2013-11-12 17:44:03 +10:30
parent b44046d612
commit 50bbb722d0
4 changed files with 222 additions and 0 deletions

View File

@ -10,6 +10,7 @@ HDRS= fifo.h \
rotbuf.h \
mem.h \
os.h \
uuid.h \
strbuf.h \
strbuf_helpers.h \
sha2.h \

View File

@ -69,6 +69,7 @@ SERVAL_SOURCES = \
$(SERVAL_BASE)strbuf.c \
$(SERVAL_BASE)strbuf_helpers.c \
$(SERVAL_BASE)strlcpy.c \
$(SERVAL_BASE)uuid.c \
$(SERVAL_BASE)vomp.c \
$(SERVAL_BASE)vomp_console.c \
$(SERVAL_BASE)xprintf.c \

107
uuid.c Normal file
View File

@ -0,0 +1,107 @@
/*
Serval DNA Universally Unique Identifier support
Copyright (C) 2013 Serval Project Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#define __SERVALDNA_UUID_H_INLINE
#include "uuid.h"
#include "os.h"
#include "str.h"
#include <assert.h>
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
enum uuid_version uuid_get_version(const uuid_t *uuid)
{
assert(uuid_is_valid(uuid));
switch (ntohs(uuid->u.record.time_hi_and_version) & 0xf000) {
case 0x1000: return UUID_VERSION_TIME_BASED;
case 0x2000: return UUID_VERSION_DCE_SECURITY;
case 0x3000: return UUID_VERSION_NAME_MD5;
case 0x4000: return UUID_VERSION_RANDOM;
case 0x5000: return UUID_VERSION_NAME_SHA1;
}
return UUID_VERSION_UNSUPPORTED;
}
void uuid_set_version(uuid_t *uuid, enum uuid_version version)
{
uint16_t version_bits;
switch (version) {
case UUID_VERSION_TIME_BASED: version_bits = 0x1000; break;
case UUID_VERSION_DCE_SECURITY: version_bits = 0x2000; break;
case UUID_VERSION_NAME_MD5: version_bits = 0x3000; break;
case UUID_VERSION_RANDOM: version_bits = 0x4000; break;
case UUID_VERSION_NAME_SHA1: version_bits = 0x5000; break;
default: abort();
}
assert(uuid_is_valid(uuid));
uuid->u.record.time_hi_and_version = htons((ntohs(uuid->u.record.time_hi_and_version) & 0xfff) | version_bits);
}
int uuid_generate_random(uuid_t *uuid)
{
if (urandombytes(uuid->u.binary, sizeof uuid->u.binary) == -1)
return -1;
// The following discards 6 random bits.
uuid->u.record.clock_seq_hi_and_reserved &= 0x3f;
uuid->u.record.clock_seq_hi_and_reserved |= 0x80;
uuid_set_version(uuid, UUID_VERSION_RANDOM);
return 0;
}
void uuid_to_str(const uuid_t *uuid, char *dst)
{
assert(uuid_is_valid(uuid));
unsigned i;
for (i = 0; i != sizeof uuid->u.binary; ++i) {
switch (i) {
case 4: case 6: case 8: case 10:
*dst++ = '-';
default:
*dst++ = hexdigit[uuid->u.binary[i] >> 4];
*dst++ = hexdigit[uuid->u.binary[i] & 0xf];
}
}
*dst = '\0';
}
int str_to_uuid(const char *str, uuid_t *uuid, const char **afterp)
{
const char *end = str;
int ret = 0;
if ( strn_fromhex(uuid->u.binary, 4, end, &end) == 4
&& *end == '-'
&& strn_fromhex(uuid->u.binary + 4, 2, end + 1, &end) == 2
&& *end == '-'
&& strn_fromhex(uuid->u.binary + 6, 2, end + 1, &end) == 2
&& *end == '-'
&& strn_fromhex(uuid->u.binary + 8, 2, end + 1, &end) == 2
&& *end == '-'
&& strn_fromhex(uuid->u.binary + 10, 6, end + 1, &end) == 6
) {
assert(end == str + 36);
ret = uuid_is_valid(uuid);
}
if (afterp)
*afterp = end;
if (ret == 0 || (!afterp && *end))
return 0;
return 1;
}

113
uuid.h Normal file
View File

@ -0,0 +1,113 @@
/*
Serval DNA Universally Unique Identifier support
Copyright (C) 2013 Serval Project Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __SERVALDNA_UUID_H
#define __SERVALDNA_UUID_H
#include <stdint.h>
#ifndef __SERVALDNA_UUID_H_INLINE
# if __GNUC__ && !__GNUC_STDC_INLINE__
# define __SERVALDNA_UUID_H_INLINE extern inline
# else
# define __SERVALDNA_UUID_H_INLINE inline
# endif
#endif
/* A UUID is defined by RFC-4122 as a 128-bit identifier with the two most
* significant bits of the ninth byte being 1 and 0, which indicates the
* "variant" that is described by the RFC. Other variants exist, but are not
* supported here, and are treated as INVALID by the functions defined below.
* Any attempt to pass an invalid UUID to a function that requires a valid UUID
* as input will probably result in the calling process being aborted (see
* SIGABRT, abort(3)).
*
* In a valid UUID, the four lowest significant bits of the seventh byte define
* the "version" of the UUID, which essentially indicates how it was generated.
* The RFC defines five SUPPORTED versions. Any other version is UNSUPPORTED.
*
* The fields in the UUID 'record' structure are stored in network byte order,
* so code wishing to make use of the record structure must use ntohl(3) and
* ntohs(3) to read values, and htonl(3) and htons(3) to assign values.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
typedef struct uuid {
union {
struct {
uint32_t time_low;
uint16_t time_mid;
uint16_t time_hi_and_version;
uint8_t clock_seq_hi_and_reserved;
uint8_t clock_seq_low;
unsigned char node[6]; // uint48_t
} record;
unsigned char binary[16];
} u;
} uuid_t;
enum uuid_version {
UUID_VERSION_UNSUPPORTED = 0,
UUID_VERSION_TIME_BASED = 1,
UUID_VERSION_DCE_SECURITY = 2,
UUID_VERSION_NAME_MD5 = 3,
UUID_VERSION_RANDOM = 4,
UUID_VERSION_NAME_SHA1 = 5
};
__SERVALDNA_UUID_H_INLINE int uuid_is_valid(const uuid_t *any_uuid) {
return (any_uuid->u.record.clock_seq_hi_and_reserved & 0xc0) == 0x80;
}
enum uuid_version uuid_get_version(const uuid_t *valid_uuid);
void uuid_set_version(uuid_t *valid_uuid, enum uuid_version);
/* Returns -1 if error (eg, cannot open /dev/urandom), 0 if successful.
*/
int uuid_generate_random(uuid_t *dest_uuid);
/* Formats the given valid UUID in its canonical string representation:
* XXXXXXXX-VXXX-MXXX-XXXXXXXXXXXX
* where X is any hex digit
* V is 1, 2, 3, 4 or 5 (supported versions) or any other hex digit
* (unsupported versions)
* M is 8, 9, A or B (high two bits are variant 01)
*
* The 'dst' argument must point to a buffer of 37 bytes. The first 36 bytes
* are filled with the representation shown above, and the 37th byte dst[36] is
* set to NUL '\0'.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
void uuid_to_str(const uuid_t *valid_uuid, char *dst);
/* Parse a canonical UUID string (as generated by uuid_to_str()) into a valid
* UUID, which may or not be supported.
*
* Returns 1 if a valid UUID is parsed, storing the value in *result (unless result is NULL) and
* storing a pointer to the immediately succeeding character in *afterp. If afterp is NULL then
* returns 0 unless the immediately succeeding character is a NUL '\0'. If no UUID is parsed or
* if the UUID is not valid, then returns 0, leaving *result unchanged and
* setting setting *afterp to point to the character where parsing failed.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int str_to_uuid(const char *str, uuid_t *result, const char **afterp);
#endif //__SERVALDNA_OS_H