Changed ServerInstanceID to UUID 83/983/3
authorErich Keane <erich.keane@intel.com>
Wed, 13 May 2015 21:20:23 +0000 (14:20 -0700)
committerErich Keane <erich.keane@intel.com>
Fri, 15 May 2015 21:57:52 +0000 (21:57 +0000)
The specification and design document both specify that server instance
ID should be a UUID, yet this was initially implemented as a Uint32.

This patch implements UUID generation, UUID-as-string generation,
and UUID-to-string conversion in the ocrandom module.  Additionally,
the server instance ID calls now correctly work with UUID rather than
the int32.  OCResourceIdentifer also now work directly with strings
rather than converting the value to an int32.

Change-Id: Ic37559973f82515f795d75f798dab6b6ff99d31f
Signed-off-by: Erich Keane <erich.keane@intel.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/983
Tested-by: jenkins-iotivity <jenkins-iotivity@opendaylight.org>
Reviewed-by: Sachin Agrawal <sachin.agrawal@intel.com>
Reviewed-by: Doug Hudson <douglas.hudson@intel.com>
Reviewed-by: Joseph Morrow <joseph.l.morrow@intel.com>
12 files changed:
Readme.scons.txt
build_common/linux/SConscript
resource/csdk/ocrandom/include/ocrandom.h
resource/csdk/ocrandom/src/ocrandom.c
resource/csdk/ocrandom/test/android/randomtest.cpp
resource/csdk/ocrandom/test/linux/randomtest.cpp
resource/csdk/stack/include/internal/ocstackinternal.h
resource/csdk/stack/src/ocstack.c
resource/include/OCResource.h
resource/src/OCResource.cpp
resource/unit_tests.scons
resource/unittests/OCResourceTest.cpp

index c13dd47..ef0542f 100644 (file)
@@ -5,7 +5,7 @@
 command in this directory)
 
     Install external libraries:
-      $ sudo apt-get install libboost-dev libboost-program-options-dev libexpat1-dev libboost-thread-dev
+      $ sudo apt-get install libboost-dev libboost-program-options-dev libexpat1-dev libboost-thread-dev uuid-dev
 
     Build release binaries:
       $ scons
index 2b26879..a95c187 100644 (file)
@@ -19,6 +19,7 @@ if env.get('LOGGING'):
 env.AppendUnique(CPPDEFINES = ['WITH_POSIX', '__linux__'])
 env.AppendUnique(CFLAGS = ['-std=gnu99'])
 env.AppendUnique(CCFLAGS = ['-Wall', '-fPIC'])
+env.AppendUnique(LIBS = ['uuid'])
 env.AppendUnique(LINKFLAGS = ['-ldl', '-lpthread'])
 
 if env.get('TARGET_OS') == 'tizen':
index d516520..c4d219e 100644 (file)
@@ -36,6 +36,17 @@ extern "C" {
 #define ANALOG_IN (10)
 #endif
 
+#define UUID_SIZE (16)
+// The characters are 36 long, 37 for the null-term
+#define UUID_STRING_SIZE (37)
+
+typedef enum
+{
+    RAND_UUID_OK = 0,
+    RAND_UUID_INVALID_PARAM = -1,
+    RAND_UUID_READ_ERROR = -2,
+    RAND_UUID_CONVERT_ERROR = -3
+} OCRandomUuidResult;
 /**
  * Seed the random number generator. Seeding depends on platform.
  * Android and Linux uses current time. Arduino uses Analog reading on pin ANALOG_IN
@@ -73,6 +84,46 @@ void OCFillRandomMem(uint8_t * location, uint16_t len);
  */
 uint32_t OCGetRandomRange(uint32_t firstBound, uint32_t secondBound);
 
+/**
+ * Generate a Uniformly Unique Identifier based on RFC4122 and
+ * provide it as a 16 byte byte-array
+ *
+ * @param[out] uuid
+ *               the 16 byte array to fill with the UUID data
+ *               of a new UUID
+ *
+ * @retval RAND_UUID_OK for success, otherwise an error value
+ */
+OCRandomUuidResult OCGenerateUuid(uint8_t uuid[UUID_SIZE]);
+
+/**
+ * Generate a Uniformly Unique Identifier based on RFC4122 and
+ * provide it as a C style string.
+ *
+ * @param[out] uuidString
+ *               a 37-byte length string to fill with the string
+ *               representation of a new UUID.  Size is 32 chars
+ *               for the hex data, 4 for '-' characters, and 1
+ *               for the NULL terminator
+ *
+ * @retval RAND_UUID_OK for success, otherwise an error value
+ */
+OCRandomUuidResult OCGenerateUuidString(char uuidString[UUID_STRING_SIZE]);
+
+/**
+ * Convert a UUID generated by OCGenerateUuid to a C style string
+ * based on RFC 4122
+ *
+ * @param[in]  uuid
+ *              The 16 byte array filled with UUID data by OCGenerateUuid
+ * @param[out] uuidString
+ *              a 37 byte length string to fill with the string
+ *              representation of the passed UUID.
+ * @retval RAND_UUID_OK for success, otherwise an error value
+ */
+OCRandomUuidResult OCConvertUuidToString(const uint8_t uuid[UUID_SIZE],
+        char uuidString[UUID_STRING_SIZE]);
+
 #ifdef __cplusplus
 }
 #endif
index 648f72e..6271fad 100644 (file)
 #include "unistd.h"
 #endif
 #include "ocrandom.h"
+#include <stdio.h>
+
+#if defined(__linux__) || defined(__APPLE__)
+#include <uuid/uuid.h>
+#endif
 
 #ifdef ARDUINO
 #include "Arduino.h"
@@ -132,3 +137,148 @@ uint32_t OCGetRandomRange(uint32_t firstBound, uint32_t secondBound){
     result = ((float)OCGetRandom()/((float)(0xFFFFFFFF))*(float)diff) + (float) base;
     return result;
 }
+
+#if defined(__ANDROID__)
+uint8_t parseUuidChar(char c)
+{
+    if(isdigit(c))
+    {
+        return c - '0';
+    }
+    else
+    {
+        return c - 'a' + 10;
+    }
+}
+uint8_t parseUuidPart(const char *c)
+{
+    return (parseUuidChar(c[0])<<4) + parseUuidChar(c[1]);
+}
+#endif
+
+OCRandomUuidResult OCGenerateUuid(uint8_t uuid[UUID_SIZE])
+{
+    if(!uuid)
+    {
+        return RAND_UUID_INVALID_PARAM;
+    }
+#if defined(__linux__) || defined(__APPLE__)
+    // note: uuid_t is typedefed as unsigned char[16] on linux/apple
+    uuid_generate(uuid);
+    return RAND_UUID_OK;
+#elif defined(__ANDROID__)
+    char uuidString[UUID_STRING_SIZE];
+    int8_t ret = OCGenerateUuidString(uuidString);
+
+    if(ret < 0)
+    {
+        return ret;
+    }
+
+    uuid[ 0] = parseUuidPart(&uuidString[0]);
+    uuid[ 1] = parseUuidPart(&uuidString[2]);
+    uuid[ 2] = parseUuidPart(&uuidString[4]);
+    uuid[ 3] = parseUuidPart(&uuidString[6]);
+
+    uuid[ 4] = parseUuidPart(&uuidString[9]);
+    uuid[ 5] = parseUuidPart(&uuidString[11]);
+
+    uuid[ 6] = parseUuidPart(&uuidString[14]);
+    uuid[ 7] = parseUuidPart(&uuidString[16]);
+
+    uuid[ 8] = parseUuidPart(&uuidString[19]);
+    uuid[ 9] = parseUuidPart(&uuidString[21]);
+
+    uuid[10] = parseUuidPart(&uuidString[24]);
+    uuid[11] = parseUuidPart(&uuidString[26]);
+    uuid[12] = parseUuidPart(&uuidString[28]);
+    uuid[13] = parseUuidPart(&uuidString[30]);
+    uuid[14] = parseUuidPart(&uuidString[32]);
+    uuid[15] = parseUuidPart(&uuidString[34]);
+
+    return RAND_UUID_OK;
+#else
+    // Fallback for all platforms is filling the array with random data
+    OCFillRandomMem(uuid, UUID_SIZE);
+    return RAND_UUID_OK;
+#endif
+}
+
+OCRandomUuidResult OCGenerateUuidString(char uuidString[UUID_STRING_SIZE])
+{
+    if(!uuidString)
+    {
+        return RAND_UUID_INVALID_PARAM;
+    }
+#if defined(__linux__) || defined(__APPLE__)
+    uint8_t uuid[UUID_SIZE];
+    int8_t ret = OCGenerateUuid(uuid);
+
+    if(ret != 0)
+    {
+        return ret;
+    }
+
+    uuid_unparse_lower(uuid, uuidString);
+    return RAND_UUID_OK;
+
+#elif defined(__ANDROID__)
+    int32_t fd = open("/proc/sys/kernel/random/uuid", O_RDONLY);
+    if(fd > 0)
+    {
+        ssize_t readResult = read(fd, uuidString, UUID_STRING_SIZE - 1);
+        close(fd);
+        if(readResult < 0)
+        {
+            return RAND_UUID_READ_ERROR;
+        }
+        else if(readResult < UUID_STRING_SIZE - 1)
+        {
+            uuidString[0] = '\0';
+            return RAND_UUID_READ_ERROR;
+        }
+
+        uuidString[UUID_STRING_SIZE - 1] = '\0';
+        for(char* p = uuidString; *p; ++p)
+        {
+            *p = tolower(*p);
+        }
+        return RAND_UUID_OK;
+    }
+    else
+    {
+        close(fd);
+        return RAND_UUID_READ_ERROR;
+    }
+#else
+    uint8_t uuid[UUID_SIZE];
+    OCGenerateUuid(uuid);
+
+    return OCConvertUuidToString(uuid, uuidString);
+#endif
+}
+
+OCRandomUuidResult OCConvertUuidToString(const uint8_t uuid[UUID_SIZE],
+                                         char uuidString[UUID_STRING_SIZE])
+{
+    if (uuid == NULL || uuidString == NULL)
+    {
+        return RAND_UUID_INVALID_PARAM;
+    }
+
+
+    int ret = snprintf(uuidString, UUID_STRING_SIZE,
+            "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+            uuid[0], uuid[1], uuid[2], uuid[3],
+            uuid[4], uuid[5], uuid[6], uuid[7],
+            uuid[8], uuid[9], uuid[10], uuid[11],
+            uuid[12], uuid[13], uuid[14], uuid[15]
+            );
+
+    if (ret != UUID_STRING_SIZE - 1)
+    {
+        return RAND_UUID_CONVERT_ERROR;
+    }
+
+    return RAND_UUID_OK;
+}
index 6dd8e7f..9f22173 100644 (file)
@@ -63,3 +63,43 @@ TEST(RandomGeneration,OCFillRandomMem) {
     EXPECT_EQ((uint8_t )0, array[ARR_SIZE - 1]);
 }
 
+TEST(RandomGeneration, OCGenerateUuid)
+{
+    EXPECT_EQ(RAND_UUID_INVALID_PARAM, OCGenerateUuid(NULL));
+
+    uint8_t uuid[16] = {};
+
+    EXPECT_EQ(RAND_UUID_OK, OCGenerateUuid(uuid));
+
+    EXPECT_FALSE(uuid[0] == '0' && uuid[1] == '0' &&
+                 uuid[2] == '0' && uuid[3] == '0' &&
+                 uuid[4] == '0' && uuid[5] == '0' &&
+                 uuid[6] == '0' && uuid[7] == '0' &&
+                 uuid[8] == '0' && uuid[9] == '0' &&
+                 uuid[10] == '0' && uuid[11] == '0' &&
+                 uuid[12] == '0' && uuid[13] == '0' &&
+                 uuid[14] == '0' && uuid[15] == '0');
+}
+
+TEST(RandomGeneration, OCGenerateUuidString)
+{
+    EXPECT_EQ(RAND_UUID_INVALID_PARAM, OCGenerateUuidString(NULL));
+
+    char uuidString[37] ={};
+
+    EXPECT_EQ(RAND_UUID_OK, OCGenerateUuidString(uuidString));
+    EXPECT_EQ(0, uuidString[36]);
+    EXPECT_EQ('-', uuidString[8]);
+    EXPECT_EQ('-', uuidString[13]);
+    EXPECT_EQ('-', uuidString[18]);
+    EXPECT_EQ('-', uuidString[23]);
+
+    for(int i = 0; i < 36; ++i)
+    {
+        EXPECT_TRUE(
+                i == 8 || i == 13 || i == 18 || i == 23 ||
+                (uuidString[i] >= 'a' && uuidString[i] <= 'f') ||
+                (uuidString[i] >= '0' && uuidString[i] <= '9'))
+                << "UUID Character out of range: "<< uuidString[i];
+    }
+}
index 46fde03..98a1d5c 100644 (file)
@@ -64,3 +64,43 @@ TEST(RandomGeneration,OCFillRandomMem) {
     EXPECT_EQ((uint8_t )0, array[ARR_SIZE - 1]);
 }
 
+TEST(RandomGeneration, OCGenerateUuid)
+{
+    EXPECT_EQ(RAND_UUID_INVALID_PARAM, OCGenerateUuid(NULL));
+
+    uint8_t uuid[16] = {};
+
+    EXPECT_EQ(RAND_UUID_OK, OCGenerateUuid(uuid));
+
+    EXPECT_FALSE(uuid[0] == '0' && uuid[1] == '0' &&
+                 uuid[2] == '0' && uuid[3] == '0' &&
+                 uuid[4] == '0' && uuid[5] == '0' &&
+                 uuid[6] == '0' && uuid[7] == '0' &&
+                 uuid[8] == '0' && uuid[9] == '0' &&
+                 uuid[10] == '0' && uuid[11] == '0' &&
+                 uuid[12] == '0' && uuid[13] == '0' &&
+                 uuid[14] == '0' && uuid[15] == '0');
+}
+
+TEST(RandomGeneration, OCGenerateUuidString)
+{
+    EXPECT_EQ(RAND_UUID_INVALID_PARAM, OCGenerateUuidString(NULL));
+
+    char uuidString[37] = {};
+
+    EXPECT_EQ(RAND_UUID_OK, OCGenerateUuidString(uuidString));
+    EXPECT_EQ('\0', uuidString[36]);
+    EXPECT_EQ('-', uuidString[8]);
+    EXPECT_EQ('-', uuidString[13]);
+    EXPECT_EQ('-', uuidString[18]);
+    EXPECT_EQ('-', uuidString[23]);
+
+    for(int i = 0; i < 36; ++i)
+    {
+        EXPECT_TRUE(
+                i == 8 || i == 13 || i == 18 || i == 23 ||
+                (uuidString[i] >= 'a' && uuidString[i] <= 'f') ||
+                (uuidString[i] >= '0' && uuidString[i] <= '9'))
+                << "UUID Character out of range: "<< uuidString[i];
+    }
+}
index 176dd41..83989a5 100644 (file)
@@ -124,7 +124,7 @@ typedef struct
 /**
  * This typedef is to represent our Server Instance identification.
  */
-typedef uint32_t ServerID;
+typedef uint8_t ServerID[16];
 
 //-----------------------------------------------------------------------------
 // Internal function prototypes
@@ -188,12 +188,25 @@ OCStackResult BindResourceTypeToResource(OCResource* resource,
  */
 OCStackResult CAResultToOCResult(CAResult_t caResult);
 
+/**
+ * Get a byte representation of the server instance ID.
+ * The memory is managed internal to this function, so freeing it externally will
+ * result in a runtime error
+ *
+ * Note: This will NOT seed the RNG, so it must be called after the RNG is seeded.
+ * This is done automatically during the OCInit process,
+ * so ensure that this call is done after that.
+ *
+ * @return A uint8_t representation the server instance ID.
+ */
+const uint8_t* OCGetServerInstanceID(void);
+
 /**
  * Get a string representation the server instance ID.
  * The memory is managed internal to this function, so freeing externally will result
- * in a compiler error
+ * in a runtime error
  * Note: This will NOT seed the RNG, so it must be called after the RNG is seeded.
- * This is done automatically during the OCInit process (via the call to OCInitCoAP),
+ * This is done automatically during the OCInit process,
  * so ensure that this call is done after that.
  *
  * @return A string representation  the server instance ID.
index 8c6d004..8e427f2 100644 (file)
@@ -435,13 +435,6 @@ static OCResourceType *findResourceType(OCResourceType * resourceTypeList,
  */
 static OCStackResult ResetPresenceTTL(ClientCB *cbNode, uint32_t maxAgeSeconds);
 
-/**
- * Return a server instance ID.
- *
- * @return Instance ID of the server.
- */
-static const ServerID OCGetServerInstanceID(void);
-
 //-----------------------------------------------------------------------------
 // Internal functions
 //-----------------------------------------------------------------------------
@@ -3802,7 +3795,7 @@ OCStackResult getQueryFromUri(const char * uri, char** query, char ** uriWithout
         return OC_STACK_NO_MEMORY;
 }
 
-const ServerID OCGetServerInstanceID(void)
+const uint8_t* OCGetServerInstanceID(void)
 {
     static bool generated = false;
     static ServerID sid;
@@ -3811,25 +3804,35 @@ const ServerID OCGetServerInstanceID(void)
         return sid;
     }
 
-    sid = OCGetRandom();
+    if (OCGenerateUuid(sid) != RAND_UUID_OK)
+    {
+        OC_LOG(FATAL, TAG, PCF("Generate UUID for Server Instance failed!"));
+        return NULL;
+    }
     generated = true;
     return sid;
 }
 
 const char* OCGetServerInstanceIDString(void)
 {
-    // max printed length of a base 10
-    // uint32 is 10 characters, so 11 includes null.
-    // This will change as the representation gets switched
-    // to another value
-    static char buffer[11];
+    static bool generated = false;
+    static char sidStr[UUID_STRING_SIZE];
 
-    if (snprintf(buffer, sizeof(buffer),"%u", OCGetServerInstanceID()) < 0)
+    if(generated)
     {
-        buffer[0]='\0';
+        return sidStr;
     }
 
-    return buffer;
+    const uint8_t* sid = OCGetServerInstanceID();
+
+    if(OCConvertUuidToString(sid, sidStr) != RAND_UUID_OK)
+    {
+        OC_LOG(FATAL, TAG, PCF("Generate UUID String for Server Instance failed!"));
+        return NULL;
+    }
+
+    generated = true;
+    return sidStr;
 }
 
 int32_t OCDevAddrToIPv4Addr(OCDevAddr *ipAddr, uint8_t *a, uint8_t *b,
index e7f5465..bc0109c 100644 (file)
@@ -84,7 +84,7 @@ namespace OC
                     const std::string& resourceUri );
 
         private:
-            uint32_t m_representation;
+            std::string m_representation;
             const std::string& m_resourceUri;
     };
 
index a0437f7..2ad836a 100644 (file)
@@ -294,9 +294,7 @@ OCResourceIdentifier OCResource::uniqueIdentifier() const
 
 std::string OCResource::sid() const
 {
-    std::ostringstream os;
-    os << this->uniqueIdentifier().m_representation;
-    return os.str();
+    return this->uniqueIdentifier().m_representation;
 }
 
 bool OCResource::operator==(const OCResource &other) const
@@ -331,13 +329,8 @@ bool OCResource::operator>=(const OCResource &other) const
 
 OCResourceIdentifier::OCResourceIdentifier(const std::string& wireServerIdentifier,
         const std::string& resourceUri)
-    :m_representation(0), m_resourceUri(resourceUri)
+    :m_representation(wireServerIdentifier), m_resourceUri(resourceUri)
 {
-    // test required so we can create Resources without a server. Will leave as default.
-    if(!wireServerIdentifier.empty())
-    {
-        m_representation = boost::lexical_cast<unsigned int>(wireServerIdentifier);
-    }
 }
 
 std::ostream& operator <<(std::ostream& os, const OCResourceIdentifier& ri)
index 51bc316..41a10bc 100644 (file)
@@ -40,6 +40,7 @@ if target_os == 'linux':
 
        # Build C unit tests
        SConscript('csdk/stack/test/SConscript')
+       SConscript('csdk/ocrandom/test/SConscript')
 
        SConscript('csdk/connectivity/test/SConscript')
 
index d647c1f..501857d 100644 (file)
@@ -466,7 +466,7 @@ namespace OCResourceTest
     {
         OCResource::Ptr resource = ConstructResourceObject("coap://192.168.1.2:5000", "/resource");
         EXPECT_TRUE(resource != NULL);
-        EXPECT_TRUE(resource->sid() == "0");
+        EXPECT_TRUE(resource->sid() == "");
     }
 
     //UniqueIdentifier Test
@@ -476,7 +476,7 @@ namespace OCResourceTest
         EXPECT_TRUE(resource != NULL);
         std::ostringstream ss;
         ss << resource->uniqueIdentifier();
-        EXPECT_TRUE(ss.str() == "0/resource");
+        EXPECT_TRUE(ss.str() == "/resource");
     }
 
     // HeaderOptions Test