Browse Source

Merge branch '0.2'

Julius Pfrommer 7 years ago
parent
commit
875d7f060c
43 changed files with 2120 additions and 1508 deletions
  1. 13 1
      CMakeLists.txt
  2. 4 0
      README.md
  3. 1 1
      doc/tutorial_client_firstSteps.rst
  4. 7 8
      examples/client.c
  5. 0 1
      examples/client_firstSteps.c
  6. 1 20
      examples/server.c
  7. 6 1
      examples/server_nodeset.c
  8. 122 18
      include/ua_config.h.in
  9. 9 11
      include/ua_connection.h
  10. 24 13
      include/ua_constants.h
  11. 21 1
      include/ua_types.h
  12. 44 40
      plugins/ua_network_tcp.c
  13. 122 82
      src/client/ua_client.c
  14. 43 29
      src/client/ua_client_highlevel.c
  15. 17 9
      src/client/ua_client_highlevel_subscriptions.c
  16. 1 1
      src/server/ua_nodes.h
  17. 1 0
      src/server/ua_nodestore_concurrent.c
  18. 7 19
      src/server/ua_securechannel_manager.c
  19. 1 1
      src/server/ua_securechannel_manager.h
  20. 16 9
      src/server/ua_server.c
  21. 3 7
      src/server/ua_server_binary.c
  22. 47 20
      src/server/ua_server_internal.h
  23. 18 16
      src/server/ua_server_worker.c
  24. 721 661
      src/server/ua_services_attribute.c
  25. 4 6
      src/server/ua_services_call.c
  26. 139 95
      src/server/ua_services_nodemanagement.c
  27. 16 22
      src/server/ua_session_manager.c
  28. 135 96
      src/server/ua_subscription.c
  29. 14 48
      src/ua_connection.c
  30. 26 39
      src/ua_securechannel.c
  31. 121 102
      src/ua_types.c
  32. 4 73
      src/ua_util.h
  33. 6 8
      tests/CMakeLists.txt
  34. 2 0
      tests/check_server_userspace.c
  35. 25 25
      tests/check_services_attributes.c
  36. 33 0
      tests/check_utils.c
  37. 2 0
      tools/generate_datatypes.py
  38. 65 0
      tools/generate_statuscode_descriptions.py
  39. 5 3
      tools/pyUANamespace/ua_builtin_types.py
  40. 37 19
      tools/pyUANamespace/ua_namespace.py
  41. 227 0
      tools/schema/Opc.Ua.StatusCodes.csv
  42. 2 2
      tools/travis/travis_linux_before_install.sh
  43. 8 1
      tools/travis/travis_linux_script.sh

+ 13 - 1
CMakeLists.txt

@@ -58,7 +58,10 @@ if(UA_ENABLE_COVERAGE)
 endif()
 
 # Advanced options
-option(UA_ENABLE_TYPENAMES "Add the type and member names to the UA_DataType structure" OFF)
+option(UA_ENABLE_STATUSCODE_DESCRIPTIONS "Enable conversion of StatusCode to human-readable error message" ON)
+mark_as_advanced(UA_ENABLE_STATUSCODE_DESCRIPTIONS)
+
+option(UA_ENABLE_TYPENAMES "Add the type and member names to the UA_DataType structure" ON)
 mark_as_advanced(UA_ENABLE_TYPENAMES)
 
 option(UA_ENABLE_EMBEDDED_LIBC "Use a custom implementation of some libc functions that might be missing on embedded targets (e.g. string handling)." OFF)
@@ -297,6 +300,15 @@ add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_nodeids.h
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_nodeids.py
                            ${CMAKE_CURRENT_SOURCE_DIR}/tools/schema/NodeIds.csv)
 
+# statuscode explanation
+add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_statuscode_descriptions.c
+        PRE_BUILD
+        COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_statuscode_descriptions.py
+        ${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.StatusCodes.csv ${PROJECT_BINARY_DIR}/src_generated/ua_statuscode_descriptions
+        DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_statuscode_descriptions.py
+        ${CMAKE_CURRENT_SOURCE_DIR}/tools/schema/Opc.Ua.StatusCodes.csv)
+list(APPEND lib_sources ${PROJECT_BINARY_DIR}/src_generated/ua_statuscode_descriptions.c)
+
 # generated namespace 0
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_generated.c
                           ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_generated.h

File diff suppressed because it is too large
+ 4 - 0
README.md


+ 1 - 1
doc/tutorial_client_firstSteps.rst

@@ -8,7 +8,7 @@ creating a server. Copy the following into a file `myClient.c`:
 .. literalinclude:: client_firstSteps.c
    :language: c
    :linenos:
-   :lines: 4,5,12,14-
+   :lines: 4,5,14,17-
 
 Compilation is similar to the server example.
 

+ 7 - 8
examples/client.c

@@ -16,22 +16,21 @@
 
 #include <stdio.h>
 
-static void handler_TheAnswerChanged(UA_UInt32 monId, UA_DataValue *value, void *context) {
+static void
+handler_TheAnswerChanged(UA_UInt32 monId, UA_DataValue *value, void *context) {
     printf("The Answer has changed!\n");
-    return;
 }
 
 static UA_StatusCode
 nodeIter(UA_NodeId childId, UA_Boolean isInverse, UA_NodeId referenceTypeId, void *handle) {
-  UA_NodeId *parent = (UA_NodeId *) handle;
-
-  if(!isInverse) {
+    if(isInverse)
+        return UA_STATUSCODE_GOOD;
+    UA_NodeId *parent = (UA_NodeId *)handle;
     printf("%d, %d --- %d ---> NodeId %d, %d\n",
            parent->namespaceIndex, parent->identifier.numeric,
            referenceTypeId.identifier.numeric, childId.namespaceIndex,
            childId.identifier.numeric);
-  }
-  return UA_STATUSCODE_GOOD;
+    return UA_STATUSCODE_GOOD;
 }
 
 int main(int argc, char *argv[]) {
@@ -212,7 +211,7 @@ int main(int argc, char *argv[]) {
     retval = UA_Client_addObjectTypeNode(client,
                                          UA_NODEID_NUMERIC(1, 12134),
                                          UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
-                                         UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                         UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
                                          UA_QUALIFIEDNAME(1, "NewObjectType"),
                                          objt_attr, &objt_id);
     if(retval == UA_STATUSCODE_GOOD)

+ 0 - 1
examples/client_firstSteps.c

@@ -7,7 +7,6 @@
 #ifdef UA_NO_AMALGAMATION
 #include "ua_client.h"
 #include "ua_config_standard.h"
-#include "ua_network_tcp.h"
 #else
 #include "open62541.h"
 #endif

+ 1 - 20
examples/server.c

@@ -1,10 +1,6 @@
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
 
-/* Compile with single file release:
- * - single-threaded: gcc -std=c99 server.c open62541.c -o server
- * - multi-threaded: gcc -std=c99 server.c open62541.c -o server -lurcu-cds -lurcu -lurcu-common -lpthread */
-
 #ifdef _MSC_VER
 #define _CRT_SECURE_NO_WARNINGS //disable fopen deprication warning in msvs
 #endif
@@ -26,22 +22,7 @@
 #include <stdlib.h>
 #include <string.h>
 
-#ifdef _MSC_VER
-# include <io.h> //access
-#else
-# include <unistd.h> //access
-#endif
-
-#ifdef UA_ENABLE_MULTITHREADING
-# ifdef UA_NO_AMALGAMATION
-#  ifndef __USE_XOPEN2K
-#   define __USE_XOPEN2K
-#  endif
-# endif
-#include <pthread.h>
-#endif
-
-UA_Boolean running = 1;
+UA_Boolean running = true;
 UA_Logger logger = UA_Log_Stdout;
 
 static UA_ByteString loadCertificate(void) {

+ 6 - 1
examples/server_nodeset.c

@@ -36,7 +36,12 @@ int main(int argc, char** argv) {
     UA_Server *server = UA_Server_new(config);
 
     /* create nodes from nodeset */
-    nodeset(server);
+    if (nodeset(server) != UA_STATUSCODE_GOOD) {
+		UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER, "Namespace index for generated nodeset does not match. The call to the generated method has to be before any other namespace add calls.");
+		UA_Server_delete(server);
+		nl.deleteMembers(&nl);
+		return (int)UA_STATUSCODE_BADUNEXPECTEDERROR;
+	}
 
     /* start server */
     UA_StatusCode retval = UA_Server_run(server, &running); //UA_blocks until running=false

+ 122 - 18
include/ua_config.h.in

@@ -20,35 +20,50 @@
 extern "C" {
 #endif
 
-#ifndef _XOPEN_SOURCE
-# define _XOPEN_SOURCE 500
-#endif
-#ifndef _DEFAULT_SOURCE
-# define _DEFAULT_SOURCE
-#endif
+/**
+ * Library Version
+ * --------------- */
+#define UA_GIT_COMMIT_ID "${GIT_COMMIT_ID}"
 
+/**
+ * Options
+ * ------- */
 #define UA_LOGLEVEL ${UA_LOGLEVEL}
-#define UA_GIT_COMMIT_ID "${GIT_COMMIT_ID}"
-#cmakedefine UA_ENABLE_MULTITHREADING
 #cmakedefine UA_ENABLE_METHODCALLS
+#cmakedefine UA_ENABLE_NODEMANAGEMENT
 #cmakedefine UA_ENABLE_SUBSCRIPTIONS
 #cmakedefine UA_ENABLE_DISCOVERY
+#cmakedefine UA_ENABLE_MULTITHREADING
+
+/**
+ * Advanced Options
+ * ---------------- */
+#cmakedefine UA_ENABLE_STATUSCODE_DESCRIPTIONS
 #cmakedefine UA_ENABLE_TYPENAMES
+#cmakedefine UA_ENABLE_EMBEDDED_LIBC
 #cmakedefine UA_ENABLE_GENERATE_NAMESPACE0
 #cmakedefine UA_ENABLE_EXTERNAL_NAMESPACES
-#cmakedefine UA_ENABLE_NODEMANAGEMENT
-
-#cmakedefine UA_ENABLE_EMBEDDED_LIBC
-
-#cmakedefine UA_ENABLE_NONSTANDARD_UDP
 #cmakedefine UA_ENABLE_NONSTANDARD_STATELESS
+#cmakedefine UA_ENABLE_NONSTANDARD_UDP
+
+/**
+ * Standard Includes
+ * ----------------- */
+#ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 500
+#endif
+#ifndef _DEFAULT_SOURCE
+# define _DEFAULT_SOURCE
+#endif
+#include <stddef.h>
+#include <stdint.h>
 
 /**
  * Function Export
- * --------------- */
-/* On Win32: Define UA_DYNAMIC_LINKING and UA_DYNAMIC_LINKING_EXPORT in order to
-   export symbols for a DLL. Define UA_DYNAMIC_LINKING only to import symbols
-   from a DLL.*/
+ * ---------------
+ * On Win32: Define ``UA_DYNAMIC_LINKING`` and ``UA_DYNAMIC_LINKING_EXPORT`` in
+ * order to export symbols for a DLL. Define ``UA_DYNAMIC_LINKING`` only to
+ * import symbols from a DLL.*/
 #cmakedefine UA_DYNAMIC_LINKING
 #if defined(_WIN32) && defined(UA_DYNAMIC_LINKING)
 # ifdef UA_DYNAMIC_LINKING_EXPORT /* export dll */
@@ -108,6 +123,96 @@ extern "C" {
 # define UA_FUNC_ATTR_WARN_UNUSED_RESULT
 #endif
 
+/**
+ * Memory Management
+ * -----------------
+ * Replace the macros for custom memory allocators if necessary */
+#include <stdlib.h>
+#ifdef _WIN32
+# ifndef __clang__
+#  include <malloc.h>
+# endif
+#endif
+
+#define UA_free(ptr) free(ptr)
+#define UA_malloc(size) malloc(size)
+#define UA_calloc(num, size) calloc(num, size)
+#define UA_realloc(ptr, size) realloc(ptr, size)
+
+#ifndef NO_ALLOCA
+# if defined(__GNUC__) || defined(__clang__)
+#  define UA_alloca(size) __builtin_alloca (size)
+# elif defined(_WIN32)
+#  define UA_alloca(SIZE) _alloca(SIZE)
+# else
+#  include <alloca.h>
+#  define UA_alloca(SIZE) alloca(SIZE)
+# endif
+#endif
+
+/**
+ * Atomic Operations
+ * -----------------
+ * Atomic operations that synchronize across processor cores (for
+ * multithreading). Only the inline-functions defined next are used. Replace
+ * with architecture-specific operations if necessary. */
+
+#ifndef UA_ENABLE_MULTITHREADING
+# define UA_atomic_sync()
+#else
+# ifdef _MSC_VER /* Visual Studio */
+#  define UA_atomic_sync() _ReadWriteBarrier()
+# else /* GCC/Clang */
+#  define UA_atomic_sync() __sync_synchronize()
+# endif
+#endif
+
+static UA_INLINE void *
+UA_atomic_xchg(void * volatile * addr, void *newptr) {
+#ifndef UA_ENABLE_MULTITHREADING
+    void *old = *addr;
+    *addr = newptr;
+    return old;
+#else
+# ifdef _MSC_VER /* Visual Studio */
+    return _InterlockedExchangePointer(addr, newptr);
+# else /* GCC/Clang */
+    return __sync_lock_test_and_set(addr, newptr);
+# endif
+#endif
+}
+
+static UA_INLINE void *
+UA_atomic_cmpxchg(void * volatile * addr, void *expected, void *newptr) {
+#ifndef UA_ENABLE_MULTITHREADING
+    void *old = *addr;
+    if(old == expected) {
+        *addr = newptr;
+    }
+    return old;
+#else
+# ifdef _MSC_VER /* Visual Studio */
+    return _InterlockedCompareExchangePointer(addr, expected, newptr);
+# else /* GCC/Clang */
+    return __sync_val_compare_and_swap(addr, expected, newptr);
+# endif
+#endif
+}
+
+static UA_INLINE uint32_t
+UA_atomic_add(volatile uint32_t *addr, uint32_t increase) {
+#ifndef UA_ENABLE_MULTITHREADING
+    *addr += increase;
+    return *addr;
+#else
+# ifdef _MSC_VER /* Visual Studio */
+    return _InterlockedExchangeAdd(addr, increase) + increase;
+# else /* GCC/Clang */
+    return __sync_add_and_fetch(addr, increase);
+# endif
+#endif
+}
+
 /**
  * Binary Encoding Overlays
  * ------------------------
@@ -180,7 +285,6 @@ extern "C" {
 /**
  * Embed unavailable libc functions
  * -------------------------------- */
-#include <stddef.h>
 #ifdef UA_ENABLE_EMBEDDED_LIBC
   void *memcpy(void *UA_RESTRICT dest, const void *UA_RESTRICT src, size_t n);
   void *memset(void *dest, int c, size_t n);

+ 9 - 11
include/ua_connection.h

@@ -22,13 +22,6 @@ extern "C" {
 
 #include "ua_types.h"
 
-/* Forward declarations */
-struct UA_Connection;
-typedef struct UA_Connection UA_Connection;
-
-struct UA_SecureChannel;
-typedef struct UA_SecureChannel UA_SecureChannel;
-
 /**
  * Networking
  * ----------
@@ -44,7 +37,7 @@ typedef struct UA_SecureChannel UA_SecureChannel;
  *
  * Connection Config
  * ^^^^^^^^^^^^^^^^^ */
-typedef struct UA_ConnectionConfig {
+typedef struct {
     UA_UInt32 protocolVersion;
     UA_UInt32 sendBufferSize;
     UA_UInt32 recvBufferSize;
@@ -57,7 +50,7 @@ extern const UA_EXPORT UA_ConnectionConfig UA_ConnectionConfig_standard;
 /**
  * Connection Structure
  * ^^^^^^^^^^^^^^^^^^^^ */
-typedef enum UA_ConnectionState {
+typedef enum {
     UA_CONNECTION_OPENING,     /* The socket is open, but the HEL/ACK handshake
                                   is not done */
     UA_CONNECTION_ESTABLISHED, /* The socket is open and the connection
@@ -66,6 +59,13 @@ typedef enum UA_ConnectionState {
                                   will be deleted */
 } UA_ConnectionState;
 
+/* Forward declarations */
+struct UA_Connection;
+typedef struct UA_Connection UA_Connection;
+
+struct UA_SecureChannel;
+typedef struct UA_SecureChannel UA_SecureChannel;
+
 struct UA_Connection {
     UA_ConnectionState state;
     UA_ConnectionConfig localConf;
@@ -114,10 +114,8 @@ struct UA_Connection {
     void (*close)(UA_Connection *connection);
 };
 
-void UA_EXPORT UA_Connection_init(UA_Connection *connection);
 void UA_EXPORT UA_Connection_deleteMembers(UA_Connection *connection);
 
-
 /**
  * EndpointURL Helper
  * ^^^^^^^^^^^^^^^^^^ */

+ 24 - 13
include/ua_constants.h

@@ -137,8 +137,9 @@ typedef enum {
 #define UA_STATUSCODE_BADCERTIFICATEUNTRUSTED 0x801a0000 // The Certificate is not trusted.
 #define UA_STATUSCODE_BADCERTIFICATEREVOCATIONUNKNOWN 0x801b0000 // It was not possible to determine if the Certificate has been revoked.
 #define UA_STATUSCODE_BADCERTIFICATEISSUERREVOCATIONUNKNOWN 0x801c0000 // It was not possible to determine if the Issuer Certificate has been revoked.
-#define UA_STATUSCODE_BADCERTIFICATEREVOKED 0x801d0000 // The Certificate has been revoked.
-#define UA_STATUSCODE_BADCERTIFICATEISSUERREVOKED 0x801e0000 // The Issuer Certificate has been revoked.
+#define UA_STATUSCODE_BADCERTIFICATEREVOKED 0x801d0000 // The certificate has been revoked.
+#define UA_STATUSCODE_BADCERTIFICATEISSUERREVOKED 0x801e0000 // The issuer certificate has been revoked.
+#define UA_STATUSCODE_BADCERTIFICATECHAININCOMPLETE 0x810d0000 // The certificate chain is incomplete.
 #define UA_STATUSCODE_BADUSERACCESSDENIED 0x801f0000 // User does not have permission to perform the requested operation.
 #define UA_STATUSCODE_BADIDENTITYTOKENINVALID 0x80200000 // The user identity token is not valid.
 #define UA_STATUSCODE_BADIDENTITYTOKENREJECTED 0x80210000 // The user identity token is valid but the server has rejected it.
@@ -152,11 +153,12 @@ typedef enum {
 #define UA_STATUSCODE_BADREQUESTHEADERINVALID 0x802a0000 // The header for the request is missing or invalid.
 #define UA_STATUSCODE_BADTIMESTAMPSTORETURNINVALID 0x802b0000 // The timestamps to return parameter is invalid.
 #define UA_STATUSCODE_BADREQUESTCANCELLEDBYCLIENT 0x802c0000 // The request was cancelled by the client.
+#define UA_STATUSCODE_BADTOOMANYARGUMENTS 0x80e50000 // Too many arguments were provided.
 #define UA_STATUSCODE_GOODSUBSCRIPTIONTRANSFERRED 0x002d0000 // The subscription was transferred to another session.
 #define UA_STATUSCODE_GOODCOMPLETESASYNCHRONOUSLY 0x002e0000 // The processing will complete asynchronously.
 #define UA_STATUSCODE_GOODOVERLOAD 0x002f0000 // Sampling has slowed down due to resource limitations.
 #define UA_STATUSCODE_GOODCLAMPED 0x00300000 // The value written was accepted but was clamped.
-#define UA_STATUSCODE_BADNOCOMMUNICATION 0x80310000 // Communication with the data source is defined, but not established and there is no last known value available.
+#define UA_STATUSCODE_BADNOCOMMUNICATION 0x80310000 // Communication with the data source is defined
 #define UA_STATUSCODE_BADWAITINGFORINITIALDATA 0x80320000 // Waiting for the server to obtain values from the underlying data source.
 #define UA_STATUSCODE_BADNODEIDINVALID 0x80330000 // The syntax of the node id is not valid.
 #define UA_STATUSCODE_BADNODEIDUNKNOWN 0x80340000 // The node id refers to a node that does not exist in the server address space.
@@ -180,8 +182,8 @@ typedef enum {
 #define UA_STATUSCODE_BADSTRUCTUREMISSING 0x80460000 // A mandatory structured parameter was missing or null.
 #define UA_STATUSCODE_BADEVENTFILTERINVALID 0x80470000 // The event filter is not valid.
 #define UA_STATUSCODE_BADCONTENTFILTERINVALID 0x80480000 // The content filter is not valid.
-#define UA_STATUSCODE_BADFILTEROPERATORINVALID 0x80c10000 // An unrecognized operator was provided in a filter.
-#define UA_STATUSCODE_BADFILTEROPERATORUNSUPPORTED 0x80c20000 // A valid operator was provided but the server does not provide support for this filter operator.
+#define UA_STATUSCODE_BADFILTEROPERATORINVALID 0x80c10000 // An unregognized operator was provided in a filter.
+#define UA_STATUSCODE_BADFILTEROPERATORUNSUPPORTED 0x80c20000 // A valid operator was provided
 #define UA_STATUSCODE_BADFILTEROPERANDCOUNTMISMATCH 0x80c30000 // The number of operands provided for the filter operator was less then expected for the operand provided.
 #define UA_STATUSCODE_BADFILTEROPERANDINVALID 0x80490000 // The operand used in a content filter is not valid.
 #define UA_STATUSCODE_BADFILTERELEMENTINVALID 0x80c40000 // The referenced element is not a valid element in the content filter.
@@ -203,7 +205,7 @@ typedef enum {
 #define UA_STATUSCODE_BADAPPLICATIONSIGNATUREINVALID 0x80580000 // The signature generated with the client certificate is missing or invalid.
 #define UA_STATUSCODE_BADNOVALIDCERTIFICATES 0x80590000 // The client did not provide at least one software certificate that is valid and meets the profile requirements for the server.
 #define UA_STATUSCODE_BADIDENTITYCHANGENOTSUPPORTED 0x80c60000 // The Server does not support changing the user identity assigned to the session.
-#define UA_STATUSCODE_BADREQUESTCANCELLEDBYREQUEST 0x805a0000 // The request was canceled by the client with the Cancel service.
+#define UA_STATUSCODE_BADREQUESTCANCELLEDBYREQUEST 0x805a0000 // The request was cancelled by the client with the Cancel service.
 #define UA_STATUSCODE_BADPARENTNODEIDINVALID 0x805b0000 // The parent node id does not to refer to a valid node.
 #define UA_STATUSCODE_BADREFERENCENOTALLOWED 0x805c0000 // The reference could not be created because it violates constraints imposed by the data model.
 #define UA_STATUSCODE_BADNODEIDREJECTED 0x805d0000 // The requested node id was reject because it was either invalid or server does not allow node ids to be specified by the client.
@@ -233,10 +235,11 @@ typedef enum {
 #define UA_STATUSCODE_BADQUERYTOOCOMPLEX 0x806e0000 // The requested operation requires too many resources in the server.
 #define UA_STATUSCODE_BADNOMATCH 0x806f0000 // The requested operation has no match to return.
 #define UA_STATUSCODE_BADMAXAGEINVALID 0x80700000 // The max age parameter is invalid.
+#define UA_STATUSCODE_BADSECURITYMODEINSUFFICIENT 0x80e60000 // The operation is not permitted over the current secure channel.
 #define UA_STATUSCODE_BADHISTORYOPERATIONINVALID 0x80710000 // The history details parameter is not valid.
 #define UA_STATUSCODE_BADHISTORYOPERATIONUNSUPPORTED 0x80720000 // The server does not support the requested operation.
 #define UA_STATUSCODE_BADINVALIDTIMESTAMPARGUMENT 0x80bd0000 // The defined timestamp to return was invalid.
-#define UA_STATUSCODE_BADWRITENOTSUPPORTED 0x80730000 // The server not does support writing the combination of value status and timestamps provided.
+#define UA_STATUSCODE_BADWRITENOTSUPPORTED 0x80730000 // The server not does support writing the combination of value
 #define UA_STATUSCODE_BADTYPEMISMATCH 0x80740000 // The value supplied for the attribute is not of the same type as the attribute's value.
 #define UA_STATUSCODE_BADMETHODINVALID 0x80750000 // The method id does not refer to a method for the specified object.
 #define UA_STATUSCODE_BADARGUMENTSMISSING 0x80760000 // The client did not specify all of the input arguments for the method.
@@ -261,7 +264,7 @@ typedef enum {
 #define UA_STATUSCODE_BADSEQUENCENUMBERINVALID 0x80880000 // The sequence number is not valid.
 #define UA_STATUSCODE_BADPROTOCOLVERSIONUNSUPPORTED 0x80be0000 // The applications do not have compatible protocol versions.
 #define UA_STATUSCODE_BADCONFIGURATIONERROR 0x80890000 // There is a problem with the configuration that affects the usefulness of the value.
-#define UA_STATUSCODE_BADNOTCONNECTED 0x808a0000 // The variable should receive its value from another variable but has never been configured to do so.
+#define UA_STATUSCODE_BADNOTCONNECTED 0x808a0000 // The variable should receive its value from another variable
 #define UA_STATUSCODE_BADDEVICEFAILURE 0x808b0000 // There has been a failure in the device/data source that generates the value that has affected the value.
 #define UA_STATUSCODE_BADSENSORFAILURE 0x808c0000 // There has been a failure in the sensor from which the value is derived by the device/data source.
 #define UA_STATUSCODE_BADOUTOFSERVICE 0x808d0000 // The source of the data is not operational.
@@ -274,10 +277,10 @@ typedef enum {
 #define UA_STATUSCODE_UNCERTAINENGINEERINGUNITSEXCEEDED 0x40940000 // The value is outside of the range of values defined for this parameter.
 #define UA_STATUSCODE_UNCERTAINSUBNORMAL 0x40950000 // The value is derived from multiple sources and has less than the required number of Good sources.
 #define UA_STATUSCODE_GOODLOCALOVERRIDE 0x00960000 // The value has been overridden.
-#define UA_STATUSCODE_BADREFRESHINPROGRESS 0x80970000 // This Condition refresh failed a Condition refresh operation is already in progress.
+#define UA_STATUSCODE_BADREFRESHINPROGRESS 0x80970000 // This Condition refresh failed
 #define UA_STATUSCODE_BADCONDITIONALREADYDISABLED 0x80980000 // This condition has already been disabled.
 #define UA_STATUSCODE_BADCONDITIONALREADYENABLED 0x80cc0000 // This condition has already been enabled.
-#define UA_STATUSCODE_BADCONDITIONDISABLED 0x80990000 // Property not available this condition is disabled.
+#define UA_STATUSCODE_BADCONDITIONDISABLED 0x80990000 // Property not available
 #define UA_STATUSCODE_BADEVENTIDUNKNOWN 0x809a0000 // The specified event id is not recognized.
 #define UA_STATUSCODE_BADEVENTNOTACKNOWLEDGEABLE 0x80bb0000 // The event cannot be acknowledged.
 #define UA_STATUSCODE_BADDIALOGNOTACTIVE 0x80cd0000 // The dialog condition is not active.
@@ -291,7 +294,7 @@ typedef enum {
 #define UA_STATUSCODE_BADBOUNDNOTFOUND 0x80d70000 // No data found to provide upper or lower bound value.
 #define UA_STATUSCODE_BADBOUNDNOTSUPPORTED 0x80d80000 // The server cannot retrieve a bound for the variable.
 #define UA_STATUSCODE_BADDATALOST 0x809d0000 // Data is missing due to collection started/stopped/lost.
-#define UA_STATUSCODE_BADDATAUNAVAILABLE 0x809e0000 // Expected data is unavailable for the requested time range due to an un-mounted volume, an off-line archive or tape or similar reason for temporary unavailability.
+#define UA_STATUSCODE_BADDATAUNAVAILABLE 0x809e0000 // Expected data is unavailable for the requested time range due to an un-mounted volume
 #define UA_STATUSCODE_BADENTRYEXISTS 0x809f0000 // The data or event was not successfully inserted because a matching entry exists.
 #define UA_STATUSCODE_BADNOENTRYEXISTS 0x80a00000 // The data or event was not successfully updated because no matching entry exists.
 #define UA_STATUSCODE_BADTIMESTAMPNOTSUPPORTED 0x80a10000 // The client requested history using a timestamp format the server does not support (i.e requested ServerTimestamp when server only supports SourceTimestamp).
@@ -304,7 +307,15 @@ typedef enum {
 #define UA_STATUSCODE_BADAGGREGATENOTSUPPORTED 0x80d50000 // The requested Aggregate is not support by the server.
 #define UA_STATUSCODE_BADAGGREGATEINVALIDINPUTS 0x80d60000 // The aggregate value could not be derived due to invalid data inputs.
 #define UA_STATUSCODE_BADAGGREGATECONFIGURATIONREJECTED 0x80da0000 // The aggregate configuration is not valid for specified node.
-#define UA_STATUSCODE_GOODDATAIGNORED 0x00d90000 // The request specifies fields which are not valid for the EventType or cannot be saved by the historian.
+#define UA_STATUSCODE_GOODDATAIGNORED 0x00d90000 // The request pecifies fields which are not valid for the EventType or cannot be saved by the historian.
+#define UA_STATUSCODE_BADREQUESTNOTALLOWED 0x80e40000 // The request was rejected by the server because it did not meet the criteria set by the server.
+#define UA_STATUSCODE_GOODEDITED 0x00dc0000 // The value does not come from the real source and has been edited by the server.
+#define UA_STATUSCODE_GOODPOSTACTIONFAILED 0x00dd0000 // There was an error in execution of these post-actions.
+#define UA_STATUSCODE_UNCERTAINDOMINANTVALUECHANGED 0x40de0000 // The related EngineeringUnit has been changed but the Variable Value is still provided based on the previous unit.
+#define UA_STATUSCODE_GOODDEPENDENTVALUECHANGED 0x00e00000 // A dependent value has been changed but the change has not been applied to the device.
+#define UA_STATUSCODE_BADDOMINANTVALUECHANGED 0x80e10000 // The related EngineeringUnit has been changed but this change has not been applied to the device. The Variable Value is still dependent on the previous unit but its status is currently Bad.
+#define UA_STATUSCODE_UNCERTAINDEPENDENTVALUECHANGED 0x40e20000 // A dependent value has been changed but the change has not been applied to the device. The quality of the dominant variable is uncertain.
+#define UA_STATUSCODE_BADDEPENDENTVALUECHANGED 0x80e30000 // A dependent value has been changed but the change has not been applied to the device. The quality of the dominant variable is Bad.
 #define UA_STATUSCODE_GOODCOMMUNICATIONEVENT 0x00a70000 // The communication layer has raised an event.
 #define UA_STATUSCODE_GOODSHUTDOWNEVENT 0x00a80000 // The system is shutting down.
 #define UA_STATUSCODE_GOODCALLAGAIN 0x00a90000 // The operation is not finished and needs to be called again.
@@ -313,7 +324,7 @@ typedef enum {
 #define UA_STATUSCODE_BADCONNECTIONREJECTED 0x80ac0000 // Could not establish a network connection to remote server.
 #define UA_STATUSCODE_BADDISCONNECT 0x80ad0000 // The server has disconnected from the client.
 #define UA_STATUSCODE_BADCONNECTIONCLOSED 0x80ae0000 // The network connection has been closed.
-#define UA_STATUSCODE_BADINVALIDSTATE 0x80af0000 // The operation cannot be completed because the object is closed uninitialized or in some other invalid state.
+#define UA_STATUSCODE_BADINVALIDSTATE 0x80af0000 // The operation cannot be completed because the object is closed
 #define UA_STATUSCODE_BADENDOFSTREAM 0x80b00000 // Cannot move beyond end of the stream.
 #define UA_STATUSCODE_BADNODATAAVAILABLE 0x80b10000 // No data is currently available for reading from a non-blocking stream.
 #define UA_STATUSCODE_BADWAITINGFORRESPONSE 0x80b20000 // The asynchronous operation is waiting for a response.

+ 21 - 1
include/ua_types.h

@@ -20,7 +20,6 @@ extern "C" {
 
 #include "ua_config.h"
 #include "ua_constants.h"
-#include <stdint.h>
 #include <stdbool.h>
 
 /**
@@ -155,6 +154,27 @@ typedef double UA_Double;
  * specific code. */
 typedef uint32_t UA_StatusCode;
 
+typedef struct {
+	UA_StatusCode code;      /* The numeric value of the StatusCode */
+	const char* name;        /* The symbolic name */
+	const char* explanation; /* Short message explaining the StatusCode */
+} UA_StatusCodeDescription;
+
+/* Returns the description of the StatusCode. Never returns NULL, but a generic
+ * description for invalid StatusCodes instead. */
+UA_EXPORT const UA_StatusCodeDescription *
+UA_StatusCode_description(UA_StatusCode code);
+
+static UA_INLINE const char *
+UA_StatusCode_name(UA_StatusCode code) {
+    return UA_StatusCode_description(code)->name;
+}
+
+static UA_INLINE const char *
+UA_StatusCode_explanation(UA_StatusCode code) {
+    return UA_StatusCode_description(code)->explanation;
+}
+
 /**
  * String
  * ^^^^^^

+ 44 - 40
plugins/ua_network_tcp.c

@@ -70,6 +70,18 @@
 # include <urcu/uatomic.h>
 #endif
 
+#ifdef _WIN32
+#define errno__ WSAGetLastError()
+# define INTERRUPTED WSAEINTR
+# define WOULDBLOCK WSAEWOULDBLOCK
+# define AGAIN WSAEWOULDBLOCK
+#else
+# define errno__ errno
+# define INTERRUPTED EINTR
+# define WOULDBLOCK EWOULDBLOCK
+# define AGAIN EAGAIN
+#endif
+
 /****************************/
 /* Generic Socket Functions */
 /****************************/
@@ -92,18 +104,13 @@ socket_write(UA_Connection *connection, UA_ByteString *buf) {
             size_t bytes_to_send = buf->length - nWritten;
             n = send((SOCKET)connection->sockfd, (const char*)buf->data + nWritten,
                      WIN32_INT bytes_to_send, 0);
-#ifdef _WIN32
-            if(n < 0 && WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK)
-#else
-            if(n == -1L && errno != EINTR && errno != EAGAIN)
-#endif
-            {
+            if(n < 0 && errno__ != INTERRUPTED && errno__ != AGAIN) {
                 connection->close(connection);
                 socket_close(connection);
                 UA_ByteString_deleteMembers(buf);
                 return UA_STATUSCODE_BADCONNECTIONCLOSED;
             }
-        } while(n == -1L);
+        } while(n < 0);
         nWritten += (size_t)n;
     } while(nWritten < buf->length);
     UA_ByteString_deleteMembers(buf);
@@ -125,8 +132,7 @@ socket_recv(UA_Connection *connection, UA_ByteString *response, UA_UInt32 timeou
 # ifdef __APPLE__
         struct timeval tmptv = {(long int)(timeout_usec / 1000000), timeout_usec % 1000000};
 # else
-        struct timeval tmptv = {(long int)(timeout_usec / 1000000),
-                                (long int)(timeout_usec % 1000000)};
+        struct timeval tmptv = {(long int)(timeout_usec / 1000000), (long int)(timeout_usec % 1000000)};
 # endif
         int ret = setsockopt(connection->sockfd, SOL_SOCKET, SO_RCVTIMEO,
                              (const char *)&tmptv, sizeof(struct timeval));
@@ -143,23 +149,16 @@ socket_recv(UA_Connection *connection, UA_ByteString *response, UA_UInt32 timeou
     }
 
 #ifdef __CYGWIN__
-    /* WORKAROUND for https://cygwin.com/ml/cygwin/2013-07/msg00107.html */
+    /* Workaround for https://cygwin.com/ml/cygwin/2013-07/msg00107.html */
     ssize_t ret;
-
-    if (timeout > 0) {
+    if(timeout > 0) {
         fd_set fdset;
+        FD_ZERO(&fdset);
+        UA_fd_set(connection->sockfd, &fdset);
         UA_UInt32 timeout_usec = timeout * 1000;
-    #ifdef __APPLE__
-        struct timeval tmptv = {(long int)(timeout_usec / 1000000), timeout_usec % 1000000};
-    #else
         struct timeval tmptv = {(long int)(timeout_usec / 1000000),
                                 (long int)(timeout_usec % 1000000)};
-    #endif
-        UA_Int32 retval;
-
-        FD_ZERO(&fdset);
-        UA_fd_set(connection->sockfd, &fdset);
-        retval = select(connection->sockfd+1, &fdset, NULL, NULL, &tmptv);
+        int retval = select(connection->sockfd+1, &fdset, NULL, NULL, &tmptv);
         if(retval && UA_fd_isset(connection->sockfd, &fdset)) {
             ret = recv(connection->sockfd, (char*)response->data,
                        connection->localConf.recvBufferSize, 0);
@@ -182,16 +181,9 @@ socket_recv(UA_Connection *connection, UA_ByteString *response, UA_UInt32 timeou
         return UA_STATUSCODE_BADCONNECTIONCLOSED;
     } else if(ret < 0) {
         UA_ByteString_deleteMembers(response);
-#ifdef _WIN32
-        const int last_error = WSAGetLastError();
-        #define TEST_RETRY (last_error == WSAEINTR || (timeout > 0) ? \
-                            0 : (last_error == WSAEWOULDBLOCK))
-#else
-        #define TEST_RETRY (errno == EINTR || (timeout > 0) ? \
-                            0 : (errno == EAGAIN || errno == EWOULDBLOCK))
-#endif
-        if (TEST_RETRY)
-            return UA_STATUSCODE_GOOD; /* retry */
+        if(errno__ == INTERRUPTED || (timeout > 0) ?
+           false : (errno__ == EAGAIN || errno__ == WOULDBLOCK))
+            return UA_STATUSCODE_GOOD; /* statuscode_good but no data -> retry */
         else {
             socket_close(connection);
             return UA_STATUSCODE_BADCONNECTIONCLOSED;
@@ -345,10 +337,11 @@ ServerNetworkLayerTCP_add(ServerNetworkLayerTCP *layer, UA_Int32 newsockfd) {
                        "Connection %i | New connection over TCP, "
                        "getpeername failed with errno %i", newsockfd, errno);
     }
-    UA_Connection_init(c);
+    memset(c, 0, sizeof(UA_Connection));
     c->sockfd = newsockfd;
     c->handle = layer;
     c->localConf = layer->conf;
+    c->remoteConf = layer->conf;
     c->send = socket_write;
     c->close = ServerNetworkLayerTCP_closeConnection;
     c->getSendBuffer = ServerNetworkLayerGetSendBuffer;
@@ -363,7 +356,8 @@ ServerNetworkLayerTCP_add(ServerNetworkLayerTCP *layer, UA_Int32 newsockfd) {
         return UA_STATUSCODE_BADINTERNALERROR;
     }
     layer->mappings = nm;
-    layer->mappings[layer->mappingsSize] = (struct ConnectionMapping){c, newsockfd};
+    layer->mappings[layer->mappingsSize].connection = c;
+    layer->mappings[layer->mappingsSize].sockfd = newsockfd;
     layer->mappingsSize++;
     return UA_STATUSCODE_GOOD;
 }
@@ -622,9 +616,10 @@ UA_ClientConnectionTCP(UA_ConnectionConfig localConf, const char *endpointUrl,
 #endif
 
     UA_Connection connection;
-    UA_Connection_init(&connection);
+    memset(&connection, 0, sizeof(UA_Connection));
+    connection.state = UA_CONNECTION_OPENING;
     connection.localConf = localConf;
-
+    connection.remoteConf = localConf;
     connection.send = socket_write;
     connection.recv = socket_recv;
     connection.close = ClientNetworkLayerClose;
@@ -658,11 +653,11 @@ UA_ClientConnectionTCP(UA_ConnectionConfig localConf, const char *endpointUrl,
     hints.ai_socktype = SOCK_STREAM;
     hints.ai_family = AF_INET;
     char portStr[6];
-    #ifndef _MSC_VER
+#ifndef _MSC_VER
     snprintf(portStr, 6, "%d", port);
-    #else
+#else
     _snprintf_s(portStr, 6, _TRUNCATE, "%d", port);
-    #endif
+#endif
     int error = getaddrinfo(hostname, portStr, &hints, &server);
     if(error != 0 || !server) {
         UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK,
@@ -684,13 +679,22 @@ UA_ClientConnectionTCP(UA_ConnectionConfig localConf, const char *endpointUrl,
     }
 
     /* Connect to the server */
-    connection.state = UA_CONNECTION_OPENING;
     connection.sockfd = (UA_Int32)clientsockfd; /* cast for win32 */
     error = connect(clientsockfd, server->ai_addr, WIN32_INT server->ai_addrlen);
     freeaddrinfo(server);
     if(error < 0) {
         ClientNetworkLayerClose(&connection);
-        UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, "Connection failed");
+#ifdef _WIN32
+        wchar_t *s = NULL;
+        FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+                       NULL, WSAGetLastError(),
+                       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                       (LPWSTR)&s, 0, NULL);
+        UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, "Connection to %s failed. Error: %d: %S", endpointUrl, WSAGetLastError(), s);
+        LocalFree(s);
+#else
+        UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, "Connection to %s failed. Error: %d: %s", endpointUrl, errno, strerror(errno));
+#endif
         return connection;
     }
 

+ 122 - 82
src/client/ua_client.c

@@ -11,32 +11,41 @@
 #include "ua_transport_generated_handling.h"
 #include "ua_transport_generated_encoding_binary.h"
 
+/* Listen with a timeout until at least one complete message is received */
+static UA_StatusCode
+Connection_receiveChunk(UA_Connection *connection, UA_ByteString * UA_RESTRICT message,
+                        UA_Boolean * UA_RESTRICT realloced, UA_UInt32 timeout) {
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    *realloced = false;
+    UA_DateTime maxDate = UA_DateTime_nowMonotonic() + (timeout * UA_MSEC_TO_DATETIME);
+    /* Receive packets until one complete message is assembled */
+    do {
+        UA_DateTime now = UA_DateTime_nowMonotonic();
+        if(now > maxDate)
+            return UA_STATUSCODE_GOODNONCRITICALTIMEOUT;
+        UA_UInt32 thisTimeout = (UA_UInt32)((maxDate - UA_DateTime_nowMonotonic()) / UA_MSEC_TO_DATETIME);
+        retval = connection->recv(connection, message, thisTimeout);
+        retval |= UA_Connection_completeMessages(connection, message, realloced);
+    } while(retval == UA_STATUSCODE_GOOD && message->length == 0);
+    return retval;
+}
+
 /*********************/
 /* Create and Delete */
 /*********************/
 
 static void UA_Client_init(UA_Client* client, UA_ClientConfig config) {
+    memset(client, 0, sizeof(UA_Client));
+
     client->state = UA_CLIENTSTATE_READY;
     client->connection = UA_malloc(sizeof(UA_Connection));
-    UA_Connection_init(client->connection);
+    memset(client->connection, 0, sizeof(UA_Connection));
     client->channel = UA_malloc(sizeof(UA_SecureChannel));
     UA_SecureChannel_init(client->channel);
     client->channel->connection = client->connection;
-    UA_String_init(&client->endpointUrl);
-    client->requestId = 0;
-
     client->authenticationMethod = UA_CLIENTAUTHENTICATION_NONE;
-    UA_String_init(&client->username);
-    UA_String_init(&client->password);
-
-    UA_NodeId_init(&client->authenticationToken);
-    client->requestHandle = 0;
-
     client->config = config;
-    client->scRenewAt = 0;
-
 #ifdef UA_ENABLE_SUBSCRIPTIONS
-    client->monitoredItemHandles = 0;
     LIST_INIT(&client->pendingNotificationsAcks);
     LIST_INIT(&client->subscriptions);
 #endif
@@ -105,7 +114,8 @@ static UA_StatusCode HelAckHandshake(UA_Client *client) {
     /* Get a buffer */
     UA_ByteString message;
     UA_StatusCode retval =
-        client->connection->getSendBuffer(client->connection, UA_MINMESSAGESIZE, &message);
+        client->connection->getSendBuffer(client->connection,
+                                          UA_MINMESSAGESIZE, &message);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
 
@@ -137,23 +147,23 @@ static UA_StatusCode HelAckHandshake(UA_Client *client) {
     message.length = messageHeader.messageSize;
     retval = client->connection->send(client->connection, &message);
     if(retval != UA_STATUSCODE_GOOD) {
-        UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_NETWORK, "Sending HEL failed");
+        UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_NETWORK,
+                    "Sending HEL failed");
         return retval;
     }
-    UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_NETWORK, "Sent HEL message");
+    UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_NETWORK,
+                 "Sent HEL message");
 
     /* Loop until we have a complete chunk */
-    /* TODO: quit after a total wait time of client->config.timeout */
     UA_ByteString reply = UA_BYTESTRING_NULL;
     UA_Boolean realloced = false;
-    do {
-        retval = client->connection->recv(client->connection, &reply, client->config.timeout);
-        retval |= UA_Connection_completeMessages(client->connection, &reply, &realloced);
-        if(retval != UA_STATUSCODE_GOOD) {
-            UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_NETWORK, "Receiving ACK message failed");
-            return retval;
-        }
-    } while(reply.length == 0);
+    retval = Connection_receiveChunk(client->connection, &reply,
+                                     &realloced, client->config.timeout);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_NETWORK,
+                    "Receiving ACK message failed");
+        return retval;
+    }
 
     /* Decode the message */
     offset = 0;
@@ -167,9 +177,11 @@ static UA_StatusCode HelAckHandshake(UA_Client *client) {
     else
         UA_ByteString_deleteMembers(&reply);
 
-    /* Store remote connection settings and adjust local configuration to not exceed the limits */
+    /* Store remote connection settings and adjust local configuration to not
+       exceed the limits */
     if(retval == UA_STATUSCODE_GOOD) {
-        UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_NETWORK, "Received ACK message");
+        UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_NETWORK,
+                     "Received ACK message");
         conn->remoteConf.maxChunkCount = ackMessage.maxChunkCount; /* may be zero -> unlimited */
         conn->remoteConf.maxMessageSize = ackMessage.maxMessageSize; /* may be zero -> unlimited */
         conn->remoteConf.protocolVersion = ackMessage.protocolVersion;
@@ -181,7 +193,8 @@ static UA_StatusCode HelAckHandshake(UA_Client *client) {
             conn->localConf.recvBufferSize = conn->remoteConf.sendBufferSize;
         conn->state = UA_CONNECTION_ESTABLISHED;
     } else {
-        UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_NETWORK, "Decoding ACK message failed");
+        UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_NETWORK,
+                    "Decoding ACK message failed");
     }
     UA_TcpAcknowledgeMessage_deleteMembers(&ackMessage);
 
@@ -266,19 +279,16 @@ SecureChannelHandshake(UA_Client *client, UA_Boolean renew) {
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
 
-    UA_ByteString reply;
-    UA_ByteString_init(&reply);
+    UA_ByteString reply = UA_BYTESTRING_NULL;
     UA_Boolean realloced = false;
-    do {
-        retval = c->recv(c, &reply, client->config.timeout);
-        retval |= UA_Connection_completeMessages(c, &reply, &realloced);
-        if(retval != UA_STATUSCODE_GOOD) {
-            UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
-                         "Receiving OpenSecureChannelResponse failed");
-            return retval;
-        }
-    } while(reply.length == 0);
+    retval = Connection_receiveChunk(c, &reply, &realloced, client->config.timeout);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
+                     "Receiving OpenSecureChannelResponse failed");
+        return retval;
+    }
 
+    /* Decode the header */
     offset = 0;
     UA_SecureConversationMessageHeader_decodeBinary(&reply, &offset, &messageHeader);
     UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(&reply, &offset, &asymHeader);
@@ -295,17 +305,19 @@ SecureChannelHandshake(UA_Client *client, UA_Boolean renew) {
         return UA_STATUSCODE_BADINTERNALERROR;
     }
 
+    /* Save the sequence number from server */
+    client->channel->receiveSequenceNumber = seqHeader.sequenceNumber;
+
+    /* Decode the response */
     UA_OpenSecureChannelResponse response;
     retval = UA_OpenSecureChannelResponse_decodeBinary(&reply, &offset, &response);
+
+    /* Free the message */
     if(!realloced)
         c->releaseRecvBuffer(c, &reply);
     else
         UA_ByteString_deleteMembers(&reply);
 
-    /* save the sequence number from server */
-    client->channel->receiveSequenceNumber = seqHeader.sequenceNumber;
-
-
     if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
                      "Decoding OpenSecureChannelResponse failed");
@@ -388,10 +400,7 @@ static UA_StatusCode ActivateSession(UA_Client *client) {
     return retval;
 }
 
-/**
- * Gets a list of endpoints
- * Memory is allocated for endpointDescription array
- */
+/* Gets a list of endpoints. Memory is allocated for endpointDescription array */
 static UA_StatusCode
 GetEndpoints(UA_Client *client, size_t* endpointDescriptionsSize,
              UA_EndpointDescription** endpointDescriptions) {
@@ -432,7 +441,8 @@ static UA_StatusCode EndpointsHandshake(UA_Client *client) {
     UA_Boolean endpointFound = false;
     UA_Boolean tokenFound = false;
     UA_String securityNone = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None");
-    UA_String binaryTransport = UA_STRING("http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary");
+    UA_String binaryTransport = UA_STRING("http://opcfoundation.org/UA-Profile/"
+                                          "Transport/uatcp-uasc-uabinary");
 
     //TODO: compare endpoint information with client->endpointUri
     for(size_t i = 0; i < endpointArraySize; i++) {
@@ -465,14 +475,17 @@ static UA_StatusCode EndpointsHandshake(UA_Client *client) {
         }
     }
 
-    UA_Array_delete(endpointArray, endpointArraySize, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
+    UA_Array_delete(endpointArray, endpointArraySize,
+                    &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
 
     if(!endpointFound) {
-        UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT, "No suitable endpoint found");
+        UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                     "No suitable endpoint found");
         return UA_STATUSCODE_BADINTERNALERROR;
     }
     if(!tokenFound) {
-        UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT, "No anonymous token found");
+        UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                     "No anonymous token found");
         return UA_STATUSCODE_BADINTERNALERROR;
     }
     return retval;
@@ -526,7 +539,8 @@ static UA_StatusCode CloseSecureChannel(UA_Client *client) {
     request.requestHeader.requestHandle = ++client->requestHandle;
     request.requestHeader.timestamp = UA_DateTime_now();
     request.requestHeader.timeoutHint = 10000;
-    UA_NodeId_copy(&client->authenticationToken, &request.requestHeader.authenticationToken);
+    UA_NodeId_copy(&client->authenticationToken,
+                   &request.requestHeader.authenticationToken);
 
     UA_SecureConversationMessageHeader msgHeader;
     msgHeader.messageHeader.messageTypeAndChunkType = UA_MESSAGETYPE_CLO + UA_CHUNKTYPE_FINAL;
@@ -539,7 +553,8 @@ static UA_StatusCode CloseSecureChannel(UA_Client *client) {
     seqHeader.sequenceNumber = ++channel->sendSequenceNumber;
     seqHeader.requestId = ++client->requestId;
 
-    UA_NodeId typeId = UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_CLOSESECURECHANNELREQUEST].binaryEncodingId);
+    UA_NodeId typeId =
+        UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_CLOSESECURECHANNELREQUEST].binaryEncodingId);
 
     UA_ByteString message;
     UA_Connection *c = client->connection;
@@ -553,8 +568,8 @@ static UA_StatusCode CloseSecureChannel(UA_Client *client) {
     retval |= UA_SymmetricAlgorithmSecurityHeader_encodeBinary(&symHeader, &message, &offset);
     retval |= UA_SequenceHeader_encodeBinary(&seqHeader, &message, &offset);
     retval |= UA_NodeId_encodeBinary(&typeId, &message, &offset);
-    retval |= UA_encodeBinary(&request, &UA_TYPES[UA_TYPES_CLOSESECURECHANNELREQUEST], NULL,
-                              NULL, &message, &offset);
+    retval |= UA_encodeBinary(&request, &UA_TYPES[UA_TYPES_CLOSESECURECHANNELREQUEST],
+                              NULL, NULL, &message, &offset);
 
     msgHeader.messageHeader.messageSize = (UA_UInt32)offset;
     offset = 0;
@@ -582,8 +597,9 @@ UA_Client_getEndpoints(UA_Client *client, const char *serverUrl,
 
 
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    *client->connection = client->config.connectionFunc(UA_ConnectionConfig_standard, serverUrl,
-                                                       client->config.logger);
+    *client->connection =
+        client->config.connectionFunc(UA_ConnectionConfig_standard, serverUrl,
+                                      client->config.logger);
     if(client->connection->state != UA_CONNECTION_OPENING) {
         retval = UA_STATUSCODE_BADCONNECTIONCLOSED;
         goto cleanup;
@@ -700,22 +716,33 @@ struct ResponseDescription {
 
 static void
 processServiceResponse(struct ResponseDescription *rd, UA_SecureChannel *channel,
-                       UA_MessageType messageType, UA_UInt32 requestId, UA_ByteString *message) {
+                       UA_MessageType messageType, UA_UInt32 requestId,
+                       UA_ByteString *message) {
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_ResponseHeader *respHeader = (UA_ResponseHeader*)rd->response;
     rd->processed = true;
 
+    if(messageType != UA_MESSAGETYPE_MSG) {
+        UA_LOG_ERROR(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
+                     "Server replied with the wrong message type");
+        retval = UA_STATUSCODE_BADTCPMESSAGETYPEINVALID;
+        goto finish;
+    }
+
     /* Check that the request id matches */
-    /* Todo: we need to demux async responses since a publish responses may come at any time */
+    /* Todo: we need to demux async responses since a publish responses may come
+       at any time */
     if(requestId != rd->requestId) {
         UA_LOG_ERROR(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
-                     "Reply answers the wrong requestId. Async services are not yet implemented.");
+                     "Reply answers the wrong requestId. "
+                     "Async services are not yet implemented.");
         retval = UA_STATUSCODE_BADINTERNALERROR;
         goto finish;
     }
 
     /* Check that the response type matches */
-    const UA_NodeId expectedNodeId = UA_NODEID_NUMERIC(0, rd->responseType->binaryEncodingId);
+    const UA_NodeId expectedNodeId =
+        UA_NODEID_NUMERIC(0, rd->responseType->binaryEncodingId);
     const UA_NodeId serviceFaultNodeId =
         UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_SERVICEFAULT].binaryEncodingId);
     size_t offset = 0;
@@ -726,12 +753,14 @@ processServiceResponse(struct ResponseDescription *rd, UA_SecureChannel *channel
     if(!UA_NodeId_equal(&responseId, &expectedNodeId)) {
         if(UA_NodeId_equal(&responseId, &serviceFaultNodeId)) {
             /* Take the statuscode from the servicefault */
-            retval = UA_decodeBinary(message, &offset, rd->response, &UA_TYPES[UA_TYPES_SERVICEFAULT]);
+            retval = UA_decodeBinary(message, &offset, rd->response,
+                                     &UA_TYPES[UA_TYPES_SERVICEFAULT]);
         } else {
             UA_LOG_ERROR(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
-                         "Reply answers the wrong request. Expected ns=%i,i=%i. But retrieved ns=%i,i=%i",
-                         expectedNodeId.namespaceIndex, expectedNodeId.identifier.numeric,
-                         responseId.namespaceIndex, responseId.identifier.numeric);
+                         "Reply answers the wrong request. Expected ns=%i,i=%i."
+                         "But retrieved ns=%i,i=%i", expectedNodeId.namespaceIndex,
+                         expectedNodeId.identifier.numeric, responseId.namespaceIndex,
+                         responseId.identifier.numeric);
             UA_NodeId_deleteMembers(&responseId);
             retval = UA_STATUSCODE_BADINTERNALERROR;
         }
@@ -748,13 +777,16 @@ processServiceResponse(struct ResponseDescription *rd, UA_SecureChannel *channel
     } else {
         if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED)
             retval = UA_STATUSCODE_BADRESPONSETOOLARGE;
-        UA_LOG_INFO(rd->client->config.logger, UA_LOGCATEGORY_CLIENT, "Error receiving the response");
+        UA_LOG_INFO(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
+                    "Error receiving the response");
         respHeader->serviceResult = retval;
     }
 }
 
-void __UA_Client_Service(UA_Client *client, const void *r, const UA_DataType *requestType,
-                         void *response, const UA_DataType *responseType) {
+void
+__UA_Client_Service(UA_Client *client, const void *r, const UA_DataType *requestType,
+                    void *response, const UA_DataType *responseType) {
+    UA_init(response, responseType);
     UA_ResponseHeader *respHeader = (UA_ResponseHeader*)response;
 
     /* Make sure we have a valid session */
@@ -783,32 +815,40 @@ void __UA_Client_Service(UA_Client *client, const void *r, const UA_DataType *re
         else
             respHeader->serviceResult = retval;
         client->state = UA_CLIENTSTATE_ERRORED;
-        goto finish;
+        UA_NodeId_deleteMembers(&request->authenticationToken);
+        return;
     }
 
-    /* Requests always begin witih a RequestHeader, therefore we can cast. */
+    /* Prepare the response and the structure we give into processServiceResponse */
     UA_init(response, responseType);
     struct ResponseDescription rd = (struct ResponseDescription){
         client, false, requestId, response, responseType};
 
     /* Retrieve the response */
-    UA_ByteString reply;
-    UA_ByteString_init(&reply);
-    UA_Boolean realloced = false;
+    UA_DateTime maxDate = UA_DateTime_nowMonotonic() + (client->config.timeout * UA_MSEC_TO_DATETIME);
     do {
-        retval = client->connection->recv(client->connection, &reply, client->config.timeout);
-        retval |= UA_Connection_completeMessages(client->connection, &reply, &realloced);
+        /* Retrieve complete chunks */
+        UA_ByteString reply = UA_BYTESTRING_NULL;
+        UA_Boolean realloced = false;
+        UA_DateTime now = UA_DateTime_nowMonotonic();
+        if(now < maxDate) {
+            UA_UInt32 timeout = (UA_UInt32)((maxDate - now) / UA_MSEC_TO_DATETIME);
+            retval = Connection_receiveChunk(client->connection, &reply, &realloced, timeout);
+        } else {
+            retval = UA_STATUSCODE_GOODNONCRITICALTIMEOUT;
+        }
         if(retval != UA_STATUSCODE_GOOD) {
             respHeader->serviceResult = retval;
-            client->state = UA_CLIENTSTATE_ERRORED;
-            goto finish;
-        }
-        if(!reply.data)
             break;
+        }
+        /* ProcessChunks and call processServiceResponse for complete messages */
         UA_SecureChannel_processChunks(client->channel, &reply,
-                                       (UA_ProcessMessageCallback*)processServiceResponse, &rd);
+                     (UA_ProcessMessageCallback*)processServiceResponse, &rd);
+        /* Free the received buffer */
+        if(!realloced)
+            client->connection->releaseRecvBuffer(client->connection, &reply);
+        else
+            UA_ByteString_deleteMembers(&reply);
     } while(!rd.processed);
-
- finish:
     UA_NodeId_deleteMembers(&request->authenticationToken);
 }

+ 43 - 29
src/client/ua_client_highlevel.c

@@ -6,7 +6,8 @@
 #include "ua_types.h"
 
 UA_StatusCode
-UA_Client_NamespaceGetIndex(UA_Client *client, UA_String *namespaceUri, UA_UInt16 *namespaceIndex) {
+UA_Client_NamespaceGetIndex(UA_Client *client, UA_String *namespaceUri,
+                            UA_UInt16 *namespaceIndex) {
     UA_ReadRequest request;
     UA_ReadRequest_init(&request);
     UA_ReadValueId id;
@@ -45,9 +46,8 @@ UA_Client_NamespaceGetIndex(UA_Client *client, UA_String *namespaceUri, UA_UInt1
 }
 
 UA_StatusCode
-UA_Client_forEachChildNodeCall(UA_Client *client, UA_NodeId parentNodeId, UA_NodeIteratorCallback callback, void *handle) {
-  UA_StatusCode retval = UA_STATUSCODE_GOOD;
-  
+UA_Client_forEachChildNodeCall(UA_Client *client, UA_NodeId parentNodeId,
+                               UA_NodeIteratorCallback callback, void *handle) {
   UA_BrowseRequest bReq;
   UA_BrowseRequest_init(&bReq);
   bReq.requestedMaxReferencesPerNode = 0;
@@ -59,16 +59,18 @@ UA_Client_forEachChildNodeCall(UA_Client *client, UA_NodeId parentNodeId, UA_Nod
   
   UA_BrowseResponse bResp = UA_Client_Service_browse(client, bReq);
   
+  UA_StatusCode retval = UA_STATUSCODE_GOOD;
   if(bResp.responseHeader.serviceResult == UA_STATUSCODE_GOOD) {
-    for (size_t i = 0; i < bResp.resultsSize; ++i) {
-      for (size_t j = 0; j < bResp.results[i].referencesSize; ++j) {
-        UA_ReferenceDescription *ref = &(bResp.results[i].references[j]);
-        retval |= callback(ref->nodeId.nodeId, ! ref->isForward, ref->referenceTypeId, handle);
+      for (size_t i = 0; i < bResp.resultsSize; ++i) {
+          for (size_t j = 0; j < bResp.results[i].referencesSize; ++j) {
+              UA_ReferenceDescription *ref = &(bResp.results[i].references[j]);
+              retval |= callback(ref->nodeId.nodeId, !ref->isForward,
+                                 ref->referenceTypeId, handle);
+          }
       }
-    }
   }
   else
-    retval = bResp.responseHeader.serviceResult;
+      retval = bResp.responseHeader.serviceResult;
   
   
   UA_BrowseRequest_deleteMembers(&bReq);
@@ -82,9 +84,11 @@ UA_Client_forEachChildNodeCall(UA_Client *client, UA_NodeId parentNodeId, UA_Nod
 /*******************/
 
 UA_StatusCode UA_EXPORT
-UA_Client_addReference(UA_Client *client, const UA_NodeId sourceNodeId, const UA_NodeId referenceTypeId,
-                       UA_Boolean isForward, const UA_String targetServerUri,
-                       const UA_ExpandedNodeId targetNodeId, UA_NodeClass targetNodeClass) {
+UA_Client_addReference(UA_Client *client, const UA_NodeId sourceNodeId,
+                       const UA_NodeId referenceTypeId, UA_Boolean isForward,
+                       const UA_String targetServerUri,
+                       const UA_ExpandedNodeId targetNodeId,
+                       UA_NodeClass targetNodeClass) {
     UA_AddReferencesItem item;
     UA_AddReferencesItem_init(&item);
     item.sourceNodeId = sourceNodeId;
@@ -113,8 +117,9 @@ UA_Client_addReference(UA_Client *client, const UA_NodeId sourceNodeId, const UA
 }
 
 UA_StatusCode UA_EXPORT
-UA_Client_deleteReference(UA_Client *client, const UA_NodeId sourceNodeId, const UA_NodeId referenceTypeId,
-                          UA_Boolean isForward, const UA_ExpandedNodeId targetNodeId,
+UA_Client_deleteReference(UA_Client *client, const UA_NodeId sourceNodeId,
+                          const UA_NodeId referenceTypeId, UA_Boolean isForward,
+                          const UA_ExpandedNodeId targetNodeId,
                           UA_Boolean deleteBidirectional) {
     UA_DeleteReferencesItem item;
     UA_DeleteReferencesItem_init(&item);
@@ -143,7 +148,8 @@ UA_Client_deleteReference(UA_Client *client, const UA_NodeId sourceNodeId, const
 }
 
 UA_StatusCode
-UA_Client_deleteNode(UA_Client *client, const UA_NodeId nodeId, UA_Boolean deleteTargetReferences) {
+UA_Client_deleteNode(UA_Client *client, const UA_NodeId nodeId,
+                     UA_Boolean deleteTargetReferences) {
     UA_DeleteNodesItem item;
     UA_DeleteNodesItem_init(&item);
     item.nodeId = nodeId;
@@ -168,10 +174,11 @@ UA_Client_deleteNode(UA_Client *client, const UA_NodeId nodeId, UA_Boolean delet
 }
 
 UA_StatusCode
-__UA_Client_addNode(UA_Client *client, const UA_NodeClass nodeClass, const UA_NodeId requestedNewNodeId,
-                    const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
-                    const UA_QualifiedName browseName, const UA_NodeId typeDefinition,
-                    const UA_NodeAttributes *attr, const UA_DataType *attributeType, UA_NodeId *outNewNodeId) {
+__UA_Client_addNode(UA_Client *client, const UA_NodeClass nodeClass,
+                    const UA_NodeId requestedNewNodeId, const UA_NodeId parentNodeId,
+                    const UA_NodeId referenceTypeId, const UA_QualifiedName browseName,
+                    const UA_NodeId typeDefinition, const UA_NodeAttributes *attr,
+                    const UA_DataType *attributeType, UA_NodeId *outNewNodeId) {
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_AddNodesRequest request;
     UA_AddNodesRequest_init(&request);
@@ -214,8 +221,10 @@ __UA_Client_addNode(UA_Client *client, const UA_NodeClass nodeClass, const UA_No
 #ifdef UA_ENABLE_METHODCALLS
 
 UA_StatusCode
-UA_Client_call(UA_Client *client, const UA_NodeId objectId, const UA_NodeId methodId, size_t inputSize,
-               const UA_Variant *input, size_t *outputSize, UA_Variant **output) {
+UA_Client_call(UA_Client *client, const UA_NodeId objectId,
+               const UA_NodeId methodId, size_t inputSize,
+               const UA_Variant *input, size_t *outputSize,
+               UA_Variant **output) {
     UA_CallRequest request;
     UA_CallRequest_init(&request);
     UA_CallMethodRequest item;
@@ -256,8 +265,9 @@ UA_Client_call(UA_Client *client, const UA_NodeId objectId, const UA_NodeId meth
 /********************/
 
 UA_StatusCode 
-__UA_Client_writeAttribute(UA_Client *client, const UA_NodeId *nodeId, UA_AttributeId attributeId,
-                           const void *in, const UA_DataType *inDataType) {
+__UA_Client_writeAttribute(UA_Client *client, const UA_NodeId *nodeId,
+                           UA_AttributeId attributeId, const void *in,
+                           const UA_DataType *inDataType) {
     if(!in)
       return UA_STATUSCODE_BADTYPEMISMATCH;
     
@@ -268,7 +278,8 @@ __UA_Client_writeAttribute(UA_Client *client, const UA_NodeId *nodeId, UA_Attrib
     if(attributeId == UA_ATTRIBUTEID_VALUE)
         wValue.value.value = *(const UA_Variant*)in;
     else
-        UA_Variant_setScalar(&wValue.value.value, (void*)(uintptr_t)in, inDataType); /* hack. is never written into. */
+        /* hack. is never written into. */
+        UA_Variant_setScalar(&wValue.value.value, (void*)(uintptr_t)in, inDataType);
     wValue.value.hasValue = true;
     UA_WriteRequest wReq;
     UA_WriteRequest_init(&wReq);
@@ -283,7 +294,8 @@ __UA_Client_writeAttribute(UA_Client *client, const UA_NodeId *nodeId, UA_Attrib
 
 UA_StatusCode
 UA_Client_writeArrayDimensionsAttribute(UA_Client *client, const UA_NodeId nodeId,
-                                        const UA_Int32 *newArrayDimensions, size_t newArrayDimensionsSize) {
+                                        const UA_Int32 *newArrayDimensions,
+                                        size_t newArrayDimensionsSize) {
     if(!newArrayDimensions)
       return UA_STATUSCODE_BADTYPEMISMATCH;
     
@@ -310,8 +322,9 @@ UA_Client_writeArrayDimensionsAttribute(UA_Client *client, const UA_NodeId nodeI
 /*******************/
 
 UA_StatusCode 
-__UA_Client_readAttribute(UA_Client *client, const UA_NodeId *nodeId, UA_AttributeId attributeId,
-                          void *out, const UA_DataType *outDataType) {
+__UA_Client_readAttribute(UA_Client *client, const UA_NodeId *nodeId,
+                          UA_AttributeId attributeId, void *out,
+                          const UA_DataType *outDataType) {
     UA_ReadValueId item;
     UA_ReadValueId_init(&item);
     item.nodeId = *nodeId;
@@ -357,7 +370,8 @@ __UA_Client_readAttribute(UA_Client *client, const UA_NodeId *nodeId, UA_Attribu
 
 UA_StatusCode
 UA_Client_readArrayDimensionsAttribute(UA_Client *client, const UA_NodeId nodeId,
-                                       UA_Int32 **outArrayDimensions, size_t *outArrayDimensionsSize) {
+                                       UA_Int32 **outArrayDimensions,
+                                       size_t *outArrayDimensionsSize) {
     UA_ReadValueId item;
     UA_ReadValueId_init(&item);
     item.nodeId = nodeId;

+ 17 - 9
src/client/ua_client_highlevel_subscriptions.c

@@ -5,8 +5,9 @@
 
 #ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */
 
-UA_StatusCode UA_Client_Subscriptions_new(UA_Client *client, UA_SubscriptionSettings settings,
-                                          UA_UInt32 *newSubscriptionId) {
+UA_StatusCode
+UA_Client_Subscriptions_new(UA_Client *client, UA_SubscriptionSettings settings,
+                            UA_UInt32 *newSubscriptionId) {
     UA_CreateSubscriptionRequest request;
     UA_CreateSubscriptionRequest_init(&request);
     request.requestedPublishingInterval = settings.requestedPublishingInterval;
@@ -45,7 +46,8 @@ UA_StatusCode UA_Client_Subscriptions_new(UA_Client *client, UA_SubscriptionSett
 }
 
 /* remove the subscription remotely */
-UA_StatusCode UA_Client_Subscriptions_remove(UA_Client *client, UA_UInt32 subscriptionId) {
+UA_StatusCode
+UA_Client_Subscriptions_remove(UA_Client *client, UA_UInt32 subscriptionId) {
     UA_Client_Subscription *sub;
     LIST_FOREACH(sub, &client->subscriptions, listEntry) {
         if(sub->SubscriptionID == subscriptionId)
@@ -57,8 +59,9 @@ UA_StatusCode UA_Client_Subscriptions_remove(UA_Client *client, UA_UInt32 subscr
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_Client_MonitoredItem *mon, *tmpmon;
     LIST_FOREACH_SAFE(mon, &sub->MonitoredItems, listEntry, tmpmon) {
-        retval = UA_Client_Subscriptions_removeMonitoredItem(client, sub->SubscriptionID,
-                                                             mon->MonitoredItemId);
+        retval =
+            UA_Client_Subscriptions_removeMonitoredItem(client, sub->SubscriptionID,
+                                                        mon->MonitoredItemId);
         if(retval != UA_STATUSCODE_GOOD)
             return retval;
     }
@@ -85,7 +88,9 @@ UA_StatusCode UA_Client_Subscriptions_remove(UA_Client *client, UA_UInt32 subscr
     return UA_STATUSCODE_GOOD;
 }
 
-void UA_Client_Subscriptions_forceDelete(UA_Client *client, UA_Client_Subscription *sub) {
+void
+UA_Client_Subscriptions_forceDelete(UA_Client *client,
+                                    UA_Client_Subscription *sub) {
     UA_Client_MonitoredItem *mon, *mon_tmp;
     LIST_FOREACH_SAFE(mon, &sub->MonitoredItems, listEntry, mon_tmp) {
         UA_NodeId_deleteMembers(&mon->monitoredNodeId);
@@ -190,7 +195,8 @@ UA_Client_Subscriptions_removeMonitoredItem(UA_Client *client, UA_UInt32 subscri
     if(retval == UA_STATUSCODE_GOOD && response.resultsSize > 1)
         retval = response.results[0];
     UA_DeleteMonitoredItemsResponse_deleteMembers(&response);
-    if(retval != UA_STATUSCODE_GOOD && retval != UA_STATUSCODE_BADMONITOREDITEMIDINVALID) {
+    if(retval != UA_STATUSCODE_GOOD &&
+       retval != UA_STATUSCODE_BADMONITOREDITEMIDINVALID) {
         UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_CLIENT,
                     "Could not remove monitoreditem %u with statuscode 0x%08x",
                     monitoredItemId, retval);
@@ -204,7 +210,8 @@ UA_Client_Subscriptions_removeMonitoredItem(UA_Client *client, UA_UInt32 subscri
 }
 
 static void
-UA_Client_processPublishResponse(UA_Client *client, UA_PublishRequest *request, UA_PublishResponse *response) {
+UA_Client_processPublishResponse(UA_Client *client, UA_PublishRequest *request,
+                                 UA_PublishResponse *response) {
     if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD)
         return;
 
@@ -276,7 +283,8 @@ UA_Client_processPublishResponse(UA_Client *client, UA_PublishRequest *request,
     LIST_INSERT_HEAD(&client->pendingNotificationsAcks, tmpAck, listEntry);
 }
 
-UA_StatusCode UA_Client_Subscriptions_manuallySendPublishRequest(UA_Client *client) {
+UA_StatusCode
+UA_Client_Subscriptions_manuallySendPublishRequest(UA_Client *client) {
     if (client->state == UA_CLIENTSTATE_ERRORED)
         return UA_STATUSCODE_BADSERVERNOTCONNECTED;
 

+ 1 - 1
src/server/ua_nodes.h

@@ -355,7 +355,7 @@ typedef struct {
  * --------
  *
  * Each View defines a subset of the Nodes in the AddressSpace. Views can be
- * used when browsing an informatin model to focus on a subset of nodes and
+ * used when browsing an information model to focus on a subset of nodes and
  * references only. ViewNodes can be created and be interacted with. But their
  * use in the :ref:`Browse<view-services>` service is currently unsupported in
  * open62541. */

+ 1 - 0
src/server/ua_nodestore_concurrent.c

@@ -3,6 +3,7 @@
 #include "ua_server_internal.h"
 
 #ifdef UA_ENABLE_MULTITHREADING /* conditional compilation */
+#include <urcu/rculfhash.h>
 
 struct nodeEntry {
     struct cds_lfht_node htn; ///< Contains the next-ptr for urcu-hashmap

+ 7 - 19
src/server/ua_securechannel_manager.c

@@ -26,12 +26,11 @@ void UA_SecureChannelManager_deleteMembers(UA_SecureChannelManager *cm) {
 
 static void removeSecureChannel(UA_SecureChannelManager *cm, channel_list_entry *entry){
     LIST_REMOVE(entry, pointers);
+    UA_atomic_add(&cm->currentChannelCount, (UA_UInt32)-1);
     UA_SecureChannel_deleteMembersCleanup(&entry->channel);
 #ifndef UA_ENABLE_MULTITHREADING
-    cm->currentChannelCount--;
     UA_free(entry);
 #else
-    cm->currentChannelCount = uatomic_add_return(&cm->currentChannelCount, -1);
     UA_Server_delayedFree(cm->server, entry);
 #endif
 }
@@ -108,11 +107,7 @@ UA_SecureChannelManager_open(UA_SecureChannelManager *cm, UA_Connection *conn,
     /* Set all the pointers internally */
     UA_Connection_attachSecureChannel(conn, &entry->channel);
     LIST_INSERT_HEAD(&cm->channels, entry, pointers);
-#ifndef UA_ENABLE_MULTITHREADING
-    cm->currentChannelCount++;
-#else
-    cm->currentChannelCount = uatomic_add_return(&cm->currentChannelCount, 1);
-#endif
+    UA_atomic_add(&cm->currentChannelCount, 1);
     return UA_STATUSCODE_GOOD;
 }
 
@@ -151,7 +146,8 @@ UA_SecureChannelManager_renew(UA_SecureChannelManager *cm, UA_Connection *conn,
     return UA_STATUSCODE_GOOD;
 }
 
-UA_SecureChannel * UA_SecureChannelManager_get(UA_SecureChannelManager *cm, UA_UInt32 channelId) {
+UA_SecureChannel *
+UA_SecureChannelManager_get(UA_SecureChannelManager *cm, UA_UInt32 channelId) {
     channel_list_entry *entry;
     LIST_FOREACH(entry, &cm->channels, pointers) {
         if(entry->channel.securityToken.channelId == channelId)
@@ -160,7 +156,8 @@ UA_SecureChannel * UA_SecureChannelManager_get(UA_SecureChannelManager *cm, UA_U
     return NULL;
 }
 
-UA_StatusCode UA_SecureChannelManager_close(UA_SecureChannelManager *cm, UA_UInt32 channelId) {
+UA_StatusCode
+UA_SecureChannelManager_close(UA_SecureChannelManager *cm, UA_UInt32 channelId) {
     channel_list_entry *entry;
     LIST_FOREACH(entry, &cm->channels, pointers) {
         if(entry->channel.securityToken.channelId == channelId)
@@ -168,15 +165,6 @@ UA_StatusCode UA_SecureChannelManager_close(UA_SecureChannelManager *cm, UA_UInt
     }
     if(!entry)
         return UA_STATUSCODE_BADINTERNALERROR;
-
-    LIST_REMOVE(entry, pointers);
-    UA_SecureChannel_deleteMembersCleanup(&entry->channel);
-#ifndef UA_ENABLE_MULTITHREADING
-    cm->currentChannelCount--;
-    UA_free(entry);
-#else
-    cm->currentChannelCount = uatomic_add_return(&cm->currentChannelCount, -1);
-    UA_Server_delayedFree(cm->server, entry);
-#endif
+    removeSecureChannel(cm, entry);
     return UA_STATUSCODE_GOOD;
 }

+ 1 - 1
src/server/ua_securechannel_manager.h

@@ -13,7 +13,7 @@ typedef struct channel_list_entry {
 
 typedef struct UA_SecureChannelManager {
     LIST_HEAD(channel_list, channel_list_entry) channels; // doubly-linked list of channels
-    size_t currentChannelCount;
+    UA_UInt32 currentChannelCount;
     UA_UInt32 lastChannelId;
     UA_UInt32 lastTokenId;
     UA_Server *server;

+ 16 - 9
src/server/ua_server.c

@@ -30,9 +30,6 @@ UA_THREAD_LOCAL UA_Session* methodCallSession = NULL;
 static const UA_NodeId nodeIdHasSubType = {
     .namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
     .identifier.numeric = UA_NS0ID_HASSUBTYPE};
-static const UA_NodeId nodeIdHasTypeDefinition = {
-    .namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
-    .identifier.numeric = UA_NS0ID_HASTYPEDEFINITION};
 static const UA_NodeId nodeIdHasComponent = {
     .namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
     .identifier.numeric = UA_NS0ID_HASCOMPONENT};
@@ -42,9 +39,6 @@ static const UA_NodeId nodeIdHasProperty = {
 static const UA_NodeId nodeIdOrganizes = {
     .namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
     .identifier.numeric = UA_NS0ID_ORGANIZES};
-static const UA_NodeId nodeIdFolderType = {
-    .namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
-    .identifier.numeric = UA_NS0ID_FOLDERTYPE};
 
 #ifndef UA_ENABLE_GENERATE_NAMESPACE0
 static const UA_NodeId nodeIdNonHierarchicalReferences = {
@@ -851,6 +845,13 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     /* Root and below */
     /******************/
 
+    static const UA_NodeId nodeIdFolderType = {
+        .namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
+        .identifier.numeric = UA_NS0ID_FOLDERTYPE};
+    static const UA_NodeId nodeIdHasTypeDefinition = {
+        .namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
+        .identifier.numeric = UA_NS0ID_HASTYPEDEFINITION};
+
     UA_ObjectNode *root = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)root, "Root");
     root->nodeId.identifier.numeric = UA_NS0ID_ROOTFOLDER;
@@ -934,7 +935,9 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     
     // If we are in an UA conformant namespace, the above function just created a full ServerType object.
     // Before readding every variable, delete whatever got instantiated.
-    deleteInstanceChildren(server, &servernode->nodeId);
+    // here we can't reuse servernode->nodeId because it may be deleted in addNodeInternalWithType if the node could not be added
+    UA_NodeId serverNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER);
+    deleteInstanceChildren(server, &serverNodeId);
     
     UA_VariableNode *namespaceArray = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)namespaceArray, "NamespaceArray");
@@ -965,7 +968,9 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addNodeInternalWithType(server, (UA_Node*)servercapablities, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
                             nodeIdHasComponent,
                             UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCAPABILITIESTYPE));
-
+    UA_NodeId ServerCapabilitiesNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES);
+    deleteInstanceChildren(server, &ServerCapabilitiesNodeId);
+    
     UA_VariableNode *localeIdArray = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)localeIdArray, "LocaleIdArray");
     localeIdArray->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES_LOCALEIDARRAY;
@@ -1080,7 +1085,9 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addNodeInternalWithType(server, (UA_Node*)serverdiagnostics,
                             UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
                             nodeIdHasComponent, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERDIAGNOSTICSTYPE));
-
+    UA_NodeId ServerDiagnosticsNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS);
+    deleteInstanceChildren(server, &ServerDiagnosticsNodeId);
+    
     UA_VariableNode *enabledFlag = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)enabledFlag, "EnabledFlag");
     enabledFlag->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERDIAGNOSTICS_ENABLEDFLAG;

+ 3 - 7
src/server/ua_server_binary.c

@@ -314,11 +314,7 @@ processOPN(UA_Server *server, UA_Connection *connection,
 
     /* Encode the message after the secureconversationmessageheader */
     size_t tmpPos = 12; /* skip the header */
-#ifndef UA_ENABLE_MULTITHREADING
-    seqHeader.sequenceNumber = ++channel->sendSequenceNumber;
-#else
-    seqHeader.sequenceNumber = uatomic_add_return(&channel->sendSequenceNumber, 1);
-#endif
+    seqHeader.sequenceNumber = UA_atomic_add(&channel->sendSequenceNumber, 1);
     retval |= UA_AsymmetricAlgorithmSecurityHeader_encodeBinary(&asymHeader, &resp_msg, &tmpPos); // just mirror back
     retval |= UA_SequenceHeader_encodeBinary(&seqHeader, &resp_msg, &tmpPos);
     UA_NodeId responseType = UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE].binaryEncodingId);
@@ -525,12 +521,12 @@ UA_Server_processSecureChannelMessage(UA_Server *server, UA_SecureChannel *chann
         break;
     case UA_MESSAGETYPE_MSG:
         UA_LOG_TRACE_CHANNEL(server->config.logger, channel,
-                             "Process a MSG", connection->sockfd);
+                             "Process a MSG", channel->connection->sockfd);
         processMSG(server, channel, requestId, message);
         break;
     case UA_MESSAGETYPE_CLO:
         UA_LOG_TRACE_CHANNEL(server->config.logger, channel,
-                             "Process a CLO", connection->sockfd);
+                             "Process a CLO", channel->connection->sockfd);
         Service_CloseSecureChannel(server, channel);
         break;
     default:

+ 47 - 20
src/server/ua_server_internal.h

@@ -12,6 +12,36 @@
 #define ANONYMOUS_POLICY "open62541-anonymous-policy"
 #define USERNAME_POLICY "open62541-username-policy"
 
+/* liburcu includes */
+#ifdef UA_ENABLE_MULTITHREADING
+# define _LGPL_SOURCE
+# include <urcu.h>
+# include <urcu/lfstack.h>
+# ifdef NDEBUG
+#  define UA_RCU_LOCK() rcu_read_lock()
+#  define UA_RCU_UNLOCK() rcu_read_unlock()
+#  define UA_ASSERT_RCU_LOCKED()
+#  define UA_ASSERT_RCU_UNLOCKED()
+# else
+   extern UA_THREAD_LOCAL bool rcu_locked;
+#   define UA_ASSERT_RCU_LOCKED() assert(rcu_locked)
+#   define UA_ASSERT_RCU_UNLOCKED() assert(!rcu_locked)
+#   define UA_RCU_LOCK() do {                     \
+        UA_ASSERT_RCU_UNLOCKED();                 \
+        rcu_locked = true;                        \
+        rcu_read_lock(); } while(0)
+#   define UA_RCU_UNLOCK() do {                   \
+        UA_ASSERT_RCU_LOCKED();                   \
+        rcu_locked = false;                       \
+        rcu_read_lock(); } while(0)
+# endif
+#else
+# define UA_RCU_LOCK()
+# define UA_RCU_UNLOCK()
+# define UA_ASSERT_RCU_LOCKED()
+# define UA_ASSERT_RCU_UNLOCKED()
+#endif
+
 #ifdef UA_ENABLE_EXTERNAL_NAMESPACES
 /** Mapping of namespace-id and url to an external nodestore. For namespaces
     that have no mapping defined, the internal nodestore is used by default. */
@@ -162,29 +192,31 @@ getNodeType(UA_Server *server, const UA_Node *node);
 /***************************************/
 
 UA_StatusCode
-UA_VariableNode_setArrayDimensions(UA_Server *server, UA_VariableNode *node,
-                                   const UA_VariableTypeNode *vt,
-                                   size_t arrayDimensionsSize, UA_UInt32 *arrayDimensions);
+readValueAttribute(const UA_VariableNode *vn, UA_DataValue *v);
 
 UA_StatusCode
-UA_VariableNode_setValueRank(UA_Server *server, UA_VariableNode *node,
-                             const UA_VariableTypeNode *vt,
-                             const UA_Int32 valueRank);
+typeCheckValue(UA_Server *server, const UA_NodeId *variableDataTypeId,
+               UA_Int32 variableValueRank, size_t variableArrayDimensionsSize,
+               const UA_UInt32 *variableArrayDimensions, const UA_Variant *value,
+               const UA_NumericRange *range, UA_Variant *equivalent);
 
 UA_StatusCode
-UA_VariableNode_setDataType(UA_Server *server, UA_VariableNode *node,
-                            const UA_VariableTypeNode *vt,
-                            const UA_NodeId *dataType);
+writeDataTypeAttribute(UA_Server *server, UA_VariableNode *node,
+                       const UA_NodeId *dataType, const UA_NodeId *constraintDataType);
 
 UA_StatusCode
-UA_VariableNode_setValue(UA_Server *server, UA_VariableNode *node,
-                         const UA_DataValue *value, const UA_String *indexRange);
+compatibleArrayDimensions(size_t constraintArrayDimensionsSize,
+                          const UA_UInt32 *constraintArrayDimensions,
+                          size_t testArrayDimensionsSize,
+                          const UA_UInt32 *testArrayDimensions);
 
 UA_StatusCode
-UA_Variant_matchVariableDefinition(UA_Server *server, const UA_NodeId *variableDataTypeId,
-                                   UA_Int32 variableValueRank, size_t variableArrayDimensionsSize,
-                                   const UA_UInt32 *variableArrayDimensions, const UA_Variant *value,
-                                   const UA_NumericRange *range, UA_Variant *equivalent);
+writeValueRankAttribute(UA_VariableNode *node, UA_Int32 valueRank,
+                        UA_Int32 constraintValueRank);
+
+UA_StatusCode
+writeValueAttribute(UA_Server *server, UA_VariableNode *node,
+                    const UA_DataValue *value, const UA_String *indexRange);
 
 /*******************/
 /* Single-Services */
@@ -219,15 +251,10 @@ void Service_Read_single(UA_Server *server, UA_Session *session,
                          UA_TimestampsToReturn timestamps,
                          const UA_ReadValueId *id, UA_DataValue *v);
 
-UA_StatusCode Service_Write_single(UA_Server *server, UA_Session *session,
-                                   const UA_WriteValue *wvalue);
-
 void Service_Call_single(UA_Server *server, UA_Session *session,
                          const UA_CallMethodRequest *request,
                          UA_CallMethodResult *result);
 
-
-
 /* Periodic task to clean up the discovery registry */
 void UA_Discovery_cleanupTimedOut(UA_Server *server, UA_DateTime now);
 

+ 18 - 16
src/server/ua_server_worker.c

@@ -107,7 +107,7 @@ workerLoop(UA_Worker *worker) {
             /* nothing to do. sleep until a job is dispatched (and wakes up all worker threads) */
             pthread_cond_wait(&server->dispatchQueue_condition, &mutex);
         }
-        uatomic_inc(counter);
+        UA_atomic_add(counter, 1);
     }
 
     pthread_mutex_unlock(&mutex);
@@ -212,12 +212,22 @@ UA_Server_addRepeatedJob(UA_Server *server, UA_Job job,
 /* Returns the next datetime when a repeated job is scheduled */
 static UA_DateTime
 processRepeatedJobs(UA_Server *server, UA_DateTime current) {
-    struct RepeatedJob *rj, *tmp_rj;
+    /* Find the last job that is executed after this iteration */
+    struct RepeatedJob *lastNow = NULL, *tmp;
+    LIST_FOREACH(tmp, &server->repeatedJobs, next) {
+        if(tmp->nextTime > current)
+            break;
+        lastNow = tmp;
+    }
+
     /* Iterate over the list of elements (sorted according to the next execution timestamp) */
+    struct RepeatedJob *rj, *tmp_rj;
     LIST_FOREACH_SAFE(rj, &server->repeatedJobs, next, tmp_rj) {
         if(rj->nextTime > current)
             break;
 
+        UA_assert(lastNow); /* at least one element at the current time */
+
         /* Dispatch/process job */
 #ifdef UA_ENABLE_MULTITHREADING
         dispatchJob(server, &rj->job);
@@ -237,13 +247,15 @@ processRepeatedJobs(UA_Server *server, UA_DateTime current) {
         /* Set the time for the next execution */
         rj->nextTime += (UA_Int64)rj->interval;
         if(rj->nextTime < current)
-            rj->nextTime = current;
+            rj->nextTime = current + 1; /* prevent to rerun the job right now
+                                           when the repeated jobs took more time
+                                           than rj->interval */
 
         /* Keep the list sorted */
-        struct RepeatedJob *prev_rj = LIST_FIRST(&server->repeatedJobs);
+        struct RepeatedJob *prev_rj = lastNow;
         while(true) {
             struct RepeatedJob *n = LIST_NEXT(prev_rj, next);
-            if(!n || n->nextTime > rj->nextTime)
+            if(!n || n->nextTime >= rj->nextTime)
                 break;
             prev_rj = n;
         }
@@ -443,25 +455,15 @@ dispatchDelayedJobs(UA_Server *server, void *_) {
         dw = dw->next;
     }
 
-#if (__GNUC__ <= 4 && __GNUC_MINOR__ <= 6)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wextra"
-#pragma GCC diagnostic ignored "-Wcast-qual"
-#pragma GCC diagnostic ignored "-Wunused-value"
-#endif
     /* process and free all delayed jobs from here on */
     while(dw) {
         for(size_t i = 0; i < dw->jobsCount; i++)
             processJob(server, &dw->jobs[i]);
-        struct DelayedJobs *next = uatomic_xchg(&beforedw->next, NULL);
+        struct DelayedJobs *next = UA_atomic_xchg((void**)&beforedw->next, NULL);
         UA_free(dw->workerCounters);
         UA_free(dw);
         dw = next;
     }
-#if (__GNUC__ <= 4 && __GNUC_MINOR__ <= 6)
-#pragma GCC diagnostic pop
-#endif
-
 }
 
 #endif

File diff suppressed because it is too large
+ 721 - 661
src/server/ua_services_attribute.c


+ 4 - 6
src/server/ua_services_call.c

@@ -26,7 +26,7 @@ getArgumentsVariableNode(UA_Server *server, const UA_MethodNode *ofMethod,
 
 static UA_StatusCode
 argumentsConformsToDefinition(UA_Server *server, const UA_VariableNode *argRequirements,
-                              size_t argsSize, const UA_Variant *args) {
+                              size_t argsSize, UA_Variant *args) {
     if(argRequirements->value.data.value.value.type != &UA_TYPES[UA_TYPES_ARGUMENT])
         return UA_STATUSCODE_BADINTERNALERROR;
     UA_Argument *argReqs = (UA_Argument*)argRequirements->value.data.value.value.data;
@@ -42,11 +42,9 @@ argumentsConformsToDefinition(UA_Server *server, const UA_VariableNode *argRequi
 
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     for(size_t i = 0; i < argReqsSize; i++)
-        retval |= UA_Variant_matchVariableDefinition(server, &argReqs[i].dataType,
-                                                     argReqs[i].valueRank,
-                                                     argReqs[i].arrayDimensionsSize,
-                                                     argReqs[i].arrayDimensions,
-                                                     &args[i], NULL, NULL);
+        retval |= typeCheckValue(server, &argReqs[i].dataType, argReqs[i].valueRank,
+                                 argReqs[i].arrayDimensionsSize, argReqs[i].arrayDimensions,
+                                 &args[i], NULL, args);
     return retval;
 }
 

+ 139 - 95
src/server/ua_services_nodemanagement.c

@@ -5,7 +5,6 @@
 /* Add Node */
 /************/
 
-
 static void
 Service_AddNodes_single(UA_Server *server, UA_Session *session,
                         const UA_AddNodesItem *item, UA_AddNodesResult *result,
@@ -29,18 +28,21 @@ copyExistingVariable(UA_Server *server, UA_Session *session, const UA_NodeId *va
     if(node->nodeClass != UA_NODECLASS_VARIABLE)
         return UA_STATUSCODE_BADNODECLASSINVALID;
 
-    // copy the variable attributes
+    /* Get the current value */
+    UA_DataValue value;
+    UA_DataValue_init(&value);
+    UA_StatusCode retval = readValueAttribute(node, &value);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    /* Prepare the variable description */
     UA_VariableAttributes attr;
     UA_VariableAttributes_init(&attr);
     attr.displayName = node->displayName;
     attr.description = node->description;
     attr.writeMask = node->writeMask;
     attr.userWriteMask = node->userWriteMask;
-    if(node->valueSource == UA_VALUESOURCE_DATA)
-        attr.value = node->value.data.value.value;
-    else {
-        // todo: handle data source and make a copy of the current value
-    }
+    attr.value = value.value;
     attr.dataType = node->dataType;
     attr.valueRank = node->valueRank;
     attr.arrayDimensionsSize = node->arrayDimensionsSize;
@@ -59,26 +61,33 @@ copyExistingVariable(UA_Server *server, UA_Session *session, const UA_NodeId *va
     item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
     item.nodeAttributes.content.decoded.type = &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES];
     item.nodeAttributes.content.decoded.data = &attr;
-    const UA_Node *vartype = getNodeType(server, (const UA_Node*)node);
-    if(vartype)
-        item.typeDefinition.nodeId = vartype->nodeId;
-    else
-        return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
+    const UA_VariableTypeNode *vt = (const UA_VariableTypeNode*)getNodeType(server, (const UA_Node*)node);
+    if(!vt || vt->nodeClass != UA_NODECLASS_VARIABLETYPE || vt->isAbstract) {
+        retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
+        goto cleanup;
+    }
+    item.typeDefinition.nodeId = vt->nodeId;
 
-    /* add the new variable */
+    /* Add the variable and instantiate the children */
     UA_AddNodesResult res;
     UA_AddNodesResult_init(&res);
     Service_AddNodes_single(server, session, &item, &res, instantiationCallback);
+    if(res.statusCode != UA_STATUSCODE_GOOD) {
+        retval = res.statusCode;
+        goto cleanup;
+    }
+    retval = copyChildNodesToNode(server, session, &node->nodeId,
+                                  &res.addedNodeId, instantiationCallback);
     
-    /* Copy any aggregated/nested variables/methods/subobjects this object contains 
-     * These objects may not be part of the nodes type. */
-    copyChildNodesToNode(server, session, &node->nodeId, &res.addedNodeId, instantiationCallback);
-    if(instantiationCallback)
-        instantiationCallback->method(res.addedNodeId, node->nodeId, 
+    if(retval == UA_STATUSCODE_GOOD && instantiationCallback)
+        instantiationCallback->method(res.addedNodeId, node->nodeId,
                                       instantiationCallback->handle);
     
     UA_NodeId_deleteMembers(&res.addedNodeId);
-    return res.statusCode;
+ cleanup:
+    if(value.hasValue && value.value.storageType == UA_VARIANT_DATA)
+        UA_Variant_deleteMembers(&value.value);
+    return retval;
 }
 
 /* Copy an existing object under the given parent. Then instantiate for all
@@ -112,27 +121,28 @@ copyExistingObject(UA_Server *server, UA_Session *session, const UA_NodeId *obje
     item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
     item.nodeAttributes.content.decoded.type = &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES];
     item.nodeAttributes.content.decoded.data = &attr;
-    const UA_Node *objtype = getNodeType(server, (const UA_Node*)node);
-    if(objtype)
-        item.typeDefinition.nodeId = objtype->nodeId;
-    else
+    const UA_ObjectTypeNode *objtype = (const UA_ObjectTypeNode*)getNodeType(server, (const UA_Node*)node);
+    if(!objtype || objtype->nodeClass != UA_NODECLASS_OBJECTTYPE || objtype->isAbstract)
         return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
+    item.typeDefinition.nodeId = objtype->nodeId;
 
     /* add the new object */
     UA_AddNodesResult res;
     UA_AddNodesResult_init(&res);
     Service_AddNodes_single(server, session, &item, &res, instantiationCallback);
+    if(res.statusCode != UA_STATUSCODE_GOOD)
+        return res.statusCode;
     
     /* Copy any aggregated/nested variables/methods/subobjects this object contains 
      * These objects may not be part of the nodes type. */
-    copyChildNodesToNode(server, session, &node->nodeId, &res.addedNodeId, instantiationCallback);
-    if(instantiationCallback)
+    UA_StatusCode retval = copyChildNodesToNode(server, session, &node->nodeId,
+                                                &res.addedNodeId, instantiationCallback);
+    if(retval == UA_STATUSCODE_GOOD && instantiationCallback)
         instantiationCallback->method(res.addedNodeId, node->nodeId, 
                                       instantiationCallback->handle);
-    
+
     UA_NodeId_deleteMembers(&res.addedNodeId);
-    
-    return res.statusCode;
+    return retval;
 }
 
 static UA_StatusCode
@@ -406,6 +416,9 @@ Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
     UA_StatusCode retval = checkParentReference(server, session, node->nodeClass,
                                                 parentNodeId, referenceTypeId);
     if(retval != UA_STATUSCODE_GOOD) {
+        UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                             "AddNodes: Checking the reference to the parent returned"
+                             "error code %s", UA_StatusCode_name(retval));
         UA_NodeStore_deleteNode(node);
         return retval;
     }
@@ -415,7 +428,7 @@ Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
     if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_DEBUG_SESSION(server->config.logger, session,
                              "AddNodes: Node could not be added to the nodestore "
-                             "with error code 0x%08x", retval);
+                             "with error code %s", UA_StatusCode_name(retval));
         return retval;
     }
 
@@ -439,7 +452,8 @@ Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
     retval = Service_AddReferences_single(server, session, &item);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                             "AddNodes: Could not add the reference to the parent");
+                             "AddNodes: Could not add the reference to the parent"
+                             "with error code %s", UA_StatusCode_name(retval));
         goto remove_node;
     }
 
@@ -460,7 +474,8 @@ Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
                                  typeDefinition, instantiationCallback);
         if(retval != UA_STATUSCODE_GOOD) {
             UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                                 "AddNodes: Could not instantiate the node");
+                                 "AddNodes: Could not instantiate the node with"
+                                 "error code 0x%08x", retval);
             goto remove_node;
         }
     }
@@ -500,10 +515,9 @@ copyCommonVariableAttributes(UA_Server *server, UA_VariableNode *node,
     const UA_NodeId basevartype = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEVARIABLETYPE);
     const UA_NodeId basedatavartype = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
     const UA_NodeId *typeDef = &item->typeDefinition.nodeId;
-    if(UA_NodeId_isNull(typeDef))
+    if(UA_NodeId_isNull(typeDef)) /* workaround when the variabletype is undefined */
         typeDef = &basedatavartype;
     
-    
     /* Make sure we can instantiate the basetypes themselves */
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     if(UA_NodeId_equal(&node->nodeId, &basevartype) || 
@@ -517,29 +531,42 @@ copyCommonVariableAttributes(UA_Server *server, UA_VariableNode *node,
         (const UA_VariableTypeNode*)UA_NodeStore_get(server->nodestore, typeDef);
     if(!vt || vt->nodeClass != UA_NODECLASS_VARIABLETYPE)
         return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
+    if(node->nodeClass == UA_NODECLASS_VARIABLE && vt->isAbstract)
+        return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
     
-    /* Set the constraints */
+    /* Set the datatype */
     if(!UA_NodeId_isNull(&attr->dataType))
-        retval  = UA_VariableNode_setDataType(server, node, vt, &attr->dataType);
+        retval  = writeDataTypeAttribute(server, node, &attr->dataType, &vt->dataType);
     else /* workaround common error where the datatype is left as NA_NODEID_NULL */
-        retval = UA_VariableNode_setDataType(server, node, vt, &vt->dataType);
+        retval = UA_NodeId_copy(&vt->dataType, &node->dataType);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
         
-    node->valueRank = -2; /* allow all dimensions first */
-    retval |= UA_VariableNode_setArrayDimensions(server, node, vt,
-                                                attr->arrayDimensionsSize,
-                                                attr->arrayDimensions);
+    /* Set the array dimensions. Check only against the vt. */
+    retval = compatibleArrayDimensions(attr->arrayDimensionsSize, attr->arrayDimensions,
+                                       vt->arrayDimensionsSize, vt->arrayDimensions);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+    retval = UA_Array_copy(attr->arrayDimensions, attr->arrayDimensionsSize,
+                           (void**)&node->arrayDimensions, &UA_TYPES[UA_TYPES_UINT32]);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+    node->arrayDimensionsSize = attr->arrayDimensionsSize;
 
+    /* Set the valuerank */
     if(attr->valueRank != 0 || !UA_Variant_isScalar(&attr->value))
-        retval |= UA_VariableNode_setValueRank(server, node, vt, attr->valueRank);
+        retval = writeValueRankAttribute(node, attr->valueRank, vt->valueRank);
     else /* workaround common error where the valuerank is left as 0 */
-        retval |= UA_VariableNode_setValueRank(server, node, vt, vt->valueRank);
+        node->valueRank = vt->valueRank;
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
     
     /* Set the value */
     UA_DataValue value;
     UA_DataValue_init(&value);
     value.value = attr->value;
     value.hasValue = true;
-    retval |= UA_VariableNode_setValue(server, node, &value, NULL);
+    retval |= writeValueAttribute(server, node, &value, NULL);
     return retval;
 }
 
@@ -667,7 +694,8 @@ Service_AddNodes_single(UA_Server *server, UA_Session *session,
                                                        instantiationCallback, &result->addedNodeId);
     } else {
         UA_LOG_DEBUG_SESSION(server->config.logger, session, "AddNodes: Could not "
-                             "prepare the new node with status code 0x%08x", result->statusCode);
+                             "prepare the new node with status code %s",
+                             UA_StatusCode_name(result->statusCode));
         UA_NodeStore_deleteNode(node);
     }
 }
@@ -928,13 +956,14 @@ UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
 /* Add References */
 /******************/
 
+static UA_StatusCode
+deleteOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node,
+                      const UA_DeleteReferencesItem *item);
+
 /* Adds a one-way reference to the local nodestore */
 static UA_StatusCode
 addOneWayReference(UA_Server *server, UA_Session *session,
                    UA_Node *node, const UA_AddReferencesItem *item) {
-    if(item->targetNodeClass != UA_NODECLASS_UNSPECIFIED &&
-       item->targetNodeClass != node->nodeClass)
-        return UA_STATUSCODE_BADNODECLASSINVALID;
     size_t i = node->referencesSize;
     size_t refssize = (i+1) | 3; // so the realloc is not necessary every time
     UA_ReferenceNode *new_refs = UA_realloc(node->references, sizeof(UA_ReferenceNode) * refssize);
@@ -955,17 +984,21 @@ addOneWayReference(UA_Server *server, UA_Session *session,
 UA_StatusCode
 Service_AddReferences_single(UA_Server *server, UA_Session *session,
                              const UA_AddReferencesItem *item) {
-UA_StatusCode retval = UA_STATUSCODE_GOOD;
-#ifdef UA_ENABLE_EXTERNAL_NAMESPACES
-    UA_Boolean handledExternally = UA_FALSE;
-#endif
+    /* Currently no expandednodeids are allowed */
     if(item->targetServerUri.length > 0)
-        return UA_STATUSCODE_BADNOTIMPLEMENTED; // currently no expandednodeids are allowed
+        return UA_STATUSCODE_BADNOTIMPLEMENTED;
 
-#ifdef UA_ENABLE_EXTERNAL_NAMESPACES
+    /* Add the first direction */
+#ifndef UA_ENABLE_EXTERNAL_NAMESPACES
+    UA_StatusCode retval = UA_Server_editNode(server, session, &item->sourceNodeId,
+                                              (UA_EditNodeCallback)addOneWayReference,
+                                              item);
+#else
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    UA_Boolean handledExternally = UA_FALSE;
     for(size_t j = 0; j <server->externalNamespacesSize; j++) {
         if(item->sourceNodeId.namespaceIndex != server->externalNamespaces[j].index) {
-                continue;
+            continue;
         } else {
             UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
             retval = ens->addOneWayReference(ens->ensHandle, item);
@@ -973,45 +1006,54 @@ UA_StatusCode retval = UA_STATUSCODE_GOOD;
             break;
         }
     }
-    if(handledExternally == UA_FALSE) {
-#endif
-    /* cast away the const to loop the call through UA_Server_editNode beware
-     * the "if" above for external nodestores */
-    retval = UA_Server_editNode(server, session, &item->sourceNodeId,
-                                (UA_EditNodeCallback)addOneWayReference, item);
-#ifdef UA_ENABLE_EXTERNAL_NAMESPACES
-    }
+    if(!handledExternally)
+        retval = UA_Server_editNode(server, session, &item->sourceNodeId,
+                                    (UA_EditNodeCallback)addOneWayReference, item);
 #endif
 
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
 
+    /* Add the second direction */
     UA_AddReferencesItem secondItem;
-    secondItem = *item;
-    secondItem.targetNodeId.nodeId = item->sourceNodeId;
+    UA_AddReferencesItem_init(&secondItem);
     secondItem.sourceNodeId = item->targetNodeId.nodeId;
+    secondItem.referenceTypeId = item->referenceTypeId;
     secondItem.isForward = !item->isForward;
-#ifdef UA_ENABLE_EXTERNAL_NAMESPACES
+    secondItem.targetNodeId.nodeId = item->sourceNodeId;
+    /* keep default secondItem.targetNodeClass = UA_NODECLASS_UNSPECIFIED */
+#ifndef UA_ENABLE_EXTERNAL_NAMESPACES
+    retval = UA_Server_editNode(server, session, &secondItem.sourceNodeId,
+                                (UA_EditNodeCallback)addOneWayReference, &secondItem);
+#else
     handledExternally = UA_FALSE;
     for(size_t j = 0; j <server->externalNamespacesSize; j++) {
         if(secondItem.sourceNodeId.namespaceIndex != server->externalNamespaces[j].index) {
-                continue;
+            continue;
         } else {
             UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
             retval = ens->addOneWayReference(ens->ensHandle, &secondItem);
             handledExternally = UA_TRUE;
             break;
         }
-
     }
-    if(handledExternally == UA_FALSE) {
+    if(!handledExternally)
+        retval = UA_Server_editNode(server, session, &secondItem.sourceNodeId,
+                                    (UA_EditNodeCallback)addOneWayReference, &secondItem);
 #endif
-    retval = UA_Server_editNode(server, session, &secondItem.sourceNodeId,
-                                (UA_EditNodeCallback)addOneWayReference, &secondItem);
-#ifdef UA_ENABLE_EXTERNAL_NAMESPACES
+
+    /* remove reference if the second direction failed */
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_DeleteReferencesItem deleteItem;
+        deleteItem.sourceNodeId = item->sourceNodeId;
+        deleteItem.referenceTypeId = item->referenceTypeId;
+        deleteItem.isForward = item->isForward;
+        deleteItem.targetNodeId = item->targetNodeId;
+        deleteItem.deleteBidirectional = false;
+        /* ignore returned status code */
+        UA_Server_editNode(server, session, &item->sourceNodeId,
+                           (UA_EditNodeCallback)deleteOneWayReference, &deleteItem);
     }
-#endif
-    // todo: remove reference if the second direction failed
     return retval;
 }
 
@@ -1024,22 +1066,27 @@ void Service_AddReferences(UA_Server *server, UA_Session *session,
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
         return;
     }
-    size_t size = request->referencesToAddSize;
 
-    if(!(response->results = UA_malloc(sizeof(UA_StatusCode) * size))) {
+    response->results = UA_malloc(sizeof(UA_StatusCode) * request->referencesToAddSize);
+    if(!response->results) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
         return;
     }
-    response->resultsSize = size;
+    response->resultsSize = request->referencesToAddSize;
 
-#ifdef UA_ENABLE_EXTERNAL_NAMESPACES
-#ifdef NO_ALLOCA
+#ifndef UA_ENABLE_EXTERNAL_NAMESPACES
+    for(size_t i = 0; i < response->resultsSize; i++)
+        response->results[i] =
+            Service_AddReferences_single(server, session, &request->referencesToAdd[i]);
+#else
+    size_t size = request->referencesToAddSize;
+# ifdef NO_ALLOCA
     UA_Boolean isExternal[size];
     UA_UInt32 indices[size];
-#else
+# else
     UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * size);
     UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * size);
-#endif /*NO_ALLOCA */
+# endif /*NO_ALLOCA */
     memset(isExternal, false, sizeof(UA_Boolean) * size);
     for(size_t j = 0; j < server->externalNamespacesSize; j++) {
         size_t indicesSize = 0;
@@ -1057,14 +1104,13 @@ void Service_AddReferences(UA_Server *server, UA_Session *session,
         ens->addReferences(ens->ensHandle, &request->requestHeader, request->referencesToAdd,
                            indices, (UA_UInt32)indicesSize, response->results, response->diagnosticInfos);
     }
-#endif
 
     for(size_t i = 0; i < response->resultsSize; i++) {
-#ifdef UA_ENABLE_EXTERNAL_NAMESPACES
         if(!isExternal[i])
-#endif
-            response->results[i] = Service_AddReferences_single(server, session, &request->referencesToAdd[i]);
+            response->results[i] =
+                Service_AddReferences_single(server, session, &request->referencesToAdd[i]);
     }
+#endif
 }
 
 UA_StatusCode
@@ -1087,10 +1133,6 @@ UA_Server_addReference(UA_Server *server, const UA_NodeId sourceId,
 /* Delete Nodes */
 /****************/
 
-static UA_StatusCode
-deleteOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node,
-                      const UA_DeleteReferencesItem *item);
-
 UA_StatusCode
 Service_DeleteNodes_single(UA_Server *server, UA_Session *session,
                            const UA_NodeId *nodeId, UA_Boolean deleteReferences) {
@@ -1161,8 +1203,8 @@ void Service_DeleteNodes(UA_Server *server, UA_Session *session,
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;;
         return;
     }
-
     response->resultsSize = request->nodesToDeleteSize;
+
     for(size_t i = 0; i < request->nodesToDeleteSize; i++) {
         UA_DeleteNodesItem *item = &request->nodesToDelete[i];
         response->results[i] = Service_DeleteNodes_single(server, session, &item->nodeId,
@@ -1208,8 +1250,10 @@ deleteOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node,
     if(!edited)
         return UA_STATUSCODE_UNCERTAINREFERENCENOTDELETED;
     /* we removed the last reference */
-    if(node->referencesSize == 0 && node->references)
+    if(node->referencesSize == 0 && node->references) {
         UA_free(node->references);
+        node->references = NULL;
+    }
     return UA_STATUSCODE_GOOD;;
 }
 
@@ -1325,9 +1369,8 @@ UA_Server_setVariableNode_dataSource(UA_Server *server, const UA_NodeId nodeId,
 /****************************/
 
 static UA_StatusCode
-setObjectTypeLifecycleManagement(UA_Server *server, UA_Session *session,
-                                 UA_ObjectTypeNode* node,
-                                 UA_ObjectLifecycleManagement *olm) {
+setOLM(UA_Server *server, UA_Session *session,
+       UA_ObjectTypeNode* node, UA_ObjectLifecycleManagement *olm) {
     if(node->nodeClass != UA_NODECLASS_OBJECTTYPE)
         return UA_STATUSCODE_BADNODECLASSINVALID;
     node->lifecycleManagement = *olm;
@@ -1339,7 +1382,7 @@ UA_Server_setObjectTypeNode_lifecycleManagement(UA_Server *server, UA_NodeId nod
                                                 UA_ObjectLifecycleManagement olm) {
     UA_RCU_LOCK();
     UA_StatusCode retval = UA_Server_editNode(server, &adminSession, &nodeId,
-                                              (UA_EditNodeCallback)setObjectTypeLifecycleManagement, &olm);
+                                              (UA_EditNodeCallback)setOLM, &olm);
     UA_RCU_UNLOCK();
     return retval;
 }
@@ -1356,7 +1399,8 @@ struct addMethodCallback {
 };
 
 static UA_StatusCode
-editMethodCallback(UA_Server *server, UA_Session* session, UA_Node* node, const void* handle) {
+editMethodCallback(UA_Server *server, UA_Session* session,
+                   UA_Node* node, const void* handle) {
     if(node->nodeClass != UA_NODECLASS_METHOD)
         return UA_STATUSCODE_BADNODECLASSINVALID;
     const struct addMethodCallback *newCallback = handle;

+ 16 - 22
src/server/ua_session_manager.c

@@ -18,6 +18,18 @@ void UA_SessionManager_deleteMembers(UA_SessionManager *sm) {
     }
 }
 
+static void
+removeSessionEntry(UA_SessionManager *sm, session_list_entry *sentry) {
+    LIST_REMOVE(sentry, pointers);
+    UA_atomic_add(&sm->currentSessionCount, (UA_UInt32)-1);
+    UA_Session_deleteMembersCleanup(&sentry->session, sm->server);
+#ifndef UA_ENABLE_MULTITHREADING
+    UA_free(sentry);
+#else
+    UA_Server_delayedFree(sm->server, sentry);
+#endif
+}
+
 void UA_SessionManager_cleanupTimedOut(UA_SessionManager *sm, UA_DateTime nowMonotonic) {
     session_list_entry *sentry, *temp;
     LIST_FOREACH_SAFE(sentry, &sm->sessions, pointers, temp) {
@@ -25,15 +37,7 @@ void UA_SessionManager_cleanupTimedOut(UA_SessionManager *sm, UA_DateTime nowMon
             UA_LOG_DEBUG(sm->server->config.logger, UA_LOGCATEGORY_SESSION,
                          "Session with token %i has timed out and is removed",
                          sentry->session.sessionId.identifier.numeric);
-            LIST_REMOVE(sentry, pointers);
-            UA_Session_deleteMembersCleanup(&sentry->session, sm->server);
-#ifndef UA_ENABLE_MULTITHREADING
-            sm->currentSessionCount--;
-            UA_free(sentry);
-#else
-            sm->currentSessionCount = uatomic_add_return(&sm->currentSessionCount, -1);
-            UA_Server_delayedFree(sm->server, sentry);
-#endif
+            removeSessionEntry(sm, sentry);
         }
     }
 }
@@ -58,7 +62,7 @@ UA_SessionManager_getSession(UA_SessionManager *sm, const UA_NodeId *token) {
     return NULL;
 }
 
-/** Creates and adds a session. But it is not yet attached to a secure channel. */
+/* Creates and adds a session. But it is not yet attached to a secure channel. */
 UA_StatusCode
 UA_SessionManager_createSession(UA_SessionManager *sm, UA_SecureChannel *channel,
                                 const UA_CreateSessionRequest *request, UA_Session **session) {
@@ -69,7 +73,7 @@ UA_SessionManager_createSession(UA_SessionManager *sm, UA_SecureChannel *channel
     if(!newentry)
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
-    sm->currentSessionCount++;
+    UA_atomic_add(&sm->currentSessionCount, 1);
     UA_Session_init(&newentry->session);
     newentry->session.sessionId = UA_NODEID_GUID(1, UA_Guid_random());
     newentry->session.authenticationToken = UA_NODEID_GUID(1, UA_Guid_random());
@@ -93,18 +97,8 @@ UA_SessionManager_removeSession(UA_SessionManager *sm, const UA_NodeId *token) {
         if(UA_NodeId_equal(&current->session.authenticationToken, token))
             break;
     }
-
     if(!current)
         return UA_STATUSCODE_BADSESSIONIDINVALID;
-
-    LIST_REMOVE(current, pointers);
-    UA_Session_deleteMembersCleanup(&current->session, sm->server);
-#ifndef UA_ENABLE_MULTITHREADING
-    sm->currentSessionCount--;
-    UA_free(current);
-#else
-    sm->currentSessionCount = uatomic_add_return(&sm->currentSessionCount, -1);
-    UA_Server_delayedFree(sm->server, current);
-#endif
+    removeSessionEntry(sm, current);
     return UA_STATUSCODE_GOOD;
 }

+ 135 - 96
src/server/ua_subscription.c

@@ -6,6 +6,8 @@
 
 #ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */
 
+#define UA_VALUENCODING_MAXSTACK 512
+
 /*****************/
 /* MonitoredItem */
 /*****************/
@@ -44,28 +46,89 @@ void MonitoredItem_delete(UA_Server *server, UA_MonitoredItem *monitoredItem) {
     UA_free(monitoredItem);
 }
 
+static void
+ensureSpaceInMonitoredItemQueue(UA_MonitoredItem *mon) {
+    if(mon->currentQueueSize < mon->maxQueueSize)
+        return;
+    MonitoredItem_queuedValue *queueItem;
+    if(mon->discardOldest)
+        queueItem = TAILQ_FIRST(&mon->queue);
+    else
+        queueItem = TAILQ_LAST(&mon->queue, QueueOfQueueDataValues);
+    UA_assert(queueItem); /* When the currentQueueSize > 0, then there is an item */
+    TAILQ_REMOVE(&mon->queue, queueItem, listEntry);
+    UA_DataValue_deleteMembers(&queueItem->value);
+    UA_free(queueItem);
+    mon->currentQueueSize--;
+}
+
+/* Has this sample changed from the last one? The method may allocate additional
+ * space for the encoding buffer. Detect the change in encoding->data. */
+static UA_StatusCode
+detectValueChange(UA_MonitoredItem *mon, UA_DataValue *value,
+                  UA_ByteString *encoding, UA_Boolean *changed) {
+    /* Apply Filter */
+    UA_Boolean hasValue = value->hasValue;
+    if(mon->trigger == UA_DATACHANGETRIGGER_STATUS)
+        value->hasValue = false;
+    UA_Boolean hasServerTimestamp = value->hasServerTimestamp;
+    UA_Boolean hasServerPicoseconds = value->hasServerPicoseconds;
+    value->hasServerTimestamp = false;
+    value->hasServerPicoseconds = false;
+    UA_Boolean hasSourceTimestamp = value->hasSourceTimestamp;
+    UA_Boolean hasSourcePicoseconds = value->hasSourcePicoseconds;
+    if(mon->trigger < UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP) {
+        value->hasSourceTimestamp = false;
+        value->hasSourcePicoseconds = false;
+    }
+
+    /* Encode the data for comparison */
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    size_t binsize = UA_calcSizeBinary(value, &UA_TYPES[UA_TYPES_DATAVALUE]);
+    if(binsize == 0) {
+        retval = UA_STATUSCODE_BADINTERNALERROR;
+        goto cleanup;
+    }
+
+    /* Allocate buffer on the heap if necessary */
+    if(binsize > UA_VALUENCODING_MAXSTACK &&
+       UA_ByteString_allocBuffer(encoding, binsize) != UA_STATUSCODE_GOOD) {
+        retval = UA_STATUSCODE_BADOUTOFMEMORY;
+        goto cleanup;
+    }
+
+    /* Encode the value */
+    size_t encodingOffset = 0;
+    retval = UA_encodeBinary(value, &UA_TYPES[UA_TYPES_DATAVALUE],
+                             NULL, NULL, encoding, &encodingOffset);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto cleanup;
+
+    /* The value has changed */
+    encoding->length = encodingOffset;
+    if(!mon->lastSampledValue.data || !UA_String_equal(encoding, &mon->lastSampledValue))
+        *changed = true;
+
+ cleanup:
+    /* Reset the filter */
+    value->hasValue = hasValue;
+    value->hasServerTimestamp = hasServerTimestamp;
+    value->hasServerPicoseconds = hasServerPicoseconds;
+    value->hasSourceTimestamp = hasSourceTimestamp;
+    value->hasSourcePicoseconds = hasSourcePicoseconds;
+    return retval;
+}
+
 void UA_MoniteredItem_SampleCallback(UA_Server *server, UA_MonitoredItem *monitoredItem) {
     UA_Subscription *sub = monitoredItem->subscription;
     if(monitoredItem->monitoredItemType != UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
         UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
                              "Subscription %u | MonitoredItem %i | "
-                             "Cannot process a monitoreditem that is not "
-                             "a data change notification",
+                             "Not a data change notification",
                              sub->subscriptionID, monitoredItem->itemId);
         return;
     }
 
-    MonitoredItem_queuedValue *newvalue = UA_malloc(sizeof(MonitoredItem_queuedValue));
-    if(!newvalue) {
-        UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
-                               "Subscription %u | MonitoredItem %i | "
-                               "Skipped a sample due to lack of memory",
-                               sub->subscriptionID, monitoredItem->itemId);
-        return;
-    }
-    UA_DataValue_init(&newvalue->value);
-    newvalue->clientHandle = monitoredItem->clientHandle;
-
     /* Adjust timestampstoreturn to get source timestamp for triggering */
     UA_TimestampsToReturn ts = monitoredItem->timestampsToReturn;
     if(ts == UA_TIMESTAMPSTORETURN_SERVER)
@@ -79,106 +142,82 @@ void UA_MoniteredItem_SampleCallback(UA_Server *server, UA_MonitoredItem *monito
     rvid.nodeId = monitoredItem->monitoredNodeId;
     rvid.attributeId = monitoredItem->attributeID;
     rvid.indexRange = monitoredItem->indexRange;
-    Service_Read_single(server, sub->session, ts, &rvid, &newvalue->value);
-
-    /* Apply Filter */
-    UA_Boolean hasValue = newvalue->value.hasValue;
-    UA_Boolean hasServerTimestamp = newvalue->value.hasServerTimestamp;
-    UA_Boolean hasServerPicoseconds = newvalue->value.hasServerPicoseconds;
-    UA_Boolean hasSourceTimestamp = newvalue->value.hasSourceTimestamp;
-    UA_Boolean hasSourcePicoseconds = newvalue->value.hasSourcePicoseconds;
-    newvalue->value.hasServerTimestamp = false;
-    newvalue->value.hasServerPicoseconds = false;
-    if(monitoredItem->trigger == UA_DATACHANGETRIGGER_STATUS)
-        newvalue->value.hasValue = false;
-    if(monitoredItem->trigger < UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP) {
-        newvalue->value.hasSourceTimestamp = false;
-        newvalue->value.hasSourcePicoseconds = false;
-    }
-
-    /* Encode the data for comparison */
-    size_t binsize = UA_calcSizeBinary(&newvalue->value, &UA_TYPES[UA_TYPES_DATAVALUE]);
-    UA_ByteString newValueAsByteString;
-    UA_StatusCode retval = UA_ByteString_allocBuffer(&newValueAsByteString, binsize);
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_DataValue_deleteMembers(&newvalue->value);
-        UA_free(newvalue);
-        return;
-    }
-    size_t encodingOffset = 0;
-    retval = UA_encodeBinary(&newvalue->value, &UA_TYPES[UA_TYPES_DATAVALUE],
-                             NULL, NULL, &newValueAsByteString, &encodingOffset);
-
-    /* Restore the settings changed for the filter */
-    newvalue->value.hasValue = hasValue;
-    newvalue->value.hasServerTimestamp = hasServerTimestamp;
-    newvalue->value.hasServerPicoseconds = hasServerPicoseconds;
-    if(monitoredItem->timestampsToReturn == UA_TIMESTAMPSTORETURN_SERVER ||
-       monitoredItem->timestampsToReturn == UA_TIMESTAMPSTORETURN_NEITHER) {
-        newvalue->value.hasSourceTimestamp = false;
-        newvalue->value.hasSourcePicoseconds = false;
-    } else {
-        newvalue->value.hasSourceTimestamp = hasSourceTimestamp;
-        newvalue->value.hasSourcePicoseconds = hasSourcePicoseconds;
-    }
+    UA_DataValue value;
+    UA_DataValue_init(&value);
+    Service_Read_single(server, sub->session, ts, &rvid, &value);
+
+    /* Stack-allocate some memory for the value encoding */
+    UA_Byte *stackValueEncoding = UA_alloca(UA_VALUENCODING_MAXSTACK);
+    UA_ByteString valueEncoding;
+    valueEncoding.data = stackValueEncoding;
+    valueEncoding.length = UA_VALUENCODING_MAXSTACK;
+
+    /* Has the value changed? */
+    UA_Boolean changed = false;
+    UA_StatusCode retval = detectValueChange(monitoredItem, &value,
+                                             &valueEncoding, &changed);
+    if(!changed || retval != UA_STATUSCODE_GOOD)
+        goto cleanup;
 
-    /* Error or the value has not changed */
-    if(retval != UA_STATUSCODE_GOOD ||
-       (monitoredItem->lastSampledValue.data &&
-        UA_String_equal(&newValueAsByteString, &monitoredItem->lastSampledValue))) {
-        UA_LOG_TRACE_SESSION(server->config.logger, sub->session, "Subscription %u | "
-                             "MonitoredItem %u | Do not sample an unchanged value",
-                             sub->subscriptionID, monitoredItem->itemId);
+    /* Allocate the entry for the publish queue */
+    MonitoredItem_queuedValue *newQueueItem = UA_malloc(sizeof(MonitoredItem_queuedValue));
+    if(!newQueueItem) {
+        UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
+                               "Subscription %u | MonitoredItem %i | "
+                               "Item for the publishing queue could not be allocated",
+                               sub->subscriptionID, monitoredItem->itemId);
         goto cleanup;
     }
 
-    UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
-                         "Subscription %u | MonitoredItem %u | "
-                         "Sampling the value", sub->subscriptionID, monitoredItem->itemId);
-
-    /* Is enough space in the queue? */
-    if(monitoredItem->currentQueueSize >= monitoredItem->maxQueueSize) {
-        MonitoredItem_queuedValue *queueItem;
-        if(monitoredItem->discardOldest)
-            queueItem = TAILQ_FIRST(&monitoredItem->queue);
-        else
-            queueItem = TAILQ_LAST(&monitoredItem->queue, QueueOfQueueDataValues);
-
-        if(!queueItem) {
-            UA_LOG_WARNING_SESSION(server->config.logger, sub->session, "Subscription %u | "
-                                   "MonitoredItem %u | Cannot remove an element from the full "
-                                   "queue. Internal error!", sub->subscriptionID,
-                                   monitoredItem->itemId);
+    /* Copy valueEncoding on the heap for the next comparison (if not already done) */
+    if(valueEncoding.data == stackValueEncoding) {
+        UA_ByteString cbs;
+        if(UA_ByteString_copy(&valueEncoding, &cbs) != UA_STATUSCODE_GOOD) {
+            UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
+                                   "Subscription %u | MonitoredItem %i | "
+                                   "ByteString to compare values could not be created",
+                                   sub->subscriptionID, monitoredItem->itemId);
+            UA_free(newQueueItem);
             goto cleanup;
         }
-
-        TAILQ_REMOVE(&monitoredItem->queue, queueItem, listEntry);
-        UA_DataValue_deleteMembers(&queueItem->value);
-        UA_free(queueItem);
-        monitoredItem->currentQueueSize--;
+        valueEncoding = cbs;
     }
 
-    /* If the read request returned a datavalue pointing into the nodestore, we
-     * must make a deep copy to keep the datavalue across mainloop iterations */
-    if(newvalue->value.hasValue &&
-       newvalue->value.value.storageType == UA_VARIANT_DATA_NODELETE) {
-        UA_Variant tempv = newvalue->value.value;
-        UA_Variant_copy(&tempv, &newvalue->value.value);
+    /* Prepare the newQueueItem */
+    if(value.hasValue && value.value.storageType == UA_VARIANT_DATA_NODELETE) {
+        if(UA_DataValue_copy(&value, &newQueueItem->value) != UA_STATUSCODE_GOOD) {
+            UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
+                                   "Subscription %u | MonitoredItem %i | "
+                                   "Item for the publishing queue could not be prepared",
+                                   sub->subscriptionID, monitoredItem->itemId);
+            UA_free(newQueueItem);
+            goto cleanup;
+        }
+    } else {
+        newQueueItem->value = value;
     }
+    newQueueItem->clientHandle = monitoredItem->clientHandle;
+
+    /* <-- Point of no return --> */
+
+    UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
+                         "Subscription %u | MonitoredItem %u | Sampled a new value",
+                         sub->subscriptionID, monitoredItem->itemId);
 
-    /* Replace the comparison bytestring with the current sample */
+    /* Replace the encoding for comparison */
     UA_ByteString_deleteMembers(&monitoredItem->lastSampledValue);
-    monitoredItem->lastSampledValue = newValueAsByteString;
+    monitoredItem->lastSampledValue = valueEncoding;
 
     /* Add the sample to the queue for publication */
-    TAILQ_INSERT_TAIL(&monitoredItem->queue, newvalue, listEntry);
+    ensureSpaceInMonitoredItemQueue(monitoredItem);
+    TAILQ_INSERT_TAIL(&monitoredItem->queue, newQueueItem, listEntry);
     monitoredItem->currentQueueSize++;
     return;
 
  cleanup:
-    UA_ByteString_deleteMembers(&newValueAsByteString);
-    UA_DataValue_deleteMembers(&newvalue->value);
-    UA_free(newvalue);
+    if(valueEncoding.data != stackValueEncoding)
+        UA_ByteString_deleteMembers(&valueEncoding);
+    UA_DataValue_deleteMembers(&value);
 }
 
 UA_StatusCode

+ 14 - 48
src/ua_connection.c

@@ -5,22 +5,6 @@
 #include "ua_types_generated_handling.h"
 #include "ua_securechannel.h"
 
-void UA_Connection_init(UA_Connection *connection) {
-    connection->state = UA_CONNECTION_CLOSED;
-    connection->localConf = UA_ConnectionConfig_standard;
-    connection->remoteConf = UA_ConnectionConfig_standard;
-    connection->channel = NULL;
-    connection->sockfd = 0;
-    connection->handle = NULL;
-    UA_ByteString_init(&connection->incompleteMessage);
-    connection->send = NULL;
-    connection->recv = NULL;
-    connection->close = NULL;
-    connection->getSendBuffer = NULL;
-    connection->releaseSendBuffer = NULL;
-    connection->releaseRecvBuffer = NULL;
-}
-
 void UA_Connection_deleteMembers(UA_Connection *connection) {
     UA_ByteString_deleteMembers(&connection->incompleteMessage);
 }
@@ -70,7 +54,9 @@ UA_Connection_completeMessages(UA_Connection *connection, UA_ByteString * UA_RES
         UA_StatusCode decode_retval = UA_UInt32_decodeBinary(message, &length_pos, &chunk_length);
 
         /* The message size is not allowed. Throw the remaining bytestring away */
-        if(decode_retval != UA_STATUSCODE_GOOD || chunk_length < 16 || chunk_length > connection->localConf.recvBufferSize) {
+        if(decode_retval != UA_STATUSCODE_GOOD ||
+           chunk_length < 16 ||
+           chunk_length > connection->localConf.recvBufferSize) {
             garbage_end = true;
             break;
         }
@@ -113,7 +99,8 @@ UA_Connection_completeMessages(UA_Connection *connection, UA_ByteString * UA_RES
         retval = UA_ByteString_allocBuffer(&connection->incompleteMessage, incomplete_length);
         if(retval != UA_STATUSCODE_GOOD)
             goto cleanup;
-        memcpy(connection->incompleteMessage.data, &message->data[complete_until], incomplete_length);
+        memcpy(connection->incompleteMessage.data,
+               &message->data[complete_until], incomplete_length);
         message->length = complete_until;
     }
 
@@ -126,42 +113,22 @@ UA_Connection_completeMessages(UA_Connection *connection, UA_ByteString * UA_RES
     return retval;
 }
 
-#if (__GNUC__ >= 4 && __GNUC_MINOR__ >= 6)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wextra"
-#pragma GCC diagnostic ignored "-Wcast-qual"
-#pragma GCC diagnostic ignored "-Wunused-value"
-#endif
-
 void UA_Connection_detachSecureChannel(UA_Connection *connection) {
-#ifdef UA_ENABLE_MULTITHREADING
     UA_SecureChannel *channel = connection->channel;
     if(channel)
-        uatomic_cmpxchg(&channel->connection, connection, NULL);
-    uatomic_set(&connection->channel, NULL);
-#else
-    if(connection->channel)
-        connection->channel->connection = NULL;
-    connection->channel = NULL;
-#endif
+        /* only replace when the channel points to this connection */
+        UA_atomic_cmpxchg((void**)&channel->connection, connection, NULL);
+    UA_atomic_xchg((void**)&connection->channel, NULL);
 }
 
-void UA_Connection_attachSecureChannel(UA_Connection *connection, UA_SecureChannel *channel) {
-#ifdef UA_ENABLE_MULTITHREADING
-    if(uatomic_cmpxchg(&channel->connection, NULL, connection) == NULL)
-        uatomic_set((void**)&connection->channel, (void*)channel);
-#else
-    if(channel->connection != NULL)
-        return;
-    channel->connection = connection;
-    connection->channel = channel;
-#endif
+// TODO: Return an error code
+void
+UA_Connection_attachSecureChannel(UA_Connection *connection,
+                                  UA_SecureChannel *channel) {
+    if(UA_atomic_cmpxchg((void**)&channel->connection, NULL, connection) == NULL)
+        UA_atomic_xchg((void**)&connection->channel, (void*)channel);
 }
 
-#if (__GNUC__ >= 4 && __GNUC_MINOR__ >= 6)
-#pragma GCC diagnostic pop
-#endif
-
 UA_StatusCode
 UA_EndpointUrl_split_ptr(const char *endpointUrl, char *hostname,
                          const char ** port, const char **path) {
@@ -279,7 +246,6 @@ UA_EndpointUrl_split(const char *endpointUrl, char *hostname,
     return UA_STATUSCODE_GOOD;
 }
 
-
 size_t UA_readNumber(UA_Byte *buf, size_t buflen, UA_UInt32 *number) {
     if (!buf)
         return 0;

+ 26 - 39
src/ua_securechannel.c

@@ -10,16 +10,7 @@
 #define UA_SECURE_MESSAGE_HEADER_LENGTH 24
 
 void UA_SecureChannel_init(UA_SecureChannel *channel) {
-    UA_MessageSecurityMode_init(&channel->securityMode);
-    UA_ChannelSecurityToken_init(&channel->securityToken);
-    UA_ChannelSecurityToken_init(&channel->nextSecurityToken);
-    UA_AsymmetricAlgorithmSecurityHeader_init(&channel->clientAsymAlgSettings);
-    UA_AsymmetricAlgorithmSecurityHeader_init(&channel->serverAsymAlgSettings);
-    UA_ByteString_init(&channel->clientNonce);
-    UA_ByteString_init(&channel->serverNonce);
-    channel->receiveSequenceNumber = 0;
-    channel->sendSequenceNumber = 0;
-    channel->connection = NULL;
+    memset(channel, 0, sizeof(UA_SecureChannel));
     LIST_INIT(&channel->sessions);
     LIST_INIT(&channel->chunks);
 }
@@ -76,18 +67,10 @@ void UA_SecureChannel_attachSession(UA_SecureChannel *channel, UA_Session *sessi
     if(!se)
         return;
     se->session = session;
-#ifdef UA_ENABLE_MULTITHREADING
-    if(uatomic_cmpxchg(&session->channel, NULL, channel) != NULL) {
+    if(UA_atomic_cmpxchg((void**)&session->channel, NULL, channel) != NULL) {
         UA_free(se);
         return;
     }
-#else
-    if(session->channel != NULL) {
-        UA_free(se);
-        return;
-    }
-    session->channel = channel;
-#endif
     LIST_INSERT_HEAD(&channel->sessions, se, pointers);
 }
 
@@ -125,7 +108,8 @@ void UA_SecureChannel_revolveTokens(UA_SecureChannel *channel) {
         return;
 
     //FIXME: not thread-safe
-    memcpy(&channel->securityToken, &channel->nextSecurityToken, sizeof(UA_ChannelSecurityToken));
+    memcpy(&channel->securityToken, &channel->nextSecurityToken,
+           sizeof(UA_ChannelSecurityToken));
     UA_ChannelSecurityToken_init(&channel->nextSecurityToken);
 }
 
@@ -145,9 +129,11 @@ UA_SecureChannel_sendChunk(UA_ChunkInfo *ci, UA_ByteString *dst, size_t offset)
     dst->length += UA_SECURE_MESSAGE_HEADER_LENGTH;
     offset += UA_SECURE_MESSAGE_HEADER_LENGTH;
 
-    if(ci->messageSizeSoFar + offset > connection->remoteConf.maxMessageSize && connection->remoteConf.maxMessageSize > 0)
+    if(ci->messageSizeSoFar + offset > connection->remoteConf.maxMessageSize &&
+       connection->remoteConf.maxMessageSize > 0)
         ci->errorCode = UA_STATUSCODE_BADRESPONSETOOLARGE;
-    if(++ci->chunksSoFar > connection->remoteConf.maxChunkCount && connection->remoteConf.maxChunkCount > 0)
+    if(++ci->chunksSoFar > connection->remoteConf.maxChunkCount &&
+       connection->remoteConf.maxChunkCount > 0)
         ci->errorCode = UA_STATUSCODE_BADRESPONSETOOLARGE;
 
     /* Prepare the chunk headers */
@@ -177,11 +163,7 @@ UA_SecureChannel_sendChunk(UA_ChunkInfo *ci, UA_ByteString *dst, size_t offset)
     symSecHeader.tokenId = channel->securityToken.tokenId;
     UA_SequenceHeader seqHeader;
     seqHeader.requestId = ci->requestId;
-#ifndef UA_ENABLE_MULTITHREADING
-    seqHeader.sequenceNumber = ++channel->sendSequenceNumber;
-#else
-    seqHeader.sequenceNumber = uatomic_add_return(&channel->sendSequenceNumber, 1);
-#endif
+    seqHeader.sequenceNumber = UA_atomic_add(&channel->sendSequenceNumber, 1);
     size_t offset_header = 0;
     UA_SecureConversationMessageHeader_encodeBinary(&respHeader, dst, &offset_header);
     UA_SymmetricAlgorithmSecurityHeader_encodeBinary(&symSecHeader, dst, &offset_header);
@@ -193,7 +175,8 @@ UA_SecureChannel_sendChunk(UA_ChunkInfo *ci, UA_ByteString *dst, size_t offset)
 
     /* Replace with the buffer for the next chunk */
     if(!ci->final) {
-        UA_StatusCode retval = connection->getSendBuffer(connection, connection->localConf.sendBufferSize, dst);
+        UA_StatusCode retval =
+            connection->getSendBuffer(connection, connection->localConf.sendBufferSize, dst);
         if(retval != UA_STATUSCODE_GOOD)
             return retval;
         /* Hide the header of the buffer, so that the ensuing encoding does not overwrite anything */
@@ -204,15 +187,16 @@ UA_SecureChannel_sendChunk(UA_ChunkInfo *ci, UA_ByteString *dst, size_t offset)
 }
 
 UA_StatusCode
-UA_SecureChannel_sendBinaryMessage(UA_SecureChannel *channel, UA_UInt32 requestId, const void *content,
-                                   const UA_DataType *contentType) {
+UA_SecureChannel_sendBinaryMessage(UA_SecureChannel *channel, UA_UInt32 requestId,
+                                   const void *content, const UA_DataType *contentType) {
     UA_Connection *connection = channel->connection;
     if(!connection)
         return UA_STATUSCODE_BADINTERNALERROR;
 
     /* Allocate the message buffer */
     UA_ByteString message;
-    UA_StatusCode retval = connection->getSendBuffer(connection, connection->localConf.sendBufferSize, &message);
+    UA_StatusCode retval =
+        connection->getSendBuffer(connection, connection->localConf.sendBufferSize, &message);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
 
@@ -239,7 +223,8 @@ UA_SecureChannel_sendBinaryMessage(UA_SecureChannel *channel, UA_UInt32 requestI
         ci.messageType = UA_MESSAGETYPE_OPN;
     else if(typeId.identifier.numeric == 452 || typeId.identifier.numeric == 455)
         ci.messageType = UA_MESSAGETYPE_CLO;
-    retval = UA_encodeBinary(content, contentType, (UA_exchangeEncodeBuffer)UA_SecureChannel_sendChunk,
+    retval = UA_encodeBinary(content, contentType,
+                             (UA_exchangeEncodeBuffer)UA_SecureChannel_sendChunk,
                              &ci, &message, &messagePos);
 
     /* Encoding failed, release the message */
@@ -366,15 +351,17 @@ UA_SecureChannel_processSequenceNumber(UA_SecureChannel *channel, UA_UInt32 Sequ
 UA_StatusCode
 UA_SecureChannel_processChunks(UA_SecureChannel *channel, const UA_ByteString *chunks,
                                UA_ProcessMessageCallback callback, void *application) {
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
     size_t offset= 0;
     do {
+        /* Store the initial offset to compute the header length */
         size_t initial_offset = offset;
-        
+
         /* Decode header */
         UA_SecureConversationMessageHeader header;
-        UA_StatusCode retval = UA_SecureConversationMessageHeader_decodeBinary(chunks, &offset, &header);
+        retval = UA_SecureConversationMessageHeader_decodeBinary(chunks, &offset, &header);
         if(retval != UA_STATUSCODE_GOOD)
-            return retval;
+            break;
 
         /* Is the channel attached to connection? */
         if(header.secureChannelId != channel->securityToken.channelId) {
@@ -416,15 +403,15 @@ UA_SecureChannel_processChunks(UA_SecureChannel *channel, const UA_ByteString *c
                                          header.messageHeader.messageSize - processed_header);
             break;
         case UA_CHUNKTYPE_FINAL: {
-            UA_Boolean deleteMessage = false;
+            UA_Boolean realloced = false;
             UA_ByteString message =
                 UA_SecureChannel_finalizeChunk(channel, sequenceHeader.requestId, chunks, offset,
                                                header.messageHeader.messageSize - processed_header,
-                                               &deleteMessage);
+                                               &realloced);
             if(message.length > 0) {
                 callback(application, channel, header.messageHeader.messageTypeAndChunkType & 0x00ffffff,
                          sequenceHeader.requestId, &message);
-                if(deleteMessage)
+                if(realloced)
                     UA_ByteString_deleteMembers(&message);
             }
             break; }
@@ -439,5 +426,5 @@ UA_SecureChannel_processChunks(UA_SecureChannel *channel, const UA_ByteString *c
         offset += (header.messageHeader.messageSize - processed_header);
     } while(chunks->length > offset);
 
-    return UA_STATUSCODE_GOOD;
+    return retval;
 }

+ 121 - 102
src/ua_types.c

@@ -14,17 +14,15 @@
  * datatypes are autogenerated. */
 
 /* Static definition of NULL type instances */
-UA_EXPORT const UA_String UA_STRING_NULL = {.length = 0, .data = NULL };
-UA_EXPORT const UA_ByteString UA_BYTESTRING_NULL = {.length = 0, .data = NULL };
-UA_EXPORT const UA_Guid UA_GUID_NULL = {.data1 = 0, .data2 = 0, .data3 = 0,
-                                        .data4 = {0,0,0,0,0,0,0,0}};
-UA_EXPORT const UA_NodeId UA_NODEID_NULL = {0, UA_NODEIDTYPE_NUMERIC, {0}};
-UA_EXPORT const UA_ExpandedNodeId UA_EXPANDEDNODEID_NULL = {
-    .nodeId = { .namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
-                .identifier.numeric = 0 },
-    .namespaceUri = {.length = 0, .data = NULL}, .serverIndex = 0 };
-
-static void UA_deleteMembers_noInit(void *p, const UA_DataType *type);
+const UA_String UA_STRING_NULL = {.length = 0, .data = NULL };
+const UA_ByteString UA_BYTESTRING_NULL = {.length = 0, .data = NULL };
+const UA_Guid UA_GUID_NULL = {.data1 = 0, .data2 = 0, .data3 = 0,
+                              .data4 = {0,0,0,0,0,0,0,0}};
+const UA_NodeId UA_NODEID_NULL = {0, UA_NODEIDTYPE_NUMERIC, {0}};
+const UA_ExpandedNodeId UA_EXPANDEDNODEID_NULL = {
+       .nodeId = {.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
+                  .identifier.numeric = 0 },
+       .namespaceUri = {.length = 0, .data = NULL}, .serverIndex = 0 };
 
 /***************************/
 /* Random Number Generator */
@@ -46,6 +44,9 @@ UA_UInt32_random(void) {
 /* Builtin Types */
 /*****************/
 
+static void deleteMembers_noInit(void *, const UA_DataType *);
+static UA_StatusCode copy_noInit(const void *, void *, const UA_DataType *);
+
 UA_String
 UA_String_fromChars(char const src[]) {
     UA_String str = UA_STRING_NULL;
@@ -86,7 +87,8 @@ UA_DateTime_toStruct(UA_DateTime t) {
     dateTimeStruct.milliSec = (UA_UInt16)((t % 10000000) / 10000);
 
     /* Calculating the unix time with #include <time.h> */
-    time_t secSinceUnixEpoch = (time_t)((t - UA_DATETIME_UNIX_EPOCH) / UA_SEC_TO_DATETIME);
+    time_t secSinceUnixEpoch =
+        (time_t)((t - UA_DATETIME_UNIX_EPOCH) / UA_SEC_TO_DATETIME);
     struct tm ts;
     memset(&ts, 0, sizeof(struct tm));
     __secs_to_tm(secSinceUnixEpoch, &ts);
@@ -166,10 +168,9 @@ UA_Guid_random(void) {
 /* ByteString */
 UA_StatusCode
 UA_ByteString_allocBuffer(UA_ByteString *bs, size_t length) {
-    if(length == 0) {
-        UA_ByteString_init(bs);
+    UA_ByteString_init(bs);
+    if(length == 0)
         return UA_STATUSCODE_GOOD;
-    }
     if(!(bs->data = UA_malloc(length)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
     bs->length = length;
@@ -196,13 +197,15 @@ NodeId_copy(UA_NodeId const *src, UA_NodeId *dst, const UA_DataType *_) {
         *dst = *src;
         return UA_STATUSCODE_GOOD;
     case UA_NODEIDTYPE_STRING:
-        retval |= UA_String_copy(&src->identifier.string, &dst->identifier.string);
+        retval |= UA_String_copy(&src->identifier.string,
+                                 &dst->identifier.string);
         break;
     case UA_NODEIDTYPE_GUID:
         retval |= UA_Guid_copy(&src->identifier.guid, &dst->identifier.guid);
         break;
     case UA_NODEIDTYPE_BYTESTRING:
-        retval |= UA_ByteString_copy(&src->identifier.byteString, &dst->identifier.byteString);
+        retval |= UA_ByteString_copy(&src->identifier.byteString,
+                                     &dst->identifier.byteString);
         break;
     default:
         return UA_STATUSCODE_BADINTERNALERROR;
@@ -239,7 +242,8 @@ UA_NodeId_isNull(const UA_NodeId *p) {
 
 UA_Boolean
 UA_NodeId_equal(const UA_NodeId *n1, const UA_NodeId *n2) {
-    if(n1->namespaceIndex != n2->namespaceIndex || n1->identifierType!=n2->identifierType)
+    if(n1->namespaceIndex != n2->namespaceIndex ||
+       n1->identifierType!=n2->identifierType)
         return false;
     switch(n1->identifierType) {
     case UA_NODEIDTYPE_NUMERIC:
@@ -248,11 +252,14 @@ UA_NodeId_equal(const UA_NodeId *n1, const UA_NodeId *n2) {
         else
             return false;
     case UA_NODEIDTYPE_STRING:
-        return UA_String_equal(&n1->identifier.string, &n2->identifier.string);
+        return UA_String_equal(&n1->identifier.string,
+                               &n2->identifier.string);
     case UA_NODEIDTYPE_GUID:
-        return UA_Guid_equal(&n1->identifier.guid, &n2->identifier.guid);
+        return UA_Guid_equal(&n1->identifier.guid,
+                             &n2->identifier.guid);
     case UA_NODEIDTYPE_BYTESTRING:
-        return UA_ByteString_equal(&n1->identifier.byteString, &n2->identifier.byteString);
+        return UA_ByteString_equal(&n1->identifier.byteString,
+                                   &n2->identifier.byteString);
     }
     return false;
 }
@@ -301,8 +308,10 @@ ExtensionObject_copy(UA_ExtensionObject const *src, UA_ExtensionObject *dst,
     case UA_EXTENSIONOBJECT_ENCODED_BYTESTRING:
     case UA_EXTENSIONOBJECT_ENCODED_XML:
         dst->encoding = src->encoding;
-        retval = NodeId_copy(&src->content.encoded.typeId, &dst->content.encoded.typeId, NULL);
-        retval |= UA_ByteString_copy(&src->content.encoded.body, &dst->content.encoded.body);
+        retval = NodeId_copy(&src->content.encoded.typeId,
+                             &dst->content.encoded.typeId, NULL);
+        retval |= UA_ByteString_copy(&src->content.encoded.body,
+                                     &dst->content.encoded.body);
         break;
     case UA_EXTENSIONOBJECT_DECODED:
     case UA_EXTENSIONOBJECT_DECODED_NODELETE:
@@ -338,7 +347,8 @@ Variant_copy(UA_Variant const *src, UA_Variant *dst, const UA_DataType *_) {
     size_t length = src->arrayLength;
     if(UA_Variant_isScalar(src))
         length = 1;
-    UA_StatusCode retval = UA_Array_copy(src->data, length, &dst->data, src->type);
+    UA_StatusCode retval = UA_Array_copy(src->data, length,
+                                         &dst->data, src->type);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
     dst->arrayLength = src->arrayLength;
@@ -399,26 +409,29 @@ UA_Variant_setArrayCopy(UA_Variant *v, const void *array,
     return UA_STATUSCODE_GOOD;
 }
 
-/* Range-wise access to Variants */
-
-/* Test if a range is compatible with a variant. If yes, the following values are set:
+/* Test if a range is compatible with a variant. If yes, the following values
+ * are set:
  * - total: how many elements are in the range
- * - block: how big is each contiguous block of elements in the variant that maps into the range
+ * - block: how big is each contiguous block of elements in the variant that
+ *   maps into the range
  * - stride: how many elements are between the blocks (beginning to beginning)
  * - first: where does the first block begin */
 static UA_StatusCode
 computeStrides(const UA_Variant *v, const UA_NumericRange range,
                size_t *total, size_t *block, size_t *stride, size_t *first) {
-    /* Test the integrity of the source variant dimensions */
-    size_t dims_count = 1;
-    UA_UInt32 elements = 1;
+    /* Test for max array size */
 #if(MAX_SIZE > 0xffffffff) /* 64bit only */
     if(v->arrayLength > UA_UINT32_MAX)
         return UA_STATUSCODE_BADINTERNALERROR;
 #endif
+
+    /* Test the integrity of the source variant dimensions, make dimensions
+       vector of one dimension if none defined */
     UA_UInt32 arrayLength = (UA_UInt32)v->arrayLength;
     const UA_UInt32 *dims = &arrayLength;
+    size_t dims_count = 1;
     if(v->arrayDimensionsSize > 0) {
+        size_t elements = 1;
         dims_count = v->arrayDimensionsSize;
         dims = (UA_UInt32*)v->arrayDimensions;
         for(size_t i = 0; i < dims_count; i++)
@@ -438,26 +451,26 @@ computeStrides(const UA_Variant *v, const UA_NumericRange range,
             return UA_STATUSCODE_BADINDEXRANGENODATA;
         count *= (range.dimensions[i].max - range.dimensions[i].min) + 1;
     }
+    *total = count;
 
     /* Compute the stride length and the position of the first element */
-    size_t b = 1, s = elements, f = 0;
+    *block = count;           /* Assume the range describes the entire array. */
+    *stride = v->arrayLength; /* So it can be copied as a contiguous block.   */
+    *first = 0;
     size_t running_dimssize = 1;
     UA_Boolean found_contiguous = false;
-    for(size_t k = dims_count - 1; ; k--) {
-        if(!found_contiguous && (range.dimensions[k].min != 0 || range.dimensions[k].max + 1 != dims[k])) {
+    for(size_t k = dims_count; k > 0;) {
+        k--;
+        size_t dimrange = 1 + range.dimensions[k].max - range.dimensions[k].min;
+        if(!found_contiguous && dimrange != dims[k]) {
+            /* Found the maximum block that can be copied contiguously */
             found_contiguous = true;
-            b = (range.dimensions[k].max - range.dimensions[k].min + 1) * running_dimssize;
-            s = dims[k] * running_dimssize;
+            *block = running_dimssize * dimrange;
+            *stride = running_dimssize * dims[k];
         }
-        f += running_dimssize * range.dimensions[k].min;
+        *first += running_dimssize * range.dimensions[k].min;
         running_dimssize *= dims[k];
-        if(k == 0)
-            break;
     }
-    *total = count;
-    *block = b;
-    *stride = s;
-    *first = f;
     return UA_STATUSCODE_GOOD;
 }
 
@@ -526,7 +539,8 @@ UA_Variant_copyRange(const UA_Variant *orig_src, UA_Variant *dst,
         
     /* Compute the strides */
     size_t count, block, stride, first;
-    UA_StatusCode retval = computeStrides(src, thisrange, &count, &block, &stride, &first);
+    UA_StatusCode retval = computeStrides(src, thisrange, &count,
+                                          &block, &stride, &first);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
 
@@ -551,8 +565,9 @@ UA_Variant_copyRange(const UA_Variant *orig_src, UA_Variant *dst,
             }
         } else {
             for(size_t i = 0; i < block_count; i++) {
-                for(size_t j = 0; j < block && retval == UA_STATUSCODE_GOOD; j++) {
-                    retval = UA_copy((const void*)nextsrc, (void*)nextdst, src->type);
+                for(size_t j = 0; j < block; j++) {
+                    retval = UA_copy((const void*)nextsrc,
+                                     (void*)nextdst, src->type);
                     nextdst += elem_size;
                     nextsrc += elem_size;
                 }
@@ -569,15 +584,17 @@ UA_Variant_copyRange(const UA_Variant *orig_src, UA_Variant *dst,
                 retval = UA_STATUSCODE_BADINDEXRANGENODATA;
         }
 
-        /* copy the content */
+        /* Copy the content */
         for(size_t i = 0; i < block_count; i++) {
             for(size_t j = 0; j < block && retval == UA_STATUSCODE_GOOD; j++) {
                 if(stringLike)
                     retval = copySubString((const UA_String*)nextsrc,
-                                           (UA_String*)nextdst, nextrange.dimensions);
+                                           (UA_String*)nextdst,
+                                           nextrange.dimensions);
                 else
                     retval = UA_Variant_copyRange((const UA_Variant*)nextsrc,
-                                                  (UA_Variant*)nextdst, nextrange);
+                                                  (UA_Variant*)nextdst,
+                                                  nextrange);
                 nextdst += elem_size;
                 nextsrc += elem_size;
             }
@@ -587,8 +604,7 @@ UA_Variant_copyRange(const UA_Variant *orig_src, UA_Variant *dst,
 
     /* Clean up if copying failed */
     if(retval != UA_STATUSCODE_GOOD) {
-        size_t copied = ((nextdst - elem_size) - (uintptr_t)dst->data) / elem_size;
-        UA_Array_delete(dst->data, copied, src->type);
+        UA_Array_delete(dst->data, count, src->type);
         dst->data = NULL;
         return retval;
     }
@@ -598,10 +614,11 @@ UA_Variant_copyRange(const UA_Variant *orig_src, UA_Variant *dst,
     if(isScalar)
         return retval;
 
-    /* Finish the array */
+    /* Copy array dimensions */
     dst->arrayLength = count;
     if(src->arrayDimensionsSize > 0) {
-        dst->arrayDimensions = UA_Array_new(thisrange.dimensionsSize, &UA_TYPES[UA_TYPES_UINT32]);
+        dst->arrayDimensions =
+            UA_Array_new(thisrange.dimensionsSize, &UA_TYPES[UA_TYPES_UINT32]);
         if(!dst->arrayDimensions) {
             Variant_deletemembers(dst, NULL);
             return UA_STATUSCODE_BADOUTOFMEMORY;
@@ -615,19 +632,20 @@ UA_Variant_copyRange(const UA_Variant *orig_src, UA_Variant *dst,
 }
 
 /* TODO: Allow ranges to reach inside a scalars that are array-like, e.g.
-   variant and strings. This is already possible for reading... */
+ * variant and strings. This is already possible for reading... */
 static UA_StatusCode
 Variant_setRange(UA_Variant *v, void *array, size_t arraySize,
                  const UA_NumericRange range, UA_Boolean copy) {
     /* Compute the strides */
     size_t count, block, stride, first;
-    UA_StatusCode retval = computeStrides(v, range, &count, &block, &stride, &first);
+    UA_StatusCode retval = computeStrides(v, range, &count,
+                                          &block, &stride, &first);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
     if(count != arraySize)
         return UA_STATUSCODE_BADINDEXRANGEINVALID;
 
-    /* Transfer the content */
+    /* Move/copy the elements */
     size_t block_count = count / block;
     size_t elem_size = v->type->memSize;
     uintptr_t nextdst = (uintptr_t)v->data + (first * elem_size);
@@ -641,7 +659,7 @@ Variant_setRange(UA_Variant *v, void *array, size_t arraySize,
     } else {
         for(size_t i = 0; i < block_count; i++) {
             for(size_t j = 0; j < block; j++) {
-                UA_deleteMembers_noInit((void*)nextdst, v->type);
+                deleteMembers_noInit((void*)nextdst, v->type);
                 retval |= UA_copy((void*)nextsrc, (void*)nextdst, v->type);
                 nextdst += elem_size;
                 nextsrc += elem_size;
@@ -650,8 +668,7 @@ Variant_setRange(UA_Variant *v, void *array, size_t arraySize,
         }
     }
 
-    /* If pointers were transferred, initialize original array to prevent
-     * reuse */
+    /* If members were moved, initialize original array to prevent reuse */
     if(!copy && !v->type->fixedSize)
         memset(array, 0, sizeof(elem_size)*arraySize);
 
@@ -659,15 +676,16 @@ Variant_setRange(UA_Variant *v, void *array, size_t arraySize,
 }
 
 UA_StatusCode
-UA_Variant_setRange(UA_Variant *v, void * UA_RESTRICT array, size_t arraySize,
-                    const UA_NumericRange range) {
+UA_Variant_setRange(UA_Variant *v, void * UA_RESTRICT array,
+                    size_t arraySize, const UA_NumericRange range) {
     return Variant_setRange(v, array, arraySize, range, false);
 }
 
 UA_StatusCode
-UA_Variant_setRangeCopy(UA_Variant *v, const void *array, size_t arraySize,
-                        const UA_NumericRange range) {
-    return Variant_setRange(v, (void*)(uintptr_t)array, arraySize, range, true);
+UA_Variant_setRangeCopy(UA_Variant *v, const void *array,
+                        size_t arraySize, const UA_NumericRange range) {
+    return Variant_setRange(v, (void*)(uintptr_t)array,
+                            arraySize, range, true);
 }
 
 /* LocalizedText */
@@ -722,7 +740,8 @@ DiagnosticInfo_copy(UA_DiagnosticInfo const *src, UA_DiagnosticInfo *dst,
     if(src->hasAdditionalInfo)
        retval = UA_String_copy(&src->additionalInfo, &dst->additionalInfo);
     if(src->hasInnerDiagnosticInfo && src->innerDiagnosticInfo) {
-        if((dst->innerDiagnosticInfo = UA_malloc(sizeof(UA_DiagnosticInfo)))) {
+        dst->innerDiagnosticInfo = UA_malloc(sizeof(UA_DiagnosticInfo));
+        if(dst->innerDiagnosticInfo) {
             retval |= DiagnosticInfo_copy(src->innerDiagnosticInfo,
                                           dst->innerDiagnosticInfo, NULL);
             dst->hasInnerDiagnosticInfo = true;
@@ -774,8 +793,6 @@ copyGuid(const UA_Guid *src, UA_Guid *dst, const UA_DataType *_) {
     return UA_STATUSCODE_GOOD;
 }
 
-static UA_StatusCode copyNoInit(const void *src, void *dst, const UA_DataType *type);
-
 typedef UA_StatusCode
 (*UA_copySignature)(const void *src, void *dst, const UA_DataType *type);
 
@@ -791,48 +808,48 @@ static const UA_copySignature copyJumpTable[UA_BUILTIN_TYPES_COUNT + 1] = {
     (UA_copySignature)copy8Byte, // UInt64
     (UA_copySignature)copy4Byte, // Float
     (UA_copySignature)copy8Byte, // Double
-    (UA_copySignature)copyNoInit, // String
+    (UA_copySignature)copy_noInit, // String
     (UA_copySignature)copy8Byte, // DateTime
     (UA_copySignature)copyGuid, // Guid
-    (UA_copySignature)copyNoInit, // ByteString
-    (UA_copySignature)copyNoInit, // XmlElement
+    (UA_copySignature)copy_noInit, // ByteString
+    (UA_copySignature)copy_noInit, // XmlElement
     (UA_copySignature)NodeId_copy,
     (UA_copySignature)ExpandedNodeId_copy,
     (UA_copySignature)copy4Byte, // StatusCode
-    (UA_copySignature)copyNoInit, // QualifiedName
+    (UA_copySignature)copy_noInit, // QualifiedName
     (UA_copySignature)LocalizedText_copy, // LocalizedText
     (UA_copySignature)ExtensionObject_copy,
     (UA_copySignature)DataValue_copy,
     (UA_copySignature)Variant_copy,
     (UA_copySignature)DiagnosticInfo_copy,
-    (UA_copySignature)copyNoInit // all others
+    (UA_copySignature)copy_noInit // all others
 };
 
 static UA_StatusCode
-copyNoInit(const void *src, void *dst, const UA_DataType *type) {
+copy_noInit(const void *src, void *dst, const UA_DataType *type) {
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     uintptr_t ptrs = (uintptr_t)src;
     uintptr_t ptrd = (uintptr_t)dst;
     UA_Byte membersSize = type->membersSize;
     for(size_t i = 0; i < membersSize; i++) {
-        const UA_DataTypeMember *member = &type->members[i];
+        const UA_DataTypeMember *m= &type->members[i];
         const UA_DataType *typelists[2] = { UA_TYPES, &type[-type->typeIndex] };
-        const UA_DataType *memberType = &typelists[!member->namespaceZero][member->memberTypeIndex];
-        if(!member->isArray) {
-            ptrs += member->padding;
-            ptrd += member->padding;
-            size_t fi = memberType->builtin ? memberType->typeIndex : UA_BUILTIN_TYPES_COUNT;
-            retval |= copyJumpTable[fi]((const void*)ptrs, (void*)ptrd, memberType);
-            ptrs += memberType->memSize;
-            ptrd += memberType->memSize;
+        const UA_DataType *mt = &typelists[!m->namespaceZero][m->memberTypeIndex];
+        if(!m->isArray) {
+            ptrs += m->padding;
+            ptrd += m->padding;
+            size_t fi = mt->builtin ? mt->typeIndex : UA_BUILTIN_TYPES_COUNT;
+            retval |= copyJumpTable[fi]((const void*)ptrs, (void*)ptrd, mt);
+            ptrs += mt->memSize;
+            ptrd += mt->memSize;
         } else {
-            ptrs += member->padding;
-            ptrd += member->padding;
+            ptrs += m->padding;
+            ptrd += m->padding;
             size_t *dst_size = (size_t*)ptrd;
             const size_t size = *((const size_t*)ptrs);
             ptrs += sizeof(size_t);
             ptrd += sizeof(size_t);
-            retval |= UA_Array_copy(*(void* const*)ptrs, size, (void**)ptrd, memberType);
+            retval |= UA_Array_copy(*(void* const*)ptrs, size, (void**)ptrd, mt);
             if(retval == UA_STATUSCODE_GOOD)
                 *dst_size = size;
             else
@@ -847,16 +864,18 @@ copyNoInit(const void *src, void *dst, const UA_DataType *type) {
 UA_StatusCode
 UA_copy(const void *src, void *dst, const UA_DataType *type) {
     memset(dst, 0, type->memSize); /* init */
-    UA_StatusCode retval = copyNoInit(src, dst, type);
+    UA_StatusCode retval = copy_noInit(src, dst, type);
     if(retval != UA_STATUSCODE_GOOD)
         UA_deleteMembers(dst, type);
     return retval;
 }
 
-typedef void (*UA_deleteMembersSignature)(void *p, const UA_DataType *type);
 static void nopDeleteMembers(void *p, const UA_DataType *type) { }
 
-static const UA_deleteMembersSignature deleteMembersJumpTable[UA_BUILTIN_TYPES_COUNT + 1] = {
+typedef void (*UA_deleteMembersSignature)(void *p, const UA_DataType *type);
+
+static const
+UA_deleteMembersSignature deleteMembersJumpTable[UA_BUILTIN_TYPES_COUNT + 1] = {
     (UA_deleteMembersSignature)nopDeleteMembers, // Boolean
     (UA_deleteMembersSignature)nopDeleteMembers, // SByte
     (UA_deleteMembersSignature)nopDeleteMembers, // Byte
@@ -876,33 +895,33 @@ static const UA_deleteMembersSignature deleteMembersJumpTable[UA_BUILTIN_TYPES_C
     (UA_deleteMembersSignature)NodeId_deleteMembers,
     (UA_deleteMembersSignature)ExpandedNodeId_deleteMembers, // ExpandedNodeId
     (UA_deleteMembersSignature)nopDeleteMembers, // StatusCode
-    (UA_deleteMembersSignature)UA_deleteMembers_noInit, // QualifiedName
+    (UA_deleteMembersSignature)deleteMembers_noInit, // QualifiedName
     (UA_deleteMembersSignature)LocalizedText_deleteMembers, // LocalizedText
     (UA_deleteMembersSignature)ExtensionObject_deleteMembers,
     (UA_deleteMembersSignature)DataValue_deleteMembers,
     (UA_deleteMembersSignature)Variant_deletemembers,
     (UA_deleteMembersSignature)DiagnosticInfo_deleteMembers,
-    (UA_deleteMembersSignature)UA_deleteMembers_noInit,
+    (UA_deleteMembersSignature)deleteMembers_noInit,
 };
 
 static void
-UA_deleteMembers_noInit(void *p, const UA_DataType *type) {
+deleteMembers_noInit(void *p, const UA_DataType *type) {
     uintptr_t ptr = (uintptr_t)p;
     UA_Byte membersSize = type->membersSize;
     for(size_t i = 0; i < membersSize; i++) {
-        const UA_DataTypeMember *member = &type->members[i];
+        const UA_DataTypeMember *m= &type->members[i];
         const UA_DataType *typelists[2] = { UA_TYPES, &type[-type->typeIndex] };
-        const UA_DataType *memberType = &typelists[!member->namespaceZero][member->memberTypeIndex];
-        if(!member->isArray) {
-            ptr += member->padding;
-            size_t fi = memberType->builtin ? memberType->typeIndex : UA_BUILTIN_TYPES_COUNT;
-            deleteMembersJumpTable[fi]((void*)ptr, memberType);
-            ptr += memberType->memSize;
+        const UA_DataType *mt = &typelists[!m->namespaceZero][m->memberTypeIndex];
+        if(!m->isArray) {
+            ptr += m->padding;
+            size_t fi = mt->builtin ? mt->typeIndex : UA_BUILTIN_TYPES_COUNT;
+            deleteMembersJumpTable[fi]((void*)ptr, mt);
+            ptr += mt->memSize;
         } else {
-            ptr += member->padding;
+            ptr += m->padding;
             size_t length = *(size_t*)ptr;
             ptr += sizeof(size_t);
-            UA_Array_delete(*(void**)ptr, length, memberType);
+            UA_Array_delete(*(void**)ptr, length, mt);
             ptr += sizeof(void*);
         }
     }
@@ -910,13 +929,13 @@ UA_deleteMembers_noInit(void *p, const UA_DataType *type) {
 
 void
 UA_deleteMembers(void *p, const UA_DataType *type) {
-    UA_deleteMembers_noInit(p, type);
+    deleteMembers_noInit(p, type);
     memset(p, 0, type->memSize); /* init */
 }
 
 void
 UA_delete(void *p, const UA_DataType *type) {
-    UA_deleteMembers_noInit(p, type);
+    deleteMembers_noInit(p, type);
     UA_free(p);
 }
 

+ 4 - 73
src/ua_util.h

@@ -3,50 +3,15 @@
 
 #include "ua_config.h"
 
+/* Assert */
 #include <assert.h>
 #define UA_assert(ignore) assert(ignore)
 
-/*********************/
-/* Memory Management */
-/*********************/
-
-/* Replace the macros with functions for custom allocators if necessary */
-#include <stdlib.h> // malloc, free
-#ifdef _WIN32
-# include <malloc.h>
-#endif
-
-#ifndef UA_free
-# define UA_free(ptr) free(ptr)
-#endif
-#ifndef UA_malloc
-# define UA_malloc(size) malloc(size)
-#endif
-#ifndef UA_calloc
-# define UA_calloc(num, size) calloc(num, size)
-#endif
-#ifndef UA_realloc
-# define UA_realloc(ptr, size) realloc(ptr, size)
-#endif
-
-#ifndef NO_ALLOCA
-# if defined(__GNUC__) || defined(__clang__)
-#  define UA_alloca(size) __builtin_alloca (size)
-# elif defined(_WIN32)
-#  define UA_alloca(SIZE) _alloca(SIZE)
-# else
-#  include <alloca.h>
-#  define UA_alloca(SIZE) alloca(SIZE)
-# endif
-#endif
-
+/* container_of */
 #define container_of(ptr, type, member) \
     (type *)((uintptr_t)ptr - offsetof(type,member))
 
-/************************/
 /* Thread Local Storage */
-/************************/
-
 #ifdef UA_ENABLE_MULTITHREADING
 # if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
 #  define UA_THREAD_LOCAL _Thread_local /* C11 */
@@ -55,7 +20,7 @@
 # elif defined(_MSC_VER)
 #  define UA_THREAD_LOCAL __declspec(thread) /* MSVC extension */
 # else
-#  warning The compiler does not allow thread-local variables. The library can be built, but will not be thread safe.
+#  warning The compiler does not allow thread-local variables. The library can be built, but will not be thread-safe.
 # endif
 #endif
 
@@ -63,41 +28,7 @@
 # define UA_THREAD_LOCAL
 #endif
 
-/*************************/
-/* External Dependencies */
-/*************************/
+/* BSD Queue Macros */
 #include "queue.h"
 
-#ifdef UA_ENABLE_MULTITHREADING
-# define _LGPL_SOURCE
-# include <urcu.h>
-# include <urcu/wfcqueue.h>
-# include <urcu/uatomic.h>
-# include <urcu/rculfhash.h>
-# include <urcu/lfstack.h>
-# ifdef NDEBUG
-#  define UA_RCU_LOCK() rcu_read_lock()
-#  define UA_RCU_UNLOCK() rcu_read_unlock()
-#  define UA_ASSERT_RCU_LOCKED()
-#  define UA_ASSERT_RCU_UNLOCKED()
-# else
-   extern UA_THREAD_LOCAL bool rcu_locked;
-#   define UA_ASSERT_RCU_LOCKED() assert(rcu_locked)
-#   define UA_ASSERT_RCU_UNLOCKED() assert(!rcu_locked)
-#   define UA_RCU_LOCK() do {                     \
-        UA_ASSERT_RCU_UNLOCKED();                 \
-        rcu_locked = true;                        \
-        rcu_read_lock(); } while(0)
-#   define UA_RCU_UNLOCK() do {                   \
-        UA_ASSERT_RCU_LOCKED();                   \
-        rcu_locked = false;                       \
-        rcu_read_lock(); } while(0)
-# endif
-#else
-# define UA_RCU_LOCK()
-# define UA_RCU_UNLOCK()
-# define UA_ASSERT_RCU_LOCKED()
-# define UA_ASSERT_RCU_UNLOCKED()
-#endif
-
 #endif /* UA_UTIL_H_ */

+ 6 - 8
tests/CMakeLists.txt

@@ -37,22 +37,20 @@ macro(add_test_valgrind TEST_NAME)
     ENDIF()
 endmacro()
 
-
-
 # the unit test are built directly on the open62541 object files. so they can
 # access symbols that are hidden/not exported to the shared library
 
 add_executable(check_types_builtin check_types_builtin.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_types_builtin ${LIBS})
-add_test(types_builtin ${CMAKE_CURRENT_BINARY_DIR}/check_types_builtin)
+add_test_valgrind(types_builtin ${CMAKE_CURRENT_BINARY_DIR}/check_types_builtin)
 
 add_executable(check_types_memory check_types_memory.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_types_memory ${LIBS})
-add_test(types_memory ${CMAKE_CURRENT_BINARY_DIR}/check_types_memory)
+add_test_valgrind(types_memory ${CMAKE_CURRENT_BINARY_DIR}/check_types_memory)
 
 add_executable(check_types_range check_types_range.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_types_range ${LIBS})
-add_test(types_range ${CMAKE_CURRENT_BINARY_DIR}/check_types_range)
+add_test_valgrind(types_range ${CMAKE_CURRENT_BINARY_DIR}/check_types_range)
 
 add_executable(check_chunking check_chunking.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_chunking ${LIBS})
@@ -72,7 +70,7 @@ add_test_valgrind(services_nodemanagement ${CMAKE_CURRENT_BINARY_DIR}/check_serv
 
 add_executable(check_services_subscriptions check_services_subscriptions.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_services_subscriptions ${LIBS})
-add_test(check_services_subscriptions ${CMAKE_CURRENT_BINARY_DIR}/check_services_subscriptions)
+add_test_valgrind(check_services_subscriptions ${CMAKE_CURRENT_BINARY_DIR}/check_services_subscriptions)
 
 add_executable(check_nodestore check_nodestore.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_nodestore ${LIBS})
@@ -84,7 +82,7 @@ add_test_valgrind(session ${CMAKE_CURRENT_BINARY_DIR}/check_session)
 
 add_executable(check_server_jobs check_server_jobs.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_server_jobs ${LIBS})
-add_test(check_server_jobs ${CMAKE_CURRENT_BINARY_DIR}/check_server_jobs)
+add_test_valgrind(check_server_jobs ${CMAKE_CURRENT_BINARY_DIR}/check_server_jobs)
 
 add_executable(check_server_userspace check_server_userspace.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_server_userspace ${LIBS})
@@ -153,4 +151,4 @@ add_test_valgrind(check_client_subscriptions ${CMAKE_CURRENT_BINARY_DIR}/check_c
 
 add_executable(check_utils check_utils.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_utils ${LIBS})
-add_test(check_utils ${CMAKE_CURRENT_BINARY_DIR}/check_utils)
+add_test_valgrind(check_utils ${CMAKE_CURRENT_BINARY_DIR}/check_utils)

+ 2 - 0
tests/check_server_userspace.c

@@ -14,6 +14,8 @@ START_TEST(Server_addNamespace_ShallWork)
     UA_UInt16 b = UA_Server_addNamespace(server, "http://nameOfNamespace");
     UA_UInt16 c = UA_Server_addNamespace(server, "http://nameOfNamespace2");
 
+	UA_Server_delete(server);
+
     ck_assert_uint_gt(a, 0);
     ck_assert_uint_eq(a,b);
     ck_assert_uint_ne(a,c);

+ 25 - 25
tests/check_services_attributes.c

@@ -675,7 +675,7 @@ START_TEST(WriteSingleAttributeNodeId) {
     wValue.attributeId = UA_ATTRIBUTEID_NODEID;
     wValue.value.hasValue = true;
     UA_Variant_setScalar(&wValue.value.value, &id, &UA_TYPES[UA_TYPES_NODEID]);
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
     UA_Server_delete(server);
 } END_TEST
@@ -690,7 +690,7 @@ START_TEST(WriteSingleAttributeNodeclass) {
     wValue.attributeId = UA_ATTRIBUTEID_NODECLASS;
     wValue.value.hasValue = true;
     UA_Variant_setScalar(&wValue.value.value, &class, &UA_TYPES[UA_TYPES_NODECLASS]);
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
     UA_Server_delete(server);
 } END_TEST
@@ -704,7 +704,7 @@ START_TEST(WriteSingleAttributeBrowseName) {
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.attributeId = UA_ATTRIBUTEID_BROWSENAME;
     wValue.value.hasValue = true;
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     UA_Server_delete(server);
 } END_TEST
@@ -718,7 +718,7 @@ START_TEST(WriteSingleAttributeDisplayName) {
     wValue.value.hasValue = true;
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.attributeId = UA_ATTRIBUTEID_DISPLAYNAME;
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     UA_Server_delete(server);
 } END_TEST
@@ -734,7 +734,7 @@ START_TEST(WriteSingleAttributeDescription) {
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.attributeId = UA_ATTRIBUTEID_DESCRIPTION;
     wValue.value.hasValue = true;
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     UA_Server_delete(server);
 } END_TEST
@@ -749,7 +749,7 @@ START_TEST(WriteSingleAttributeWriteMask) {
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.attributeId = UA_ATTRIBUTEID_WRITEMASK;
     wValue.value.hasValue = true;
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     UA_Server_delete(server);
 } END_TEST
@@ -763,7 +763,7 @@ START_TEST(WriteSingleAttributeUserWriteMask) {
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.attributeId = UA_ATTRIBUTEID_USERWRITEMASK;
     wValue.value.hasValue = true;
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     UA_Server_delete(server);
 } END_TEST
@@ -777,7 +777,7 @@ START_TEST(WriteSingleAttributeIsAbstract) {
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.attributeId = UA_ATTRIBUTEID_ISABSTRACT;
     wValue.value.hasValue = true;
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     ck_assert_int_eq(retval, UA_STATUSCODE_BADNODECLASSINVALID);
     UA_Server_delete(server);
 } END_TEST
@@ -791,7 +791,7 @@ START_TEST(WriteSingleAttributeSymmetric) {
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.attributeId = UA_ATTRIBUTEID_SYMMETRIC;
     wValue.value.hasValue = true;
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     ck_assert_int_eq(retval, UA_STATUSCODE_BADNODECLASSINVALID);
     UA_Server_delete(server);
 } END_TEST
@@ -805,7 +805,7 @@ START_TEST(WriteSingleAttributeInverseName) {
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.attributeId = UA_ATTRIBUTEID_INVERSENAME;
     wValue.value.hasValue = true;
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     ck_assert_int_eq(retval, UA_STATUSCODE_BADNODECLASSINVALID);
     UA_Server_delete(server);
 } END_TEST
@@ -819,7 +819,7 @@ START_TEST(WriteSingleAttributeContainsNoLoops) {
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.attributeId = UA_ATTRIBUTEID_CONTAINSNOLOOPS;
     wValue.value.hasValue = true;
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     ck_assert_int_eq(retval, UA_STATUSCODE_BADNODECLASSINVALID);
     UA_Server_delete(server);
 } END_TEST
@@ -833,7 +833,7 @@ START_TEST(WriteSingleAttributeEventNotifier) {
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.attributeId = UA_ATTRIBUTEID_EVENTNOTIFIER;
     wValue.value.hasValue = true;
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     ck_assert_int_eq(retval, UA_STATUSCODE_BADNODECLASSINVALID);
     UA_Server_delete(server);
 } END_TEST
@@ -847,7 +847,7 @@ START_TEST(WriteSingleAttributeValue) {
     wValue.value.hasValue = true;
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.attributeId = UA_ATTRIBUTEID_VALUE;
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 
     UA_DataValue resp;
@@ -874,7 +874,7 @@ START_TEST(WriteSingleAttributeValueRangeFromScalar) {
     wValue.nodeId = UA_NODEID_STRING(1, "myarray");
     wValue.indexRange = UA_STRING("0,0");
     wValue.attributeId = UA_ATTRIBUTEID_VALUE;
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     UA_Server_delete(server);
 } END_TEST
@@ -889,7 +889,7 @@ START_TEST(WriteSingleAttributeValueRangeFromArray) {
     wValue.nodeId = UA_NODEID_STRING(1, "myarray");
     wValue.indexRange = UA_STRING("0,0");
     wValue.attributeId = UA_ATTRIBUTEID_VALUE;
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     UA_Server_delete(server);
 } END_TEST
@@ -904,7 +904,7 @@ START_TEST(WriteSingleAttributeDataType) {
     wValue.attributeId = UA_ATTRIBUTEID_DATATYPE;
     wValue.value.hasValue = true;
     UA_Variant_setScalar(&wValue.value.value, &typeId, &UA_TYPES[UA_TYPES_NODEID]);
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     ck_assert_int_eq(retval, UA_STATUSCODE_BADTYPEMISMATCH);
     UA_Server_delete(server);
 } END_TEST
@@ -918,7 +918,7 @@ START_TEST(WriteSingleAttributeValueRank) {
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.attributeId = UA_ATTRIBUTEID_VALUERANK;
     wValue.value.hasValue = true;
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     // Returns attributeInvalid, since variant/value may be writable
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     UA_Server_delete(server);
@@ -933,7 +933,7 @@ START_TEST(WriteSingleAttributeArrayDimensions) {
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.attributeId = UA_ATTRIBUTEID_ARRAYDIMENSIONS;
     wValue.value.hasValue = true;
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     // Returns attributeInvalid, since variant/value may be writable
     ck_assert_int_eq(retval, UA_STATUSCODE_BADTYPEMISMATCH);
     UA_Server_delete(server);
@@ -948,7 +948,7 @@ START_TEST(WriteSingleAttributeAccessLevel) {
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.attributeId = UA_ATTRIBUTEID_ACCESSLEVEL;
     wValue.value.hasValue = true;
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     UA_Server_delete(server);
 } END_TEST
@@ -962,7 +962,7 @@ START_TEST(WriteSingleAttributeUserAccessLevel) {
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.attributeId = UA_ATTRIBUTEID_USERACCESSLEVEL;
     wValue.value.hasValue = true;
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     UA_Server_delete(server);
 } END_TEST
@@ -976,7 +976,7 @@ START_TEST(WriteSingleAttributeMinimumSamplingInterval) {
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.attributeId = UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL;
     wValue.value.hasValue = true;
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     UA_Server_delete(server);
 } END_TEST
@@ -990,7 +990,7 @@ START_TEST(WriteSingleAttributeHistorizing) {
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.attributeId = UA_ATTRIBUTEID_HISTORIZING;
     wValue.value.hasValue = true;
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     UA_Server_delete(server);
 } END_TEST
@@ -1004,7 +1004,7 @@ START_TEST(WriteSingleAttributeExecutable) {
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.attributeId = UA_ATTRIBUTEID_EXECUTABLE;
     wValue.value.hasValue = true;
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     ck_assert_int_eq(retval, UA_STATUSCODE_BADNODECLASSINVALID);
     UA_Server_delete(server);
 } END_TEST
@@ -1018,7 +1018,7 @@ START_TEST(WriteSingleAttributeUserExecutable) {
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.attributeId = UA_ATTRIBUTEID_USEREXECUTABLE;
     wValue.value.hasValue = true;
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     ck_assert_int_eq(retval, UA_STATUSCODE_BADNODECLASSINVALID);
     UA_Server_delete(server);
 } END_TEST
@@ -1032,7 +1032,7 @@ START_TEST(WriteSingleDataSourceAttributeValue) {
     wValue.nodeId = UA_NODEID_STRING(1, "cpu.temperature");
     wValue.attributeId = UA_ATTRIBUTEID_VALUE;
     wValue.value.hasValue = true;
-    UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
     ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
     UA_Server_delete(server);
 } END_TEST

+ 33 - 0
tests/check_utils.c

@@ -112,6 +112,38 @@ START_TEST(readNumber) {
 }
 END_TEST
 
+
+START_TEST(StatusCode_msg) {
+#ifndef UA_ENABLE_STATUSCODE_DESCRIPTIONS
+    ck_assert_str_eq(UA_StatusCode_msg(UA_STATUSCODE_GOOD), "StatusCode descriptions not available");
+    return;
+#endif
+		// first element in table
+    ck_assert_str_eq(UA_StatusCode_explanation(UA_STATUSCODE_GOOD), "Success / No error");
+    ck_assert_str_eq(UA_StatusCode_name(UA_STATUSCODE_GOOD), "Good");
+
+		// just some randomly picked status codes
+    ck_assert_str_eq(UA_StatusCode_explanation(UA_STATUSCODE_BADNOCOMMUNICATION),
+                     "Communication with the data source is defined");
+    ck_assert_str_eq(UA_StatusCode_name(UA_STATUSCODE_BADNOCOMMUNICATION),
+                     "BadNoCommunication");
+
+    ck_assert_str_eq(UA_StatusCode_explanation(UA_STATUSCODE_GOODNODATA),
+                     "No data exists for the requested time range or event filter.");
+    ck_assert_str_eq(UA_StatusCode_name(UA_STATUSCODE_GOODNODATA), "GoodNoData");
+
+		// last element in table
+    ck_assert_str_eq(UA_StatusCode_explanation(UA_STATUSCODE_BADMAXCONNECTIONSREACHED),
+                     "The operation could not be finished because all available connections are in use.");
+    ck_assert_str_eq(UA_StatusCode_name(UA_STATUSCODE_BADMAXCONNECTIONSREACHED),
+                     "BadMaxConnectionsReached");
+
+		// an invalid status code
+	ck_assert_str_eq(UA_StatusCode_explanation(0x80123456), "Unknown StatusCode");
+	ck_assert_str_eq(UA_StatusCode_name(0x80123456), "Unknown");
+}
+END_TEST
+
 static Suite* testSuite_Utils(void) {
     Suite *s = suite_create("Utils");
     TCase *tc_endpointUrl_split = tcase_create("EndpointUrl_split");
@@ -119,6 +151,7 @@ static Suite* testSuite_Utils(void) {
     suite_add_tcase(s,tc_endpointUrl_split);
     TCase *tc_utils = tcase_create("Utils");
     tcase_add_test(tc_utils, readNumber);
+    tcase_add_test(tc_utils, StatusCode_msg);
     suite_add_tcase(s,tc_utils);
     return s;
 }

+ 2 - 0
tools/generate_datatypes.py

@@ -97,6 +97,8 @@ class Type(object):
             #",\n  .xmlEncodingId = " + xmlEncodingId + \ Not used for now
 
     def members_c(self):
+        if len(self.members)==0:
+            return "#define %s_members NULL" % (self.name)
         members = "static UA_DataTypeMember %s_members[%s] = {" % (self.name, len(self.members))
         before = None
         for index, member in enumerate(self.members):

+ 65 - 0
tools/generate_statuscode_descriptions.py

@@ -0,0 +1,65 @@
+from __future__ import print_function
+import sys
+import platform
+import getpass
+import time
+import argparse
+
+parser = argparse.ArgumentParser()
+parser.add_argument('statuscodes', help='path/to/Opc.Ua.StatusCodes.csv')
+parser.add_argument('outfile', help='outfile w/o extension')
+args = parser.parse_args()
+
+f = open(args.statuscodes)
+input_str = f.read()
+f.close()
+input_str = input_str.replace('\r','')
+rows = map(lambda x:tuple(x.split(',')), input_str.split('\n'))
+
+fc = open(args.outfile + ".c",'w')
+def printc(string):
+    print(string, end='\n', file=fc)
+
+printc('''/**********************************************************
+ * '''+args.outfile+'''.hgen -- do not modify
+ **********************************************************
+ * Generated from '''+args.statuscodes+''' with script '''+sys.argv[0]+'''
+ * on host '''+platform.uname()[1]+''' by user '''+getpass.getuser()+''' at '''+
+       time.strftime("%Y-%m-%d %I:%M:%S")+'''
+ **********************************************************/\n
+
+#include "ua_types.h"''')
+
+count = 2
+for row in rows:
+    count += 1
+
+printc('''
+#ifndef UA_ENABLE_STATUSCODE_DESCRIPTIONS
+static const size_t statusCodeDescriptionsSize = 1;
+static const UA_StatusCodeDescription statusCodeDescriptions[1] = {
+{0xffffffff, \"StatusCode descriptions not available\", \"open62541 was compiled without support for statuscode descriptions\"}
+};
+#else
+static const size_t statusCodeDescriptionsSize = %s;
+static const UA_StatusCodeDescription statusCodeDescriptions[%i] =
+{''' % (count, count))
+
+printc(" {UA_STATUSCODE_GOOD, \"Good\", \"Success / No error\"},")
+for row in rows:
+    printc(" {UA_STATUSCODE_%s, \"%s\", \"%s\"}," % (row[0].upper(), row[0], row[2]))
+printc(" {0xffffffff, \"Unknown\", \"Unknown StatusCode\"},")
+printc('''\n};
+#endif''')
+
+printc('''
+const UA_StatusCodeDescription * UA_StatusCode_description(UA_StatusCode code) {
+    for(size_t i = 0; i < statusCodeDescriptionsSize; i++) {
+        if(statusCodeDescriptions[i].code == code)
+            return &statusCodeDescriptions[i];
+    }
+    return &statusCodeDescriptions[statusCodeDescriptionsSize-1];
+}
+''')
+
+fc.close()

+ 5 - 3
tools/pyUANamespace/ua_builtin_types.py

@@ -506,13 +506,15 @@ class opcua_BuiltinType_extensionObject_t(opcua_value_t):
       encField = self.getEncodingRule()[encFieldIdx]
       encFieldIdx = encFieldIdx + 1;
       if encField[2] == 0:
-        code.append("UA_" + subv.stringRepresentation + "_encodeBinary(&" + self.getCodeInstanceName()+"_struct."+subv.alias() + ", &" + self.getCodeInstanceName() + "->content.encoded.body, &" + self.getCodeInstanceName() + "_encOffset);" )
+        #code.append("UA_" + subv.stringRepresentation + "_encodeBinary(&" + self.getCodeInstanceName()+"_struct."+subv.alias() + ", &" + self.getCodeInstanceName() + "->content.encoded.body, &" + self.getCodeInstanceName() + "_encOffset);" )
+        code.append("retval |= UA_encodeBinary(&" + self.getCodeInstanceName()+"_struct."+subv.alias() + ", &UA_TYPES[UA_TYPES_" + subv.stringRepresentation.upper() + "], NULL, NULL, &" + self.getCodeInstanceName() + "->content.encoded.body, &" + self.getCodeInstanceName() + "_encOffset);" )
       else:
         if isinstance(subv, list):
           for subvidx in range(0,len(subv)):
-            code.append("UA_" + subv.stringRepresentation + "_encodeBinary(&" + self.getCodeInstanceName()+"_struct."+subv.alias() + "[" + str(subvidx) + "], &" + self.getCodeInstanceName() + "->content.encoded.body, &" + self.getCodeInstanceName() + "_encOffset);" )
+            #code.append("UA_" + subv.stringRepresentation + "_encodeBinary(&" + self.getCodeInstanceName()+"_struct."+subv.alias() + "[" + str(subvidx) + "], &" + self.getCodeInstanceName() + "->content.encoded.body, &" + self.getCodeInstanceName() + "_encOffset);" )
+            code.append("retval |= UA_encodeBinary(&" + self.getCodeInstanceName()+"_struct."+subv.alias() + "[" + str(subvidx) + "], &UA_TYPES[UA_TYPES_"  + subv.stringRepresentation.upper() + "], NULL, NULL, &" + self.getCodeInstanceName() + "->content.encoded.body, &" + self.getCodeInstanceName() + "_encOffset);" )
         else:
-          code.append("UA_" + subv.stringRepresentation + "_encodeBinary(&" + self.getCodeInstanceName()+"_struct."+subv.alias() + "[0], &" + self.getCodeInstanceName() + "->content.encoded.body, &" + self.getCodeInstanceName() + "_encOffset);" )
+          code.append("retval |= UA_encodeBinary(&" + self.getCodeInstanceName()+"_struct."+subv.alias() + "[0], &UA_TYPES[UA_TYPES_"  + subv.stringRepresentation.upper() + "], NULL, NULL, &" + self.getCodeInstanceName() + "->content.encoded.body, &" + self.getCodeInstanceName() + "_encOffset);" )
 
     # Reallocate the memory by swapping the 65k Bytestring for a new one
     code.append(self.getCodeInstanceName() + "->content.encoded.body.length = " + self.getCodeInstanceName() + "_encOffset;");

+ 37 - 19
tools/pyUANamespace/ua_namespace.py

@@ -572,6 +572,7 @@ class opcua_namespace():
       nmatrix[nind][0] = node
 
     # Determine the dependencies of all nodes
+    logger.debug("Determining node interdependencies.")
     for node in self.nodes:
       nind = self.nodes.index(node)
       #print "Examining node " + str(nind) + " " + str(node)
@@ -580,13 +581,13 @@ class opcua_namespace():
           tind = self.nodes.index(ref.target())
           # Typedefinition of this node has precedence over this node
           if ref.referenceType() in typeRefs and ref.isForward():
-            nmatrix[nind][tind+1] += 1
+            nmatrix[nind][tind+1] += 200 # Very big weight for typedefs
           # isSubTypeOf/typeDefinition of this node has precedence over this node
           elif ref.referenceType() in subTypeRefs and not ref.isForward():
-            nmatrix[nind][tind+1] += 1
+            nmatrix[nind][tind+1] += 100 # Big weight for subtypes
           # Else the target depends on us
           elif ref.isForward():
-            nmatrix[tind][nind+1] += 1
+            nmatrix[tind][nind+1] += 1 # regular weight for dependencies
 
     logger.debug("Using Djikstra topological sorting to determine printing order.")
     reorder = []
@@ -598,9 +599,9 @@ class opcua_namespace():
         if isinstance(ref.target(), opcua_node_t):
           tind = self.nodes.index(ref.target())
           if ref.referenceType() in typeRefs and ref.isForward():
-            nmatrix[nind][tind+1] -= 1
+            nmatrix[nind][tind+1] -= 200
           elif ref.referenceType() in subTypeRefs and not ref.isForward():
-            nmatrix[nind][tind+1] -= 1
+            nmatrix[nind][tind+1] -= 100
           elif ref.isForward():
             nmatrix[tind][nind+1] -= 1
       nmatrix[nind][0] = None
@@ -642,23 +643,39 @@ class opcua_namespace():
     header.append('#ifndef '+outfilename.upper()+'_H_')
     header.append('#define '+outfilename.upper()+'_H_')
     header.append('#ifdef UA_NO_AMALGAMATION')
-    header.append('#include "server/ua_server_internal.h"')
-    header.append('#include "server/ua_nodes.h"')
-    header.append('#include "ua_util.h"')
-    header.append('#include "ua_types.h"')
-    header.append('#include "ua_types_encoding_binary.h"')
-    header.append('#include "ua_types_generated_encoding_binary.h"')
-    header.append('#include "ua_transport_generated_encoding_binary.h"')
+    header.append(  '#include "server/ua_server_internal.h"')
+    header.append(  '#include "server/ua_nodes.h"')
+    header.append('  #include "ua_util.h"')
+    header.append('  #include "ua_types.h"')
+    header.append('  #include "ua_types_encoding_binary.h"')
+    header.append('  #include "ua_types_generated_encoding_binary.h"')
+    header.append('  #include "ua_transport_generated_encoding_binary.h"')
     header.append('#else')
-    header.append('#include "open62541.h"')
-    header.append('#define NULL ((void *)0)')
+    header.append('  #include "open62541.h"')
     header.append('#endif')
+    header.append('')
+    header.append('/* Definition that (in userspace models) may be ')
+    header.append(' * - not included in the amalgamated header or')
+    header.append(' * - not part of public headers or')
+    header.append(' * - not exported in the shared object in combination with any of the above')
+    header.append(' * but are required for value encoding.')
+    header.append(' * NOTE: Userspace UA_(decode|encode)Binary /wo amalgamations requires UA_EXPORT to be appended to the appropriate definitions. */')
     header.append('#ifndef UA_ENCODINGOFFSET_BINARY')
-    header.append('#define UA_ENCODINGOFFSET_BINARY 2')
+    header.append('#  define UA_ENCODINGOFFSET_BINARY 2')
     header.append('#endif')
-
+    header.append('#ifndef NULL')
+    header.append('  #define NULL ((void *)0)')
+    header.append('#endif')
+    header.append('#ifndef UA_malloc')
+    header.append('  #define UA_malloc(_p_size) malloc(_p_size)')
+    header.append('#endif')
+    header.append('#ifndef UA_free')
+    header.append('  #define UA_free(_p_ptr) free(_p_ptr)')
+    header.append('#endif')
+    
     code.append('#include "'+outfilename+'.h"')
-    code.append("UA_INLINE void "+outfilename+"(UA_Server *server) {")
+    code.append("UA_INLINE UA_StatusCode "+outfilename+"(UA_Server *server) {")
+    code.append('UA_StatusCode retval = UA_STATUSCODE_GOOD; ')
 
     # Before printing nodes, we need to request additional namespace arrays from the server
     for nsid in self.namespaceIdentifiers:
@@ -667,7 +684,7 @@ class opcua_namespace():
       else:
         name =  self.namespaceIdentifiers[nsid]
         name = name.replace("\"","\\\"")
-        code.append("UA_Server_addNamespace(server, \"" + name + "\");")
+        code.append("if (UA_Server_addNamespace(server, \"{0}\") != {1})\n    return UA_STATUSCODE_BADUNEXPECTEDERROR;".format(name, nsid))
 
     # Find all references necessary to create the namespace and
     # "Bootstrap" them so all other nodes can safely use these referencetypes whenever
@@ -690,7 +707,7 @@ class opcua_namespace():
     for r in refsUsed:
       code = code + r.printOpen62541CCode(unPrintedNodes, unPrintedRefs);
 
-    header.append("extern void "+outfilename+"(UA_Server *server);\n")
+    header.append("extern UA_StatusCode "+outfilename+"(UA_Server *server);\n")
     header.append("#endif /* "+outfilename.upper()+"_H_ */")
 
     # Note to self: do NOT - NOT! - try to iterate over unPrintedNodes!
@@ -727,6 +744,7 @@ class opcua_namespace():
     else:
       logger.debug("Printing succeeded for all references")
 
+    code.append("return UA_STATUSCODE_GOOD;")
     code.append("}")
     return (header,code)
 

+ 227 - 0
tools/schema/Opc.Ua.StatusCodes.csv

@@ -0,0 +1,227 @@
+BadUnexpectedError,0x80010000,An unexpected error occurred.
+BadInternalError,0x80020000,An internal error occurred as a result of a programming or configuration error.
+BadOutOfMemory,0x80030000,Not enough memory to complete the operation.
+BadResourceUnavailable,0x80040000,An operating system resource is not available.
+BadCommunicationError,0x80050000,A low level communication error occurred.
+BadEncodingError,0x80060000,Encoding halted because of invalid data in the objects being serialized.
+BadDecodingError,0x80070000,Decoding halted because of invalid data in the stream.
+BadEncodingLimitsExceeded,0x80080000,The message encoding/decoding limits imposed by the stack have been exceeded.
+BadRequestTooLarge,0x80B80000,The request message size exceeds limits set by the server.
+BadResponseTooLarge,0x80B90000,The response message size exceeds limits set by the client.
+BadUnknownResponse,0x80090000,An unrecognized response was received from the server.
+BadTimeout,0x800A0000,The operation timed out.
+BadServiceUnsupported,0x800B0000,The server does not support the requested service.
+BadShutdown,0x800C0000,The operation was cancelled because the application is shutting down.
+BadServerNotConnected,0x800D0000,The operation could not complete because the client is not connected to the server.
+BadServerHalted,0x800E0000,The server has stopped and cannot process any requests.
+BadNothingToDo,0x800F0000,There was nothing to do because the client passed a list of operations with no elements.
+BadTooManyOperations,0x80100000,The request could not be processed because it specified too many operations.
+BadTooManyMonitoredItems,0x80DB0000,The request could not be processed because there are too many monitored items in the subscription.
+BadDataTypeIdUnknown,0x80110000,The extension object cannot be (de)serialized because the data type id is not recognized.
+BadCertificateInvalid,0x80120000,The certificate provided as a parameter is not valid.
+BadSecurityChecksFailed,0x80130000,An error occurred verifying security.
+BadCertificateTimeInvalid,0x80140000,The Certificate has expired or is not yet valid.
+BadCertificateIssuerTimeInvalid,0x80150000,An Issuer Certificate has expired or is not yet valid.
+BadCertificateHostNameInvalid,0x80160000,The HostName used to connect to a Server does not match a HostName in the Certificate.
+BadCertificateUriInvalid,0x80170000,The URI specified in the ApplicationDescription does not match the URI in the Certificate.
+BadCertificateUseNotAllowed,0x80180000,The Certificate may not be used for the requested operation.
+BadCertificateIssuerUseNotAllowed,0x80190000,The Issuer Certificate may not be used for the requested operation.
+BadCertificateUntrusted,0x801A0000,The Certificate is not trusted.
+BadCertificateRevocationUnknown,0x801B0000,It was not possible to determine if the Certificate has been revoked.
+BadCertificateIssuerRevocationUnknown,0x801C0000,It was not possible to determine if the Issuer Certificate has been revoked.
+BadCertificateRevoked,0x801D0000,The certificate has been revoked.
+BadCertificateIssuerRevoked,0x801E0000,The issuer certificate has been revoked.
+BadCertificateChainIncomplete,0x810D0000,The certificate chain is incomplete.
+BadUserAccessDenied,0x801F0000,User does not have permission to perform the requested operation.
+BadIdentityTokenInvalid,0x80200000,The user identity token is not valid.
+BadIdentityTokenRejected,0x80210000,The user identity token is valid but the server has rejected it.
+BadSecureChannelIdInvalid,0x80220000,The specified secure channel is no longer valid.
+BadInvalidTimestamp,0x80230000,The timestamp is outside the range allowed by the server.
+BadNonceInvalid,0x80240000,The nonce does appear to be not a random value or it is not the correct length.
+BadSessionIdInvalid,0x80250000,The session id is not valid.
+BadSessionClosed,0x80260000,The session was closed by the client.
+BadSessionNotActivated,0x80270000,The session cannot be used because ActivateSession has not been called.
+BadSubscriptionIdInvalid,0x80280000,The subscription id is not valid.
+BadRequestHeaderInvalid,0x802A0000,The header for the request is missing or invalid.
+BadTimestampsToReturnInvalid,0x802B0000,The timestamps to return parameter is invalid.
+BadRequestCancelledByClient,0x802C0000,The request was cancelled by the client.
+BadTooManyArguments,0x80E50000,Too many arguments were provided.
+GoodSubscriptionTransferred,0x002D0000,The subscription was transferred to another session.
+GoodCompletesAsynchronously,0x002E0000,The processing will complete asynchronously.
+GoodOverload,0x002F0000,Sampling has slowed down due to resource limitations.
+GoodClamped,0x00300000,The value written was accepted but was clamped.
+BadNoCommunication,0x80310000,Communication with the data source is defined, but not established, and there is no last known value available.
+BadWaitingForInitialData,0x80320000,Waiting for the server to obtain values from the underlying data source.
+BadNodeIdInvalid,0x80330000,The syntax of the node id is not valid.
+BadNodeIdUnknown,0x80340000,The node id refers to a node that does not exist in the server address space.
+BadAttributeIdInvalid,0x80350000,The attribute is not supported for the specified Node.
+BadIndexRangeInvalid,0x80360000,The syntax of the index range parameter is invalid.
+BadIndexRangeNoData,0x80370000,No data exists within the range of indexes specified.
+BadDataEncodingInvalid,0x80380000,The data encoding is invalid.
+BadDataEncodingUnsupported,0x80390000,The server does not support the requested data encoding for the node.
+BadNotReadable,0x803A0000,The access level does not allow reading or subscribing to the Node.
+BadNotWritable,0x803B0000,The access level does not allow writing to the Node.
+BadOutOfRange,0x803C0000,The value was out of range.
+BadNotSupported,0x803D0000,The requested operation is not supported.
+BadNotFound,0x803E0000,A requested item was not found or a search operation ended without success.
+BadObjectDeleted,0x803F0000,The object cannot be used because it has been deleted.
+BadNotImplemented,0x80400000,Requested operation is not implemented.
+BadMonitoringModeInvalid,0x80410000,The monitoring mode is invalid.
+BadMonitoredItemIdInvalid,0x80420000,The monitoring item id does not refer to a valid monitored item.
+BadMonitoredItemFilterInvalid,0x80430000,The monitored item filter parameter is not valid.
+BadMonitoredItemFilterUnsupported,0x80440000,The server does not support the requested monitored item filter.
+BadFilterNotAllowed,0x80450000,A monitoring filter cannot be used in combination with the attribute specified.
+BadStructureMissing,0x80460000,A mandatory structured parameter was missing or null.
+BadEventFilterInvalid,0x80470000,The event filter is not valid.
+BadContentFilterInvalid,0x80480000,The content filter is not valid.
+BadFilterOperatorInvalid,0x80C10000,An unregognized operator was provided in a filter.
+BadFilterOperatorUnsupported,0x80C20000,A valid operator was provided, but the server does not provide support for this filter operator.
+BadFilterOperandCountMismatch,0x80C30000,The number of operands provided for the filter operator was less then expected for the operand provided.
+BadFilterOperandInvalid,0x80490000,The operand used in a content filter is not valid.
+BadFilterElementInvalid,0x80C40000,The referenced element is not a valid element in the content filter.
+BadFilterLiteralInvalid,0x80C50000,The referenced literal is not a valid value.
+BadContinuationPointInvalid,0x804A0000,The continuation point provide is longer valid.
+BadNoContinuationPoints,0x804B0000,The operation could not be processed because all continuation points have been allocated.
+BadReferenceTypeIdInvalid,0x804C0000,The operation could not be processed because all continuation points have been allocated.
+BadBrowseDirectionInvalid,0x804D0000,The browse direction is not valid.
+BadNodeNotInView,0x804E0000,The node is not part of the view.
+BadServerUriInvalid,0x804F0000,The ServerUri is not a valid URI.
+BadServerNameMissing,0x80500000,No ServerName was specified.
+BadDiscoveryUrlMissing,0x80510000,No DiscoveryUrl was specified.
+BadSempahoreFileMissing,0x80520000,The semaphore file specified by the client is not valid.
+BadRequestTypeInvalid,0x80530000,The security token request type is not valid.
+BadSecurityModeRejected,0x80540000,The security mode does not meet the requirements set by the Server.
+BadSecurityPolicyRejected,0x80550000,The security policy does not meet the requirements set by the Server.
+BadTooManySessions,0x80560000,The server has reached its maximum number of sessions.
+BadUserSignatureInvalid,0x80570000,The user token signature is missing or invalid.
+BadApplicationSignatureInvalid,0x80580000,The signature generated with the client certificate is missing or invalid.
+BadNoValidCertificates,0x80590000,The client did not provide at least one software certificate that is valid and meets the profile requirements for the server.
+BadIdentityChangeNotSupported,0x80C60000,The Server does not support changing the user identity assigned to the session.
+BadRequestCancelledByRequest,0x805A0000,The request was cancelled by the client with the Cancel service.
+BadParentNodeIdInvalid,0x805B0000,The parent node id does not to refer to a valid node.
+BadReferenceNotAllowed,0x805C0000,The reference could not be created because it violates constraints imposed by the data model.
+BadNodeIdRejected,0x805D0000,The requested node id was reject because it was either invalid or server does not allow node ids to be specified by the client.
+BadNodeIdExists,0x805E0000,The requested node id is already used by another node.
+BadNodeClassInvalid,0x805F0000,The node class is not valid.
+BadBrowseNameInvalid,0x80600000,The browse name is invalid.
+BadBrowseNameDuplicated,0x80610000,The browse name is not unique among nodes that share the same relationship with the parent.
+BadNodeAttributesInvalid,0x80620000,The node attributes are not valid for the node class.
+BadTypeDefinitionInvalid,0x80630000,The type definition node id does not reference an appropriate type node.
+BadSourceNodeIdInvalid,0x80640000,The source node id does not reference a valid node.
+BadTargetNodeIdInvalid,0x80650000,The target node id does not reference a valid node.
+BadDuplicateReferenceNotAllowed,0x80660000,The reference type between the nodes is already defined.
+BadInvalidSelfReference,0x80670000,The server does not allow this type of self reference on this node.
+BadReferenceLocalOnly,0x80680000,The reference type is not valid for a reference to a remote server.
+BadNoDeleteRights,0x80690000,The server will not allow the node to be deleted.
+UncertainReferenceNotDeleted,0x40BC0000,The server was not able to delete all target references.
+BadServerIndexInvalid,0x806A0000,The server index is not valid.
+BadViewIdUnknown,0x806B0000,The view id does not refer to a valid view node.
+BadViewTimestampInvalid,0x80C90000,The view timestamp is not available or not supported.
+BadViewParameterMismatch,0x80CA0000,The view parameters are not consistent with each other.
+BadViewVersionInvalid,0x80CB0000,The view version is not available or not supported.
+UncertainNotAllNodesAvailable,0x40C00000,The list of references may not be complete because the underlying system is not available.
+GoodResultsMayBeIncomplete,0x00BA0000,The server should have followed a reference to a node in a remote server but did not. The result set may be incomplete.
+BadNotTypeDefinition,0x80C80000,The provided Nodeid was not a type definition nodeid.
+UncertainReferenceOutOfServer,0x406C0000,One of the references to follow in the relative path references to a node in the address space in another server.
+BadTooManyMatches,0x806D0000,The requested operation has too many matches to return.
+BadQueryTooComplex,0x806E0000,The requested operation requires too many resources in the server.
+BadNoMatch,0x806F0000,The requested operation has no match to return.
+BadMaxAgeInvalid,0x80700000,The max age parameter is invalid.
+BadSecurityModeInsufficient,0x80E60000,The operation is not permitted over the current secure channel.
+BadHistoryOperationInvalid,0x80710000,The history details parameter is not valid.
+BadHistoryOperationUnsupported,0x80720000,The server does not support the requested operation.
+BadInvalidTimestampArgument,0x80BD0000,The defined timestamp to return was invalid.
+BadWriteNotSupported,0x80730000,The server not does support writing the combination of value, status and timestamps provided.
+BadTypeMismatch,0x80740000,The value supplied for the attribute is not of the same type as the attribute's value.
+BadMethodInvalid,0x80750000,The method id does not refer to a method for the specified object.
+BadArgumentsMissing,0x80760000,The client did not specify all of the input arguments for the method.
+BadTooManySubscriptions,0x80770000,The server has reached its  maximum number of subscriptions.
+BadTooManyPublishRequests,0x80780000,The server has reached the maximum number of queued publish requests.
+BadNoSubscription,0x80790000,There is no subscription available for this session.
+BadSequenceNumberUnknown,0x807A0000,The sequence number is unknown to the server.
+BadMessageNotAvailable,0x807B0000,The requested notification message is no longer available.
+BadInsufficientClientProfile,0x807C0000,The Client of the current Session does not support one or more Profiles that are necessary for the Subscription.
+BadStateNotActive,0x80BF0000,The sub-state machine is not currently active.
+BadTcpServerTooBusy,0x807D0000,The server cannot process the request because it is too busy.
+BadTcpMessageTypeInvalid,0x807E0000,The type of the message specified in the header invalid.
+BadTcpSecureChannelUnknown,0x807F0000,The SecureChannelId and/or TokenId are not currently in use.
+BadTcpMessageTooLarge,0x80800000,The size of the message specified in the header is too large.
+BadTcpNotEnoughResources,0x80810000,There are not enough resources to process the request.
+BadTcpInternalError,0x80820000,An internal error occurred.
+BadTcpEndpointUrlInvalid,0x80830000,The Server does not recognize the QueryString specified.
+BadRequestInterrupted,0x80840000,The request could not be sent because of a network interruption.
+BadRequestTimeout,0x80850000,Timeout occurred while processing the request.
+BadSecureChannelClosed,0x80860000,The secure channel has been closed.
+BadSecureChannelTokenUnknown,0x80870000,The token has expired or is not recognized.
+BadSequenceNumberInvalid,0x80880000,The sequence number is not valid.
+BadProtocolVersionUnsupported,0x80BE0000,The applications do not have compatible protocol versions.
+BadConfigurationError,0x80890000,There is a problem with the configuration that affects the usefulness of the value.
+BadNotConnected,0x808A0000,The variable should receive its value from another variable, but has never been configured to do so.
+BadDeviceFailure,0x808B0000,There has been a failure in the device/data source that generates the value that has affected the value.
+BadSensorFailure,0x808C0000,There has been a failure in the sensor from which the value is derived by the device/data source.
+BadOutOfService,0x808D0000,The source of the data is not operational.
+BadDeadbandFilterInvalid,0x808E0000,The deadband filter is not valid.
+UncertainNoCommunicationLastUsableValue,0x408F0000,Communication to the data source has failed. The variable value is the last value that had a good quality.
+UncertainLastUsableValue,0x40900000,Whatever was updating this value has stopped doing so.
+UncertainSubstituteValue,0x40910000,The value is an operational value that was manually overwritten.
+UncertainInitialValue,0x40920000,The value is an initial value for a variable that normally receives its value from another variable.
+UncertainSensorNotAccurate,0x40930000,The value is at one of the sensor limits.
+UncertainEngineeringUnitsExceeded,0x40940000,The value is outside of the range of values defined for this parameter.
+UncertainSubNormal,0x40950000,The value is derived from multiple sources and has less than the required number of Good sources.
+GoodLocalOverride,0x00960000,The value has been overridden.
+BadRefreshInProgress,0x80970000,This Condition refresh failed, a Condition refresh operation is already in progress.
+BadConditionAlreadyDisabled,0x80980000,This condition has already been disabled.
+BadConditionAlreadyEnabled,0x80CC0000,This condition has already been enabled.
+BadConditionDisabled,0x80990000,Property not available, this condition is disabled.
+BadEventIdUnknown,0x809A0000,The specified event id is not recognized.
+BadEventNotAcknowledgeable,0x80BB0000,The event cannot be acknowledged.
+BadDialogNotActive,0x80CD0000,The dialog condition is not active.
+BadDialogResponseInvalid,0x80CE0000,The response is not valid for the dialog.
+BadConditionBranchAlreadyAcked,0x80CF0000,The condition branch has already been acknowledged.
+BadConditionBranchAlreadyConfirmed,0x80D00000,The condition branch has already been confirmed.
+BadConditionAlreadyShelved,0x80D10000,The condition has already been shelved.
+BadConditionNotShelved,0x80D20000,The condition is not currently shelved.
+BadShelvingTimeOutOfRange,0x80D30000,The shelving time not within an acceptable range.
+BadNoData,0x809B0000,No data exists for the requested time range or event filter.
+BadBoundNotFound,0x80D70000,No data found to provide upper or lower bound value.
+BadBoundNotSupported,0x80D80000,The server cannot retrieve a bound for the variable.
+BadDataLost,0x809D0000,Data is missing due to collection started/stopped/lost.
+BadDataUnavailable,0x809E0000,Expected data is unavailable for the requested time range due to an un-mounted volume, an off-line archive or tape, or similar reason for temporary unavailability.
+BadEntryExists,0x809F0000,The data or event was not successfully inserted because a matching entry exists.
+BadNoEntryExists,0x80A00000,The data or event was not successfully updated because no matching entry exists.
+BadTimestampNotSupported,0x80A10000,The client requested history using a timestamp format the server does not support (i.e requested ServerTimestamp when server only supports SourceTimestamp).
+GoodEntryInserted,0x00A20000,The data or event was successfully inserted into the historical database.
+GoodEntryReplaced,0x00A30000,The data or event field was successfully replaced in the historical database.
+UncertainDataSubNormal,0x40A40000,The value is derived from multiple values and has less than the required number of Good values.
+GoodNoData,0x00A50000,No data exists for the requested time range or event filter.
+GoodMoreData,0x00A60000,The data or event field was successfully replaced in the historical database.
+BadAggregateListMismatch,0x80D40000,The requested number of Aggregates does not match the requested number of NodeIds.
+BadAggregateNotSupported,0x80D50000,The requested Aggregate is not support by the server.
+BadAggregateInvalidInputs,0x80D60000,The aggregate value could not be derived due to invalid data inputs.
+BadAggregateConfigurationRejected,0x80DA0000,The aggregate configuration is not valid for specified node.
+GoodDataIgnored,0x00D90000,The request pecifies fields which are not valid for the EventType or cannot be saved by the historian.
+BadRequestNotAllowed,0x80E40000,The request was rejected by the server because it did not meet the criteria set by the server.
+GoodEdited,0x00DC0000,The value does not come from the real source and has been edited by the server.
+GoodPostActionFailed,0x00DD0000,There was an error in execution of these post-actions.
+UncertainDominantValueChanged,0x40DE0000,The related EngineeringUnit has been changed but the Variable Value is still provided based on the previous unit.
+GoodDependentValueChanged,0x00E00000,A dependent value has been changed but the change has not been applied to the device.
+BadDominantValueChanged,0x80E10000,The related EngineeringUnit has been changed but this change has not been applied to the device. The Variable Value is still dependent on the previous unit but its status is currently Bad.
+UncertainDependentValueChanged,0x40E20000,A dependent value has been changed but the change has not been applied to the device. The quality of the dominant variable is uncertain.
+BadDependentValueChanged,0x80E30000,A dependent value has been changed but the change has not been applied to the device. The quality of the dominant variable is Bad.
+GoodCommunicationEvent,0x00A70000,The communication layer has raised an event.
+GoodShutdownEvent,0x00A80000,The system is shutting down.
+GoodCallAgain,0x00A90000,The operation is not finished and needs to be called again.
+GoodNonCriticalTimeout,0x00AA0000,A non-critical timeout occurred.
+BadInvalidArgument,0x80AB0000,One or more arguments are invalid.
+BadConnectionRejected,0x80AC0000,Could not establish a network connection to remote server.
+BadDisconnect,0x80AD0000,The server has disconnected from the client.
+BadConnectionClosed,0x80AE0000,The network connection has been closed.
+BadInvalidState,0x80AF0000,The operation cannot be completed because the object is closed, uninitialized or in some other invalid state.
+BadEndOfStream,0x80B00000,Cannot move beyond end of the stream.
+BadNoDataAvailable,0x80B10000,No data is currently available for reading from a non-blocking stream.
+BadWaitingForResponse,0x80B20000,The asynchronous operation is waiting for a response.
+BadOperationAbandoned,0x80B30000,The asynchronous operation was abandoned by the caller.
+BadExpectedStreamToBlock,0x80B40000,The stream did not return all data requested (possibly because it is a non-blocking stream).
+BadWouldBlock,0x80B50000,Non blocking behaviour is required and the operation would block.
+BadSyntaxError,0x80B60000,A value had an invalid syntax.
+BadMaxConnectionsReached,0x80B70000,The operation could not be finished because all available connections are in use.

+ 2 - 2
tools/travis/travis_linux_before_install.sh

@@ -50,8 +50,8 @@ cd ..
 # Install specific check version which is not yet in the apt package
 echo "=== Installing check ==="
 mkdir tmp_check
-wget http://ftp.de.debian.org/debian/pool/main/c/check/check_0.10.0-3_amd64.deb
-dpkg -x check_0.10.0-3_amd64.deb ./tmp_check
+wget http://ftp.de.debian.org/debian/pool/main/c/check/check_0.10.0-3+b1_amd64.deb
+dpkg -x check_0.10.0-3+b1_amd64.deb ./tmp_check
 # change pkg-config file path
 sed -i "s|prefix=/usr|prefix=${LOCAL_PKG}|g" ./tmp_check/usr/lib/x86_64-linux-gnu/pkgconfig/check.pc
 sed -i 's|libdir=.*|libdir=${prefix}/lib|g' ./tmp_check/usr/lib/x86_64-linux-gnu/pkgconfig/check.pc

+ 8 - 1
tools/travis/travis_linux_script.sh

@@ -45,7 +45,14 @@ else
     cp -r doc ../../
     cp ./examples/server_cert.der ../../
     cd .. && rm build -rf
-
+    
+    echo "Full Namespace 0 Generation"
+    mkdir -p build
+    cd build
+    cmake -DCMAKE_BUILD_TYPE=Debug -DUA_ENABLE_GENERATE_NAMESPACE0=On -DUA_BUILD_EXAMPLES=ON  ..
+    make -j8
+    cd .. && rm build -rf
+    
     # cross compilation only with gcc
     if [ "$CC" = "gcc" ]; then
         echo "Cross compile release build for MinGW 32 bit"