diff --git a/configure.ac b/configure.ac index e74865b8..18c641ce 100644 --- a/configure.ac +++ b/configure.ac @@ -75,21 +75,50 @@ AC_CHECK_LIB([dl], [dlopen], AC_MSG_ERROR("libdl is required on systems which do not otherwise provide dlopen()"), [#include ])]) -# OSSP UUID -AC_CHECK_LIB([ossp-uuid], [uuid_make], [UUID_LIBS=-lossp-uuid], - AC_CHECK_LIB([uuid], [uuid_make], [UUID_LIBS=-luuid], - AC_MSG_ERROR("The OSSP UUID library is required"))) +# +# libuuid +# -# Check for and validate OSSP uuid.h header -AC_CHECK_HEADERS([ossp/uuid.h]) -AC_CHECK_DECL([uuid_make],, - AC_MSG_ERROR("No OSSP uuid.h found in include path"), - [#ifdef HAVE_OSSP_UUID_H - #include - #else - #include - #endif - ]) +have_libuuid=disabled +AC_ARG_WITH([libuuid], + [AS_HELP_STRING([--with-libuuid], + [use libuuid to generate unique identifiers @<:@default=check@:>@])], + [], + [with_libuuid=check]) + +if test "x$with_libuuid" != "xno" +then + have_libuuid=yes + AC_CHECK_LIB([uuid], [uuid_generate], + [UUID_LIBS=-luuid] + [AC_DEFINE([HAVE_LIBUUID],, [Whether libuuid is available])], + [have_libuuid=no]) +fi + +# OSSP UUID (if libuuid is unavilable) +if test "x${have_libuuid}" != "xyes" +then + + AC_CHECK_LIB([ossp-uuid], [uuid_make], [UUID_LIBS=-lossp-uuid], + AC_CHECK_LIB([uuid], [uuid_make], [UUID_LIBS=-luuid], + AC_MSG_ERROR([ + -------------------------------------------- + Unable to find libuuid or the OSSP UUID library. + Either libuuid (from util-linux) or the OSSP UUID library is required for + guacamole-server to be built. + --------------------------------------------]))) + + # Check for and validate OSSP uuid.h header + AC_CHECK_HEADERS([ossp/uuid.h]) + AC_CHECK_DECL([uuid_make],, + AC_MSG_ERROR("No OSSP uuid.h found in include path"), + [#ifdef HAVE_OSSP_UUID_H + #include + #else + #include + #endif + ]) +fi # cunit AC_CHECK_LIB([cunit], [CU_run_test], [CUNIT_LIBS=-lcunit]) diff --git a/src/libguac/id.c b/src/libguac/id.c index 27a714c0..e627f89d 100644 --- a/src/libguac/id.c +++ b/src/libguac/id.c @@ -22,7 +22,9 @@ #include "guacamole/error.h" #include "id.h" -#ifdef HAVE_OSSP_UUID_H +#if defined(HAVE_LIBUUID) +#include +#elif defined(HAVE_OSSP_UUID_H) #include #else #include @@ -30,54 +32,73 @@ #include +/** + * The length of a UUID in bytes. All UUIDs are guaranteed to be 36 1-byte + * characters long. + */ +#define GUAC_UUID_LEN 36 + char* guac_generate_id(char prefix) { char* buffer; char* identifier; - size_t identifier_length; + /* Prepare object to receive generated UUID */ +#ifdef HAVE_LIBUUID + uuid_t uuid; +#else uuid_t* uuid; - - /* Attempt to create UUID object */ if (uuid_create(&uuid) != UUID_RC_OK) { guac_error = GUAC_STATUS_NO_MEMORY; guac_error_message = "Could not allocate memory for UUID"; return NULL; } +#endif - /* Generate random UUID */ + /* Generate unique identifier */ +#ifdef HAVE_LIBUUID + uuid_generate(uuid); +#else if (uuid_make(uuid, UUID_MAKE_V4) != UUID_RC_OK) { uuid_destroy(uuid); guac_error = GUAC_STATUS_NO_MEMORY; guac_error_message = "UUID generation failed"; return NULL; } +#endif /* Allocate buffer for future formatted ID */ - buffer = malloc(UUID_LEN_STR + 2); + buffer = malloc(GUAC_UUID_LEN + 2); if (buffer == NULL) { +#ifndef HAVE_LIBUUID uuid_destroy(uuid); +#endif guac_error = GUAC_STATUS_NO_MEMORY; - guac_error_message = "Could not allocate memory for connection ID"; + guac_error_message = "Could not allocate memory for unique ID"; return NULL; } identifier = &(buffer[1]); - identifier_length = UUID_LEN_STR + 1; - /* Build connection ID from UUID */ + /* Convert UUID to string to produce unique identifier */ +#ifdef HAVE_LIBUUID + uuid_unparse_lower(uuid, identifier); +#else + size_t identifier_length = GUAC_UUID_LEN + 1; if (uuid_export(uuid, UUID_FMT_STR, &identifier, &identifier_length) != UUID_RC_OK) { free(buffer); uuid_destroy(uuid); guac_error = GUAC_STATUS_INTERNAL_ERROR; - guac_error_message = "Conversion of UUID to connection ID failed"; + guac_error_message = "Conversion of UUID to unique ID failed"; return NULL; } + /* Clean up generated UUID */ uuid_destroy(uuid); +#endif buffer[0] = prefix; - buffer[UUID_LEN_STR + 1] = '\0'; + buffer[GUAC_UUID_LEN + 1] = '\0'; return buffer; } diff --git a/src/libguac/tests/Makefile.am b/src/libguac/tests/Makefile.am index 7ee55948..d406c4cd 100644 --- a/src/libguac/tests/Makefile.am +++ b/src/libguac/tests/Makefile.am @@ -36,6 +36,7 @@ TESTS = $(check_PROGRAMS) test_libguac_SOURCES = \ client/buffer_pool.c \ client/layer_pool.c \ + id/generate.c \ parser/append.c \ parser/read.c \ pool/next_free.c \ diff --git a/src/libguac/tests/id/generate.c b/src/libguac/tests/id/generate.c new file mode 100644 index 00000000..3142a6f1 --- /dev/null +++ b/src/libguac/tests/id/generate.c @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "id.h" + +#include +#include +#include +#include + +/** + * Test which verifies that each call to guac_generate_id() produces a + * different string. + */ +void test_id__unique() { + + char* id1 = guac_generate_id('x'); + char* id2 = guac_generate_id('x'); + + /* Neither string may be NULL */ + CU_ASSERT_PTR_NOT_NULL_FATAL(id1); + CU_ASSERT_PTR_NOT_NULL_FATAL(id2); + + /* Both strings should be different */ + CU_ASSERT_STRING_NOT_EQUAL(id1, id2); + + free(id1); + free(id2); + +} + +/** + * Test which verifies that guac_generate_id() produces strings are in the + * correc UUID-based format. + */ +void test_id__format() { + + unsigned int ignore; + + char* id = guac_generate_id('x'); + CU_ASSERT_PTR_NOT_NULL_FATAL(id); + + int items_read = sscanf(id, "x%08x-%04x-%04x-%04x-%08x%04x", + &ignore, &ignore, &ignore, &ignore, &ignore, &ignore); + + CU_ASSERT_EQUAL(items_read, 6); + CU_ASSERT_EQUAL(strlen(id), 37); + + free(id); + +} + +/** + * Test which verifies that guac_generate_id() takes the specified prefix + * character into account when generating the ID string. + */ +void test_id__prefix() { + + char* id; + + id = guac_generate_id('a'); + CU_ASSERT_PTR_NOT_NULL_FATAL(id); + CU_ASSERT_EQUAL(id[0], 'a'); + free(id); + + id = guac_generate_id('b'); + CU_ASSERT_PTR_NOT_NULL_FATAL(id); + CU_ASSERT_EQUAL(id[0], 'b'); + free(id); + +} +