Selaa lähdekoodia

Merge branch 'master' into feature/architectures

Jose Cabral 5 vuotta sitten
vanhempi
commit
cb061851bc

+ 0 - 2
arch/common/freeRTOS62541.h

@@ -7,8 +7,6 @@
 #ifndef ARCH_COMMON_FREERTOS62541_H_
 #define ARCH_COMMON_FREERTOS62541_H_
 
-#define UA_THREAD_LOCAL
-
 #include <stdlib.h>
 #include <string.h>
 

+ 0 - 2
arch/eCos/ua_architecture.h

@@ -8,8 +8,6 @@
 #ifndef PLUGINS_ARCH_POSIX_UA_ARCHITECTURE_H_
 #define PLUGINS_ARCH_POSIX_UA_ARCHITECTURE_H_
 
-#define UA_THREAD_LOCAL
-
 #include <../deps/queue.h>  //TODO: in some compilers there's already a _SYS_QUEUE_H_ who is included first and doesn't have all functions
 
 #include <pkgconf/system.h>

+ 0 - 17
arch/posix/ua_architecture.h

@@ -59,23 +59,6 @@ void UA_sleep_ms(size_t ms);
 # include <netinet/tcp.h>
 #endif
 
-/* Thread-Local Storage
- * --------------------
- * Thread-local storage is not required by the main library functionality. It is
- * only used for some testing strategies. ``UA_THREAD_LOCAL`` is empty if the
- * feature is not available. */
-
-#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
-# define UA_THREAD_LOCAL _Thread_local /* C11 */
-#elif defined(__cplusplus) && __cplusplus > 199711L
-# define UA_THREAD_LOCAL thread_local /* C++11 */
-#elif defined(__GNUC__)
-# define UA_THREAD_LOCAL __thread /* GNU extension */
-#else
-# define UA_THREAD_LOCAL
-#endif
-
-
 /* unsigned int for windows and workaround to a glibc bug */
 /* Additionally if GNU_LIBRARY is not defined, it may be using
  * musl libc (e.g. Docker Alpine) */

+ 0 - 17
arch/vxworks/ua_architecture.h

@@ -41,23 +41,6 @@
 #endif
 #define UA_BINARY_OVERLAYABLE_FLOAT 1
 
-/* Thread-Local Storage
- * --------------------
- * Thread-local storage is not required by the main library functionality. It is
- * only used for some testing strategies. ``UA_THREAD_LOCAL`` is empty if the
- * feature is not available. */
-
-#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
-# define UA_THREAD_LOCAL _Thread_local /* C11 */
-#elif defined(__cplusplus) && __cplusplus > 199711L
-# define UA_THREAD_LOCAL thread_local /* C++11 */
-#elif defined(__GNUC__)
-# define UA_THREAD_LOCAL __thread /* GNU extension */
-#else
-# define UA_THREAD_LOCAL
-#endif
-
-
 #define OPTVAL_TYPE int
 
 #include <fcntl.h>

+ 0 - 16
arch/win32/ua_architecture.h

@@ -29,22 +29,6 @@
 # define _WIN32_WINNT 0x0600 //windows vista version, which included InepPton
 #endif
 
-/* Thread-Local Storage
- * --------------------
- * Thread-local storage is not required by the main library functionality. It is
- * only used for some testing strategies. ``UA_THREAD_LOCAL`` is empty if the
- * feature is not available. */
-
-#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
-# define UA_THREAD_LOCAL _Thread_local /* C11 */
-#elif defined(__cplusplus) && __cplusplus > 199711L
-# define UA_THREAD_LOCAL thread_local /* C++11 */
-#elif defined(_MSC_VER)
-# define UA_THREAD_LOCAL __declspec(thread) /* MSVC extension */
-#else
-# define UA_THREAD_LOCAL
-#endif
-
 #include <stdlib.h>
 #if defined(_WIN32) && !defined(__clang__)
 # include <malloc.h>

+ 1 - 1
deps/mdnsd

@@ -1 +1 @@
-Subproject commit f4aee59d4bbed766a9a8ef3c44492e98edfdd654
+Subproject commit e98eab03631bdf57f39b3cb9acce1ee1786aa411

+ 9 - 1
doc/building.rst

@@ -198,6 +198,12 @@ Detailed SDK Features
 
 **UA_ENABLE_COVERAGE**
    Measure the coverage of unit tests
+**UA_ENABLE_DISCOVERY**
+   Enable Discovery Service (LDS)
+**UA_ENABLE_DISCOVERY_MULTICAST**
+   Enable Discovery Service with multicast support (LDS-ME)
+**UA_ENABLE_DISCOVERY_SEMAPHORE**
+   Enable Discovery Semaphore support
 
 **UA_NAMESPACE_ZERO**
 
@@ -221,7 +227,9 @@ be visible in the cmake GUIs.
 
 **UA_ENABLE_STATUSCODE_DESCRIPTIONS**
    Compile the human-readable name of the StatusCodes into the binary. Enabled by default.
-
+**UA_ENABLE_FULL_NS0**
+   Use the full NS0 instead of a minimal Namespace 0 nodeset
+   ``UA_FILE_NS0`` is used to specify the file for NS0 generation from namespace0 folder. Default value is ``Opc.Ua.NodeSet2.xml``
 **UA_ENABLE_NONSTANDARD_UDP**
    Enable udp extension
 

+ 120 - 75
examples/server_ctt.c

@@ -18,15 +18,17 @@
  * corresponding CTT configuration is available at
  * https://github.com/open62541/open62541-ctt */
 
-UA_Boolean running = true;
-UA_Logger logger = UA_Log_Stdout;
-
 static const UA_NodeId baseDataVariableType = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_BASEDATAVARIABLETYPE}};
-
-static void
-stopHandler(int sign) {
-    UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Received Ctrl-C");
-    running = 0;
+static const UA_NodeId accessDenied = {1, UA_NODEIDTYPE_NUMERIC, {1337}};
+
+/* Custom AccessControl policy that disallows access to one specific node */
+static UA_Byte
+getUserAccessLevel_disallowSpecific(UA_Server *server, UA_AccessControl *ac,
+                                    const UA_NodeId *sessionId, void *sessionContext,
+                                    const UA_NodeId *nodeId, void *nodeContext) {
+    if(UA_NodeId_equal(nodeId, &accessDenied))
+        return 0x00;
+    return 0xFF;
 }
 
 /* Datasource Example */
@@ -98,68 +100,8 @@ outargMethod(UA_Server *server,
 
 #endif
 
-int
-main(int argc, char **argv) {
-    signal(SIGINT, stopHandler); /* catches ctrl-c */
-    signal(SIGTERM, stopHandler);
-
-#ifdef UA_ENABLE_ENCRYPTION
-    if(argc < 3) {
-        UA_LOG_FATAL(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
-                     "Missing arguments for encryption support. "
-                         "Arguments are <server-certificate.der> "
-                         "<private-key.der> [<trustlist1.crl>, ...]");
-        return 1;
-    }
-
-    /* Load certificate and private key */
-    UA_ByteString certificate = loadFile(argv[1]);
-    UA_ByteString privateKey = loadFile(argv[2]);
-
-    /* Load the trustlist */
-    size_t trustListSize = 0;
-    if(argc > 3)
-        trustListSize = (size_t)argc-3;
-    UA_STACKARRAY(UA_ByteString, trustList, trustListSize);
-    for(size_t i = 0; i < trustListSize; i++)
-        trustList[i] = loadFile(argv[i+3]);
-
-    /* Loading of a revocation list currently unsupported */
-    UA_ByteString *revocationList = NULL;
-    size_t revocationListSize = 0;
-
-    UA_ServerConfig *config =
-        UA_ServerConfig_new_allSecurityPolicies(4840, &certificate, &privateKey,
-                                                trustList, trustListSize,
-                                                revocationList, revocationListSize);
-    UA_ByteString_deleteMembers(&certificate);
-    UA_ByteString_deleteMembers(&privateKey);
-    for(size_t i = 0; i < trustListSize; i++)
-        UA_ByteString_deleteMembers(&trustList[i]);
-
-    if(!config) {
-        UA_LOG_FATAL(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
-                     "Could not create the server config");
-        return 1;
-    }
-#else
-    if(argc < 2) {
-        UA_LOG_FATAL(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
-                     "Missing argument for the server certificate");
-        return 1;
-    }
-    UA_ByteString certificate = loadFile(argv[1]);
-    UA_ServerConfig *config = UA_ServerConfig_new_minimal(4840, &certificate);
-    UA_ByteString_deleteMembers(&certificate);
-#endif
-
-    /* uncomment next line to add a custom hostname */
-    // UA_ServerConfig_set_customHostname(config, UA_STRING("custom"));
-
-    UA_Server *server = UA_Server_new(config);
-    if(server == NULL)
-        return 1;
-
+static void
+setInformationModel(UA_Server *server) {
     /* add a static variable node to the server */
     UA_VariableAttributes myVar = UA_VariableAttributes_default;
     myVar.description = UA_LOCALIZEDTEXT("en-US", "the answer");
@@ -168,14 +110,13 @@ main(int argc, char **argv) {
     myVar.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
     myVar.valueRank = -1;
     UA_Int32 myInteger = 42;
-    UA_Variant_setScalarCopy(&myVar.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
+    UA_Variant_setScalar(&myVar.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
     const UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
     const UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
     UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
     UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId, parentReferenceNodeId,
                               myIntegerName, baseDataVariableType, myVar, NULL, NULL);
-    UA_Variant_deleteMembers(&myVar.value);
 
     /* add a static variable that is readable but not writable*/
     myVar = UA_VariableAttributes_default;
@@ -184,12 +125,24 @@ main(int argc, char **argv) {
     myVar.accessLevel = UA_ACCESSLEVELMASK_WRITE;
     myVar.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
     myVar.valueRank = -1;
-    UA_Variant_setScalarCopy(&myVar.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
+    UA_Variant_setScalar(&myVar.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
     const UA_QualifiedName myInteger2Name = UA_QUALIFIEDNAME(1, "the answer - not readable");
     const UA_NodeId myInteger2NodeId = UA_NODEID_STRING(1, "the.answer.no.read");
     UA_Server_addVariableNode(server, myInteger2NodeId, parentNodeId, parentReferenceNodeId,
                               myInteger2Name, baseDataVariableType, myVar, NULL, NULL);
-    UA_Variant_deleteMembers(&myVar.value);
+
+    /* add a variable that is not readable or writable for the current user */
+    myVar = UA_VariableAttributes_default;
+    myVar.description = UA_LOCALIZEDTEXT("en-US", "the answer - not current user");
+    myVar.displayName = UA_LOCALIZEDTEXT("en-US", "the answer - not current user");
+    myVar.accessLevel = UA_ACCESSLEVELMASK_WRITE;
+    myVar.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
+    myVar.valueRank = -1;
+    myVar.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
+    UA_Variant_setScalar(&myVar.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
+    const UA_QualifiedName accessDeniedName = UA_QUALIFIEDNAME(1, "the answer - not current user");
+    UA_Server_addVariableNode(server, accessDenied, parentNodeId, parentReferenceNodeId,
+                              accessDeniedName, baseDataVariableType, myVar, NULL, NULL);
 
     /* add a variable with the datetime data source */
     UA_DataSource dateDataSource;
@@ -206,6 +159,21 @@ main(int argc, char **argv) {
                                         UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), dateName,
                                         baseDataVariableType, v_attr, dateDataSource, NULL, NULL);
 
+    /* add a bytestring variable with some content */
+    myVar = UA_VariableAttributes_default;
+    myVar.description = UA_LOCALIZEDTEXT("", "");
+    myVar.displayName = UA_LOCALIZEDTEXT("", "example bytestring");
+    myVar.dataType = UA_TYPES[UA_TYPES_BYTESTRING].typeId;
+    myVar.valueRank = -1;
+    myVar.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
+    UA_ByteString myByteString = UA_BYTESTRING("test123\0test123");
+    UA_Variant_setScalar(&myVar.value, &myByteString, &UA_TYPES[UA_TYPES_BYTESTRING]);
+    const UA_QualifiedName byteStringName = UA_QUALIFIEDNAME(1, "example bytestring");
+    UA_Server_addVariableNode(server, UA_NODEID_STRING(1, "myByteString"),
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), byteStringName,
+                              baseDataVariableType, myVar, NULL, NULL);
+
     /* Add HelloWorld method to the server */
 #ifdef UA_ENABLE_METHODCALLS
     /* Method with IO Arguments */
@@ -279,7 +247,6 @@ main(int argc, char **argv) {
             continue;
 
         UA_VariableAttributes attr = UA_VariableAttributes_default;
-        attr.valueRank = -2;
         attr.dataType = UA_TYPES[type].typeId;
 #ifndef UA_ENABLE_TYPENAMES
         char name[15];
@@ -295,6 +262,7 @@ main(int argc, char **argv) {
         attr.userWriteMask = UA_WRITEMASK_DISPLAYNAME | UA_WRITEMASK_DESCRIPTION;
 
         /* add a scalar node for every built-in type */
+        attr.valueRank = -1;
         void *value = UA_new(&UA_TYPES[type]);
         UA_Variant_setScalar(&attr.value, value, &UA_TYPES[type]);
         UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id),
@@ -303,6 +271,7 @@ main(int argc, char **argv) {
         UA_Variant_deleteMembers(&attr.value);
 
         /* add an array node for every built-in type */
+        attr.valueRank = 1;
         UA_Variant_setArray(&attr.value, UA_Array_new(10, &UA_TYPES[type]), 10, &UA_TYPES[type]);
         UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id), UA_NODEID_NUMERIC(1, ARRAYID),
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), qualifiedName,
@@ -310,6 +279,7 @@ main(int argc, char **argv) {
         UA_Variant_deleteMembers(&attr.value);
 
         /* add an matrix node for every built-in type */
+        attr.valueRank = 2;
         void *myMultiArray = UA_Array_new(9, &UA_TYPES[type]);
         attr.value.arrayDimensions = (UA_UInt32 *)UA_Array_new(2, &UA_TYPES[UA_TYPES_INT32]);
         attr.value.arrayDimensions[0] = 3;
@@ -430,6 +400,81 @@ main(int argc, char **argv) {
                             &outargMethod, /* callback of the method node */
                             1, &inputArguments, 1, &outputArguments, NULL, NULL);
 #endif
+}
+
+UA_Boolean running = true;
+
+static void
+stopHandler(int sign) {
+    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Received Ctrl-C");
+    running = 0;
+}
+
+int main(int argc, char **argv) {
+    signal(SIGINT, stopHandler); /* catches ctrl-c */
+    signal(SIGTERM, stopHandler);
+
+#ifdef UA_ENABLE_ENCRYPTION
+    if(argc < 3) {
+        UA_LOG_FATAL(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
+                     "Missing arguments for encryption support. "
+                         "Arguments are <server-certificate.der> "
+                         "<private-key.der> [<trustlist1.crl>, ...]");
+        return 1;
+    }
+
+    /* Load certificate and private key */
+    UA_ByteString certificate = loadFile(argv[1]);
+    UA_ByteString privateKey = loadFile(argv[2]);
+
+    /* Load the trustlist */
+    size_t trustListSize = 0;
+    if(argc > 3)
+        trustListSize = (size_t)argc-3;
+    UA_STACKARRAY(UA_ByteString, trustList, trustListSize);
+    for(size_t i = 0; i < trustListSize; i++)
+        trustList[i] = loadFile(argv[i+3]);
+
+    /* Loading of a revocation list currently unsupported */
+    UA_ByteString *revocationList = NULL;
+    size_t revocationListSize = 0;
+
+    UA_ServerConfig *config =
+        UA_ServerConfig_new_allSecurityPolicies(4840, &certificate, &privateKey,
+                                                trustList, trustListSize,
+                                                revocationList, revocationListSize);
+    UA_ByteString_deleteMembers(&certificate);
+    UA_ByteString_deleteMembers(&privateKey);
+    for(size_t i = 0; i < trustListSize; i++)
+        UA_ByteString_deleteMembers(&trustList[i]);
+
+    if(!config) {
+        UA_LOG_FATAL(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
+                     "Could not create the server config");
+        return 1;
+    }
+#else
+    if(argc < 2) {
+        UA_LOG_FATAL(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
+                     "Missing argument for the server certificate");
+        return 1;
+    }
+    UA_ByteString certificate = loadFile(argv[1]);
+    UA_ServerConfig *config = UA_ServerConfig_new_minimal(4840, &certificate);
+    UA_ByteString_deleteMembers(&certificate);
+#endif
+
+    /* Override with a custom access control policy */
+    config->accessControl.getUserAccessLevel = getUserAccessLevel_disallowSpecific;
+
+    /* uncomment next line to add a custom hostname */
+    // UA_ServerConfig_set_customHostname(config, UA_STRING("custom"));
+
+    UA_Server *server = UA_Server_new(config);
+    if(server == NULL)
+        return 1;
+
+    setInformationModel(server);
 
     /* run server */
     UA_StatusCode retval = UA_Server_run(server, &running);

+ 7 - 0
include/ua_client_highlevel_async.h

@@ -8,6 +8,10 @@
 #define UA_CLIENT_HIGHLEVEL_ASYNC_H_
 #include "ua_client.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /**
  * Raw Services
  * ^^^^^^^^^^^^ */
@@ -627,5 +631,8 @@ static UA_INLINE UA_StatusCode UA_Cient_translateBrowsePathsToNodeIds_async(
 			pathSize, (UA_ClientAsyncServiceCallback) callback, userdata, reqId);
 }
 
+#ifdef __cplusplus
+} // extern "C"
+#endif
 
 #endif /* UA_CLIENT_HIGHLEVEL_ASYNC_H_ */

+ 0 - 1
include/ua_config.h.in

@@ -45,7 +45,6 @@ extern "C" {
 #cmakedefine UA_ENABLE_STATUSCODE_DESCRIPTIONS
 #cmakedefine UA_ENABLE_TYPENAMES
 #cmakedefine UA_ENABLE_DETERMINISTIC_RNG
-#cmakedefine UA_ENABLE_GENERATE_NAMESPACE0
 #cmakedefine UA_ENABLE_NONSTANDARD_UDP
 #cmakedefine UA_ENABLE_DISCOVERY
 #cmakedefine UA_ENABLE_DISCOVERY_MULTICAST

+ 1 - 4
src/server/ua_server.c

@@ -20,9 +20,6 @@
 #include "ua_types.h"
 #include "ua_server_internal.h"
 
-#ifdef UA_ENABLE_GENERATE_NAMESPACE0
-#include "ua_namespaceinit_generated.h"
-#endif
 #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
 #include "ua_pubsub_ns0.h"
 #endif
@@ -67,7 +64,7 @@ UA_UInt16 UA_Server_addNamespace(UA_Server *server, const char* name) {
     return addNamespace(server, nameString);
 }
 
-UA_StatusCode 
+UA_StatusCode
 UA_Server_getNamespaceByName(UA_Server *server, const UA_String namespaceUri,
                              size_t* foundIndex) {
   for(size_t idx = 0; idx < server->namespacesSize; idx++)

+ 1 - 0
src/server/ua_services_subscription.c

@@ -162,6 +162,7 @@ setMonitoredItemSettings(UA_Server *server, UA_MonitoredItem *mon,
                          // Then numeric type will be detected from this value. Set null as defaut.
                          const UA_DataType* dataType) {
 
+
     /* Filter */
     if(params->filter.encoding != UA_EXTENSIONOBJECT_DECODED) {
         UA_DataChangeFilter_init(&(mon->filter.dataChangeFilter));

+ 3 - 0
src/server/ua_subscription_datachange.c

@@ -81,6 +81,8 @@ void UA_MonitoredItem_delete(UA_Server *server, UA_MonitoredItem *monitoredItem)
 #endif /* UA_ENABLE_SUBSCRIPTIONS_EVENTS */
 
     /* Remove the monitored item */
+    if(monitoredItem->listEntry.le_prev != NULL)
+        LIST_REMOVE(monitoredItem, listEntry);
     UA_String_deleteMembers(&monitoredItem->indexRange);
     UA_ByteString_deleteMembers(&monitoredItem->lastSampledValue);
     UA_Variant_deleteMembers(&monitoredItem->lastValue);
@@ -267,6 +269,7 @@ updateNeededForFilteredValue(const UA_Variant *value, const UA_Variant *oldValue
     return false;
 }
 
+
 /* When a change is detected, encoding contains the heap-allocated binary encoded value */
 static UA_Boolean
 detectValueChangeWithFilter(UA_Server *server, UA_MonitoredItem *mon, UA_DataValue *value,

+ 68 - 43
src/ua_securechannel.c

@@ -33,9 +33,9 @@ const UA_ByteString
     UA_SECURITY_POLICY_NONE_URI = {47, (UA_Byte *)"http://opcfoundation.org/UA/SecurityPolicy#None"};
 
 #ifdef UA_ENABLE_UNIT_TEST_FAILURE_HOOKS
-UA_THREAD_LOCAL UA_StatusCode decrypt_verifySignatureFailure;
-UA_THREAD_LOCAL UA_StatusCode sendAsym_sendFailure;
-UA_THREAD_LOCAL UA_StatusCode processSym_seqNumberFailure;
+UA_StatusCode decrypt_verifySignatureFailure;
+UA_StatusCode sendAsym_sendFailure;
+UA_StatusCode processSym_seqNumberFailure;
 #endif
 
 UA_StatusCode
@@ -108,11 +108,15 @@ UA_SecureChannel_deleteMembersCleanup(UA_SecureChannel *channel) {
     }
 
     /* Remove the buffered chunks */
-    struct ChunkEntry *ch, *temp_ch;
-    LIST_FOREACH_SAFE(ch, &channel->chunks, pointers, temp_ch) {
-        UA_ByteString_deleteMembers(&ch->bytes);
-        LIST_REMOVE(ch, pointers);
-        UA_free(ch);
+    struct MessageEntry *me, *temp_me;
+    LIST_FOREACH_SAFE(me, &channel->chunks, pointers, temp_me) {
+        struct ChunkPayload *cp, *temp_cp;
+        SIMPLEQ_FOREACH_SAFE(cp, &me->chunkPayload, pointers, temp_cp) {
+            UA_ByteString_deleteMembers(&cp->bytes);
+            UA_free(cp);
+        }
+        LIST_REMOVE(me, pointers);
+        UA_free(me);
     }
 }
 
@@ -725,77 +729,98 @@ UA_SecureChannel_sendSymmetricMessage(UA_SecureChannel *channel, UA_UInt32 reque
 
 static void
 UA_SecureChannel_removeChunks(UA_SecureChannel *channel, UA_UInt32 requestId) {
-    struct ChunkEntry *ch;
-    LIST_FOREACH(ch, &channel->chunks, pointers) {
-        if(ch->requestId == requestId) {
-            UA_ByteString_deleteMembers(&ch->bytes);
-            LIST_REMOVE(ch, pointers);
-            UA_free(ch);
+    struct MessageEntry *me;
+    LIST_FOREACH(me, &channel->chunks, pointers) {
+        if(me->requestId == requestId) {
+            struct ChunkPayload *cp, *temp_cp;
+            SIMPLEQ_FOREACH_SAFE(cp, &me->chunkPayload, pointers, temp_cp) {
+                UA_ByteString_deleteMembers(&cp->bytes);
+                UA_free(cp);
+            }
+            LIST_REMOVE(me, pointers);
+            UA_free(me);
             return;
         }
     }
 }
 
 static UA_StatusCode
-appendChunk(struct ChunkEntry *chunkEntry, const UA_ByteString *chunkBody) {
-    UA_Byte *new_bytes = (UA_Byte *)
-        UA_realloc(chunkEntry->bytes.data, chunkEntry->bytes.length + chunkBody->length);
-    if(!new_bytes) {
-        UA_ByteString_deleteMembers(&chunkEntry->bytes);
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-    }
-    chunkEntry->bytes.data = new_bytes;
-    memcpy(&chunkEntry->bytes.data[chunkEntry->bytes.length], chunkBody->data, chunkBody->length);
-    chunkEntry->bytes.length += chunkBody->length;
+appendChunk(struct MessageEntry *messageEntry, const UA_ByteString *chunkBody) {
+    
+    struct ChunkPayload* cp = (struct ChunkPayload*)UA_malloc(sizeof(struct ChunkPayload));
+    UA_StatusCode retval = UA_ByteString_copy(chunkBody, &cp->bytes);
+    if (retval != UA_STATUSCODE_GOOD)
+        return retval;
+    
+    SIMPLEQ_INSERT_TAIL(&messageEntry->chunkPayload, cp, pointers);
+    messageEntry->chunkPayloadSize += chunkBody->length;
     return UA_STATUSCODE_GOOD;
 }
 
 static UA_StatusCode
 UA_SecureChannel_appendChunk(UA_SecureChannel *channel, UA_UInt32 requestId,
                              const UA_ByteString *chunkBody) {
-    struct ChunkEntry *ch;
-    LIST_FOREACH(ch, &channel->chunks, pointers) {
-        if(ch->requestId == requestId)
+    struct MessageEntry *me;
+    LIST_FOREACH(me, &channel->chunks, pointers) {
+        if(me->requestId == requestId)
             break;
     }
 
     /* No chunkentry on the channel, create one */
-    if(!ch) {
-        ch = (struct ChunkEntry *)UA_malloc(sizeof(struct ChunkEntry));
-        if(!ch)
+    if(!me) {
+        me = (struct MessageEntry *)UA_malloc(sizeof(struct MessageEntry));
+        if(!me)
             return UA_STATUSCODE_BADOUTOFMEMORY;
-        ch->requestId = requestId;
-        UA_ByteString_init(&ch->bytes);
-        LIST_INSERT_HEAD(&channel->chunks, ch, pointers);
+        memset(me, 0, sizeof(struct MessageEntry));
+        me->requestId = requestId;
+        SIMPLEQ_INIT(&me->chunkPayload);
+        LIST_INSERT_HEAD(&channel->chunks, me, pointers);
     }
 
-    return appendChunk(ch, chunkBody);
+    return appendChunk(me, chunkBody);
 }
 
 static UA_StatusCode
 UA_SecureChannel_finalizeChunk(UA_SecureChannel *channel, UA_UInt32 requestId,
                                const UA_ByteString *chunkBody, UA_MessageType messageType,
                                UA_ProcessMessageCallback callback, void *application) {
-    struct ChunkEntry *chunkEntry;
-    LIST_FOREACH(chunkEntry, &channel->chunks, pointers) {
-        if(chunkEntry->requestId == requestId)
+    struct MessageEntry *messageEntry;
+    LIST_FOREACH(messageEntry, &channel->chunks, pointers) {
+        if(messageEntry->requestId == requestId)
             break;
     }
 
     UA_ByteString bytes;
-    if(!chunkEntry) {
+    if(!messageEntry) {
         bytes = *chunkBody;
     } else {
-        UA_StatusCode retval = appendChunk(chunkEntry, chunkBody);
+        UA_StatusCode retval = appendChunk(messageEntry, chunkBody);
         if(retval != UA_STATUSCODE_GOOD)
             return retval;
-        bytes = chunkEntry->bytes;
-        LIST_REMOVE(chunkEntry, pointers);
-        UA_free(chunkEntry);
+        
+        UA_ByteString_init(&bytes);
+
+        bytes.data = (UA_Byte*) UA_malloc(messageEntry->chunkPayloadSize);
+        if (!bytes.data)
+            return UA_STATUSCODE_BADOUTOFMEMORY;
+
+        struct ChunkPayload *cp, *temp_cp;
+        size_t curPos = 0;
+        SIMPLEQ_FOREACH_SAFE(cp, &messageEntry->chunkPayload, pointers, temp_cp) {
+            memcpy(&bytes.data[curPos], cp->bytes.data, cp->bytes.length);
+            curPos += cp->bytes.length;
+            UA_ByteString_deleteMembers(&cp->bytes);
+            UA_free(cp);
+        }
+
+        bytes.length = messageEntry->chunkPayloadSize;
+
+        LIST_REMOVE(messageEntry, pointers);
+        UA_free(messageEntry);
     }
 
     UA_StatusCode retval = callback(application, channel, messageType, requestId, &bytes);
-    if(chunkEntry)
+    if(messageEntry)
         UA_ByteString_deleteMembers(&bytes);
     return retval;
 }

+ 13 - 7
src/ua_securechannel.h

@@ -27,9 +27,9 @@ extern "C" {
 
 /* Thread-local variables to force failure modes during testing */
 #ifdef UA_ENABLE_UNIT_TEST_FAILURE_HOOKS
-extern UA_THREAD_LOCAL UA_StatusCode decrypt_verifySignatureFailure;
-extern UA_THREAD_LOCAL UA_StatusCode sendAsym_sendFailure;
-extern UA_THREAD_LOCAL UA_StatusCode processSym_seqNumberFailure;
+extern UA_StatusCode decrypt_verifySignatureFailure;
+extern UA_StatusCode sendAsym_sendFailure;
+extern UA_StatusCode processSym_seqNumberFailure;
 #endif
 
 /* The Session implementation differs between client and server. Still, it is
@@ -44,12 +44,18 @@ typedef struct UA_SessionHeader {
 } UA_SessionHeader;
 
 /* For chunked requests */
-struct ChunkEntry {
-    LIST_ENTRY(ChunkEntry) pointers;
-    UA_UInt32 requestId;
+struct ChunkPayload {
+    SIMPLEQ_ENTRY(ChunkPayload) pointers;
     UA_ByteString bytes;
 };
 
+struct MessageEntry {
+    LIST_ENTRY(MessageEntry) pointers;
+    UA_UInt32 requestId;
+    SIMPLEQ_HEAD(chunkpayload_pointerlist, ChunkPayload) chunkPayload;
+    size_t chunkPayloadSize;
+};
+
 typedef enum {
     UA_SECURECHANNELSTATE_FRESH,
     UA_SECURECHANNELSTATE_OPEN,
@@ -79,7 +85,7 @@ struct UA_SecureChannel {
     UA_UInt32 sendSequenceNumber;
 
     LIST_HEAD(session_pointerlist, UA_SessionHeader) sessions;
-    LIST_HEAD(chunk_pointerlist, ChunkEntry) chunks;
+    LIST_HEAD(chunk_pointerlist, MessageEntry) chunks;
 };
 
 UA_StatusCode

+ 2 - 1
src/ua_types.c

@@ -71,7 +71,8 @@ UA_findDataType(const UA_NodeId *typeId) {
 /* Random Number Generator */
 /***************************/
 
-static UA_THREAD_LOCAL pcg32_random_t UA_rng = PCG32_INITIALIZER;
+//TODO is this safe for multithreading?
+static pcg32_random_t UA_rng = PCG32_INITIALIZER;
 
 void
 UA_random_seed(u64 seed) {

+ 3 - 3
tools/appveyor/install.ps1

@@ -13,8 +13,8 @@ try {
 
     Write-Host -ForegroundColor Green "`n### Installing Miktex ###`n"
     if (-not (Test-Path "c:\miktex\texmfs\install\miktex\bin\pdflatex.exe")) {
-        & appveyor DownloadFile https://ftp.uni-erlangen.de/mirrors/CTAN/systems/win32/miktex/setup/windows-x86/miktex-portable.exe
-        & 7z x miktex-portable.exe -oc:\miktex -bso0 -bsp0
+        & appveyor DownloadFile https://ftp.uni-erlangen.de/mirrors/CTAN/systems/win32/miktex/setup/windows-x86/miktex-portable-2.9.6753.exe
+        & 7z x miktex-portable-2.9.6753.exe -oc:\miktex -bso0 -bsp0
 
         # Remove some big files to reduce size to be cached
         Remove-Item -Path c:\miktex\texmfs\install\doc -Recurse
@@ -60,4 +60,4 @@ try {
     # Wait a bit to make sure appveyor shows the error message
     Start-Sleep 10
     throw
-}
+}

+ 10 - 3
tools/clang-format_precommit_hook

@@ -5,11 +5,11 @@
 # the staged changes.
 #
 # To install, copy this script to `.git/hooks/pre-commit`:
-# ln -s ../../tools/clang-format_precommit_hook .git/hooks/pre-commit
+# cd .git/hooks && ln -s ../../tools/clang-format_precommit_hook pre-commit
 # and make sure that `clang-format` is installed on your system
 
 maj_min=1
-maj_max=3
+maj_max=8
 
 base=clang-format
 format=""
@@ -52,5 +52,12 @@ fi
 # do the formatting
 for file in `git diff-index --cached --name-only $against`
 do
-    "$format" -i "$file"
+    case "$file" in
+    *.h | *.hpp | *.c | *.cpp )
+            "$format" -i "$file"
+            ;;
+    *)
+            # do nothing with file
+            ;;
+    esac
 done