Browse Source

Merge remote-tracking branch 'upstream/0.3' into feature/unit-tests/securechannel

Stefan Profanter 6 years ago
parent
commit
880b5fb33c
100 changed files with 7379 additions and 2099 deletions
  1. 7 0
      .clang-format
  2. 27 0
      .editorconfig
  3. 1 0
      .gitignore
  4. 8 0
      .travis.yml
  5. 1 0
      AUTHORS
  6. 13 0
      CHANGELOG
  7. 125 37
      CMakeLists.txt
  8. 16 0
      CPPLINT.cfg
  9. 3 3
      FEATURES.md
  10. 29 11
      README.md
  11. 2 1
      appveyor.yml
  12. 13 4
      doc/CMakeLists.txt
  13. 3 3
      doc/building.rst
  14. 1 1
      doc/conf.py
  15. 10 14
      doc/index.rst
  16. 0 360
      doc/namespace_compiler.rst
  17. 456 0
      doc/nodeset_compiler.rst
  18. BIN
      doc/nodeset_compiler_pump.png
  19. 1 1
      doc/protocol.rst
  20. 1 1
      doc/toc.rst
  21. 2 0
      doc/tutorials.rst
  22. 10 2
      examples/CMakeLists.txt
  23. 12 12
      examples/access_control/server_access_control.c
  24. 28 19
      examples/client.c
  25. 9 8
      examples/client_connect_loop.c
  26. 142 0
      examples/client_subscription_loop.c
  27. 2 2
      examples/custom_datatype/client_types_custom.c
  28. 2 9
      examples/custom_datatype/server_types_custom.c
  29. 12 12
      examples/discovery/client_find_servers.c
  30. 7 7
      examples/discovery/server_multicast.c
  31. 2 2
      examples/discovery/server_register.c
  32. 90 0
      examples/encryption/server_basic128rsa15.c
  33. 4 2
      examples/server_certificate.c
  34. 144 78
      examples/server.c
  35. 155 28
      examples/server_inheritance.c
  36. 12 12
      examples/server_instantiation.c
  37. 1 1
      examples/server_udp.c
  38. 74 32
      examples/tutorial_client_events.c
  39. 7 8
      examples/tutorial_client_firststeps.c
  40. 1 1
      examples/tutorial_datatypes.c
  41. 4 4
      examples/tutorial_server_datasource.c
  42. 1 1
      examples/tutorial_server_firststeps.c
  43. 11 11
      examples/tutorial_server_object.c
  44. 1 1
      examples/tutorial_server_variable.c
  45. 1 1
      examples/tutorial_server_variabletype.c
  46. 98 116
      include/ua_client.h
  47. 109 0
      include/ua_client_config.h
  48. 11 73
      include/ua_client_highlevel.h
  49. 294 0
      include/ua_client_subscriptions.h
  50. 49 19
      include/ua_config.h.in
  51. 19 9
      include/ua_constants.h
  52. 38 17
      include/ua_plugin_access_control.h
  53. 5 1
      include/ua_plugin_log.h
  54. 12 6
      include/ua_plugin_network.h
  55. 6 1
      include/ua_plugin_nodestore.h
  56. 49 0
      include/ua_plugin_pki.h
  57. 130 54
      include/ua_plugin_securitypolicy.h
  58. 86 24
      include/ua_server.h
  59. 43 5
      include/ua_server_config.h
  60. 73 18
      include/ua_types.h
  61. 2 2
      open62541.spec
  62. 162 51
      plugins/ua_accesscontrol_default.c
  63. 17 41
      plugins/ua_accesscontrol_default.h
  64. 47 6
      plugins/ua_clock.c
  65. 412 74
      plugins/ua_config_default.c
  66. 39 4
      plugins/ua_config_default.h
  67. 4 1
      plugins/ua_debug_dump_pkgs.c
  68. 4 1
      plugins/ua_log_socket_error.h
  69. 13 5
      plugins/ua_log_stdout.c
  70. 4 1
      plugins/ua_log_stdout.h
  71. 173 95
      plugins/ua_network_tcp.c
  72. 7 3
      plugins/ua_network_tcp.h
  73. 18 12
      plugins/ua_network_udp.c
  74. 4 1
      plugins/ua_network_udp.h
  75. 7 6
      plugins/ua_nodestore_default.c
  76. 5 1
      plugins/ua_nodestore_default.h
  77. 148 0
      plugins/ua_pki_certificate.c
  78. 37 0
      plugins/ua_pki_certificate.h
  79. 967 0
      plugins/ua_securitypolicy_basic128rsa15.c
  80. 29 0
      plugins/ua_securitypolicy_basic128rsa15.h
  81. 991 0
      plugins/ua_securitypolicy_basic256sha256.c
  82. 30 0
      plugins/ua_securitypolicy_basic256sha256.h
  83. 57 51
      plugins/ua_securitypolicy_none.c
  84. 7 3
      plugins/ua_securitypolicy_none.h
  85. 138 65
      src/client/ua_client.c
  86. 120 45
      src/client/ua_client_connect.c
  87. 6 3
      src/client/ua_client_discovery.c
  88. 9 2
      src/client/ua_client_highlevel.c
  89. 0 455
      src/client/ua_client_highlevel_subscriptions.c
  90. 62 24
      src/client/ua_client_internal.h
  91. 761 0
      src/client/ua_client_subscriptions.c
  92. 378 0
      src/client/ua_client_subscriptions_deprecated.c
  93. 23 19
      src/server/ua_mdns.c
  94. 4 1
      src/server/ua_mdns_internal.h
  95. 38 13
      src/server/ua_nodes.c
  96. 57 44
      src/server/ua_securechannel_manager.c
  97. 8 2
      src/server/ua_securechannel_manager.h
  98. 37 13
      src/server/ua_server.c
  99. 81 23
      src/server/ua_server_binary.c
  100. 0 0
      src/server/ua_server_discovery.c

+ 7 - 0
.clang-format

@@ -0,0 +1,7 @@
+---
+Language:        Cpp
+BasedOnStyle:    llvm
+ColumnLimit:     120
+ForEachMacros:   [ foreach, LIST_FOREACH, LIST_FOREACH_SAFE ]
+DisableFormat:   false
+

+ 27 - 0
.editorconfig

@@ -0,0 +1,27 @@
+# EditorConfig is awesome: http://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+# Unix-style newlines with a newline ending every file
+[*]
+end_of_line = lf
+insert_final_newline = true
+indent_style = space
+indent_size = 4
+
+# Matches multiple files with brace expansion notation
+# Set default charset
+[*.{js,py}]
+charset = utf-8
+
+# Tab indentation (no size specified)
+[Makefile]
+indent_style = tab
+
+# Matches the exact files either package.json or .travis.yml
+[{package.json,.travis.yml,appveyor.yml}]
+indent_style = space
+indent_size = 2
+
+

+ 1 - 0
.gitignore

@@ -80,3 +80,4 @@ Makefile
 /build
 /.idea
 /cmake-build*
+/tools/certs/certs/*

+ 8 - 0
.travis.yml

@@ -39,12 +39,20 @@ matrix:
       env:
         - ANALYZE=false
         - PYTHON=python3
+    - os: linux
+      compiler: tcc
+      env:
+        - ANALYZE=false
+        - PYTHON=python2
     - os: linux
       compiler: clang
       env: ANALYZE=true
     - os: linux
       compiler: clang
       env: FUZZER=true
+    #- os: linux
+    #  compiler: gcc
+    #  env: LINT=true
     - os: osx
       compiler: clang
       # disable homebrew auto update which takes a lot of time

+ 1 - 0
AUTHORS

@@ -9,4 +9,5 @@ Jeromin, Holger
 Palm, Florian <f.palm (at) plt.rwth-aachen.de>
 Pfrommer, Julius <julius.pfrommer (at) kit.edu>
 Profanter, Stefan <profanter (at) fortiss.org>
+Stalder, Thomas <t.stalder (at) bluetimeconcept.ch>
 Urbas, Leon <leon.urbas (at) tu-dresden.de>

+ 13 - 0
CHANGELOG

@@ -1,6 +1,19 @@
 This changelog reports changes to the public API. Internal refactorings and bug
 fixes are not reported here.
 
+2018-02-05 pro <profanter at fortiss.org>
+
+ * Also pass client to monitoredItem/Events callback
+
+   The UA_MonitoredItemHandlingFunction and UA_MonitoredEventHandlingFunction
+   did not include a reference to the corresponding client used for the call of
+   processPublishResponse.
+   This now also allows to get the client context within the monitored item
+   handling function.
+
+   The API changes are detected by the type-matching in the compiler. So there
+   is little risk for bugs due to unaligned implementations.
+
 2017-09-07 jpfr <julius.pfrommer at web.de>
 
  * Make Client Highlevel Interface consistent

+ 125 - 37
CMakeLists.txt

@@ -15,8 +15,6 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
 set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
 set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
 
-set(LIB_INSTALL_DIR /usr/lib CACHE PATH "Installation path of libraries")
-
 ###########
 # Version #
 ###########
@@ -24,8 +22,9 @@ set(LIB_INSTALL_DIR /usr/lib CACHE PATH "Installation path of libraries")
 set(OPEN62541_VER_MAJOR 0)
 set(OPEN62541_VER_MINOR 3)
 set(OPEN62541_VER_PATCH 0)
-set(OPEN62541_VER_LABEL "dev") # Appended to the X.Y.Z version format. For example "-rc1" or an empty string
+set(OPEN62541_VER_LABEL "-rc1") # Appended to the X.Y.Z version format. For example "-rc1" or an empty string
 
+#  If a relative path is specified, it is treated as relative to the $<INSTALL_PREFIX>
 set(LIB_INSTALL_DIR lib CACHE PATH "Installation path of libraries")
 
 # Set OPEN62541_VER_COMMIT
@@ -57,14 +56,12 @@ option(UA_ENABLE_NODEMANAGEMENT "Enable dynamic addition and removal of nodes at
 option(UA_ENABLE_SUBSCRIPTIONS "Enable subscriptions support." ON)
 option(UA_ENABLE_DISCOVERY "Enable Discovery Service (LDS)" ON)
 option(UA_ENABLE_DISCOVERY_MULTICAST "Enable Discovery Service with multicast support (LDS-ME)" OFF)
-# Semaphores/file system may not be available on embedded devices. It can be disabled with the following option
-option(UA_ENABLE_DISCOVERY_SEMAPHORE "Enable Discovery Semaphore support" ON)
-mark_as_advanced(UA_ENABLE_DISCOVERY_SEMAPHORE)
 option(UA_ENABLE_AMALGAMATION "Concatenate the library to a single file open62541.h/.c" OFF)
 option(UA_ENABLE_COVERAGE "Enable gcov coverage" OFF)
 option(BUILD_SHARED_LIBS "Enable building of shared libraries (dll/so)" OFF)
 
-set(UA_VXWORKS_WRS_KERNEL OFF CACHE BOOL "Enable if you want to compile for VxWorks as kernel Module")
+# Encryption Options
+option(UA_ENABLE_ENCRYPTION "Enable encryption support (uses mbedTLS)" OFF)
 
 if(UA_ENABLE_COVERAGE)
   set(CMAKE_BUILD_TYPE DEBUG)
@@ -73,6 +70,13 @@ if(UA_ENABLE_COVERAGE)
   set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fprofile-arcs -ftest-coverage")
 endif()
 
+if(UA_BUILD_FUZZING OR UA_BUILD_OSS_FUZZ OR UA_BUILD_FUZZING_CORPUS)
+    # Force enable options not passed in the build script, to also fuzzy-test this code
+    set(UA_ENABLE_DISCOVERY ON CACHE STRING "" FORCE)
+    set(UA_ENABLE_DISCOVERY_MULTICAST ON CACHE STRING "" FORCE)
+    set(UA_ENABLE_ENCRYPTION ON CACHE STRING "" FORCE)
+endif()
+
 if(UA_ENABLE_DISCOVERY_MULTICAST AND NOT UA_ENABLE_DISCOVERY)
     MESSAGE(WARNING "UA_ENABLE_DISCOVERY_MULTICAST is enabled, but not UA_ENABLE_DISCOVERY. UA_ENABLE_DISCOVERY_MULTICAST will be set to OFF")
     SET(UA_ENABLE_DISCOVERY_MULTICAST OFF CACHE BOOL "Enable Discovery Service with multicast support (LDS-ME)" FORCE)
@@ -91,6 +95,12 @@ mark_as_advanced(UA_ENABLE_TYPENAMES)
 option(UA_ENABLE_DETERMINISTIC_RNG "Do not seed the random number generator (e.g. for unit tests)." OFF)
 mark_as_advanced(UA_ENABLE_DETERMINISTIC_RNG)
 
+option(UA_ENABLE_VALGRIND_INTERACTIVE "Enable dumping valgrind every iteration. CAUTION! SLOWDOWN!" OFF)
+mark_as_advanced(UA_ENABLE_VALGRIND_INTERACTIVE)
+
+option(UA_MSVC_FORCE_STATIC_CRT "Force linking with the static C-runtime library when compiling to static library with MSVC" ON)
+mark_as_advanced(UA_MSVC_FORCE_STATIC_CRT)
+
 option(UA_ENABLE_FULL_NS0 "Use the full NS0 instead of a minimal Namespace 0 nodeset" OFF)
 if (MSVC AND UA_ENABLE_FULL_NS0)
     # For the full NS0 we need a stack size of 8MB (as it is default on linux)
@@ -98,6 +108,14 @@ if (MSVC AND UA_ENABLE_FULL_NS0)
     set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:8000000")
 endif()
 
+option(UA_FILE_NS0 "Custom NodeSet file containing NS0")
+mark_as_advanced(UA_FILE_NS0)
+
+# Semaphores/file system may not be available on embedded devices. It can be
+# disabled with the following option
+option(UA_ENABLE_DISCOVERY_SEMAPHORE "Enable Discovery Semaphore support" ON)
+mark_as_advanced(UA_ENABLE_DISCOVERY_SEMAPHORE)
+
 option(UA_ENABLE_UNIT_TESTS_MEMCHECK "Use Valgrind (Linux) or DrMemory (Windows) to detect memory leaks when running the unit tests" OFF)
 mark_as_advanced(UA_ENABLE_UNIT_TESTS_MEMCHECK)
 
@@ -108,6 +126,15 @@ option(UA_ENABLE_UNIT_TEST_FAILURE_HOOKS
        "Add hooks to force failure modes for additional unit tests. Not for production use!" OFF)
 mark_as_advanced(UA_ENABLE_UNIT_TEST_FAILURE_HOOKS)
 
+set(UA_VXWORKS_WRS_KERNEL OFF CACHE BOOL "Enable if you want to compile for VxWorks as kernel Module")
+mark_as_advanced(UA_VXWORKS_WRS_KERNEL)
+
+set(UA_FREERTOS OFF CACHE BOOL "Enable if you want to compile for freeRTOS")
+mark_as_advanced(UA_FREERTOS)
+
+set(UA_VALGRIND_INTERACTIVE_INTERVAL 1000 CACHE STRING "The number of iterations to wait before creating the next dump")
+mark_as_advanced(UA_VALGRIND_INTERACTIVE_INTERVAL)
+
 # Build options for debugging
 option(UA_DEBUG "Enable assertions and additional functionality that should not be included in release builds" OFF)
 mark_as_advanced(UA_DEBUG)
@@ -151,6 +178,12 @@ if(BUILD_SHARED_LIBS)
   endif()
 endif()
 
+######################
+# External Libraries #
+######################
+
+list(APPEND open62541_LIBRARIES "")
+
 # Force compilation with as C++
 option(UA_COMPILE_AS_CXX "Force compilation with a C++ compiler" OFF)
 mark_as_advanced(UA_COMPILE_AS_CXX)
@@ -159,19 +192,24 @@ if (UA_COMPILE_AS_CXX)
     add_definitions(-D__STDC_CONSTANT_MACROS)
 endif()
 
-#####################
-# Compiler Settings #
-#####################
+if(UA_ENABLE_ENCRYPTION)
+    # The recommended way is to install mbedtls via the OS package manager. If
+    # that is not possible, manually compile mbedTLS and set the cmake variables
+    # defined in /tools/cmake/FindMbedTLS.cmake.
+    find_package(MbedTLS REQUIRED)
+    list(APPEND open62541_LIBRARIES ${MBEDTLS_LIBRARIES})
+endif()
 
-# Collect libraries
-list(APPEND open62541_LIBRARIES "")
 if(NOT WIN32)
   if(QNXNTO)
     list(APPEND open62541_LIBRARIES socket)
     list(APPEND open62541_LIBRARIES c)
     list(APPEND open62541_LIBRARIES stdc++)
   else()
-    list(APPEND open62541_LIBRARIES pthread m)
+    list(APPEND open62541_LIBRARIES m)
+    if(UA_ENABLE_MULTITHREADING OR UA_BUILD_UNIT_TESTS)
+      list(APPEND open62541_LIBRARIES pthread)
+    endif()
     if(NOT APPLE AND (NOT ${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD"))
       list(APPEND open62541_LIBRARIES rt)
     endif()
@@ -182,9 +220,10 @@ else()
         list(APPEND open62541_LIBRARIES iphlpapi)
     endif()
 endif()
-if(UA_ENABLE_MULTITHREADING)
-  list(APPEND open62541_LIBRARIES urcu-cds urcu-bp urcu-common)
-endif(UA_ENABLE_MULTITHREADING)
+
+#####################
+# Compiler Settings #
+#####################
 
 if(NOT UA_COMPILE_AS_CXX AND (CMAKE_COMPILER_IS_GNUCC OR "x${CMAKE_C_COMPILER_ID}" STREQUAL "xClang"))
     # Compiler
@@ -217,6 +256,16 @@ if(NOT UA_COMPILE_AS_CXX AND (CMAKE_COMPILER_IS_GNUCC OR "x${CMAKE_C_COMPILER_ID
         add_definitions(-D_WRS_KERNEL)
     endif()
 
+    if(UA_FREERTOS)
+       SET(UA_FREERTOS_INCLUDES "" CACHE STRING "Folders to include from the freeRTOS OS")
+       include_directories(${UA_FREERTOS_INCLUDES})
+        # Disable flags for freeRTOS
+        remove_definitions(-fPIC -Wconversion )
+        add_definitions(-DUA_FREERTOS -DLWIP_TIMEVAL_PRIVATE=0 -DLWIP_COMPAT_MUTEX=0 -DLWIP_POSIX_SOCKETS_IO_NAMES=0 -mcpu=cortex-m3 -mthumb -g -Wall -O0 -specs=nano.specs
+                        -ffunction-sections -fdata-sections  -fno-exceptions -fstack-usage -Wno-unused-variable -Wno-format -Wno-format-security -Wno-format-nonliteral)
+        list(APPEND open62541_LIBRARIES c m stdc++ supc++)
+    endif(UA_FREERTOS)
+
     # Linker
     set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") # cmake sets -rdynamic by default
 
@@ -254,9 +303,9 @@ if(APPLE)
 endif()
 
 if(MSVC)
-  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W3 /WX") # Compiler warnings, error on warning
+  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W3 /WX /w44996") # Compiler warnings, error on warning
 
-  if(NOT BUILD_SHARED_LIB)
+  if(UA_MSVC_FORCE_STATIC_CRT AND NOT BUILD_SHARED_LIBS)
     set(CompilerFlags CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_C_FLAGS
         CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE)
     foreach(CompilerFlag ${CompilerFlags})
@@ -282,7 +331,8 @@ include_directories(${PROJECT_SOURCE_DIR}/include
                     ${PROJECT_SOURCE_DIR}/plugins # TODO: discovery depends on the default config
                     ${PROJECT_SOURCE_DIR}/deps
                     ${PROJECT_BINARY_DIR}
-                    ${PROJECT_BINARY_DIR}/src_generated)
+                    ${PROJECT_BINARY_DIR}/src_generated
+                    ${MBEDTLS_INCLUDE_DIRS})
 
 set(exported_headers ${PROJECT_BINARY_DIR}/src_generated/ua_config.h
                      ${PROJECT_SOURCE_DIR}/deps/ms_stdint.h
@@ -291,15 +341,18 @@ set(exported_headers ${PROJECT_BINARY_DIR}/src_generated/ua_config.h
                      ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.h
                      ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated_handling.h
                      ${PROJECT_SOURCE_DIR}/include/ua_server.h
-                     ${PROJECT_SOURCE_DIR}/include/ua_plugin_network.h
                      ${PROJECT_SOURCE_DIR}/include/ua_plugin_log.h
+                     ${PROJECT_SOURCE_DIR}/include/ua_plugin_network.h
                      ${PROJECT_SOURCE_DIR}/include/ua_plugin_access_control.h
+                     ${PROJECT_SOURCE_DIR}/include/ua_plugin_pki.h
                      ${PROJECT_SOURCE_DIR}/include/ua_plugin_securitypolicy.h
                      ${PROJECT_SOURCE_DIR}/include/ua_plugin_nodestore.h
                      ${PROJECT_SOURCE_DIR}/include/ua_server_config.h
+                     ${PROJECT_SOURCE_DIR}/include/ua_client_config.h
                      ${PROJECT_SOURCE_DIR}/include/ua_client.h
                      ${PROJECT_SOURCE_DIR}/include/ua_client_highlevel.h
-)
+                     ${PROJECT_SOURCE_DIR}/include/ua_client_subscriptions.h)
+
 set(internal_headers ${PROJECT_SOURCE_DIR}/deps/queue.h
                      ${PROJECT_SOURCE_DIR}/deps/pcg_basic.h
                      ${PROJECT_SOURCE_DIR}/deps/libc_time.h
@@ -311,8 +364,8 @@ set(internal_headers ${PROJECT_SOURCE_DIR}/deps/queue.h
                      ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated_encoding_binary.h
                      ${PROJECT_SOURCE_DIR}/src/ua_connection_internal.h
                      ${PROJECT_SOURCE_DIR}/src/ua_securechannel.h
-                     ${PROJECT_SOURCE_DIR}/src/ua_session.h
                      ${PROJECT_SOURCE_DIR}/src/ua_timer.h
+                     ${PROJECT_SOURCE_DIR}/src/server/ua_session.h
                      ${PROJECT_SOURCE_DIR}/src/server/ua_subscription.h
                      ${PROJECT_SOURCE_DIR}/src/server/ua_session_manager.h
                      ${PROJECT_SOURCE_DIR}/src/server/ua_securechannel_manager.h
@@ -329,9 +382,9 @@ set(lib_sources ${PROJECT_SOURCE_DIR}/src/ua_types.c
                 ${PROJECT_BINARY_DIR}/src_generated/ua_statuscode_descriptions.c
                 ${PROJECT_SOURCE_DIR}/src/ua_util.c
                 ${PROJECT_SOURCE_DIR}/src/ua_timer.c
-                ${PROJECT_SOURCE_DIR}/src/ua_session.c
                 ${PROJECT_SOURCE_DIR}/src/ua_connection.c
                 ${PROJECT_SOURCE_DIR}/src/ua_securechannel.c
+                ${PROJECT_SOURCE_DIR}/src/server/ua_session.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_nodes.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server_ns0.c
@@ -359,7 +412,8 @@ set(lib_sources ${PROJECT_SOURCE_DIR}/src/ua_types.c
                 ${PROJECT_SOURCE_DIR}/src/client/ua_client_connect.c
                 ${PROJECT_SOURCE_DIR}/src/client/ua_client_discovery.c
                 ${PROJECT_SOURCE_DIR}/src/client/ua_client_highlevel.c
-                ${PROJECT_SOURCE_DIR}/src/client/ua_client_highlevel_subscriptions.c
+                ${PROJECT_SOURCE_DIR}/src/client/ua_client_subscriptions.c
+                ${PROJECT_SOURCE_DIR}/src/client/ua_client_subscriptions_deprecated.c
 
                 # dependencies
                 ${PROJECT_SOURCE_DIR}/deps/libc_time.c
@@ -367,19 +421,34 @@ set(lib_sources ${PROJECT_SOURCE_DIR}/src/ua_types.c
 
 set(default_plugin_headers ${PROJECT_SOURCE_DIR}/plugins/ua_network_tcp.h
                            ${PROJECT_SOURCE_DIR}/plugins/ua_accesscontrol_default.h
+                           ${PROJECT_SOURCE_DIR}/plugins/ua_pki_certificate.h
                            ${PROJECT_SOURCE_DIR}/plugins/ua_log_stdout.h
                            ${PROJECT_SOURCE_DIR}/plugins/ua_nodestore_default.h
                            ${PROJECT_SOURCE_DIR}/plugins/ua_config_default.h
                            ${PROJECT_SOURCE_DIR}/plugins/ua_securitypolicy_none.h
-                           ${PROJECT_SOURCE_DIR}/plugins/ua_log_socket_error.h)
+                           ${PROJECT_SOURCE_DIR}/plugins/ua_log_socket_error.h
+)
 
 set(default_plugin_sources ${PROJECT_SOURCE_DIR}/plugins/ua_network_tcp.c
                            ${PROJECT_SOURCE_DIR}/plugins/ua_clock.c
                            ${PROJECT_SOURCE_DIR}/plugins/ua_log_stdout.c
                            ${PROJECT_SOURCE_DIR}/plugins/ua_accesscontrol_default.c
+                           ${PROJECT_SOURCE_DIR}/plugins/ua_pki_certificate.c
                            ${PROJECT_SOURCE_DIR}/plugins/ua_nodestore_default.c
                            ${PROJECT_SOURCE_DIR}/plugins/ua_config_default.c
-                           ${PROJECT_SOURCE_DIR}/plugins/ua_securitypolicy_none.c)
+                           ${PROJECT_SOURCE_DIR}/plugins/ua_securitypolicy_none.c
+)
+
+if(UA_ENABLE_ENCRYPTION)
+    set(default_plugin_headers ${default_plugin_headers}
+            ${PROJECT_SOURCE_DIR}/plugins/ua_securitypolicy_basic128rsa15.h)
+    set(default_plugin_headers ${default_plugin_headers}
+            ${PROJECT_SOURCE_DIR}/plugins/ua_securitypolicy_basic256sha256.h)
+    set(default_plugin_sources ${default_plugin_sources}
+            ${PROJECT_SOURCE_DIR}/plugins/ua_securitypolicy_basic128rsa15.c)
+    set(default_plugin_sources ${default_plugin_sources}
+            ${PROJECT_SOURCE_DIR}/plugins/ua_securitypolicy_basic256sha256.c)
+endif()
 
 if(UA_DEBUG_DUMP_PKGS)
     list(APPEND lib_sources ${PROJECT_SOURCE_DIR}/plugins/ua_debug_dump_pkgs.c)
@@ -411,19 +480,24 @@ endif()
 #########################
 
 if (UA_ENABLE_FULL_NS0)
-    set(UA_FILE_NS0 ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml)
+    if(NOT UA_FILE_NS0)
+        set(UA_FILE_NS0 ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml)
+    endif()
     set(UA_FILE_DATATYPES "")
     set(UA_FILE_NODEIDS ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/NodeIds.csv)
     set(UA_FILE_TYPES_BSD ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.Types.bsd)
-    if(NOT EXISTS "${UA_FILE_NS0}")
-        MESSAGE(FATAL_ERROR "File ${UA_FILE_NS0} not found. You probably need to initialize the git submodule for deps/ua-nodeset.")
-    endif()
 else()
-    set(UA_FILE_NS0 ${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.NodeSet2.Minimal.xml)
+    if(NOT UA_FILE_NS0)
+        set(UA_FILE_NS0 ${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.NodeSet2.Minimal.xml)
+    endif()
     set(UA_FILE_DATATYPES "${PROJECT_SOURCE_DIR}/tools/schema/datatypes_minimal.txt")
     set(UA_FILE_NODEIDS ${PROJECT_SOURCE_DIR}/tools/schema/NodeIds.csv)
     set(UA_FILE_TYPES_BSD ${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd)
 endif()
+if(NOT EXISTS "${UA_FILE_NS0}")
+    message(FATAL_ERROR "File ${UA_FILE_NS0} not found. You probably need to initialize the git submodule for deps/ua-nodeset.")
+endif()
+
 
 if (UA_FILE_DATATYPES STREQUAL "")
     set(SELECTED_TYPES_TMP "")
@@ -504,7 +578,7 @@ add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/open62541.c
                            ${OPEN62541_VER_COMMIT} ${CMAKE_CURRENT_BINARY_DIR}/open62541.c
                            ${internal_headers} ${lib_sources} ${default_plugin_sources}
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/amalgamate.py ${internal_headers}
-                           ${lib_sources})
+                           ${lib_sources} ${default_plugin_sources})
 
 add_custom_target(open62541-amalgamation-source DEPENDS ${PROJECT_BINARY_DIR}/open62541.c)
 add_custom_target(open62541-amalgamation-header DEPENDS ${PROJECT_BINARY_DIR}/open62541.h)
@@ -644,9 +718,6 @@ if(UA_BUILD_UNIT_TESTS)
 endif()
 
 if(UA_BUILD_FUZZING OR UA_BUILD_OSS_FUZZ OR UA_BUILD_FUZZING_CORPUS)
-    # Force enable discovery, to also fuzzy-test this code
-    set(UA_ENABLE_DISCOVERY ON CACHE STRING "" FORCE)
-    set(UA_ENABLE_DISCOVERY_MULTICAST ON CACHE STRING "" FORCE)
     add_subdirectory(tests/fuzz)
 endif()
 
@@ -673,6 +744,18 @@ add_custom_target(lint ${CLANG_TIDY_PROGRAM}
                   COMMENT "Run clang-tidy on the library")
 add_dependencies(lint open62541)
 
+add_custom_target(cpplint cpplint
+                  ${lib_sources}
+                  ${internal_headers}
+                  ${default_plugin_headers}
+                  ${default_plugin_sources}
+                  DEPENDS ${lib_sources}
+                          ${internal_headers}
+                          ${default_plugin_headers}
+                          ${default_plugin_sources}
+
+                  COMMENT "Run cpplint code style checker on the library")
+
 ##########################
 # Installation           #
 ##########################
@@ -680,7 +763,7 @@ add_dependencies(lint open62541)
 # specify install location with `-DCMAKE_INSTALL_PREFIX=xyz`
 # Enable shared library with `-DBUILD_SHARED_LIBS=ON`
 
-set(cmake_configfile_install ${LIB_INSTALL_DIR}/cmake3)
+set(cmake_configfile_install ${LIB_INSTALL_DIR}/cmake)
 set(target_install_dest_name "${cmake_configfile_install}/open62541Targets.cmake")
 set(open62541_tools_dir share/open62541/tools)
 set(open62541_deps_dir include/open62541/deps)
@@ -691,7 +774,7 @@ install(TARGETS open62541
         LIBRARY DESTINATION ${LIB_INSTALL_DIR}
         ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
         RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}
-        INCLUDES DESTINATION ${INCLUDE_INSTALL_DIR}/open62541 ${open62541_deps_dir})
+        INCLUDES DESTINATION include/open62541 ${open62541_deps_dir})
 
 include(CMakePackageConfigHelpers)
 configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake/open62541-config.cmake.in"
@@ -757,3 +840,8 @@ set_target_properties(open62541-object PROPERTIES FOLDER "open62541/lib")
 set_target_properties(lint PROPERTIES FOLDER "CodeAnalysis")
 set_target_properties(open62541-amalgamation-header PROPERTIES FOLDER "open62541/lib")
 set_target_properties(open62541-amalgamation-source PROPERTIES FOLDER "open62541/lib")
+
+set_target_properties(open62541-generator-namespace PROPERTIES FOLDER "open62541/generators")
+set_target_properties(open62541-generator-statuscode PROPERTIES FOLDER "open62541/generators")
+set_target_properties(open62541-generator-transport PROPERTIES FOLDER "open62541/generators")
+set_target_properties(open62541-generator-types PROPERTIES FOLDER "open62541/generators")

+ 16 - 0
CPPLINT.cfg

@@ -0,0 +1,16 @@
+set noparent
+filter=-whitespace/line_length
+filter=-readability/casting
+filter=-legal/copyright
+filter=-build/include_subdir
+filter=-readability/todo
+filter=-runtime/casting
+filter=-runtime/int
+filter=-build/include_what_you_use
+filter=-readability/multiline_comment
+filter=-runtime/printf
+filter=-build/include
+filter=-build/header_guard
+filter=-readability/alt_tokens
+filter=-runtime/indentation_namespace
+exclude_files=deps/*

+ 3 - 3
FEATURES.md

@@ -63,9 +63,9 @@ open62541 Supported Features
 | SOAP-HTTP WS-SC UA XML-UA Binary        |      :new_moon:      |                      |
 | **Encryption**                          |                      |                      |
 | None                                    |  :white_check_mark:  |                      |
-| Basic128Rsa15                           |      :waning_gibbous_moon:      | [WIP](https://github.com/open62541/open62541/pull/1282), Release 0.3     |
-| Basic256                                |      :waning_gibbous_moon:      | [WIP](https://github.com/open62541/open62541/pull/1282), Release 0.3     |
-| Basic256Sha256                          |      :waning_gibbous_moon:      | [WIP](https://github.com/open62541/open62541/pull/1282), Release 0.3     |
+| Basic128Rsa15                           |  :white_check_mark:  | master, Release 0.3  |
+| Basic256                                | :waning_gibbous_moon: | [WIP](https://github.com/open62541/open62541/pull/1282), Release 0.3     |
+| Basic256Sha256                          |  :waning_gibbous_moon: | [WIP](https://github.com/open62541/open62541/pull/1282), Release 0.3     |
 | **Authentication**                      |                      |                      |
 | Anonymous                               |  :white_check_mark:  |                      |
 | User Name Password                      |  :white_check_mark:  |                      |

+ 29 - 11
README.md

@@ -33,32 +33,50 @@ open62541 implements the OPC UA binary protocol stack as well as a client and se
 - Code-Generation
   - Support for generating data types from standard XML definitions
   - Support for generating server-side information models (nodesets) from standard XML definitions
-- Code quality checks
-  - Every commit checked with continuous integration and unit tests (code coverage > 80%)
-  - Memory leak detection using Valgrind (Linux) and DrMemory (Windows)
-  - Static code analysis with cpp-check and clang static code analysis
-  - Fuzz testing with [oss-fuzz](https://github.com/google/oss-fuzz)
   
 Features currently being implemented:
 - Target 0.3 release (to be released in the coming weeks):
-  - Encryption (#1282)
+  - Secure communication with encrypted messages (Done)
   - Access control for individual nodes (Done)
+  - Asynchronous service requests in the client (Done)
 - Target 0.4 release:
   - Events (notifications emitted by objects, data change notifications are implemented), WIP by @Pro
-  - Event-loop (background tasks) and asynchronous service requests in the client, WIP #1117 #1410
-  - Publish/Subscribe based on UDP (Specification Part 14), WIP by @jpfr
+  - Event-loop (background tasks) in the client
+  - Publish/Subscribe based on UDP (Specification Part 14), WIP @ Fraunhofer IOSB
 
-### Using open62541
+### Dependencies
+
+On most systems, open62541 requires the C standard library only. For dependencies during the build process, see the following list and the [build documentation](https://open62541.org/doc/current/building.html) for details.
+
+- Core Library: The core library has no dependencies besides the C99 standard headers.
+- Default Plugins: The default plugins use the POSIX interfaces for networking and accessing the system clock. Ports to different (embedded) architectures are achieved by customizing the plugins.
+- Building and Code Generation: The build environment is generated via CMake. Some code is auto-generated from XML definitions that are part of the OPC UA standard. The code generation scripts run with both Python 2 and 3.
+
+### Code Quality
+
+We emphasize code quality. The following quality metrics are continuously checked and are ensured to hold before an official release is made:
+
+- Zero errors indicated by the Compliance Testing Tool (CTT) of the OPC Foundation for the supported features
+- Zero compiler warnings from GCC/Clang/MSVC with very strict compilation flags
+- Zero issues indicated by unit tests (more than 80% coverage)
+- Zero issues indicated by clang-analyzer, clang-tidy, cpp-check and the Coverity static code analysis tools
+- Zero unresolved issues from fuzzing the library in Google's oss-fuzz infrastructure
+- Zero issues indicated by Valgrind (Linux), DrMemory (Windows) and Clang AddressSanitizer / MemorySanitizer for the CTT tests, unit tests and fuzzing
+
+### Documentation and Support
 
 A general introduction to OPC UA and the open62541 documentation can be found at http://open62541.org/doc/current.
 Past releases of the library can be downloaded at https://github.com/open62541/open62541/releases.
 To use the latest improvements, download a nightly build of the *single-file distribution* (the entire library merged into a single source and header file) from http://open62541.org/releases. Nightly builds of MSVC binaries of the library are available [here](https://ci.appveyor.com/project/open62541/open62541/build/artifacts).
 
-For discussion and help, you can use
+For individual discussion and support, use the following channels
+
 - the [mailing list](https://groups.google.com/d/forum/open62541)
 - our [IRC channel](http://webchat.freenode.net/?channels=%23open62541)
 - the [bugtracker](https://github.com/open62541/open62541/issues)
 
+or contact a member of the core development group (see below).
+
 ### Development
 
 Besides the general open62541 community, a group of core maintainers jointly steers the long-term development. The current core maintainers are (as of Mai 2017, in alphabetical order):
@@ -68,7 +86,7 @@ Besides the general open62541 community, a group of core maintainers jointly ste
 - Julius Pfrommer (Fraunhofer IOSB, Karlsruhe)
 - Stefan Profanter (fortiss, Munich)
 
-As an open source project, we encourage new contributors to help improve open62541. There are ways to begin contributing without deep knowledge of the OPC UA standard:
+As an open source project, we encourage new contributors to help improve open62541. The following are good starting points for new contributors:
 - [Report bugs](https://github.com/open62541/open62541/issues)
 - Improve the [documentation](http://open62541.org/doc/current)
 - Work on issues marked as "[good first issue](https://github.com/open62541/open62541/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)"

+ 2 - 1
appveyor.yml

@@ -40,6 +40,7 @@ environment:
 cache:
   - C:\ProgramData\chocolatey\bin -> tools/appveyor/install.ps1
   - C:\ProgramData\chocolatey\lib -> tools/appveyor/install.ps1
+  - C:\tools\vcpkg\installed -> tools/appveyor/install.ps1
   #- 'c:\miktex'
 
 init:
@@ -50,7 +51,7 @@ install:
 
 before_build:
   # Add installed tools to PATH
-  - set PATH=C:\mingw-w64\i686-5.3.0-posix-dwarf-rt_v4-rev0\mingw32\bin;C:\Program Files (x86)\Dr. Memory\bin;C:\Program Files\CMake\bin;%PATH%
+  - set PATH=C:\Python27;C:\msys64\mingw64\bin;C:\Program Files (x86)\Dr. Memory\bin;C:\Program Files\CMake\bin;%PATH%
   # Workaround for CMake not wanting sh.exe on PATH for MinGW
   - set PATH=%PATH:C:\Program Files\Git\usr\bin;=%
   # Miktex

+ 13 - 4
doc/CMakeLists.txt

@@ -20,7 +20,10 @@ endfunction()
 generate_rst(${PROJECT_SOURCE_DIR}/include/ua_types.h ${DOC_SRC_DIR}/types.rst)
 generate_rst(${PROJECT_SOURCE_DIR}/include/ua_constants.h ${DOC_SRC_DIR}/constants.rst)
 generate_rst(${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.h ${DOC_SRC_DIR}/types_generated.rst)
+generate_rst(${PROJECT_SOURCE_DIR}/include/ua_server_config.h ${DOC_SRC_DIR}/server_config.rst)
 generate_rst(${PROJECT_SOURCE_DIR}/include/ua_server.h ${DOC_SRC_DIR}/server.rst)
+generate_rst(${PROJECT_SOURCE_DIR}/include/ua_client_config.h ${DOC_SRC_DIR}/client_config.rst)
+generate_rst(${PROJECT_SOURCE_DIR}/include/ua_client_subscriptions.h ${DOC_SRC_DIR}/client_subscriptions.rst)
 generate_rst(${PROJECT_SOURCE_DIR}/include/ua_client.h ${DOC_SRC_DIR}/client.rst)
 generate_rst(${PROJECT_SOURCE_DIR}/include/ua_client_highlevel.h ${DOC_SRC_DIR}/client_highlevel.rst)
 generate_rst(${PROJECT_SOURCE_DIR}/include/ua_plugin_log.h ${DOC_SRC_DIR}/plugin_log.rst)
@@ -42,10 +45,13 @@ generate_rst(${PROJECT_SOURCE_DIR}/examples/tutorial_client_firststeps.c ${DOC_S
 add_custom_target(doc_latex ${SPHINX_EXECUTABLE}
   -b latex "${DOC_SRC_DIR}" "${DOC_LATEX_DIR}"
   DEPENDS ${DOC_SRC_DIR}/types.rst ${DOC_SRC_DIR}/constants.rst ${DOC_SRC_DIR}/types_generated.rst
-          ${DOC_SRC_DIR}/server.rst ${DOC_SRC_DIR}/client.rst ${DOC_SRC_DIR}/client_highlevel.rst
+          ${DOC_SRC_DIR}/server.rst ${DOC_SRC_DIR}/server_config.rst
+          ${DOC_SRC_DIR}/client.rst ${DOC_SRC_DIR}/client_subscriptions.rst
+          ${DOC_SRC_DIR}/client_highlevel.rst ${DOC_SRC_DIR}/client_config.rst
           ${DOC_SRC_DIR}/plugin_log.rst ${DOC_SRC_DIR}/plugin_network.rst
           ${DOC_SRC_DIR}/services.rst ${DOC_SRC_DIR}/plugin_access_control.rst
           ${DOC_SRC_DIR}/nodestore.rst
+          ${DOC_SRC_DIR}/tutorials.rst
           ${DOC_SRC_DIR}/tutorial_datatypes.rst
           ${DOC_SRC_DIR}/tutorial_client_firststeps.rst
           ${DOC_SRC_DIR}/tutorial_server_firststeps.rst
@@ -57,10 +63,10 @@ add_custom_target(doc_latex ${SPHINX_EXECUTABLE}
   COMMENT "Building LaTeX sources for documentation with Sphinx")
 add_dependencies(doc_latex open62541)
 
-add_custom_target(doc_pdf ${PDFLATEX_COMPILER} --quiet "open62541.tex"
+add_custom_target(doc_pdf ${PDFLATEX_COMPILER} -interaction=batchmode "open62541.tex"
   WORKING_DIRECTORY ${DOC_LATEX_DIR}
   # compile it twice so that the contents pages are correct
-  COMMAND ${PDFLATEX_COMPILER} --quiet "open62541.tex"
+  COMMAND ${PDFLATEX_COMPILER} -interaction=batchmode "open62541.tex"
   DEPENDS ${PDFLATEX_COMPILER}
   COMMENT "Generating PDF documentation from LaTeX sources")
 add_dependencies(doc_pdf doc_latex)
@@ -68,10 +74,13 @@ add_dependencies(doc_pdf doc_latex)
 add_custom_target(doc ${SPHINX_EXECUTABLE}
   -b html "${DOC_SRC_DIR}" "${DOC_HTML_DIR}"
   DEPENDS ${DOC_SRC_DIR}/types.rst ${DOC_SRC_DIR}/constants.rst ${DOC_SRC_DIR}/types_generated.rst
-          ${DOC_SRC_DIR}/server.rst ${DOC_SRC_DIR}/client.rst ${DOC_SRC_DIR}/client_highlevel.rst
+          ${DOC_SRC_DIR}/server.rst ${DOC_SRC_DIR}/server_config.rst
+          ${DOC_SRC_DIR}/client.rst ${DOC_SRC_DIR}/client_subscriptions.rst
+          ${DOC_SRC_DIR}/client_highlevel.rst ${DOC_SRC_DIR}/client_config.rst
           ${DOC_SRC_DIR}/plugin_log.rst ${DOC_SRC_DIR}/plugin_network.rst
           ${DOC_SRC_DIR}/services.rst ${DOC_SRC_DIR}/plugin_access_control.rst
           ${DOC_SRC_DIR}/nodestore.rst
+          ${DOC_SRC_DIR}/tutorials.rst
           ${DOC_SRC_DIR}/tutorial_datatypes.rst
           ${DOC_SRC_DIR}/tutorial_client_firststeps.rst
           ${DOC_SRC_DIR}/tutorial_server_firststeps.rst

+ 3 - 3
doc/building.rst

@@ -25,8 +25,8 @@ Building with CMake on Ubuntu or Debian
    sudo apt-get install git build-essential gcc pkg-config cmake python python-six
 
    # enable additional features
-   sudo apt-get install cmake-curses-gui # for ccmake
-   sudo apt-get install liburcu-dev # for multithreading
+   sudo apt-get install cmake-curses-gui # for the ccmake graphical interface
+   sudo apt-get install libmbedtls-dev # for encryption support
    sudo apt-get install check # for unit tests
    sudo apt-get install python-sphinx graphviz # for documentation generation
    sudo apt-get install python-sphinx-rtd-theme # documentation style
@@ -168,7 +168,7 @@ specified by the following options:
 **UA_BUILD_EXAMPLES_NODESET_COMPILER**
    Generate an OPC UA information model from a nodeset XML (experimental)
 
-**UA_BUILD_SELFIGNED_CERTIFICATE**
+**UA_BUILD_SELFSIGNED_CERTIFICATE**
    Generate a self-signed certificate for the server (openSSL required)
 
 UA_ENABLE_* group

+ 1 - 1
doc/conf.py

@@ -82,7 +82,7 @@ language = "en"
 
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
-exclude_patterns = []
+exclude_patterns = ["client_config.rst", "server_config.rst"]
 
 # The reST default role (used for this markup: `text`) to use for all
 # documents.

+ 10 - 14
doc/index.rst

@@ -36,7 +36,7 @@ At its core, OPC UA defines
   response message in the protocol type system.
 
 The standard itself can be purchased from IEC or downloaded for free on the
-website of the OPC Foundation at https://opcfoundation.org/ (you only need to
+website of the OPC Foundation at https://opcfoundation.org/ (you need to
 register with a valid email).
 
 The OPC Foundation drives the continuous improvement of the standard and the
@@ -55,23 +55,20 @@ server SDK. It currently supports the Micro Embedded Device Server Profile plus
 some additional features. Server binaries can be well under 100kb in size,
 depending on the contained information model.
 
-open62541 adheres to the OPC UA specification as closely as possible and the
-released features pass the official Conformance Testing Tools (CTT). However,
-the library comes without any warranty. If you intend to use OPC UA in a
-mission-critical product, please consider talking to a commercial vendor of OPC
-UA SDKs and services.
-
 - Communication Stack
 
   - OPC UA binary protocol
   - Chunking (splitting of large messages)
   - Exchangeable network layer (plugin) for using custom networking APIs (e.g. on embedded targets)
+  - Encrypted communication
+  - Asynchronous service requests in the client
 
 - Information model
 
   - Support for all OPC UA node types (including method nodes)
   - Support for adding and removing nodes and references also at runtime.
   - Support for inheritance and instantiation of object- and variable-types (custom constructor/destructor, instantiation of child nodes)
+  - Access control for individual nodes
 
 - Subscriptions
 
@@ -83,12 +80,11 @@ UA SDKs and services.
   - Support for generating data types from standard XML definitions
   - Support for generating server-side information models (nodesets) from standard XML definitions
 
-Features still missing in the 0.2 release are:
+Features on the roadmap for the 0.3 release series but missing in the initial v0.3 release are:
 
-- Encryption
-- Access control for individual nodes
+- Encrypted communication in the client
 - Events (notifications emitted by objects, data change notifications are implemented)
-- Event-loop (background tasks) and asynchronous service requests in the client
+- Event-loop (background tasks) in the client
 
 Getting Help
 ------------
@@ -103,9 +99,9 @@ Contributing
 ------------
 
 As an open source project, we invite new contributors to help improve open62541.
-Issue reports, bugfixes and new features are very welcome. Note that there are
-ways to begin contributing without deep knowledge of the OPC UA standard:
+Issue reports, bugfixes and new features are very welcome. The following are
+good starting points for new contributors:
 
 - `Report bugs <https://github.com/open62541/open62541/issues>`_
 - Improve the `documentation <http://open62541.org/doc/current>`_
-- Work on issues marked as `easy hacks <https://github.com/open62541/open62541/labels/easy%20hack>`_
+- Work on issues marked as `good first issue <https://github.com/open62541/open62541/labels/good%20first%20issue>`_

File diff suppressed because it is too large
+ 0 - 360
doc/namespace_compiler.rst


File diff suppressed because it is too large
+ 456 - 0
doc/nodeset_compiler.rst


BIN
doc/nodeset_compiler_pump.png


+ 1 - 1
doc/protocol.rst

@@ -73,7 +73,7 @@ Session
   in cleartext. Currently defined authentication mechanisms are anonymous login,
   username/password, Kerberos and x509 certificates. The latter requires that
   the request message is accompanied by a signature to prove that the sender is
-  in posession of the private key with which the certificate was created.
+  in possession of the private key with which the certificate was created.
 
   There are two message exchanges required to establish a session:
   *CreateSession* and *ActicateSession*. The ActivateSession service can be used

+ 1 - 1
doc/toc.rst

@@ -13,5 +13,5 @@ open62541 Documentation
    server
    client
    constants
-   namespace_compiler
+   nodeset_compiler
    internal

+ 2 - 0
doc/tutorials.rst

@@ -1,3 +1,5 @@
+.. _tutorials:
+
 Tutorials
 =========
 

+ 10 - 2
examples/CMakeLists.txt

@@ -14,7 +14,7 @@ macro(add_example EXAMPLE_NAME EXAMPLE_SOURCE)
   assign_source_group(${EXAMPLE_SOURCE})
   add_dependencies(${EXAMPLE_NAME} open62541-amalgamation-header open62541-amalgamation-source)
   set_target_properties(${EXAMPLE_NAME} PROPERTIES FOLDER "open62541/examples")
-  set_target_properties(${EXAMPLE_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/bin/examples")
+  set_target_properties(${EXAMPLE_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
   if(UA_COMPILE_AS_CXX)
     set_source_files_properties(${EXAMPLE_SOURCE} PROPERTIES LANGUAGE CXX)
   endif()
@@ -48,7 +48,7 @@ add_example(tutorial_client_events tutorial_client_events.c)
 # Example Server #
 ##################
 
-add_example(server server.c)
+add_example(server_ctt server_ctt.c)
 
 ##################
 # Example Client #
@@ -58,6 +58,10 @@ add_example(client client.c)
 
 add_example(client_connect_loop client_connect_loop.c)
 
+if(UA_ENABLE_SUBSCRIPTIONS)
+add_example(client_subscription_loop client_subscription_loop.c)
+endif()
+
 ####################
 # Feature Examples #
 ####################
@@ -70,6 +74,10 @@ add_example(server_repeated_job server_repeated_job.c)
 
 add_example(server_inheritance server_inheritance.c)
 
+if(UA_ENABLE_ENCRYPTION)
+    add_example(server_basic128rsa15 encryption/server_basic128rsa15.c)
+endif()
+
 add_example(custom_datatype_client custom_datatype/client_types_custom.c)
 add_example(custom_datatype_server custom_datatype/server_types_custom.c)
 

+ 12 - 12
examples/access_control/server_access_control.c

@@ -7,33 +7,33 @@
 #include "open62541.h"
 
 static UA_Boolean
-allowAddNode(const UA_NodeId *sessionId, void *sessionContext,
-    const UA_AddNodesItem *item)
-{
+allowAddNode(UA_Server *server, UA_AccessControl *ac,
+             const UA_NodeId *sessionId, void *sessionContext,
+             const UA_AddNodesItem *item) {
     printf("Called allowAddNode\n");
     return UA_TRUE;
 }
 
 static UA_Boolean
-allowAddReference(const UA_NodeId *sessionId, void *sessionContext,
-    const UA_AddReferencesItem *item)
-{
+allowAddReference(UA_Server *server, UA_AccessControl *ac,
+                  const UA_NodeId *sessionId, void *sessionContext,
+                  const UA_AddReferencesItem *item) {
     printf("Called allowAddReference\n");
     return UA_TRUE;
 }
 
 static UA_Boolean
-allowDeleteNode(const UA_NodeId *sessionId, void *sessionContext,
-    const UA_DeleteNodesItem *item)
-{
+allowDeleteNode(UA_Server *server, UA_AccessControl *ac,
+                const UA_NodeId *sessionId, void *sessionContext,
+                const UA_DeleteNodesItem *item) {
     printf("Called allowDeleteNode\n");
     return UA_FALSE; // Do not allow deletion from client
 }
 
 static UA_Boolean
-allowDeleteReference(const UA_NodeId *sessionId, void *sessionContext,
-    const UA_DeleteReferencesItem *item)
-{
+allowDeleteReference(UA_Server *server, UA_AccessControl *ac,
+                     const UA_NodeId *sessionId, void *sessionContext,
+                     const UA_DeleteReferencesItem *item) {
     printf("Called allowDeleteReference\n");
     return UA_TRUE;
 }

+ 28 - 19
examples/client.c

@@ -6,7 +6,8 @@
 
 #ifdef UA_ENABLE_SUBSCRIPTIONS
 static void
-handler_TheAnswerChanged(UA_UInt32 monId, UA_DataValue *value, void *context) {
+handler_TheAnswerChanged(UA_Client *client, UA_UInt32 subId, void *subContext,
+                         UA_UInt32 monId, void *monContext, UA_DataValue *value) {
     printf("The Answer has changed!\n");
 }
 #endif
@@ -37,7 +38,7 @@ int main(int argc, char *argv[]) {
         return (int)retval;
     }
     printf("%i endpoints found\n", (int)endpointArraySize);
-    for(size_t i=0;i<endpointArraySize;i++){
+    for(size_t i=0;i<endpointArraySize;i++) {
         printf("URL of endpoint %i is %.*s\n", (int)i,
                (int)endpointArray[i].endpointUrl.length,
                endpointArray[i].endpointUrl.data);
@@ -63,8 +64,8 @@ int main(int argc, char *argv[]) {
     bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL; /* return everything */
     UA_BrowseResponse bResp = UA_Client_Service_browse(client, bReq);
     printf("%-9s %-16s %-16s %-16s\n", "NAMESPACE", "NODEID", "BROWSE NAME", "DISPLAY NAME");
-    for (size_t i = 0; i < bResp.resultsSize; ++i) {
-        for (size_t j = 0; j < bResp.results[i].referencesSize; ++j) {
+    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]);
             if(ref->nodeId.nodeId.identifierType == UA_NODEIDTYPE_NUMERIC) {
                 printf("%-9d %-16d %-16.*s %-16.*s\n", ref->nodeId.nodeId.namespaceIndex,
@@ -93,19 +94,27 @@ int main(int argc, char *argv[]) {
 
 #ifdef UA_ENABLE_SUBSCRIPTIONS
     /* Create a subscription */
-    UA_UInt32 subId = 0;
-    UA_Client_Subscriptions_new(client, UA_SubscriptionSettings_default, &subId);
-    if(subId)
+    UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default();
+    UA_CreateSubscriptionResponse response = UA_Client_Subscriptions_create(client, request,
+                                                                            NULL, NULL, NULL);
+
+    UA_UInt32 subId = response.subscriptionId;
+    if(response.responseHeader.serviceResult == UA_STATUSCODE_GOOD)
         printf("Create subscription succeeded, id %u\n", subId);
-    /* Add a MonitoredItem */
-    UA_NodeId monitorThis = UA_NODEID_STRING(1, "the.answer");
-    UA_UInt32 monId = 0;
-    UA_Client_Subscriptions_addMonitoredItem(client, subId, monitorThis, UA_ATTRIBUTEID_VALUE,
-                                             &handler_TheAnswerChanged, NULL, &monId, 250);
-    if (monId)
-        printf("Monitoring 'the.answer', id %u\n", subId);
+
+    UA_MonitoredItemCreateRequest monRequest =
+        UA_MonitoredItemCreateRequest_default(UA_NODEID_STRING(1, "the.answer"));
+
+    UA_MonitoredItemCreateResult monResponse =
+    UA_Client_MonitoredItems_createDataChange(client, response.subscriptionId,
+                                              UA_TIMESTAMPSTORETURN_BOTH,
+                                              monRequest, NULL, handler_TheAnswerChanged, NULL);
+    if(monResponse.statusCode == UA_STATUSCODE_GOOD)
+        printf("Monitoring 'the.answer', id %u\n", monResponse.monitoredItemId);
+
+
     /* The first publish request should return the initial value of the variable */
-    UA_Client_Subscriptions_manuallySendPublishRequest(client);
+    UA_Client_runAsync(client, 1000);
 #endif
 
     /* Read attribute */
@@ -148,9 +157,9 @@ int main(int argc, char *argv[]) {
 
 #ifdef UA_ENABLE_SUBSCRIPTIONS
     /* Take another look at the.answer */
-    UA_Client_Subscriptions_manuallySendPublishRequest(client);
+    UA_Client_runAsync(client, 100);
     /* Delete the subscription */
-    if(!UA_Client_Subscriptions_remove(client, subId))
+    if(UA_Client_Subscriptions_deleteSingle(client, subId) == UA_STATUSCODE_GOOD)
         printf("Subscription removed\n");
 #endif
 
@@ -165,11 +174,11 @@ int main(int argc, char *argv[]) {
     retval = UA_Client_call(client, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                             UA_NODEID_NUMERIC(1, 62541), 1, &input, &outputSize, &output);
     if(retval == UA_STATUSCODE_GOOD) {
-        printf("Method call was successfull, and %lu returned values available.\n",
+        printf("Method call was successful, and %lu returned values available.\n",
                (unsigned long)outputSize);
         UA_Array_delete(output, outputSize, &UA_TYPES[UA_TYPES_VARIANT]);
     } else {
-        printf("Method call was unsuccessfull, and %x returned values available.\n", retval);
+        printf("Method call was unsuccessful, and %x returned values available.\n", retval);
     }
     UA_Variant_deleteMembers(&input);
 #endif

+ 9 - 8
examples/client_connect_loop.c

@@ -56,12 +56,12 @@ int main(void) {
     UA_Variant_init(&value);
 
     /* Endless loop reading the server time */
-    while (running) {
+    while(running) {
         /* if already connected, this will return GOOD and do nothing */
         /* if the connection is closed/errored, the connection will be reset and then reconnected */
         /* Alternatively you can also use UA_Client_getState to get the current state */
         UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
-        if (retval != UA_STATUSCODE_GOOD) {
+        if(retval != UA_STATUSCODE_GOOD) {
             UA_LOG_ERROR(logger, UA_LOGCATEGORY_CLIENT, "Not connected. Retrying to connect in 1 second");
             /* The connect may timeout after 1 second (see above) or it may fail immediately on network errors */
             /* E.g. name resolution errors or unreachable network. Thus there should be a small sleep here */
@@ -74,17 +74,18 @@ int main(void) {
                 UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
 
         retval = UA_Client_readValueAttribute(client, nodeId, &value);
-        if (retval == UA_STATUSCODE_BADCONNECTIONCLOSED) {
+        if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED) {
             UA_LOG_ERROR(logger, UA_LOGCATEGORY_CLIENT, "Connection was closed. Reconnecting ...");
             continue;
         }
-        if (retval == UA_STATUSCODE_GOOD &&
-            UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_DATETIME])) {
+        if(retval == UA_STATUSCODE_GOOD &&
+           UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_DATETIME])) {
             UA_DateTime raw_date = *(UA_DateTime *) value.data;
-            UA_String string_date = UA_DateTime_toString(raw_date);
-            UA_LOG_INFO(logger, UA_LOGCATEGORY_CLIENT, "string date is: %.*s", (int) string_date.length, string_date.data);
-            UA_String_deleteMembers(&string_date);
+            UA_DateTimeStruct dts = UA_DateTime_toStruct(raw_date);
+            UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "date is: %02u-%02u-%04u %02u:%02u:%02u.%03u",
+                        dts.day, dts.month, dts.year, dts.hour, dts.min, dts.sec, dts.milliSec);
         }
+        UA_Variant_deleteMembers(&value);
         UA_sleep_ms(1000);
     };
 

+ 142 - 0
examples/client_subscription_loop.c

@@ -0,0 +1,142 @@
+/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+
+/* Enable POSIX features */
+#if !defined(_XOPEN_SOURCE) && !defined(_WRS_KERNEL)
+# define _XOPEN_SOURCE 600
+#endif
+#ifndef _DEFAULT_SOURCE
+# define _DEFAULT_SOURCE
+#endif
+/* On older systems we need to define _BSD_SOURCE.
+ * _DEFAULT_SOURCE is an alias for that. */
+#ifndef _BSD_SOURCE
+# define _BSD_SOURCE
+#endif
+
+/**
+ * Client disconnect handling
+ * --------------------------
+ * This example shows you how to handle a client disconnect, e.g., if the server
+ * is shut down while the client is connected. You just need to call connect
+ * again and the client will automatically reconnect.
+ *
+ * This example is very similar to the tutorial_client_firststeps.c. */
+
+#include "open62541.h"
+#include <signal.h>
+
+#ifdef _WIN32
+# include <windows.h>
+# define UA_sleep_ms(X) Sleep(X)
+#else
+# include <unistd.h>
+# define UA_sleep_ms(X) usleep(X * 1000)
+#endif
+
+UA_Boolean running = true;
+UA_Logger logger = UA_Log_Stdout;
+
+static void stopHandler(int sign) {
+    UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Received Ctrl-C");
+    running = 0;
+}
+
+static void
+handler_currentTimeChanged(UA_Client *client, UA_UInt32 subId, void *subContext,
+                           UA_UInt32 monId, void *monContext, UA_DataValue *value) {
+    UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "currentTime has changed!");
+    if(UA_Variant_hasScalarType(&value->value, &UA_TYPES[UA_TYPES_DATETIME])) {
+        UA_DateTime raw_date = *(UA_DateTime *) value->value.data;
+        UA_DateTimeStruct dts = UA_DateTime_toStruct(raw_date);
+        UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "date is: %02u-%02u-%04u %02u:%02u:%02u.%03u",
+                        dts.day, dts.month, dts.year, dts.hour, dts.min, dts.sec, dts.milliSec);
+    }
+}
+
+static void
+deleteSubscriptionCallback(UA_Client *client, UA_UInt32 subscriptionId, void *subscriptionContext) {
+    UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Subscription Id %u was deleted", subscriptionId);
+}
+
+static void
+subscriptionInactivityCallback (UA_Client *client, UA_UInt32 subId, void *subContext) {
+    UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Inactivity for subscription %u", subId);
+}
+
+static void
+stateCallback (UA_Client *client, UA_ClientState clientState) {
+    switch(clientState) {
+        case UA_CLIENTSTATE_DISCONNECTED:
+            UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "The client is disconnected");
+        break;
+        case UA_CLIENTSTATE_CONNECTED:
+            UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "A TCP connection to the server is open");
+        break;
+        case UA_CLIENTSTATE_SECURECHANNEL:
+            UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "A SecureChannel to the server is open");
+        break;
+        case UA_CLIENTSTATE_SESSION:{
+            UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "A session with the server is open");
+            /* A new session was created. We need to create the subscription. */
+            /* Create a subscription */
+            UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default();
+            UA_CreateSubscriptionResponse response = UA_Client_Subscriptions_create(client, request,
+                                                                                    NULL, NULL, deleteSubscriptionCallback);
+
+            if(response.responseHeader.serviceResult == UA_STATUSCODE_GOOD)
+                UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Create subscription succeeded, id %u", response.subscriptionId);
+            else
+                return;
+
+            /* Add a MonitoredItem */
+            UA_MonitoredItemCreateRequest monRequest =
+                UA_MonitoredItemCreateRequest_default(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME));
+
+            UA_MonitoredItemCreateResult monResponse =
+                UA_Client_MonitoredItems_createDataChange(client, response.subscriptionId,
+                                                          UA_TIMESTAMPSTORETURN_BOTH,
+                                                          monRequest, NULL, handler_currentTimeChanged, NULL);
+            if(monResponse.statusCode == UA_STATUSCODE_GOOD)
+                UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Monitoring UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME', id %u", monResponse.monitoredItemId);
+        }
+        break;
+        case UA_CLIENTSTATE_SESSION_RENEWED:
+            UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "A session with the server is open (renewed)");
+            /* The session was renewed. We don't need to recreate the subscription. */
+        break;
+    }
+    return;
+}
+
+int main(void) {
+    signal(SIGINT, stopHandler); /* catches ctrl-c */
+
+    UA_ClientConfig config = UA_ClientConfig_default;
+    /* Set stateCallback */
+    config.stateCallback = stateCallback;
+    config.subscriptionInactivityCallback = subscriptionInactivityCallback;
+
+    UA_Client *client = UA_Client_new(config);
+
+    /* Endless loop runAsync */
+    while (running) {
+        /* if already connected, this will return GOOD and do nothing */
+        /* if the connection is closed/errored, the connection will be reset and then reconnected */
+        /* Alternatively you can also use UA_Client_getState to get the current state */
+        UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_LOG_ERROR(logger, UA_LOGCATEGORY_USERLAND, "Not connected. Retrying to connect in 1 second");
+            /* The connect may timeout after 1 second (see above) or it may fail immediately on network errors */
+            /* E.g. name resolution errors or unreachable network. Thus there should be a small sleep here */
+            UA_sleep_ms(1000);
+            continue;
+        }
+
+        UA_Client_runAsync(client, 1000);
+    };
+
+    /* Clean up */
+    UA_Client_delete(client); /* Disconnects the client internally */
+    return UA_STATUSCODE_GOOD;
+}

+ 2 - 2
examples/custom_datatype/client_types_custom.c

@@ -16,7 +16,7 @@ int main(void) {
 
     UA_Client *client = UA_Client_new(config);
     UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
-    if (retval != UA_STATUSCODE_GOOD) {
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_Client_delete(client);
         return (int)retval;
     }
@@ -29,7 +29,7 @@ int main(void) {
 
     retval = UA_Client_readValueAttribute(client, nodeId, &value);
             
-    if (retval == UA_STATUSCODE_GOOD) {
+    if(retval == UA_STATUSCODE_GOOD) {
         Point *p = (Point *)value.data;
         printf("Point = %f, %f, %f \n", p->x, p->y, p->z);
     }

+ 2 - 9
examples/custom_datatype/server_types_custom.c

@@ -26,15 +26,12 @@ add3PointDataType(UA_Server *server) {
     p.z = 0.0;
     UA_Variant_setScalar(&dattr.value, &p, &PointType);
 
-
     UA_Server_addVariableTypeNode(server, PointType.typeId,
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
                                   UA_QUALIFIEDNAME(1, "3D.Point"),
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
-                                  dattr,
-                                  NULL, NULL
-    );
+                                  dattr, NULL, NULL);
 
 }
 
@@ -55,11 +52,7 @@ add3DPointVariable(UA_Server *server) {
                               UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                               UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
                               UA_QUALIFIEDNAME(1, "3D.Point"),
-                              PointType.typeId,
-                              vattr,
-                              NULL,
-                              NULL
-    );
+                              PointType.typeId, vattr, NULL, NULL);
 }
 
 int main(void) {

+ 12 - 12
examples/discovery/client_find_servers.c

@@ -26,7 +26,7 @@ int main(void) {
         UA_Client *client = UA_Client_new(UA_ClientConfig_default);
         UA_StatusCode retval = UA_Client_findServersOnNetwork(client, DISCOVERY_SERVER_ENDPOINT, 0, 0,
                                                               0, NULL, &serverOnNetworkSize, &serverOnNetwork);
-        if (retval != UA_STATUSCODE_GOOD) {
+        if(retval != UA_STATUSCODE_GOOD) {
             UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER,
                          "Could not call FindServersOnNetwork service. "
                          "Is the discovery server started? StatusCode %s",
@@ -36,7 +36,7 @@ int main(void) {
         }
 
         // output all the returned/registered servers
-        for (size_t i = 0; i < serverOnNetworkSize; i++) {
+        for(size_t i = 0; i < serverOnNetworkSize; i++) {
             UA_ServerOnNetwork *server = &serverOnNetwork[i];
             printf("Server[%lu]: %.*s", (unsigned long) i,
                    (int) server->serverName.length, server->serverName.data);
@@ -44,7 +44,7 @@ int main(void) {
             printf("\n\tDiscovery URL: %.*s", (int) server->discoveryUrl.length,
                    server->discoveryUrl.data);
             printf("\n\tCapabilities: ");
-            for (size_t j = 0; j < server->serverCapabilitiesSize; j++) {
+            for(size_t j = 0; j < server->serverCapabilitiesSize; j++) {
                 printf("%.*s,", (int) server->serverCapabilities[j].length,
                        server->serverCapabilities[j].data);
             }
@@ -66,14 +66,14 @@ int main(void) {
                                        &applicationDescriptionArraySize, &applicationDescriptionArray);
         UA_Client_delete(client);
     }
-    if (retval != UA_STATUSCODE_GOOD) {
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER, "Could not call FindServers service. "
                 "Is the discovery server started? StatusCode %s", UA_StatusCode_name(retval));
         return (int) retval;
     }
 
     // output all the returned/registered servers
-    for (size_t i = 0; i < applicationDescriptionArraySize; i++) {
+    for(size_t i = 0; i < applicationDescriptionArraySize; i++) {
         UA_ApplicationDescription *description = &applicationDescriptionArray[i];
         printf("Server[%lu]: %.*s", (unsigned long) i, (int) description->applicationUri.length,
                description->applicationUri.data);
@@ -84,7 +84,7 @@ int main(void) {
         printf("\n\tProduct URI: %.*s", (int) description->productUri.length,
                description->productUri.data);
         printf("\n\tType: ");
-        switch (description->applicationType) {
+        switch(description->applicationType) {
             case UA_APPLICATIONTYPE_SERVER:
                 printf("Server");
                 break;
@@ -101,7 +101,7 @@ int main(void) {
                 printf("Unknown");
         }
         printf("\n\tDiscovery URLs:");
-        for (size_t j = 0; j < description->discoveryUrlsSize; j++) {
+        for(size_t j = 0; j < description->discoveryUrlsSize; j++) {
             printf("\n\t\t[%lu]: %.*s", (unsigned long) j,
                    (int) description->discoveryUrls[j].length,
                    description->discoveryUrls[j].data);
@@ -116,9 +116,9 @@ int main(void) {
 
     printf("-------- Server Endpoints --------\n");
 
-    for (size_t i = 0; i < applicationDescriptionArraySize; i++) {
+    for(size_t i = 0; i < applicationDescriptionArraySize; i++) {
         UA_ApplicationDescription *description = &applicationDescriptionArray[i];
-        if (description->discoveryUrlsSize == 0) {
+        if(description->discoveryUrlsSize == 0) {
             UA_LOG_INFO(logger, UA_LOGCATEGORY_CLIENT,
                         "[GetEndpoints] Server %.*s did not provide any discovery urls. Skipping.",
                         (int)description->applicationUri.length, description->applicationUri.data);
@@ -138,20 +138,20 @@ int main(void) {
         size_t endpointArraySize = 0;
         retval = UA_Client_getEndpoints(client, discoveryUrl, &endpointArraySize, &endpointArray);
         UA_free(discoveryUrl);
-        if (retval != UA_STATUSCODE_GOOD) {
+        if(retval != UA_STATUSCODE_GOOD) {
             UA_Client_disconnect(client);
             UA_Client_delete(client);
             break;
         }
 
-        for (size_t j = 0; j < endpointArraySize; j++) {
+        for(size_t j = 0; j < endpointArraySize; j++) {
             UA_EndpointDescription *endpoint = &endpointArray[j];
             printf("\n\tEndpoint[%lu]:", (unsigned long) j);
             printf("\n\t\tEndpoint URL: %.*s", (int) endpoint->endpointUrl.length, endpoint->endpointUrl.data);
             printf("\n\t\tTransport profile URI: %.*s", (int) endpoint->transportProfileUri.length,
                    endpoint->transportProfileUri.data);
             printf("\n\t\tSecurity Mode: ");
-            switch (endpoint->securityMode) {
+            switch(endpoint->securityMode) {
             case UA_MESSAGESECURITYMODE_INVALID:
                 printf("Invalid");
                 break;

+ 7 - 7
examples/discovery/server_multicast.c

@@ -64,19 +64,19 @@ static void
 serverOnNetworkCallback(const UA_ServerOnNetwork *serverOnNetwork, UA_Boolean isServerAnnounce,
                         UA_Boolean isTxtReceived, void *data) {
 
-    if (discovery_url != NULL || !isServerAnnounce) {
+    if(discovery_url != NULL || !isServerAnnounce) {
         UA_LOG_DEBUG(logger, UA_LOGCATEGORY_SERVER,
                      "serverOnNetworkCallback called, but discovery URL "
                      "already initialized or is not announcing. Ignoring.");
         return; // we already have everything we need or we only want server announces
     }
 
-    if (self_discovery_url != NULL && UA_String_equal(&serverOnNetwork->discoveryUrl, self_discovery_url)) {
+    if(self_discovery_url != NULL && UA_String_equal(&serverOnNetwork->discoveryUrl, self_discovery_url)) {
         // skip self
         return;
     }
 
-    if (!isTxtReceived)
+    if(!isTxtReceived)
         return; // we wait until the corresponding TXT record is announced.
                 // Problem: how to handle if a Server does not announce the
                 // optional TXT?
@@ -87,7 +87,7 @@ serverOnNetworkCallback(const UA_ServerOnNetwork *serverOnNetwork, UA_Boolean is
     UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Another server announced itself on %.*s",
                 (int)serverOnNetwork->discoveryUrl.length, serverOnNetwork->discoveryUrl.data);
 
-    if (discovery_url != NULL)
+    if(discovery_url != NULL)
         UA_free(discovery_url);
     discovery_url = (char*)UA_malloc(serverOnNetwork->discoveryUrl.length + 1);
     memcpy(discovery_url, serverOnNetwork->discoveryUrl.data, serverOnNetwork->discoveryUrl.length);
@@ -134,7 +134,7 @@ int main(int argc, char **argv) {
 
     // Start the server and call iterate to wait for the multicast discovery of the LDS
     UA_StatusCode retval = UA_Server_run_startup(server);
-    if (retval != UA_STATUSCODE_GOOD) {
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER,
                      "Could not start the server. StatusCode %s",
                      UA_StatusCode_name(retval));
@@ -147,7 +147,7 @@ int main(int argc, char **argv) {
                 "Server started. Waiting for announce of LDS Server.");
     while (running && discovery_url == NULL)
         UA_Server_run_iterate(server, true);
-    if (!running) {
+    if(!running) {
         UA_Server_delete(server);
         UA_ServerConfig_delete(config);
         UA_free(discovery_url);
@@ -158,7 +158,7 @@ int main(int argc, char **argv) {
     // periodic server register after 10 Minutes, delay first register for 500ms
     retval = UA_Server_addPeriodicServerRegisterCallback(server, discovery_url,
                                                          10 * 60 * 1000, 500, NULL);
-    if (retval != UA_STATUSCODE_GOOD) {
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER,
                      "Could not create periodic job for server register. StatusCode %s",
                      UA_StatusCode_name(retval));

+ 2 - 2
examples/discovery/server_register.c

@@ -98,7 +98,7 @@ int main(int argc, char **argv) {
                                                     10 * 60 * 1000, 500, NULL);
     // UA_StatusCode retval = UA_Server_addPeriodicServerRegisterJob(server,
     // "opc.tcp://localhost:4840", 10*60*1000, 500, NULL);
-    if (retval != UA_STATUSCODE_GOOD) {
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER,
                      "Could not create periodic job for server register. StatusCode %s",
                      UA_StatusCode_name(retval));
@@ -108,7 +108,7 @@ int main(int argc, char **argv) {
     }
 
     retval = UA_Server_run(server, &running);
-    if (retval != UA_STATUSCODE_GOOD) {
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER,
                      "Could not start the server. StatusCode %s",
                      UA_StatusCode_name(retval));

+ 90 - 0
examples/encryption/server_basic128rsa15.c

@@ -0,0 +1,90 @@
+/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+
+#include <signal.h>
+#include <stdio.h>
+#include <errno.h>
+#include "open62541.h"
+
+static UA_ByteString loadFile(const char *const path) {
+    UA_ByteString fileContents = UA_STRING_NULL;
+
+    /* Open the file */
+    FILE *fp = fopen(path, "rb");
+    if(!fp) {
+        errno = 0; /* We read errno also from the tcp layer... */
+        return fileContents;
+    }
+
+    /* Get the file length, allocate the data and read */
+    fseek(fp, 0, SEEK_END);
+    fileContents.length = (size_t)ftell(fp);
+    fileContents.data = (UA_Byte*)UA_malloc(fileContents.length * sizeof(UA_Byte));
+    if(fileContents.data) {
+        fseek(fp, 0, SEEK_SET);
+        size_t read = fread(fileContents.data, sizeof(UA_Byte), fileContents.length, fp);
+        if(read != fileContents.length)
+            UA_ByteString_deleteMembers(&fileContents);
+    } else {
+        fileContents.length = 0;
+    }
+    fclose(fp);
+
+    return fileContents;
+}
+
+UA_Boolean running = true;
+static void stopHandler(int sig) {
+    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "received ctrl-c");
+    running = false;
+}
+
+int main(int argc, char* argv[]) {
+    signal(SIGINT, stopHandler);
+    signal(SIGTERM, stopHandler);
+
+    if(argc < 3) {
+        UA_LOG_FATAL(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
+                     "Missing arguments. Arguments are "
+                     "<server-certificate.der> <private-key.der> "
+                     "[<trustlist1.crl>, ...]");
+        return 1;
+    }
+
+    /* Load certificate and private key */
+    UA_ByteString certificate = loadFile(argv[1]);
+    UA_ByteString privateKey = loadFile(argv[2]);
+
+    /* Load the trustlist */
+    size_t trustListSize = 0;
+    if(argc > 3)
+        trustListSize = (size_t)argc-3;
+    UA_STACKARRAY(UA_ByteString, trustList, trustListSize);
+    for(size_t i = 0; i < trustListSize; i++)
+        trustList[i] = loadFile(argv[i+3]);
+
+    /* Loading of a revocation list currentlu unsupported */
+    UA_ByteString *revocationList = NULL;
+    size_t revocationListSize = 0;
+
+    UA_ServerConfig *config =
+        UA_ServerConfig_new_basic128rsa15(4840, &certificate, &privateKey,
+                                          trustList, trustListSize,
+                                          revocationList, revocationListSize);
+    UA_ByteString_deleteMembers(&certificate);
+    UA_ByteString_deleteMembers(&privateKey);
+    for(size_t i = 0; i < trustListSize; i++)
+        UA_ByteString_deleteMembers(&trustList[i]);
+
+    if(!config) {
+        UA_LOG_FATAL(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
+                     "Could not create the server config");
+        return 1;
+    }
+
+    UA_Server *server = UA_Server_new(config);
+    UA_StatusCode retval = UA_Server_run(server, &running);
+    UA_Server_delete(server);
+    UA_ServerConfig_delete(config);
+    return (int)retval;
+}

+ 4 - 2
examples/server_certificate.c

@@ -27,8 +27,10 @@ static UA_ByteString loadCertificate(void) {
     fseek(fp, 0, SEEK_END);
     certificate.length = (size_t)ftell(fp);
     certificate.data = (UA_Byte *)UA_malloc(certificate.length*sizeof(UA_Byte));
-    if(!certificate.data)
-        return certificate;
+    if(!certificate.data) {
+        fclose(fp);
+        return UA_STRING_NULL;
+    }
 
     fseek(fp, 0, SEEK_SET);
     if(fread(certificate.data, sizeof(UA_Byte), certificate.length, fp) < (size_t)certificate.length)

+ 144 - 78
examples/server.c

@@ -5,43 +5,53 @@
 #define _CRT_SECURE_NO_WARNINGS /* disable fopen deprication warning in msvs */
 #endif
 
-
 #include <signal.h>
 #include <errno.h> // errno, EINTR
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <open62541.h>
 #include "open62541.h"
 
+/* This server is configured to the Compliance Testing Tools (CTT) against. The
+ * corresponding CTT configuration is available at
+ * https://github.com/open62541/open62541-ctt */
+
 UA_Boolean running = true;
 UA_Logger logger = UA_Log_Stdout;
 
-static UA_ByteString loadCertificate(void) {
-    UA_ByteString certificate = UA_STRING_NULL;
-    FILE *fp = NULL;
-    //FIXME: a potiential bug of locating the certificate, we need to get the path from the server's config
-    fp=fopen("server_cert.der", "rb");
+static const UA_NodeId baseDataVariableType = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_BASEDATAVARIABLETYPE}};
 
+static UA_ByteString
+loadFile(const char *const path) {
+    UA_ByteString fileContents = UA_STRING_NULL;
+
+    /* Open the file */
+    FILE *fp = fopen(path, "rb");
     if(!fp) {
-        errno = 0; // we read errno also from the tcp layer...
-        return certificate;
+        errno = 0; /* We read errno also from the tcp layer... */
+        return fileContents;
     }
 
+    /* Get the file length, allocate the data and read */
     fseek(fp, 0, SEEK_END);
-    certificate.length = (size_t)ftell(fp);
-    certificate.data = (UA_Byte*)UA_malloc(certificate.length*sizeof(UA_Byte));
-    if(!certificate.data)
-        return certificate;
-
-    fseek(fp, 0, SEEK_SET);
-    if(fread(certificate.data, sizeof(UA_Byte), certificate.length, fp) < (size_t)certificate.length)
-        UA_ByteString_deleteMembers(&certificate); // error reading the cert
+    fileContents.length = (size_t)ftell(fp);
+    fileContents.data = (UA_Byte *)UA_malloc(fileContents.length * sizeof(UA_Byte));
+    if(fileContents.data) {
+        fseek(fp, 0, SEEK_SET);
+        size_t read = fread(fileContents.data, sizeof(UA_Byte), fileContents.length, fp);
+        if(read != fileContents.length)
+            UA_ByteString_deleteMembers(&fileContents);
+    } else {
+        fileContents.length = 0;
+    }
     fclose(fp);
 
-    return certificate;
+    return fileContents;
 }
 
-static void stopHandler(int sign) {
+static void
+stopHandler(int sign) {
     UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Received Ctrl-C");
     running = 0;
 }
@@ -70,6 +80,7 @@ readTimeData(UA_Server *server,
 
 /* Method Node Example */
 #ifdef UA_ENABLE_METHODCALLS
+
 static UA_StatusCode
 helloWorld(UA_Server *server,
            const UA_NodeId *sessionId, void *sessionContext,
@@ -78,11 +89,11 @@ helloWorld(UA_Server *server,
            size_t inputSize, const UA_Variant *input,
            size_t outputSize, UA_Variant *output) {
     /* input is a scalar string (checked by the server) */
-    UA_String *name = (UA_String*)input[0].data;
+    UA_String *name = (UA_String *)input[0].data;
     UA_String hello = UA_STRING("Hello ");
     UA_String greet;
     greet.length = hello.length + name->length;
-    greet.data = (UA_Byte*)UA_malloc(greet.length);
+    greet.data = (UA_Byte *)UA_malloc(greet.length);
     memcpy(greet.data, hello.data, hello.length);
     memcpy(greet.data + hello.length, name->data, name->length);
     UA_Variant_setScalarCopy(output, &greet, &UA_TYPES[UA_TYPES_STRING]);
@@ -111,25 +122,78 @@ outargMethod(UA_Server *server,
     UA_Variant_setScalarCopy(output, &out, &UA_TYPES[UA_TYPES_INT32]);
     return UA_STATUSCODE_GOOD;
 }
+
 #endif
 
-int main(int argc, char** argv) {
+int
+main(int argc, char **argv) {
     signal(SIGINT, stopHandler); /* catches ctrl-c */
+    signal(SIGTERM, stopHandler);
+
+#ifdef UA_ENABLE_ENCRYPTION
+    if(argc < 3) {
+        UA_LOG_FATAL(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
+                     "Missing arguments for encryption support. "
+                         "Arguments are <server-certificate.der> "
+                         "<private-key.der> [<trustlist1.crl>, ...]");
+        return 1;
+    }
+
+    /* Load certificate and private key */
+    UA_ByteString certificate = loadFile(argv[1]);
+    UA_ByteString privateKey = loadFile(argv[2]);
+
+    /* Load the trustlist */
+    size_t trustListSize = 0;
+    if(argc > 3)
+        trustListSize = (size_t)argc-3;
+    UA_STACKARRAY(UA_ByteString, trustList, trustListSize);
+    for(size_t i = 0; i < trustListSize; i++)
+        trustList[i] = loadFile(argv[i+3]);
+
+    /* Loading of a revocation list currently unsupported */
+    UA_ByteString *revocationList = NULL;
+    size_t revocationListSize = 0;
 
-    UA_ByteString certificate = loadCertificate();
     UA_ServerConfig *config =
-        UA_ServerConfig_new_minimal(4840, &certificate);
+        UA_ServerConfig_new_allSecurityPolicies(4840, &certificate, &privateKey,
+                                                trustList, trustListSize,
+                                                revocationList, revocationListSize);
+    UA_ByteString_deleteMembers(&certificate);
+    UA_ByteString_deleteMembers(&privateKey);
+    for(size_t i = 0; i < trustListSize; i++)
+        UA_ByteString_deleteMembers(&trustList[i]);
+
+    if(!config) {
+        UA_LOG_FATAL(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
+                     "Could not create the server config");
+        return 1;
+    }
+#else
+    if(argc < 2) {
+        UA_LOG_FATAL(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
+                     "Missing argument for the server certificate");
+        return 1;
+    }
+    UA_ByteString certificate = loadFile(argv[1]);
+    UA_ServerConfig *config = UA_ServerConfig_new_minimal(4840, &certificate);
     UA_ByteString_deleteMembers(&certificate);
+#endif
+
     /* uncomment next line to add a custom hostname */
     // UA_ServerConfig_set_customHostname(config, UA_STRING("custom"));
-    
+
     UA_Server *server = UA_Server_new(config);
+    if(server == NULL)
+        return 1;
 
     /* add a static variable node to the server */
     UA_VariableAttributes myVar = UA_VariableAttributes_default;
     myVar.description = UA_LOCALIZEDTEXT("en-US", "the answer");
     myVar.displayName = UA_LOCALIZEDTEXT("en-US", "the answer");
     myVar.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
+    myVar.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
+    myVar.valueRank = -1;
     UA_Int32 myInteger = 42;
     UA_Variant_setScalarCopy(&myVar.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
     const UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
@@ -137,7 +201,7 @@ int main(int argc, char** argv) {
     UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
     UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId, parentReferenceNodeId,
-        myIntegerName, UA_NODEID_NULL, myVar, NULL, NULL);
+                              myIntegerName, baseDataVariableType, myVar, NULL, NULL);
     UA_Variant_deleteMembers(&myVar.value);
 
     /* add a variable with the datetime data source */
@@ -145,13 +209,15 @@ int main(int argc, char** argv) {
     dateDataSource.read = readTimeData;
     dateDataSource.write = NULL;
     UA_VariableAttributes v_attr = UA_VariableAttributes_default;
-    v_attr.description = UA_LOCALIZEDTEXT("en-US","current time");
-    v_attr.displayName = UA_LOCALIZEDTEXT("en-US","current time");
-    v_attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
+    v_attr.description = UA_LOCALIZEDTEXT("en-US", "current time");
+    v_attr.displayName = UA_LOCALIZEDTEXT("en-US", "current time");
+    v_attr.accessLevel = UA_ACCESSLEVELMASK_READ;
+    v_attr.dataType = UA_TYPES[UA_TYPES_DATETIME].typeId;
+    v_attr.valueRank = -1;
     const UA_QualifiedName dateName = UA_QUALIFIEDNAME(1, "current time");
     UA_Server_addDataSourceVariableNode(server, UA_NODEID_NULL, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                                         UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), dateName,
-                                        UA_NODEID_NULL, v_attr, dateDataSource, NULL, NULL);
+                                        baseDataVariableType, v_attr, dateDataSource, NULL, NULL);
 
     /* Add HelloWorld method to the server */
 #ifdef UA_ENABLE_METHODCALLS
@@ -177,11 +243,11 @@ int main(int argc, char** argv) {
     addmethodattributes.executable = true;
     addmethodattributes.userExecutable = true;
     UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1, 62541),
-        UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-        UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
-        UA_QUALIFIEDNAME(1, "hello_world"), addmethodattributes,
-        &helloWorld, /* callback of the method node */
-        1, &inputArguments, 1, &outputArguments, NULL, NULL);
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                            UA_QUALIFIEDNAME(1, "hello_world"), addmethodattributes,
+                            &helloWorld, /* callback of the method node */
+                            1, &inputArguments, 1, &outputArguments, NULL, NULL);
 #endif
 
     /* Add folders for demo information model */
@@ -195,29 +261,29 @@ int main(int argc, char** argv) {
     object_attr.description = UA_LOCALIZEDTEXT("en-US", "Demo");
     object_attr.displayName = UA_LOCALIZEDTEXT("en-US", "Demo");
     UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, DEMOID),
-        UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, "Demo"),
-        UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL, NULL);
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, "Demo"),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL, NULL);
 
     object_attr.description = UA_LOCALIZEDTEXT("en-US", "Scalar");
     object_attr.displayName = UA_LOCALIZEDTEXT("en-US", "Scalar");
     UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, SCALARID),
-        UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-        UA_QUALIFIEDNAME(1, "Scalar"),
-        UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL, NULL);
+                            UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                            UA_QUALIFIEDNAME(1, "Scalar"),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL, NULL);
 
     object_attr.description = UA_LOCALIZEDTEXT("en-US", "Array");
     object_attr.displayName = UA_LOCALIZEDTEXT("en-US", "Array");
     UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, ARRAYID),
-        UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-        UA_QUALIFIEDNAME(1, "Array"),
-        UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL, NULL);
+                            UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                            UA_QUALIFIEDNAME(1, "Array"),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL, NULL);
 
     object_attr.description = UA_LOCALIZEDTEXT("en-US", "Matrix");
     object_attr.displayName = UA_LOCALIZEDTEXT("en-US", "Matrix");
     UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, MATRIXID), UA_NODEID_NUMERIC(1, DEMOID),
-        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, "Matrix"),
-        UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL, NULL);
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, "Matrix"),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL, NULL);
 
     /* Fill demo nodes for each type*/
     UA_UInt32 id = 51000; // running id in namespace 0
@@ -250,19 +316,19 @@ int main(int argc, char** argv) {
         UA_Variant_setScalar(&attr.value, value, &UA_TYPES[type]);
         UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id),
                                   UA_NODEID_NUMERIC(1, SCALARID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                                  qualifiedName, UA_NODEID_NULL, attr, NULL, NULL);
+                                  qualifiedName, baseDataVariableType, attr, NULL, NULL);
         UA_Variant_deleteMembers(&attr.value);
 
         /* add an array node for every built-in type */
         UA_Variant_setArray(&attr.value, UA_Array_new(10, &UA_TYPES[type]), 10, &UA_TYPES[type]);
         UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id), UA_NODEID_NUMERIC(1, ARRAYID),
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), qualifiedName,
-                                  UA_NODEID_NULL, attr, NULL, NULL);
+                                  baseDataVariableType, attr, NULL, NULL);
         UA_Variant_deleteMembers(&attr.value);
 
         /* add an matrix node for every built-in type */
-        void* myMultiArray = UA_Array_new(9, &UA_TYPES[type]);
-        attr.value.arrayDimensions = (UA_UInt32*)UA_Array_new(2, &UA_TYPES[UA_TYPES_INT32]);
+        void *myMultiArray = UA_Array_new(9, &UA_TYPES[type]);
+        attr.value.arrayDimensions = (UA_UInt32 *)UA_Array_new(2, &UA_TYPES[UA_TYPES_INT32]);
         attr.value.arrayDimensions[0] = 3;
         attr.value.arrayDimensions[1] = 3;
         attr.value.arrayDimensionsSize = 2;
@@ -271,7 +337,7 @@ int main(int argc, char** argv) {
         attr.value.type = &UA_TYPES[type];
         UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id), UA_NODEID_NUMERIC(1, MATRIXID),
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), qualifiedName,
-                                  UA_NODEID_NULL, attr, NULL, NULL);
+                                  baseDataVariableType, attr, NULL, NULL);
         UA_Variant_deleteMembers(&attr.value);
 #ifdef UA_ENABLE_TYPENAMES
         UA_LocalizedText_deleteMembers(&attr.displayName);
@@ -282,10 +348,9 @@ int main(int argc, char** argv) {
     /* Hierarchy of depth 10 for CTT testing with forward and inverse references */
     /* Enter node "depth 9" in CTT configuration - Project->Settings->Server
        Test->NodeIds->Paths->Starting Node 1 */
-    object_attr.description = UA_LOCALIZEDTEXT("en-US","DepthDemo");
-    object_attr.displayName = UA_LOCALIZEDTEXT("en-US","DepthDemo");
-    UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, DEPTHID),
-                            UA_NODEID_NUMERIC(1, DEMOID),
+    object_attr.description = UA_LOCALIZEDTEXT("en-US", "DepthDemo");
+    object_attr.displayName = UA_LOCALIZEDTEXT("en-US", "DepthDemo");
+    UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, DEPTHID), UA_NODEID_NUMERIC(1, DEMOID),
                             UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, "DepthDemo"),
                             UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL, NULL);
 
@@ -297,10 +362,11 @@ int main(int argc, char** argv) {
 #else
         sprintf(name, "depth%i", i);
 #endif
-        object_attr.description = UA_LOCALIZEDTEXT("en-US",name);
-        object_attr.displayName = UA_LOCALIZEDTEXT("en-US",name);
-        UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, id+i),
-                                UA_NODEID_NUMERIC(1, i==1 ? DEPTHID : id+i-1), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+        object_attr.description = UA_LOCALIZEDTEXT("en-US", name);
+        object_attr.displayName = UA_LOCALIZEDTEXT("en-US", name);
+        UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, id + i),
+                                UA_NODEID_NUMERIC(1, i == 1 ? DEPTHID : id + i - 1),
+                                UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
                                 UA_QUALIFIEDNAME(1, name),
                                 UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL, NULL);
     }
@@ -328,11 +394,11 @@ int main(int argc, char** argv) {
     addmethodattributes.executable = true;
     addmethodattributes.userExecutable = true;
     UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1, NOARGID),
-        UA_NODEID_NUMERIC(1, DEMOID),
-        UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
-        UA_QUALIFIEDNAME(1, "noarg"), addmethodattributes,
-        &noargMethod, /* callback of the method node */
-        0, NULL, 0, NULL, NULL, NULL);
+                            UA_NODEID_NUMERIC(1, DEMOID),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                            UA_QUALIFIEDNAME(1, "noarg"), addmethodattributes,
+                            &noargMethod, /* callback of the method node */
+                            0, NULL, 0, NULL, NULL, NULL);
 
     /* Method with in arguments */
     addmethodattributes = UA_MethodAttributes_default;
@@ -347,11 +413,11 @@ int main(int argc, char** argv) {
     inputArguments.valueRank = -1; //uaexpert will crash if set to 0 ;)
 
     UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1, INARGID),
-        UA_NODEID_NUMERIC(1, DEMOID),
-        UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
-        UA_QUALIFIEDNAME(1, "noarg"), addmethodattributes,
-        &noargMethod, /* callback of the method node */
-        1, &inputArguments, 0, NULL, NULL, NULL);
+                            UA_NODEID_NUMERIC(1, DEMOID),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                            UA_QUALIFIEDNAME(1, "noarg"), addmethodattributes,
+                            &noargMethod, /* callback of the method node */
+                            1, &inputArguments, 0, NULL, NULL, NULL);
 
     /* Method with out arguments */
     addmethodattributes = UA_MethodAttributes_default;
@@ -366,11 +432,11 @@ int main(int argc, char** argv) {
     outputArguments.valueRank = -1;
 
     UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1, OUTARGID),
-        UA_NODEID_NUMERIC(1, DEMOID),
-        UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
-        UA_QUALIFIEDNAME(1, "outarg"), addmethodattributes,
-        &outargMethod, /* callback of the method node */
-        0, NULL, 1, &outputArguments, NULL, NULL);
+                            UA_NODEID_NUMERIC(1, DEMOID),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                            UA_QUALIFIEDNAME(1, "outarg"), addmethodattributes,
+                            &outargMethod, /* callback of the method node */
+                            0, NULL, 1, &outputArguments, NULL, NULL);
 
     /* Method with inout arguments */
     addmethodattributes = UA_MethodAttributes_default;
@@ -379,11 +445,11 @@ int main(int argc, char** argv) {
     addmethodattributes.userExecutable = true;
 
     UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1, INOUTARGID),
-        UA_NODEID_NUMERIC(1, DEMOID),
-        UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
-        UA_QUALIFIEDNAME(1, "inoutarg"), addmethodattributes,
-        &outargMethod, /* callback of the method node */
-        1, &inputArguments, 1, &outputArguments, NULL, NULL);
+                            UA_NODEID_NUMERIC(1, DEMOID),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                            UA_QUALIFIEDNAME(1, "inoutarg"), addmethodattributes,
+                            &outargMethod, /* callback of the method node */
+                            1, &inputArguments, 1, &outputArguments, NULL, NULL);
 #endif
 
     /* run server */

+ 155 - 28
examples/server_inheritance.c

@@ -9,31 +9,28 @@ static void stopHandler(int sig) {
     running = false;
 }
 
-int main(void) {
-    signal(SIGINT,  stopHandler);
-    signal(SIGTERM, stopHandler);
+/**
+ * This will create a type structure and some instances of the types:
+ *
+ * Create a rudimentary objectType
+ *
+ * Type:
+ * + MamalType
+ *  v- Class  = "mamalia"
+ *  v- Species
+ *  o- Abilities
+ *      v- MakeSound
+ *      v- Breathe = True
+ *  + DogType
+ *      v- Species = "Canis"
+ *      v- Name
+ *      o- Abilities
+ *          v- MakeSound = "Wuff"
+ *           v- FetchNewPaper
+ */
+static void createMamals(UA_Server *server) {
 
-    UA_ServerConfig *config = UA_ServerConfig_new_default();
-    UA_Server *server = UA_Server_new(config);
-
-    /* Create a rudimentary objectType
-     *
-     * Type:
-     * + MamalType
-     *  v- Class  = "mamalia"
-     *  v- Species
-     *  o- Abilities
-     *      v- MakeSound
-     *      v- Breathe = True
-     *  + DogType
-     *      v- Species = "Canis"
-     *      v- Name
-     *      o- Abilities
-     *          v- MakeSound = "Wuff"
-     *           v- FetchNewPaper
-     */
 
-    UA_StatusCode retval;
     UA_ObjectTypeAttributes otAttr = UA_ObjectTypeAttributes_default;
     otAttr.description = UA_LOCALIZEDTEXT("en-US", "A mamal");
     otAttr.displayName = UA_LOCALIZEDTEXT("en-US", "MamalType");
@@ -50,7 +47,7 @@ int main(void) {
     UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 10001),
                               UA_NODEID_NUMERIC(1, 10000),
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
-                              UA_QUALIFIEDNAME(1, "Class"), UA_NODEID_NULL,
+                              UA_QUALIFIEDNAME(1, "Class"), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
                               vAttr, NULL, NULL);
 
     vAttr = UA_VariableAttributes_default;
@@ -59,7 +56,7 @@ int main(void) {
     UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 10002),
                               UA_NODEID_NUMERIC(1, 10000),
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
-                              UA_QUALIFIEDNAME(1, "Species"), UA_NODEID_NULL,
+                              UA_QUALIFIEDNAME(1, "Species"), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
                               vAttr, NULL, NULL);
 
     otAttr = UA_ObjectTypeAttributes_default;
@@ -78,7 +75,7 @@ int main(void) {
     UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 20001),
                               UA_NODEID_NUMERIC(1, 20000),
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
-                              UA_QUALIFIEDNAME(1, "Species"), UA_NODEID_NULL,
+                              UA_QUALIFIEDNAME(1, "Species"), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
                               vAttr, NULL, NULL);
 
     vAttr = UA_VariableAttributes_default;
@@ -89,7 +86,7 @@ int main(void) {
     UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 20002),
                               UA_NODEID_NUMERIC(1, 20000),
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
-                              UA_QUALIFIEDNAME(1, "Name"), UA_NODEID_NULL,
+                              UA_QUALIFIEDNAME(1, "Name"), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
                               vAttr, NULL, NULL);
 
     /* Instatiate a dog named bello:
@@ -126,8 +123,138 @@ int main(void) {
                             UA_QUALIFIEDNAME(1, "Mamal1"), UA_NODEID_NUMERIC(1, 10000),
                             oAttr, NULL, NULL);
 
+}
+
+/**
+ * This method shows the usage of _begin and _finish methods.
+ * Normally, if you create an instance of an object type, all its
+ * mandatory children are inherited and created.
+ * It could be the case that you first need to create a node,
+ * add some children with specific IDs and then all the remaining
+ * inherited children should be created.
+ * For this use-case you can use first the _begin method,
+ * which creates the node, including its parent references.
+ * Then you can add any children, and then you should
+ * call _finish on that node, which then adds all the inherited children.
+ *
+ * For further details check the example below or the corresponding
+ * method documentation.
+ *
+ * To demonstrate this, we use the following example:
+ *
+ * + ObjectType
+ *      + LampType (Object)
+ *          + IsOn (Variable, Boolean, Mandatory)
+ *          + Brightness (Variable, UInt16, Mandatory)
+ * + Objects
+ *      + LampGreen
+ *          Should inherit the mandatory IsOn and Brightness with a generated node ID
+ *      + LampRed
+ *          IsOn should have the node ID 30101, Brightness will be inherited with a generated node ID
+ *
+ */
+static void createCustomInheritance(UA_Server *server) {
+
+    /* Add LampType object type node */
+
+    UA_ObjectTypeAttributes otAttr = UA_ObjectTypeAttributes_default;
+    otAttr.description = UA_LOCALIZEDTEXT("en-US", "A Lamp");
+    otAttr.displayName = UA_LOCALIZEDTEXT("en-US", "LampType");
+    UA_Server_addObjectTypeNode(server, UA_NODEID_NUMERIC(1, 30000),
+                                UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
+                                UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
+                                UA_QUALIFIEDNAME(1, "LampType"), otAttr, NULL, NULL);
+
+
+    /* Add the two mandatory children, IsOn and Brightness */
+    UA_VariableAttributes vAttr = UA_VariableAttributes_default;
+    vAttr.description =  UA_LOCALIZEDTEXT("en-US", "Switch lamp on/off");
+    vAttr.displayName =  UA_LOCALIZEDTEXT("en-US", "IsOn");
+    UA_Boolean isOn = UA_FALSE;
+    UA_Variant_setScalar(&vAttr.value, &isOn, &UA_TYPES[UA_TYPES_BOOLEAN]);
+    UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 30001),
+                              UA_NODEID_NUMERIC(1, 30000),
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
+                              UA_QUALIFIEDNAME(1, "IsOn"), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
+                              vAttr, NULL, NULL);
+    UA_Server_addReference(server, UA_NODEID_NUMERIC(1, 30001),
+                           UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE),
+                           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY), true);
+
+    vAttr = UA_VariableAttributes_default;
+    vAttr.description =  UA_LOCALIZEDTEXT("en-US", "Lamp brightness");
+    vAttr.displayName =  UA_LOCALIZEDTEXT("en-US", "Brightness");
+    UA_UInt16 brightness = 142;
+    UA_Variant_setScalar(&vAttr.value, &brightness, &UA_TYPES[UA_TYPES_UINT16]);
+    UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 30002),
+                              UA_NODEID_NUMERIC(1, 30000),
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
+                              UA_QUALIFIEDNAME(1, "Brightness"), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
+                              vAttr, NULL, NULL);
+    UA_Server_addReference(server, UA_NODEID_NUMERIC(1, 30002),
+                           UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE),
+                           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY), true);
+    
+    /* Now we want to inherit all the mandatory children for LampGreen and don't care about the node ids.
+     * These will be automatically generated. This will internally call the _begin and _finish methods */
+
+    UA_ObjectAttributes oAttr = UA_ObjectAttributes_default;
+    oAttr.description = UA_LOCALIZEDTEXT("en-US", "A green lamp");
+    oAttr.displayName = UA_LOCALIZEDTEXT("en-US", "LampGreen");
+    UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 0),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                            UA_QUALIFIEDNAME(1, "LampGreen"), UA_NODEID_NUMERIC(1, 30000),
+                            oAttr, NULL, NULL);
+
+    /* For the red lamp we want to set the node ID of the IsOn child manually, thus we need to use
+     * the _begin method, add the child and then _finish: */
+
+    /* The call to UA_Server_addNode_begin will create the node and its parent references,
+     * but it will not instantiate the mandatory children */
+    oAttr = UA_ObjectAttributes_default;
+    oAttr.description = UA_LOCALIZEDTEXT("en-US", "A red lamp");
+    oAttr.displayName = UA_LOCALIZEDTEXT("en-US", "LampRed");
+    UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT,
+                            UA_NODEID_NUMERIC(1, 30100),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                            UA_QUALIFIEDNAME(1, "LampRed"),
+                            UA_NODEID_NUMERIC(1, 30000),
+                            (const UA_NodeAttributes*)&oAttr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],
+                            NULL, NULL);
+
+    /* Now we can add the IsOn with our own node ID */
+    vAttr = UA_VariableAttributes_default;
+    vAttr.description =  UA_LOCALIZEDTEXT("en-US", "Switch lamp on/off");
+    vAttr.displayName =  UA_LOCALIZEDTEXT("en-US", "IsOn");
+    isOn = UA_FALSE;
+    UA_Variant_setScalar(&vAttr.value, &isOn, &UA_TYPES[UA_TYPES_BOOLEAN]);
+    UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 30101),
+                              UA_NODEID_NUMERIC(1, 30100),
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
+                              UA_QUALIFIEDNAME(1, "IsOn"), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
+                              vAttr, NULL, NULL);
+
+    /* And then we need to call the UA_Server_addNode_finish which adds all the remaining
+     * children and does some further initialization. It will not add the IsNode child,
+     * since a child with the same browse name already exists */
+    UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(1, 30100));
+}
+
+int main(void) {
+    signal(SIGINT,  stopHandler);
+    signal(SIGTERM, stopHandler);
+
+    UA_ServerConfig *config = UA_ServerConfig_new_default();
+    UA_Server *server = UA_Server_new(config);
+
+    createMamals(server);
+
+    createCustomInheritance(server);
+
     /* Run the server */
-    retval = UA_Server_run(server, &running);
+    UA_StatusCode retval = UA_Server_run(server, &running);
     UA_Server_delete(server);
     UA_ServerConfig_delete(config);
     return (int)retval;

+ 12 - 12
examples/server_instantiation.c

@@ -15,7 +15,7 @@ int main(void) {
 
     UA_ServerConfig *config = UA_ServerConfig_new_default();
     UA_Server *server = UA_Server_new(config);
-    
+
     /* Create a rudimentary objectType
      * 
      * BaseObjectType
@@ -30,43 +30,43 @@ int main(void) {
     UA_ObjectTypeAttributes otAttr = UA_ObjectTypeAttributes_default;
     otAttr.description = UA_LOCALIZEDTEXT("en-US", "A mamal");
     otAttr.displayName = UA_LOCALIZEDTEXT("en-US", "MamalType");
-    UA_Server_addObjectTypeNode(server, UA_NODEID_NUMERIC(1, 10000), 
+    UA_Server_addObjectTypeNode(server, UA_NODEID_NUMERIC(1, 10000),
                                 UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
                                 UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
                                 UA_QUALIFIEDNAME(1, "MamalType"), otAttr, NULL, NULL);
-  
+
     UA_VariableAttributes vAttr = UA_VariableAttributes_default;
     vAttr.description =  UA_LOCALIZEDTEXT("en-US", "This mamals Age in months");
     vAttr.displayName =  UA_LOCALIZEDTEXT("en-US", "Age");
     UA_UInt32 ageVar = 0;
     UA_Variant_setScalar(&vAttr.value, &ageVar, &UA_TYPES[UA_TYPES_UINT32]);
-    UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 10001), 
+    UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 10001),
                               UA_NODEID_NUMERIC(1, 10000), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
-                              UA_QUALIFIEDNAME(1, "Age"), UA_NODEID_NULL, vAttr, NULL, NULL);
-  
+                              UA_QUALIFIEDNAME(1, "Age"), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), vAttr, NULL, NULL);
+
     otAttr = UA_ObjectTypeAttributes_default;
     otAttr.description = UA_LOCALIZEDTEXT("en-US", "A dog, subtype of mamal");
     otAttr.displayName = UA_LOCALIZEDTEXT("en-US", "DogType");
     UA_Server_addObjectTypeNode(server, UA_NODEID_NUMERIC(1, 10002),
                                 UA_NODEID_NUMERIC(1, 10000), UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
                                 UA_QUALIFIEDNAME(1, "DogType"), otAttr, NULL, NULL);
-    
+
     vAttr = UA_VariableAttributes_default;
     vAttr.description =  UA_LOCALIZEDTEXT("en-US", "This mamals Age in months");
     vAttr.displayName =  UA_LOCALIZEDTEXT("en-US", "Name");
     UA_String defaultName = UA_STRING("unnamed dog");
     UA_Variant_setScalar(&vAttr.value, &defaultName, &UA_TYPES[UA_TYPES_STRING]);
-    UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 10003), 
+    UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 10003),
                               UA_NODEID_NUMERIC(1, 10002), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
-                              UA_QUALIFIEDNAME(1, "Name"), UA_NODEID_NULL, vAttr, NULL, NULL);
-    
+                              UA_QUALIFIEDNAME(1, "Name"), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), vAttr, NULL, NULL);
+
     /* Instatiate a dog named bello:
      * (O) Objects
      *   + O Bello <DogType>
      *     + Age 
      *     + Name
      */
-    
+
     UA_ObjectAttributes oAttr = UA_ObjectAttributes_default;
     oAttr.description = UA_LOCALIZEDTEXT("en-US", "A dog named Bello");
     oAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Bello");
@@ -75,7 +75,7 @@ int main(void) {
                             UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                             UA_QUALIFIEDNAME(1, "Bello"), UA_NODEID_NUMERIC(1, 10002),
                             oAttr, NULL, NULL);
-    
+
     retval = UA_Server_run(server, &running);
     UA_Server_delete(server);
     UA_ServerConfig_delete(config);

+ 1 - 1
examples/server_udp.c

@@ -20,7 +20,7 @@ int main(int argc, char** argv) {
 
     UA_ServerConfig config = UA_ServerConfig_standard;
     UA_ServerNetworkLayer nl;
-    nl = UA_ServerNetworkLayerUDP(UA_ConnectionConfig_standard, 16664);
+    nl = UA_ServerNetworkLayerUDP(UA_ConnectionConfig_standard, 4840);
     config.networkLayers = &nl;
     config.networkLayersSize = 1;
     UA_Server *server = UA_Server_new(config);

+ 74 - 32
examples/tutorial_client_events.c

@@ -2,8 +2,21 @@
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
 
 #include <signal.h>
+#include <stdio.h>
 #include "open62541.h"
 
+#ifdef _MSC_VER
+#pragma warning(disable:4996) // warning C4996: 'UA_Client_Subscriptions_addMonitoredEvent': was declared deprecated
+#endif
+
+#ifdef __clang__
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
 static UA_Boolean running = true;
 static void stopHandler(int sig) {
     UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "received ctrl-c");
@@ -11,11 +24,18 @@ static void stopHandler(int sig) {
 }
 
 #ifdef UA_ENABLE_SUBSCRIPTIONS
+
 static void
-handler_events(const UA_UInt32 monId, const size_t nEventFields, const UA_Variant *eventFields, void *context) {
+handler_events(UA_Client *client, UA_UInt32 subId, void *subContext,
+               UA_UInt32 monId, void *monContext,
+               size_t nEventFields, UA_Variant *eventFields) {
     UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Notification");
+
+    /* The context should point to the monId on the stack */
+    UA_assert(*(UA_UInt32*)monContext == monId);
+
     for(size_t i = 0; i < nEventFields; ++i) {
-        if (UA_Variant_hasScalarType(&eventFields[i], &UA_TYPES[UA_TYPES_UINT16])) {
+        if(UA_Variant_hasScalarType(&eventFields[i], &UA_TYPES[UA_TYPES_UINT16])) {
             UA_UInt16 severity = *(UA_UInt16 *)eventFields[i].data;
             UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Severity: %u", severity);
         } else if (UA_Variant_hasScalarType(&eventFields[i], &UA_TYPES[UA_TYPES_LOCALIZEDTEXT])) {
@@ -45,7 +65,8 @@ setupSelectClauses(void) {
 
     selectClauses[0].typeDefinitionId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE);
     selectClauses[0].browsePathSize = 1;
-    selectClauses[0].browsePath = (UA_QualifiedName*)UA_Array_new(selectClauses[0].browsePathSize, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
+    selectClauses[0].browsePath = (UA_QualifiedName*)
+        UA_Array_new(selectClauses[0].browsePathSize, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
     if(!selectClauses[0].browsePath) {
         UA_SimpleAttributeOperand_delete(selectClauses);
         return NULL;
@@ -55,7 +76,8 @@ setupSelectClauses(void) {
 
     selectClauses[1].typeDefinitionId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE);
     selectClauses[1].browsePathSize = 1;
-    selectClauses[1].browsePath = (UA_QualifiedName*)UA_Array_new(selectClauses[1].browsePathSize, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
+    selectClauses[1].browsePath = (UA_QualifiedName*)
+        UA_Array_new(selectClauses[1].browsePathSize, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
     if(!selectClauses[1].browsePath) {
         UA_SimpleAttributeOperand_deleteMembers(selectClauses);
         UA_SimpleAttributeOperand_delete(selectClauses);
@@ -73,9 +95,16 @@ int main(int argc, char *argv[]) {
     signal(SIGINT, stopHandler);
     signal(SIGTERM, stopHandler);
 
+    if(argc < 2) {
+        printf("Usage: tutorial_client_events <opc.tcp://server-url>\n");
+        return 1;
+    }
+
     UA_Client *client = UA_Client_new(UA_ClientConfig_default);
 
-    UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://uademo.prosysopc.com:53530/OPCUA/SimulationServer");
+    /* opc.tcp://uademo.prosysopc.com:53530/OPCUA/SimulationServer */
+    /* opc.tcp://opcua.demo-this.com:51210/UA/SampleServer */
+    UA_StatusCode retval = UA_Client_connect(client, argv[1]);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_Client_delete(client);
         return (int)retval;
@@ -83,49 +112,62 @@ int main(int argc, char *argv[]) {
 
 #ifdef UA_ENABLE_SUBSCRIPTIONS
     /* Create a subscription */
-    UA_UInt32 subId = 0;
-    retval = UA_Client_Subscriptions_new(client, UA_SubscriptionSettings_default, &subId);
-    if(!subId) {
+    UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default();
+    UA_CreateSubscriptionResponse response = UA_Client_Subscriptions_create(client, request,
+                                                                            NULL, NULL, NULL);
+    if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
         UA_Client_disconnect(client);
         UA_Client_delete(client);
         return (int)retval;
     }
+    UA_UInt32 subId = response.subscriptionId;
     UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Create subscription succeeded, id %u", subId);
 
     /* Add a MonitoredItem */
-    UA_NodeId monitorThis = UA_NODEID_NUMERIC(0, 2253); // Root->Objects->Server
+    UA_MonitoredItemCreateRequest item;
+    UA_MonitoredItemCreateRequest_init(&item);
+    item.itemToMonitor.nodeId = UA_NODEID_NUMERIC(0, 2253); // Root->Objects->Server
+    item.itemToMonitor.attributeId = UA_ATTRIBUTEID_EVENTNOTIFIER;
+    item.monitoringMode = UA_MONITORINGMODE_REPORTING;
+
+    UA_EventFilter filter;
+    UA_EventFilter_init(&filter);
+    filter.selectClauses = setupSelectClauses();
+    filter.selectClausesSize = nSelectClauses;
+
+    item.requestedParameters.filter.encoding = UA_EXTENSIONOBJECT_DECODED;
+    item.requestedParameters.filter.content.decoded.data = &filter;
+    item.requestedParameters.filter.content.decoded.type = &UA_TYPES[UA_TYPES_EVENTFILTER];
+
     UA_UInt32 monId = 0;
 
-    UA_SimpleAttributeOperand *selectClauses = setupSelectClauses();
-    if(!selectClauses){
-        UA_Client_Subscriptions_remove(client, subId);
-        UA_Client_disconnect(client);
-        UA_Client_delete(client);
-        return (int)UA_STATUSCODE_BADOUTOFMEMORY;
+    UA_MonitoredItemCreateResult result =
+        UA_Client_MonitoredItems_createEvent(client, subId,
+                                             UA_TIMESTAMPSTORETURN_BOTH, item,
+                                             &monId, handler_events, NULL);
+
+    if(result.statusCode != UA_STATUSCODE_GOOD) {
+        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
+                    "Could not add the MonitoredItem with %s", UA_StatusCode_name(retval));
+        goto cleanup;
+    } else {
+        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
+                    "Monitoring 'Root->Objects->Server', id %u", response.subscriptionId);
     }
 
-    UA_Client_Subscriptions_addMonitoredEvent(client, subId, monitorThis, UA_ATTRIBUTEID_EVENTNOTIFIER,
-                                              selectClauses, nSelectClauses,
-                                              NULL, 0,
-                                              &handler_events, NULL, &monId);
-    if (!monId) {
-        UA_Array_delete(selectClauses, nSelectClauses, &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND]);
-        UA_Client_Subscriptions_remove(client, subId);
-        UA_Client_disconnect(client);
-        UA_Client_delete(client);
-        return (int)retval;
-    }
-    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Monitoring 'Root->Objects->Server', id %u", subId);
+    monId = result.monitoredItemId;
 
-    while (running)
-        UA_Client_Subscriptions_manuallySendPublishRequest(client);
+    while(running)
+        UA_Client_runAsync(client, 100);
 
     /* Delete the subscription */
-    if(!UA_Client_Subscriptions_remove(client, subId))
-        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Subscription removed");
+ cleanup:
+    UA_MonitoredItemCreateResult_deleteMembers(&result);
+    UA_Client_Subscriptions_deleteSingle(client, response.subscriptionId);
+    UA_Array_delete(filter.selectClauses, nSelectClauses, &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND]);
 #endif
 
     UA_Client_disconnect(client);
     UA_Client_delete(client);
-    return (int) UA_STATUSCODE_GOOD;
+    return (int) retval;
 }

+ 7 - 8
examples/tutorial_client_firststeps.c

@@ -25,16 +25,15 @@ int main(void) {
     UA_Variant_init(&value);
 
     /* NodeId of the variable holding the current time */
-    const UA_NodeId nodeId =
-        UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
-
+    const UA_NodeId nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
     retval = UA_Client_readValueAttribute(client, nodeId, &value);
+
     if(retval == UA_STATUSCODE_GOOD &&
        UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_DATETIME])) {
-        UA_DateTime raw_date = *(UA_DateTime*)value.data;
-        UA_String string_date = UA_DateTime_toString(raw_date);
-        printf("string date is: %.*s\n", (int)string_date.length, string_date.data);
-        UA_String_deleteMembers(&string_date);
+        UA_DateTime raw_date = *(UA_DateTime *) value.data;
+        UA_DateTimeStruct dts = UA_DateTime_toStruct(raw_date);
+        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "date is: %u-%u-%u %u:%u:%u.%03u\n",
+                    dts.day, dts.month, dts.year, dts.hour, dts.min, dts.sec, dts.milliSec);
     }
 
     /* Clean up */
@@ -60,7 +59,7 @@ int main(void) {
  * ^^^^^^^^^^^^^
  *
  * - Try to connect to some other OPC UA server by changing
- *   ``opc.tcp://localhost:16664`` to an appropriate address (remember that the
+ *   ``opc.tcp://localhost:4840`` to an appropriate address (remember that the
  *   queried node is contained in any OPC UA server).
  *
  * - Try to set the value of the variable node (ns=1,i="the.answer") containing

+ 1 - 1
examples/tutorial_datatypes.c

@@ -47,7 +47,7 @@ variables_basic(void) {
     UA_String_deleteMembers(&s4);
     if(!eq)
         return;
-    
+
     /* Structured Type */
     UA_CallRequest cr;
     UA_init(&cr, &UA_TYPES[UA_TYPES_CALLREQUEST]); /* Generic method */

+ 4 - 4
examples/tutorial_server_datasource.c

@@ -10,7 +10,7 @@
  * near the physical process and clients consuming the data at runtime. In the
  * previous tutorial, we saw how to add variables to an OPC UA information
  * model. This tutorial shows how to connect a variable to runtime information,
- * for example from measurements of a physical process. For simplicty, we take
+ * for example from measurements of a physical process. For simplicity, we take
  * the system clock as the underlying "process".
  *
  * The following code snippets are each concerned with a different way of
@@ -49,7 +49,7 @@ addCurrentTimeVariable(UA_Server *server) {
     UA_QualifiedName currentName = UA_QUALIFIEDNAME(1, "current-time");
     UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
-    UA_NodeId variableTypeNodeId = UA_NODEID_NULL;
+    UA_NodeId variableTypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
     UA_Server_addVariableNode(server, currentNodeId, parentNodeId,
                               parentReferenceNodeId, currentName,
                               variableTypeNodeId, attr, NULL, NULL);
@@ -70,7 +70,7 @@ addCurrentTimeVariable(UA_Server *server) {
 static void
 beforeReadTime(UA_Server *server,
                const UA_NodeId *sessionId, void *sessionContext,
-               const UA_NodeId *nodeid, void *nodeContext, 
+               const UA_NodeId *nodeid, void *nodeContext,
                const UA_NumericRange *range, const UA_DataValue *data) {
     UA_DateTime now = UA_DateTime_now();
     UA_Variant value;
@@ -140,7 +140,7 @@ addCurrentTimeDataSourceVariable(UA_Server *server) {
     UA_QualifiedName currentName = UA_QUALIFIEDNAME(1, "current-time-datasource");
     UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
-    UA_NodeId variableTypeNodeId = UA_NODEID_NULL;
+    UA_NodeId variableTypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
 
     UA_DataSource timeDataSource;
     timeDataSource.read = readCurrentTime;

+ 1 - 1
examples/tutorial_server_firststeps.c

@@ -71,7 +71,7 @@ int main(void) {
  * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  *
  * *open62541* provides a flexible framework for building OPC UA servers and
- * clients. The goals is to have a core library that accomodates for all use
+ * clients. The goals is to have a core library that accommodates for all use
  * cases and runs on all platforms. Users can then adjust the library to fit
  * their use case via configuration and by developing (platform-specific)
  * plugins. The core library is based on C99 only and does not even require

+ 11 - 11
examples/tutorial_server_object.c

@@ -66,7 +66,7 @@ manuallyDefinePump(UA_Server *server) {
     UA_Server_addObjectNode(server, UA_NODEID_NULL,
                             UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                             UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                            UA_QUALIFIEDNAME(1, "Pump (Manual)"), UA_NODEID_NULL,
+                            UA_QUALIFIEDNAME(1, "Pump (Manual)"), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
                             oAttr, NULL, &pumpId);
 
     UA_VariableAttributes mnAttr = UA_VariableAttributes_default;
@@ -76,7 +76,7 @@ manuallyDefinePump(UA_Server *server) {
     UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpId,
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_QUALIFIEDNAME(1, "ManufacturerName"),
-                              UA_NODEID_NULL, mnAttr, NULL, NULL);
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), mnAttr, NULL, NULL);
 
     UA_VariableAttributes modelAttr = UA_VariableAttributes_default;
     UA_String modelName = UA_STRING("Mega Pump 3000");
@@ -85,7 +85,7 @@ manuallyDefinePump(UA_Server *server) {
     UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpId,
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_QUALIFIEDNAME(1, "ModelName"),
-                              UA_NODEID_NULL, modelAttr, NULL, NULL);
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), modelAttr, NULL, NULL);
 
     UA_VariableAttributes statusAttr = UA_VariableAttributes_default;
     UA_Boolean status = true;
@@ -94,7 +94,7 @@ manuallyDefinePump(UA_Server *server) {
     UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpId,
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_QUALIFIEDNAME(1, "Status"),
-                              UA_NODEID_NULL, statusAttr, NULL, NULL);
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), statusAttr, NULL, NULL);
 
     UA_VariableAttributes rpmAttr = UA_VariableAttributes_default;
     UA_Double rpm = 50.0;
@@ -103,7 +103,7 @@ manuallyDefinePump(UA_Server *server) {
     UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpId,
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_QUALIFIEDNAME(1, "MotorRPMs"),
-                              UA_NODEID_NULL, rpmAttr, NULL, NULL);
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), rpmAttr, NULL, NULL);
 }
 
 /**
@@ -183,7 +183,7 @@ defineObjectTypes(UA_Server *server) {
     UA_Server_addVariableNode(server, UA_NODEID_NULL, deviceTypeId,
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_QUALIFIEDNAME(1, "ManufacturerName"),
-                              UA_NODEID_NULL, mnAttr, NULL, &manufacturerNameId);
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), mnAttr, NULL, &manufacturerNameId);
     /* Make the manufacturer name mandatory */
     UA_Server_addReference(server, manufacturerNameId,
                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE),
@@ -195,7 +195,7 @@ defineObjectTypes(UA_Server *server) {
     UA_Server_addVariableNode(server, UA_NODEID_NULL, deviceTypeId,
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_QUALIFIEDNAME(1, "ModelName"),
-                              UA_NODEID_NULL, modelAttr, NULL, NULL);
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), modelAttr, NULL, NULL);
 
     /* Define the object type for "Pump" */
     UA_ObjectTypeAttributes ptAttr = UA_ObjectTypeAttributes_default;
@@ -212,7 +212,7 @@ defineObjectTypes(UA_Server *server) {
     UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpTypeId,
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_QUALIFIEDNAME(1, "Status"),
-                              UA_NODEID_NULL, statusAttr, NULL, &statusId);
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), statusAttr, NULL, &statusId);
     /* Make the status variable mandatory */
     UA_Server_addReference(server, statusId,
                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE),
@@ -224,7 +224,7 @@ defineObjectTypes(UA_Server *server) {
     UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpTypeId,
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_QUALIFIEDNAME(1, "MotorRPMs"),
-                              UA_NODEID_NULL, rpmAttr, NULL, NULL);
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), rpmAttr, NULL, NULL);
 }
 
 /**
@@ -270,13 +270,13 @@ pumpTypeConstructor(UA_Server *server,
     rpe.isInverse = false;
     rpe.includeSubtypes = false;
     rpe.targetName = UA_QUALIFIEDNAME(1, "Status");
-    
+
     UA_BrowsePath bp;
     UA_BrowsePath_init(&bp);
     bp.startingNode = *nodeId;
     bp.relativePath.elementsSize = 1;
     bp.relativePath.elements = &rpe;
-    
+
     UA_BrowsePathResult bpr =
         UA_Server_translateBrowsePathToNodeIds(server, &bp);
     if(bpr.statusCode != UA_STATUSCODE_GOOD ||

+ 1 - 1
examples/tutorial_server_variable.c

@@ -36,7 +36,7 @@ addVariable(UA_Server *server) {
     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
     UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
                               parentReferenceNodeId, myIntegerName,
-                              UA_NODEID_NULL, attr, NULL, NULL);
+                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr, NULL, NULL);
 }
 
 /**

+ 1 - 1
examples/tutorial_server_variabletype.c

@@ -89,7 +89,7 @@ addVariableFail(UA_Server *server) {
     UA_String s = UA_STRING("2dpoint?");
     UA_Variant_setScalar(&vAttr.value, &s, &UA_TYPES[UA_TYPES_STRING]);
 
-    /* Add the node */
+    /* Add the node will return UA_STATUSCODE_BADTYPEMISMATCH*/
     UA_Server_addVariableNode(server, UA_NODEID_NULL,
                               UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),

+ 98 - 116
include/ua_client.h

@@ -1,6 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2015-2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2015-2016 (c) Sten Grüner
+ *    Copyright 2015-2016 (c) Chris Iatrou
+ *    Copyright 2015-2017 (c) Florian Palm
+ *    Copyright 2015 (c) Holger Jeromin
+ *    Copyright 2015 (c) Oleksiy Vasylyev
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ *    Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB
+ *    Copyright 2018 (c) Thomas Stalder
+ */
 
 #ifndef UA_CLIENT_H_
 #define UA_CLIENT_H_
@@ -14,6 +25,7 @@ extern "C" {
 #include "ua_types_generated_handling.h"
 #include "ua_plugin_network.h"
 #include "ua_plugin_log.h"
+#include "ua_client_config.h"
 
 /**
  * .. _client:
@@ -32,40 +44,12 @@ extern "C" {
  * `UA_Client_Subscriptions_manuallySendPublishRequest`. See also :ref:`here
  * <client-subscriptions>`.
  *
- * Client Configuration
- * -------------------- */
-
-typedef struct UA_ClientConfig {
-    UA_UInt32 timeout;               /* Sync response timeout in ms */
-    UA_UInt32 secureChannelLifeTime; /* Lifetime in ms (then the channel needs
-                                        to be renewed) */
-    UA_Logger logger;
-    UA_ConnectionConfig localConnectionConfig;
-    UA_ConnectClientConnection connectionFunc;
-
-    /* Custom DataTypes */
-    size_t customDataTypesSize;
-    const UA_DataType *customDataTypes;
-} UA_ClientConfig;
-
-/**
+ *
+ * .. include:: client_config.rst
+ *
  * Client Lifecycle
  * ---------------- */
 
-typedef enum {
-    UA_CLIENTSTATE_DISCONNECTED,        /* The client is not connected */
-    UA_CLIENTSTATE_CONNECTED,           /* A TCP connection to the server is open */
-    UA_CLIENTSTATE_SECURECHANNEL,       /* A SecureChannel to the server is open */
-    UA_CLIENTSTATE_SESSION,             /* A session with the server is open */
-    UA_CLIENTSTATE_SESSION_DISCONNECTED /* A session with the server is open.
-                                         * But the SecureChannel was lost. Try
-                                         * to establish a new SecureChannel and
-                                         * reattach the existing session. */
-} UA_ClientState;
-
-struct UA_Client;
-typedef struct UA_Client UA_Client;
-
 /* Create a new client */
 UA_Client UA_EXPORT *
 UA_Client_new(UA_ClientConfig config);
@@ -74,6 +58,10 @@ UA_Client_new(UA_ClientConfig config);
 UA_ClientState UA_EXPORT
 UA_Client_getState(UA_Client *client);
 
+/* Get the client context */
+void UA_EXPORT *
+UA_Client_getContext(UA_Client *client);
+
 /* Reset a client */
 void UA_EXPORT
 UA_Client_reset(UA_Client *client);
@@ -89,7 +77,7 @@ UA_Client_delete(UA_Client *client);
 /* Connect to the server
  *
  * @param client to use
- * @param endpointURL to connect (for example "opc.tcp://localhost:16664")
+ * @param endpointURL to connect (for example "opc.tcp://localhost:4840")
  * @return Indicates whether the operation succeeded or returns an error code */
 UA_StatusCode UA_EXPORT
 UA_Client_connect(UA_Client *client, const char *endpointUrl);
@@ -97,7 +85,7 @@ UA_Client_connect(UA_Client *client, const char *endpointUrl);
 /* Connect to the selected server with the given username and password
  *
  * @param client to use
- * @param endpointURL to connect (for example "opc.tcp://localhost:16664")
+ * @param endpointURL to connect (for example "opc.tcp://localhost:4840")
  * @param username
  * @param password
  * @return Indicates whether the operation succeeded or returns an error code */
@@ -105,10 +93,14 @@ UA_StatusCode UA_EXPORT
 UA_Client_connect_username(UA_Client *client, const char *endpointUrl,
                            const char *username, const char *password);
 
-/* Close a connection to the selected server */
+/* Disconnect and close a connection to the selected server */
 UA_StatusCode UA_EXPORT
 UA_Client_disconnect(UA_Client *client);
 
+/* Close a connection to the selected server */
+UA_StatusCode UA_EXPORT
+UA_Client_close(UA_Client *client);
+
 /* Renew the underlying secure channel */
 UA_StatusCode UA_EXPORT
 UA_Client_manuallyRenewSecureChannel(UA_Client *client);
@@ -121,7 +113,7 @@ UA_Client_manuallyRenewSecureChannel(UA_Client *client);
  *
  * @param client to use. Must be connected to the same endpoint given in
  *        serverUrl or otherwise in disconnected state.
- * @param serverUrl url to connect (for example "opc.tcp://localhost:16664")
+ * @param serverUrl url to connect (for example "opc.tcp://localhost:4840")
  * @param endpointDescriptionsSize size of the array of endpoint descriptions
  * @param endpointDescriptions array of endpoint descriptions that is allocated
  *        by the function (you need to free manually)
@@ -143,7 +135,7 @@ UA_Client_getEndpoints(UA_Client *client, const char *serverUrl,
  *
  * @param client to use. Must be connected to the same endpoint given in
  *        serverUrl or otherwise in disconnected state.
- * @param serverUrl url to connect (for example "opc.tcp://localhost:16664")
+ * @param serverUrl url to connect (for example "opc.tcp://localhost:4840")
  * @param serverUrisSize Optional filter for specific server uris
  * @param serverUris Optional filter for specific server uris
  * @param localeIdsSize Optional indication which locale you prefer
@@ -162,7 +154,7 @@ UA_Client_findServers(UA_Client *client, const char *serverUrl,
  *
  * @param client to use. Must be connected to the same endpoint given in
  * serverUrl or otherwise in disconnected state.
- * @param serverUrl url to connect (for example "opc.tcp://localhost:16664")
+ * @param serverUrl url to connect (for example "opc.tcp://localhost:4840")
  * @param startingRecordId optional. Only return the records with an ID higher
  *        or equal the given. Can be used for pagination to only get a subset of
  *        the full list
@@ -187,6 +179,7 @@ UA_Client_findServersOnNetwork(UA_Client *client, const char *serverUrl,
  *
  * Services
  * --------
+ *
  * The raw OPC UA services are exposed to the client. But most of them time, it
  * is better to use the convenience functions from ``ua_client_highlevel.h``
  * that wrap the raw services. */
@@ -196,7 +189,7 @@ __UA_Client_Service(UA_Client *client, const void *request,
                     const UA_DataType *requestType, void *response,
                     const UA_DataType *responseType);
 
-/**
+/*
  * Attribute Service Set
  * ^^^^^^^^^^^^^^^^^^^^^ */
 static UA_INLINE UA_ReadResponse
@@ -215,7 +208,7 @@ UA_Client_Service_write(UA_Client *client, const UA_WriteRequest request) {
     return response;
 }
 
-/**
+/*
  * Method Service Set
  * ^^^^^^^^^^^^^^^^^^ */
 #ifdef UA_ENABLE_METHODCALLS
@@ -228,7 +221,7 @@ UA_Client_Service_call(UA_Client *client, const UA_CallRequest request) {
 }
 #endif
 
-/**
+/*
  * NodeManagement Service Set
  * ^^^^^^^^^^^^^^^^^^^^^^^^^^ */
 static UA_INLINE UA_AddNodesResponse
@@ -266,7 +259,7 @@ UA_Client_Service_deleteReferences(UA_Client *client,
     return response;
 }
 
-/**
+/*
  * View Service Set
  * ^^^^^^^^^^^^^^^^ */
 static UA_INLINE UA_BrowseResponse
@@ -316,7 +309,7 @@ UA_Client_Service_unregisterNodes(UA_Client *client,
     return response;
 }
 
-/**
+/*
  * Query Service Set
  * ^^^^^^^^^^^^^^^^^ */
 static UA_INLINE UA_QueryFirstResponse
@@ -337,74 +330,6 @@ UA_Client_Service_queryNext(UA_Client *client,
     return response;
 }
 
-#ifdef UA_ENABLE_SUBSCRIPTIONS
-
-/**
- * MonitoredItem Service Set
- * ^^^^^^^^^^^^^^^^^^^^^^^^^ */
-static UA_INLINE UA_CreateMonitoredItemsResponse
-UA_Client_Service_createMonitoredItems(UA_Client *client,
-                                 const UA_CreateMonitoredItemsRequest request) {
-    UA_CreateMonitoredItemsResponse response;
-    __UA_Client_Service(client, &request,
-                        &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSREQUEST], &response,
-                        &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSRESPONSE]);
-    return response;
-}
-
-static UA_INLINE UA_DeleteMonitoredItemsResponse
-UA_Client_Service_deleteMonitoredItems(UA_Client *client,
-                                 const UA_DeleteMonitoredItemsRequest request) {
-    UA_DeleteMonitoredItemsResponse response;
-    __UA_Client_Service(client, &request,
-                        &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSREQUEST], &response,
-                        &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSRESPONSE]);
-    return response;
-}
-
-/**
- * Subscription Service Set
- * ^^^^^^^^^^^^^^^^^^^^^^^^ */
-static UA_INLINE UA_CreateSubscriptionResponse
-UA_Client_Service_createSubscription(UA_Client *client,
-                                   const UA_CreateSubscriptionRequest request) {
-    UA_CreateSubscriptionResponse response;
-    __UA_Client_Service(client, &request,
-                        &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONREQUEST], &response,
-                        &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONRESPONSE]);
-    return response;
-}
-
-static UA_INLINE UA_ModifySubscriptionResponse
-UA_Client_Service_modifySubscription(UA_Client *client,
-                                   const UA_ModifySubscriptionRequest request) {
-    UA_ModifySubscriptionResponse response;
-    __UA_Client_Service(client, &request,
-                        &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONREQUEST], &response,
-                        &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONRESPONSE]);
-    return response;
-}
-
-static UA_INLINE UA_DeleteSubscriptionsResponse
-UA_Client_Service_deleteSubscriptions(UA_Client *client,
-                                  const UA_DeleteSubscriptionsRequest request) {
-    UA_DeleteSubscriptionsResponse response;
-    __UA_Client_Service(client, &request,
-                        &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSREQUEST], &response,
-                        &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSRESPONSE]);
-    return response;
-}
-
-static UA_INLINE UA_PublishResponse
-UA_Client_Service_publish(UA_Client *client, const UA_PublishRequest request) {
-    UA_PublishResponse response;
-    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_PUBLISHREQUEST],
-                        &response, &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
-    return response;
-}
-
-#endif
-
 /**
  * .. _client-async-services:
  *
@@ -414,11 +339,30 @@ UA_Client_Service_publish(UA_Client *client, const UA_PublishRequest request) {
  * be made without waiting for a response first. Responess may come in a
  * different ordering. */
 
+/* Listen on the network and process arriving asynchronous responses in the
+ * background. Internal housekeeping and subscription management is done as
+ * well. */
+UA_StatusCode UA_EXPORT
+UA_Client_runAsync(UA_Client *client, UA_UInt16 timeout);
+
 typedef void
 (*UA_ClientAsyncServiceCallback)(UA_Client *client, void *userdata,
-                                 UA_UInt32 requestId, const void *response);
+                                 UA_UInt32 requestId, void *response,
+                                 const UA_DataType *responseType);
 
-/* Don't use this function. Use the type versions below instead. */
+/* Use the type versions of this method. See below. However, the general
+ * mechanism of async service calls is explained here.
+ *
+ * We say that an async service call has been dispatched once this method
+ * returns UA_STATUSCODE_GOOD. If there is an error after an async service has
+ * been dispatched, the callback is called with an "empty" response where the
+ * statusCode has been set accordingly. This is also done if the client is
+ * shutting down and the list of dispatched async services is emptied.
+ *
+ * The statusCode received when the client is shutting down is
+ * UA_STATUSCODE_BADSHUTDOWN.
+ *
+ * The userdata and requestId arguments can be NULL. */
 UA_StatusCode UA_EXPORT
 __UA_Client_AsyncService(UA_Client *client, const void *request,
                          const UA_DataType *requestType,
@@ -426,13 +370,51 @@ __UA_Client_AsyncService(UA_Client *client, const void *request,
                          const UA_DataType *responseType,
                          void *userdata, UA_UInt32 *requestId);
 
-UA_StatusCode UA_EXPORT
-UA_Client_runAsync(UA_Client *client, UA_UInt16 timeout);
+static UA_INLINE UA_StatusCode
+UA_Client_AsyncService_read(UA_Client *client, const UA_ReadRequest *request,
+                            UA_ClientAsyncServiceCallback callback,
+                            void *userdata, UA_UInt32 *requestId) {
+    return __UA_Client_AsyncService(client, (const void*)request,
+                                    &UA_TYPES[UA_TYPES_READREQUEST], callback,
+                                    &UA_TYPES[UA_TYPES_READRESPONSE],
+                                    userdata, requestId);
+}
+
+static UA_INLINE UA_StatusCode
+UA_Client_AsyncService_write(UA_Client *client, const UA_WriteRequest *request,
+                             UA_ClientAsyncServiceCallback callback,
+                             void *userdata, UA_UInt32 *requestId) {
+    return __UA_Client_AsyncService(client, (const void*)request,
+                                    &UA_TYPES[UA_TYPES_WRITEREQUEST], callback, 
+                                    &UA_TYPES[UA_TYPES_WRITERESPONSE],
+                                    userdata, requestId);
+}
+
+static UA_INLINE UA_StatusCode
+UA_Client_AsyncService_call(UA_Client *client, const UA_CallRequest *request,
+                            UA_ClientAsyncServiceCallback callback,
+                            void *userdata, UA_UInt32 *requestId) {
+    return __UA_Client_AsyncService(client, (const void*)request,
+                                    &UA_TYPES[UA_TYPES_CALLREQUEST], callback,
+                                    &UA_TYPES[UA_TYPES_CALLRESPONSE],
+                                    userdata, requestId);
+}
+
+static UA_INLINE UA_StatusCode
+UA_Client_AsyncService_browse(UA_Client *client, const UA_BrowseRequest *request,
+                              UA_ClientAsyncServiceCallback callback,
+                              void *userdata, UA_UInt32 *requestId) {
+    return __UA_Client_AsyncService(client, (const void*)request,
+                                    &UA_TYPES[UA_TYPES_BROWSEREQUEST], callback,
+                                    &UA_TYPES[UA_TYPES_BROWSERESPONSE],
+                                    userdata, requestId);
+}
 
 /**
  * .. toctree::
  *
- *    client_highlevel */
+ *    client_highlevel
+ *    client_subscriptions */
 
 #ifdef __cplusplus
 } // extern "C"

+ 109 - 0
include/ua_client_config.h

@@ -0,0 +1,109 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ *    Copyright 2018 (c) Stefan Profanter, fortiss GmbH
+ */
+
+#ifndef UA_CLIENT_CONFIG_H
+#define UA_CLIENT_CONFIG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ua_plugin_network.h"
+
+/**
+ * .. _client-config:
+ *
+ * Client Configuration
+ * --------------------
+ *
+ * The client configuration is used for setting connection parameters and
+ * additional settings used by the client.
+ * The configuration should not be modified after it is passed to a client.
+ * Currently, only one client can use a configuration at a time.
+ *
+ * Examples for configurations are provided in the ``/plugins`` folder.
+ * The usual usage is as follows:
+ *
+ * 1. Create a client configuration with default settings as a starting point
+ * 2. Modifiy the configuration, e.g. modifying the timeout
+ * 3. Instantiate a client with it
+ * 4. After shutdown of the client, clean up the configuration (free memory)
+ *
+ * The :ref:`tutorials` provide a good starting point for this. */
+
+typedef enum {
+    UA_CLIENTSTATE_DISCONNECTED,        /* The client is disconnected */
+    UA_CLIENTSTATE_CONNECTED,           /* A TCP connection to the server is open */
+    UA_CLIENTSTATE_SECURECHANNEL,       /* A SecureChannel to the server is open */
+    UA_CLIENTSTATE_SESSION,             /* A session with the server is open */
+    UA_CLIENTSTATE_SESSION_RENEWED      /* A session with the server is open (renewed) */
+} UA_ClientState;
+
+
+struct UA_Client;
+typedef struct UA_Client UA_Client;
+
+/**
+ * Client Lifecycle callback
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^ */
+
+typedef void (*UA_ClientStateCallback)(UA_Client *client, UA_ClientState clientState);
+
+/**
+ * Subscription Inactivity callback
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
+
+#ifdef UA_ENABLE_SUBSCRIPTIONS
+typedef void (*UA_SubscriptionInactivityCallback)(UA_Client *client, UA_UInt32 subscriptionId, void *subContext);
+#endif
+
+/**
+ * Client Configuration Data
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^ */
+
+typedef struct UA_ClientConfig {
+    UA_UInt32 timeout;               /* Sync response timeout in ms */
+    UA_UInt32 secureChannelLifeTime; /* Lifetime in ms (then the channel needs
+                                        to be renewed) */
+    UA_Logger logger;
+    UA_ConnectionConfig localConnectionConfig;
+    UA_ConnectClientConnection connectionFunc;
+
+    /* Custom DataTypes */
+    size_t customDataTypesSize;
+    const UA_DataType *customDataTypes;
+
+    /* Callback function */
+    UA_ClientStateCallback stateCallback;
+#ifdef UA_ENABLE_SUBSCRIPTIONS
+    UA_SubscriptionInactivityCallback subscriptionInactivityCallback;
+#endif
+
+    void *clientContext;
+
+#ifdef UA_ENABLE_SUBSCRIPTIONS
+    /* number of PublishResponse standing in the sever */
+    /* 0 = background task disabled                    */
+    UA_UInt16 outStandingPublishRequests;
+#endif
+} UA_ClientConfig;
+
+
+/* Get the client configuration from the configuration plugin. Used by the
+ * server when it needs client functionality to register to a discovery server
+ * or when the server needs to create a client for other purposes
+ *
+ * @return The client configuration structure */
+UA_ClientConfig UA_EXPORT
+UA_Server_getClientConfig(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* UA_CLIENT_CONFIG_H */

+ 11 - 73
include/ua_client_highlevel.h

@@ -1,6 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2015-2018 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2015 (c) Oleksiy Vasylyev
+ *    Copyright 2017 (c) Florian Palm
+ *    Copyright 2016 (c) Chris Iatrou
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ *    Copyright 2017 (c) Frank Meerkötter
+ */
 
 #ifndef UA_CLIENT_HIGHLEVEL_H_
 #define UA_CLIENT_HIGHLEVEL_H_
@@ -554,74 +562,6 @@ UA_Client_addMethodNode(UA_Client *client, const UA_NodeId requestedNewNodeId,
                                &UA_TYPES[UA_TYPES_METHODATTRIBUTES], outNewNodeId);
 }
 
-/**
- * .. _client-subscriptions:
- *
- * Subscriptions Handling
- * ^^^^^^^^^^^^^^^^^^^^^^
- * At this time, the client does not yet contain its own thread or event-driven
- * main-loop. So the client will not perform any actions automatically in the
- * background. This is especially relevant for subscriptions. The user will have
- * to periodically call `UA_Client_Subscriptions_manuallySendPublishRequest`.
- * See also :ref:`here <client-subscriptions>`. */
-#ifdef UA_ENABLE_SUBSCRIPTIONS
-
-typedef struct {
-    UA_Double requestedPublishingInterval;
-    UA_UInt32 requestedLifetimeCount;
-    UA_UInt32 requestedMaxKeepAliveCount;
-    UA_UInt32 maxNotificationsPerPublish;
-    UA_Boolean publishingEnabled;
-    UA_Byte priority;
-} UA_SubscriptionSettings;
-
-extern const UA_EXPORT UA_SubscriptionSettings UA_SubscriptionSettings_default;
-
-UA_StatusCode UA_EXPORT
-UA_Client_Subscriptions_new(UA_Client *client, UA_SubscriptionSettings settings,
-                            UA_UInt32 *newSubscriptionId);
-
-UA_StatusCode UA_EXPORT
-UA_Client_Subscriptions_remove(UA_Client *client, UA_UInt32 subscriptionId);
-
-UA_StatusCode UA_EXPORT
-UA_Client_Subscriptions_manuallySendPublishRequest(UA_Client *client);
-
-typedef void (*UA_MonitoredEventHandlingFunction)(const UA_UInt32 monId,
-                                                  const size_t nEventFields,
-                                                  const UA_Variant *eventFields,
-                                                  void *context);
-
-UA_StatusCode UA_EXPORT
-UA_Client_Subscriptions_addMonitoredEvent(UA_Client *client, const UA_UInt32 subscriptionId,
-                                          const UA_NodeId nodeId, const UA_UInt32 attributeID,
-                                          UA_SimpleAttributeOperand *selectClause,
-                                          const size_t nSelectClauses,
-                                          UA_ContentFilterElement *whereClause,
-                                          const size_t nWhereClauses,
-                                          const UA_MonitoredEventHandlingFunction hf,
-                                          void *hfContext, UA_UInt32 *newMonitoredItemId);
-
-typedef void (*UA_MonitoredItemHandlingFunction)(UA_UInt32 monId,
-                                                 UA_DataValue *value,
-                                                 void *context);
-
-UA_StatusCode UA_EXPORT
-UA_Client_Subscriptions_addMonitoredItem(UA_Client *client,
-                                         UA_UInt32 subscriptionId,
-                                         UA_NodeId nodeId, UA_UInt32 attributeID,
-                                         UA_MonitoredItemHandlingFunction hf,
-                                         void *hfContext,
-                                         UA_UInt32 *newMonitoredItemId,
-                                         UA_Double samplingInterval);
-
-UA_StatusCode UA_EXPORT
-UA_Client_Subscriptions_removeMonitoredItem(UA_Client *client,
-                                            UA_UInt32 subscriptionId,
-                                            UA_UInt32 monitoredItemId);
-
-#endif
-
 /**
  * Misc Highlevel Functionality
  * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
@@ -640,10 +580,8 @@ UA_Client_NamespaceGetIndex(UA_Client *client, UA_String *namespaceUri,
 #define HAVE_NODEITER_CALLBACK
 /* Iterate over all nodes referenced by parentNodeId by calling the callback
    function for each child node */
-typedef UA_StatusCode (*UA_NodeIteratorCallback)(UA_NodeId childId,
-                                                 UA_Boolean isInverse,
-                                                 UA_NodeId referenceTypeId,
-                                                 void *handle);
+typedef UA_StatusCode (*UA_NodeIteratorCallback)(UA_NodeId childId, UA_Boolean isInverse,
+                                                 UA_NodeId referenceTypeId, void *handle);
 #endif
 
 UA_StatusCode UA_EXPORT

+ 294 - 0
include/ua_client_subscriptions.h

@@ -0,0 +1,294 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef UA_CLIENT_SUBSCRIPTIONS_H_
+#define UA_CLIENT_SUBSCRIPTIONS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ua_client.h"
+
+#ifdef UA_ENABLE_SUBSCRIPTIONS
+
+/**
+ * .. _client-subscriptions:
+ *
+ * Subscriptions
+ * -------------
+ *
+ * Subscriptions in OPC UA are asynchronous. That is, the client sends several
+ * PublishRequests to the server. The server returns PublishResponses with
+ * notifications. But only when a notification has been generated. The client
+ * does not wait for the responses and continues normal operations.
+ *
+ * Note the difference between Subscriptions and MonitoredItems. Subscriptions
+ * are used to report back notifications. MonitoredItems are used to generate
+ * notifications. Every MonitoredItem is attached to exactly one Subscription.
+ * And a Subscription can contain many MonitoredItems.
+ *
+ * The client automatically processes PublishResponses (with a callback) in the
+ * background and keeps enough PublishRequests in transit. The PublishResponses
+ * may be recieved during a synchronous service call or in
+ * ``UA_Client_runAsync``. */
+
+/* Callbacks defined for Subscriptions */
+typedef void (*UA_Client_DeleteSubscriptionCallback)
+    (UA_Client *client, UA_UInt32 subId, void *subContext);
+
+typedef void (*UA_Client_StatusChangeNotificationCallback)
+    (UA_Client *client, UA_UInt32 subId, void *subContext,
+     UA_StatusChangeNotification *notification);
+
+/* Provides default values for a new subscription.
+ *
+ * RequestedPublishingInterval:  500.0 [ms]
+ * RequestedLifetimeCount: 10000
+ * RequestedMaxKeepAliveCount: 10
+ * MaxNotificationsPerPublish: 0 (unlimited)
+ * PublishingEnabled: true
+ * Priority: 0 */
+static UA_INLINE UA_CreateSubscriptionRequest
+UA_CreateSubscriptionRequest_default(void) {
+    UA_CreateSubscriptionRequest request;
+    UA_CreateSubscriptionRequest_init(&request);
+
+    request.requestedPublishingInterval = 500.0;
+    request.requestedLifetimeCount = 10000;
+    request.requestedMaxKeepAliveCount = 10;
+    request.maxNotificationsPerPublish = 0;
+    request.publishingEnabled = true;
+    request.priority = 0;
+    return request;
+}
+
+UA_CreateSubscriptionResponse UA_EXPORT
+UA_Client_Subscriptions_create(UA_Client *client,
+                               const UA_CreateSubscriptionRequest request,
+                               void *subscriptionContext,
+                               UA_Client_StatusChangeNotificationCallback statusChangeCallback,
+                               UA_Client_DeleteSubscriptionCallback deleteCallback);
+
+UA_ModifySubscriptionResponse UA_EXPORT
+UA_Client_Subscriptions_modify(UA_Client *client, const UA_ModifySubscriptionRequest request);
+
+UA_DeleteSubscriptionsResponse UA_EXPORT
+UA_Client_Subscriptions_delete(UA_Client *client,
+                               const UA_DeleteSubscriptionsRequest request);
+
+/* Delete a single subscription */
+UA_StatusCode UA_EXPORT
+UA_Client_Subscriptions_deleteSingle(UA_Client *client, UA_UInt32 subscriptionId);
+
+static UA_INLINE UA_SetPublishingModeResponse
+UA_Client_Subscriptions_setPublishingMode(UA_Client *client,
+                                          const UA_SetPublishingModeRequest request) {
+    UA_SetPublishingModeResponse response;
+    __UA_Client_Service(client, &request,
+                        &UA_TYPES[UA_TYPES_SETPUBLISHINGMODEREQUEST], &response,
+                        &UA_TYPES[UA_TYPES_SETPUBLISHINGMODERESPONSE]);
+    return response;
+}
+
+/**
+ * MonitoredItems
+ * --------------
+ *
+ * MonitoredItems for Events indicate the ``EventNotifier`` attribute. This
+ * indicates to the server not to monitor changes of the attribute, but to
+ * forward Event notifications from that node.
+ *
+ * During the creation of a MonitoredItem, the server may return changed
+ * adjusted parameters. Use ``UA_Client_MonitoredItem_getParameters`` to get the
+ * current parameters. */
+
+/* Provides default values for a new monitored item. */
+static UA_INLINE UA_MonitoredItemCreateRequest
+UA_MonitoredItemCreateRequest_default(UA_NodeId nodeId) {
+    UA_MonitoredItemCreateRequest request;
+    UA_MonitoredItemCreateRequest_init(&request);
+    request.itemToMonitor.nodeId = nodeId;
+    request.itemToMonitor.attributeId = UA_ATTRIBUTEID_VALUE;
+    request.monitoringMode = UA_MONITORINGMODE_REPORTING;
+    request.requestedParameters.samplingInterval = 250;
+    request.requestedParameters.discardOldest = true;
+    request.requestedParameters.queueSize = 1;
+    return request;
+}
+
+/* Callback for the deletion of a MonitoredItem */
+typedef void (*UA_Client_DeleteMonitoredItemCallback)
+    (UA_Client *client, UA_UInt32 subId, void *subContext,
+     UA_UInt32 monId, void *monContext);
+
+/* Callback for DataChange notifications */
+typedef void (*UA_Client_DataChangeNotificationCallback)
+    (UA_Client *client, UA_UInt32 subId, void *subContext,
+     UA_UInt32 monId, void *monContext,
+     UA_DataValue *value);
+
+/* Callback for Event notifications */
+typedef void (*UA_Client_EventNotificationCallback)
+    (UA_Client *client, UA_UInt32 subId, void *subContext,
+     UA_UInt32 monId, void *monContext,
+     size_t nEventFields, UA_Variant *eventFields);
+
+/* Don't use to monitor the EventNotifier attribute */
+UA_CreateMonitoredItemsResponse UA_EXPORT
+UA_Client_MonitoredItems_createDataChanges(UA_Client *client,
+            const UA_CreateMonitoredItemsRequest request, void **contexts,
+            UA_Client_DataChangeNotificationCallback *callbacks,
+            UA_Client_DeleteMonitoredItemCallback *deleteCallbacks);
+
+UA_MonitoredItemCreateResult UA_EXPORT
+UA_Client_MonitoredItems_createDataChange(UA_Client *client, UA_UInt32 subscriptionId,
+          UA_TimestampsToReturn timestampsToReturn, const UA_MonitoredItemCreateRequest item,
+          void *context, UA_Client_DataChangeNotificationCallback callback,
+          UA_Client_DeleteMonitoredItemCallback deleteCallback);
+
+/* Monitor the EventNotifier attribute only */
+UA_CreateMonitoredItemsResponse UA_EXPORT
+UA_Client_MonitoredItems_createEvents(UA_Client *client,
+            const UA_CreateMonitoredItemsRequest request, void **contexts,
+            UA_Client_EventNotificationCallback *callbacks,
+            UA_Client_DeleteMonitoredItemCallback *deleteCallback);
+
+UA_MonitoredItemCreateResult UA_EXPORT
+UA_Client_MonitoredItems_createEvent(UA_Client *client, UA_UInt32 subscriptionId,
+          UA_TimestampsToReturn timestampsToReturn, const UA_MonitoredItemCreateRequest item,
+          void *context, UA_Client_EventNotificationCallback callback,
+          UA_Client_DeleteMonitoredItemCallback deleteCallback);
+
+UA_DeleteMonitoredItemsResponse UA_EXPORT
+UA_Client_MonitoredItems_delete(UA_Client *client, const UA_DeleteMonitoredItemsRequest);
+
+UA_StatusCode UA_EXPORT
+UA_Client_MonitoredItems_deleteSingle(UA_Client *client, UA_UInt32 subscriptionId, UA_UInt32 monitoredItemId);
+
+/**
+ * The following service calls go directly to the server. The MonitoredItem settings are
+ * not stored in the client. */
+
+static UA_INLINE UA_ModifyMonitoredItemsResponse
+UA_Client_MonitoredItems_modify(UA_Client *client,
+                                const UA_ModifyMonitoredItemsRequest request) {
+    UA_ModifyMonitoredItemsResponse response;
+    __UA_Client_Service(client,
+                        &request, &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSRESPONSE]);
+    return response;
+}
+
+static UA_INLINE UA_SetMonitoringModeResponse
+UA_Client_MonitoredItems_setMonitoringMode(UA_Client *client,
+                                           const UA_SetMonitoringModeRequest request) {
+    UA_SetMonitoringModeResponse response;
+    __UA_Client_Service(client,
+                        &request, &UA_TYPES[UA_TYPES_SETMONITORINGMODEREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_SETMONITORINGMODERESPONSE]);
+    return response;
+}
+
+static UA_INLINE UA_SetTriggeringResponse
+UA_Client_MonitoredItems_setTriggering(UA_Client *client,
+                                       const UA_SetTriggeringRequest request) {
+    UA_SetTriggeringResponse response;
+    __UA_Client_Service(client,
+                        &request, &UA_TYPES[UA_TYPES_SETTRIGGERINGREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_SETTRIGGERINGRESPONSE]);
+    return response;
+}
+
+/**
+ * Deprecated API
+ * --------------
+ * The following API is kept for backwards compatibility. It will be removed in
+ * future releases. */
+
+typedef struct {
+    UA_Double requestedPublishingInterval;
+    UA_UInt32 requestedLifetimeCount;
+    UA_UInt32 requestedMaxKeepAliveCount;
+    UA_UInt32 maxNotificationsPerPublish;
+    UA_Boolean publishingEnabled;
+    UA_Byte priority;
+} UA_SubscriptionSettings;
+
+extern const UA_EXPORT UA_SubscriptionSettings UA_SubscriptionSettings_default;
+
+UA_DEPRECATED UA_StatusCode UA_EXPORT
+UA_Client_Subscriptions_new(UA_Client *client, UA_SubscriptionSettings settings,
+                            UA_UInt32 *newSubscriptionId);
+
+UA_DEPRECATED UA_StatusCode UA_EXPORT
+UA_Client_Subscriptions_remove(UA_Client *client, UA_UInt32 subscriptionId);
+
+/* Send a publish request and wait until a response to the request is processed.
+ * Note that other publish responses may be processed in the background until
+ * then. */
+UA_DEPRECATED UA_StatusCode UA_EXPORT
+UA_Client_Subscriptions_manuallySendPublishRequest(UA_Client *client);
+
+/* For monitoring DataChanges */
+typedef void (*UA_MonitoredItemHandlingFunction)(UA_Client *client, UA_UInt32 monId,
+                                                 UA_DataValue *value, void *context);
+
+UA_DEPRECATED UA_StatusCode UA_EXPORT
+UA_Client_Subscriptions_addMonitoredItems(UA_Client *client, const UA_UInt32 subscriptionId,
+                                          UA_MonitoredItemCreateRequest *items, size_t itemsSize,
+                                          UA_MonitoredItemHandlingFunction *hfs,
+                                          void **hfContexts, UA_StatusCode *itemResults,
+                                          UA_UInt32 *newMonitoredItemIds);
+
+UA_DEPRECATED UA_StatusCode UA_EXPORT
+UA_Client_Subscriptions_addMonitoredItem(UA_Client *client, UA_UInt32 subscriptionId,
+                                         UA_NodeId nodeId, UA_UInt32 attributeID,
+                                         UA_MonitoredItemHandlingFunction hf,
+                                         void *hfContext,
+                                         UA_UInt32 *newMonitoredItemId,
+                                         UA_Double samplingInterval);
+
+/* Monitored Events have different payloads from DataChanges. So they use a
+ * different callback method signature. */
+typedef void (*UA_MonitoredEventHandlingFunction)(UA_Client *client,
+                                                  const UA_UInt32 monId,
+                                                  const size_t nEventFields,
+                                                  const UA_Variant *eventFields,
+                                                  void *context);
+
+UA_DEPRECATED UA_StatusCode UA_EXPORT
+UA_Client_Subscriptions_addMonitoredEvents(UA_Client *client, const UA_UInt32 subscriptionId,
+                                           UA_MonitoredItemCreateRequest *items, size_t itemsSize,
+                                           UA_MonitoredEventHandlingFunction *hfs,
+                                           void **hfContexts, UA_StatusCode *itemResults,
+                                           UA_UInt32 *newMonitoredItemIds);
+
+/* TODO for 0.4: attribute is fix for events. */
+UA_DEPRECATED UA_StatusCode UA_EXPORT
+UA_Client_Subscriptions_addMonitoredEvent(UA_Client *client, UA_UInt32 subscriptionId,
+                                          const UA_NodeId nodeId, UA_UInt32 attributeID,
+                                          const UA_SimpleAttributeOperand *selectClauses,
+                                          size_t selectClausesSize,
+                                          const UA_ContentFilterElement *whereClauses,
+                                          size_t whereClausesSize,
+                                          const UA_MonitoredEventHandlingFunction hf,
+                                          void *hfContext, UA_UInt32 *newMonitoredItemId);
+
+UA_DEPRECATED UA_StatusCode UA_EXPORT
+UA_Client_Subscriptions_removeMonitoredItem(UA_Client *client, UA_UInt32 subscriptionId,
+                                            UA_UInt32 monitoredItemId);
+
+UA_DEPRECATED UA_StatusCode UA_EXPORT
+UA_Client_Subscriptions_removeMonitoredItems(UA_Client *client, UA_UInt32 subscriptionId,
+                                             UA_UInt32 *monitoredItemIds, size_t itemsSize,
+                                             UA_StatusCode *itemResults);
+
+#endif
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* UA_CLIENT_SUBSCRIPTIONS_H_ */

+ 49 - 19
include/ua_config.h.in

@@ -27,6 +27,7 @@ extern "C" {
 #cmakedefine UA_ENABLE_NODEMANAGEMENT
 #cmakedefine UA_ENABLE_SUBSCRIPTIONS
 #cmakedefine UA_ENABLE_MULTITHREADING
+#cmakedefine UA_ENABLE_ENCRYPTION
 
 /* Advanced Options */
 #cmakedefine UA_ENABLE_STATUSCODE_DESCRIPTIONS
@@ -38,6 +39,8 @@ extern "C" {
 #cmakedefine UA_ENABLE_DISCOVERY_MULTICAST
 #cmakedefine UA_ENABLE_DISCOVERY_SEMAPHORE
 #cmakedefine UA_ENABLE_UNIT_TEST_FAILURE_HOOKS
+#cmakedefine UA_ENABLE_VALGRIND_INTERACTIVE
+#define UA_VALGRIND_INTERACTIVE_INTERVAL ${UA_VALGRIND_INTERACTIVE_INTERVAL}
 
 /* Options for Debugging */
 #cmakedefine UA_DEBUG
@@ -90,6 +93,9 @@ extern "C" {
 #elif defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER) /* GCC, Clang, MSC */
 # define UA_CTASTR2(pre,post) pre ## post
 # define UA_CTASTR(pre,post) UA_CTASTR2(pre,post)
+# ifndef __COUNTER__ /* PPC GCC fix */
+#  define __COUNTER__ __LINE__
+# endif
 # define UA_STATIC_ASSERT(cond,msg)                             \
     typedef struct {                                            \
         int UA_CTASTR(static_assertion_failed_,msg) : !!(cond); \
@@ -109,18 +115,36 @@ extern "C" {
 # include <malloc.h>
 #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)
+#if !defined(UA_FREERTOS)
+
+# 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)
+
+#else
+
+# include <FreeRTOS.h>
+
+# define UA_free(ptr) vPortFree(ptr)
+# define UA_malloc(size) pvPortMalloc(size)
+# define UA_calloc(num, size) pvPortCalloc(num, size)
+# define UA_realloc(ptr, size) pvPortRealloc(ptr, size)
 
+#endif
+
+/* Stack-allocation of memory. Use C99 variable-length arrays if possible.
+ * Otherwise revert to alloca. Note that alloca is not supported on some
+ * plattforms. */
 #if defined(__GNUC__) || defined(__clang__)
-# define UA_alloca(size) __builtin_alloca (size)
+# define UA_STACKARRAY(TYPE, NAME, SIZE) TYPE NAME[SIZE]
 #elif defined(_WIN32)
-# define UA_alloca(SIZE) _alloca(SIZE)
+# define UA_STACKARRAY(TYPE, NAME, SIZE) \
+    TYPE *NAME = (TYPE*)_alloca(sizeof(TYPE) * SIZE)
 #else
 # include <alloca.h>
-# define UA_alloca(SIZE) alloca(SIZE)
+# define UA_STACKARRAY(TYPE, NAME, SIZE) \
+    TYPE *NAME = (TYPE*)alloca(sizeof(TYPE) * SIZE)
 #endif
 
 /**
@@ -211,22 +235,16 @@ extern "C" {
  * ^^^^^^^^^^^^^^^^^^
  * The definition ``UA_BINARY_OVERLAYABLE_INTEGER`` is true when the integer
  * representation of the target architecture is little-endian. */
-#if defined(_WIN32) || (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
-                        (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
+#if defined(_WIN32)
 # define UA_BINARY_OVERLAYABLE_INTEGER 1
-#elif defined(__ANDROID__) /* Andoid */
-# include <endian.h>
-# if __BYTE_ORDER == __LITTLE_ENDIAN
-#  define UA_BINARY_OVERLAYABLE_INTEGER 1
-# endif
-#elif defined(__linux__) /* Linux */
+#elif (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
+      (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
+# define UA_BINARY_OVERLAYABLE_INTEGER 1
+#elif defined(__linux__) /* Linux (including Android) */
 # include <endian.h>
 # if __BYTE_ORDER == __LITTLE_ENDIAN
 #  define UA_BINARY_OVERLAYABLE_INTEGER 1
 # endif
-# if __FLOAT_BYTE_ORDER == __LITTLE_ENDIAN
-#  define UA_BINARY_OVERLAYABLE_FLOAT 1
-# endif
 #elif defined(__OpenBSD__) /* OpenBSD */
 # include <sys/endian.h>
 # if BYTE_ORDER == LITTLE_ENDIAN
@@ -259,7 +277,10 @@ extern "C" {
  * The definition ``UA_BINARY_OVERLAYABLE_FLOAT`` is true when the floating
  * point number representation of the target architecture is IEEE 754. Note that
  * this cannot be reliable detected with macros for the clang compiler
- * (beginning of 2017). Just override if necessary. */
+ * (beginning of 2017). ``UA_BINARY_OVERLAYABLE_FLOAT`` can be manually set if
+ * the target is known to be little endian with floats in the IEEE 754
+ * format. */
+
 #if defined(_WIN32)
 # define UA_BINARY_OVERLAYABLE_FLOAT 1
 #elif defined(__FLOAT_WORD_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
@@ -268,6 +289,15 @@ extern "C" {
 #elif defined(__FLOAT_WORD_ORDER) && defined(__LITTLE_ENDIAN) && \
     (__FLOAT_WORD_ORDER == __LITTLE_ENDIAN) /* Defined only in GCC */
 # define UA_BINARY_OVERLAYABLE_FLOAT 1
+#elif defined(__linux__) /* Linux (including Android) */
+# include <endian.h>
+# if defined(__ANDROID__)
+#  if __BYTE_ORDER == __LITTLE_ENDIAN
+#   define UA_BINARY_OVERLAYABLE_INTEGER 1
+#  endif
+# elif __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
+#  define UA_BINARY_OVERLAYABLE_FLOAT 1
+# endif
 #elif defined(_WRS_KERNEL)
 # define UA_BINARY_OVERLAYABLE_FLOAT 1
 #endif

+ 19 - 9
include/ua_constants.h

@@ -1,6 +1,12 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2016-2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2016 (c) Sten Grüner
+ *    Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH
+ *    Copyright 2017 (c) Florian Palm
+ */
 
 #ifndef UA_CONSTANTS_H_
 #define UA_CONSTANTS_H_
@@ -156,7 +162,7 @@ typedef enum {
 #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
+#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_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.
@@ -181,7 +187,7 @@ typedef enum {
 #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 unregognized operator was provided in a filter.
-#define UA_STATUSCODE_BADFILTEROPERATORUNSUPPORTED 0x80c20000 // A valid operator was provided
+#define UA_STATUSCODE_BADFILTEROPERATORUNSUPPORTED 0x80c20000 // A valid operator was provided, but the server does not provide support for this filter operator.
 #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.
@@ -237,7 +243,7 @@ typedef enum {
 #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
+#define UA_STATUSCODE_BADWRITENOTSUPPORTED 0x80730000 // The server not does support writing the combination of value, status and timestamps provided.
 #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.
@@ -262,7 +268,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
+#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_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.
@@ -275,10 +281,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
+#define UA_STATUSCODE_BADREFRESHINPROGRESS 0x80970000 // This Condition refresh failed, a Condition refresh operation is already in progress.
 #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
+#define UA_STATUSCODE_BADCONDITIONDISABLED 0x80990000 // Property not available, this condition is disabled.
 #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.
@@ -292,7 +298,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
+#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_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).
@@ -322,7 +328,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
+#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_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.
@@ -332,6 +338,10 @@ typedef enum {
 #define UA_STATUSCODE_BADSYNTAXERROR 0x80b60000 // A value had an invalid syntax.
 #define UA_STATUSCODE_BADMAXCONNECTIONSREACHED 0x80b70000 // The operation could not be finished because all available connections are in use.
 
+/* These StatusCodes are manually generated. */
+#define UA_STATUSCODE_INFOTYPE_DATAVALUE 0x00000400
+#define UA_STATUSCODE_INFOBITS_OVERFLOW 0x00000080
+
 /**
  * Namespace Zero NodeIds
  * ----------------------

+ 38 - 17
include/ua_plugin_access_control.h

@@ -1,6 +1,10 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ */
 
 #ifndef UA_PLUGIN_ACCESS_CONTROL_H_
 #define UA_PLUGIN_ACCESS_CONTROL_H_
@@ -12,60 +16,77 @@ extern "C" {
 #include "ua_types.h"
 
 /**
+ * .. _access-control:
+ *
  * Access Control Plugin API
  * =========================
  * The access control callback is used to authenticate sessions and grant access
  * rights accordingly. */
 
-typedef struct {
-    /* These booleans are used to create endpoints for the possible
-     * authentication methods */
-    UA_Boolean enableAnonymousLogin;
-    UA_Boolean enableUsernamePasswordLogin;
+struct UA_AccessControl;
+typedef struct UA_AccessControl UA_AccessControl;
 
+struct UA_AccessControl {
+    void *context;
+    void (*deleteMembers)(UA_AccessControl *ac);
+
+    /* Supported login mechanisms. The server endpoints are created from here. */
+    size_t userTokenPoliciesSize;
+    UA_UserTokenPolicy *userTokenPolicies;
+    
     /* Authenticate a session. The session context is attached to the session and
      * later passed into the node-based access control callbacks. */
-    UA_StatusCode (*activateSession)(const UA_NodeId *sessionId,
+    UA_StatusCode (*activateSession)(UA_Server *server, UA_AccessControl *ac,
+                                     const UA_NodeId *sessionId,
                                      const UA_ExtensionObject *userIdentityToken,
                                      void **sessionContext);
 
     /* Deauthenticate a session and cleanup */
-    void (*closeSession)(const UA_NodeId *sessionId, void *sessionContext);
+    void (*closeSession)(UA_Server *server, UA_AccessControl *ac,
+                         const UA_NodeId *sessionId, void *sessionContext);
 
     /* Access control for all nodes*/
-    UA_UInt32 (*getUserRightsMask)(const UA_NodeId *sessionId, void *sessionContext,
+    UA_UInt32 (*getUserRightsMask)(UA_Server *server, UA_AccessControl *ac,
+                                   const UA_NodeId *sessionId, void *sessionContext,
                                    const UA_NodeId *nodeId, void *nodeContext);
 
     /* Additional access control for variable nodes */
-    UA_Byte (*getUserAccessLevel)(const UA_NodeId *sessionId, void *sessionContext,
+    UA_Byte (*getUserAccessLevel)(UA_Server *server, UA_AccessControl *ac,
+                                  const UA_NodeId *sessionId, void *sessionContext,
                                   const UA_NodeId *nodeId, void *nodeContext);
 
     /* Additional access control for method nodes */
-    UA_Boolean (*getUserExecutable)(const UA_NodeId *sessionId, void *sessionContext,
+    UA_Boolean (*getUserExecutable)(UA_Server *server, UA_AccessControl *ac,
+                                    const UA_NodeId *sessionId, void *sessionContext,
                                     const UA_NodeId *methodId, void *methodContext);
 
     /* Additional access control for calling a method node in the context of a
      * specific object */
-    UA_Boolean (*getUserExecutableOnObject)(const UA_NodeId *sessionId, void *sessionContext,
+    UA_Boolean (*getUserExecutableOnObject)(UA_Server *server, UA_AccessControl *ac,
+                                            const UA_NodeId *sessionId, void *sessionContext,
                                             const UA_NodeId *methodId, void *methodContext,
                                             const UA_NodeId *objectId, void *objectContext);
 
     /* Allow adding a node */
-    UA_Boolean (*allowAddNode)(const UA_NodeId *sessionId, void *sessionContext,
+    UA_Boolean (*allowAddNode)(UA_Server *server, UA_AccessControl *ac,
+                               const UA_NodeId *sessionId, void *sessionContext,
                                const UA_AddNodesItem *item);
 
     /* Allow adding a reference */
-    UA_Boolean (*allowAddReference)(const UA_NodeId *sessionId, void *sessionContext,
+    UA_Boolean (*allowAddReference)(UA_Server *server, UA_AccessControl *ac,
+                                    const UA_NodeId *sessionId, void *sessionContext,
                                     const UA_AddReferencesItem *item);
 
     /* Allow deleting a node */
-    UA_Boolean (*allowDeleteNode)(const UA_NodeId *sessionId, void *sessionContext,
+    UA_Boolean (*allowDeleteNode)(UA_Server *server, UA_AccessControl *ac,
+                                  const UA_NodeId *sessionId, void *sessionContext,
                                   const UA_DeleteNodesItem *item);
 
     /* Allow deleting a reference */
-    UA_Boolean (*allowDeleteReference)(const UA_NodeId *sessionId, void *sessionContext,
+    UA_Boolean (*allowDeleteReference)(UA_Server *server, UA_AccessControl *ac,
+                                       const UA_NodeId *sessionId, void *sessionContext,
                                        const UA_DeleteReferencesItem *item);
-} UA_AccessControl;
+};
 
 #ifdef __cplusplus
 }

+ 5 - 1
include/ua_plugin_log.h

@@ -1,6 +1,10 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this 
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ */
 
 #ifndef UA_PLUGIN_LOG_H_
 #define UA_PLUGIN_LOG_H_

+ 12 - 6
include/ua_plugin_network.h

@@ -1,6 +1,10 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ */
 
 #ifndef UA_PLUGIN_NETWORK_H_
 #define UA_PLUGIN_NETWORK_H_
@@ -10,6 +14,7 @@ extern "C" {
 #endif
 
 #include "ua_server.h"
+#include "ua_plugin_log.h"
 
 /* Forward declarations */
 struct UA_Connection;
@@ -46,12 +51,12 @@ typedef struct {
 } UA_ConnectionConfig;
 
 typedef enum {
+    UA_CONNECTION_CLOSED,      /* The socket has been closed and the connection
+                                * will be deleted */
     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
+    UA_CONNECTION_ESTABLISHED  /* The socket is open and the connection
                                 * configured */
-    UA_CONNECTION_CLOSED       /* The socket has been closed and the connection
-                                * will be deleted */
 } UA_ConnectionState;
 
 struct UA_Connection {
@@ -189,10 +194,11 @@ struct UA_ServerNetworkLayer {
 
 /* @param localConf the connection config for this client
  * @param endpointUrl to where to connect
- * @param timeout in ms until the connection try times out if remote not reachable */
+ * @param timeout in ms until the connection try times out if remote not reachable
+ * @param logger the logger to use */
 typedef UA_Connection
 (*UA_ConnectClientConnection)(UA_ConnectionConfig localConf, const char *endpointUrl,
-                              const UA_UInt32 timeout);
+                              const UA_UInt32 timeout, UA_Logger logger);
 
 /**
  * Endpoint URL Parser

+ 6 - 1
include/ua_plugin_nodestore.h

@@ -1,6 +1,11 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2017 (c) Julian Grothoff
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ */
 
 #ifndef UA_SERVER_NODES_H_
 #define UA_SERVER_NODES_H_

+ 49 - 0
include/ua_plugin_pki.h

@@ -0,0 +1,49 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2018 (c) Mark Giraud, Fraunhofer IOSB
+ */
+
+#ifndef UA_PLUGIN_PKI_H_
+#define UA_PLUGIN_PKI_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ua_types.h"
+#include "ua_server.h"
+
+/**
+ * Public Key Infrastructure Integration
+ * =====================================
+ * This file contains interface definitions for integration in a Public Key
+ * Infrastructure (PKI). Currently only one plugin interface is defined.
+ *
+ * Certificate Verification
+ * ------------------------
+ * This plugin verifies that the origin of the certificate is trusted. It does
+ * not assign any access rights/roles to the holder of the certificate.
+ *
+ * Usually, implementations of the certificate verification plugin provide an
+ * initialization method that takes a trust-list and a revocation-list as input.
+ * The lifecycle of the plugin is attached to a server or client config. The
+ * ``deleteMembers`` method is called automatically when the config is
+ * destroyed. */
+
+struct UA_CertificateVerification;
+typedef struct UA_CertificateVerification UA_CertificateVerification;
+
+struct UA_CertificateVerification {
+    void *context;
+    UA_StatusCode (*verifyCertificate)(void *verificationContext,
+                                       const UA_ByteString *certificate);
+    void (*deleteMembers)(UA_CertificateVerification *cv);
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UA_PLUGIN_PKI_H_ */

+ 130 - 54
include/ua_plugin_securitypolicy.h

@@ -1,6 +1,11 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2017-2018 (c) Mark Giraud, Fraunhofer IOSB
+ *    Copyright 2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ */
 
 #ifndef UA_PLUGIN_SECURITYPOLICY_H_
 #define UA_PLUGIN_SECURITYPOLICY_H_
@@ -12,6 +17,7 @@ extern "C" {
 #include "ua_types.h"
 #include "ua_types_generated.h"
 #include "ua_plugin_log.h"
+#include "ua_plugin_pki.h"
 
 extern const UA_ByteString UA_SECURITY_POLICY_NONE_URI;
 
@@ -19,7 +25,7 @@ struct UA_SecurityPolicy;
 typedef struct UA_SecurityPolicy UA_SecurityPolicy;
 
 typedef struct {
-    UA_String signatureAlgorithmUri;
+    UA_String uri;
 
     /* Verifies the signature of the message using the provided keys in the context.
      *
@@ -29,7 +35,7 @@ typedef struct {
      * @param message the message to which the signature is supposed to belong.
      * @param signature the signature of the message, that should be verified. */
     UA_StatusCode (*verify)(const UA_SecurityPolicy *securityPolicy,
-                            const void *channelContext, const UA_ByteString *message,
+                            void *channelContext, const UA_ByteString *message,
                             const UA_ByteString *signature) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 
     /* Signs the given message using this policys signing algorithm and the
@@ -44,7 +50,7 @@ typedef struct {
      *                  necessary size can be acquired with the signatureSize
      *                  attribute of this module. */
     UA_StatusCode (*sign)(const UA_SecurityPolicy *securityPolicy,
-                          const void *channelContext, const UA_ByteString *message,
+                          void *channelContext, const UA_ByteString *message,
                           UA_ByteString *signature) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 
     /* Gets the signature size that depends on the local (private) key.
@@ -66,7 +72,27 @@ typedef struct {
     size_t (*getRemoteSignatureSize)(const UA_SecurityPolicy *securityPolicy,
                                      const void *channelContext);
 
-    UA_String encryptionAlgorithmUri;
+    /* Gets the local signing key length.
+     *
+     * @param securityPolicy the securityPolicy the function is invoked on.
+     * @param channelContext the context to retrieve data from.
+     * @return the length of the signing key in bytes. Returns 0 if no length can be found.
+     */
+    size_t (*getLocalKeyLength)(const UA_SecurityPolicy *securityPolicy,
+                                const void *channelContext);
+
+    /* Gets the local signing key length.
+     *
+     * @param securityPolicy the securityPolicy the function is invoked on.
+     * @param channelContext the context to retrieve data from.
+     * @return the length of the signing key in bytes. Returns 0 if no length can be found.
+     */
+    size_t (*getRemoteKeyLength)(const UA_SecurityPolicy *securityPolicy,
+                                 const void *channelContext);
+} UA_SecurityPolicySignatureAlgorithm;
+
+typedef struct {
+    UA_String uri;
 
     /* Encrypt the given data in place using an asymmetric algorithm and keys.
      *
@@ -75,9 +101,9 @@ typedef struct {
      *                       the keys to encrypt data.
      * @param data the data that is encrypted. The encrypted data will overwrite
      *             the data that was supplied. */
-    UA_StatusCode(*encrypt)(const UA_SecurityPolicy *securityPolicy,
-                            const void *channelContext,
-                            UA_ByteString *data) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
+    UA_StatusCode (*encrypt)(const UA_SecurityPolicy *securityPolicy,
+                             void *channelContext,
+                             UA_ByteString *data) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 
     /* Decrypts the given ciphertext in place using an asymmetric algorithm and
      * key.
@@ -86,9 +112,9 @@ typedef struct {
      * @param channelContext the channelContext which contains information about
      *                       the keys needed to decrypt the message.
      * @param data the data to decrypt. The decryption is done in place. */
-    UA_StatusCode(*decrypt)(const UA_SecurityPolicy *securityPolicy,
-                            const void *channelContext,
-                            UA_ByteString *data) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
+    UA_StatusCode (*decrypt)(const UA_SecurityPolicy *securityPolicy,
+                             void *channelContext,
+                             UA_ByteString *data) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 
     /* Returns the length of the key used locally to encrypt messages in bits
      *
@@ -96,8 +122,8 @@ typedef struct {
      * @param channelContext the context to retrieve data from.
      * @return the length of the local key. Returns 0 if no
      *         key length is known. */
-    size_t (*getLocalEncryptionKeyLength)(const UA_SecurityPolicy *securityPolicy,
-                                          const void *channelContext);
+    size_t (*getLocalKeyLength)(const UA_SecurityPolicy *securityPolicy,
+                                const void *channelContext);
 
     /* Returns the length of the key used remotely to encrypt messages in bits
      *
@@ -105,22 +131,71 @@ typedef struct {
      * @param channelContext the context to retrieve data from.
      * @return the length of the remote key. Returns 0 if no
      *         key length is known. */
-    size_t (*getRemoteEncryptionKeyLength)(const UA_SecurityPolicy *securityPolicy,
-                                           const void *channelContext);
+    size_t (*getRemoteKeyLength)(const UA_SecurityPolicy *securityPolicy,
+                                 const void *channelContext);
+
+    /* Returns the size of encrypted blocks used by the local encryption algorithm.
+     *
+     * @param securityPolicy the securityPolicy the function is invoked on.
+     * @param channelContext the context to retrieve data from.
+     * @return the size of encrypted blocks in bytes. Returns 0 if no key length is known.
+     */
+    size_t (*getLocalBlockSize)(const UA_SecurityPolicy *securityPolicy,
+                                const void *channelContext);
+
+    /* Returns the size of encrypted blocks used by the remote encryption algorithm.
+     *
+     * @param securityPolicy the securityPolicy the function is invoked on.
+     * @param channelContext the context to retrieve data from.
+     * @return the size of encrypted blocks in bytes. Returns 0 if no key length is known.
+     */
+    size_t (*getRemoteBlockSize)(const UA_SecurityPolicy *securityPolicy,
+                                 const void *channelContext);
+
+    /* Returns the size of plaintext blocks used by the local encryption algorithm.
+     *
+     * @param securityPolicy the securityPolicy the function is invoked on.
+     * @param channelContext the context to retrieve data from.
+     * @return the size of plaintext blocks in bytes. Returns 0 if no key length is known.
+     */
+    size_t (*getLocalPlainTextBlockSize)(const UA_SecurityPolicy *securityPolicy,
+                                         const void *channelContext);
+
+    /* Returns the size of plaintext blocks used by the remote encryption algorithm.
+     *
+     * @param securityPolicy the securityPolicy the function is invoked on.
+     * @param channelContext the context to retrieve data from.
+     * @return the size of plaintext blocks in bytes. Returns 0 if no key length is known.
+     */
+    size_t (*getRemotePlainTextBlockSize)(const UA_SecurityPolicy *securityPolicy,
+                                          const void *channelContext);
+} UA_SecurityPolicyEncryptionAlgorithm;
+
+typedef struct {
+    /*
+     * The algorithm used to sign and verify certificates.
+     */
+    UA_SecurityPolicySignatureAlgorithm signatureAlgorithm;
+
+    /*
+     * The algorithm used to encrypt and decrypt messages.
+     */
+    UA_SecurityPolicyEncryptionAlgorithm encryptionAlgorithm;
+
 } UA_SecurityPolicyCryptoModule;
 
 typedef struct {
-    /* Generates a thumprint for the specified certificate.
+    /* Generates a thumbprint for the specified certificate.
      *
      * @param securityPolicy the securityPolicy the function is invoked on.
      * @param certificate the certificate to make a thumbprint of.
      * @param thumbprint an output buffer for the resulting thumbprint. Always
-     *                   has the length specified in the thumprintLenght in the
+     *                   has the length specified in the thumbprintLength in the
      *                   asymmetricModule. */
     UA_StatusCode (*makeCertificateThumbprint)(const UA_SecurityPolicy *securityPolicy,
                                                const UA_ByteString *certificate,
                                                UA_ByteString *thumbprint)
-        UA_FUNC_ATTR_WARN_UNUSED_RESULT;
+    UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 
     /* Compares the supplied certificate with the certificate in the endpoit context.
      *
@@ -129,10 +204,10 @@ typedef struct {
      * @param certificateThumbprint the certificate thumbprint to compare to the
      *                              one stored in the context.
      * @return if the thumbprints match UA_STATUSCODE_GOOD is returned. If they
-     *         don't match or an error occured an error code is returned. */
+     *         don't match or an error occurred an error code is returned. */
     UA_StatusCode (*compareCertificateThumbprint)(const UA_SecurityPolicy *securityPolicy,
                                                   const UA_ByteString *certificateThumbprint)
-        UA_FUNC_ATTR_WARN_UNUSED_RESULT;
+    UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 
     UA_SecurityPolicyCryptoModule cryptoModule;
 } UA_SecurityPolicyAsymmetricModule;
@@ -151,7 +226,8 @@ typedef struct {
     UA_StatusCode (*generateKey)(const UA_SecurityPolicy *securityPolicy,
                                  const UA_ByteString *secret,
                                  const UA_ByteString *seed, UA_ByteString *out)
-        UA_FUNC_ATTR_WARN_UNUSED_RESULT;
+    UA_FUNC_ATTR_WARN_UNUSED_RESULT;
+
     /* Random generator for generating nonces.
      *
      * @param securityPolicy the securityPolicy this function is invoked on.
@@ -162,17 +238,20 @@ typedef struct {
      *            data. */
     UA_StatusCode (*generateNonce)(const UA_SecurityPolicy *securityPolicy,
                                    UA_ByteString *out)
-        UA_FUNC_ATTR_WARN_UNUSED_RESULT;
+    UA_FUNC_ATTR_WARN_UNUSED_RESULT;
+
+    /*
+     * The length of the nonce used in the SecureChannel as specified in the standard.
+     */
+    size_t secureChannelNonceLength;
 
     UA_SecurityPolicyCryptoModule cryptoModule;
-    size_t encryptionBlockSize;
-    size_t signingKeyLength;
 } UA_SecurityPolicySymmetricModule;
 
 typedef struct {
     /* This method creates a new context data object.
      *
-     * The caller needs to call delete on the recieved object to free allocated
+     * The caller needs to call delete on the received object to free allocated
      * memory. Memory is only allocated if the function succeeds so there is no
      * need to manually free the memory pointed to by *channelContext or to
      * call delete in case of failure.
@@ -189,7 +268,7 @@ typedef struct {
     UA_StatusCode (*newContext)(const UA_SecurityPolicy *securityPolicy,
                                 const UA_ByteString *remoteCertificate,
                                 void **channelContext)
-        UA_FUNC_ATTR_WARN_UNUSED_RESULT;
+    UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 
     /* Deletes the the security context. */
     void (*deleteContext)(void *channelContext);
@@ -200,7 +279,7 @@ typedef struct {
      * @param key the local encrypting key to store in the context. */
     UA_StatusCode (*setLocalSymEncryptingKey)(void *channelContext,
                                               const UA_ByteString *key)
-        UA_FUNC_ATTR_WARN_UNUSED_RESULT;
+    UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 
     /* Sets the local signing key in the supplied context.
      *
@@ -208,7 +287,7 @@ typedef struct {
      * @param key the local signing key to store in the context. */
     UA_StatusCode (*setLocalSymSigningKey)(void *channelContext,
                                            const UA_ByteString *key)
-        UA_FUNC_ATTR_WARN_UNUSED_RESULT;
+    UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 
     /* Sets the local initialization vector in the supplied context.
      *
@@ -216,7 +295,7 @@ typedef struct {
      * @param iv the local initialization vector to store in the context. */
     UA_StatusCode (*setLocalSymIv)(void *channelContext,
                                    const UA_ByteString *iv)
-        UA_FUNC_ATTR_WARN_UNUSED_RESULT;
+    UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 
     /* Sets the remote encrypting key in the supplied context.
      *
@@ -224,7 +303,7 @@ typedef struct {
      * @param key the remote encrypting key to store in the context. */
     UA_StatusCode (*setRemoteSymEncryptingKey)(void *channelContext,
                                                const UA_ByteString *key)
-        UA_FUNC_ATTR_WARN_UNUSED_RESULT;
+    UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 
     /* Sets the remote signing key in the supplied context.
      *
@@ -232,7 +311,7 @@ typedef struct {
      * @param key the remote signing key to store in the context. */
     UA_StatusCode (*setRemoteSymSigningKey)(void *channelContext,
                                             const UA_ByteString *key)
-        UA_FUNC_ATTR_WARN_UNUSED_RESULT;
+    UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 
     /* Sets the remote initialization vector in the supplied context.
      *
@@ -240,7 +319,7 @@ typedef struct {
      * @param iv the remote initialization vector to store in the context. */
     UA_StatusCode (*setRemoteSymIv)(void *channelContext,
                                     const UA_ByteString *iv)
-        UA_FUNC_ATTR_WARN_UNUSED_RESULT;
+    UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 
     /* Compares the supplied certificate with the certificate in the channel
      * context.
@@ -249,30 +328,10 @@ typedef struct {
      *                       certificate to compare to.
      * @param certificate the certificate to compare to the one stored in the context.
      * @return if the certificates match UA_STATUSCODE_GOOD is returned. If they
-     *         don't match or an errror occured an error code is returned. */
+     *         don't match or an errror occurred an error code is returned. */
     UA_StatusCode (*compareCertificate)(const void *channelContext,
                                         const UA_ByteString *certificate)
-        UA_FUNC_ATTR_WARN_UNUSED_RESULT;
-
-    /* Gets the plaintext block size that depends on the remote public key.
-     *
-     * @param channelContext the context to retrieve data from.
-     * @return the size of the plain text block size when encrypting with the
-     *         remote public key. Returns 0 as long as no remote certificate was
-     *         set previousely. */
-    size_t (*getRemoteAsymPlainTextBlockSize)(const void *channelContext);
-
-    /* Gets the number of bytes that are needed by the encryption function in
-     * addition to the length of the plaintext message. This is needed, since
-     * most RSA encryption methods have their own padding mechanism included.
-     * This makes the encrypted message larger than the plainText, so we need to
-     * have enough room in the buffer for the overhead.
-     *
-     * @param channelContext the retrieve data from.
-     * @param maxEncryptionLength the maximum number of bytes that the data to
-     *                            encrypt can be. */
-    size_t (*getRemoteAsymEncryptionBufferLengthOverhead)(const void *channelContext,
-                                                          size_t maxEncryptionLength);
+    UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 } UA_SecurityPolicyChannelModule;
 
 struct UA_SecurityPolicy {
@@ -289,7 +348,9 @@ struct UA_SecurityPolicy {
     /* Function pointers grouped into modules */
     UA_SecurityPolicyAsymmetricModule asymmetricModule;
     UA_SecurityPolicySymmetricModule symmetricModule;
+    UA_SecurityPolicySignatureAlgorithm certificateSigningAlgorithm;
     UA_SecurityPolicyChannelModule channelModule;
+    UA_CertificateVerification *certificateVerification;
 
     UA_Logger logger;
 
@@ -302,6 +363,21 @@ typedef struct {
     UA_EndpointDescription endpointDescription;
 } UA_Endpoint;
 
+/* Gets the number of bytes that are needed by the encryption function in
+ * addition to the length of the plaintext message. This is needed, since
+ * most RSA encryption methods have their own padding mechanism included.
+ * This makes the encrypted message larger than the plainText, so we need to
+ * have enough room in the buffer for the overhead.
+ *
+ * @param securityPolicy the algorithms to use.
+ * @param channelContext the retrieve data from.
+ * @param maxEncryptionLength the maximum number of bytes that the data to
+ *                            encrypt can be. */
+size_t
+UA_SecurityPolicy_getRemoteAsymEncryptionBufferLengthOverhead(const UA_SecurityPolicy *securityPolicy,
+                                                              const void *channelContext,
+                                                              size_t maxEncryptionLength);
+
 #ifdef __cplusplus
 }
 #endif

+ 86 - 24
include/ua_server.h

@@ -1,6 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2014-2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2015-2016 (c) Sten Grüner
+ *    Copyright 2014-2015, 2017 (c) Florian Palm
+ *    Copyright 2015-2016 (c) Chris Iatrou
+ *    Copyright 2015-2016 (c) Oleksiy Vasylyev
+ *    Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH
+ */
 
 #ifndef UA_SERVER_H_
 #define UA_SERVER_H_
@@ -19,12 +27,16 @@ typedef struct UA_ServerConfig UA_ServerConfig;
 struct UA_Server;
 typedef struct UA_Server UA_Server;
 
+struct UA_ClientConfig;
+
 /**
  * .. _server:
  *
  * Server
  * ======
  *
+ * .. include:: server_config.rst
+ *
  * .. _server-lifecycle:
  *
  * Server Lifecycle
@@ -92,7 +104,7 @@ UA_Server_changeRepeatedCallbackInterval(UA_Server *server, UA_UInt64 callbackId
  *
  * @param server The server object.
  * @param callbackId The id of the callback that shall be removed.
- * @return Upon sucess, UA_STATUSCODE_GOOD is returned.
+ * @return Upon success, UA_STATUSCODE_GOOD is returned.
  *         An error code otherwise. */
 UA_StatusCode UA_EXPORT
 UA_Server_removeRepeatedCallback(UA_Server *server, UA_UInt64 callbackId);
@@ -123,7 +135,7 @@ UA_Server_removeRepeatedCallback(UA_Server *server, UA_UInt64 callbackId);
 UA_DataValue UA_EXPORT
 UA_Server_read(UA_Server *server, const UA_ReadValueId *item,
                UA_TimestampsToReturn timestamps);
-    
+
 /* Don't use this function. There are typed versions for every supported
  * attribute. */
 UA_StatusCode UA_EXPORT
@@ -638,6 +650,18 @@ UA_Server_setNodeContext(UA_Server *server, UA_NodeId nodeId,
  * be set to a null-pointer. */
 typedef struct {
     /* Copies the data from the source into the provided value.
+     *
+     * !! ZERO-COPY OPERATIONS POSSIBLE !!
+     * It is not required to return a copy of the actual content data. You can
+     * return a pointer to memory owned by the user. Memory can be reused
+     * between read callbacks of a DataSource, as the result is already encoded
+     * on the network buffer between each read operation.
+     *
+     * To use zero-copy reads, set the value of the `value->value` Variant
+     * without copying, e.g. with `UA_Variant_setScalar`. Then, also set
+     * `value->value.storageType` to `UA_VARIANT_DATA_NODELETE` to prevent the
+     * memory being cleaned up. Don't forget to also set `value->hasValue` to
+     * true to indicate the presence of a value.
      *
      * @param handle An optional pointer to user-defined data for the
      *        specific data source
@@ -659,8 +683,8 @@ typedef struct {
                           void *nodeContext, UA_Boolean includeSourceTimeStamp,
                           const UA_NumericRange *range, UA_DataValue *value);
 
-    /* Write into a data source. The write member of UA_DataSource can be empty
-     * if the operation is unsupported.
+    /* Write into a data source. This method pointer can be NULL if the
+     * operation is unsupported.
      *
      * @param handle An optional pointer to user-defined data for the
      *        specific data source
@@ -764,7 +788,7 @@ UA_Server_call(UA_Server *server, const UA_CallMethodRequest *request);
  * reference it later. When passing numeric NodeIds with a numeric identifier 0,
  * the stack evaluates this as "select a random unassigned numeric NodeId in
  * that namespace". To find out which NodeId was actually assigned to the new
- * node, you may pass a pointer `outNewNodeId`, which will (after a successfull
+ * node, you may pass a pointer `outNewNodeId`, which will (after a successful
  * node insertion) contain the nodeId of the new node. You may also pass a
  * ``NULL`` pointer if this result is not needed.
  *
@@ -926,14 +950,33 @@ UA_Server_addDataSourceVariableNode(UA_Server *server,
                                     void *nodeContext, UA_NodeId *outNewNodeId);
 
 UA_StatusCode UA_EXPORT
+UA_Server_addMethodNodeEx(UA_Server *server, const UA_NodeId requestedNewNodeId,
+                          const UA_NodeId parentNodeId,
+                          const UA_NodeId referenceTypeId,
+                          const UA_QualifiedName browseName,
+                          const UA_MethodAttributes attr, UA_MethodCallback method,
+                          size_t inputArgumentsSize, const UA_Argument *inputArguments,
+                          const UA_NodeId inputArgumentsRequestedNewNodeId,
+                          UA_NodeId *inputArgumentsOutNewNodeId,
+                          size_t outputArgumentsSize, const UA_Argument *outputArguments,
+                          const UA_NodeId outputArgumentsRequestedNewNodeId,
+                          UA_NodeId *outputArgumentsOutNewNodeId,
+                          void *nodeContext, UA_NodeId *outNewNodeId);
+
+static UA_INLINE UA_StatusCode
 UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
-                        const UA_NodeId parentNodeId,
-                        const UA_NodeId referenceTypeId,
-                        const UA_QualifiedName browseName,
-                        const UA_MethodAttributes attr, UA_MethodCallback method,
-                        size_t inputArgumentsSize, const UA_Argument* inputArguments, 
-                        size_t outputArgumentsSize, const UA_Argument* outputArguments,
-                        void *nodeContext, UA_NodeId *outNewNodeId);
+                        const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                        const UA_QualifiedName browseName, const UA_MethodAttributes attr,
+                        UA_MethodCallback method,
+                        size_t inputArgumentsSize, const UA_Argument *inputArguments,
+                        size_t outputArgumentsSize, const UA_Argument *outputArguments,
+                        void *nodeContext, UA_NodeId *outNewNodeId) {
+    return UA_Server_addMethodNodeEx(server, requestedNewNodeId,  parentNodeId,
+                                     referenceTypeId, browseName, attr, method,
+                                     inputArgumentsSize, inputArguments, UA_NODEID_NULL, NULL,
+                                     outputArgumentsSize, outputArguments, UA_NODEID_NULL, NULL,
+                                     nodeContext, outNewNodeId);
+}
 
 
 /**
@@ -944,12 +987,26 @@ UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
  * pseudo-random unique NodeIds. Existing children are detected during the
  * _finish part via their matching BrowseName.
  *
- * The _begin method prepares the node and adds it to the nodestore. It may copy
- * some unassigned attributes from the TypeDefinition node internally. The
- * _finish method adds the references to the parent (and the TypeDefinition if
- * applicable), copies mandatory children, performs type-checking of variables
- * and calls the node constructor(s) at the end. The _finish method may remove
- * the node if it encounters an error. */
+ * The _begin method:
+ *  - prepares the node and adds it to the nodestore
+ *  - copies some unassigned attributes from the TypeDefinition node internally
+ *  - adds the references to the parent (and the TypeDefinition if applicable)
+ *  - performs type-checking of variables.
+ *
+ * You can add an object node without a parent if you set the parentNodeId and
+ * referenceTypeId to UA_NODE_ID_NULL. Then you need to add the parent reference
+ * and hasTypeDef reference yourself before calling the _finish method.
+ * Not that this is only allowed for object nodes.
+ *
+ * The _finish method:
+ *  - copies mandatory children
+ *  - calls the node constructor(s) at the end
+ *  - may remove the node if it encounters an error.
+ *
+ * The special UA_Server_addMethodNode_finish method needs to be used for
+ * method nodes, since there you need to explicitly specifiy the input
+ * and output arguments which are added in the finish step (if not yet already there)
+ **/
 
 /* The ``attr`` argument must have a type according to the NodeClass.
  * ``VariableAttributes`` for variables, ``ObjectAttributes`` for objects, and
@@ -958,16 +1015,21 @@ UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
 UA_StatusCode UA_EXPORT
 UA_Server_addNode_begin(UA_Server *server, 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 void *attr, const UA_DataType *attributeType,
                         void *nodeContext, UA_NodeId *outNewNodeId);
 
 UA_StatusCode UA_EXPORT
-UA_Server_addNode_finish(UA_Server *server, const UA_NodeId nodeId,
-                         const UA_NodeId parentNodeId,
-                         const UA_NodeId referenceTypeId,
-                         const UA_NodeId typeDefinitionId);
+UA_Server_addNode_finish(UA_Server *server, const UA_NodeId nodeId);
+
+UA_StatusCode UA_EXPORT
+UA_Server_addMethodNode_finish(UA_Server *server, const UA_NodeId nodeId,
+                         UA_MethodCallback method,
+                         size_t inputArgumentsSize, const UA_Argument* inputArguments,
+                         size_t outputArgumentsSize, const UA_Argument* outputArguments);
 
 /* Deletes a node and optionally all references leading to the node. */
 UA_StatusCode UA_EXPORT
@@ -1003,7 +1065,7 @@ UA_UInt16 UA_EXPORT UA_Server_addNamespace(UA_Server *server, const char* name);
  *
  * UA_Job API
  * ^^^^^^^^^^
- * UA_Job was replaced since it unneccessarily exposed server internals to the
+ * UA_Job was replaced since it unnecessarily exposed server internals to the
  * end-user. Please use plain UA_ServerCallbacks instead. The following UA_Job
  * definition contains just the fraction of the original struct that was useful
  * to end-users. */

+ 43 - 5
include/ua_server_config.h

@@ -1,6 +1,11 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ *    Copyright 2017 (c) Henrik Norrman
+ */
 
 #ifndef UA_SERVER_CONFIG_H_
 #define UA_SERVER_CONFIG_H_
@@ -13,13 +18,31 @@ extern "C" {
 #include "ua_plugin_log.h"
 #include "ua_plugin_network.h"
 #include "ua_plugin_access_control.h"
+#include "ua_plugin_pki.h"
 #include "ua_plugin_securitypolicy.h"
 #include "ua_plugin_nodestore.h"
 
 /**
+ * .. _server-configuration:
+ *
  * Server Configuration
- * ====================
- * The configuration structure is passed to the server during initialization. */
+ * --------------------
+
+ * The configuration structure is passed to the server during initialization.
+ * The server expects that the configuration is not modified during runtime.
+ * Currently, only one server can use a configuration at a time. During
+ * shutdown, the server will clean up the parts of the configuration that are
+ * modified at runtime through the provided API.
+ *
+ * Examples for configurations are provided in the ``/plugins`` folder.
+ * The usual usage is as follows:
+ *
+ * 1. Create a server configuration with default settings as a starting point
+ * 2. Modifiy the configuration, e.g. by adding a server certificate
+ * 3. Instantiate a server with it
+ * 4. After shutdown of the server, clean up the configuration (free memory)
+ *
+ * The :ref:`tutorials` provide a good starting point for this. */
 
 typedef struct {
     UA_UInt32 min;
@@ -39,6 +62,8 @@ struct UA_ServerConfig {
     UA_BuildInfo buildInfo;
     UA_ApplicationDescription applicationDescription;
     UA_ByteString serverCertificate;
+
+    /* MDNS Discovery */
 #ifdef UA_ENABLE_DISCOVERY
     UA_String mdnsServerName;
     size_t serverCapabilitiesSize;
@@ -48,6 +73,10 @@ struct UA_ServerConfig {
     /* Custom DataTypes */
     size_t customDataTypesSize;
     UA_DataType *customDataTypes;
+    /**
+     * .. note:: See the section on :ref:`generic-types`. Examples for working
+     *    with custom data types are provided in
+     *    ``/examples/custom_datatype/``. */
 
     /* Nodestore */
     UA_Nodestore nodestore;
@@ -56,16 +85,25 @@ struct UA_ServerConfig {
     size_t networkLayersSize;
     UA_ServerNetworkLayer *networkLayers;
     UA_String customHostname;
-    
+
     /* Available endpoints */
     size_t endpointsSize;
     UA_Endpoint *endpoints;
 
-    /* Global Node Lifecycle */
+    /* Node Lifecycle callbacks */
     UA_GlobalNodeLifecycle nodeLifecycle;
+    /**
+     * .. note:: See the section for :ref:`node lifecycle
+     *    handling<node-lifecycle>`. */
 
     /* Access Control */
     UA_AccessControl accessControl;
+    /**
+     * .. note:: See the section for :ref:`access-control
+     *    handling<access-control>`. */
+
+    /* Certificate Verification */
+    UA_CertificateVerification certificateVerification;
 
     /* Limits for SecureChannels */
     UA_UInt16 maxSecureChannels;

+ 73 - 18
include/ua_types.h

@@ -1,6 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2014 (c) Leon Urbas
+ *    Copyright 2014, 2016-2017 (c) Florian Palm
+ *    Copyright 2014-2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2015-2016 (c) Sten Grüner
+ *    Copyright 2015-2016 (c) Chris Iatrou
+ *    Copyright 2015 (c) Nick Goossens
+ *    Copyright 2015-2016 (c) Oleksiy Vasylyev
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ *    Copyright 2017 (c) Thomas Stalder
+ */
 
 #ifndef UA_TYPES_H_
 #define UA_TYPES_H_
@@ -163,6 +174,9 @@ UA_STRING(char *chars) {
 
 #define UA_STRING_ALLOC(CHARS) UA_String_fromChars(CHARS)
 
+/* Define strings at compile time (in ROM) */
+#define UA_STRING_STATIC(CHARS) {sizeof(CHARS)-1, (UA_Byte*)CHARS}
+
 /**
  * .. _datetime:
  *
@@ -170,27 +184,29 @@ UA_STRING(char *chars) {
  * ^^^^^^^^
  * An instance in time. A DateTime value is encoded as a 64-bit signed integer
  * which represents the number of 100 nanosecond intervals since January 1, 1601
- * (UTC). */
-typedef int64_t UA_DateTime;
+ * (UTC).
+ *
+ * The methods providing an interface to the system clock are provided by a
+ * "plugin" that is statically linked with the library. */
 
-/* Multiply to convert units for time difference computations */
-#define UA_USEC_TO_DATETIME 10LL
-#define UA_MSEC_TO_DATETIME (UA_USEC_TO_DATETIME * 1000LL)
-#define UA_SEC_TO_DATETIME (UA_MSEC_TO_DATETIME * 1000LL)
-#define UA_DATETIME_TO_USEC (1/10.0)
-#define UA_DATETIME_TO_MSEC (UA_DATETIME_TO_USEC / 1000.0)
-#define UA_DATETIME_TO_SEC (UA_DATETIME_TO_MSEC / 1000.0)
+typedef int64_t UA_DateTime;
 
-/* Datetime of 1 Jan 1970 00:00 UTC */
-#define UA_DATETIME_UNIX_EPOCH (11644473600LL * UA_SEC_TO_DATETIME)
+/* Multiples to convert durations to DateTime */
+#define UA_DATETIME_USEC 10LL
+#define UA_DATETIME_MSEC (UA_DATETIME_USEC * 1000LL)
+#define UA_DATETIME_SEC (UA_DATETIME_MSEC * 1000LL)
 
-/* The current time */
+/* The current time in UTC time */
 UA_DateTime UA_EXPORT UA_DateTime_now(void);
 
-/* CPU clock invariant to system time changes. Use only for time diffs, not
- * current time */
+/* Offset between local time and UTC time */
+UA_Int64 UA_EXPORT UA_DateTime_localTimeUtcOffset(void);
+
+/* CPU clock invariant to system time changes. Use only to measure durations,
+ * not absolute time. */
 UA_DateTime UA_EXPORT UA_DateTime_nowMonotonic(void);
 
+/* Represents a Datetime as a structure */
 typedef struct UA_DateTimeStruct {
     UA_UInt16 nanoSec;
     UA_UInt16 microSec;
@@ -205,7 +221,23 @@ typedef struct UA_DateTimeStruct {
 
 UA_DateTimeStruct UA_EXPORT UA_DateTime_toStruct(UA_DateTime t);
 
-UA_String UA_EXPORT UA_DateTime_toString(UA_DateTime t);
+/* The C99 standard (7.23.1) says: "The range and precision of times
+ * representable in clock_t and time_t are implementation-defined." On most
+ * systems, time_t is a 4 or 8 byte integer counting seconds since the UTC Unix
+ * epoch. The following methods are used for conversion. */
+
+/* Datetime of 1 Jan 1970 00:00 */
+#define UA_DATETIME_UNIX_EPOCH (11644473600LL * UA_DATETIME_SEC)
+
+static UA_INLINE UA_Int64
+UA_DateTime_toUnixTime(UA_DateTime date) {
+    return (date - UA_DATETIME_UNIX_EPOCH) / UA_DATETIME_SEC;
+}
+
+static UA_INLINE UA_DateTime
+UA_DateTime_fromUnixTime(UA_Int64 unixDate) {
+    return (unixDate * UA_DATETIME_SEC) + UA_DATETIME_UNIX_EPOCH;
+}
 
 /**
  * Guid
@@ -348,6 +380,9 @@ typedef struct {
     UA_UInt32 serverIndex;
 } UA_ExpandedNodeId;
 
+UA_Boolean UA_EXPORT UA_ExpandedNodeId_equal(const UA_ExpandedNodeId *n1,
+                                             const UA_ExpandedNodeId *n2);
+
 UA_EXPORT extern const UA_ExpandedNodeId UA_EXPANDEDNODEID_NULL;
 
 /** The following functions are shorthand for creating ExpandedNodeIds. */
@@ -415,6 +450,10 @@ UA_QUALIFIEDNAME_ALLOC(UA_UInt16 nsIndex, const char *chars) {
     qn.name = UA_STRING_ALLOC(chars); return qn;
 }
 
+UA_Boolean UA_EXPORT
+UA_QualifiedName_equal(const UA_QualifiedName *qn1,
+                       const UA_QualifiedName *qn2);
+
 /**
  * LocalizedText
  * ^^^^^^^^^^^^^
@@ -641,7 +680,7 @@ UA_Variant_setRangeCopy(UA_Variant *v, const void *array,
  *
  * ExtensionObjects may contain scalars of any data type. Even those that are
  * unknown to the receiver. See the section on :ref:`generic-types` on how types
- * are described. If the received data type is unkown, the encoded string and
+ * are described. If the received data type is unknown, the encoded string and
  * target NodeId is stored instead of the decoded value. */
 typedef enum {
     UA_EXTENSIONOBJECT_ENCODED_NOBODY     = 0,
@@ -744,7 +783,7 @@ typedef struct {
                                      types */
     UA_Byte   padding;            /* How much padding is there before this
                                      member element? For arrays this is the
-                                     padding before the size_t lenght member.
+                                     padding before the size_t length member.
                                      (No padding between size_t and the
                                      following ptr.) */
     UA_Boolean namespaceZero : 1; /* The type of the member is defined in
@@ -920,6 +959,22 @@ UA_StatusCode_explanation(UA_StatusCode code) {
     return statusCodeExplanation_default.name;
 }
 
+UA_DEPRECATED UA_String
+UA_DateTime_toString(UA_DateTime t);
+
+/* The old DateTime conversion macros */
+UA_DEPRECATED static UA_INLINE double
+deprecatedDateTimeMultiple(double multiple) {
+    return multiple;
+}
+
+#define UA_USEC_TO_DATETIME deprecatedDateTimeMultiple((UA_Double)UA_DATETIME_USEC)
+#define UA_MSEC_TO_DATETIME deprecatedDateTimeMultiple((UA_Double)UA_DATETIME_MSEC)
+#define UA_SEC_TO_DATETIME deprecatedDateTimeMultiple((UA_Double)UA_DATETIME_SEC)
+#define UA_DATETIME_TO_USEC deprecatedDateTimeMultiple(1.0 / ((UA_Double)UA_DATETIME_USEC))
+#define UA_DATETIME_TO_MSEC deprecatedDateTimeMultiple(1.0 / ((UA_Double)UA_DATETIME_MSEC))
+#define UA_DATETIME_TO_SEC deprecatedDateTimeMultiple(1.0 / ((UA_Double)UA_DATETIME_SEC))
+
 #ifdef __cplusplus
 } // extern "C"
 #endif

+ 2 - 2
open62541.spec

@@ -6,7 +6,7 @@ License:  MPLv2.0
 URL:      http://open62541.org
 Source0:  https://github.com/open62541/open62541/archive/%{name}-%{version}.tar.gz
 
-BuildRequires: cmake3, python
+BuildRequires: cmake3, python, python-six
 
 %description
 open62541 is a C-based library (linking with C++ projects is possible)
@@ -49,7 +49,7 @@ rm examples/CMakeLists.txt
 %{_libdir}/pkgconfig/open62541.pc
 %dir %{_includedir}/open62541
 %{_includedir}/open62541/*
-%{_libdir}/cmake3/open62541*
+%{_libdir}/cmake/open62541*
 %dir %{_exec_prefix}/share/open62541
 %{_exec_prefix}/share/open62541/*
 

+ 162 - 51
plugins/ua_accesscontrol_default.c

@@ -1,49 +1,63 @@
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
- * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. 
+ *
+ *    Copyright 2016-2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ */
 
 #include "ua_accesscontrol_default.h"
 
 /* Example access control management. Anonymous and username / password login.
  * The access rights are maximally permissive. */
 
+typedef struct {
+    UA_Boolean allowAnonymous;
+    size_t usernamePasswordLoginSize;
+    UA_UsernamePasswordLogin *usernamePasswordLogin;
+} AccessControlContext;
+
 #define ANONYMOUS_POLICY "open62541-anonymous-policy"
 #define USERNAME_POLICY "open62541-username-policy"
-
-// TODO: There should be one definition of these strings in the endpoint.
-// Put the endpoint definition in the access control struct?
-#define UA_STRING_STATIC(s) {sizeof(s)-1, (UA_Byte*)s}
 const UA_String anonymous_policy = UA_STRING_STATIC(ANONYMOUS_POLICY);
 const UA_String username_policy = UA_STRING_STATIC(USERNAME_POLICY);
 
-typedef struct {
-    UA_String username;
-    UA_String password;
-} UA_UsernamePasswordLogin;
-
-const size_t usernamePasswordsSize = 2;
-UA_UsernamePasswordLogin usernamePasswords[2] = {
-    { UA_STRING_STATIC("user1"), UA_STRING_STATIC("password") },
-    { UA_STRING_STATIC("user2"), UA_STRING_STATIC("password1") } };
+/************************/
+/* Access Control Logic */
+/************************/
 
-UA_StatusCode
-activateSession_default(const UA_NodeId *sessionId,
+static UA_StatusCode
+activateSession_default(UA_Server *server, UA_AccessControl *ac,
+                        const UA_NodeId *sessionId,
                         const UA_ExtensionObject *userIdentityToken,
                         void **sessionContext) {
+    AccessControlContext *context = (AccessControlContext*)ac->context;
+
+    /* The empty token is interpreted as anonymous */
+    if(userIdentityToken->encoding == UA_EXTENSIONOBJECT_ENCODED_NOBODY) {
+        if(!context->allowAnonymous)
+            return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+
+        /* No userdata atm */
+        *sessionContext = NULL;
+        return UA_STATUSCODE_GOOD;
+    }
+
     /* Could the token be decoded? */
     if(userIdentityToken->encoding < UA_EXTENSIONOBJECT_DECODED)
         return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
 
     /* Anonymous login */
-    if(userIdentityToken->content.decoded.type ==
-       &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN]) {
-        const UA_AnonymousIdentityToken *token =
-            (UA_AnonymousIdentityToken*)userIdentityToken->content.decoded.data;
+    if(userIdentityToken->content.decoded.type == &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN]) {
+        if(!context->allowAnonymous)
+            return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+
+        const UA_AnonymousIdentityToken *token = (UA_AnonymousIdentityToken*)
+            userIdentityToken->content.decoded.data;
 
         /* Compatibility notice: Siemens OPC Scout v10 provides an empty
          * policyId. This is not compliant. For compatibility, assume that empty
          * policyId == ANONYMOUS_POLICY */
-        if(token->policyId.data &&
-           !UA_String_equal(&token->policyId, &anonymous_policy))
+        if(token->policyId.data && !UA_String_equal(&token->policyId, &anonymous_policy))
             return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
 
         /* No userdata atm */
@@ -52,24 +66,26 @@ activateSession_default(const UA_NodeId *sessionId,
     }
 
     /* Username and password */
-    if(userIdentityToken->content.decoded.type ==
-       &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]) {
-        const UA_UserNameIdentityToken *token =
+    if(userIdentityToken->content.decoded.type == &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]) {
+        const UA_UserNameIdentityToken *userToken =
             (UA_UserNameIdentityToken*)userIdentityToken->content.decoded.data;
-        if(!UA_String_equal(&token->policyId, &username_policy) || token->encryptionAlgorithm.length > 0)
+
+        if(!UA_String_equal(&userToken->policyId, &username_policy))
+            return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+
+        /* TODO: Support encrypted username/password over unencrypted SecureChannels */
+        if(userToken->encryptionAlgorithm.length > 0)
             return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
 
         /* Empty username and password */
-        if(token->userName.length == 0 && token->password.length == 0)
+        if(userToken->userName.length == 0 && userToken->password.length == 0)
             return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
 
         /* Try to match username/pw */
         UA_Boolean match = false;
-        for(size_t i = 0; i < usernamePasswordsSize; i++) {
-            const UA_String *user = &usernamePasswords[i].username;
-            const UA_String *pw = &usernamePasswords[i].password;
-            if(UA_String_equal(&token->userName, user) &&
-               UA_String_equal(&token->password, pw)) {
+        for(size_t i = 0; i < context->usernamePasswordLoginSize; i++) {
+            if(UA_String_equal(&userToken->userName, &context->usernamePasswordLogin[i].username) &&
+               UA_String_equal(&userToken->password, &context->usernamePasswordLogin[i].password)) {
                 match = true;
                 break;
             }
@@ -86,56 +102,151 @@ activateSession_default(const UA_NodeId *sessionId,
     return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
 }
 
-void
-closeSession_default(const UA_NodeId *sessionId, void *sessionContext) {
+static void
+closeSession_default(UA_Server *server, UA_AccessControl *ac,
+                     const UA_NodeId *sessionId, void *sessionContext) {
     /* no context to clean up */
 }
 
-UA_UInt32
-getUserRightsMask_default(const UA_NodeId *sessionId, void *sessionContext,
+static UA_UInt32
+getUserRightsMask_default(UA_Server *server, UA_AccessControl *ac,
+                          const UA_NodeId *sessionId, void *sessionContext,
                           const UA_NodeId *nodeId, void *nodeContext) {
     return 0xFFFFFFFF;
 }
 
-UA_Byte
-getUserAccessLevel_default(const UA_NodeId *sessionId, void *sessionContext,
+static UA_Byte
+getUserAccessLevel_default(UA_Server *server, UA_AccessControl *ac,
+                           const UA_NodeId *sessionId, void *sessionContext,
                            const UA_NodeId *nodeId, void *nodeContext) {
     return 0xFF;
 }
 
-UA_Boolean
-getUserExecutable_default(const UA_NodeId *sessionId, void *sessionContext,
+static UA_Boolean
+getUserExecutable_default(UA_Server *server, UA_AccessControl *ac,
+                          const UA_NodeId *sessionId, void *sessionContext,
                           const UA_NodeId *methodId, void *methodContext) {
     return true;
 }
 
-UA_Boolean
-getUserExecutableOnObject_default(const UA_NodeId *sessionId, void *sessionContext,
+static UA_Boolean
+getUserExecutableOnObject_default(UA_Server *server, UA_AccessControl *ac,
+                                  const UA_NodeId *sessionId, void *sessionContext,
                                   const UA_NodeId *methodId, void *methodContext,
                                   const UA_NodeId *objectId, void *objectContext) {
     return true;
 }
 
-UA_Boolean
-allowAddNode_default(const UA_NodeId *sessionId, void *sessionContext,
+static UA_Boolean
+allowAddNode_default(UA_Server *server, UA_AccessControl *ac,
+                     const UA_NodeId *sessionId, void *sessionContext,
                      const UA_AddNodesItem *item) {
     return true;
 }
 
-UA_Boolean
-allowAddReference_default(const UA_NodeId *sessionId, void *sessionContext,
+static UA_Boolean
+allowAddReference_default(UA_Server *server, UA_AccessControl *ac,
+                          const UA_NodeId *sessionId, void *sessionContext,
                           const UA_AddReferencesItem *item) {
     return true;
 }
 
-UA_Boolean
-allowDeleteNode_default(const UA_NodeId *sessionId, void *sessionContext,
+static UA_Boolean
+allowDeleteNode_default(UA_Server *server, UA_AccessControl *ac,
+                        const UA_NodeId *sessionId, void *sessionContext,
                         const UA_DeleteNodesItem *item) {
     return true;
 }
-      
-UA_Boolean
-allowDeleteReference_default(const UA_NodeId *sessionId, void *sessionContext,
+
+static UA_Boolean
+allowDeleteReference_default(UA_Server *server, UA_AccessControl *ac,
+                             const UA_NodeId *sessionId, void *sessionContext,
                              const UA_DeleteReferencesItem *item) {
     return true;
 }
+
+/***************************************/
+/* Create Delete Access Control Plugin */
+/***************************************/
+
+static void deleteMembers_default(UA_AccessControl *ac) {
+    UA_Array_delete((void*)(uintptr_t)ac->userTokenPolicies,
+                    ac->userTokenPoliciesSize,
+                    &UA_TYPES[UA_TYPES_USERTOKENPOLICY]);
+
+    AccessControlContext *context = (AccessControlContext*)ac->context;
+    for(size_t i = 0; i < context->usernamePasswordLoginSize; i++) {
+        UA_String_deleteMembers(&context->usernamePasswordLogin[i].username);
+        UA_String_deleteMembers(&context->usernamePasswordLogin[i].password);
+    }
+    if(context->usernamePasswordLoginSize > 0)
+        UA_free(context->usernamePasswordLogin);
+    UA_free(ac->context);
+}
+
+UA_AccessControl
+UA_AccessControl_default(UA_Boolean allowAnonymous, size_t usernamePasswordLoginSize,
+                         const UA_UsernamePasswordLogin *usernamePasswordLogin) {
+    AccessControlContext *context = (AccessControlContext*)
+        UA_malloc(sizeof(AccessControlContext));
+    
+    UA_AccessControl ac;
+    memset(&ac, 0, sizeof(ac));
+    ac.context = context;
+    ac.deleteMembers = deleteMembers_default;
+    ac.activateSession = activateSession_default;
+    ac.closeSession = closeSession_default;
+    ac.getUserRightsMask = getUserRightsMask_default;
+    ac.getUserAccessLevel = getUserAccessLevel_default;
+    ac.getUserExecutable = getUserExecutable_default;
+    ac.getUserExecutableOnObject = getUserExecutableOnObject_default;
+    ac.allowAddNode = allowAddNode_default;
+    ac.allowAddReference = allowAddReference_default;
+    ac.allowDeleteNode = allowDeleteNode_default;
+    ac.allowDeleteReference = allowDeleteReference_default;
+
+    /* Allow anonymous? */
+    context->allowAnonymous = allowAnonymous;
+
+    /* Copy username/password to the access control plugin */
+    if(usernamePasswordLoginSize > 0) {
+        context->usernamePasswordLogin = (UA_UsernamePasswordLogin*)
+            UA_malloc(usernamePasswordLoginSize * sizeof(UA_UsernamePasswordLogin));
+        if(!context->usernamePasswordLogin)
+            return ac;
+        context->usernamePasswordLoginSize = usernamePasswordLoginSize;
+        for(size_t i = 0; i < usernamePasswordLoginSize; i++) {
+            UA_String_copy(&usernamePasswordLogin[i].username, &context->usernamePasswordLogin[i].username);
+            UA_String_copy(&usernamePasswordLogin[i].password, &context->usernamePasswordLogin[i].password);
+        }
+    }
+
+    /* Set the allowed policies */
+    size_t policies = 0;
+    if(allowAnonymous)
+        policies++;
+    if(usernamePasswordLoginSize > 0)
+        policies++;
+    ac.userTokenPoliciesSize = 0;
+    ac.userTokenPolicies = (UA_UserTokenPolicy *)
+        UA_Array_new(policies, &UA_TYPES[UA_TYPES_USERTOKENPOLICY]);
+    if(!ac.userTokenPolicies)
+        return ac;
+    ac.userTokenPoliciesSize = policies;
+
+    policies = 0;
+    if(allowAnonymous) {
+        ac.userTokenPolicies[policies].tokenType = UA_USERTOKENTYPE_ANONYMOUS;
+        ac.userTokenPolicies[policies].policyId = UA_STRING_ALLOC(ANONYMOUS_POLICY);
+        policies++;
+    }
+
+    if(usernamePasswordLoginSize > 0) {
+        ac.userTokenPolicies[policies].tokenType = UA_USERTOKENTYPE_USERNAME;
+        ac.userTokenPolicies[policies].policyId = UA_STRING_ALLOC(USERNAME_POLICY);
+        /* No encryption of username/password supported at the moment */
+        ac.userTokenPolicies[policies].securityPolicyUri =
+            UA_STRING_ALLOC("http://opcfoundation.org/UA/SecurityPolicy#None");
+    }
+    return ac;
+}

+ 17 - 41
plugins/ua_accesscontrol_default.h

@@ -1,55 +1,31 @@
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
- * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. 
+ *
+ *    Copyright 2016-2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ */
 
 #ifndef UA_ACCESSCONTROL_DEFAULT_H_
 #define UA_ACCESSCONTROL_DEFAULT_H_
 
 #include "ua_server.h"
+#include "ua_plugin_access_control.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-UA_StatusCode UA_EXPORT
-activateSession_default(const UA_NodeId *sessionId,
-                        const UA_ExtensionObject *userIdentityToken,
-                        void **sessionContext);
-
-void UA_EXPORT
-closeSession_default(const UA_NodeId *sessionId, void *sessionContext);
-
-UA_UInt32 UA_EXPORT
-getUserRightsMask_default(const UA_NodeId *sessionId, void *sessionContext,
-                          const UA_NodeId *nodeId, void *nodeContext);
-
-UA_Byte UA_EXPORT
-getUserAccessLevel_default(const UA_NodeId *sessionId, void *sessionContext,
-                          const UA_NodeId *nodeId, void *nodeContext);
-
-UA_Boolean UA_EXPORT
-getUserExecutable_default(const UA_NodeId *sessionId, void *sessionContext,
-                          const UA_NodeId *methodId, void *methodContext);
-
-UA_Boolean UA_EXPORT
-getUserExecutableOnObject_default(const UA_NodeId *sessionId, void *sessionContext,
-                                  const UA_NodeId *methodId, void *methodContext,
-                                  const UA_NodeId *objectId, void *objectContext);
-
-UA_Boolean UA_EXPORT
-allowAddNode_default(const UA_NodeId *sessionId, void *sessionContext,
-                     const UA_AddNodesItem *item);
-
-UA_Boolean UA_EXPORT
-allowAddReference_default(const UA_NodeId *sessionId, void *sessionContext,
-                          const UA_AddReferencesItem *item);
-
-UA_Boolean UA_EXPORT
-allowDeleteNode_default(const UA_NodeId *sessionId, void *sessionContext,
-                        const UA_DeleteNodesItem *item);
-
-UA_Boolean UA_EXPORT
-allowDeleteReference_default(const UA_NodeId *sessionId, void *sessionContext,
-                             const UA_DeleteReferencesItem *item);
+typedef struct {
+    UA_String username;
+    UA_String password;
+} UA_UsernamePasswordLogin;
+
+/* Default access control. The log-in can be anonymous or username-password. A
+ * logged-in user has all access rights. */
+UA_EXPORT UA_AccessControl
+UA_AccessControl_default(UA_Boolean allowAnonymous,
+                         size_t usernamePasswordLoginSize,
+                         const UA_UsernamePasswordLogin *usernamePasswordLogin);
 
 #ifdef __cplusplus
 }

+ 47 - 6
plugins/ua_clock.c

@@ -1,5 +1,10 @@
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
- * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. 
+ *
+ *    Copyright 2016-2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ *    Copyright 2017 (c) Thomas Stalder, Blue Time Concept SA
+ */
 
 /* Enable POSIX features */
 #if !defined(_XOPEN_SOURCE) && !defined(_WRS_KERNEL)
@@ -40,6 +45,10 @@
 
 #include "ua_types.h"
 
+#if defined(UA_FREERTOS)
+#include <task.h>
+#endif
+
 UA_DateTime UA_DateTime_now(void) {
 #if defined(_WIN32)
     /* Windows filetime has the same definition as UA_DateTime */
@@ -54,16 +63,38 @@ UA_DateTime UA_DateTime_now(void) {
 #else
     struct timeval tv;
     gettimeofday(&tv, NULL);
-    return (tv.tv_sec * UA_SEC_TO_DATETIME) + (tv.tv_usec * UA_USEC_TO_DATETIME) + UA_DATETIME_UNIX_EPOCH;
+    return (tv.tv_sec * UA_DATETIME_SEC) + (tv.tv_usec * UA_DATETIME_USEC) + UA_DATETIME_UNIX_EPOCH;
 #endif
 }
 
+/* Credit to https://stackoverflow.com/questions/13804095/get-the-time-zone-gmt-offset-in-c */
+UA_Int64 UA_DateTime_localTimeUtcOffset(void) {
+    time_t gmt, rawtime = time(NULL);
+
+#ifdef _WIN32
+    struct tm ptm;
+    gmtime_s(&ptm, &rawtime);
+    // Request that mktime() looksup dst in timezone database
+    ptm.tm_isdst = -1;
+    gmt = mktime(&ptm);
+#else
+    struct tm *ptm;
+    struct tm gbuf;
+    ptm = gmtime_r(&rawtime, &gbuf);
+    // Request that mktime() looksup dst in timezone database
+    ptm->tm_isdst = -1;
+    gmt = mktime(ptm);
+#endif
+
+    return (UA_Int64) (difftime(rawtime, gmt) * UA_DATETIME_SEC);
+}
+
 UA_DateTime UA_DateTime_nowMonotonic(void) {
 #if defined(_WIN32)
     LARGE_INTEGER freq, ticks;
     QueryPerformanceFrequency(&freq);
     QueryPerformanceCounter(&ticks);
-    UA_Double ticks2dt = UA_SEC_TO_DATETIME / (UA_Double)freq.QuadPart;
+    UA_Double ticks2dt = UA_DATETIME_SEC / (UA_Double)freq.QuadPart;
     return (UA_DateTime)(ticks.QuadPart * ticks2dt);
 #elif defined(__APPLE__) || defined(__MACH__)
     /* OS X does not have clock_gettime, use clock_get_time */
@@ -72,14 +103,24 @@ UA_DateTime UA_DateTime_nowMonotonic(void) {
     host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
     clock_get_time(cclock, &mts);
     mach_port_deallocate(mach_task_self(), cclock);
-    return (mts.tv_sec * UA_SEC_TO_DATETIME) + (mts.tv_nsec / 100);
+    return (mts.tv_sec * UA_DATETIME_SEC) + (mts.tv_nsec / 100);
 #elif !defined(CLOCK_MONOTONIC_RAW)
+# if defined(UA_FREERTOS)
+    portTickType TaskTime = xTaskGetTickCount();
+    UA_DateTimeStruct UATime;
+    UATime.milliSec = (UA_UInt16) TaskTime;
+    struct timespec ts;
+    ts.tv_sec = UATime.milliSec/1000;
+    ts.tv_nsec = (UATime.milliSec % 1000)* 1000000;
+    return (ts.tv_sec * UA_DATETIME_SEC) + (ts.tv_nsec / 100);
+# else
     struct timespec ts;
     clock_gettime(CLOCK_MONOTONIC, &ts);
-    return (ts.tv_sec * UA_SEC_TO_DATETIME) + (ts.tv_nsec / 100);
+    return (ts.tv_sec * UA_DATETIME_SEC) + (ts.tv_nsec / 100);
+# endif
 #else
     struct timespec ts;
     clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
-    return (ts.tv_sec * UA_SEC_TO_DATETIME) + (ts.tv_nsec / 100);
+    return (ts.tv_sec * UA_DATETIME_SEC) + (ts.tv_nsec / 100);
 #endif
 }

+ 412 - 74
plugins/ua_config_default.c

@@ -1,19 +1,29 @@
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
- * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
-
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
+ *
+ *    Copyright 2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2017 (c) Julian Grothoff
+ *    Copyright 2017-2018 (c) Mark Giraud, Fraunhofer IOSB
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ *    Copyright 2017 (c) Thomas Stalder, Blue Time Concept SA
+ *    Copyright 2018 (c) Daniel Feist, Precitec GmbH & Co. KG
+ */
+
+#include "ua_plugin_securitypolicy.h"
 #include "ua_config_default.h"
+#include "ua_client_config.h"
 #include "ua_log_stdout.h"
 #include "ua_network_tcp.h"
 #include "ua_accesscontrol_default.h"
+#include "ua_pki_certificate.h"
 #include "ua_nodestore_default.h"
-#include "ua_types_generated.h"
 #include "ua_securitypolicy_none.h"
-#include "ua_types.h"
-#include "ua_types_generated_handling.h"
-#include "ua_client_highlevel.h"
 
-#define ANONYMOUS_POLICY "open62541-anonymous-policy"
-#define USERNAME_POLICY "open62541-username-policy"
+#ifdef UA_ENABLE_ENCRYPTION
+#include "ua_securitypolicy_basic128rsa15.h"
+#include "ua_securitypolicy_basic256sha256.h"
+#endif
+
 
 /* Struct initialization works across ANSI C/C99/C++ if it is done when the
  * variable is first declared. Assigning values to existing structs is
@@ -25,7 +35,7 @@ UA_UINT32RANGE(UA_UInt32 min, UA_UInt32 max) {
 }
 
 static UA_INLINE UA_DurationRange
-UA_DURATIONRANGE(UA_Double min, UA_Double max) {
+UA_DURATIONRANGE(UA_Duration min, UA_Duration max) {
     UA_DurationRange range = {min, max};
     return range;
 }
@@ -61,33 +71,24 @@ createSecurityPolicyNoneEndpoint(UA_ServerConfig *conf, UA_Endpoint *endpoint,
                                  const UA_ByteString localCertificate) {
     UA_EndpointDescription_init(&endpoint->endpointDescription);
 
-    UA_SecurityPolicy_None(&endpoint->securityPolicy, localCertificate, conf->logger);
+    UA_SecurityPolicy_None(&endpoint->securityPolicy, NULL, localCertificate, conf->logger);
     endpoint->endpointDescription.securityMode = UA_MESSAGESECURITYMODE_NONE;
     endpoint->endpointDescription.securityPolicyUri =
         UA_STRING_ALLOC("http://opcfoundation.org/UA/SecurityPolicy#None");
     endpoint->endpointDescription.transportProfileUri =
         UA_STRING_ALLOC("http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary");
 
-    /* enable anonymous and username/password */
-    size_t policies = 2;
-    endpoint->endpointDescription.userIdentityTokens = (UA_UserTokenPolicy*)
-        UA_Array_new(policies, &UA_TYPES[UA_TYPES_USERTOKENPOLICY]);
-    if(!endpoint->endpointDescription.userIdentityTokens)
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-    endpoint->endpointDescription.userIdentityTokensSize = policies;
-
-    endpoint->endpointDescription.userIdentityTokens[0].tokenType =
-        UA_USERTOKENTYPE_ANONYMOUS;
-    endpoint->endpointDescription.userIdentityTokens[0].policyId =
-        UA_STRING_ALLOC(ANONYMOUS_POLICY);
-
-    endpoint->endpointDescription.userIdentityTokens[1].tokenType =
-        UA_USERTOKENTYPE_USERNAME;
-    endpoint->endpointDescription.userIdentityTokens[1].policyId =
-        UA_STRING_ALLOC(USERNAME_POLICY);
+    /* Enable all login mechanisms from the access control plugin  */
+    UA_StatusCode retval = UA_Array_copy(conf->accessControl.userTokenPolicies,
+                                         conf->accessControl.userTokenPoliciesSize,
+                                         (void **)&endpoint->endpointDescription.userIdentityTokens,
+                                         &UA_TYPES[UA_TYPES_USERTOKENPOLICY]);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+    endpoint->endpointDescription.userIdentityTokensSize =
+        conf->accessControl.userTokenPoliciesSize;
 
     UA_String_copy(&localCertificate, &endpoint->endpointDescription.serverCertificate);
-
     UA_ApplicationDescription_copy(&conf->applicationDescription,
                                    &endpoint->endpointDescription.server);
 
@@ -95,17 +96,103 @@ createSecurityPolicyNoneEndpoint(UA_ServerConfig *conf, UA_Endpoint *endpoint,
 }
 
 void
-UA_ServerConfig_set_customHostname(UA_ServerConfig *config, const UA_String customHostname){
+UA_ServerConfig_set_customHostname(UA_ServerConfig *config, const UA_String customHostname) {
     if(!config)
         return;
     UA_String_deleteMembers(&config->customHostname);
     UA_String_copy(&customHostname, &config->customHostname);
 }
 
-UA_ServerConfig *
-UA_ServerConfig_new_minimal(UA_UInt16 portNumber,
-                            const UA_ByteString *certificate) {
-    UA_ServerConfig *conf = (UA_ServerConfig*)UA_malloc(sizeof(UA_ServerConfig));
+#ifdef UA_ENABLE_ENCRYPTION
+
+static UA_StatusCode
+createSecurityPolicyBasic128Rsa15Endpoint(UA_ServerConfig *const conf,
+                                          UA_Endpoint *endpoint,
+                                          UA_MessageSecurityMode securityMode,
+                                          const UA_ByteString localCertificate,
+                                          const UA_ByteString localPrivateKey) {
+    UA_EndpointDescription_init(&endpoint->endpointDescription);
+
+    UA_StatusCode retval =
+        UA_SecurityPolicy_Basic128Rsa15(&endpoint->securityPolicy, &conf->certificateVerification,
+                                        localCertificate, localPrivateKey, conf->logger);
+    if(retval != UA_STATUSCODE_GOOD) {
+        endpoint->securityPolicy.deleteMembers(&endpoint->securityPolicy);
+        return retval;
+    }
+
+    endpoint->endpointDescription.securityMode = securityMode;
+    endpoint->endpointDescription.securityPolicyUri =
+        UA_STRING_ALLOC("http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15");
+    endpoint->endpointDescription.transportProfileUri =
+        UA_STRING_ALLOC("http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary");
+
+    /* Enable all login mechanisms from the access control plugin  */
+    retval = UA_Array_copy(conf->accessControl.userTokenPolicies,
+                           conf->accessControl.userTokenPoliciesSize,
+                           (void **)&endpoint->endpointDescription.userIdentityTokens,
+                           &UA_TYPES[UA_TYPES_USERTOKENPOLICY]);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+    endpoint->endpointDescription.userIdentityTokensSize =
+        conf->accessControl.userTokenPoliciesSize;
+
+    UA_String_copy(&localCertificate, &endpoint->endpointDescription.serverCertificate);
+    UA_ApplicationDescription_copy(&conf->applicationDescription,
+                                   &endpoint->endpointDescription.server);
+
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode
+createSecurityPolicyBasic256Sha256Endpoint(UA_ServerConfig *const conf,
+                                           UA_Endpoint *endpoint,
+                                           UA_MessageSecurityMode securityMode,
+                                           const UA_ByteString localCertificate,
+                                           const UA_ByteString localPrivateKey) {
+    UA_EndpointDescription_init(&endpoint->endpointDescription);
+
+    UA_StatusCode retval =
+        UA_SecurityPolicy_Basic256Sha256(&endpoint->securityPolicy, &conf->certificateVerification, localCertificate,
+                                         localPrivateKey, conf->logger);
+    if(retval != UA_STATUSCODE_GOOD) {
+        endpoint->securityPolicy.deleteMembers(&endpoint->securityPolicy);
+        return retval;
+    }
+
+    endpoint->endpointDescription.securityMode = securityMode;
+    endpoint->endpointDescription.securityPolicyUri =
+        UA_STRING_ALLOC("http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256");
+    endpoint->endpointDescription.transportProfileUri =
+        UA_STRING_ALLOC("http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary");
+
+    /* Enable all login mechanisms from the access control plugin  */
+    retval = UA_Array_copy(conf->accessControl.userTokenPolicies,
+                           conf->accessControl.userTokenPoliciesSize,
+                           (void **)&endpoint->endpointDescription.userIdentityTokens,
+                           &UA_TYPES[UA_TYPES_USERTOKENPOLICY]);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+    endpoint->endpointDescription.userIdentityTokensSize =
+        conf->accessControl.userTokenPoliciesSize;
+
+    UA_String_copy(&localCertificate, &endpoint->endpointDescription.serverCertificate);
+    UA_ApplicationDescription_copy(&conf->applicationDescription,
+                                   &endpoint->endpointDescription.server);
+
+    return UA_STATUSCODE_GOOD;
+}
+
+#endif
+
+const size_t usernamePasswordsSize = 2;
+UA_UsernamePasswordLogin usernamePasswords[2] = {
+    {UA_STRING_STATIC("user1"), UA_STRING_STATIC("password")},
+    {UA_STRING_STATIC("user2"), UA_STRING_STATIC("password1")}};
+
+static UA_ServerConfig *
+createDefaultConfig(void) {
+    UA_ServerConfig *conf = (UA_ServerConfig *)UA_malloc(sizeof(UA_ServerConfig));
     if(!conf)
         return NULL;
 
@@ -123,12 +210,14 @@ UA_ServerConfig_new_minimal(UA_UInt16 portNumber,
     conf->buildInfo.softwareVersion =
         UA_STRING_ALLOC(VERSION(UA_OPEN62541_VER_MAJOR, UA_OPEN62541_VER_MINOR,
                                 UA_OPEN62541_VER_PATCH, UA_OPEN62541_VER_LABEL));
-    conf->buildInfo.buildNumber = UA_STRING_ALLOC(__DATE__ " " __TIME__);
+    conf->buildInfo.buildNumber = UA_STRING_ALLOC(__DATE__
+                                                      " "
+                                                      __TIME__);
     conf->buildInfo.buildDate = 0;
 
     conf->applicationDescription.applicationUri = UA_STRING_ALLOC(APPLICATION_URI);
     conf->applicationDescription.productUri = UA_STRING_ALLOC(PRODUCT_URI);
-    conf->applicationDescription.applicationName = 
+    conf->applicationDescription.applicationName =
         UA_LOCALIZEDTEXT_ALLOC("en", APPLICATION_NAME);
     conf->applicationDescription.applicationType = UA_APPLICATIONTYPE_SERVER;
     /* conf->applicationDescription.gatewayServerUri = UA_STRING_NULL; */
@@ -150,27 +239,20 @@ UA_ServerConfig_new_minimal(UA_UInt16 portNumber,
     /* conf->networkLayersSize = 0; */
     /* conf->networkLayers = NULL; */
     /* conf->customHostname = UA_STRING_NULL; */
- 
+
     /* Endpoints */
     /* conf->endpoints = {0, NULL}; */
 
+    /* Certificate Verification that accepts every certificate. Can be
+     * overwritten when the policy is specialized. */
+    UA_CertificateVerification_AcceptAll(&conf->certificateVerification);
+
     /* Global Node Lifecycle */
     conf->nodeLifecycle.constructor = NULL;
     conf->nodeLifecycle.destructor = NULL;
 
-    /* Access Control */
-    conf->accessControl.enableAnonymousLogin = true;
-    conf->accessControl.enableUsernamePasswordLogin = true;
-    conf->accessControl.activateSession = activateSession_default;
-    conf->accessControl.closeSession = closeSession_default;
-    conf->accessControl.getUserRightsMask = getUserRightsMask_default;
-    conf->accessControl.getUserAccessLevel = getUserAccessLevel_default;
-    conf->accessControl.getUserExecutable = getUserExecutable_default;
-    conf->accessControl.getUserExecutableOnObject = getUserExecutableOnObject_default;
-    conf->accessControl.allowAddNode = allowAddNode_default;
-    conf->accessControl.allowAddReference = allowAddReference_default;
-    conf->accessControl.allowDeleteNode = allowDeleteNode_default;
-    conf->accessControl.allowDeleteReference = allowDeleteReference_default;
+    /* Access Control. Anonymous Login only. */
+    conf->accessControl = UA_AccessControl_default(true, usernamePasswordsSize, usernamePasswords);
 
     /* Limits for SecureChannels */
     conf->maxSecureChannels = 40;
@@ -197,26 +279,43 @@ UA_ServerConfig_new_minimal(UA_UInt16 portNumber,
 
     /* --> Finish setting the default static config <-- */
 
+    return conf;
+}
+
+static UA_StatusCode
+addDefaultNetworkLayers(UA_ServerConfig *conf, UA_UInt16 portNumber) {
+    /* Add a network layer */
+    conf->networkLayers = (UA_ServerNetworkLayer *)
+        UA_malloc(sizeof(UA_ServerNetworkLayer));
+    if(!conf->networkLayers)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+
+    conf->networkLayers[0] =
+        UA_ServerNetworkLayerTCP(UA_ConnectionConfig_default, portNumber, conf->logger);
+    conf->networkLayersSize = 1;
+
+    return UA_STATUSCODE_GOOD;
+}
+
+UA_ServerConfig *
+UA_ServerConfig_new_minimal(UA_UInt16 portNumber,
+                            const UA_ByteString *certificate) {
+    UA_ServerConfig *conf = createDefaultConfig();
+
     UA_StatusCode retval = UA_Nodestore_default_new(&conf->nodestore);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_ServerConfig_delete(conf);
         return NULL;
     }
 
-    /* Add a network layer */
-    conf->networkLayers = (UA_ServerNetworkLayer*)
-        UA_malloc(sizeof(UA_ServerNetworkLayer));
-    if(!conf->networkLayers) {
+    if(addDefaultNetworkLayers(conf, portNumber) != UA_STATUSCODE_GOOD) {
         UA_ServerConfig_delete(conf);
         return NULL;
     }
-    conf->networkLayers[0] =
-        UA_ServerNetworkLayerTCP(UA_ConnectionConfig_default, portNumber);
-    conf->networkLayersSize = 1;
 
     /* Allocate the endpoint */
     conf->endpointsSize = 1;
-    conf->endpoints = (UA_Endpoint*)UA_malloc(sizeof(UA_Endpoint));
+    conf->endpoints = (UA_Endpoint *)UA_malloc(sizeof(UA_Endpoint));
     if(!conf->endpoints) {
         UA_ServerConfig_delete(conf);
         return NULL;
@@ -236,6 +335,239 @@ UA_ServerConfig_new_minimal(UA_UInt16 portNumber,
     return conf;
 }
 
+#ifdef UA_ENABLE_ENCRYPTION
+
+UA_ServerConfig *
+UA_ServerConfig_new_basic128rsa15(UA_UInt16 portNumber,
+                                  const UA_ByteString *certificate,
+                                  const UA_ByteString *privateKey,
+                                  const UA_ByteString *trustList,
+                                  size_t trustListSize,
+                                  const UA_ByteString *revocationList,
+                                  size_t revocationListSize) {
+    UA_ServerConfig *conf = createDefaultConfig();
+
+    UA_StatusCode retval = UA_CertificateVerification_Trustlist(&conf->certificateVerification,
+                                                                trustList, trustListSize,
+                                                                revocationList, revocationListSize);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ServerConfig_delete(conf);
+        return NULL;
+    }
+
+    retval = UA_Nodestore_default_new(&conf->nodestore);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ServerConfig_delete(conf);
+        return NULL;
+    }
+
+    if(addDefaultNetworkLayers(conf, portNumber) != UA_STATUSCODE_GOOD) {
+        UA_ServerConfig_delete(conf);
+        return NULL;
+    }
+
+    if(trustListSize == 0)
+        UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
+                       "No CA trust-list provided. Any remote certificate will be accepted.");
+
+    /* Allocate the endpoints */
+    conf->endpointsSize = 0;
+    conf->endpoints = (UA_Endpoint *)UA_malloc(sizeof(UA_Endpoint) * 3);
+    if(!conf->endpoints) {
+        UA_ServerConfig_delete(conf);
+        return NULL;
+    }
+
+    /* Populate the endpoints */
+    ++conf->endpointsSize;
+    retval = createSecurityPolicyNoneEndpoint(conf, &conf->endpoints[0], *certificate);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ServerConfig_delete(conf);
+        return NULL;
+    }
+
+    ++conf->endpointsSize;
+    retval = createSecurityPolicyBasic128Rsa15Endpoint(conf, &conf->endpoints[1],
+                                                       UA_MESSAGESECURITYMODE_SIGN, *certificate,
+                                                       *privateKey);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ServerConfig_delete(conf);
+        return NULL;
+    }
+
+    ++conf->endpointsSize;
+    retval = createSecurityPolicyBasic128Rsa15Endpoint(conf, &conf->endpoints[2],
+                                                       UA_MESSAGESECURITYMODE_SIGNANDENCRYPT, *certificate,
+                                                       *privateKey);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ServerConfig_delete(conf);
+        return NULL;
+    }
+
+    return conf;
+}
+
+UA_ServerConfig *
+UA_ServerConfig_new_basic256sha256(UA_UInt16 portNumber,
+                                   const UA_ByteString *certificate,
+                                   const UA_ByteString *privateKey,
+                                   const UA_ByteString *trustList,
+                                   size_t trustListSize,
+                                   const UA_ByteString *revocationList,
+                                   size_t revocationListSize) {
+    UA_ServerConfig *conf = createDefaultConfig();
+
+    UA_StatusCode retval = UA_CertificateVerification_Trustlist(&conf->certificateVerification,
+                                                                trustList, trustListSize,
+                                                                revocationList, revocationListSize);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ServerConfig_delete(conf);
+        return NULL;
+    }
+
+    retval = UA_Nodestore_default_new(&conf->nodestore);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ServerConfig_delete(conf);
+        return NULL;
+    }
+
+    if(addDefaultNetworkLayers(conf, portNumber) != UA_STATUSCODE_GOOD) {
+        UA_ServerConfig_delete(conf);
+        return NULL;
+    }
+
+    if(trustListSize == 0)
+        UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
+                       "No CA trust-list provided. Any remote certificate will be accepted.");
+
+    /* Allocate the endpoints */
+    conf->endpointsSize = 0;
+    conf->endpoints = (UA_Endpoint *)UA_malloc(sizeof(UA_Endpoint) * 3);
+    if(!conf->endpoints) {
+        UA_ServerConfig_delete(conf);
+        return NULL;
+    }
+
+    /* Populate the endpoints */
+    ++conf->endpointsSize;
+    retval = createSecurityPolicyNoneEndpoint(conf, &conf->endpoints[0], *certificate);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ServerConfig_delete(conf);
+        return NULL;
+    }
+
+    ++conf->endpointsSize;
+    retval = createSecurityPolicyBasic256Sha256Endpoint(conf, &conf->endpoints[1],
+                                                        UA_MESSAGESECURITYMODE_SIGN, *certificate,
+                                                        *privateKey);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ServerConfig_delete(conf);
+        return NULL;
+    }
+
+    ++conf->endpointsSize;
+    retval = createSecurityPolicyBasic256Sha256Endpoint(conf, &conf->endpoints[2],
+                                                        UA_MESSAGESECURITYMODE_SIGNANDENCRYPT, *certificate,
+                                                        *privateKey);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ServerConfig_delete(conf);
+        return NULL;
+    }
+
+    return conf;
+}
+
+UA_ServerConfig *
+UA_ServerConfig_new_allSecurityPolicies(UA_UInt16 portNumber,
+                                        const UA_ByteString *certificate,
+                                        const UA_ByteString *privateKey,
+                                        const UA_ByteString *trustList,
+                                        size_t trustListSize,
+                                        const UA_ByteString *revocationList,
+                                        size_t revocationListSize) {
+    UA_ServerConfig *conf = createDefaultConfig();
+
+    UA_StatusCode retval = UA_CertificateVerification_Trustlist(&conf->certificateVerification,
+                                                                trustList, trustListSize,
+                                                                revocationList, revocationListSize);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ServerConfig_delete(conf);
+        return NULL;
+    }
+
+    retval = UA_Nodestore_default_new(&conf->nodestore);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ServerConfig_delete(conf);
+        return NULL;
+    }
+
+    if(addDefaultNetworkLayers(conf, portNumber) != UA_STATUSCODE_GOOD) {
+        UA_ServerConfig_delete(conf);
+        return NULL;
+    }
+
+    if(trustListSize == 0)
+        UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
+                       "No CA trust-list provided. Any remote certificate will be accepted.");
+
+    /* Allocate the endpoints */
+    conf->endpointsSize = 0;
+    conf->endpoints = (UA_Endpoint *)UA_malloc(sizeof(UA_Endpoint) * 5);
+    if(!conf->endpoints) {
+        UA_ServerConfig_delete(conf);
+        return NULL;
+    }
+
+    /* Populate the endpoints */
+    retval = createSecurityPolicyNoneEndpoint(conf, &conf->endpoints[conf->endpointsSize], *certificate);
+    ++conf->endpointsSize;
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ServerConfig_delete(conf);
+        return NULL;
+    }
+
+    retval = createSecurityPolicyBasic128Rsa15Endpoint(conf, &conf->endpoints[conf->endpointsSize],
+                                                       UA_MESSAGESECURITYMODE_SIGN, *certificate,
+                                                       *privateKey);
+    ++conf->endpointsSize;
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ServerConfig_delete(conf);
+        return NULL;
+    }
+
+    retval = createSecurityPolicyBasic128Rsa15Endpoint(conf, &conf->endpoints[conf->endpointsSize],
+                                                       UA_MESSAGESECURITYMODE_SIGNANDENCRYPT, *certificate,
+                                                       *privateKey);
+    ++conf->endpointsSize;
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ServerConfig_delete(conf);
+        return NULL;
+    }
+
+    retval = createSecurityPolicyBasic256Sha256Endpoint(conf, &conf->endpoints[conf->endpointsSize],
+                                                        UA_MESSAGESECURITYMODE_SIGN, *certificate,
+                                                        *privateKey);
+    ++conf->endpointsSize;
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ServerConfig_delete(conf);
+        return NULL;
+    }
+
+    retval = createSecurityPolicyBasic256Sha256Endpoint(conf, &conf->endpoints[conf->endpointsSize],
+                                                        UA_MESSAGESECURITYMODE_SIGNANDENCRYPT, *certificate,
+                                                        *privateKey);
+    ++conf->endpointsSize;
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ServerConfig_delete(conf);
+        return NULL;
+    }
+
+    return conf;
+}
+
+
+#endif
+
 void
 UA_ServerConfig_delete(UA_ServerConfig *config) {
     if(!config)
@@ -281,6 +613,12 @@ UA_ServerConfig_delete(UA_ServerConfig *config) {
     config->endpoints = NULL;
     config->endpointsSize = 0;
 
+    /* Certificate Validation */
+    config->certificateVerification.deleteMembers(&config->certificateVerification);
+
+    /* Access Control */
+    config->accessControl.deleteMembers(&config->accessControl);
+
     UA_free(config);
 }
 
@@ -292,31 +630,31 @@ const UA_ClientConfig UA_ClientConfig_default = {
     5000, /* .timeout, 5 seconds */
     10 * 60 * 1000, /* .secureChannelLifeTime, 10 minutes */
     UA_Log_Stdout, /* .logger */
-    /* .localConnectionConfig */
-    {0, /* .protocolVersion */
+    { /* .localConnectionConfig */
+        0, /* .protocolVersion */
         65535, /* .sendBufferSize, 64k per chunk */
         65535, /* .recvBufferSize, 64k per chunk */
         0, /* .maxMessageSize, 0 -> unlimited */
-        0}, /* .maxChunkCount, 0 -> unlimited */
+        0 /* .maxChunkCount, 0 -> unlimited */
+    },
     UA_ClientConnectionTCP, /* .connectionFunc */
 
     0, /* .customDataTypesSize */
-    NULL /*.customDataTypes */
-};
-
-/****************************************/
-/* Default Client Subscription Settings */
-/****************************************/
+    NULL, /*.customDataTypes */
 
+    NULL, /*.stateCallback */
 #ifdef UA_ENABLE_SUBSCRIPTIONS
+    NULL, /*.subscriptionInactivityCallback */
+#endif
 
-const UA_SubscriptionSettings UA_SubscriptionSettings_default = {
-    500.0, /* .requestedPublishingInterval */
-    10000, /* .requestedLifetimeCount */
-    1, /* .requestedMaxKeepAliveCount */
-    10, /* .maxNotificationsPerPublish */
-    true, /* .publishingEnabled */
-    0 /* .priority */
-};
+    NULL,  /*.clientContext */
 
+#ifdef UA_ENABLE_SUBSCRIPTIONS
+    10 /* .outStandingPublishRequests */
 #endif
+};
+
+UA_ClientConfig UA_Server_getClientConfig(void)
+{
+    return UA_ClientConfig_default;
+}

+ 39 - 4
plugins/ua_config_default.h

@@ -1,5 +1,10 @@
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
- * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. 
+ *
+ *    Copyright 2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ *    Copyright 2018 (c) Mark Giraud, Fraunhofer IOSB
+ */
 
 #ifndef UA_CONFIG_DEFAULT_H_
 #define UA_CONFIG_DEFAULT_H_
@@ -32,8 +37,38 @@ extern const UA_EXPORT UA_ConnectionConfig UA_ConnectionConfig_default;
  * @param certificate Optional certificate for the server endpoint. Can be
  *        ``NULL``. */
 UA_EXPORT UA_ServerConfig *
-UA_ServerConfig_new_minimal(UA_UInt16 portNumber,
-                            const UA_ByteString *certificate);
+UA_ServerConfig_new_minimal(UA_UInt16 portNumber, const UA_ByteString *certificate);
+
+#ifdef UA_ENABLE_ENCRYPTION
+
+UA_EXPORT UA_ServerConfig *
+UA_ServerConfig_new_basic128rsa15(UA_UInt16 portNumber,
+                                  const UA_ByteString *certificate,
+                                  const UA_ByteString *privateKey,
+                                  const UA_ByteString *trustList,
+                                  size_t trustListSize,
+                                  const UA_ByteString *revocationList,
+                                  size_t revocationListSize);
+
+UA_EXPORT UA_ServerConfig *
+UA_ServerConfig_new_basic256sha256(UA_UInt16 portNumber,
+                                   const UA_ByteString *certificate,
+                                   const UA_ByteString *privateKey,
+                                   const UA_ByteString *trustList,
+                                   size_t trustListSize,
+                                   const UA_ByteString *revocationList,
+                                   size_t revocationListSize);
+
+UA_EXPORT UA_ServerConfig *
+UA_ServerConfig_new_allSecurityPolicies(UA_UInt16 portNumber,
+                                        const UA_ByteString *certificate,
+                                        const UA_ByteString *privateKey,
+                                        const UA_ByteString *trustList,
+                                        size_t trustListSize,
+                                        const UA_ByteString *revocationList,
+                                        size_t revocationListSize);
+
+#endif
 
 /* Creates a server config on the default port 4840 with no server
  * certificate. */
@@ -50,7 +85,7 @@ UA_ServerConfig_new_default(void) {
 UA_EXPORT void
 UA_ServerConfig_set_customHostname(UA_ServerConfig *config,
                                    const UA_String customHostname);
-  
+
 /* Frees allocated memory in the server config */
 UA_EXPORT void
 UA_ServerConfig_delete(UA_ServerConfig *config);

+ 4 - 1
plugins/ua_debug_dump_pkgs.c

@@ -1,5 +1,8 @@
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
- * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. 
+ *
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ */
 
 #include "ua_util.h"
 

+ 4 - 1
plugins/ua_log_socket_error.h

@@ -1,5 +1,8 @@
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
- * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. 
+ *
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ */
 
 #ifndef UA_LOG_SOCKET_ERROR_H_
 #define UA_LOG_SOCKET_ERROR_H_

+ 13 - 5
plugins/ua_log_stdout.c

@@ -1,8 +1,11 @@
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
- * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. 
+ *
+ *    Copyright 2016-2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2017 (c) Thomas Stalder, Blue Time Concept SA
+ */
 
 #include <stdio.h>
-#include <stdarg.h>
 #include "ua_log_stdout.h"
 #include "ua_types_generated.h"
 #include "ua_types_generated_handling.h"
@@ -47,16 +50,21 @@ __attribute__((__format__(__printf__, 3 , 0)))
 void
 UA_Log_Stdout(UA_LogLevel level, UA_LogCategory category,
               const char *msg, va_list args) {
-    UA_String t = UA_DateTime_toString(UA_DateTime_now());
+    UA_Int64 tOffset = UA_DateTime_localTimeUtcOffset();
+    UA_DateTimeStruct dts = UA_DateTime_toStruct(UA_DateTime_now() + tOffset);
+
 #ifdef UA_ENABLE_MULTITHREADING
     pthread_mutex_lock(&printf_mutex);
 #endif
-    printf("[%.23s] %s/%s" ANSI_COLOR_RESET "\t", t.data, logLevelNames[level], logCategoryNames[category]);
+
+    printf("[%04u-%02u-%02u %02u:%02u:%02u.%03u (UTC%+05d)] %s/%s" ANSI_COLOR_RESET "\t",
+           dts.year, dts.month, dts.day, dts.hour, dts.min, dts.sec, dts.milliSec,
+           (int)(tOffset / UA_DATETIME_SEC / 36), logLevelNames[level], logCategoryNames[category]);
     vprintf(msg, args);
     printf("\n");
     fflush(stdout);
+
 #ifdef UA_ENABLE_MULTITHREADING
     pthread_mutex_unlock(&printf_mutex);
 #endif
-    UA_ByteString_deleteMembers(&t);
 }

+ 4 - 1
plugins/ua_log_stdout.h

@@ -1,5 +1,8 @@
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
- * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. 
+ *
+ *    Copyright 2016 (c) Julius Pfrommer, Fraunhofer IOSB
+ */
 
 #ifndef UA_LOG_STDOUT_H_
 #define UA_LOG_STDOUT_H_

+ 173 - 95
plugins/ua_network_tcp.c

@@ -1,5 +1,12 @@
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
- * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. 
+ *
+ *    Copyright 2016-2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH
+ *    Copyright 2017 (c) frax2222
+ *    Copyright 2017 (c) Jose Cabral
+ *    Copyright 2017 (c) Thomas Stalder, Blue Time Concept SA
+ */
 
 /* Enable POSIX features */
 #if !defined(_XOPEN_SOURCE) && !defined(_WRS_KERNEL)
@@ -31,11 +38,19 @@
 
 #include "ua_network_tcp.h"
 #include "ua_log_stdout.h"
-#include "queue.h"
+#include "../deps/queue.h"
 
 #include <stdio.h> // snprintf
 #include <string.h> // memset
-#include <errno.h>
+
+#if !defined(UA_FREERTOS)
+# include <errno.h>
+#else
+# define AI_PASSIVE 0x01
+# define TRUE 1
+# define FALSE 0
+# define ioctl ioctlsocket
+#endif
 
 #ifdef _WIN32
 # include <winsock2.h>
@@ -46,45 +61,68 @@
 # define OPTVAL_TYPE char
 # define ERR_CONNECTION_PROGRESS WSAEWOULDBLOCK
 # define UA_sleep_ms(X) Sleep(X)
-#else
-# define CLOSESOCKET(S) close(S)
+#else /* _WIN32 */
+# if defined(UA_FREERTOS)
+#  define UA_FREERTOS_HOSTNAME "10.200.4.114"
+static inline int gethostname_freertos(char* name, size_t len){
+  if(strlen(UA_FREERTOS_HOSTNAME) > (len))
+    return -1;
+  strcpy(name, UA_FREERTOS_HOSTNAME);
+  return 0;
+}
+#define gethostname gethostname_freertos
+#  include <lwip/tcpip.h>
+#  include <lwip/netdb.h>
+#  define CLOSESOCKET(S) lwip_close(S)
+#  define sockaddr_storage sockaddr
+#  ifdef BYTE_ORDER
+#   undef BYTE_ORDER
+#  endif
+#  define UA_sleep_ms(X) vTaskDelay(pdMS_TO_TICKS(X))
+# else /* Not freeRTOS */
+#  define CLOSESOCKET(S) close(S)
+#  include <arpa/inet.h>
+#  include <netinet/in.h>
+#  include <netdb.h>
+#  include <sys/ioctl.h>
+#  if defined(_WRS_KERNEL)
+#   include <hostLib.h>
+#   include <selectLib.h>
+#   define UA_sleep_ms(X)                            \
+    {                                                \
+    struct timespec timeToSleep;                     \
+      timeToSleep.tv_sec = X / 1000;                 \
+      timeToSleep.tv_nsec = 1000000 * (X % 1000);    \
+      nanosleep(&timeToSleep, NULL);                 \
+    }
+#  else /* defined(_WRS_KERNEL) */
+#   include <sys/select.h>
+#   define UA_sleep_ms(X) usleep(X * 1000)
+#  endif /* defined(_WRS_KERNEL) */
+# endif /* Not freeRTOS */
+
 # define SOCKET int
 # define WIN32_INT
 # define OPTVAL_TYPE int
 # define ERR_CONNECTION_PROGRESS EINPROGRESS
-# include <arpa/inet.h>
-# include <netinet/in.h>
-# ifndef _WRS_KERNEL
-#  include <sys/select.h>
-#  define UA_sleep_ms(X) usleep(X * 1000)
-# else
-#  include <hostLib.h>
-#  include <selectLib.h>
-#  define UA_sleep_ms(X)                           \
-   {                                               \
-   struct timespec timeToSleep;                    \
-   timeToSleep.tv_sec = X / 1000;                  \
-   timeToSleep.tv_nsec = 1000000 * (X % 1000);     \
-   nanosleep(&timeToSleep, NULL);                  \
-   }
-# endif
-# include <sys/ioctl.h>
+
+
 # include <fcntl.h>
 # include <unistd.h> // read, write, close
-# include <netdb.h>
+
 # ifdef __QNX__
 #  include <sys/socket.h>
 # endif
-#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
-# include <sys/param.h>
-# if defined(BSD)
-#  include<sys/socket.h>
+# if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+#  include <sys/param.h>
+#  if defined(BSD)
+#   include<sys/socket.h>
+#  endif
 # endif
-#endif
-# ifndef __CYGWIN__
+# if !defined(__CYGWIN__) && !defined(UA_FREERTOS)
 #  include <netinet/tcp.h>
 # endif
-#endif
+#endif /* _WIN32 */
 
 /* unsigned int for windows and workaround to a glibc bug */
 /* Additionally if GNU_LIBRARY is not defined, it may be using
@@ -144,6 +182,11 @@ connection_releaserecvbuffer(UA_Connection *connection,
 
 static UA_StatusCode
 connection_write(UA_Connection *connection, UA_ByteString *buf) {
+    if(connection->state == UA_CONNECTION_CLOSED) {
+        UA_ByteString_deleteMembers(buf);
+        return UA_STATUSCODE_BADCONNECTIONCLOSED;
+    }
+
     /* Prevent OS signals when sending to a closed socket */
     int flags = 0;
 #ifdef MSG_NOSIGNAL
@@ -176,6 +219,9 @@ connection_write(UA_Connection *connection, UA_ByteString *buf) {
 static UA_StatusCode
 connection_recv(UA_Connection *connection, UA_ByteString *response,
                 UA_UInt32 timeout) {
+    if(connection->state == UA_CONNECTION_CLOSED)
+        return UA_STATUSCODE_BADCONNECTIONCLOSED;
+
     /* Listen on the socket for the given timeout until a message arrives */
     if(timeout > 0) {
         fd_set fdset;
@@ -190,6 +236,17 @@ connection_recv(UA_Connection *connection, UA_ByteString *response,
         /* No result */
         if(resultsize == 0)
             return UA_STATUSCODE_GOODNONCRITICALTIMEOUT;
+
+        if(resultsize == -1) {
+            /* The call to select was interrupted manually. Act as if it timed
+             * out */
+            if(errno == EINTR)
+                return UA_STATUSCODE_GOODNONCRITICALTIMEOUT;
+
+            /* The error cannot be recovered. Close the connection. */
+            connection->close(connection);
+            return UA_STATUSCODE_BADCONNECTIONCLOSED;
+        }
     }
 
     response->data = (UA_Byte*)
@@ -231,7 +288,7 @@ socket_set_nonblocking(SOCKET sockfd) {
     u_long iMode = 1;
     if(ioctlsocket(sockfd, FIONBIO, &iMode) != NO_ERROR)
         return UA_STATUSCODE_BADINTERNALERROR;
-#elif defined(_WRS_KERNEL)
+#elif defined(_WRS_KERNEL) || defined(UA_FREERTOS)
     int on = TRUE;
     if(ioctl(sockfd, FIONBIO, &on) < 0)
       return UA_STATUSCODE_BADINTERNALERROR;
@@ -249,7 +306,7 @@ socket_set_blocking(SOCKET sockfd) {
     u_long iMode = 0;
     if(ioctlsocket(sockfd, FIONBIO, &iMode) != NO_ERROR)
         return UA_STATUSCODE_BADINTERNALERROR;
-#elif defined(_WRS_KERNEL)
+#elif defined(_WRS_KERNEL) || defined(UA_FREERTOS)
     int on = FALSE;
     if(ioctl(sockfd, FIONBIO, &on) < 0)
       return UA_STATUSCODE_BADINTERNALERROR;
@@ -275,6 +332,7 @@ typedef struct ConnectionEntry {
 } ConnectionEntry;
 
 typedef struct {
+    UA_Logger logger;
     UA_ConnectionConfig conf;
     UA_UInt16 port;
     UA_Int32 serverSockets[FD_SETSIZE];
@@ -292,6 +350,8 @@ ServerNetworkLayerTCP_freeConnection(UA_Connection *connection) {
  * socket is returned from select. */
 static void
 ServerNetworkLayerTCP_close(UA_Connection *connection) {
+    if (connection->state == UA_CONNECTION_CLOSED)
+        return;
     shutdown((SOCKET)connection->sockfd, 2);
     connection->state = UA_CONNECTION_CLOSED;
 }
@@ -307,12 +367,13 @@ ServerNetworkLayerTCP_add(ServerNetworkLayerTCP *layer, UA_Int32 newsockfd,
     if(setsockopt(newsockfd, IPPROTO_TCP, TCP_NODELAY,
                (const char *)&dummy, sizeof(dummy)) < 0) {
         UA_LOG_SOCKET_ERRNO_WRAP(
-                UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+                UA_LOG_ERROR(layer->logger, UA_LOGCATEGORY_NETWORK,
                              "Cannot set socket option TCP_NODELAY. Error: %s",
                              errno_str));
         return UA_STATUSCODE_BADUNEXPECTEDERROR;
     }
 
+#if !defined(UA_FREERTOS)
     /* Get the peer name for logging */
     char remote_name[100];
     int res = getnameinfo((struct sockaddr*)remote,
@@ -320,16 +381,16 @@ ServerNetworkLayerTCP_add(ServerNetworkLayerTCP *layer, UA_Int32 newsockfd,
                           remote_name, sizeof(remote_name),
                           NULL, 0, NI_NUMERICHOST);
     if(res == 0) {
-        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+        UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK,
                     "Connection %i | New connection over TCP from %s",
                     (int)newsockfd, remote_name);
     } else {
-        UA_LOG_SOCKET_ERRNO_WRAP(UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+        UA_LOG_SOCKET_ERRNO_WRAP(UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK,
                                                 "Connection %i | New connection over TCP, "
                                                         "getnameinfo failed with error: %s",
                                                 (int)newsockfd, errno_str));
     }
-
+#endif
     /* Allocate and initialize the connection */
     ConnectionEntry *e = (ConnectionEntry*)UA_malloc(sizeof(ConnectionEntry));
     if(!e){
@@ -367,7 +428,7 @@ addServerSocket(ServerNetworkLayerTCP *layer, struct addrinfo *ai) {
     if(newsock < 0)
 #endif
     {
-        UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+        UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK,
                        "Error opening the server socket");
         return;
     }
@@ -375,26 +436,29 @@ addServerSocket(ServerNetworkLayerTCP *layer, struct addrinfo *ai) {
     /* Some Linux distributions have net.ipv6.bindv6only not activated. So
      * sockets can double-bind to IPv4 and IPv6. This leads to problems. Use
      * AF_INET6 sockets only for IPv6. */
+
     int optval = 1;
+#if !defined(UA_FREERTOS)
     if(ai->ai_family == AF_INET6 &&
        setsockopt(newsock, IPPROTO_IPV6, IPV6_V6ONLY,
                   (const char*)&optval, sizeof(optval)) == -1) {
-        UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+        UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK,
                        "Could not set an IPv6 socket to IPv6 only");
         CLOSESOCKET(newsock);
         return;
     }
-
+#endif
     if(setsockopt(newsock, SOL_SOCKET, SO_REUSEADDR,
                   (const char *)&optval, sizeof(optval)) == -1) {
-        UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+        UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK,
                        "Could not make the socket reusable");
         CLOSESOCKET(newsock);
         return;
     }
 
+
     if(socket_set_nonblocking(newsock) != UA_STATUSCODE_GOOD) {
-        UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+        UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK,
                        "Could not set the server socket to nonblocking");
         CLOSESOCKET(newsock);
         return;
@@ -403,7 +467,7 @@ addServerSocket(ServerNetworkLayerTCP *layer, struct addrinfo *ai) {
     /* Bind socket to address */
     if(bind(newsock, ai->ai_addr, WIN32_INT ai->ai_addrlen) < 0) {
         UA_LOG_SOCKET_ERRNO_WRAP(
-            UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+            UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK,
                            "Error binding a server socket: %s", errno_str));
         CLOSESOCKET(newsock);
         return;
@@ -412,7 +476,7 @@ addServerSocket(ServerNetworkLayerTCP *layer, struct addrinfo *ai) {
     /* Start listening */
     if(listen(newsock, MAXBACKLOG) < 0) {
         UA_LOG_SOCKET_ERRNO_WRAP(
-                UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+                UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK,
                        "Error listening on server socket: %s", errno_str));
         CLOSESOCKET(newsock);
         return;
@@ -449,7 +513,7 @@ ServerNetworkLayerTCP_start(UA_ServerNetworkLayer *nl, const UA_String *customHo
                                         layer->port);
 #endif
         du.data = (UA_Byte*)discoveryUrl;
-    }else{    
+    }else{
         char hostname[256];
         if(gethostname(hostname, 255) == 0) {
             char discoveryUrl[256];
@@ -478,7 +542,13 @@ ServerNetworkLayerTCP_start(UA_ServerNetworkLayer *nl, const UA_String *customHo
     hints.ai_family = AF_UNSPEC;
     hints.ai_socktype = SOCK_STREAM;
     hints.ai_flags = AI_PASSIVE;
+#if defined(UA_FREERTOS)
+    hints.ai_protocol = IPPROTO_TCP;
+    char hostname[] = UA_FREERTOS_HOSTNAME;
+    if(getaddrinfo(hostname, portno, &hints, &res) != 0)
+#else
     if(getaddrinfo(NULL, portno, &hints, &res) != 0)
+#endif
         return UA_STATUSCODE_BADINTERNALERROR;
 
     /* There might be serveral addrinfos (for different network cards,
@@ -490,7 +560,7 @@ ServerNetworkLayerTCP_start(UA_ServerNetworkLayer *nl, const UA_String *customHo
         addServerSocket(layer, ai);
     freeaddrinfo(res);
 
-    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+    UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK,
                 "TCP network layer listening on %.*s",
                 (int)nl->discoveryUrl.length, nl->discoveryUrl.data);
     return UA_STATUSCODE_GOOD;
@@ -533,7 +603,7 @@ ServerNetworkLayerTCP_listen(UA_ServerNetworkLayer *nl, UA_Server *server,
     struct timeval tmptv = {0, timeout * 1000};
     if (select(highestfd+1, &fdset, NULL, &errset, &tmptv) < 0) {
         UA_LOG_SOCKET_ERRNO_WRAP(
-            UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+            UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK,
                                   "Socket select failed with %s", errno_str));
         // we will retry, so do not return bad
         return UA_STATUSCODE_GOOD;
@@ -555,7 +625,7 @@ ServerNetworkLayerTCP_listen(UA_ServerNetworkLayer *nl, UA_Server *server,
 #endif
             continue;
 
-        UA_LOG_TRACE(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+        UA_LOG_TRACE(layer->logger, UA_LOGCATEGORY_NETWORK,
                     "Connection %i | New TCP connection on server socket %i",
                     (int)newsockfd, layer->serverSockets[i]);
 
@@ -567,8 +637,8 @@ ServerNetworkLayerTCP_listen(UA_ServerNetworkLayer *nl, UA_Server *server,
     UA_DateTime now = UA_DateTime_nowMonotonic();
     LIST_FOREACH_SAFE(e, &layer->connections, pointers, e_tmp) {
         if ((e->connection.state == UA_CONNECTION_OPENING) &&
-            (now > (e->connection.openingDate + (NOHELLOTIMEOUT * UA_MSEC_TO_DATETIME)))){
-            UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+            (now > (e->connection.openingDate + (NOHELLOTIMEOUT * UA_DATETIME_MSEC)))){
+            UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK,
                         "Connection %i | Closed by the server (no Hello Message)",
                          e->connection.sockfd);
             LIST_REMOVE(e, pointers);
@@ -581,7 +651,7 @@ ServerNetworkLayerTCP_listen(UA_ServerNetworkLayer *nl, UA_Server *server,
            !UA_fd_isset(e->connection.sockfd, &fdset))
           continue;
 
-        UA_LOG_TRACE(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+        UA_LOG_TRACE(layer->logger, UA_LOGCATEGORY_NETWORK,
                     "Connection %i | Activity on the socket",
                     e->connection.sockfd);
 
@@ -594,15 +664,9 @@ ServerNetworkLayerTCP_listen(UA_ServerNetworkLayer *nl, UA_Server *server,
             connection_releaserecvbuffer(&e->connection, &buf);
         } else if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED) {
             /* The socket is shutdown but not closed */
-            if(e->connection.state != UA_CONNECTION_CLOSED) {
-                UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
-                            "Connection %i | Closed by the client",
-                            e->connection.sockfd);
-            } else {
-                UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
-                            "Connection %i | Closed by the server",
-                            e->connection.sockfd);
-            }
+            UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK,
+                        "Connection %i | Closed",
+                        e->connection.sockfd);
             LIST_REMOVE(e, pointers);
             CLOSESOCKET(e->connection.sockfd);
             UA_Server_removeConnection(server, &e->connection);
@@ -614,7 +678,7 @@ ServerNetworkLayerTCP_listen(UA_ServerNetworkLayer *nl, UA_Server *server,
 static void
 ServerNetworkLayerTCP_stop(UA_ServerNetworkLayer *nl, UA_Server *server) {
     ServerNetworkLayerTCP *layer = (ServerNetworkLayerTCP *)nl->handle;
-    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+    UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK,
                 "Shutting down the TCP network layer");
 
     /* Close the server sockets */
@@ -649,7 +713,6 @@ ServerNetworkLayerTCP_deleteMembers(UA_ServerNetworkLayer *nl) {
     ConnectionEntry *e, *e_tmp;
     LIST_FOREACH_SAFE(e, &layer->connections, pointers, e_tmp) {
         LIST_REMOVE(e, pointers);
-        ServerNetworkLayerTCP_close(&e->connection);
         CLOSESOCKET(e->connection.sockfd);
         UA_free(e);
     }
@@ -659,7 +722,7 @@ ServerNetworkLayerTCP_deleteMembers(UA_ServerNetworkLayer *nl) {
 }
 
 UA_ServerNetworkLayer
-UA_ServerNetworkLayerTCP(UA_ConnectionConfig conf, UA_UInt16 port) {
+UA_ServerNetworkLayerTCP(UA_ConnectionConfig conf, UA_UInt16 port, UA_Logger logger) {
     UA_ServerNetworkLayer nl;
     memset(&nl, 0, sizeof(UA_ServerNetworkLayer));
     ServerNetworkLayerTCP *layer = (ServerNetworkLayerTCP*)
@@ -667,6 +730,7 @@ UA_ServerNetworkLayerTCP(UA_ConnectionConfig conf, UA_UInt16 port) {
     if(!layer)
         return nl;
 
+    layer->logger = (logger != NULL ? logger : UA_Log_Stdout);
     layer->conf = conf;
     layer->port = port;
 
@@ -684,6 +748,8 @@ UA_ServerNetworkLayerTCP(UA_ConnectionConfig conf, UA_UInt16 port) {
 
 static void
 ClientNetworkLayerTCP_close(UA_Connection *connection) {
+    if (connection->state == UA_CONNECTION_CLOSED)
+        return;
     shutdown((SOCKET)connection->sockfd, 2);
     CLOSESOCKET(connection->sockfd);
     connection->state = UA_CONNECTION_CLOSED;
@@ -691,7 +757,8 @@ ClientNetworkLayerTCP_close(UA_Connection *connection) {
 
 UA_Connection
 UA_ClientConnectionTCP(UA_ConnectionConfig conf,
-                       const char *endpointUrl, const UA_UInt32 timeout) {
+                       const char *endpointUrl, const UA_UInt32 timeout,
+                       UA_Logger logger) {
 #ifdef _WIN32
     WORD wVersionRequested;
     WSADATA wsaData;
@@ -699,9 +766,13 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
     WSAStartup(wVersionRequested, &wsaData);
 #endif
 
+    if(logger == NULL) {
+        logger = UA_Log_Stdout;
+    }
+
     UA_Connection connection;
     memset(&connection, 0, sizeof(UA_Connection));
-    connection.state = UA_CONNECTION_OPENING;
+    connection.state = UA_CONNECTION_CLOSED;
     connection.localConf = conf;
     connection.remoteConf = conf;
     connection.send = connection_write;
@@ -722,7 +793,7 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
         UA_parseEndpointUrl(&endpointUrlString, &hostnameString,
                             &port, &pathString);
     if(parse_retval != UA_STATUSCODE_GOOD || hostnameString.length > 511) {
-        UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+        UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK,
                        "Server url is invalid: %s", endpointUrl);
         return connection;
     }
@@ -731,7 +802,7 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
 
     if(port == 0) {
         port = 4840;
-        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+        UA_LOG_INFO(logger, UA_LOGCATEGORY_NETWORK,
                     "No port defined, using default port %d", port);
     }
 
@@ -739,6 +810,9 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
     memset(&hints, 0, sizeof(hints));
     hints.ai_family = AF_UNSPEC;
     hints.ai_socktype = SOCK_STREAM;
+#if defined(UA_FREERTOS)
+    hints.ai_protocol = IPPROTO_TCP;
+#endif
     char portStr[6];
 #ifndef _MSC_VER
     snprintf(portStr, 6, "%d", port);
@@ -747,15 +821,21 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
 #endif
     int error = getaddrinfo(hostname, portStr, &hints, &server);
     if(error != 0 || !server) {
-        UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+#if !defined(UA_FREERTOS)
+        UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK,
+
                        "DNS lookup of %s failed with error %s",
                        hostname, gai_strerror(error));
+#else
+        UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK,
+                        "DNS lookup of %s failed with error",
+                        hostname);
+#endif
         return connection;
     }
 
-
     UA_Boolean connected = UA_FALSE;
-
+    UA_DateTime dtTimeout = timeout * UA_DATETIME_MSEC;
     UA_DateTime connStart = UA_DateTime_nowMonotonic();
     SOCKET clientsockfd;
 
@@ -763,8 +843,6 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
      * want to try to connect. So use a loop and retry until timeout is
      * reached. */
     do {
-
-        connection.state = UA_CONNECTION_OPENING;
         /* Get a socket */
         clientsockfd = socket(server->ai_family,
                               server->ai_socktype,
@@ -774,19 +852,20 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
     #else
         if(clientsockfd < 0) {
     #endif
-            UA_LOG_SOCKET_ERRNO_WRAP(
-                    UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
-                                          "Could not create client socket: %s", errno_str));
+            UA_LOG_SOCKET_ERRNO_WRAP(UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK,
+                                                    "Could not create client socket: %s", errno_str));
             freeaddrinfo(server);
             return connection;
         }
 
+        connection.state = UA_CONNECTION_OPENING;
+
         /* Connect to the server */
         connection.sockfd = (UA_Int32) clientsockfd; /* cast for win32 */
 
         /* Non blocking connect to be able to timeout */
         if (socket_set_nonblocking(clientsockfd) != UA_STATUSCODE_GOOD) {
-            UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+            UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK,
                            "Could not set the client socket to nonblocking");
             ClientNetworkLayerTCP_close(&connection);
             freeaddrinfo(server);
@@ -794,15 +873,14 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
         }
 
         /* Non blocking connect */
-        error = connect(clientsockfd, server->ai_addr,
-                        WIN32_INT server->ai_addrlen);
+        error = connect(clientsockfd, server->ai_addr, WIN32_INT server->ai_addrlen);
 
         if ((error == -1) && (errno__ != ERR_CONNECTION_PROGRESS)) {
             ClientNetworkLayerTCP_close(&connection);
             UA_LOG_SOCKET_ERRNO_WRAP(
-                    UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
-                                          "Connection to %s failed with error: %s",
-                                          endpointUrl, errno_str));
+                    UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK,
+                                   "Connection to %s failed with error: %s",
+                                   endpointUrl, errno_str));
             freeaddrinfo(server);
             return connection;
         }
@@ -810,25 +888,23 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
         /* Use select to wait and check if connected */
         if (error == -1 && (errno__ == ERR_CONNECTION_PROGRESS)) {
             /* connection in progress. Wait until connected using select */
-            UA_UInt32 timeSinceStart = (UA_UInt32)
-                ((UA_Double)(UA_DateTime_nowMonotonic() - connStart) * UA_DATETIME_TO_MSEC);
-            if(timeSinceStart > timeout)
+            UA_DateTime timeSinceStart = UA_DateTime_nowMonotonic() - connStart;
+            if(timeSinceStart > dtTimeout)
                 break;
 
             fd_set fdset;
             FD_ZERO(&fdset);
             UA_fd_set(clientsockfd, &fdset);
-            UA_UInt32 timeout_usec = (timeout - timeSinceStart) * 1000;
+            UA_DateTime timeout_usec = (dtTimeout - timeSinceStart) / UA_DATETIME_USEC;
             struct timeval tmptv = {(long int) (timeout_usec / 1000000),
                                     (long int) (timeout_usec % 1000000)};
 
-            int resultsize = select((UA_Int32)(clientsockfd + 1), NULL, &fdset,
-                                    NULL, &tmptv);
+            int resultsize = select((UA_Int32)(clientsockfd + 1), NULL, &fdset, NULL, &tmptv);
 
-            if (resultsize == 1) {
+            if(resultsize == 1) {
+#ifdef _WIN32
                 /* Windows does not have any getsockopt equivalent and it is not
                  * needed there */
-#ifdef _WIN32
                 connected = true;
                 break;
 #else
@@ -842,7 +918,7 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
                     /* connection refused happens on localhost or local ip without timeout */
                     if (so_error != ECONNREFUSED) {
                         ClientNetworkLayerTCP_close(&connection);
-                        UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+                        UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK,
                                        "Connection to %s failed with error: %s",
                                        endpointUrl, strerror(ret == 0 ? so_error : errno__));
                         freeaddrinfo(server);
@@ -863,13 +939,15 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
         }
         ClientNetworkLayerTCP_close(&connection);
 
-    } while ((UA_Double)(UA_DateTime_nowMonotonic() - connStart)*UA_DATETIME_TO_MSEC < timeout);
+    } while ((UA_DateTime_nowMonotonic() - connStart) < dtTimeout);
 
     freeaddrinfo(server);
-    if (!connected) {
+
+    if(!connected) {
         /* connection timeout */
-        ClientNetworkLayerTCP_close(&connection);
-        UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+        if (connection.state != UA_CONNECTION_CLOSED)
+            ClientNetworkLayerTCP_close(&connection);
+        UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK,
                        "Trying to connect to %s timed out",
                        endpointUrl);
         return connection;
@@ -878,7 +956,7 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
 
     /* We are connected. Reset socket to blocking */
     if(socket_set_blocking(clientsockfd) != UA_STATUSCODE_GOOD) {
-        UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+        UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK,
                        "Could not set the client socket to blocking");
         ClientNetworkLayerTCP_close(&connection);
         return connection;
@@ -889,7 +967,7 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
     int sso_result = setsockopt(connection.sockfd, SOL_SOCKET,
                                 SO_NOSIGPIPE, (void*)&val, sizeof(val));
     if(sso_result < 0)
-        UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+        UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK,
                        "Couldn't set SO_NOSIGPIPE");
 #endif
 

+ 7 - 3
plugins/ua_network_tcp.h

@@ -1,5 +1,8 @@
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
- * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. 
+ *
+ *    Copyright 2016 (c) Julius Pfrommer, Fraunhofer IOSB
+ */
 
 #ifndef UA_NETWORK_TCP_H_
 #define UA_NETWORK_TCP_H_
@@ -10,12 +13,13 @@ extern "C" {
 
 #include "ua_server.h"
 #include "ua_client.h"
+#include "ua_plugin_log.h"
 
 UA_ServerNetworkLayer UA_EXPORT
-UA_ServerNetworkLayerTCP(UA_ConnectionConfig conf, UA_UInt16 port);
+UA_ServerNetworkLayerTCP(UA_ConnectionConfig conf, UA_UInt16 port, UA_Logger logger);
 
 UA_Connection UA_EXPORT
-UA_ClientConnectionTCP(UA_ConnectionConfig conf, const char *endpointUrl, const UA_UInt32 timeout);
+UA_ClientConnectionTCP(UA_ConnectionConfig conf, const char *endpointUrl, const UA_UInt32 timeout, UA_Logger logger);
 
 #ifdef __cplusplus
 } // extern "C"

+ 18 - 12
plugins/ua_network_udp.c

@@ -1,29 +1,35 @@
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
- * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. 
+ *
+ *    Copyright 2016-2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH
+ */
 
 #include "ua_network_udp.h"
 #include <stdio.h>
 #include <string.h> // memset
 
-#ifdef UA_ENABLE_MULTITHREADING
-# include <urcu/uatomic.h>
-#endif
-
 /* with a space so amalgamation does not remove the includes */
 # include <errno.h> // errno, EINTR
 # include <fcntl.h> // fcntl
 # include <strings.h> //bzero
+
+#if defined(UA_FREERTOS)
+ # include <lwip/udp.h>
+ # include <lwip/tcpip.h>
+#else
 # ifndef _WRS_KERNEL
 #  include <sys/select.h>
 # else
-# include <selectLib.h>
+#  include <selectLib.h>
 # endif
-# include <netinet/in.h>
-# include <netinet/tcp.h>
-# include <sys/socketvar.h>
-# include <sys/ioctl.h>
-# include <unistd.h> // read, write, close
-# include <arpa/inet.h>
+#  include <netinet/in.h>
+#  include <netinet/tcp.h>
+#  include <sys/socketvar.h>
+#  include <sys/ioctl.h>
+#  include <unistd.h> // read, write, close
+#  include <arpa/inet.h>
+#endif
 #ifdef __QNX__
 #include <sys/socket.h>
 #endif

+ 4 - 1
plugins/ua_network_udp.h

@@ -1,5 +1,8 @@
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
- * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. 
+ *
+ *    Copyright 2016 (c) Julius Pfrommer, Fraunhofer IOSB
+ */
 
 #ifndef UA_NETWORK_UDP_H_
 #define UA_NETWORK_UDP_H_

+ 7 - 6
plugins/ua_nodestore_default.c

@@ -1,5 +1,9 @@
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
- * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. 
+ *
+ *    Copyright 2017 (c) Julian Grothoff
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ */
 
 #include "ua_nodestore_default.h"
 
@@ -325,12 +329,9 @@ UA_NodeMap_insertNode(void *context, UA_Node *node,
         }
     }
 
-    UA_NodeId tempNodeid;
-    tempNodeid = node->nodeId;
-    tempNodeid.namespaceIndex = 0;
     UA_NodeMapEntry **slot;
-    if(tempNodeid.identifierType == UA_NODEIDTYPE_NUMERIC &&
-       tempNodeid.identifier.numeric == 0) {
+    if(node->nodeId.identifierType == UA_NODEIDTYPE_NUMERIC &&
+            node->nodeId.identifier.numeric == 0) {
         /* create a random nodeid */
         /* start at least with 50,000 to make sure we don not conflict with nodes from the spec */
         /* E.g. adding a nodeset will create children while there are still other nodes which need to be created */

+ 5 - 1
plugins/ua_nodestore_default.h

@@ -1,5 +1,9 @@
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
- * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. 
+ *
+ *    Copyright 2017 (c) Julian Grothoff
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ */
 
 #ifndef UA_NODESTORE_DEFAULT_H_
 #define UA_NODESTORE_DEFAULT_H_

+ 148 - 0
plugins/ua_pki_certificate.c

@@ -0,0 +1,148 @@
+/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. 
+ *
+ *    Copyright 2018 (c) Mark Giraud, Fraunhofer IOSB
+ */
+
+#include "ua_pki_certificate.h"
+
+#ifdef UA_ENABLE_ENCRYPTION
+#include <mbedtls/x509.h>
+#include <mbedtls/x509_crt.h>
+#endif
+
+/************/
+/* AllowAll */
+/************/
+
+static UA_StatusCode
+verifyAllowAll(void *verificationContext, const UA_ByteString *certificate) {
+    return UA_STATUSCODE_GOOD;
+}
+
+static void
+deleteVerifyAllowAll(UA_CertificateVerification *cv) {
+
+}
+
+void UA_CertificateVerification_AcceptAll(UA_CertificateVerification *cv) {
+    cv->verifyCertificate = verifyAllowAll;
+    cv->deleteMembers = deleteVerifyAllowAll;
+}
+
+#ifdef UA_ENABLE_ENCRYPTION
+
+typedef struct {
+    mbedtls_x509_crt certificateTrustList;
+    mbedtls_x509_crl certificateRevocationList;
+} CertInfo;
+
+static UA_StatusCode
+certificateVerification_verify(void *verificationContext,
+                               const UA_ByteString *certificate) {
+    CertInfo *ci = (CertInfo*)verificationContext;
+    if(!ci)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    /* Parse the certificate */
+    mbedtls_x509_crt remoteCertificate;
+    mbedtls_x509_crt_init(&remoteCertificate);
+    int mbedErr = mbedtls_x509_crt_parse(&remoteCertificate, certificate->data,
+                                         certificate->length);
+    if(mbedErr) {
+        /* char errBuff[300]; */
+        /* mbedtls_strerror(mbedErr, errBuff, 300); */
+        /* UA_LOG_WARNING(data->policyContext->securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, */
+        /*                "Could not parse the remote certificate with error: %s", errBuff); */
+        return UA_STATUSCODE_BADSECURITYCHECKSFAILED;
+    }
+
+    /* Verify */
+    mbedtls_x509_crt_profile crtProfile = {
+        MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA1) | MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA256),
+        0xFFFFFF, 0x000000, 128 * 8 // in bits
+    }; // TODO: remove magic numbers
+
+    uint32_t flags = 0;
+    mbedErr = mbedtls_x509_crt_verify_with_profile(&remoteCertificate,
+                                                   &ci->certificateTrustList,
+                                                   &ci->certificateRevocationList,
+                                                   &crtProfile, NULL, &flags, NULL, NULL);
+
+    // TODO: Extend verification
+    if(mbedErr) {
+        /* char buff[100]; */
+        /* mbedtls_x509_crt_verify_info(buff, 100, "", flags); */
+        /* UA_LOG_ERROR(channelContextData->policyContext->securityPolicy->logger, */
+        /*              UA_LOGCATEGORY_SECURITYPOLICY, */
+        /*              "Verifying the certificate failed with error: %s", buff); */
+
+        mbedtls_x509_crt_free(&remoteCertificate);
+        if(flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED)
+            return UA_STATUSCODE_BADCERTIFICATEUNTRUSTED;
+
+        if(flags & MBEDTLS_X509_BADCERT_FUTURE ||
+           flags & MBEDTLS_X509_BADCERT_EXPIRED)
+            return UA_STATUSCODE_BADCERTIFICATETIMEINVALID;
+
+        if(flags & MBEDTLS_X509_BADCERT_REVOKED ||
+           flags & MBEDTLS_X509_BADCRL_EXPIRED)
+            return UA_STATUSCODE_BADCERTIFICATEREVOKED;
+
+        return UA_STATUSCODE_BADSECURITYCHECKSFAILED;
+    }
+
+    mbedtls_x509_crt_free(&remoteCertificate);
+    return UA_STATUSCODE_GOOD;
+}
+
+static void
+certificateVerification_deleteMembers(UA_CertificateVerification *cv) {
+    CertInfo *ci = (CertInfo*)cv->context;
+    if(!ci)
+        return;
+    mbedtls_x509_crt_free(&ci->certificateTrustList);
+    mbedtls_x509_crl_free(&ci->certificateRevocationList);
+    UA_free(ci);
+    cv->context = NULL;
+}
+
+UA_StatusCode
+UA_CertificateVerification_Trustlist(UA_CertificateVerification *cv,
+                                     const UA_ByteString *certificateTrustList,
+                                     size_t certificateTrustListSize,
+                                     const UA_ByteString *certificateRevocationList,
+                                     size_t certificateRevocationListSize) {
+    CertInfo *ci = (CertInfo*)malloc(sizeof(CertInfo));
+    if(!ci)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    mbedtls_x509_crt_init(&ci->certificateTrustList);
+    mbedtls_x509_crl_init(&ci->certificateRevocationList);
+
+    cv->context = (void*)ci;
+    if(certificateTrustListSize > 0)
+        cv->verifyCertificate = certificateVerification_verify;
+    else
+        cv->verifyCertificate = verifyAllowAll;
+    cv->deleteMembers = certificateVerification_deleteMembers;
+
+    int err = 0;
+    for(size_t i = 0; i < certificateTrustListSize; i++) {
+        err |= mbedtls_x509_crt_parse(&ci->certificateTrustList,
+                                      certificateTrustList[i].data,
+                                      certificateTrustList[i].length);
+    }
+    for(size_t i = 0; i < certificateRevocationListSize; i++) {
+        err |= mbedtls_x509_crl_parse(&ci->certificateRevocationList,
+                                      certificateRevocationList[i].data,
+                                      certificateRevocationList[i].length);
+    }
+
+    if(err) {
+        certificateVerification_deleteMembers(cv);
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
+    return UA_STATUSCODE_GOOD;
+}
+
+#endif

+ 37 - 0
plugins/ua_pki_certificate.h

@@ -0,0 +1,37 @@
+/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. 
+ *
+ *    Copyright 2018 (c) Mark Giraud, Fraunhofer IOSB
+ */
+
+#ifndef UA_PKI_CERTIFICATE_H_
+#define UA_PKI_CERTIFICATE_H_
+
+#include "ua_plugin_pki.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Default implementation that accepts all certificates */
+UA_EXPORT void
+UA_CertificateVerification_AcceptAll(UA_CertificateVerification *cv);
+
+#ifdef UA_ENABLE_ENCRYPTION
+
+/* Accept certificates based on a trust-list and a revocation-list. Based on
+ * mbedTLS. */
+UA_EXPORT UA_StatusCode
+UA_CertificateVerification_Trustlist(UA_CertificateVerification *cv,
+                                     const UA_ByteString *certificateTrustList,
+                                     size_t certificateTrustListSize,
+                                     const UA_ByteString *certificateRevocationList,
+                                     size_t certificateRevocationListSize);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UA_PKI_CERTIFICATE_H_ */

+ 967 - 0
plugins/ua_securitypolicy_basic128rsa15.c

@@ -0,0 +1,967 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2018 (c) Mark Giraud, Fraunhofer IOSB
+ */
+
+#include <mbedtls/aes.h>
+#include <mbedtls/md.h>
+#include <mbedtls/x509_crt.h>
+#include <mbedtls/ctr_drbg.h>
+#include <mbedtls/entropy.h>
+#include <mbedtls/entropy_poll.h>
+#include <mbedtls/error.h>
+#include <mbedtls/version.h>
+#include <mbedtls/sha1.h>
+
+#include "ua_plugin_pki.h"
+#include "ua_plugin_securitypolicy.h"
+#include "ua_securitypolicy_basic128rsa15.h"
+#include "ua_types.h"
+#include "ua_types_generated_handling.h"
+
+/* Notes:
+ * mbedTLS' AES allows in-place encryption and decryption. Sow we don't have to
+ * allocate temp buffers.
+ * https://tls.mbed.org/discussions/generic/in-place-decryption-with-aes256-same-input-output-buffer
+ */
+
+#define UA_SECURITYPOLICY_BASIC128RSA15_RSAPADDING_LEN 11
+#define UA_SHA1_LENGTH 20
+#define UA_SECURITYPOLICY_BASIC128RSA15_SYM_KEY_LENGTH 16
+#define UA_BASIC128RSA15_SYM_SIGNING_KEY_LENGTH 16
+#define UA_SECURITYPOLICY_BASIC128RSA15_SYM_ENCRYPTION_BLOCK_SIZE 16
+#define UA_SECURITYPOLICY_BASIC128RSA15_SYM_PLAIN_TEXT_BLOCK_SIZE 16
+#define UA_SECURITYPOLICY_BASIC128RSA15_MINASYMKEYLENGTH 128
+#define UA_SECURITYPOLICY_BASIC128RSA15_MAXASYMKEYLENGTH 256
+
+#define UA_LOG_MBEDERR                                                  \
+    char errBuff[300];                                                  \
+    mbedtls_strerror(mbedErr, errBuff, 300);                            \
+    UA_LOG_WARNING(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, \
+                   "mbedTLS returned an error: %s", errBuff);           \
+
+#define UA_MBEDTLS_ERRORHANDLING(errorcode)                             \
+    if(mbedErr) {                                                       \
+        UA_LOG_MBEDERR                                                  \
+        retval = errorcode;                                             \
+    }
+
+#define UA_MBEDTLS_ERRORHANDLING_RETURN(errorcode)                      \
+    if(mbedErr) {                                                       \
+        UA_LOG_MBEDERR                                                  \
+        return errorcode;                                               \
+    }
+
+typedef struct {
+    const UA_SecurityPolicy *securityPolicy;
+    UA_ByteString localCertThumbprint;
+
+    mbedtls_ctr_drbg_context drbgContext;
+    mbedtls_entropy_context entropyContext;
+    mbedtls_md_context_t sha1MdContext;
+    mbedtls_pk_context localPrivateKey;
+} Basic128Rsa15_PolicyContext;
+
+typedef struct {
+    Basic128Rsa15_PolicyContext *policyContext;
+
+    UA_ByteString localSymSigningKey;
+    UA_ByteString localSymEncryptingKey;
+    UA_ByteString localSymIv;
+
+    UA_ByteString remoteSymSigningKey;
+    UA_ByteString remoteSymEncryptingKey;
+    UA_ByteString remoteSymIv;
+
+    mbedtls_x509_crt remoteCertificate;
+} Basic128Rsa15_ChannelContext;
+
+
+/********************/
+/* AsymmetricModule */
+/********************/
+
+static UA_StatusCode
+asym_verify_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy,
+                             Basic128Rsa15_ChannelContext *cc,
+                             const UA_ByteString *message,
+                             const UA_ByteString *signature) {
+    if(securityPolicy == NULL || message == NULL || signature == NULL || cc == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    /* Compute the sha1 hash */
+    unsigned char hash[UA_SHA1_LENGTH];
+#if MBEDTLS_VERSION_NUMBER >= 0x02070000
+    mbedtls_sha1_ret(message->data, message->length, hash);
+#else
+    mbedtls_sha1(message->data, message->length, hash);
+#endif
+
+    /* Set the RSA settings */
+    mbedtls_rsa_context *rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk);
+    mbedtls_rsa_set_padding(rsaContext, MBEDTLS_RSA_PKCS_V15, 0);
+
+    /* Verify */
+    int mbedErr = mbedtls_pk_verify(&cc->remoteCertificate.pk,
+                                    MBEDTLS_MD_SHA1, hash, UA_SHA1_LENGTH,
+                                    signature->data, signature->length);
+    UA_MBEDTLS_ERRORHANDLING_RETURN(UA_STATUSCODE_BADSECURITYCHECKSFAILED);
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode
+asym_sign_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy,
+                           Basic128Rsa15_ChannelContext *cc,
+                           const UA_ByteString *message,
+                           UA_ByteString *signature) {
+    if(securityPolicy == NULL || message == NULL || signature == NULL || cc == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    unsigned char hash[UA_SHA1_LENGTH];
+#if MBEDTLS_VERSION_NUMBER >= 0x02070000
+    mbedtls_sha1_ret(message->data, message->length, hash);
+#else
+    mbedtls_sha1(message->data, message->length, hash);
+#endif
+
+    Basic128Rsa15_PolicyContext *pc = cc->policyContext;
+    mbedtls_rsa_context *rsaContext = mbedtls_pk_rsa(pc->localPrivateKey);
+    mbedtls_rsa_set_padding(rsaContext, MBEDTLS_RSA_PKCS_V15, 0);
+
+    size_t sigLen = 0;
+    int mbedErr = mbedtls_pk_sign(&pc->localPrivateKey,
+                                  MBEDTLS_MD_SHA1, hash,
+                                  UA_SHA1_LENGTH, signature->data,
+                                  &sigLen, mbedtls_ctr_drbg_random,
+                                  &pc->drbgContext);
+    UA_MBEDTLS_ERRORHANDLING_RETURN(UA_STATUSCODE_BADINTERNALERROR);
+    return UA_STATUSCODE_GOOD;
+}
+
+static size_t
+asym_getLocalSignatureSize_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy,
+                                            const Basic128Rsa15_ChannelContext *cc) {
+    if(securityPolicy == NULL || cc == NULL)
+        return 0;
+
+    return mbedtls_pk_rsa(cc->policyContext->localPrivateKey)->len;
+}
+
+static size_t
+asym_getRemoteSignatureSize_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy,
+                                             const Basic128Rsa15_ChannelContext *cc) {
+    if(securityPolicy == NULL || cc == NULL)
+        return 0;
+
+    return mbedtls_pk_rsa(cc->remoteCertificate.pk)->len;
+}
+
+static UA_StatusCode
+asym_encrypt_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy,
+                              Basic128Rsa15_ChannelContext *cc,
+                              UA_ByteString *data) {
+    if(securityPolicy == NULL || cc == NULL || data == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    const size_t plainTextBlockSize = securityPolicy->asymmetricModule.cryptoModule.encryptionAlgorithm.
+        getRemotePlainTextBlockSize(securityPolicy, cc);
+
+    if(data->length % plainTextBlockSize != 0)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    mbedtls_rsa_context *remoteRsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk);
+    mbedtls_rsa_set_padding(remoteRsaContext, MBEDTLS_RSA_PKCS_V15, 0);
+
+    UA_ByteString encrypted;
+    const size_t bufferOverhead =
+        UA_SecurityPolicy_getRemoteAsymEncryptionBufferLengthOverhead(securityPolicy, cc, data->length);
+    UA_StatusCode retval = UA_ByteString_allocBuffer(&encrypted, data->length + bufferOverhead);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    size_t lenDataToEncrypt = data->length;
+    size_t inOffset = 0;
+    size_t offset = 0;
+    size_t outLength = 0;
+    Basic128Rsa15_PolicyContext *pc = cc->policyContext;
+    while(lenDataToEncrypt >= plainTextBlockSize) {
+        int mbedErr = mbedtls_pk_encrypt(&cc->remoteCertificate.pk,
+                                         data->data + inOffset, plainTextBlockSize,
+                                         encrypted.data + offset, &outLength,
+                                         encrypted.length - offset,
+                                         mbedtls_ctr_drbg_random,
+                                         &pc->drbgContext);
+        UA_MBEDTLS_ERRORHANDLING(UA_STATUSCODE_BADINTERNALERROR);
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_ByteString_deleteMembers(&encrypted);
+            return retval;
+        }
+
+        inOffset += plainTextBlockSize;
+        offset += outLength;
+        lenDataToEncrypt -= plainTextBlockSize;
+    }
+
+    memcpy(data->data, encrypted.data, offset);
+    UA_ByteString_deleteMembers(&encrypted);
+
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode
+asym_decrypt_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy,
+                              Basic128Rsa15_ChannelContext *cc,
+                              UA_ByteString *data) {
+    if(securityPolicy == NULL || cc == NULL || data == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    mbedtls_rsa_context *rsaContext =
+        mbedtls_pk_rsa(cc->policyContext->localPrivateKey);
+
+    if(data->length % rsaContext->len != 0)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    UA_ByteString decrypted;
+    UA_StatusCode retval = UA_ByteString_allocBuffer(&decrypted, data->length);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    size_t lenDataToDecrypt = data->length;
+    size_t inOffset = 0;
+    size_t offset = 0;
+    size_t outLength = 0;
+    while(lenDataToDecrypt >= rsaContext->len) {
+        int mbedErr = mbedtls_pk_decrypt(&cc->policyContext->localPrivateKey,
+                                         data->data + inOffset, rsaContext->len,
+                                         decrypted.data + offset, &outLength,
+                                         decrypted.length - offset, NULL, NULL);
+        if(mbedErr)
+            UA_ByteString_deleteMembers(&decrypted); // TODO: Maybe change error macro to jump to cleanup?
+        UA_MBEDTLS_ERRORHANDLING_RETURN(UA_STATUSCODE_BADSECURITYCHECKSFAILED);
+
+        inOffset += rsaContext->len;
+        offset += outLength;
+        lenDataToDecrypt -= rsaContext->len;
+    }
+
+    if(lenDataToDecrypt == 0) {
+        memcpy(data->data, decrypted.data, offset);
+        data->length = offset;
+    } else {
+        retval = UA_STATUSCODE_BADINTERNALERROR;
+    }
+
+    UA_ByteString_deleteMembers(&decrypted);
+    return retval;
+}
+
+static size_t
+asym_getRemoteEncryptionKeyLength_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy,
+                                                   const Basic128Rsa15_ChannelContext *cc) {
+    return mbedtls_pk_get_len(&cc->remoteCertificate.pk) * 8;
+}
+
+static size_t
+asym_getRemoteBlockSize_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy,
+                                         const Basic128Rsa15_ChannelContext *cc) {
+    mbedtls_rsa_context *const rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk);
+    return rsaContext->len;
+}
+
+static size_t
+asym_getRemotePlainTextBlockSize_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy,
+                                                  const Basic128Rsa15_ChannelContext *cc) {
+    mbedtls_rsa_context *const rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk);
+    return rsaContext->len - UA_SECURITYPOLICY_BASIC128RSA15_RSAPADDING_LEN;
+}
+
+static UA_StatusCode
+asym_makeThumbprint_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy,
+                                     const UA_ByteString *certificate,
+                                     UA_ByteString *thumbprint) {
+    if(securityPolicy == NULL || certificate == NULL || thumbprint == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    if(UA_ByteString_equal(certificate, &UA_BYTESTRING_NULL))
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    if(thumbprint->length != UA_SHA1_LENGTH)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+#if MBEDTLS_VERSION_NUMBER >= 0x02070000
+    mbedtls_sha1_ret(certificate->data, certificate->length, thumbprint->data);
+#else
+    mbedtls_sha1(certificate->data, certificate->length, thumbprint->data);
+#endif
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode
+asymmetricModule_compareCertificateThumbprint_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy,
+                                                               const UA_ByteString *certificateThumbprint) {
+    if(securityPolicy == NULL || certificateThumbprint == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    Basic128Rsa15_PolicyContext *pc = (Basic128Rsa15_PolicyContext *)securityPolicy->policyContext;
+    if(!UA_ByteString_equal(certificateThumbprint, &pc->localCertThumbprint))
+        return UA_STATUSCODE_BADCERTIFICATEINVALID;
+
+    return UA_STATUSCODE_GOOD;
+}
+
+/*******************/
+/* SymmetricModule */
+/*******************/
+
+static void
+md_hmac(mbedtls_md_context_t *context, const UA_ByteString *key,
+        const UA_ByteString *in, unsigned char out[20]) {
+    mbedtls_md_hmac_starts(context, key->data, key->length);
+    mbedtls_md_hmac_update(context, in->data, in->length);
+    mbedtls_md_hmac_finish(context, out);
+}
+
+static UA_StatusCode
+sym_verify_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy,
+                            Basic128Rsa15_ChannelContext *cc,
+                            const UA_ByteString *message,
+                            const UA_ByteString *signature) {
+    if(securityPolicy == NULL || cc == NULL || message == NULL || signature == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    /* Compute MAC */
+    if(signature->length != UA_SHA1_LENGTH) {
+        UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY,
+                     "Signature size does not have the desired size defined by the security policy");
+        return UA_STATUSCODE_BADSECURITYCHECKSFAILED;
+    }
+
+    Basic128Rsa15_PolicyContext *pc =
+        (Basic128Rsa15_PolicyContext *)securityPolicy->policyContext;
+
+    unsigned char mac[UA_SHA1_LENGTH];
+    md_hmac(&pc->sha1MdContext, &cc->remoteSymSigningKey, message, mac);
+
+    /* Compare with Signature */
+    if(memcmp(signature->data, mac, UA_SHA1_LENGTH) != 0)
+        return UA_STATUSCODE_BADSECURITYCHECKSFAILED;
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode
+sym_sign_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy,
+                          const Basic128Rsa15_ChannelContext *cc,
+                          const UA_ByteString *message,
+                          UA_ByteString *signature) {
+    if(signature->length != UA_SHA1_LENGTH)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    md_hmac(&cc->policyContext->sha1MdContext, &cc->localSymSigningKey,
+            message, signature->data);
+    return UA_STATUSCODE_GOOD;
+}
+
+static size_t
+sym_getSignatureSize_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy,
+                                      const void *channelContext) {
+    return UA_SHA1_LENGTH;
+}
+
+static size_t
+sym_getSigningKeyLength_sp_basic128rsa15(const UA_SecurityPolicy *const securityPolicy,
+                                         const void *const channelContext) {
+    return UA_BASIC128RSA15_SYM_SIGNING_KEY_LENGTH;
+}
+
+static size_t
+sym_getEncryptionKeyLength_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy,
+                                            const void *channelContext) {
+    return UA_SECURITYPOLICY_BASIC128RSA15_SYM_KEY_LENGTH;
+}
+
+static size_t
+sym_getEncryptionBlockSize_sp_basic128rsa15(const UA_SecurityPolicy *const securityPolicy,
+                                            const void *const channelContext) {
+    return UA_SECURITYPOLICY_BASIC128RSA15_SYM_ENCRYPTION_BLOCK_SIZE;
+}
+
+static size_t
+sym_getPlainTextBlockSize_sp_basic128rsa15(const UA_SecurityPolicy *const securityPolicy,
+                                           const void *const channelContext) {
+    return UA_SECURITYPOLICY_BASIC128RSA15_SYM_PLAIN_TEXT_BLOCK_SIZE;
+}
+
+static UA_StatusCode
+sym_encrypt_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy,
+                             const Basic128Rsa15_ChannelContext *cc,
+                             UA_ByteString *data) {
+    if(securityPolicy == NULL || cc == NULL || data == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    if(cc->localSymIv.length !=
+       securityPolicy->symmetricModule.cryptoModule.encryptionAlgorithm.getLocalBlockSize(securityPolicy, cc))
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    size_t plainTextBlockSize =
+        securityPolicy->symmetricModule.cryptoModule.encryptionAlgorithm.getLocalPlainTextBlockSize(securityPolicy, cc);
+
+    if(data->length % plainTextBlockSize != 0) {
+        UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY,
+                     "Length of data to encrypt is not a multiple of the plain text block size."
+                         "Padding might not have been calculated appropriately.");
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
+
+    /* Keylength in bits */
+    unsigned int keylength = (unsigned int)(cc->localSymEncryptingKey.length * 8);
+    mbedtls_aes_context aesContext;
+    int mbedErr = mbedtls_aes_setkey_enc(&aesContext, cc->localSymEncryptingKey.data, keylength);
+    UA_MBEDTLS_ERRORHANDLING_RETURN(UA_STATUSCODE_BADINTERNALERROR);
+
+    UA_ByteString ivCopy;
+    UA_StatusCode retval = UA_ByteString_copy(&cc->localSymIv, &ivCopy);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    mbedErr = mbedtls_aes_crypt_cbc(&aesContext, MBEDTLS_AES_ENCRYPT, data->length,
+                                    ivCopy.data, data->data, data->data);
+    UA_MBEDTLS_ERRORHANDLING(UA_STATUSCODE_BADINTERNALERROR);
+    UA_ByteString_deleteMembers(&ivCopy);
+    return retval;
+}
+
+static UA_StatusCode
+sym_decrypt_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy,
+                             const Basic128Rsa15_ChannelContext *cc,
+                             UA_ByteString *data) {
+    if(securityPolicy == NULL || cc == NULL || data == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    size_t encryptionBlockSize =
+        securityPolicy->symmetricModule.cryptoModule.encryptionAlgorithm.getLocalBlockSize(securityPolicy, cc);
+
+    if(cc->remoteSymIv.length != encryptionBlockSize)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    if(data->length % encryptionBlockSize != 0) {
+        UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY,
+                     "Length of data to decrypt is not a multiple of the encryptingBlock size.");
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
+
+    unsigned int keylength = (unsigned int)(cc->remoteSymEncryptingKey.length * 8);
+    mbedtls_aes_context aesContext;
+    int mbedErr = mbedtls_aes_setkey_dec(&aesContext, cc->remoteSymEncryptingKey.data, keylength);
+    UA_MBEDTLS_ERRORHANDLING_RETURN(UA_STATUSCODE_BADINTERNALERROR);
+
+    UA_ByteString ivCopy;
+    UA_StatusCode retval = UA_ByteString_copy(&cc->remoteSymIv, &ivCopy);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    mbedErr = mbedtls_aes_crypt_cbc(&aesContext, MBEDTLS_AES_DECRYPT, data->length,
+                                    ivCopy.data, data->data, data->data);
+    UA_MBEDTLS_ERRORHANDLING(UA_STATUSCODE_BADINTERNALERROR);
+    UA_ByteString_deleteMembers(&ivCopy);
+    return retval;
+}
+
+static void
+swapBuffers(UA_ByteString *const bufA, UA_ByteString *const bufB) {
+    UA_ByteString tmp = *bufA;
+    *bufA = *bufB;
+    *bufB = tmp;
+}
+
+static UA_StatusCode
+sym_generateKey_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy,
+                                 const UA_ByteString *secret, const UA_ByteString *seed,
+                                 UA_ByteString *out) {
+    if(securityPolicy == NULL || secret == NULL || seed == NULL || out == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    Basic128Rsa15_PolicyContext *pc =
+        (Basic128Rsa15_PolicyContext *)securityPolicy->policyContext;
+
+    size_t hashLen = 0;
+    const mbedtls_md_info_t *mdInfo = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
+    hashLen = (size_t)mbedtls_md_get_size(mdInfo);
+
+    UA_ByteString A_and_seed;
+    UA_ByteString_allocBuffer(&A_and_seed, hashLen + seed->length);
+    memcpy(A_and_seed.data + hashLen, seed->data, seed->length);
+
+    UA_ByteString ANext_and_seed;
+    UA_ByteString_allocBuffer(&ANext_and_seed, hashLen + seed->length);
+    memcpy(ANext_and_seed.data + hashLen, seed->data, seed->length);
+
+    UA_ByteString A = {
+        hashLen,
+        A_and_seed.data
+    };
+
+    UA_ByteString ANext = {
+        hashLen,
+        ANext_and_seed.data
+    };
+
+    md_hmac(&pc->sha1MdContext, secret, seed, A.data);
+
+    UA_StatusCode retval = 0;
+    for(size_t offset = 0; offset < out->length; offset += hashLen) {
+        UA_ByteString outSegment = {
+            hashLen,
+            out->data + offset
+        };
+        UA_Boolean bufferAllocated = UA_FALSE;
+        // Not enough room in out buffer to write the hash.
+        if(offset + hashLen > out->length) {
+            outSegment.data = NULL;
+            outSegment.length = 0;
+            retval |= UA_ByteString_allocBuffer(&outSegment, hashLen);
+            if(retval != UA_STATUSCODE_GOOD) {
+                UA_ByteString_deleteMembers(&A_and_seed);
+                UA_ByteString_deleteMembers(&ANext_and_seed);
+                return retval;
+            }
+            bufferAllocated = UA_TRUE;
+        }
+
+        md_hmac(&pc->sha1MdContext, secret, &A_and_seed, outSegment.data);
+        md_hmac(&pc->sha1MdContext, secret, &A, ANext.data);
+
+        if(retval != UA_STATUSCODE_GOOD) {
+            if(bufferAllocated)
+                UA_ByteString_deleteMembers(&outSegment);
+            UA_ByteString_deleteMembers(&A_and_seed);
+            UA_ByteString_deleteMembers(&ANext_and_seed);
+            return retval;
+        }
+
+        if(bufferAllocated) {
+            memcpy(out->data + offset, outSegment.data, out->length - offset);
+            UA_ByteString_deleteMembers(&outSegment);
+        }
+
+        swapBuffers(&ANext_and_seed, &A_and_seed);
+        swapBuffers(&ANext, &A);
+    }
+
+    UA_ByteString_deleteMembers(&A_and_seed);
+    UA_ByteString_deleteMembers(&ANext_and_seed);
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode
+sym_generateNonce_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy,
+                                   UA_ByteString *out) {
+    if(securityPolicy == NULL || securityPolicy->policyContext == NULL || out == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    Basic128Rsa15_PolicyContext *data =
+        (Basic128Rsa15_PolicyContext *)securityPolicy->policyContext;
+
+    int mbedErr = mbedtls_ctr_drbg_random(&data->drbgContext, out->data, out->length);
+    UA_MBEDTLS_ERRORHANDLING_RETURN(UA_STATUSCODE_BADUNEXPECTEDERROR);
+
+    return UA_STATUSCODE_GOOD;
+}
+
+/*****************/
+/* ChannelModule */
+/*****************/
+
+/* Assumes that the certificate has been verified externally */
+static UA_StatusCode
+parseRemoteCertificate_sp_basic128rsa15(Basic128Rsa15_ChannelContext *cc,
+                                        const UA_ByteString *remoteCertificate) {
+    if(remoteCertificate == NULL || cc == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    const UA_SecurityPolicy *securityPolicy = cc->policyContext->securityPolicy;
+
+    /* Parse the certificate */
+    int mbedErr = mbedtls_x509_crt_parse(&cc->remoteCertificate, remoteCertificate->data,
+                                         remoteCertificate->length);
+    UA_MBEDTLS_ERRORHANDLING_RETURN(UA_STATUSCODE_BADSECURITYCHECKSFAILED);
+
+    /* Check the key length */
+    mbedtls_rsa_context *rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk);
+    if(rsaContext->len < UA_SECURITYPOLICY_BASIC128RSA15_MINASYMKEYLENGTH ||
+       rsaContext->len > UA_SECURITYPOLICY_BASIC128RSA15_MAXASYMKEYLENGTH)
+        return UA_STATUSCODE_BADCERTIFICATEUSENOTALLOWED;
+
+    return UA_STATUSCODE_GOOD;
+}
+
+static void
+channelContext_deleteContext_sp_basic128rsa15(Basic128Rsa15_ChannelContext *cc) {
+    UA_ByteString_deleteMembers(&cc->localSymSigningKey);
+    UA_ByteString_deleteMembers(&cc->localSymEncryptingKey);
+    UA_ByteString_deleteMembers(&cc->localSymIv);
+
+    UA_ByteString_deleteMembers(&cc->remoteSymSigningKey);
+    UA_ByteString_deleteMembers(&cc->remoteSymEncryptingKey);
+    UA_ByteString_deleteMembers(&cc->remoteSymIv);
+
+    mbedtls_x509_crt_free(&cc->remoteCertificate);
+
+    UA_free(cc);
+}
+
+static UA_StatusCode
+channelContext_newContext_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy,
+                                           const UA_ByteString *remoteCertificate,
+                                           void **pp_contextData) {
+    if(securityPolicy == NULL || remoteCertificate == NULL || pp_contextData == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    /* Allocate the channel context */
+    *pp_contextData = UA_malloc(sizeof(Basic128Rsa15_ChannelContext));
+    if(*pp_contextData == NULL)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+
+    Basic128Rsa15_ChannelContext *cc = (Basic128Rsa15_ChannelContext *)*pp_contextData;
+
+    /* Initialize the channel context */
+    cc->policyContext = (Basic128Rsa15_PolicyContext *)securityPolicy->policyContext;
+
+    UA_ByteString_init(&cc->localSymSigningKey);
+    UA_ByteString_init(&cc->localSymEncryptingKey);
+    UA_ByteString_init(&cc->localSymIv);
+
+    UA_ByteString_init(&cc->remoteSymSigningKey);
+    UA_ByteString_init(&cc->remoteSymEncryptingKey);
+    UA_ByteString_init(&cc->remoteSymIv);
+
+    mbedtls_x509_crt_init(&cc->remoteCertificate);
+
+    // TODO: this can be optimized so that we dont allocate memory before parsing the certificate
+    UA_StatusCode retval = parseRemoteCertificate_sp_basic128rsa15(cc, remoteCertificate);
+    if(retval != UA_STATUSCODE_GOOD) {
+        channelContext_deleteContext_sp_basic128rsa15(cc);
+        *pp_contextData = NULL;
+    }
+    return retval;
+}
+
+static UA_StatusCode
+channelContext_setLocalSymEncryptingKey_sp_basic128rsa15(Basic128Rsa15_ChannelContext *cc,
+                                                         const UA_ByteString *key) {
+    if(key == NULL || cc == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    UA_ByteString_deleteMembers(&cc->localSymEncryptingKey);
+    return UA_ByteString_copy(key, &cc->localSymEncryptingKey);
+}
+
+static UA_StatusCode
+channelContext_setLocalSymSigningKey_sp_basic128rsa15(Basic128Rsa15_ChannelContext *cc,
+                                                      const UA_ByteString *key) {
+    if(key == NULL || cc == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    UA_ByteString_deleteMembers(&cc->localSymSigningKey);
+    return UA_ByteString_copy(key, &cc->localSymSigningKey);
+}
+
+
+static UA_StatusCode
+channelContext_setLocalSymIv_sp_basic128rsa15(Basic128Rsa15_ChannelContext *cc,
+                                              const UA_ByteString *iv) {
+    if(iv == NULL || cc == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    UA_ByteString_deleteMembers(&cc->localSymIv);
+    return UA_ByteString_copy(iv, &cc->localSymIv);
+}
+
+static UA_StatusCode
+channelContext_setRemoteSymEncryptingKey_sp_basic128rsa15(Basic128Rsa15_ChannelContext *cc,
+                                                          const UA_ByteString *key) {
+    if(key == NULL || cc == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    UA_ByteString_deleteMembers(&cc->remoteSymEncryptingKey);
+    return UA_ByteString_copy(key, &cc->remoteSymEncryptingKey);
+}
+
+static UA_StatusCode
+channelContext_setRemoteSymSigningKey_sp_basic128rsa15(Basic128Rsa15_ChannelContext *cc,
+                                                       const UA_ByteString *key) {
+    if(key == NULL || cc == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    UA_ByteString_deleteMembers(&cc->remoteSymSigningKey);
+    return UA_ByteString_copy(key, &cc->remoteSymSigningKey);
+}
+
+static UA_StatusCode
+channelContext_setRemoteSymIv_sp_basic128rsa15(Basic128Rsa15_ChannelContext *cc,
+                                               const UA_ByteString *iv) {
+    if(iv == NULL || cc == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    UA_ByteString_deleteMembers(&cc->remoteSymIv);
+    return UA_ByteString_copy(iv, &cc->remoteSymIv);
+}
+
+static UA_StatusCode
+channelContext_compareCertificate_sp_basic128rsa15(const Basic128Rsa15_ChannelContext *cc,
+                                                   const UA_ByteString *certificate) {
+    if(cc == NULL || certificate == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    const UA_SecurityPolicy *securityPolicy = cc->policyContext->securityPolicy;
+
+    mbedtls_x509_crt cert;
+    int mbedErr = mbedtls_x509_crt_parse(&cert, certificate->data, certificate->length);
+    UA_MBEDTLS_ERRORHANDLING_RETURN(UA_STATUSCODE_BADSECURITYCHECKSFAILED);
+
+    if(cert.raw.len != cc->remoteCertificate.raw.len)
+        return UA_STATUSCODE_BADSECURITYCHECKSFAILED;
+
+    if(memcmp(cert.raw.p, cc->remoteCertificate.raw.p, cert.raw.len) != 0)
+        return UA_STATUSCODE_BADSECURITYCHECKSFAILED;
+
+    return UA_STATUSCODE_GOOD;
+}
+
+static void
+deleteMembers_sp_basic128rsa15(UA_SecurityPolicy *securityPolicy) {
+    if(securityPolicy == NULL)
+        return;
+
+    if(securityPolicy->policyContext == NULL)
+        return;
+
+    UA_ByteString_deleteMembers(&securityPolicy->localCertificate);
+
+    /* delete all allocated members in the context */
+    Basic128Rsa15_PolicyContext *pc = (Basic128Rsa15_PolicyContext *)
+        securityPolicy->policyContext;
+
+    mbedtls_ctr_drbg_free(&pc->drbgContext);
+    mbedtls_entropy_free(&pc->entropyContext);
+    mbedtls_pk_free(&pc->localPrivateKey);
+    mbedtls_md_free(&pc->sha1MdContext);
+    UA_ByteString_deleteMembers(&pc->localCertThumbprint);
+
+    UA_LOG_DEBUG(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY,
+                 "Deleted members of EndpointContext for sp_basic128rsa15");
+
+    UA_free(pc);
+    securityPolicy->policyContext = NULL;
+}
+
+static UA_StatusCode
+policyContext_newContext_sp_basic128rsa15(UA_SecurityPolicy *securityPolicy,
+                                          const UA_ByteString localPrivateKey) {
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    if(securityPolicy == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    Basic128Rsa15_PolicyContext *pc = (Basic128Rsa15_PolicyContext *)
+        UA_malloc(sizeof(Basic128Rsa15_PolicyContext));
+    securityPolicy->policyContext = (void *)pc;
+    if(!pc) {
+        retval = UA_STATUSCODE_BADOUTOFMEMORY;
+        goto error;
+    }
+
+    /* Initialize the PolicyContext */
+    memset(pc, 0, sizeof(Basic128Rsa15_PolicyContext));
+    mbedtls_ctr_drbg_init(&pc->drbgContext);
+    mbedtls_entropy_init(&pc->entropyContext);
+    mbedtls_pk_init(&pc->localPrivateKey);
+    mbedtls_md_init(&pc->sha1MdContext);
+    pc->securityPolicy = securityPolicy;
+
+    /* Initialized the message digest */
+    const mbedtls_md_info_t *const mdInfo = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
+    int mbedErr = mbedtls_md_setup(&pc->sha1MdContext, mdInfo, MBEDTLS_MD_SHA1);
+    UA_MBEDTLS_ERRORHANDLING(UA_STATUSCODE_BADOUTOFMEMORY);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto error;
+
+    /* Add the system entropy source */
+    mbedErr = mbedtls_entropy_add_source(&pc->entropyContext,
+                                         mbedtls_platform_entropy_poll, NULL, 0,
+                                         MBEDTLS_ENTROPY_SOURCE_STRONG);
+    UA_MBEDTLS_ERRORHANDLING(UA_STATUSCODE_BADSECURITYCHECKSFAILED);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto error;
+
+    /* Seed the RNG */
+    char *personalization = "open62541-drbg";
+    mbedErr = mbedtls_ctr_drbg_seed(&pc->drbgContext, mbedtls_entropy_func,
+                                    &pc->entropyContext,
+                                    (const unsigned char *)personalization, 14);
+    UA_MBEDTLS_ERRORHANDLING(UA_STATUSCODE_BADSECURITYCHECKSFAILED);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto error;
+
+    /* Set the private key */
+    mbedErr = mbedtls_pk_parse_key(&pc->localPrivateKey,
+                                   localPrivateKey.data, localPrivateKey.length,
+                                   NULL, 0);
+    UA_MBEDTLS_ERRORHANDLING(UA_STATUSCODE_BADSECURITYCHECKSFAILED);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto error;
+
+    /* Set the local certificate thumbprint */
+    retval = UA_ByteString_allocBuffer(&pc->localCertThumbprint, UA_SHA1_LENGTH);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto error;
+    retval = asym_makeThumbprint_sp_basic128rsa15(pc->securityPolicy,
+                                                  &securityPolicy->localCertificate,
+                                                  &pc->localCertThumbprint);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto error;
+
+    return UA_STATUSCODE_GOOD;
+
+error:
+    UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY,
+                 "Could not create securityContext");
+    if(securityPolicy->policyContext != NULL)
+        deleteMembers_sp_basic128rsa15(securityPolicy);
+    return retval;
+}
+
+UA_StatusCode
+UA_SecurityPolicy_Basic128Rsa15(UA_SecurityPolicy *policy, UA_CertificateVerification *certificateVerification,
+                                const UA_ByteString localCertificate, const UA_ByteString localPrivateKey,
+                                UA_Logger logger) {
+    memset(policy, 0, sizeof(UA_SecurityPolicy));
+    policy->logger = logger;
+
+    policy->policyUri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15");
+
+    UA_SecurityPolicyAsymmetricModule *const asymmetricModule = &policy->asymmetricModule;
+    UA_SecurityPolicySymmetricModule *const symmetricModule = &policy->symmetricModule;
+    UA_SecurityPolicyChannelModule *const channelModule = &policy->channelModule;
+
+    /* Copy the certificate and add a NULL to the end */
+    UA_StatusCode retval =
+        UA_ByteString_allocBuffer(&policy->localCertificate, localCertificate.length + 1);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+    memcpy(policy->localCertificate.data, localCertificate.data, localCertificate.length);
+    policy->localCertificate.data[localCertificate.length] = '\0';
+    policy->localCertificate.length--;
+    policy->certificateVerification = certificateVerification;
+
+    /* AsymmetricModule */
+    UA_SecurityPolicySignatureAlgorithm *asym_signatureAlgorithm =
+        &asymmetricModule->cryptoModule.signatureAlgorithm;
+    asym_signatureAlgorithm->uri =
+        UA_STRING("http://www.w3.org/2000/09/xmldsig#rsa-sha1\0");
+    asym_signatureAlgorithm->verify =
+        (UA_StatusCode (*)(const UA_SecurityPolicy *, void *,
+                           const UA_ByteString *, const UA_ByteString *))asym_verify_sp_basic128rsa15;
+    asym_signatureAlgorithm->sign =
+        (UA_StatusCode (*)(const UA_SecurityPolicy *, void *,
+                           const UA_ByteString *, UA_ByteString *))asym_sign_sp_basic128rsa15;
+    asym_signatureAlgorithm->getLocalSignatureSize =
+        (size_t (*)(const UA_SecurityPolicy *, const void *))asym_getLocalSignatureSize_sp_basic128rsa15;
+    asym_signatureAlgorithm->getRemoteSignatureSize =
+        (size_t (*)(const UA_SecurityPolicy *, const void *))asym_getRemoteSignatureSize_sp_basic128rsa15;
+    asym_signatureAlgorithm->getLocalKeyLength = NULL; // TODO: Write function
+    asym_signatureAlgorithm->getRemoteKeyLength = NULL; // TODO: Write function
+
+    UA_SecurityPolicyEncryptionAlgorithm *asym_encryptionAlgorithm =
+        &asymmetricModule->cryptoModule.encryptionAlgorithm;
+    asym_encryptionAlgorithm->uri = UA_STRING("TODO: ALG URI");
+    asym_encryptionAlgorithm->encrypt =
+        (UA_StatusCode(*)(const UA_SecurityPolicy *, void *, UA_ByteString *))asym_encrypt_sp_basic128rsa15;
+    asym_encryptionAlgorithm->decrypt =
+        (UA_StatusCode(*)(const UA_SecurityPolicy *, void *, UA_ByteString *))
+            asym_decrypt_sp_basic128rsa15;
+    asym_encryptionAlgorithm->getLocalKeyLength = NULL; // TODO: Write function
+    asym_encryptionAlgorithm->getRemoteKeyLength =
+        (size_t (*)(const UA_SecurityPolicy *, const void *))asym_getRemoteEncryptionKeyLength_sp_basic128rsa15;
+    asym_encryptionAlgorithm->getLocalBlockSize = NULL; // TODO: Write function
+    asym_encryptionAlgorithm->getRemoteBlockSize = (size_t (*)(const UA_SecurityPolicy *,
+                                                               const void *))asym_getRemoteBlockSize_sp_basic128rsa15;
+    asym_encryptionAlgorithm->getLocalPlainTextBlockSize = NULL; // TODO: Write function
+    asym_encryptionAlgorithm->getRemotePlainTextBlockSize =
+        (size_t (*)(const UA_SecurityPolicy *, const void *))asym_getRemotePlainTextBlockSize_sp_basic128rsa15;
+
+    asymmetricModule->makeCertificateThumbprint = asym_makeThumbprint_sp_basic128rsa15;
+    asymmetricModule->compareCertificateThumbprint =
+        asymmetricModule_compareCertificateThumbprint_sp_basic128rsa15;
+
+    /* SymmetricModule */
+    symmetricModule->generateKey = sym_generateKey_sp_basic128rsa15;
+    symmetricModule->generateNonce = sym_generateNonce_sp_basic128rsa15;
+
+    UA_SecurityPolicySignatureAlgorithm *sym_signatureAlgorithm =
+        &symmetricModule->cryptoModule.signatureAlgorithm;
+    sym_signatureAlgorithm->uri =
+        UA_STRING("http://www.w3.org/2000/09/xmldsig#hmac-sha1\0");
+    sym_signatureAlgorithm->verify =
+        (UA_StatusCode (*)(const UA_SecurityPolicy *, void *, const UA_ByteString *,
+                           const UA_ByteString *))sym_verify_sp_basic128rsa15;
+    sym_signatureAlgorithm->sign =
+        (UA_StatusCode (*)(const UA_SecurityPolicy *, void *,
+                           const UA_ByteString *, UA_ByteString *))sym_sign_sp_basic128rsa15;
+    sym_signatureAlgorithm->getLocalSignatureSize = sym_getSignatureSize_sp_basic128rsa15;
+    sym_signatureAlgorithm->getRemoteSignatureSize = sym_getSignatureSize_sp_basic128rsa15;
+    sym_signatureAlgorithm->getLocalKeyLength =
+        (size_t (*)(const UA_SecurityPolicy *,
+                    const void *))sym_getSigningKeyLength_sp_basic128rsa15;
+    sym_signatureAlgorithm->getRemoteKeyLength =
+        (size_t (*)(const UA_SecurityPolicy *,
+                    const void *))sym_getSigningKeyLength_sp_basic128rsa15;
+
+    UA_SecurityPolicyEncryptionAlgorithm *sym_encryptionAlgorithm =
+        &symmetricModule->cryptoModule.encryptionAlgorithm;
+    sym_encryptionAlgorithm->uri = UA_STRING("http://www.w3.org/2001/04/xmlenc#aes128-cbc");
+    sym_encryptionAlgorithm->encrypt =
+        (UA_StatusCode(*)(const UA_SecurityPolicy *, void *, UA_ByteString *))sym_encrypt_sp_basic128rsa15;
+    sym_encryptionAlgorithm->decrypt =
+        (UA_StatusCode(*)(const UA_SecurityPolicy *, void *, UA_ByteString *))sym_decrypt_sp_basic128rsa15;
+    sym_encryptionAlgorithm->getLocalKeyLength = sym_getEncryptionKeyLength_sp_basic128rsa15;
+    sym_encryptionAlgorithm->getRemoteKeyLength = sym_getEncryptionKeyLength_sp_basic128rsa15;
+    sym_encryptionAlgorithm->getLocalBlockSize =
+        (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getEncryptionBlockSize_sp_basic128rsa15;
+    sym_encryptionAlgorithm->getRemoteBlockSize =
+        (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getEncryptionBlockSize_sp_basic128rsa15;
+    sym_encryptionAlgorithm->getLocalPlainTextBlockSize =
+        (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getPlainTextBlockSize_sp_basic128rsa15;
+    sym_encryptionAlgorithm->getRemotePlainTextBlockSize =
+        (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getPlainTextBlockSize_sp_basic128rsa15;
+    symmetricModule->secureChannelNonceLength = 16;
+
+    // Use the same signature algorithm as the asymmetric component for certificate signing (see standard)
+    policy->certificateSigningAlgorithm = policy->asymmetricModule.cryptoModule.signatureAlgorithm;
+
+    /* ChannelModule */
+    channelModule->newContext = channelContext_newContext_sp_basic128rsa15;
+    channelModule->deleteContext = (void (*)(void *))
+        channelContext_deleteContext_sp_basic128rsa15;
+
+    channelModule->setLocalSymEncryptingKey = (UA_StatusCode (*)(void *, const UA_ByteString *))
+        channelContext_setLocalSymEncryptingKey_sp_basic128rsa15;
+    channelModule->setLocalSymSigningKey = (UA_StatusCode (*)(void *, const UA_ByteString *))
+        channelContext_setLocalSymSigningKey_sp_basic128rsa15;
+    channelModule->setLocalSymIv = (UA_StatusCode (*)(void *, const UA_ByteString *))
+        channelContext_setLocalSymIv_sp_basic128rsa15;
+
+    channelModule->setRemoteSymEncryptingKey = (UA_StatusCode (*)(void *, const UA_ByteString *))
+        channelContext_setRemoteSymEncryptingKey_sp_basic128rsa15;
+    channelModule->setRemoteSymSigningKey = (UA_StatusCode (*)(void *, const UA_ByteString *))
+        channelContext_setRemoteSymSigningKey_sp_basic128rsa15;
+    channelModule->setRemoteSymIv = (UA_StatusCode (*)(void *, const UA_ByteString *))
+        channelContext_setRemoteSymIv_sp_basic128rsa15;
+
+    channelModule->compareCertificate = (UA_StatusCode (*)(const void *, const UA_ByteString *))
+        channelContext_compareCertificate_sp_basic128rsa15;
+
+    policy->deleteMembers = deleteMembers_sp_basic128rsa15;
+
+    return policyContext_newContext_sp_basic128rsa15(policy, localPrivateKey);
+}

+ 29 - 0
plugins/ua_securitypolicy_basic128rsa15.h

@@ -0,0 +1,29 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2018 (c) Mark Giraud, Fraunhofer IOSB
+ */
+
+#ifndef UA_SECURITYPOLICY_BASIC128RSA15_H_
+#define UA_SECURITYPOLICY_BASIC128RSA15_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ua_plugin_securitypolicy.h"
+#include "ua_plugin_log.h"
+
+UA_EXPORT UA_StatusCode
+UA_SecurityPolicy_Basic128Rsa15(UA_SecurityPolicy *policy,
+                                UA_CertificateVerification *certificateVerification,
+                                const UA_ByteString localCertificate,
+                                const UA_ByteString localPrivateKey,
+                                UA_Logger logger);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // UA_SECURITYPOLICY_BASIC128RSA15_H_

+ 991 - 0
plugins/ua_securitypolicy_basic256sha256.c

@@ -0,0 +1,991 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ *    Copyright 2018 (c) Mark Giraud, Fraunhofer IOSB
+ *    Copyright 2018 (c) Daniel Feist, Precitec GmbH & Co. KG
+ */
+
+#include <mbedtls/aes.h>
+#include <mbedtls/md.h>
+#include <mbedtls/sha256.h>
+#include <mbedtls/x509_crt.h>
+#include <mbedtls/ctr_drbg.h>
+#include <mbedtls/entropy.h>
+#include <mbedtls/entropy_poll.h>
+#include <mbedtls/error.h>
+#include <mbedtls/version.h>
+#include <mbedtls/sha1.h>
+
+#include "ua_plugin_pki.h"
+#include "ua_plugin_securitypolicy.h"
+#include "ua_securitypolicy_basic256sha256.h"
+#include "ua_types.h"
+#include "ua_types_generated_handling.h"
+
+/* Notes:
+ * mbedTLS' AES allows in-place encryption and decryption. Sow we don't have to
+ * allocate temp buffers.
+ * https://tls.mbed.org/discussions/generic/in-place-decryption-with-aes256-same-input-output-buffer
+ */
+
+#define UA_SECURITYPOLICY_BASIC256SHA256_RSAPADDING_LEN 42
+#define UA_SHA1_LENGTH 20
+#define UA_SHA256_LENGTH 32
+#define UA_BASIC256SHA256_SYM_SIGNING_KEY_LENGTH 32
+#define UA_SECURITYPOLICY_BASIC256SHA256_SYM_KEY_LENGTH 32
+#define UA_SECURITYPOLICY_BASIC256SHA256_SYM_ENCRYPTION_BLOCK_SIZE 16
+#define UA_SECURITYPOLICY_BASIC256SHA256_SYM_PLAIN_TEXT_BLOCK_SIZE 16
+#define UA_SECURITYPOLICY_BASIC256SHA256_MINASYMKEYLENGTH 256
+#define UA_SECURITYPOLICY_BASIC256SHA256_MAXASYMKEYLENGTH 512
+
+#define UA_LOG_MBEDERR                                                  \
+    char errBuff[300];                                                  \
+    mbedtls_strerror(mbedErr, errBuff, 300);                            \
+    UA_LOG_WARNING(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, \
+                   "mbedTLS returned an error: %s", errBuff);           \
+
+#define UA_MBEDTLS_ERRORHANDLING(errorcode)                             \
+    if(mbedErr) {                                                       \
+        UA_LOG_MBEDERR                                                  \
+        retval = errorcode;                                             \
+    }
+
+#define UA_MBEDTLS_ERRORHANDLING_RETURN(errorcode)                      \
+    if(mbedErr) {                                                       \
+        UA_LOG_MBEDERR                                                  \
+        return errorcode;                                               \
+    }
+
+typedef struct {
+    const UA_SecurityPolicy *securityPolicy;
+    UA_ByteString localCertThumbprint;
+
+    mbedtls_ctr_drbg_context drbgContext;
+    mbedtls_entropy_context entropyContext;
+    mbedtls_md_context_t sha256MdContext;
+    mbedtls_pk_context localPrivateKey;
+} Basic256Sha256_PolicyContext;
+
+typedef struct {
+    Basic256Sha256_PolicyContext *policyContext;
+
+    UA_ByteString localSymSigningKey;
+    UA_ByteString localSymEncryptingKey;
+    UA_ByteString localSymIv;
+
+    UA_ByteString remoteSymSigningKey;
+    UA_ByteString remoteSymEncryptingKey;
+    UA_ByteString remoteSymIv;
+
+    mbedtls_x509_crt remoteCertificate;
+} Basic256Sha256_ChannelContext;
+
+/********************/
+/* AsymmetricModule */
+/********************/
+
+/* VERIFY AsymmetricSignatureAlgorithm_RSA-PKCS15-SHA2-256 */
+static UA_StatusCode
+asym_verify_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy,
+                              Basic256Sha256_ChannelContext *cc,
+                              const UA_ByteString *message,
+                              const UA_ByteString *signature) {
+    if(securityPolicy == NULL || message == NULL || signature == NULL || cc == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    unsigned char hash[UA_SHA256_LENGTH];
+#if MBEDTLS_VERSION_NUMBER >= 0x02070000
+    // TODO check return status
+    mbedtls_sha256_ret(message->data, message->length, hash, 0);
+#else
+    mbedtls_sha256(message->data, message->length, hash, 0);
+#endif
+
+    /* Set the RSA settings */
+    mbedtls_rsa_context *rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk);
+    mbedtls_rsa_set_padding(rsaContext, MBEDTLS_RSA_PKCS_V15, MBEDTLS_MD_SHA256);
+
+    /* For RSA keys, the default padding type is PKCS#1 v1.5 in mbedtls_pk_verify() */
+    /* Alternatively, use more specific function mbedtls_rsa_rsassa_pkcs1_v15_verify(), i.e. */
+    /* int mbedErr = mbedtls_rsa_rsassa_pkcs1_v15_verify(rsaContext, NULL, NULL,
+                                                         MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA256,
+                                                         UA_SHA256_LENGTH, hash,
+                                                         signature->data); */
+
+    int mbedErr = mbedtls_pk_verify(&cc->remoteCertificate.pk,
+                                    MBEDTLS_MD_SHA256, hash, UA_SHA256_LENGTH,
+                                    signature->data, signature->length);
+
+    UA_MBEDTLS_ERRORHANDLING_RETURN(UA_STATUSCODE_BADSECURITYCHECKSFAILED);
+    return UA_STATUSCODE_GOOD;
+}
+
+/* AsymmetricSignatureAlgorithm_RSA-PKCS15-SHA2-256 */
+static UA_StatusCode
+asym_sign_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy,
+                            Basic256Sha256_ChannelContext *cc,
+                            const UA_ByteString *message,
+                            UA_ByteString *signature) {
+    if(securityPolicy == NULL || message == NULL || signature == NULL || cc == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    unsigned char hash[UA_SHA256_LENGTH];
+#if MBEDTLS_VERSION_NUMBER >= 0x02070000
+    // TODO check return status
+    mbedtls_sha256_ret(message->data, message->length, hash, 0);
+#else
+    mbedtls_sha256(message->data, message->length, hash, 0);
+#endif
+
+    Basic256Sha256_PolicyContext *pc = cc->policyContext;
+    mbedtls_rsa_context *rsaContext = mbedtls_pk_rsa(pc->localPrivateKey);
+    mbedtls_rsa_set_padding(rsaContext, MBEDTLS_RSA_PKCS_V15, MBEDTLS_MD_SHA256);
+
+    size_t sigLen = 0;
+
+    /* For RSA keys, the default padding type is PKCS#1 v1.5 in mbedtls_pk_sign */
+    /* Alternatively use more specific function mbedtls_rsa_rsassa_pkcs1_v15_sign() */
+    int mbedErr = mbedtls_pk_sign(&pc->localPrivateKey,
+                                  MBEDTLS_MD_SHA256, hash,
+                                  UA_SHA256_LENGTH, signature->data,
+                                  &sigLen, mbedtls_ctr_drbg_random,
+                                  &pc->drbgContext);
+    UA_MBEDTLS_ERRORHANDLING_RETURN(UA_STATUSCODE_BADINTERNALERROR);
+    return UA_STATUSCODE_GOOD;
+}
+
+static size_t
+asym_getLocalSignatureSize_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy,
+                                             const Basic256Sha256_ChannelContext *cc) {
+    if(securityPolicy == NULL || cc == NULL)
+        return 0;
+
+    return mbedtls_pk_rsa(cc->policyContext->localPrivateKey)->len;
+}
+
+static size_t
+asym_getRemoteSignatureSize_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy,
+                                              const Basic256Sha256_ChannelContext *cc) {
+    if(securityPolicy == NULL || cc == NULL)
+        return 0;
+
+    return mbedtls_pk_rsa(cc->remoteCertificate.pk)->len;
+}
+
+/* AsymmetricEncryptionAlgorithm_RSA-OAEP-SHA1 */
+static UA_StatusCode
+asym_encrypt_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy,
+                               Basic256Sha256_ChannelContext *cc,
+                               UA_ByteString *data) {
+    if(securityPolicy == NULL || cc == NULL || data == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    const size_t plainTextBlockSize = securityPolicy->asymmetricModule.cryptoModule.encryptionAlgorithm.
+        getRemotePlainTextBlockSize(securityPolicy, cc);
+
+    if(data->length % plainTextBlockSize != 0)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    mbedtls_rsa_context *remoteRsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk);
+    mbedtls_rsa_set_padding(remoteRsaContext, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA1);
+
+    UA_ByteString encrypted;
+    const size_t bufferOverhead =
+        UA_SecurityPolicy_getRemoteAsymEncryptionBufferLengthOverhead(securityPolicy, cc, data->length);
+    UA_StatusCode retval = UA_ByteString_allocBuffer(&encrypted, data->length + bufferOverhead);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    size_t lenDataToEncrypt = data->length;
+    size_t inOffset = 0;
+    size_t offset = 0;
+    const unsigned char *label = NULL;
+    Basic256Sha256_PolicyContext *pc = cc->policyContext;
+    while(lenDataToEncrypt >= plainTextBlockSize) {
+        int mbedErr = mbedtls_rsa_rsaes_oaep_encrypt(remoteRsaContext, mbedtls_ctr_drbg_random,
+                                                     &pc->drbgContext, MBEDTLS_RSA_PUBLIC,
+                                                     label, 0, plainTextBlockSize,
+                                                     data->data + inOffset, encrypted.data + offset);
+
+        UA_MBEDTLS_ERRORHANDLING(UA_STATUSCODE_BADINTERNALERROR);
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_ByteString_deleteMembers(&encrypted);
+            return retval;
+        }
+
+        inOffset += plainTextBlockSize;
+        offset += remoteRsaContext->len;
+        lenDataToEncrypt -= plainTextBlockSize;
+    }
+
+    memcpy(data->data, encrypted.data, offset);
+    UA_ByteString_deleteMembers(&encrypted);
+
+    return UA_STATUSCODE_GOOD;
+}
+
+/* AsymmetricEncryptionAlgorithm_RSA-OAEP-SHA1 */
+static UA_StatusCode
+asym_decrypt_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy,
+                               Basic256Sha256_ChannelContext *cc,
+                               UA_ByteString *data) {
+    if(securityPolicy == NULL || cc == NULL || data == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    mbedtls_rsa_context *rsaContext =
+        mbedtls_pk_rsa(cc->policyContext->localPrivateKey);
+
+    mbedtls_rsa_set_padding(rsaContext, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA1);
+
+    if(data->length % rsaContext->len != 0)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    UA_ByteString decrypted;
+    UA_StatusCode retval = UA_ByteString_allocBuffer(&decrypted, data->length);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    size_t lenDataToDecrypt = data->length;
+    size_t inOffset = 0;
+    size_t offset = 0;
+    size_t outLength = 0;
+    const unsigned char *label = NULL;
+    Basic256Sha256_PolicyContext *pc = cc->policyContext;
+
+    while(lenDataToDecrypt >= rsaContext->len) {
+        int mbedErr = mbedtls_rsa_rsaes_oaep_decrypt(rsaContext, mbedtls_ctr_drbg_random,
+                                                     &pc->drbgContext, MBEDTLS_RSA_PRIVATE,
+                                                     label, 0, &outLength,
+                                                     data->data + inOffset,
+                                                     decrypted.data + offset,
+                                                     decrypted.length - offset);
+        if(mbedErr)
+            UA_ByteString_deleteMembers(&decrypted); // TODO: Maybe change error macro to jump to cleanup?
+        UA_MBEDTLS_ERRORHANDLING_RETURN(UA_STATUSCODE_BADSECURITYCHECKSFAILED);
+
+        inOffset += rsaContext->len;
+        offset += outLength;
+        lenDataToDecrypt -= rsaContext->len;
+    }
+
+    if(lenDataToDecrypt == 0) {
+        memcpy(data->data, decrypted.data, offset);
+        data->length = offset;
+    } else {
+        retval = UA_STATUSCODE_BADINTERNALERROR;
+    }
+
+    UA_ByteString_deleteMembers(&decrypted);
+    return retval;
+}
+
+static size_t
+asym_getRemoteEncryptionKeyLength_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy,
+                                                    const Basic256Sha256_ChannelContext *cc) {
+    return mbedtls_pk_get_len(&cc->remoteCertificate.pk) * 8;
+}
+
+static size_t
+asym_getRemoteBlockSize_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy,
+                                          const Basic256Sha256_ChannelContext *cc) {
+    mbedtls_rsa_context *const rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk);
+    return rsaContext->len;
+}
+
+static size_t
+asym_getRemotePlainTextBlockSize_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy,
+                                                   const Basic256Sha256_ChannelContext *cc) {
+    mbedtls_rsa_context *const rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk);
+    return rsaContext->len - UA_SECURITYPOLICY_BASIC256SHA256_RSAPADDING_LEN;
+}
+
+static UA_StatusCode
+asym_makeThumbprint_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy,
+                                      const UA_ByteString *certificate,
+                                      UA_ByteString *thumbprint) {
+    if(securityPolicy == NULL || certificate == NULL || thumbprint == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    if(UA_ByteString_equal(certificate, &UA_BYTESTRING_NULL))
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    if(thumbprint->length != UA_SHA1_LENGTH)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    /* The certificate thumbprint is always a 20 bit sha1 hash, see Part 4 of the Specification. */
+#if MBEDTLS_VERSION_NUMBER >= 0x02070000
+    mbedtls_sha1_ret(certificate->data, certificate->length, thumbprint->data);
+#else
+    mbedtls_sha1(certificate->data, certificate->length, thumbprint->data);
+#endif
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode
+asymmetricModule_compareCertificateThumbprint_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy,
+                                                                const UA_ByteString *certificateThumbprint) {
+    if(securityPolicy == NULL || certificateThumbprint == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    Basic256Sha256_PolicyContext *pc = (Basic256Sha256_PolicyContext *)securityPolicy->policyContext;
+    if(!UA_ByteString_equal(certificateThumbprint, &pc->localCertThumbprint))
+        return UA_STATUSCODE_BADCERTIFICATEINVALID;
+
+    return UA_STATUSCODE_GOOD;
+}
+
+/*******************/
+/* SymmetricModule */
+/*******************/
+
+static void
+md_hmac_Basic256Sha256(mbedtls_md_context_t *context, const UA_ByteString *key,
+                       const UA_ByteString *in, unsigned char out[32]) {
+    mbedtls_md_hmac_starts(context, key->data, key->length);
+    mbedtls_md_hmac_update(context, in->data, in->length);
+    mbedtls_md_hmac_finish(context, out);
+}
+
+static UA_StatusCode
+sym_verify_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy,
+                             Basic256Sha256_ChannelContext *cc,
+                             const UA_ByteString *message,
+                             const UA_ByteString *signature) {
+    if(securityPolicy == NULL || cc == NULL || message == NULL || signature == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    /* Compute MAC */
+    if(signature->length != UA_SHA256_LENGTH) {
+        UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY,
+                     "Signature size does not have the desired size defined by the security policy");
+        return UA_STATUSCODE_BADSECURITYCHECKSFAILED;
+    }
+
+    Basic256Sha256_PolicyContext *pc =
+        (Basic256Sha256_PolicyContext *)securityPolicy->policyContext;
+
+    unsigned char mac[UA_SHA256_LENGTH];
+    md_hmac_Basic256Sha256(&pc->sha256MdContext, &cc->remoteSymSigningKey, message, mac);
+
+    /* Compare with Signature */
+    if(memcmp(signature->data, mac, UA_SHA256_LENGTH) != 0)
+        return UA_STATUSCODE_BADSECURITYCHECKSFAILED;
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode
+sym_sign_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy,
+                           const Basic256Sha256_ChannelContext *cc,
+                           const UA_ByteString *message,
+                           UA_ByteString *signature) {
+    if(signature->length != UA_SHA256_LENGTH)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    md_hmac_Basic256Sha256(&cc->policyContext->sha256MdContext, &cc->localSymSigningKey,
+                           message, signature->data);
+    return UA_STATUSCODE_GOOD;
+}
+
+static size_t
+sym_getSignatureSize_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy,
+                                       const void *channelContext) {
+    return UA_SHA256_LENGTH;
+}
+
+static size_t
+sym_getSigningKeyLength_sp_basic256sha256(const UA_SecurityPolicy *const securityPolicy,
+                                          const void *const channelContext) {
+    return UA_BASIC256SHA256_SYM_SIGNING_KEY_LENGTH;
+}
+
+static size_t
+sym_getEncryptionKeyLength_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy,
+                                             const void *channelContext) {
+    return UA_SECURITYPOLICY_BASIC256SHA256_SYM_KEY_LENGTH;
+}
+
+static size_t
+sym_getEncryptionBlockSize_sp_basic256sha256(const UA_SecurityPolicy *const securityPolicy,
+                                             const void *const channelContext) {
+    return UA_SECURITYPOLICY_BASIC256SHA256_SYM_ENCRYPTION_BLOCK_SIZE;
+}
+
+static size_t
+sym_getPlainTextBlockSize_sp_basic256sha256(const UA_SecurityPolicy *const securityPolicy,
+                                            const void *const channelContext) {
+    return UA_SECURITYPOLICY_BASIC256SHA256_SYM_PLAIN_TEXT_BLOCK_SIZE;
+}
+
+static UA_StatusCode
+sym_encrypt_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy,
+                              const Basic256Sha256_ChannelContext *cc,
+                              UA_ByteString *data) {
+    if(securityPolicy == NULL || cc == NULL || data == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    if(cc->localSymIv.length !=
+       securityPolicy->symmetricModule.cryptoModule.encryptionAlgorithm.getLocalBlockSize(securityPolicy, cc))
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    size_t plainTextBlockSize =
+        securityPolicy->symmetricModule.cryptoModule.encryptionAlgorithm.getLocalPlainTextBlockSize(securityPolicy, cc);
+
+    if(data->length % plainTextBlockSize != 0) {
+        UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY,
+                     "Length of data to encrypt is not a multiple of the plain text block size."
+                         "Padding might not have been calculated appropriately.");
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
+
+    /* Keylength in bits */
+    unsigned int keylength = (unsigned int)(cc->localSymEncryptingKey.length * 8);
+    mbedtls_aes_context aesContext;
+    int mbedErr = mbedtls_aes_setkey_enc(&aesContext, cc->localSymEncryptingKey.data, keylength);
+    UA_MBEDTLS_ERRORHANDLING_RETURN(UA_STATUSCODE_BADINTERNALERROR);
+
+    UA_ByteString ivCopy;
+    UA_StatusCode retval = UA_ByteString_copy(&cc->localSymIv, &ivCopy);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    mbedErr = mbedtls_aes_crypt_cbc(&aesContext, MBEDTLS_AES_ENCRYPT, data->length,
+                                    ivCopy.data, data->data, data->data);
+    UA_MBEDTLS_ERRORHANDLING(UA_STATUSCODE_BADINTERNALERROR);
+    UA_ByteString_deleteMembers(&ivCopy);
+    return retval;
+}
+
+static UA_StatusCode
+sym_decrypt_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy,
+                              const Basic256Sha256_ChannelContext *cc,
+                              UA_ByteString *data) {
+    if(securityPolicy == NULL || cc == NULL || data == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    size_t encryptionBlockSize =
+        securityPolicy->symmetricModule.cryptoModule.encryptionAlgorithm.getLocalBlockSize(securityPolicy, cc);
+
+    if(cc->remoteSymIv.length != encryptionBlockSize)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    if(data->length % encryptionBlockSize != 0) {
+        UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY,
+                     "Length of data to decrypt is not a multiple of the encryptingBlock size.");
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
+
+    unsigned int keylength = (unsigned int)(cc->remoteSymEncryptingKey.length * 8);
+    mbedtls_aes_context aesContext;
+    int mbedErr = mbedtls_aes_setkey_dec(&aesContext, cc->remoteSymEncryptingKey.data, keylength);
+    UA_MBEDTLS_ERRORHANDLING_RETURN(UA_STATUSCODE_BADINTERNALERROR);
+
+    UA_ByteString ivCopy;
+    UA_StatusCode retval = UA_ByteString_copy(&cc->remoteSymIv, &ivCopy);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    mbedErr = mbedtls_aes_crypt_cbc(&aesContext, MBEDTLS_AES_DECRYPT, data->length,
+                                    ivCopy.data, data->data, data->data);
+    UA_MBEDTLS_ERRORHANDLING(UA_STATUSCODE_BADINTERNALERROR);
+    UA_ByteString_deleteMembers(&ivCopy);
+    return retval;
+}
+
+static void
+swapBuffers_Basic256Sha256(UA_ByteString *const bufA, UA_ByteString *const bufB) {
+    UA_ByteString tmp = *bufA;
+    *bufA = *bufB;
+    *bufB = tmp;
+}
+
+static UA_StatusCode
+sym_generateKey_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy,
+                                  const UA_ByteString *secret, const UA_ByteString *seed,
+                                  UA_ByteString *out) {
+    if(securityPolicy == NULL || secret == NULL || seed == NULL || out == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    Basic256Sha256_PolicyContext *pc =
+        (Basic256Sha256_PolicyContext *)securityPolicy->policyContext;
+
+    size_t hashLen = 0;
+    const mbedtls_md_info_t *mdInfo = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
+    hashLen = (size_t)mbedtls_md_get_size(mdInfo);
+
+    UA_ByteString A_and_seed;
+    UA_ByteString_allocBuffer(&A_and_seed, hashLen + seed->length);
+    memcpy(A_and_seed.data + hashLen, seed->data, seed->length);
+
+    UA_ByteString ANext_and_seed;
+    UA_ByteString_allocBuffer(&ANext_and_seed, hashLen + seed->length);
+    memcpy(ANext_and_seed.data + hashLen, seed->data, seed->length);
+
+    UA_ByteString A = {
+        hashLen,
+        A_and_seed.data
+    };
+
+    UA_ByteString ANext = {
+        hashLen,
+        ANext_and_seed.data
+    };
+
+    md_hmac_Basic256Sha256(&pc->sha256MdContext, secret, seed, A.data);
+
+    UA_StatusCode retval = 0;
+    for(size_t offset = 0; offset < out->length; offset += hashLen) {
+        UA_ByteString outSegment = {
+            hashLen,
+            out->data + offset
+        };
+        UA_Boolean bufferAllocated = UA_FALSE;
+        // Not enough room in out buffer to write the hash.
+        if(offset + hashLen > out->length) {
+            outSegment.data = NULL;
+            outSegment.length = 0;
+            retval |= UA_ByteString_allocBuffer(&outSegment, hashLen);
+            if(retval != UA_STATUSCODE_GOOD) {
+                UA_ByteString_deleteMembers(&A_and_seed);
+                UA_ByteString_deleteMembers(&ANext_and_seed);
+                return retval;
+            }
+            bufferAllocated = UA_TRUE;
+        }
+
+        md_hmac_Basic256Sha256(&pc->sha256MdContext, secret, &A_and_seed, outSegment.data);
+        md_hmac_Basic256Sha256(&pc->sha256MdContext, secret, &A, ANext.data);
+
+        if(retval != UA_STATUSCODE_GOOD) {
+            if(bufferAllocated)
+                UA_ByteString_deleteMembers(&outSegment);
+            UA_ByteString_deleteMembers(&A_and_seed);
+            UA_ByteString_deleteMembers(&ANext_and_seed);
+            return retval;
+        }
+
+        if(bufferAllocated) {
+            memcpy(out->data + offset, outSegment.data, out->length - offset);
+            UA_ByteString_deleteMembers(&outSegment);
+        }
+
+        swapBuffers_Basic256Sha256(&ANext_and_seed, &A_and_seed);
+        swapBuffers_Basic256Sha256(&ANext, &A);
+    }
+
+    UA_ByteString_deleteMembers(&A_and_seed);
+    UA_ByteString_deleteMembers(&ANext_and_seed);
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode
+sym_generateNonce_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy,
+                                    UA_ByteString *out) {
+    if(securityPolicy == NULL || securityPolicy->policyContext == NULL || out == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    Basic256Sha256_PolicyContext *data =
+        (Basic256Sha256_PolicyContext *)securityPolicy->policyContext;
+
+    int mbedErr = mbedtls_ctr_drbg_random(&data->drbgContext, out->data, out->length);
+    UA_MBEDTLS_ERRORHANDLING_RETURN(UA_STATUSCODE_BADUNEXPECTEDERROR);
+
+    return UA_STATUSCODE_GOOD;
+}
+
+/*****************/
+/* ChannelModule */
+/*****************/
+
+/* Assumes that the certificate has been verified externally */
+static UA_StatusCode
+parseRemoteCertificate_sp_basic256sha256(Basic256Sha256_ChannelContext *cc,
+                                         const UA_ByteString *remoteCertificate) {
+    if(remoteCertificate == NULL || cc == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    const UA_SecurityPolicy *securityPolicy = cc->policyContext->securityPolicy;
+
+    /* Parse the certificate */
+    int mbedErr = mbedtls_x509_crt_parse(&cc->remoteCertificate, remoteCertificate->data,
+                                         remoteCertificate->length);
+    UA_MBEDTLS_ERRORHANDLING_RETURN(UA_STATUSCODE_BADSECURITYCHECKSFAILED);
+
+    /* Check the key length */
+    mbedtls_rsa_context *rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk);
+    if(rsaContext->len < UA_SECURITYPOLICY_BASIC256SHA256_MINASYMKEYLENGTH ||
+       rsaContext->len > UA_SECURITYPOLICY_BASIC256SHA256_MAXASYMKEYLENGTH)
+        return UA_STATUSCODE_BADCERTIFICATEUSENOTALLOWED;
+
+    return UA_STATUSCODE_GOOD;
+}
+
+static void
+channelContext_deleteContext_sp_basic256sha256(Basic256Sha256_ChannelContext *cc) {
+    UA_ByteString_deleteMembers(&cc->localSymSigningKey);
+    UA_ByteString_deleteMembers(&cc->localSymEncryptingKey);
+    UA_ByteString_deleteMembers(&cc->localSymIv);
+
+    UA_ByteString_deleteMembers(&cc->remoteSymSigningKey);
+    UA_ByteString_deleteMembers(&cc->remoteSymEncryptingKey);
+    UA_ByteString_deleteMembers(&cc->remoteSymIv);
+
+    mbedtls_x509_crt_free(&cc->remoteCertificate);
+
+    UA_free(cc);
+}
+
+static UA_StatusCode
+channelContext_newContext_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy,
+                                            const UA_ByteString *remoteCertificate,
+                                            void **pp_contextData) {
+    if(securityPolicy == NULL || remoteCertificate == NULL || pp_contextData == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    /* Allocate the channel context */
+    *pp_contextData = UA_malloc(sizeof(Basic256Sha256_ChannelContext));
+    if(*pp_contextData == NULL)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+
+    Basic256Sha256_ChannelContext *cc = (Basic256Sha256_ChannelContext *)*pp_contextData;
+
+    /* Initialize the channel context */
+    cc->policyContext = (Basic256Sha256_PolicyContext *)securityPolicy->policyContext;
+
+    UA_ByteString_init(&cc->localSymSigningKey);
+    UA_ByteString_init(&cc->localSymEncryptingKey);
+    UA_ByteString_init(&cc->localSymIv);
+
+    UA_ByteString_init(&cc->remoteSymSigningKey);
+    UA_ByteString_init(&cc->remoteSymEncryptingKey);
+    UA_ByteString_init(&cc->remoteSymIv);
+
+    mbedtls_x509_crt_init(&cc->remoteCertificate);
+
+    // TODO: this can be optimized so that we dont allocate memory before parsing the certificate
+    UA_StatusCode retval = parseRemoteCertificate_sp_basic256sha256(cc, remoteCertificate);
+    if(retval != UA_STATUSCODE_GOOD) {
+        channelContext_deleteContext_sp_basic256sha256(cc);
+        *pp_contextData = NULL;
+    }
+    return retval;
+}
+
+static UA_StatusCode
+channelContext_setLocalSymEncryptingKey_sp_basic256sha256(Basic256Sha256_ChannelContext *cc,
+                                                          const UA_ByteString *key) {
+    if(key == NULL || cc == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    UA_ByteString_deleteMembers(&cc->localSymEncryptingKey);
+    return UA_ByteString_copy(key, &cc->localSymEncryptingKey);
+}
+
+static UA_StatusCode
+channelContext_setLocalSymSigningKey_sp_basic256sha256(Basic256Sha256_ChannelContext *cc,
+                                                       const UA_ByteString *key) {
+    if(key == NULL || cc == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    UA_ByteString_deleteMembers(&cc->localSymSigningKey);
+    return UA_ByteString_copy(key, &cc->localSymSigningKey);
+}
+
+
+static UA_StatusCode
+channelContext_setLocalSymIv_sp_basic256sha256(Basic256Sha256_ChannelContext *cc,
+                                               const UA_ByteString *iv) {
+    if(iv == NULL || cc == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    UA_ByteString_deleteMembers(&cc->localSymIv);
+    return UA_ByteString_copy(iv, &cc->localSymIv);
+}
+
+static UA_StatusCode
+channelContext_setRemoteSymEncryptingKey_sp_basic256sha256(Basic256Sha256_ChannelContext *cc,
+                                                           const UA_ByteString *key) {
+    if(key == NULL || cc == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    UA_ByteString_deleteMembers(&cc->remoteSymEncryptingKey);
+    return UA_ByteString_copy(key, &cc->remoteSymEncryptingKey);
+}
+
+static UA_StatusCode
+channelContext_setRemoteSymSigningKey_sp_basic256sha256(Basic256Sha256_ChannelContext *cc,
+                                                        const UA_ByteString *key) {
+    if(key == NULL || cc == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    UA_ByteString_deleteMembers(&cc->remoteSymSigningKey);
+    return UA_ByteString_copy(key, &cc->remoteSymSigningKey);
+}
+
+static UA_StatusCode
+channelContext_setRemoteSymIv_sp_basic256sha256(Basic256Sha256_ChannelContext *cc,
+                                                const UA_ByteString *iv) {
+    if(iv == NULL || cc == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    UA_ByteString_deleteMembers(&cc->remoteSymIv);
+    return UA_ByteString_copy(iv, &cc->remoteSymIv);
+}
+
+static UA_StatusCode
+channelContext_compareCertificate_sp_basic256sha256(const Basic256Sha256_ChannelContext *cc,
+                                                    const UA_ByteString *certificate) {
+    if(cc == NULL || certificate == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    const UA_SecurityPolicy *securityPolicy = cc->policyContext->securityPolicy;
+
+    mbedtls_x509_crt cert;
+    int mbedErr = mbedtls_x509_crt_parse(&cert, certificate->data, certificate->length);
+    UA_MBEDTLS_ERRORHANDLING_RETURN(UA_STATUSCODE_BADSECURITYCHECKSFAILED);
+
+    if(cert.raw.len != cc->remoteCertificate.raw.len)
+        return UA_STATUSCODE_BADSECURITYCHECKSFAILED;
+
+    if(memcmp(cert.raw.p, cc->remoteCertificate.raw.p, cert.raw.len) != 0)
+        return UA_STATUSCODE_BADSECURITYCHECKSFAILED;
+
+    return UA_STATUSCODE_GOOD;
+}
+
+static void
+deleteMembers_sp_basic256sha256(UA_SecurityPolicy *securityPolicy) {
+    if(securityPolicy == NULL)
+        return;
+
+    if(securityPolicy->policyContext == NULL)
+        return;
+
+    UA_ByteString_deleteMembers(&securityPolicy->localCertificate);
+
+    /* delete all allocated members in the context */
+    Basic256Sha256_PolicyContext *pc = (Basic256Sha256_PolicyContext *)
+        securityPolicy->policyContext;
+
+    mbedtls_ctr_drbg_free(&pc->drbgContext);
+    mbedtls_entropy_free(&pc->entropyContext);
+    mbedtls_pk_free(&pc->localPrivateKey);
+    mbedtls_md_free(&pc->sha256MdContext);
+    UA_ByteString_deleteMembers(&pc->localCertThumbprint);
+
+    UA_LOG_DEBUG(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY,
+                 "Deleted members of EndpointContext for sp_basic256sha256");
+
+    UA_free(pc);
+    securityPolicy->policyContext = NULL;
+}
+
+static UA_StatusCode
+policyContext_newContext_sp_basic256sha256(UA_SecurityPolicy *securityPolicy,
+                                           const UA_ByteString localPrivateKey) {
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    if(securityPolicy == NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    Basic256Sha256_PolicyContext *pc = (Basic256Sha256_PolicyContext *)
+        UA_malloc(sizeof(Basic256Sha256_PolicyContext));
+    securityPolicy->policyContext = (void *)pc;
+    if(!pc) {
+        retval = UA_STATUSCODE_BADOUTOFMEMORY;
+        goto error;
+    }
+
+    /* Initialize the PolicyContext */
+    memset(pc, 0, sizeof(Basic256Sha256_PolicyContext));
+    mbedtls_ctr_drbg_init(&pc->drbgContext);
+    mbedtls_entropy_init(&pc->entropyContext);
+    mbedtls_pk_init(&pc->localPrivateKey);
+    mbedtls_md_init(&pc->sha256MdContext);
+    pc->securityPolicy = securityPolicy;
+
+    /* Initialized the message digest */
+    const mbedtls_md_info_t *const mdInfo = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
+    int mbedErr = mbedtls_md_setup(&pc->sha256MdContext, mdInfo, MBEDTLS_MD_SHA256);
+    UA_MBEDTLS_ERRORHANDLING(UA_STATUSCODE_BADOUTOFMEMORY);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto error;
+
+    /* Add the system entropy source */
+    mbedErr = mbedtls_entropy_add_source(&pc->entropyContext,
+                                         mbedtls_platform_entropy_poll, NULL, 0,
+                                         MBEDTLS_ENTROPY_SOURCE_STRONG);
+    UA_MBEDTLS_ERRORHANDLING(UA_STATUSCODE_BADSECURITYCHECKSFAILED);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto error;
+
+    /* Seed the RNG */
+    char *personalization = "open62541-drbg";
+    mbedErr = mbedtls_ctr_drbg_seed(&pc->drbgContext, mbedtls_entropy_func,
+                                    &pc->entropyContext,
+                                    (const unsigned char *)personalization, 14);
+    UA_MBEDTLS_ERRORHANDLING(UA_STATUSCODE_BADSECURITYCHECKSFAILED);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto error;
+
+    /* Set the private key */
+    mbedErr = mbedtls_pk_parse_key(&pc->localPrivateKey,
+                                   localPrivateKey.data, localPrivateKey.length,
+                                   NULL, 0);
+    UA_MBEDTLS_ERRORHANDLING(UA_STATUSCODE_BADSECURITYCHECKSFAILED);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto error;
+
+    /* Set the local certificate thumbprint */
+    retval = UA_ByteString_allocBuffer(&pc->localCertThumbprint, UA_SHA1_LENGTH);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto error;
+    retval = asym_makeThumbprint_sp_basic256sha256(pc->securityPolicy,
+                                                  &securityPolicy->localCertificate,
+                                                  &pc->localCertThumbprint);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto error;
+
+    return UA_STATUSCODE_GOOD;
+
+error:
+    UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY,
+                 "Could not create securityContext");
+    if(securityPolicy->policyContext != NULL)
+        deleteMembers_sp_basic256sha256(securityPolicy);
+    return retval;
+}
+
+UA_StatusCode
+UA_SecurityPolicy_Basic256Sha256(UA_SecurityPolicy *policy, UA_CertificateVerification *certificateVerification,
+                                 const UA_ByteString localCertificate, const UA_ByteString localPrivateKey,
+                                 UA_Logger logger) {
+    memset(policy, 0, sizeof(UA_SecurityPolicy));
+    policy->logger = logger;
+
+    policy->policyUri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256");
+
+    UA_SecurityPolicyAsymmetricModule *const asymmetricModule = &policy->asymmetricModule;
+    UA_SecurityPolicySymmetricModule *const symmetricModule = &policy->symmetricModule;
+    UA_SecurityPolicyChannelModule *const channelModule = &policy->channelModule;
+
+    /* Copy the certificate and add a NULL to the end */
+    UA_StatusCode retval =
+        UA_ByteString_allocBuffer(&policy->localCertificate, localCertificate.length + 1);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+    memcpy(policy->localCertificate.data, localCertificate.data, localCertificate.length);
+    policy->localCertificate.data[localCertificate.length] = '\0';
+    policy->localCertificate.length--;
+    policy->certificateVerification = certificateVerification;
+
+    /* AsymmetricModule */
+    UA_SecurityPolicySignatureAlgorithm *asym_signatureAlgorithm =
+        &asymmetricModule->cryptoModule.signatureAlgorithm;
+    asym_signatureAlgorithm->uri =
+        UA_STRING("http://www.w3.org/2000/09/xmldsig#rsa-sha1\0");
+    asym_signatureAlgorithm->verify =
+        (UA_StatusCode (*)(const UA_SecurityPolicy *, void *,
+                           const UA_ByteString *, const UA_ByteString *))asym_verify_sp_basic256sha256;
+    asym_signatureAlgorithm->sign =
+        (UA_StatusCode (*)(const UA_SecurityPolicy *, void *,
+                           const UA_ByteString *, UA_ByteString *))asym_sign_sp_basic256sha256;
+    asym_signatureAlgorithm->getLocalSignatureSize =
+        (size_t (*)(const UA_SecurityPolicy *, const void *))asym_getLocalSignatureSize_sp_basic256sha256;
+    asym_signatureAlgorithm->getRemoteSignatureSize =
+        (size_t (*)(const UA_SecurityPolicy *, const void *))asym_getRemoteSignatureSize_sp_basic256sha256;
+    asym_signatureAlgorithm->getLocalKeyLength = NULL; // TODO: Write function
+    asym_signatureAlgorithm->getRemoteKeyLength = NULL; // TODO: Write function
+
+    UA_SecurityPolicyEncryptionAlgorithm *asym_encryptionAlgorithm =
+        &asymmetricModule->cryptoModule.encryptionAlgorithm;
+    asym_encryptionAlgorithm->uri = UA_STRING("TODO: ALG URI");
+    asym_encryptionAlgorithm->encrypt =
+        (UA_StatusCode(*)(const UA_SecurityPolicy *, void *, UA_ByteString *))asym_encrypt_sp_basic256sha256;
+    asym_encryptionAlgorithm->decrypt =
+        (UA_StatusCode(*)(const UA_SecurityPolicy *, void *, UA_ByteString *))
+            asym_decrypt_sp_basic256sha256;
+    asym_encryptionAlgorithm->getLocalKeyLength = NULL; // TODO: Write function
+    asym_encryptionAlgorithm->getRemoteKeyLength =
+        (size_t (*)(const UA_SecurityPolicy *, const void *))asym_getRemoteEncryptionKeyLength_sp_basic256sha256;
+    asym_encryptionAlgorithm->getLocalBlockSize = NULL; // TODO: Write function
+    asym_encryptionAlgorithm->getRemoteBlockSize = (size_t (*)(const UA_SecurityPolicy *,
+                                                               const void *))asym_getRemoteBlockSize_sp_basic256sha256;
+    asym_encryptionAlgorithm->getLocalPlainTextBlockSize = NULL; // TODO: Write function
+    asym_encryptionAlgorithm->getRemotePlainTextBlockSize =
+        (size_t (*)(const UA_SecurityPolicy *, const void *))asym_getRemotePlainTextBlockSize_sp_basic256sha256;
+
+    asymmetricModule->makeCertificateThumbprint = asym_makeThumbprint_sp_basic256sha256;
+    asymmetricModule->compareCertificateThumbprint =
+        asymmetricModule_compareCertificateThumbprint_sp_basic256sha256;
+
+    /* SymmetricModule */
+    symmetricModule->generateKey = sym_generateKey_sp_basic256sha256;
+    symmetricModule->generateNonce = sym_generateNonce_sp_basic256sha256;
+
+    UA_SecurityPolicySignatureAlgorithm *sym_signatureAlgorithm =
+        &symmetricModule->cryptoModule.signatureAlgorithm;
+    sym_signatureAlgorithm->uri =
+        UA_STRING("http://www.w3.org/2000/09/xmldsig#hmac-sha1\0");
+    sym_signatureAlgorithm->verify =
+        (UA_StatusCode (*)(const UA_SecurityPolicy *, void *, const UA_ByteString *,
+                           const UA_ByteString *))sym_verify_sp_basic256sha256;
+    sym_signatureAlgorithm->sign =
+        (UA_StatusCode (*)(const UA_SecurityPolicy *, void *,
+                           const UA_ByteString *, UA_ByteString *))sym_sign_sp_basic256sha256;
+    sym_signatureAlgorithm->getLocalSignatureSize = sym_getSignatureSize_sp_basic256sha256;
+    sym_signatureAlgorithm->getRemoteSignatureSize = sym_getSignatureSize_sp_basic256sha256;
+    sym_signatureAlgorithm->getLocalKeyLength =
+        (size_t (*)(const UA_SecurityPolicy *,
+                    const void *))sym_getSigningKeyLength_sp_basic256sha256;
+    sym_signatureAlgorithm->getRemoteKeyLength =
+        (size_t (*)(const UA_SecurityPolicy *,
+                    const void *))sym_getSigningKeyLength_sp_basic256sha256;
+
+    UA_SecurityPolicyEncryptionAlgorithm *sym_encryptionAlgorithm =
+        &symmetricModule->cryptoModule.encryptionAlgorithm;
+    sym_encryptionAlgorithm->uri = UA_STRING("http://www.w3.org/2001/04/xmlenc#aes128-cbc");
+    sym_encryptionAlgorithm->encrypt =
+        (UA_StatusCode(*)(const UA_SecurityPolicy *, void *, UA_ByteString *))sym_encrypt_sp_basic256sha256;
+    sym_encryptionAlgorithm->decrypt =
+        (UA_StatusCode(*)(const UA_SecurityPolicy *, void *, UA_ByteString *))sym_decrypt_sp_basic256sha256;
+    sym_encryptionAlgorithm->getLocalKeyLength = sym_getEncryptionKeyLength_sp_basic256sha256;
+    sym_encryptionAlgorithm->getRemoteKeyLength = sym_getEncryptionKeyLength_sp_basic256sha256;
+    sym_encryptionAlgorithm->getLocalBlockSize =
+        (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getEncryptionBlockSize_sp_basic256sha256;
+    sym_encryptionAlgorithm->getRemoteBlockSize =
+        (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getEncryptionBlockSize_sp_basic256sha256;
+    sym_encryptionAlgorithm->getLocalPlainTextBlockSize =
+        (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getPlainTextBlockSize_sp_basic256sha256;
+    sym_encryptionAlgorithm->getRemotePlainTextBlockSize =
+        (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getPlainTextBlockSize_sp_basic256sha256;
+    symmetricModule->secureChannelNonceLength = 32;
+
+    // Use the same signature algorithm as the asymmetric component for certificate signing (see standard)
+    policy->certificateSigningAlgorithm = policy->asymmetricModule.cryptoModule.signatureAlgorithm;
+
+    /* ChannelModule */
+    channelModule->newContext = channelContext_newContext_sp_basic256sha256;
+    channelModule->deleteContext = (void (*)(void *))
+        channelContext_deleteContext_sp_basic256sha256;
+
+    channelModule->setLocalSymEncryptingKey = (UA_StatusCode (*)(void *, const UA_ByteString *))
+        channelContext_setLocalSymEncryptingKey_sp_basic256sha256;
+    channelModule->setLocalSymSigningKey = (UA_StatusCode (*)(void *, const UA_ByteString *))
+        channelContext_setLocalSymSigningKey_sp_basic256sha256;
+    channelModule->setLocalSymIv = (UA_StatusCode (*)(void *, const UA_ByteString *))
+        channelContext_setLocalSymIv_sp_basic256sha256;
+
+    channelModule->setRemoteSymEncryptingKey = (UA_StatusCode (*)(void *, const UA_ByteString *))
+        channelContext_setRemoteSymEncryptingKey_sp_basic256sha256;
+    channelModule->setRemoteSymSigningKey = (UA_StatusCode (*)(void *, const UA_ByteString *))
+        channelContext_setRemoteSymSigningKey_sp_basic256sha256;
+    channelModule->setRemoteSymIv = (UA_StatusCode (*)(void *, const UA_ByteString *))
+        channelContext_setRemoteSymIv_sp_basic256sha256;
+
+    channelModule->compareCertificate = (UA_StatusCode (*)(const void *, const UA_ByteString *))
+        channelContext_compareCertificate_sp_basic256sha256;
+
+    policy->deleteMembers = deleteMembers_sp_basic256sha256;
+
+    return policyContext_newContext_sp_basic256sha256(policy, localPrivateKey);
+}

+ 30 - 0
plugins/ua_securitypolicy_basic256sha256.h

@@ -0,0 +1,30 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ *    Copyright 2018 (c) Mark Giraud, Fraunhofer IOSB
+ *    Copyright 2018 (c) Daniel Feist, Precitec GmbH & Co. KG
+ */
+
+#ifndef UA_SECURITYPOLICY_BASIC256SHA256_H_
+#define UA_SECURITYPOLICY_BASIC256SHA256_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ua_plugin_securitypolicy.h"
+#include "ua_plugin_log.h"
+
+UA_EXPORT UA_StatusCode
+UA_SecurityPolicy_Basic256Sha256(UA_SecurityPolicy *policy,
+                                 UA_CertificateVerification *certificateVerification,
+                                 const UA_ByteString localCertificate,
+                                 const UA_ByteString localPrivateKey,
+                                 UA_Logger logger);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // UA_SECURITYPOLICY_BASIC256SHA256_H_

+ 57 - 51
plugins/ua_securitypolicy_none.c

@@ -1,5 +1,9 @@
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
- * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. 
+ *
+ *    Copyright 2017-2018 (c) Mark Giraud, Fraunhofer IOSB
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ */
 
 #include "ua_types.h"
 #include "ua_securitypolicy_none.h"
@@ -7,7 +11,7 @@
 
 static UA_StatusCode
 verify_none(const UA_SecurityPolicy *securityPolicy,
-            const void *channelContext,
+            void *channelContext,
             const UA_ByteString *message,
             const UA_ByteString *signature) {
     return UA_STATUSCODE_GOOD;
@@ -15,7 +19,7 @@ verify_none(const UA_SecurityPolicy *securityPolicy,
 
 static UA_StatusCode
 sign_none(const UA_SecurityPolicy *securityPolicy,
-          const void *channelContext,
+          void *channelContext,
           const UA_ByteString *message,
           UA_ByteString *signature) {
     return UA_STATUSCODE_GOOD;
@@ -29,14 +33,14 @@ length_none(const UA_SecurityPolicy *securityPolicy,
 
 static UA_StatusCode
 encrypt_none(const UA_SecurityPolicy *securityPolicy,
-             const void *channelContext,
+             void *channelContext,
              UA_ByteString *data) {
     return UA_STATUSCODE_GOOD;
 }
 
 static UA_StatusCode
 decrypt_none(const UA_SecurityPolicy *securityPolicy,
-             const void *channelContext,
+             void *channelContext,
              UA_ByteString *data) {
     return UA_STATUSCODE_GOOD;
 }
@@ -62,18 +66,24 @@ generateKey_none(const UA_SecurityPolicy *securityPolicy,
     return UA_STATUSCODE_GOOD;
 }
 
+/* Use the non-cryptographic RNG to set the nonce */
 static UA_StatusCode
-generateNonce_none(const UA_SecurityPolicy *securityPolicy,
-                   UA_ByteString *out) {
+generateNonce_none(const UA_SecurityPolicy *securityPolicy, UA_ByteString *out) {
     if(securityPolicy == NULL || out == NULL)
         return UA_STATUSCODE_BADINTERNALERROR;
-    if(out->length != 0)
-        return UA_STATUSCODE_BADINTERNALERROR;
 
-    if(out->data != NULL)
-        UA_ByteString_deleteMembers(out);
+    /* Fill blocks of four byte */
+    size_t i = 0;
+    while(i + 3 < out->length) {
+        UA_UInt32 rand = UA_UInt32_random();
+        memcpy(&out->data[i], &rand, 4);
+        i = i+4;
+    }
+
+    /* Fill the remaining byte */
+    UA_UInt32 rand = UA_UInt32_random();
+    memcpy(&out->data[i], &rand, out->length % 4);
 
-    out->data = (UA_Byte *) UA_EMPTY_ARRAY_SENTINEL;
     return UA_STATUSCODE_GOOD;
 }
 
@@ -94,17 +104,6 @@ setContextValue_none(void *channelContext,
     return UA_STATUSCODE_GOOD;
 }
 
-static size_t
-getRemoteAsymPlainTextBlockSize_none(const void *channelContext) {
-    return 0;
-}
-
-static size_t
-getRemoteAsymEncryptionBufferLengthOverhead_none(const void *channelContext,
-                                                 size_t maxEncryptionLength) {
-    return 0;
-}
-
 static UA_StatusCode
 compareCertificate_none(const void *channelContext,
                         const UA_ByteString *certificate) {
@@ -117,38 +116,48 @@ policy_deletemembers_none(UA_SecurityPolicy *policy) {
 }
 
 UA_StatusCode
-UA_SecurityPolicy_None(UA_SecurityPolicy *policy, const UA_ByteString localCertificate,
-                       UA_Logger logger) {
-    policy->policyContext = (void *) (uintptr_t) logger;
+UA_SecurityPolicy_None(UA_SecurityPolicy *policy, UA_CertificateVerification *certificateVerification,
+                       const UA_ByteString localCertificate, UA_Logger logger) {
+    policy->policyContext = (void *)(uintptr_t)logger;
     policy->policyUri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None");
     policy->logger = logger;
     UA_ByteString_copy(&localCertificate, &policy->localCertificate);
 
-    policy->asymmetricModule.makeCertificateThumbprint = makeThumbprint_none;
-    policy->asymmetricModule.compareCertificateThumbprint = compareThumbprint_none;
-    policy->asymmetricModule.cryptoModule.signatureAlgorithmUri = UA_STRING_NULL;
-    policy->asymmetricModule.cryptoModule.verify = verify_none;
-    policy->asymmetricModule.cryptoModule.sign = sign_none;
-    policy->asymmetricModule.cryptoModule.getLocalSignatureSize = length_none;
-    policy->asymmetricModule.cryptoModule.getRemoteSignatureSize = length_none;
-    policy->asymmetricModule.cryptoModule.encrypt = encrypt_none;
-    policy->asymmetricModule.cryptoModule.decrypt = decrypt_none;
-    policy->asymmetricModule.cryptoModule.getLocalEncryptionKeyLength = length_none;
-    policy->asymmetricModule.cryptoModule.getRemoteEncryptionKeyLength = length_none;
+    policy->certificateVerification = certificateVerification;
 
     policy->symmetricModule.generateKey = generateKey_none;
     policy->symmetricModule.generateNonce = generateNonce_none;
-    policy->symmetricModule.cryptoModule.signatureAlgorithmUri = UA_STRING_NULL;
-    policy->symmetricModule.cryptoModule.verify = verify_none;
-    policy->symmetricModule.cryptoModule.sign = sign_none;
-    policy->symmetricModule.cryptoModule.getLocalSignatureSize = length_none;
-    policy->symmetricModule.cryptoModule.getRemoteSignatureSize = length_none;
-    policy->symmetricModule.cryptoModule.encrypt = encrypt_none;
-    policy->symmetricModule.cryptoModule.decrypt = decrypt_none;
-    policy->symmetricModule.cryptoModule.getLocalEncryptionKeyLength = length_none;
-    policy->symmetricModule.cryptoModule.getRemoteEncryptionKeyLength = length_none;
-    policy->symmetricModule.encryptionBlockSize = 0;
-    policy->symmetricModule.signingKeyLength = 0;
+
+    UA_SecurityPolicySignatureAlgorithm *sym_signatureAlgorithm =
+        &policy->symmetricModule.cryptoModule.signatureAlgorithm;
+    sym_signatureAlgorithm->uri = UA_STRING_NULL;
+    sym_signatureAlgorithm->verify = verify_none;
+    sym_signatureAlgorithm->sign = sign_none;
+    sym_signatureAlgorithm->getLocalSignatureSize = length_none;
+    sym_signatureAlgorithm->getRemoteSignatureSize = length_none;
+    sym_signatureAlgorithm->getLocalKeyLength = length_none;
+    sym_signatureAlgorithm->getRemoteKeyLength = length_none;
+
+    UA_SecurityPolicyEncryptionAlgorithm *sym_encryptionAlgorithm =
+        &policy->symmetricModule.cryptoModule.encryptionAlgorithm;
+    sym_encryptionAlgorithm->encrypt = encrypt_none;
+    sym_encryptionAlgorithm->decrypt = decrypt_none;
+    sym_encryptionAlgorithm->getLocalKeyLength = length_none;
+    sym_encryptionAlgorithm->getRemoteKeyLength = length_none;
+    sym_encryptionAlgorithm->getLocalBlockSize = length_none;
+    sym_encryptionAlgorithm->getRemoteBlockSize = length_none;
+    sym_encryptionAlgorithm->getLocalPlainTextBlockSize = length_none;
+    sym_encryptionAlgorithm->getRemotePlainTextBlockSize = length_none;
+    policy->symmetricModule.secureChannelNonceLength = 0;
+
+    policy->asymmetricModule.makeCertificateThumbprint = makeThumbprint_none;
+    policy->asymmetricModule.compareCertificateThumbprint = compareThumbprint_none;
+
+    // This only works for none since symmetric and asymmetric crypto modules do the same i.e. nothing
+    policy->asymmetricModule.cryptoModule = policy->symmetricModule.cryptoModule;
+
+    // Use the same signing algorithm as for asymmetric signing
+    policy->certificateSigningAlgorithm = policy->asymmetricModule.cryptoModule.signatureAlgorithm;
 
     policy->channelModule.newContext = newContext_none;
     policy->channelModule.deleteContext = deleteContext_none;
@@ -159,9 +168,6 @@ UA_SecurityPolicy_None(UA_SecurityPolicy *policy, const UA_ByteString localCerti
     policy->channelModule.setRemoteSymSigningKey = setContextValue_none;
     policy->channelModule.setRemoteSymIv = setContextValue_none;
     policy->channelModule.compareCertificate = compareCertificate_none;
-    policy->channelModule.getRemoteAsymPlainTextBlockSize = getRemoteAsymPlainTextBlockSize_none;
-    policy->channelModule.getRemoteAsymEncryptionBufferLengthOverhead =
-        getRemoteAsymEncryptionBufferLengthOverhead_none;
     policy->deleteMembers = policy_deletemembers_none;
 
     return UA_STATUSCODE_GOOD;

+ 7 - 3
plugins/ua_securitypolicy_none.h

@@ -1,5 +1,9 @@
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
- * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. 
+ *
+ *    Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ */
 
 #ifndef UA_SECURITYPOLICY_NONE_H_
 #define UA_SECURITYPOLICY_NONE_H_
@@ -12,8 +16,8 @@ extern "C" {
 #include "ua_plugin_log.h"
 
 UA_StatusCode UA_EXPORT
-UA_SecurityPolicy_None(UA_SecurityPolicy *policy, const UA_ByteString localCertificate,
-                       UA_Logger logger);
+UA_SecurityPolicy_None(UA_SecurityPolicy *policy, UA_CertificateVerification *certificateVerification,
+                       const UA_ByteString localCertificate, UA_Logger logger);
 
 #ifdef __cplusplus
 }

+ 138 - 65
src/client/ua_client.c

@@ -1,6 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2015-2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2015-2016 (c) Sten Grüner
+ *    Copyright 2015-2016 (c) Chris Iatrou
+ *    Copyright 2015 (c) hfaham
+ *    Copyright 2015-2017 (c) Florian Palm
+ *    Copyright 2017-2018 (c) Thomas Stalder, Blue Time Concept SA
+ *    Copyright 2015 (c) Holger Jeromin
+ *    Copyright 2015 (c) Oleksiy Vasylyev
+ *    Copyright 2016 (c) TorbenD
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ *    Copyright 2016 (c) Lykurg
+ *    Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB
+ */
 
 #include "ua_client.h"
 #include "ua_client_internal.h"
@@ -10,18 +24,20 @@
 #include "ua_util.h"
 #include "ua_securitypolicy_none.h"
 
- /********************/
- /* Client Lifecycle */
- /********************/
+/********************/
+/* Client Lifecycle */
+/********************/
 
 static void
 UA_Client_init(UA_Client* client, UA_ClientConfig config) {
     memset(client, 0, sizeof(UA_Client));
     /* TODO: Select policy according to the endpoint */
-    UA_SecurityPolicy_None(&client->securityPolicy, UA_BYTESTRING_NULL, config.logger);
+    UA_SecurityPolicy_None(&client->securityPolicy, NULL, UA_BYTESTRING_NULL, config.logger);
     client->channel.securityPolicy = &client->securityPolicy;
     client->channel.securityMode = UA_MESSAGESECURITYMODE_NONE;
     client->config = config;
+    if(client->config.stateCallback)
+        client->config.stateCallback(client, client->state);
 }
 
 UA_Client *
@@ -49,22 +65,11 @@ UA_Client_deleteMembers(UA_Client* client) {
         UA_String_deleteMembers(&client->password);
 
     /* Delete the async service calls */
-    AsyncServiceCall *ac, *ac_tmp;
-    LIST_FOREACH_SAFE(ac, &client->asyncServiceCalls, pointers, ac_tmp) {
-        LIST_REMOVE(ac, pointers);
-        UA_free(ac);
-    }
+    UA_Client_AsyncService_removeAll(client, UA_STATUSCODE_BADSHUTDOWN);
 
     /* Delete the subscriptions */
 #ifdef UA_ENABLE_SUBSCRIPTIONS
-    UA_Client_NotificationsAckNumber *n, *tmp;
-    LIST_FOREACH_SAFE(n, &client->pendingNotificationsAcks, listEntry, tmp) {
-        LIST_REMOVE(n, listEntry);
-        UA_free(n);
-    }
-    UA_Client_Subscription *sub, *tmps;
-    LIST_FOREACH_SAFE(sub, &client->subscriptions, listEntry, tmps)
-        UA_Client_Subscriptions_forceDelete(client, sub); /* force local removal */
+    UA_Client_Subscriptions_clean(client);
 #endif
 }
 
@@ -85,6 +90,13 @@ UA_Client_getState(UA_Client *client) {
     return client->state;
 }
 
+void *
+UA_Client_getContext(UA_Client *client) {
+    if(!client)
+        return NULL;
+    return client->config.clientContext;
+}
+
 /****************/
 /* Raw Services */
 /****************/
@@ -130,9 +142,12 @@ sendSymmetricServiceRequest(UA_Client *client, const void *request,
     return UA_STATUSCODE_GOOD;
 }
 
+static const UA_NodeId
+serviceFaultId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_SERVICEFAULT_ENCODING_DEFAULTBINARY}};
+
 /* Look for the async callback in the linked list, execute and delete it */
 static UA_StatusCode
-processAsyncResponse(UA_Client *client, UA_UInt32 requestId, UA_NodeId *responseTypeId,
+processAsyncResponse(UA_Client *client, UA_UInt32 requestId, const UA_NodeId *responseTypeId,
                      const UA_ByteString *responseMessage, size_t *offset) {
     /* Find the callback */
     AsyncServiceCall *ac;
@@ -143,20 +158,46 @@ processAsyncResponse(UA_Client *client, UA_UInt32 requestId, UA_NodeId *response
     if(!ac)
         return UA_STATUSCODE_BADREQUESTHEADERINVALID;
 
+    /* Allocate the response */
+    UA_STACKARRAY(UA_Byte, responseBuf, ac->responseType->memSize);
+    void *response = (void*)(uintptr_t)&responseBuf[0]; /* workaround aliasing rules */
+
+    /* Verify the type of the response */
+    const UA_DataType *responseType = ac->responseType;
+    const UA_NodeId expectedNodeId = UA_NODEID_NUMERIC(0, ac->responseType->binaryEncodingId);
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    if(!UA_NodeId_equal(responseTypeId, &expectedNodeId)) {
+        UA_init(response, ac->responseType);
+        if(UA_NodeId_equal(responseTypeId, &serviceFaultId)) {
+            /* Decode as a ServiceFault, i.e. only the response header */
+            UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                         "Received a ServiceFault response");
+            responseType = &UA_TYPES[UA_TYPES_SERVICEFAULT];
+        } else {
+            /* Close the connection */
+            UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                         "Reply contains the wrong service response");
+            retval = UA_STATUSCODE_BADCOMMUNICATIONERROR;
+            goto process;
+        }
+    }
+
     /* Decode the response */
-    void *response = UA_alloca(ac->responseType->memSize);
-    UA_StatusCode retval = UA_decodeBinary(responseMessage, offset, response,
-                                           ac->responseType, 0, NULL);
+    retval = UA_decodeBinary(responseMessage, offset, response,
+                             responseType, 0, NULL);
 
-    /* Call the callback */
-    if(retval == UA_STATUSCODE_GOOD) {
-        ac->callback(client, ac->userdata, requestId, response);
-        UA_deleteMembers(response, ac->responseType);
-    } else {
+ process:
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_CLIENT,
-                    "Could not decodee the response with Id %u", requestId);
+                    "Could not decode the response with id %u due to %s",
+                    requestId, UA_StatusCode_name(retval));
+        ((UA_ResponseHeader*)response)->serviceResult = retval;
     }
 
+    /* Call the callback */
+    ac->callback(client, ac->userdata, requestId, response, ac->responseType);
+    UA_deleteMembers(response, ac->responseType);
+
     /* Remove the callback */
     LIST_REMOVE(ac, pointers);
     UA_free(ac);
@@ -184,15 +225,10 @@ processServiceResponse(void *application, UA_SecureChannel *channel,
      * TODO: Solve this for client and server together */
     if(rd->client->state >= UA_CLIENTSTATE_SECURECHANNEL &&
        (channel->securityToken.createdAt +
-        (channel->securityToken.revisedLifetime * UA_MSEC_TO_DATETIME))
+        (channel->securityToken.revisedLifetime * UA_DATETIME_MSEC))
        < UA_DateTime_nowMonotonic())
         return UA_STATUSCODE_BADSECURECHANNELCLOSED;
 
-    /* Forward declaration for the goto */
-    UA_NodeId expectedNodeId;
-    const UA_NodeId serviceFaultNodeId =
-        UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_SERVICEFAULT].binaryEncodingId);
-
     /* Decode the data type identifier of the response */
     size_t offset = 0;
     UA_NodeId responseId;
@@ -210,32 +246,37 @@ processServiceResponse(void *application, UA_SecureChannel *channel,
     /* Got the synchronous response */
     rd->received = true;
 
+    /* Forward declaration for the goto */
+    UA_NodeId expectedNodeId = UA_NODEID_NUMERIC(0, rd->responseType->binaryEncodingId);
+
     /* Check that the response type matches */
-    expectedNodeId = UA_NODEID_NUMERIC(0, rd->responseType->binaryEncodingId);
-    if(UA_NodeId_equal(&responseId, &expectedNodeId)) {
-        /* Decode the response */
-        retval = UA_decodeBinary(message, &offset, rd->response, rd->responseType,
-                                 rd->client->config.customDataTypesSize,
-                                 rd->client->config.customDataTypes);
-    } else {
-        UA_LOG_ERROR(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
-                     "Reply contains the wrong service response");
-        if(UA_NodeId_equal(&responseId, &serviceFaultNodeId)) {
-            /* Decode only the message header with the servicefault */
+    if(!UA_NodeId_equal(&responseId, &expectedNodeId)) {
+        if(UA_NodeId_equal(&responseId, &serviceFaultId)) {
+            UA_LOG_INFO(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
+                         "Received a ServiceFault response");
+            UA_init(rd->response, rd->responseType);
             retval = UA_decodeBinary(message, &offset, rd->response,
                                      &UA_TYPES[UA_TYPES_SERVICEFAULT], 0, NULL);
         } else {
             /* Close the connection */
+            UA_LOG_ERROR(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
+                         "Reply contains the wrong service response");
             retval = UA_STATUSCODE_BADCOMMUNICATIONERROR;
         }
+        goto finish;
     }
 
+    UA_LOG_DEBUG(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
+                 "Decode a message of type %u", responseId.identifier.numeric);
+
+    /* Decode the response */
+    retval = UA_decodeBinary(message, &offset, rd->response, rd->responseType,
+                             rd->client->config.customDataTypesSize,
+                             rd->client->config.customDataTypes);
 
 finish:
-    if(retval == UA_STATUSCODE_GOOD) {
-        UA_LOG_DEBUG(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
-                     "Received a response of type %i", responseId.identifier.numeric);
-    } else {
+    UA_NodeId_deleteMembers(&responseId);
+    if(retval != UA_STATUSCODE_GOOD) {
         if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED)
             retval = UA_STATUSCODE_BADRESPONSETOOLARGE;
         UA_LOG_INFO(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
@@ -247,8 +288,6 @@ finish:
             respHeader->serviceResult = retval;
         }
     }
-    UA_NodeId_deleteMembers(&responseId);
-
     return retval;
 }
 
@@ -283,17 +322,15 @@ receiveServiceResponse(UA_Client *client, void *response, const UA_DataType *res
             return UA_STATUSCODE_GOODNONCRITICALTIMEOUT;
 
         /* round always to upper value to avoid timeout to be set to 0
-         * if (maxDate - now) < (UA_MSEC_TO_DATETIME/2) */
-        UA_UInt32 timeout = (UA_UInt32)(((maxDate - now) + (UA_MSEC_TO_DATETIME - 1)) / UA_MSEC_TO_DATETIME);
+         * if(maxDate - now) < (UA_DATETIME_MSEC/2) */
+        UA_UInt32 timeout = (UA_UInt32)(((maxDate - now) + (UA_DATETIME_MSEC - 1)) / UA_DATETIME_MSEC);
 
-        retval = UA_Connection_receiveChunksBlocking(&client->connection, &rd,
-                                                     client_processChunk, timeout);
+        retval = UA_Connection_receiveChunksBlocking(&client->connection, &rd, client_processChunk, timeout);
 
         if(retval != UA_STATUSCODE_GOOD && retval != UA_STATUSCODE_GOODNONCRITICALTIMEOUT) {
             if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED)
-                client->state = UA_CLIENTSTATE_DISCONNECTED;
-            else
-                UA_Client_disconnect(client);
+                setClientState(client, UA_CLIENTSTATE_DISCONNECTED);
+            UA_Client_close(client);
             break;
         }
     } while(!rd.received);
@@ -315,23 +352,47 @@ __UA_Client_Service(UA_Client *client, const void *request,
             respHeader->serviceResult = UA_STATUSCODE_BADREQUESTTOOLARGE;
         else
             respHeader->serviceResult = retval;
-        UA_Client_disconnect(client);
+        UA_Client_close(client);
         return;
     }
 
     /* Retrieve the response */
     UA_DateTime maxDate = UA_DateTime_nowMonotonic() +
-        (client->config.timeout * UA_MSEC_TO_DATETIME);
+        (client->config.timeout * UA_DATETIME_MSEC);
     retval = receiveServiceResponse(client, response, responseType, maxDate, &requestId);
-    if (retval == UA_STATUSCODE_GOODNONCRITICALTIMEOUT){
+    if(retval == UA_STATUSCODE_GOODNONCRITICALTIMEOUT) {
         /* In synchronous service, if we have don't have a reply we need to close the connection */
-        UA_Client_disconnect(client);
+        UA_Client_close(client);
         retval = UA_STATUSCODE_BADCONNECTIONCLOSED;
     }
     if(retval != UA_STATUSCODE_GOOD)
         respHeader->serviceResult = retval;
 }
 
+void
+UA_Client_AsyncService_cancel(UA_Client *client, AsyncServiceCall *ac,
+                              UA_StatusCode statusCode) {
+    /* Create an empty response with the statuscode */
+    UA_STACKARRAY(UA_Byte, responseBuf, ac->responseType->memSize);
+    void *resp = (void*)(uintptr_t)&responseBuf[0]; /* workaround aliasing rules */
+    UA_init(resp, ac->responseType);
+    ((UA_ResponseHeader*)resp)->serviceResult = statusCode;
+
+    ac->callback(client, ac->userdata, ac->requestId, resp, ac->responseType);
+
+    /* Clean up the response. Users might move data into it. For whatever reasons. */
+    UA_deleteMembers(resp, ac->responseType);
+}
+
+void UA_Client_AsyncService_removeAll(UA_Client *client, UA_StatusCode statusCode) {
+    AsyncServiceCall *ac, *ac_tmp;
+    LIST_FOREACH_SAFE(ac, &client->asyncServiceCalls, pointers, ac_tmp) {
+        LIST_REMOVE(ac, pointers);
+        UA_Client_AsyncService_cancel(client, ac, statusCode);
+        UA_free(ac);
+    }
+}
+
 UA_StatusCode
 __UA_Client_AsyncService(UA_Client *client, const void *request,
                          const UA_DataType *requestType,
@@ -363,10 +424,22 @@ __UA_Client_AsyncService(UA_Client *client, const void *request,
 UA_StatusCode
 UA_Client_runAsync(UA_Client *client, UA_UInt16 timeout) {
     /* TODO: Call repeated jobs that are scheduled */
-    UA_DateTime maxDate = UA_DateTime_nowMonotonic() +
-        (timeout * UA_MSEC_TO_DATETIME);
-    UA_StatusCode retval = receiveServiceResponse(client, NULL, NULL, maxDate, NULL);
+#ifdef UA_ENABLE_SUBSCRIPTIONS
+    UA_StatusCode retvalPublish = UA_Client_Subscriptions_backgroundPublish(client);
+    if (retvalPublish != UA_STATUSCODE_GOOD)
+        return retvalPublish;
+#endif
+    UA_StatusCode retval = UA_Client_manuallyRenewSecureChannel(client);
+    if (retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    UA_DateTime maxDate = UA_DateTime_nowMonotonic() + (timeout * UA_DATETIME_MSEC);
+    retval = receiveServiceResponse(client, NULL, NULL, maxDate, NULL);
     if(retval == UA_STATUSCODE_GOODNONCRITICALTIMEOUT)
         retval = UA_STATUSCODE_GOOD;
+#ifdef UA_ENABLE_SUBSCRIPTIONS
+    /* The inactivity check must be done after receiveServiceResponse */
+    UA_Client_Subscriptions_backgroundPublishInactivityCheck(client);
+#endif
     return retval;
 }

+ 120 - 45
src/client/ua_client_connect.c

@@ -1,6 +1,12 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB
+ *    Copyright 2017-2018 (c) Thomas Stalder, Blue Time Concept SA
+ *    Copyright 2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ */
 
 #include "ua_client.h"
 #include "ua_client_internal.h"
@@ -12,9 +18,22 @@
 
 #define UA_MINMESSAGESIZE 8192
 
- /***********************/
- /* Open the Connection */
- /***********************/
+
+ /********************/
+ /* Set client state */
+ /********************/
+void
+setClientState(UA_Client *client, UA_ClientState state) {
+    if(client->state != state) {
+        client->state = state;
+        if(client->config.stateCallback)
+            client->config.stateCallback(client, client->state);
+    }
+}
+
+/***********************/
+/* Open the Connection */
+/***********************/
 
 static UA_StatusCode
 processACKResponse(void *application, UA_Connection *connection, UA_ByteString *chunk) {
@@ -97,9 +116,13 @@ HelAckHandshake(UA_Client *client) {
     /* Loop until we have a complete chunk */
     retval = UA_Connection_receiveChunksBlocking(conn, client, processACKResponse,
                                                  client->config.timeout);
-    if(retval != UA_STATUSCODE_GOOD)
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_NETWORK,
                     "Receiving ACK message failed");
+        if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED)
+            client->state = UA_CLIENTSTATE_DISCONNECTED;
+        UA_Client_close(client);
+    }
     return retval;
 }
 
@@ -108,12 +131,12 @@ processDecodedOPNResponse(UA_Client *client, UA_OpenSecureChannelResponse *respo
     /* Replace the token */
     UA_ChannelSecurityToken_deleteMembers(&client->channel.securityToken);
     client->channel.securityToken = response->securityToken;
-    UA_ChannelSecurityToken_deleteMembers(&response->securityToken);
+    UA_ChannelSecurityToken_init(&response->securityToken);
 
     /* Replace the nonce */
     UA_ByteString_deleteMembers(&client->channel.remoteNonce);
     client->channel.remoteNonce = response->serverNonce;
-    UA_ByteString_deleteMembers(&response->serverNonce);
+    UA_ByteString_init(&response->serverNonce);
 
     if(client->channel.state == UA_SECURECHANNELSTATE_OPEN)
         UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
@@ -127,7 +150,7 @@ processDecodedOPNResponse(UA_Client *client, UA_OpenSecureChannelResponse *respo
      * standard */
     client->channel.state = UA_SECURECHANNELSTATE_OPEN;
     client->nextChannelRenewal = UA_DateTime_nowMonotonic() + (UA_DateTime)
-        (response->securityToken.revisedLifetime * (UA_Double)UA_MSEC_TO_DATETIME * 0.75);
+        (client->channel.securityToken.revisedLifetime * (UA_Double)UA_DATETIME_MSEC * 0.75);
 }
 
 static UA_StatusCode
@@ -166,7 +189,7 @@ openSecureChannel(UA_Client *client, UA_Boolean renew) {
     if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
                      "Sending OPN message failed with error %s", UA_StatusCode_name(retval));
-        UA_Client_disconnect(client);
+        UA_Client_close(client);
         return retval;
     }
 
@@ -178,11 +201,11 @@ openSecureChannel(UA_Client *client, UA_Boolean renew) {
     retval = receiveServiceResponse(client, &response,
                                     &UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE],
                                     UA_DateTime_nowMonotonic() +
-                                    ((UA_DateTime)client->config.timeout * UA_MSEC_TO_DATETIME),
+                                    ((UA_DateTime)client->config.timeout * UA_DATETIME_MSEC),
                                     &requestId);
-                                    
+
     if(retval != UA_STATUSCODE_GOOD) {
-        UA_Client_disconnect(client);
+        UA_Client_close(client);
         return retval;
     }
 
@@ -222,7 +245,7 @@ activateSession(UA_Client *client) {
     __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST],
                         &response, &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE]);
 
-    if(response.responseHeader.serviceResult) {
+    if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
         UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
                      "ActivateSession failed with error code %s",
                      UA_StatusCode_name(response.responseHeader.serviceResult));
@@ -246,7 +269,6 @@ UA_Client_getEndpointsInternal(UA_Client *client, size_t* endpointDescriptionsSi
     request.endpointUrl = client->endpointUrl;
 
     UA_GetEndpointsResponse response;
-    UA_GetEndpointsResponse_init(&response);
     __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST],
                         &response, &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE]);
 
@@ -347,7 +369,6 @@ createSession(UA_Client *client) {
     UA_String_copy(&client->endpointUrl, &request.endpointUrl);
 
     UA_CreateSessionResponse response;
-    UA_CreateSessionResponse_init(&response);
     __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST],
                         &response, &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE]);
 
@@ -364,14 +385,14 @@ UA_Client_connectInternal(UA_Client *client, const char *endpointUrl,
                           UA_Boolean endpointsHandshake, UA_Boolean createNewSession) {
     if(client->state >= UA_CLIENTSTATE_CONNECTED)
         return UA_STATUSCODE_GOOD;
-
     UA_ChannelSecurityToken_init(&client->channel.securityToken);
     client->channel.state = UA_SECURECHANNELSTATE_FRESH;
 
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     client->connection =
         client->config.connectionFunc(client->config.localConnectionConfig,
-                                      endpointUrl, client->config.timeout);
+                                      endpointUrl, client->config.timeout,
+                                      client->config.logger);
     if(client->connection.state != UA_CONNECTION_OPENING) {
         retval = UA_STATUSCODE_BADCONNECTIONCLOSED;
         goto cleanup;
@@ -389,49 +410,72 @@ UA_Client_connectInternal(UA_Client *client, const char *endpointUrl,
     retval = HelAckHandshake(client);
     if(retval != UA_STATUSCODE_GOOD)
         goto cleanup;
-    client->state = UA_CLIENTSTATE_CONNECTED;
+    setClientState(client, UA_CLIENTSTATE_CONNECTED);
 
     /* Open a SecureChannel. TODO: Select with endpoint  */
     client->channel.connection = &client->connection;
     retval = openSecureChannel(client, false);
     if(retval != UA_STATUSCODE_GOOD)
         goto cleanup;
-    client->state = UA_CLIENTSTATE_SECURECHANNEL;
+    setClientState(client, UA_CLIENTSTATE_SECURECHANNEL);
+
+
+    /* Delete async service. TODO: Move this from connect to the disconnect/cleanup phase */
+    UA_Client_AsyncService_removeAll(client, UA_STATUSCODE_BADSHUTDOWN);
 
-    /* There is no session to recover and we want to have a session */
-    if(createNewSession && UA_NodeId_equal(&client->authenticationToken, &UA_NODEID_NULL)) {
-        /* Get Endpoints */
-        if(endpointsHandshake) {
-            retval = getEndpoints(client);
+#ifdef UA_ENABLE_SUBSCRIPTIONS
+    client->currentlyOutStandingPublishRequests = 0;
+#endif
+
+// TODO: actually, reactivate an existing session is working, but currently republish is not implemented
+// This option is disabled until we have a good implementation of the subscription recovery.
+
+#ifdef UA_SESSION_RECOVERY
+    /* Try to activate an existing Session for this SecureChannel */
+    if((!UA_NodeId_equal(&client->authenticationToken, &UA_NODEID_NULL)) && (createNewSession)) {
+        retval = activateSession(client);
+        if(retval == UA_STATUSCODE_BADSESSIONIDINVALID) {
+            /* Could not recover an old session. Remove authenticationToken */
+            UA_NodeId_deleteMembers(&client->authenticationToken);
+        } else {
             if(retval != UA_STATUSCODE_GOOD)
                 goto cleanup;
+            setClientState(client, UA_CLIENTSTATE_SESSION_RENEWED);
+            return retval;
         }
-        /* Create a Session */
-        retval = createSession(client);
+    } else {
+        UA_NodeId_deleteMembers(&client->authenticationToken);
+    }
+#else
+    UA_NodeId_deleteMembers(&client->authenticationToken);
+#endif /* UA_SESSION_RECOVERY */
+
+    /* Get Endpoints */
+    if(endpointsHandshake) {
+        retval = getEndpoints(client);
         if(retval != UA_STATUSCODE_GOOD)
             goto cleanup;
     }
 
-    /* Activate the Session for this SecureChannel */
-    if(!UA_NodeId_equal(&client->authenticationToken, &UA_NODEID_NULL)) {
+    /* Create the Session for this SecureChannel */
+    if(createNewSession) {
+        retval = createSession(client);
+        if(retval != UA_STATUSCODE_GOOD)
+            goto cleanup;
+#ifdef UA_ENABLE_SUBSCRIPTIONS
+        /* A new session has been created. We need to clean up the subscriptions */
+        UA_Client_Subscriptions_clean(client);
+#endif
         retval = activateSession(client);
-
-        /* Could not recover an old session. Create a new one */
-        if(retval == UA_STATUSCODE_BADSESSIONIDINVALID) {
-            retval = createSession(client);
-            if(retval != UA_STATUSCODE_GOOD)
-                goto cleanup;
-            retval = activateSession(client);
-        }
-
         if(retval != UA_STATUSCODE_GOOD)
             goto cleanup;
-        client->state = UA_CLIENTSTATE_SESSION;
+        setClientState(client, UA_CLIENTSTATE_SESSION);
     }
+
     return retval;
 
 cleanup:
-    UA_Client_disconnect(client);
+    UA_Client_close(client);
     return retval;
 }
 
@@ -453,7 +497,8 @@ UA_StatusCode
 UA_Client_manuallyRenewSecureChannel(UA_Client *client) {
     UA_StatusCode retval = openSecureChannel(client, true);
     if(retval != UA_STATUSCODE_GOOD)
-        client->state = UA_CLIENTSTATE_DISCONNECTED;
+        UA_Client_close(client);
+
     return retval;
 }
 
@@ -488,27 +533,57 @@ sendCloseSecureChannel(UA_Client *client) {
     UA_SecureChannel_sendSymmetricMessage(channel, ++client->requestId,
                                           UA_MESSAGETYPE_CLO, &request,
                                           &UA_TYPES[UA_TYPES_CLOSESECURECHANNELREQUEST]);
+    UA_CloseSecureChannelRequest_deleteMembers(&request);
     UA_SecureChannel_deleteMembersCleanup(&client->channel);
 }
 
 UA_StatusCode
 UA_Client_disconnect(UA_Client *client) {
     /* Is a session established? */
-    if(client->state == UA_CLIENTSTATE_SESSION){
-        client->state = UA_CLIENTSTATE_SESSION_DISCONNECTED;
+    if(client->state >= UA_CLIENTSTATE_SESSION) {
+        client->state = UA_CLIENTSTATE_SECURECHANNEL;
         sendCloseSession(client);
     }
     UA_NodeId_deleteMembers(&client->authenticationToken);
     client->requestHandle = 0;
 
     /* Is a secure channel established? */
-    if(client->state >= UA_CLIENTSTATE_SECURECHANNEL)
+    if(client->state >= UA_CLIENTSTATE_SECURECHANNEL) {
+        client->state = UA_CLIENTSTATE_CONNECTED;
         sendCloseSecureChannel(client);
+    }
 
     /* Close the TCP connection */
-    if(client->state >= UA_CLIENTSTATE_CONNECTED)
+    if(client->connection.state != UA_CONNECTION_CLOSED)
+        client->connection.close(&client->connection);
+
+#ifdef UA_ENABLE_SUBSCRIPTIONS
+// TODO REMOVE WHEN UA_SESSION_RECOVERY IS READY
+        /* We need to clean up the subscriptions */
+        UA_Client_Subscriptions_clean(client);
+#endif
+
+    setClientState(client, UA_CLIENTSTATE_DISCONNECTED);
+    return UA_STATUSCODE_GOOD;
+}
+
+UA_StatusCode
+UA_Client_close(UA_Client *client) {
+    client->requestHandle = 0;
+
+    if(client->state >= UA_CLIENTSTATE_SECURECHANNEL)
+        UA_SecureChannel_deleteMembersCleanup(&client->channel);
+
+    /* Close the TCP connection */
+    if(client->connection.state != UA_CONNECTION_CLOSED)
         client->connection.close(&client->connection);
 
-    client->state = UA_CLIENTSTATE_DISCONNECTED;
+#ifdef UA_ENABLE_SUBSCRIPTIONS
+// TODO REMOVE WHEN UA_SESSION_RECOVERY IS READY
+        /* We need to clean up the subscriptions */
+        UA_Client_Subscriptions_clean(client);
+#endif
+
+    setClientState(client, UA_CLIENTSTATE_DISCONNECTED);
     return UA_STATUSCODE_GOOD;
 }

+ 6 - 3
src/client/ua_client_discovery.c

@@ -1,6 +1,11 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ */
 
 #include "ua_client.h"
 #include "ua_client_internal.h"
@@ -58,7 +63,6 @@ UA_Client_findServers(UA_Client *client, const char *serverUrl,
 
     /* Send the request */
     UA_FindServersResponse response;
-    UA_FindServersResponse_init(&response);
     __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_FINDSERVERSREQUEST],
                         &response, &UA_TYPES[UA_TYPES_FINDSERVERSRESPONSE]);
 
@@ -109,7 +113,6 @@ UA_Client_findServersOnNetwork(UA_Client *client, const char *serverUrl,
 
     /* Send the request */
     UA_FindServersOnNetworkResponse response;
-    UA_FindServersOnNetworkResponse_init(&response);
     __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKREQUEST],
                         &response, &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKRESPONSE]);
 

+ 9 - 2
src/client/ua_client_highlevel.c

@@ -1,6 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2015-2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2015 (c) Oleksiy Vasylyev
+ *    Copyright 2017 (c) Florian Palm
+ *    Copyright 2016 (c) Chris Iatrou
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ */
 
 #include "ua_client.h"
 #include "ua_client_highlevel.h"
@@ -35,7 +42,7 @@ UA_Client_NamespaceGetIndex(UA_Client *client, UA_String *namespaceUri,
 
     retval = UA_STATUSCODE_BADNOTFOUND;
     UA_String *ns = (UA_String *)response.results[0].value.data;
-    for(size_t i = 0; i < response.results[0].value.arrayLength; ++i){
+    for(size_t i = 0; i < response.results[0].value.arrayLength; ++i) {
         if(UA_String_equal(namespaceUri, &ns[i])) {
             *namespaceIndex = (UA_UInt16)i;
             retval = UA_STATUSCODE_GOOD;

+ 0 - 455
src/client/ua_client_highlevel_subscriptions.c

@@ -1,455 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "ua_client_highlevel.h"
-#include "ua_client_internal.h"
-#include "ua_util.h"
-
-#ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */
-
-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;
-    request.requestedLifetimeCount = settings.requestedLifetimeCount;
-    request.requestedMaxKeepAliveCount = settings.requestedMaxKeepAliveCount;
-    request.maxNotificationsPerPublish = settings.maxNotificationsPerPublish;
-    request.publishingEnabled = settings.publishingEnabled;
-    request.priority = settings.priority;
-
-    UA_CreateSubscriptionResponse response = UA_Client_Service_createSubscription(client, request);
-    UA_StatusCode retval = response.responseHeader.serviceResult;
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_CreateSubscriptionResponse_deleteMembers(&response);
-        return retval;
-    }
-
-    UA_Client_Subscription *newSub = (UA_Client_Subscription *)UA_malloc(sizeof(UA_Client_Subscription));
-    if(!newSub) {
-        UA_CreateSubscriptionResponse_deleteMembers(&response);
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-    }
-
-    LIST_INIT(&newSub->monitoredItems);
-    newSub->lifeTime = response.revisedLifetimeCount;
-    newSub->keepAliveCount = response.revisedMaxKeepAliveCount;
-    newSub->publishingInterval = response.revisedPublishingInterval;
-    newSub->subscriptionID = response.subscriptionId;
-    newSub->notificationsPerPublish = request.maxNotificationsPerPublish;
-    newSub->priority = request.priority;
-    LIST_INSERT_HEAD(&client->subscriptions, newSub, listEntry);
-
-    if(newSubscriptionId)
-        *newSubscriptionId = newSub->subscriptionID;
-
-    UA_CreateSubscriptionResponse_deleteMembers(&response);
-    return UA_STATUSCODE_GOOD;
-}
-
-static UA_Client_Subscription *findSubscription(const UA_Client *client, UA_UInt32 subscriptionId)
-{
-    UA_Client_Subscription *sub = NULL;
-    LIST_FOREACH(sub, &client->subscriptions, listEntry) {
-        if(sub->subscriptionID == subscriptionId)
-            break;
-    }
-    return sub;
-}
-
-/* remove the subscription remotely */
-UA_StatusCode
-UA_Client_Subscriptions_remove(UA_Client *client, UA_UInt32 subscriptionId) {
-    UA_Client_Subscription *sub = findSubscription(client, subscriptionId);
-    if(!sub)
-        return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
-
-    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);
-        if(retval != UA_STATUSCODE_GOOD)
-            return retval;
-    }
-
-    /* remove the subscription remotely */
-    UA_DeleteSubscriptionsRequest request;
-    UA_DeleteSubscriptionsRequest_init(&request);
-    request.subscriptionIdsSize = 1;
-    request.subscriptionIds = &sub->subscriptionID;
-    UA_DeleteSubscriptionsResponse response = UA_Client_Service_deleteSubscriptions(client, request);
-    retval = response.responseHeader.serviceResult;
-    if(retval == UA_STATUSCODE_GOOD && response.resultsSize > 0)
-        retval = response.results[0];
-    UA_DeleteSubscriptionsResponse_deleteMembers(&response);
-
-    if(retval != UA_STATUSCODE_GOOD && retval != UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID) {
-        UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_CLIENT,
-                    "Could not remove subscription %u with error code %s",
-                    sub->subscriptionID, UA_StatusCode_name(retval));
-        return retval;
-    }
-
-    UA_Client_Subscriptions_forceDelete(client, sub);
-    return UA_STATUSCODE_GOOD;
-}
-
-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);
-        LIST_REMOVE(mon, listEntry);
-        UA_free(mon);
-    }
-    LIST_REMOVE(sub, listEntry);
-    UA_free(sub);
-}
-
-UA_StatusCode
-UA_Client_Subscriptions_addMonitoredEvent(UA_Client *client, const UA_UInt32 subscriptionId,
-                                         const UA_NodeId nodeId, const UA_UInt32 attributeID,
-                                         UA_SimpleAttributeOperand *selectClause,
-                                         const size_t nSelectClauses,
-                                         UA_ContentFilterElement *whereClause,
-                                         const size_t nWhereClauses,
-                                         const UA_MonitoredEventHandlingFunction hf,
-                                         void *hfContext, UA_UInt32 *newMonitoredItemId) {
-    UA_Client_Subscription *sub = findSubscription(client, subscriptionId);
-    if(!sub)
-        return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
-
-    /* Send the request */
-    UA_CreateMonitoredItemsRequest request;
-    UA_CreateMonitoredItemsRequest_init(&request);
-    request.subscriptionId = subscriptionId;
-
-    UA_MonitoredItemCreateRequest item;
-    UA_MonitoredItemCreateRequest_init(&item);
-    item.itemToMonitor.nodeId = nodeId;
-    item.itemToMonitor.attributeId = attributeID;
-    item.monitoringMode = UA_MONITORINGMODE_REPORTING;
-    item.requestedParameters.clientHandle = ++(client->monitoredItemHandles);
-    item.requestedParameters.samplingInterval = 0;
-    item.requestedParameters.discardOldest = false;
-
-    UA_EventFilter *evFilter = UA_EventFilter_new();
-    if(!evFilter) {
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-    }
-    UA_EventFilter_init(evFilter);
-    evFilter->selectClausesSize = nSelectClauses;
-    evFilter->selectClauses = selectClause;
-    evFilter->whereClause.elementsSize = nWhereClauses;
-    evFilter->whereClause.elements = whereClause;
-
-    item.requestedParameters.filter.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
-    item.requestedParameters.filter.content.decoded.type = &UA_TYPES[UA_TYPES_EVENTFILTER];
-    item.requestedParameters.filter.content.decoded.data = evFilter;
-
-    request.itemsToCreate = &item;
-    request.itemsToCreateSize = 1;
-    UA_CreateMonitoredItemsResponse response = UA_Client_Service_createMonitoredItems(client, request);
-
-    // slight misuse of retval here to check if the deletion was successfull.
-    UA_StatusCode retval;
-    if(response.resultsSize == 0)
-        retval = response.responseHeader.serviceResult;
-    else
-        retval = response.results[0].statusCode;
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_CreateMonitoredItemsResponse_deleteMembers(&response);
-        UA_EventFilter_delete(evFilter);
-        return retval;
-    }
-
-    /* Create the handler */
-    UA_Client_MonitoredItem *newMon = (UA_Client_MonitoredItem *)UA_malloc(sizeof(UA_Client_MonitoredItem));
-    if(!newMon) {
-        UA_CreateMonitoredItemsResponse_deleteMembers(&response);
-        UA_EventFilter_delete(evFilter);
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-    }
-
-    newMon->monitoringMode = UA_MONITORINGMODE_REPORTING;
-    UA_NodeId_copy(&nodeId, &newMon->monitoredNodeId);
-    newMon->attributeID = attributeID;
-    newMon->clientHandle = client->monitoredItemHandles;
-    newMon->samplingInterval = 0;
-    newMon->queueSize = 0;
-    newMon->discardOldest = false;
-
-    newMon->handlerEvents = hf;
-    newMon->handlerEventsContext = hfContext;
-    newMon->monitoredItemId = response.results[0].monitoredItemId;
-    LIST_INSERT_HEAD(&sub->monitoredItems, newMon, listEntry);
-    *newMonitoredItemId = newMon->monitoredItemId;
-
-    UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_CLIENT,
-                 "Created a monitored item with client handle %u", client->monitoredItemHandles);
-
-    UA_EventFilter_delete(evFilter);
-    UA_CreateMonitoredItemsResponse_deleteMembers(&response);
-    return UA_STATUSCODE_GOOD;
-}
-
-UA_StatusCode
-UA_Client_Subscriptions_addMonitoredItem(UA_Client *client, UA_UInt32 subscriptionId,
-                                         UA_NodeId nodeId, UA_UInt32 attributeID,
-                                         UA_MonitoredItemHandlingFunction hf,
-                                         void *hfContext, UA_UInt32 *newMonitoredItemId,
-                                         UA_Double samplingInterval) {
-    UA_Client_Subscription *sub = findSubscription(client, subscriptionId);
-    if(!sub)
-        return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
-
-    /* Create the handler */
-    UA_Client_MonitoredItem *newMon = (UA_Client_MonitoredItem*)UA_malloc(sizeof(UA_Client_MonitoredItem));
-    if(!newMon)
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-
-    /* Send the request */
-    UA_CreateMonitoredItemsRequest request;
-    UA_CreateMonitoredItemsRequest_init(&request);
-    request.subscriptionId = subscriptionId;
-    UA_MonitoredItemCreateRequest item;
-    UA_MonitoredItemCreateRequest_init(&item);
-    item.itemToMonitor.nodeId = nodeId;
-    item.itemToMonitor.attributeId = attributeID;
-    item.monitoringMode = UA_MONITORINGMODE_REPORTING;
-    item.requestedParameters.clientHandle = ++(client->monitoredItemHandles);
-    item.requestedParameters.samplingInterval = samplingInterval;
-    item.requestedParameters.discardOldest = true;
-    item.requestedParameters.queueSize = 1;
-    request.itemsToCreate = &item;
-    request.itemsToCreateSize = 1;
-    UA_CreateMonitoredItemsResponse response = UA_Client_Service_createMonitoredItems(client, request);
-
-    // slight misuse of retval here to check if the addition was successfull.
-    UA_StatusCode retval = response.responseHeader.serviceResult;
-    if(retval == UA_STATUSCODE_GOOD) {
-        if(response.resultsSize == 1)
-            retval = response.results[0].statusCode;
-        else
-            retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
-    }
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_free(newMon);
-        UA_CreateMonitoredItemsResponse_deleteMembers(&response);
-        return retval;
-    }
-
-    /* Set the handler */
-    newMon->monitoringMode = UA_MONITORINGMODE_REPORTING;
-    UA_NodeId_copy(&nodeId, &newMon->monitoredNodeId);
-    newMon->attributeID = attributeID;
-    newMon->clientHandle = client->monitoredItemHandles;
-    newMon->samplingInterval = samplingInterval;
-    newMon->queueSize = 1;
-    newMon->discardOldest = true;
-    newMon->handler = hf;
-    newMon->handlerContext = hfContext;
-    newMon->monitoredItemId = response.results[0].monitoredItemId;
-    LIST_INSERT_HEAD(&sub->monitoredItems, newMon, listEntry);
-    *newMonitoredItemId = newMon->monitoredItemId;
-
-    UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_CLIENT,
-                 "Created a monitored item with client handle %u",
-                 client->monitoredItemHandles);
-
-    UA_CreateMonitoredItemsResponse_deleteMembers(&response);
-    return UA_STATUSCODE_GOOD;
-}
-
-UA_StatusCode
-UA_Client_Subscriptions_removeMonitoredItem(UA_Client *client, UA_UInt32 subscriptionId,
-                                            UA_UInt32 monitoredItemId) {
-    UA_Client_Subscription *sub = findSubscription(client, subscriptionId);
-    if(!sub)
-        return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
-
-    UA_Client_MonitoredItem *mon;
-    LIST_FOREACH(mon, &sub->monitoredItems, listEntry) {
-        if(mon->monitoredItemId == monitoredItemId)
-            break;
-    }
-    if(!mon)
-        return UA_STATUSCODE_BADMONITOREDITEMIDINVALID;
-
-    /* remove the monitoreditem remotely */
-    UA_DeleteMonitoredItemsRequest request;
-    UA_DeleteMonitoredItemsRequest_init(&request);
-    request.subscriptionId = sub->subscriptionID;
-    request.monitoredItemIdsSize = 1;
-    request.monitoredItemIds = &mon->monitoredItemId;
-    UA_DeleteMonitoredItemsResponse response = UA_Client_Service_deleteMonitoredItems(client, request);
-
-    UA_StatusCode retval = response.responseHeader.serviceResult;
-    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) {
-        UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_CLIENT,
-                    "Could not remove monitoreditem %u with error code %s",
-                    monitoredItemId, UA_StatusCode_name(retval));
-        return retval;
-    }
-
-    LIST_REMOVE(mon, listEntry);
-    UA_NodeId_deleteMembers(&mon->monitoredNodeId);
-    UA_free(mon);
-    return UA_STATUSCODE_GOOD;
-}
-
-static void
-UA_Client_processPublishResponse(UA_Client *client, UA_PublishRequest *request,
-                                 UA_PublishResponse *response) {
-    if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD)
-        return;
-
-    UA_Client_Subscription *sub = findSubscription(client, response->subscriptionId);
-    if(!sub)
-        return;
-
-    /* Check if the server has acknowledged any of the sent ACKs */
-    for(size_t i = 0; i < response->resultsSize && i < request->subscriptionAcknowledgementsSize; ++i) {
-        /* remove also acks that are unknown to the server */
-        if(response->results[i] != UA_STATUSCODE_GOOD &&
-           response->results[i] != UA_STATUSCODE_BADSEQUENCENUMBERUNKNOWN)
-            continue;
-
-        /* Remove the ack from the list */
-        UA_SubscriptionAcknowledgement *orig_ack = &request->subscriptionAcknowledgements[i];
-        UA_Client_NotificationsAckNumber *ack;
-        LIST_FOREACH(ack, &client->pendingNotificationsAcks, listEntry) {
-            if(ack->subAck.subscriptionId == orig_ack->subscriptionId &&
-               ack->subAck.sequenceNumber == orig_ack->sequenceNumber) {
-                LIST_REMOVE(ack, listEntry);
-                UA_free(ack);
-                UA_assert(ack != LIST_FIRST(&client->pendingNotificationsAcks));
-                break;
-            }
-        }
-    }
-
-    /* Process the notification messages */
-    UA_NotificationMessage *msg = &response->notificationMessage;
-    for(size_t k = 0; k < msg->notificationDataSize; ++k) {
-        if(msg->notificationData[k].encoding != UA_EXTENSIONOBJECT_DECODED)
-            continue;
-
-        if(msg->notificationData[k].content.decoded.type == &UA_TYPES[UA_TYPES_DATACHANGENOTIFICATION]) {
-            UA_DataChangeNotification *dataChangeNotification = (UA_DataChangeNotification *)msg->notificationData[k].content.decoded.data;
-            for(size_t j = 0; j < dataChangeNotification->monitoredItemsSize; ++j) {
-                UA_MonitoredItemNotification *mitemNot = &dataChangeNotification->monitoredItems[j];
-                UA_Client_MonitoredItem *mon;
-                LIST_FOREACH(mon, &sub->monitoredItems, listEntry) {
-                    if(mon->clientHandle == mitemNot->clientHandle) {
-                        mon->handler(mon->monitoredItemId, &mitemNot->value, mon->handlerContext);
-                        break;
-                    }
-                }
-                if(!mon)
-                    UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_CLIENT,
-                                 "Could not process a notification with clienthandle %u on subscription %u",
-                                 mitemNot->clientHandle, sub->subscriptionID);
-            }
-        }
-        else if(msg->notificationData[k].content.decoded.type == &UA_TYPES[UA_TYPES_EVENTNOTIFICATIONLIST]) {
-            UA_EventNotificationList *eventNotificationList = (UA_EventNotificationList *)msg->notificationData[k].content.decoded.data;
-            for (size_t j = 0; j < eventNotificationList->eventsSize; ++j) {
-                UA_EventFieldList *eventFieldList = &eventNotificationList->events[j];
-                UA_Client_MonitoredItem *mon;
-                LIST_FOREACH(mon, &sub->monitoredItems, listEntry) {
-                    if(mon->clientHandle == eventFieldList->clientHandle) {
-                        mon->handlerEvents(mon->monitoredItemId, eventFieldList->eventFieldsSize,
-                                           eventFieldList->eventFields, mon->handlerContext);
-                        break;
-                    }
-                }
-                if(!mon)
-                    UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_CLIENT,
-                                 "Could not process a notification with clienthandle %u on subscription %u",
-                                 eventFieldList->clientHandle, sub->subscriptionID);
-            }
-        }
-        else {
-            continue; // no other types are supported
-        }
-    }
-
-    /* Add to the list of pending acks */
-    UA_Client_NotificationsAckNumber *tmpAck =
-        (UA_Client_NotificationsAckNumber*)UA_malloc(sizeof(UA_Client_NotificationsAckNumber));
-    if(!tmpAck) {
-        UA_LOG_WARNING(client->config.logger, UA_LOGCATEGORY_CLIENT,
-                       "Not enough memory to store the acknowledgement for a publish "
-                       "message on subscription %u", sub->subscriptionID);
-        return;
-    }
-    tmpAck->subAck.sequenceNumber = msg->sequenceNumber;
-    tmpAck->subAck.subscriptionId = sub->subscriptionID;
-    LIST_INSERT_HEAD(&client->pendingNotificationsAcks, tmpAck, listEntry);
-}
-
-UA_StatusCode
-UA_Client_Subscriptions_manuallySendPublishRequest(UA_Client *client) {
-    if(client->state < UA_CLIENTSTATE_SESSION)
-        return UA_STATUSCODE_BADSERVERNOTCONNECTED;
-
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
-
-    UA_DateTime now = UA_DateTime_nowMonotonic();
-    UA_DateTime maxDate = now + (UA_DateTime)(client->config.timeout * UA_MSEC_TO_DATETIME);
-
-    UA_Boolean moreNotifications = true;
-    while(moreNotifications) {
-        UA_PublishRequest request;
-        UA_PublishRequest_init(&request);
-        request.subscriptionAcknowledgementsSize = 0;
-
-        UA_Client_NotificationsAckNumber *ack;
-        LIST_FOREACH(ack, &client->pendingNotificationsAcks, listEntry)
-            ++request.subscriptionAcknowledgementsSize;
-        if(request.subscriptionAcknowledgementsSize > 0) {
-            request.subscriptionAcknowledgements = (UA_SubscriptionAcknowledgement*)
-                UA_malloc(sizeof(UA_SubscriptionAcknowledgement) * request.subscriptionAcknowledgementsSize);
-            if(!request.subscriptionAcknowledgements)
-                return UA_STATUSCODE_BADOUTOFMEMORY;
-        }
-
-        int i = 0;
-        LIST_FOREACH(ack, &client->pendingNotificationsAcks, listEntry) {
-            request.subscriptionAcknowledgements[i].sequenceNumber = ack->subAck.sequenceNumber;
-            request.subscriptionAcknowledgements[i].subscriptionId = ack->subAck.subscriptionId;
-            ++i;
-        }
-
-        UA_PublishResponse response = UA_Client_Service_publish(client, request);
-        UA_Client_processPublishResponse(client, &request, &response);
-        
-        now = UA_DateTime_nowMonotonic();
-        if (now > maxDate){
-            moreNotifications = UA_FALSE;
-            retval = UA_STATUSCODE_GOODNONCRITICALTIMEOUT;
-        }else{
-            moreNotifications = response.moreNotifications;
-        }
-        
-        UA_PublishResponse_deleteMembers(&response);
-        UA_PublishRequest_deleteMembers(&request);
-    }
-    
-    if(client->state < UA_CLIENTSTATE_SESSION)
-        return UA_STATUSCODE_BADSERVERNOTCONNECTED;
-
-    return retval;
-}
-
-#endif /* UA_ENABLE_SUBSCRIPTIONS */

+ 62 - 24
src/client/ua_client_internal.h

@@ -1,16 +1,26 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2015-2016 (c) Sten Grüner
+ *    Copyright 2015-2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2015 (c) Oleksiy Vasylyev
+ *    Copyright 2016-2017 (c) Florian Palm
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ *    Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB
+ */
 
 #ifndef UA_CLIENT_INTERNAL_H_
 #define UA_CLIENT_INTERNAL_H_
 
 #include "ua_securechannel.h"
-#include "queue.h"
+#include "ua_client_highlevel.h"
+#include "ua_client_subscriptions.h"
+#include "../../deps/queue.h"
 
- /**************************/
- /* Subscriptions Handling */
- /**************************/
+/**************************/
+/* Subscriptions Handling */
+/**************************/
 
 #ifdef UA_ENABLE_SUBSCRIPTIONS
 
@@ -20,35 +30,53 @@ typedef struct UA_Client_NotificationsAckNumber {
 } UA_Client_NotificationsAckNumber;
 
 typedef struct UA_Client_MonitoredItem {
-    LIST_ENTRY(UA_Client_MonitoredItem)  listEntry;
+    LIST_ENTRY(UA_Client_MonitoredItem) listEntry;
     UA_UInt32 monitoredItemId;
-    UA_UInt32 monitoringMode;
-    UA_NodeId monitoredNodeId;
-    UA_UInt32 attributeID;
     UA_UInt32 clientHandle;
-    UA_Double samplingInterval;
-    UA_UInt32 queueSize;
-    UA_Boolean discardOldest;
-    void(*handler)(UA_UInt32 monId, UA_DataValue *value, void *context);
-    void *handlerContext;
-    void(*handlerEvents)(const UA_UInt32 monId, const size_t nEventFields, const UA_Variant *eventFields, void *context);
-    void *handlerEventsContext;
+    void *context;
+    UA_Client_DeleteMonitoredItemCallback deleteCallback;
+    union {
+        UA_Client_DataChangeNotificationCallback dataChangeCallback;
+        UA_Client_EventNotificationCallback eventCallback;
+    } handler;
+    UA_Boolean isEventMonitoredItem; /* Otherwise a DataChange MoniitoredItem */
 } UA_Client_MonitoredItem;
 
 typedef struct UA_Client_Subscription {
     LIST_ENTRY(UA_Client_Subscription) listEntry;
-    UA_UInt32 lifeTime;
-    UA_UInt32 keepAliveCount;
+    UA_UInt32 subscriptionId;
+    void *context;
     UA_Double publishingInterval;
-    UA_UInt32 subscriptionID;
-    UA_UInt32 notificationsPerPublish;
-    UA_UInt32 priority;
+    UA_UInt32 maxKeepAliveCount;
+    UA_Client_StatusChangeNotificationCallback statusChangeCallback;
+    UA_Client_DeleteSubscriptionCallback deleteCallback;
+    UA_UInt32 sequenceNumber;
+    UA_DateTime lastActivity;
     LIST_HEAD(UA_ListOfClientMonitoredItems, UA_Client_MonitoredItem) monitoredItems;
 } UA_Client_Subscription;
 
-void UA_Client_Subscriptions_forceDelete(UA_Client *client, UA_Client_Subscription *sub);
+void
+UA_Client_Subscriptions_clean(UA_Client *client);
 
-#endif
+void
+UA_Client_MonitoredItem_remove(UA_Client *client, UA_Client_Subscription *sub,
+                               UA_Client_MonitoredItem *mon);
+
+void
+UA_Client_Subscriptions_processPublishResponse(UA_Client *client,
+                                               UA_PublishRequest *request,
+                                               UA_PublishResponse *response);
+
+UA_StatusCode
+UA_Client_preparePublishRequest(UA_Client *client, UA_PublishRequest *request);
+
+UA_StatusCode
+UA_Client_Subscriptions_backgroundPublish(UA_Client *client);
+
+void
+UA_Client_Subscriptions_backgroundPublishInactivityCheck(UA_Client *client);
+
+#endif /* UA_ENABLE_SUBSCRIPTIONS */
 
 /**********/
 /* Client */
@@ -62,6 +90,11 @@ typedef struct AsyncServiceCall {
     void *userdata;
 } AsyncServiceCall;
 
+void UA_Client_AsyncService_cancel(UA_Client *client, AsyncServiceCall *ac,
+                                   UA_StatusCode statusCode);
+
+void UA_Client_AsyncService_removeAll(UA_Client *client, UA_StatusCode statusCode);
+
 typedef enum {
     UA_CLIENTAUTHENTICATION_NONE,
     UA_CLIENTAUTHENTICATION_USERNAME
@@ -70,6 +103,7 @@ typedef enum {
 struct UA_Client {
     /* State */
     UA_ClientState state;
+
     UA_ClientConfig config;
 
     /* Connection */
@@ -77,7 +111,7 @@ struct UA_Client {
     UA_String endpointUrl;
 
     /* SecureChannel */
-    UA_SecurityPolicy securityPolicy;
+    UA_SecurityPolicy securityPolicy; /* TODO: Move supported policies to the config */
     UA_SecureChannel channel;
     UA_UInt32 requestId;
     UA_DateTime nextChannelRenewal;
@@ -100,9 +134,13 @@ struct UA_Client {
     UA_UInt32 monitoredItemHandles;
     LIST_HEAD(ListOfUnacknowledgedNotifications, UA_Client_NotificationsAckNumber) pendingNotificationsAcks;
     LIST_HEAD(ListOfClientSubscriptionItems, UA_Client_Subscription) subscriptions;
+    UA_UInt16 currentlyOutStandingPublishRequests;
 #endif
 };
 
+void
+setClientState(UA_Client *client, UA_ClientState state);
+
 UA_StatusCode
 UA_Client_connectInternal(UA_Client *client, const char *endpointUrl,
                           UA_Boolean endpointsHandshake, UA_Boolean createNewSession);

+ 761 - 0
src/client/ua_client_subscriptions.c

@@ -0,0 +1,761 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2015-2018 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2015 (c) Oleksiy Vasylyev
+ *    Copyright 2016 (c) Sten Grüner
+ *    Copyright 2017-2018 (c) Thomas Stalder, Blue Time Concept SA
+ *    Copyright 2016-2017 (c) Florian Palm
+ *    Copyright 2017 (c) Frank Meerkötter
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ */
+
+#include "ua_client_highlevel.h"
+#include "ua_client_internal.h"
+#include "ua_util.h"
+
+#ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */
+
+/*****************/
+/* Subscriptions */
+/*****************/
+
+UA_CreateSubscriptionResponse UA_EXPORT
+UA_Client_Subscriptions_create(UA_Client *client,
+                               const UA_CreateSubscriptionRequest request,
+                               void *subscriptionContext,
+                               UA_Client_StatusChangeNotificationCallback statusChangeCallback,
+                               UA_Client_DeleteSubscriptionCallback deleteCallback) {
+    UA_CreateSubscriptionResponse response;
+    UA_CreateSubscriptionResponse_init(&response);
+    
+    /* Allocate the internal representation */
+    UA_Client_Subscription *newSub = (UA_Client_Subscription*)
+        UA_malloc(sizeof(UA_Client_Subscription));
+    if(!newSub) {
+        response.responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
+        return response;
+    }
+
+    /* Send the request as a synchronous service call */
+    __UA_Client_Service(client,
+                        &request, &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONRESPONSE]);
+    if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
+        UA_free(newSub);
+        return response;
+    }
+
+    /* Prepare the internal representation */
+    newSub->context = subscriptionContext;
+    newSub->subscriptionId = response.subscriptionId;
+    newSub->sequenceNumber = 0;
+    newSub->lastActivity = UA_DateTime_nowMonotonic();
+    newSub->statusChangeCallback = statusChangeCallback;
+    newSub->deleteCallback = deleteCallback;
+    newSub->publishingInterval = response.revisedPublishingInterval;
+    newSub->maxKeepAliveCount = response.revisedMaxKeepAliveCount;
+    LIST_INIT(&newSub->monitoredItems);
+    LIST_INSERT_HEAD(&client->subscriptions, newSub, listEntry);
+
+    return response;
+}
+
+static UA_Client_Subscription *
+findSubscription(const UA_Client *client, UA_UInt32 subscriptionId) {
+    UA_Client_Subscription *sub = NULL;
+    LIST_FOREACH(sub, &client->subscriptions, listEntry) {
+        if(sub->subscriptionId == subscriptionId)
+            break;
+    }
+    return sub;
+}
+
+UA_ModifySubscriptionResponse UA_EXPORT
+UA_Client_Subscriptions_modify(UA_Client *client, const UA_ModifySubscriptionRequest request) {
+    UA_ModifySubscriptionResponse response;
+    UA_ModifySubscriptionResponse_init(&response);
+
+    /* Find the internal representation */
+    UA_Client_Subscription *sub = findSubscription(client, request.subscriptionId);
+    if(!sub) {
+        response.responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
+        return response;
+    }
+    
+    /* Call the service */
+    __UA_Client_Service(client,
+                        &request, &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONRESPONSE]);
+
+    /* Adjust the internal representation */
+    sub->publishingInterval = response.revisedPublishingInterval;
+    sub->maxKeepAliveCount = response.revisedMaxKeepAliveCount;
+    return response;
+}
+
+static void
+UA_Client_Subscription_deleteInternal(UA_Client *client, UA_Client_Subscription *sub) {
+    /* Remove the MonitoredItems */
+    UA_Client_MonitoredItem *mon, *mon_tmp;
+    LIST_FOREACH_SAFE(mon, &sub->monitoredItems, listEntry, mon_tmp)
+        UA_Client_MonitoredItem_remove(client, sub, mon);
+
+    /* Call the delete callback */
+    if(sub->deleteCallback)
+        sub->deleteCallback(client, sub->subscriptionId, sub->context);
+
+    /* Remove */
+    LIST_REMOVE(sub, listEntry);
+    UA_free(sub);
+}
+
+UA_DeleteSubscriptionsResponse UA_EXPORT
+UA_Client_Subscriptions_delete(UA_Client *client, const UA_DeleteSubscriptionsRequest request) {
+    UA_STACKARRAY(UA_Client_Subscription*, subs, request.subscriptionIdsSize);
+    memset(subs, 0, sizeof(void*) * request.subscriptionIdsSize);
+
+    /* temporary remove the subscriptions from the list */
+    for(size_t i = 0; i < request.subscriptionIdsSize; i++) {
+        subs[i] = findSubscription(client, request.subscriptionIds[i]);
+        if (subs[i])
+            LIST_REMOVE(subs[i], listEntry);
+    }
+
+    /* Send the request */
+    UA_DeleteSubscriptionsResponse response;
+    __UA_Client_Service(client,
+                        &request, &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSRESPONSE]);
+    if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
+        goto cleanup;
+
+    if(request.subscriptionIdsSize != response.resultsSize) {
+        response.responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR;
+        goto cleanup;
+    }
+
+    /* Loop over the removed subscriptions and remove internally */
+    for(size_t i = 0; i < request.subscriptionIdsSize; i++) {
+        if(response.results[i] != UA_STATUSCODE_GOOD && response.results[i] != UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID) {
+            /* Something was wrong, reinsert the subscription in the list */
+            if (subs[i])
+                LIST_INSERT_HEAD(&client->subscriptions, subs[i], listEntry);
+            continue;
+        }
+
+        if(!subs[i]) {
+            UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                        "No internal representation of subscription %u",
+                        request.subscriptionIds[i]);
+            continue;
+        } else {
+            LIST_INSERT_HEAD(&client->subscriptions, subs[i], listEntry);
+        }
+
+        UA_Client_Subscription_deleteInternal(client, subs[i]);
+    }
+
+    return response;
+
+cleanup:
+    for(size_t i = 0; i < request.subscriptionIdsSize; i++) {
+        if (subs[i]) {
+            LIST_INSERT_HEAD(&client->subscriptions, subs[i], listEntry);
+        }
+    }
+    return response;
+}
+
+UA_StatusCode UA_EXPORT
+UA_Client_Subscriptions_deleteSingle(UA_Client *client, UA_UInt32 subscriptionId) {
+    UA_DeleteSubscriptionsRequest request;
+    UA_DeleteSubscriptionsRequest_init(&request);
+    request.subscriptionIds = &subscriptionId;
+    request.subscriptionIdsSize = 1;
+    
+    UA_DeleteSubscriptionsResponse response =
+        UA_Client_Subscriptions_delete(client, request);
+
+    UA_StatusCode retval = response.responseHeader.serviceResult;
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_DeleteSubscriptionsResponse_deleteMembers(&response);
+        return retval;
+    }
+
+    if(response.resultsSize != 1) {
+        UA_DeleteSubscriptionsResponse_deleteMembers(&response);
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
+
+    retval = response.results[0];
+    UA_DeleteSubscriptionsResponse_deleteMembers(&response);
+    return retval;
+}
+
+/******************/
+/* MonitoredItems */
+/******************/
+
+void
+UA_Client_MonitoredItem_remove(UA_Client *client, UA_Client_Subscription *sub,
+                               UA_Client_MonitoredItem *mon) {
+    LIST_REMOVE(mon, listEntry);
+    if(mon->deleteCallback)
+        mon->deleteCallback(client, sub->subscriptionId, sub->context,
+                            mon->monitoredItemId, mon->context);
+    UA_free(mon);
+}
+
+static void
+__UA_Client_MonitoredItems_create(UA_Client *client,
+                                  const UA_CreateMonitoredItemsRequest *request,
+                                  void **contexts, void **handlingCallbacks,
+                                  UA_Client_DeleteMonitoredItemCallback *deleteCallbacks,
+                                  UA_CreateMonitoredItemsResponse *response) {
+    UA_CreateMonitoredItemsResponse_init(response);
+
+    if (!request->itemsToCreateSize) {
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR;
+        return;
+    }
+
+    /* Fix clang warning */
+    size_t itemsToCreateSize = request->itemsToCreateSize;
+    UA_Client_Subscription *sub = NULL;
+    
+    /* Allocate the memory for internal representations */
+    UA_STACKARRAY(UA_Client_MonitoredItem*, mis, itemsToCreateSize);
+    memset(mis, 0, sizeof(void*) * itemsToCreateSize);
+    for(size_t i = 0; i < itemsToCreateSize; i++) {
+        mis[i] = (UA_Client_MonitoredItem*)UA_malloc(sizeof(UA_Client_MonitoredItem));
+        if(!mis[i]) {
+            response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
+            goto cleanup;
+        }
+    }
+
+    /* Get the subscription */
+    sub = findSubscription(client, request->subscriptionId);
+    if(!sub) {
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
+        goto cleanup;
+    }
+
+    /* Set the clientHandle */
+    for(size_t i = 0; i < itemsToCreateSize; i++)
+        request->itemsToCreate[i].requestedParameters.clientHandle = ++(client->monitoredItemHandles);
+
+    /* Call the service */
+    __UA_Client_Service(client, request, &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSREQUEST],
+                        response, &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSRESPONSE]);
+    if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD)
+        goto cleanup;
+
+    if(response->resultsSize != itemsToCreateSize) {
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR;
+        goto cleanup;
+    }
+
+    /* Add internally */
+    for(size_t i = 0; i < itemsToCreateSize; i++) {
+        if(response->results[i].statusCode != UA_STATUSCODE_GOOD) {
+            if (deleteCallbacks[i])
+                deleteCallbacks[i](client, sub->subscriptionId, sub->context, 0, contexts[i]);
+            UA_free(mis[i]);
+            mis[i] = NULL;
+            continue;
+        }
+            
+        UA_Client_MonitoredItem *newMon = mis[i];
+        newMon->clientHandle = request->itemsToCreate[i].requestedParameters.clientHandle;
+        newMon->monitoredItemId = response->results[i].monitoredItemId;
+        newMon->context = contexts[i];
+        newMon->deleteCallback = deleteCallbacks[i];
+        newMon->handler.dataChangeCallback =
+            (UA_Client_DataChangeNotificationCallback)(uintptr_t)handlingCallbacks[i];
+        newMon->isEventMonitoredItem =
+            (request->itemsToCreate[i].itemToMonitor.attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER);
+        LIST_INSERT_HEAD(&sub->monitoredItems, newMon, listEntry);
+    }
+
+    return;
+
+ cleanup:
+    for(size_t i = 0; i < itemsToCreateSize; i++) {
+        if (deleteCallbacks[i]) {
+            if (sub)
+                deleteCallbacks[i](client, sub->subscriptionId, sub->context, 0, contexts[i]);
+            else
+                deleteCallbacks[i](client, 0, NULL, 0, contexts[i]);
+        }
+        if(mis[i])
+            UA_free(mis[i]);
+    }
+}
+
+UA_CreateMonitoredItemsResponse UA_EXPORT
+UA_Client_MonitoredItems_createDataChanges(UA_Client *client,
+            const UA_CreateMonitoredItemsRequest request, void **contexts,
+            UA_Client_DataChangeNotificationCallback *callbacks,
+            UA_Client_DeleteMonitoredItemCallback *deleteCallbacks) {
+    UA_CreateMonitoredItemsResponse response;
+    __UA_Client_MonitoredItems_create(client, &request, contexts,
+                (void**)(uintptr_t)callbacks, deleteCallbacks, &response);
+    return response;
+}
+
+UA_MonitoredItemCreateResult UA_EXPORT
+UA_Client_MonitoredItems_createDataChange(UA_Client *client, UA_UInt32 subscriptionId,
+          UA_TimestampsToReturn timestampsToReturn, const UA_MonitoredItemCreateRequest item,
+          void *context, UA_Client_DataChangeNotificationCallback callback,
+          UA_Client_DeleteMonitoredItemCallback deleteCallback) {
+    UA_CreateMonitoredItemsRequest request;
+    UA_CreateMonitoredItemsRequest_init(&request);
+    request.subscriptionId = subscriptionId;
+    request.timestampsToReturn = timestampsToReturn;
+    request.itemsToCreate = (UA_MonitoredItemCreateRequest*)(uintptr_t)&item;
+    request.itemsToCreateSize = 1;
+    UA_CreateMonitoredItemsResponse response = 
+       UA_Client_MonitoredItems_createDataChanges(client, request, &context,
+                                                   &callback, &deleteCallback);
+    UA_MonitoredItemCreateResult result;
+    UA_MonitoredItemCreateResult_init(&result);
+    if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
+        result.statusCode = response.responseHeader.serviceResult;
+
+    if(result.statusCode == UA_STATUSCODE_GOOD &&
+       response.resultsSize != 1)
+        result.statusCode = UA_STATUSCODE_BADINTERNALERROR;
+    
+    if(result.statusCode == UA_STATUSCODE_GOOD)
+       UA_MonitoredItemCreateResult_copy(&response.results[0] , &result);
+    UA_CreateMonitoredItemsResponse_deleteMembers(&response);
+    return result;
+}
+
+UA_CreateMonitoredItemsResponse UA_EXPORT
+UA_Client_MonitoredItems_createEvents(UA_Client *client,
+            const UA_CreateMonitoredItemsRequest request, void **contexts,
+            UA_Client_EventNotificationCallback *callbacks,
+            UA_Client_DeleteMonitoredItemCallback *deleteCallbacks) {
+    UA_CreateMonitoredItemsResponse response;
+    __UA_Client_MonitoredItems_create(client, &request, contexts,
+                (void**)(uintptr_t)callbacks, deleteCallbacks, &response);
+    return response;
+}
+
+UA_MonitoredItemCreateResult UA_EXPORT
+UA_Client_MonitoredItems_createEvent(UA_Client *client, UA_UInt32 subscriptionId,
+          UA_TimestampsToReturn timestampsToReturn, const UA_MonitoredItemCreateRequest item,
+          void *context, UA_Client_EventNotificationCallback callback,
+          UA_Client_DeleteMonitoredItemCallback deleteCallback) {
+    UA_CreateMonitoredItemsRequest request;
+    UA_CreateMonitoredItemsRequest_init(&request);
+    request.subscriptionId = subscriptionId;
+    request.timestampsToReturn = timestampsToReturn;
+    request.itemsToCreate = (UA_MonitoredItemCreateRequest*)(uintptr_t)&item;
+    request.itemsToCreateSize = 1;
+    UA_CreateMonitoredItemsResponse response = 
+       UA_Client_MonitoredItems_createEvents(client, request, &context,
+                                             &callback, &deleteCallback);
+    UA_MonitoredItemCreateResult result;
+    UA_MonitoredItemCreateResult_copy(response.results , &result);
+    UA_CreateMonitoredItemsResponse_deleteMembers(&response);
+    return result;
+}
+
+UA_DeleteMonitoredItemsResponse UA_EXPORT
+UA_Client_MonitoredItems_delete(UA_Client *client, const UA_DeleteMonitoredItemsRequest request) {
+    /* Send the request */
+    UA_DeleteMonitoredItemsResponse response;
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSRESPONSE]);
+    if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
+        return response;
+
+    UA_Client_Subscription *sub = findSubscription(client, request.subscriptionId);
+    if(!sub) {
+        UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                    "No internal representation of subscription %u",
+                    request.subscriptionId);
+        return response;
+    }
+
+    /* Loop over deleted MonitoredItems */
+    for(size_t i = 0; i < response.resultsSize; i++) {
+        if(response.results[i] != UA_STATUSCODE_GOOD &&
+           response.results[i] != UA_STATUSCODE_BADMONITOREDITEMIDINVALID) {
+            continue;
+        }
+
+#ifndef __clang_analyzer__
+        /* Delete the internal representation */
+        UA_Client_MonitoredItem *mon;
+        LIST_FOREACH(mon, &sub->monitoredItems, listEntry) {
+            if(mon->monitoredItemId == request.monitoredItemIds[i]) {
+                UA_Client_MonitoredItem_remove(client, sub, mon);
+                break;
+            }
+        }
+#endif
+    }
+
+    return response;
+}
+
+UA_StatusCode UA_EXPORT
+UA_Client_MonitoredItems_deleteSingle(UA_Client *client, UA_UInt32 subscriptionId, UA_UInt32 monitoredItemId) {
+    UA_DeleteMonitoredItemsRequest request;
+    UA_DeleteMonitoredItemsRequest_init(&request);
+    request.subscriptionId = subscriptionId;
+    request.monitoredItemIds = &monitoredItemId;
+    request.monitoredItemIdsSize = 1;
+
+    UA_DeleteMonitoredItemsResponse response =
+        UA_Client_MonitoredItems_delete(client, request);
+
+    UA_StatusCode retval = response.responseHeader.serviceResult;
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_DeleteMonitoredItemsResponse_deleteMembers(&response);
+        return retval;
+    }
+
+    if(response.resultsSize != 1) {
+        UA_DeleteMonitoredItemsResponse_deleteMembers(&response);
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
+
+    retval = response.results[0];
+    UA_DeleteMonitoredItemsResponse_deleteMembers(&response);
+    return retval;
+}
+
+/*************************************/
+/* Async Processing of Notifications */
+/*************************************/
+
+/* Assume the request is already initialized */
+UA_StatusCode
+UA_Client_preparePublishRequest(UA_Client *client, UA_PublishRequest *request) {
+    /* Count acks */
+    UA_Client_NotificationsAckNumber *ack;
+    LIST_FOREACH(ack, &client->pendingNotificationsAcks, listEntry)
+        ++request->subscriptionAcknowledgementsSize;
+
+    /* Create the array. Returns a sentinel pointer if the length is zero. */
+    request->subscriptionAcknowledgements = (UA_SubscriptionAcknowledgement*)
+        UA_Array_new(request->subscriptionAcknowledgementsSize,
+                     &UA_TYPES[UA_TYPES_SUBSCRIPTIONACKNOWLEDGEMENT]);
+    if(!request->subscriptionAcknowledgements) {
+        request->subscriptionAcknowledgementsSize = 0;
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    }
+
+    size_t i = 0;
+    UA_Client_NotificationsAckNumber *ack_tmp;
+    LIST_FOREACH_SAFE(ack, &client->pendingNotificationsAcks, listEntry, ack_tmp) {
+        request->subscriptionAcknowledgements[i].sequenceNumber = ack->subAck.sequenceNumber;
+        request->subscriptionAcknowledgements[i].subscriptionId = ack->subAck.subscriptionId;
+        ++i;
+        LIST_REMOVE(ack, listEntry);
+        UA_free(ack);
+    }
+    return UA_STATUSCODE_GOOD;
+}
+
+/* According to OPC Unified Architecture, Part 4 5.13.1.1 i) */
+/* The value 0 is never used for the sequence number         */
+static UA_UInt32
+UA_Client_Subscriptions_nextSequenceNumber(UA_UInt32 sequenceNumber) {
+    UA_UInt32 nextSequenceNumber = sequenceNumber + 1;
+    if(nextSequenceNumber == 0)
+        nextSequenceNumber = 1;
+    return nextSequenceNumber;
+}
+
+static void
+processDataChangeNotification(UA_Client *client, UA_Client_Subscription *sub,
+                              UA_DataChangeNotification *dataChangeNotification) {
+    for(size_t j = 0; j < dataChangeNotification->monitoredItemsSize; ++j) {
+        UA_MonitoredItemNotification *min = &dataChangeNotification->monitoredItems[j];
+
+        /* Find the MonitoredItem */
+        UA_Client_MonitoredItem *mon;
+        LIST_FOREACH(mon, &sub->monitoredItems, listEntry) {
+            if(mon->clientHandle == min->clientHandle)
+                break;
+        }
+
+        if(!mon) {
+            UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                         "Could not process a notification with clienthandle %u on subscription %u",
+                         min->clientHandle, sub->subscriptionId);
+            continue;
+        }
+
+        if(mon->isEventMonitoredItem) {
+            UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                         "MonitoredItem is configured for Events. But received a "
+                         "DataChangeNotification.");
+            continue;
+        }
+
+        mon->handler.dataChangeCallback(client, sub->subscriptionId, sub->context,
+                                        mon->monitoredItemId, mon->context,
+                                        &min->value);
+    }
+}
+
+static void
+processEventNotification(UA_Client *client, UA_Client_Subscription *sub,
+                         UA_EventNotificationList *eventNotificationList) {
+    for(size_t j = 0; j < eventNotificationList->eventsSize; ++j) {
+        UA_EventFieldList *eventFieldList = &eventNotificationList->events[j];
+
+        /* Find the MonitoredItem */
+        UA_Client_MonitoredItem *mon;
+        LIST_FOREACH(mon, &sub->monitoredItems, listEntry) {
+            if(mon->monitoredItemId == eventFieldList->clientHandle)
+                break;
+        }
+
+        if(!mon) {
+            UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                         "Could not process a notification with clienthandle %u on subscription %u",
+                         eventFieldList->clientHandle, sub->subscriptionId);
+            continue;
+        }
+
+        if(!mon->isEventMonitoredItem) {
+            UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                         "MonitoredItem is configured for DataChanges. But received a "
+                         "EventNotification.");
+            continue;
+        }
+
+        mon->handler.eventCallback(client, sub->subscriptionId, sub->context,
+                                   mon->monitoredItemId, mon->context,
+                                   eventFieldList->eventFieldsSize,
+                                   eventFieldList->eventFields);
+    }
+}
+
+static void
+processNotificationMessage(UA_Client *client, UA_Client_Subscription *sub,
+                           UA_ExtensionObject *msg) {
+    if(msg->encoding != UA_EXTENSIONOBJECT_DECODED)
+        return;
+
+    /* Handle DataChangeNotification */
+    if(msg->content.decoded.type == &UA_TYPES[UA_TYPES_DATACHANGENOTIFICATION]) {
+        UA_DataChangeNotification *dataChangeNotification =
+            (UA_DataChangeNotification *)msg->content.decoded.data;
+        processDataChangeNotification(client, sub, dataChangeNotification);
+        return;
+    }
+
+    /* Handle EventNotification */
+    if(msg->content.decoded.type == &UA_TYPES[UA_TYPES_EVENTNOTIFICATIONLIST]) {
+        UA_EventNotificationList *eventNotificationList =
+            (UA_EventNotificationList *)msg->content.decoded.data;
+        processEventNotification(client, sub, eventNotificationList);
+        return;
+    }
+
+    /* Handle StatusChangeNotification */
+    if(msg->content.decoded.type == &UA_TYPES[UA_TYPES_STATUSCHANGENOTIFICATION]) {
+        if(sub->statusChangeCallback) {
+            sub->statusChangeCallback(client, sub->subscriptionId, sub->context,
+                                      (UA_StatusChangeNotification*)msg->content.decoded.data);
+        } else {
+            UA_LOG_WARNING(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                           "Dropped a StatusChangeNotification since no callback is registered");
+        }
+        return;
+    }
+
+    UA_LOG_WARNING(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                   "Unknown notification message type");
+}
+
+void
+UA_Client_Subscriptions_processPublishResponse(UA_Client *client, UA_PublishRequest *request,
+                                               UA_PublishResponse *response) {
+    UA_NotificationMessage *msg = &response->notificationMessage;
+
+    client->currentlyOutStandingPublishRequests--;
+
+    if(response->responseHeader.serviceResult == UA_STATUSCODE_BADTOOMANYPUBLISHREQUESTS) {
+        if(client->config.outStandingPublishRequests > 1) {
+            client->config.outStandingPublishRequests--;
+            UA_LOG_WARNING(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                          "Too many publishrequest, reduce outStandingPublishRequests to %d",
+                           client->config.outStandingPublishRequests);
+        } else {
+            UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                         "Too many publishrequest when outStandingPublishRequests = 1");
+            UA_Client_Subscriptions_deleteSingle(client, response->subscriptionId);
+        }
+        return;
+    }
+
+    if(response->responseHeader.serviceResult == UA_STATUSCODE_BADSHUTDOWN)
+        return;
+
+    if(!LIST_FIRST(&client->subscriptions)) {
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOSUBSCRIPTION;
+        return;
+    }
+
+    if(response->responseHeader.serviceResult == UA_STATUSCODE_BADSESSIONIDINVALID) {
+        UA_Client_close(client); /* TODO: This should be handled before the process callback */
+        UA_LOG_WARNING(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                       "Received BadSessionIdInvalid");
+        return;
+    }
+
+    if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
+        UA_LOG_WARNING(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                       "Received Publish Response with code %s",
+                       UA_StatusCode_name(response->responseHeader.serviceResult));
+        return;
+    }
+
+    UA_Client_Subscription *sub = findSubscription(client, response->subscriptionId);
+    if(!sub) {
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR;
+        UA_LOG_WARNING(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                       "Received Publish Response for a non-existant subscription");
+        return;
+    }
+
+    sub->lastActivity = UA_DateTime_nowMonotonic();
+
+    /* Detect missing message - OPC Unified Architecture, Part 4 5.13.1.1 e) */
+    if((sub->sequenceNumber != msg->sequenceNumber) && (msg->sequenceNumber != 0) &&
+        (UA_Client_Subscriptions_nextSequenceNumber(sub->sequenceNumber) != msg->sequenceNumber)) {
+        UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                     "Invalid subscritpion sequenceNumber");
+        UA_Client_close(client);
+        return;
+    }
+    sub->sequenceNumber = msg->sequenceNumber;
+
+    /* Process the notification messages */
+    for(size_t k = 0; k < msg->notificationDataSize; ++k)
+        processNotificationMessage(client, sub, &msg->notificationData[k]);
+
+    /* Add to the list of pending acks */
+    for(size_t i = 0; i < response->availableSequenceNumbersSize; i++) {
+        if(response->availableSequenceNumbers[i] != msg->sequenceNumber)
+            continue;
+        UA_Client_NotificationsAckNumber *tmpAck = (UA_Client_NotificationsAckNumber*)
+            UA_malloc(sizeof(UA_Client_NotificationsAckNumber));
+        if(!tmpAck) {
+            UA_LOG_WARNING(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                           "Not enough memory to store the acknowledgement for a publish "
+                           "message on subscription %u", sub->subscriptionId);
+            break;
+        }   
+        tmpAck->subAck.sequenceNumber = msg->sequenceNumber;
+        tmpAck->subAck.subscriptionId = sub->subscriptionId;
+        LIST_INSERT_HEAD(&client->pendingNotificationsAcks, tmpAck, listEntry);
+        break;
+    } 
+}
+
+static void
+processPublishResponseAsync(UA_Client *client, void *userdata, UA_UInt32 requestId,
+                            void *response, const UA_DataType *responseType) {
+    UA_PublishRequest *req = (UA_PublishRequest*)userdata;
+    UA_PublishResponse *res = (UA_PublishResponse*)response;
+
+    /* Process the response */
+    UA_Client_Subscriptions_processPublishResponse(client, req, res);
+
+    /* Delete the cached request */
+    UA_PublishRequest_delete(req);
+
+    /* Fill up the outstanding publish requests */
+    UA_Client_Subscriptions_backgroundPublish(client);
+}
+
+void
+UA_Client_Subscriptions_clean(UA_Client *client) {
+    UA_Client_NotificationsAckNumber *n, *tmp;
+    LIST_FOREACH_SAFE(n, &client->pendingNotificationsAcks, listEntry, tmp) {
+        LIST_REMOVE(n, listEntry);
+        UA_free(n);
+    }
+
+    UA_Client_Subscription *sub, *tmps;
+    LIST_FOREACH_SAFE(sub, &client->subscriptions, listEntry, tmps)
+        UA_Client_Subscription_deleteInternal(client, sub); /* force local removal */
+
+    client->monitoredItemHandles = 0;
+}
+
+void
+UA_Client_Subscriptions_backgroundPublishInactivityCheck(UA_Client *client) {
+    if(client->state < UA_CLIENTSTATE_SESSION)
+        return;
+
+    /* Is the lack of responses the client's fault? */
+    if(client->currentlyOutStandingPublishRequests == 0)
+        return;
+
+    UA_Client_Subscription *sub;
+    LIST_FOREACH(sub, &client->subscriptions, listEntry) {
+        UA_DateTime maxSilence = (UA_DateTime)
+            ((sub->publishingInterval * sub->maxKeepAliveCount) +
+             client->config.timeout) * UA_DATETIME_MSEC;
+        if(maxSilence + sub->lastActivity < UA_DateTime_nowMonotonic()) {
+            /* Reset activity */
+            sub->lastActivity = UA_DateTime_nowMonotonic();
+
+            if (client->config.subscriptionInactivityCallback)
+                client->config.subscriptionInactivityCallback(client, sub->subscriptionId, sub->context);
+            UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                         "Inactivity for Subscription %u.", sub->subscriptionId);
+        }
+    }
+}
+
+UA_StatusCode
+UA_Client_Subscriptions_backgroundPublish(UA_Client *client) {
+    if(client->state < UA_CLIENTSTATE_SESSION)
+        return UA_STATUSCODE_BADSERVERNOTCONNECTED;
+
+    /* The session must have at least one subscription */
+    if(!LIST_FIRST(&client->subscriptions))
+        return UA_STATUSCODE_GOOD;
+
+    while(client->currentlyOutStandingPublishRequests < client->config.outStandingPublishRequests) {
+        UA_PublishRequest *request = UA_PublishRequest_new();
+        if (!request)
+            return UA_STATUSCODE_BADOUTOFMEMORY;
+
+        UA_StatusCode retval = UA_Client_preparePublishRequest(client, request);
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_PublishRequest_delete(request);
+            return retval;
+        }
+    
+        UA_UInt32 requestId;
+        client->currentlyOutStandingPublishRequests++;
+        retval = __UA_Client_AsyncService(client, request, &UA_TYPES[UA_TYPES_PUBLISHREQUEST],
+                                          processPublishResponseAsync,
+                                          &UA_TYPES[UA_TYPES_PUBLISHRESPONSE],
+                                          (void*)request, &requestId);
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_PublishRequest_delete(request);
+            return retval;
+        }
+    }
+
+    return UA_STATUSCODE_GOOD;
+}
+
+#endif /* UA_ENABLE_SUBSCRIPTIONS */

+ 378 - 0
src/client/ua_client_subscriptions_deprecated.c

@@ -0,0 +1,378 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2015-2018 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2015 (c) Oleksiy Vasylyev
+ *    Copyright 2016 (c) Sten Grüner
+ *    Copyright 2017-2018 (c) Thomas Stalder, Blue Time Concept SA
+ *    Copyright 2016-2017 (c) Florian Palm
+ *    Copyright 2017 (c) Frank Meerkötter
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ */
+
+#include "ua_client_highlevel.h"
+#include "ua_client_internal.h"
+#include "ua_util.h"
+
+#ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */
+
+const UA_SubscriptionSettings UA_SubscriptionSettings_default = {
+    500.0, /* .requestedPublishingInterval */
+    10000, /* .requestedLifetimeCount */
+    1, /* .requestedMaxKeepAliveCount */
+    0, /* .maxNotificationsPerPublish */
+    true, /* .publishingEnabled */
+    0 /* .priority */
+};
+
+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;
+    request.requestedLifetimeCount = settings.requestedLifetimeCount;
+    request.requestedMaxKeepAliveCount = settings.requestedMaxKeepAliveCount;
+    request.maxNotificationsPerPublish = settings.maxNotificationsPerPublish;
+    request.publishingEnabled = settings.publishingEnabled;
+    request.priority = settings.priority;
+    
+    UA_CreateSubscriptionResponse response =
+        UA_Client_Subscriptions_create(client, request, NULL, NULL, NULL);
+
+    UA_StatusCode retval = response.responseHeader.serviceResult;
+    if(retval == UA_STATUSCODE_GOOD && newSubscriptionId)
+        *newSubscriptionId = response.subscriptionId;
+    
+    UA_CreateSubscriptionResponse_deleteMembers(&response);
+    return retval;
+}
+
+UA_StatusCode
+UA_Client_Subscriptions_remove(UA_Client *client, UA_UInt32 subscriptionId) {
+    UA_DeleteSubscriptionsRequest request;
+    UA_DeleteSubscriptionsRequest_init(&request);
+    request.subscriptionIdsSize = 1;
+    request.subscriptionIds = &subscriptionId;
+
+    UA_DeleteSubscriptionsResponse response =
+        UA_Client_Subscriptions_delete(client, request);
+
+    UA_StatusCode retval = response.responseHeader.serviceResult;
+    if(retval == UA_STATUSCODE_GOOD) {
+        if(response.resultsSize != 1)
+            retval = UA_STATUSCODE_BADINTERNALERROR;
+    }
+
+    if(retval == UA_STATUSCODE_GOOD)
+        retval = response.results[0];
+
+    UA_DeleteSubscriptionsResponse_deleteMembers(&response);
+    return retval;
+}
+
+UA_StatusCode
+UA_Client_Subscriptions_manuallySendPublishRequest(UA_Client *client) {
+    if(client->state < UA_CLIENTSTATE_SESSION)
+        return UA_STATUSCODE_BADSERVERNOTCONNECTED;
+
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+
+    UA_DateTime now = UA_DateTime_nowMonotonic();
+    UA_DateTime maxDate = now + (UA_DateTime)(client->config.timeout * UA_DATETIME_MSEC);
+
+    UA_Boolean moreNotifications = true;
+    while(moreNotifications) {
+        UA_PublishRequest request;
+        UA_PublishRequest_init(&request);
+        retval = UA_Client_preparePublishRequest(client, &request);
+        if(retval != UA_STATUSCODE_GOOD)
+            return retval;
+
+        /* Manually increase the number of sent publish requests. Otherwise we
+         * send out one too many when we process async responses when we wait
+         * for the correct publish response. The
+         * currentlyOutStandingPublishRequests will be reduced during processing
+         * of the response. */
+        client->currentlyOutStandingPublishRequests++;
+
+        UA_PublishResponse response;
+        __UA_Client_Service(client,
+                            &request, &UA_TYPES[UA_TYPES_PUBLISHREQUEST],
+                            &response, &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
+        UA_Client_Subscriptions_processPublishResponse(client, &request, &response);
+        UA_PublishRequest_deleteMembers(&request);
+        
+        now = UA_DateTime_nowMonotonic();
+        if(now > maxDate) {
+            moreNotifications = UA_FALSE;
+            retval = UA_STATUSCODE_GOODNONCRITICALTIMEOUT;
+        } else {
+            moreNotifications = response.moreNotifications;
+        }
+
+        UA_PublishResponse_deleteMembers(&response);
+        UA_PublishRequest_deleteMembers(&request);
+    }
+
+    if(client->state < UA_CLIENTSTATE_SESSION)
+        return UA_STATUSCODE_BADSERVERNOTCONNECTED;
+
+    return retval;
+}
+
+/* Callbacks for the MonitoredItems. The callbacks for the deprecated API are
+ * wrapped. The wrapper is cleaned up upon destruction. */
+
+typedef struct {
+    UA_MonitoredItemHandlingFunction origCallback;
+    void *context;
+} dataChangeCallbackWrapper;
+
+static void
+dataChangeCallback(UA_Client *client, UA_UInt32 subId, void *subContext,
+                   UA_UInt32 monId, void *monContext, UA_DataValue *value) {
+    dataChangeCallbackWrapper *wrapper = (dataChangeCallbackWrapper*)monContext;
+    wrapper->origCallback(client, monId, value, wrapper->context);
+}
+
+typedef struct {
+    UA_MonitoredEventHandlingFunction origCallback;
+    void *context;
+} eventCallbackWrapper;
+
+static void
+eventCallback(UA_Client *client, UA_UInt32 subId, void *subContext,
+              UA_UInt32 monId, void *monContext, size_t nEventFields,
+              UA_Variant *eventFields) {
+    eventCallbackWrapper *wrapper = (eventCallbackWrapper*)monContext;
+    wrapper->origCallback(client, monId, nEventFields, eventFields, wrapper->context);
+}
+
+static void
+deleteMonitoredItemCallback(UA_Client *client, UA_UInt32 subId, void *subContext,
+                            UA_UInt32 monId, void *monContext) {
+    UA_free(monContext);
+}
+
+static UA_StatusCode
+addMonitoredItems(UA_Client *client, const UA_UInt32 subscriptionId,
+                  UA_MonitoredItemCreateRequest *items, size_t itemsSize,
+                  UA_MonitoredItemHandlingFunction *hfs, void **hfContexts,
+                  UA_StatusCode *itemResults, UA_UInt32 *newMonitoredItemIds) {
+    /* Create array of wrappers and callbacks */
+    UA_STACKARRAY(dataChangeCallbackWrapper*, wrappers, itemsSize);
+    UA_STACKARRAY(UA_Client_DeleteMonitoredItemCallback, deleteCbs, itemsSize);
+    UA_STACKARRAY(UA_Client_DataChangeNotificationCallback, wrapperCbs, itemsSize);
+
+    for(size_t i = 0; i < itemsSize; i++) {
+        wrappers[i] = (dataChangeCallbackWrapper*)UA_malloc(sizeof(dataChangeCallbackWrapper));
+        if(!wrappers[i]) {
+            for(size_t j = 0; j < i; j++)
+                UA_free(wrappers[j]);
+            return UA_STATUSCODE_BADOUTOFMEMORY;
+        }
+        wrappers[i]->origCallback = (UA_MonitoredItemHandlingFunction)(uintptr_t)hfs[i];
+        wrappers[i]->context = hfContexts[i];
+
+        deleteCbs[i] = deleteMonitoredItemCallback;
+        wrapperCbs[i] = dataChangeCallback;
+    }
+
+    /* Prepare the request */
+    UA_CreateMonitoredItemsRequest request;
+    UA_CreateMonitoredItemsRequest_init(&request);
+    request.subscriptionId = subscriptionId;
+    request.itemsToCreateSize = itemsSize;
+    request.itemsToCreate = items;
+
+    /* Process and return */
+    UA_CreateMonitoredItemsResponse response =
+        UA_Client_MonitoredItems_createDataChanges(client, request, (void**)wrappers,
+                                                   wrapperCbs, deleteCbs);
+
+    UA_StatusCode retval = response.responseHeader.serviceResult;
+    if(retval == UA_STATUSCODE_GOOD && response.resultsSize != itemsSize)
+        retval = UA_STATUSCODE_BADINTERNALERROR;
+
+    if(retval == UA_STATUSCODE_GOOD) {
+        for(size_t i = 0; i < itemsSize; i++) {
+            itemResults[i] = response.results[i].statusCode;
+            newMonitoredItemIds[i] = response.results[i].monitoredItemId;
+        }
+    }
+
+    UA_CreateMonitoredItemsResponse_deleteMembers(&response);
+    return retval;
+}
+
+UA_StatusCode
+UA_Client_Subscriptions_addMonitoredItems(UA_Client *client, const UA_UInt32 subscriptionId,
+                                          UA_MonitoredItemCreateRequest *items, size_t itemsSize,
+                                          UA_MonitoredItemHandlingFunction *hfs,
+                                          void **hfContexts, UA_StatusCode *itemResults,
+                                          UA_UInt32 *newMonitoredItemIds) {
+    return addMonitoredItems(client, subscriptionId, items, itemsSize, hfs, hfContexts, itemResults,
+                             newMonitoredItemIds);
+}
+
+UA_StatusCode
+UA_Client_Subscriptions_addMonitoredItem(UA_Client *client, UA_UInt32 subscriptionId,
+                                         UA_NodeId nodeId, UA_UInt32 attributeID,
+                                         UA_MonitoredItemHandlingFunction hf, void *hfContext,
+                                         UA_UInt32 *newMonitoredItemId, UA_Double samplingInterval) {
+    UA_MonitoredItemCreateRequest item;
+    UA_MonitoredItemCreateRequest_init(&item);
+    item.itemToMonitor.nodeId = nodeId;
+    item.itemToMonitor.attributeId = attributeID;
+    item.monitoringMode = UA_MONITORINGMODE_REPORTING;
+    item.requestedParameters.samplingInterval = samplingInterval;
+    item.requestedParameters.discardOldest = true;
+    item.requestedParameters.queueSize = 1;
+
+    UA_StatusCode retval_item = UA_STATUSCODE_GOOD;
+    UA_StatusCode retval =
+        addMonitoredItems(client, subscriptionId, &item, 1,
+                          (UA_MonitoredItemHandlingFunction*)(uintptr_t)&hf,
+                          &hfContext, &retval_item, newMonitoredItemId);
+    return retval | retval_item;
+}
+
+static UA_StatusCode
+addMonitoredEvents(UA_Client *client, const UA_UInt32 subscriptionId,
+                   UA_MonitoredItemCreateRequest *items, size_t itemsSize,
+                   UA_MonitoredEventHandlingFunction *hfs,
+                   void **hfContexts, UA_StatusCode *itemResults,
+                   UA_UInt32 *newMonitoredItemIds) {
+    /* Create array of wrappers and callbacks */
+    UA_STACKARRAY(eventCallbackWrapper*, wrappers, itemsSize);
+    UA_STACKARRAY(UA_Client_DeleteMonitoredItemCallback, deleteCbs, itemsSize);
+    UA_STACKARRAY(UA_Client_EventNotificationCallback, wrapperCbs, itemsSize);
+
+    for(size_t i = 0; i < itemsSize; i++) {
+        wrappers[i] = (eventCallbackWrapper*)UA_malloc(sizeof(eventCallbackWrapper));
+        if(!wrappers[i]) {
+            for(size_t j = 0; j < i; j++)
+                UA_free(wrappers[j]);
+            return UA_STATUSCODE_BADOUTOFMEMORY;
+        }
+        wrappers[i]->origCallback = (UA_MonitoredEventHandlingFunction)(uintptr_t)hfs[i];
+        wrappers[i]->context = hfContexts[i];
+
+        deleteCbs[i] = deleteMonitoredItemCallback;
+        wrapperCbs[i] = eventCallback;
+    }
+
+    /* Prepare the request */
+    UA_CreateMonitoredItemsRequest request;
+    UA_CreateMonitoredItemsRequest_init(&request);
+    request.subscriptionId = subscriptionId;
+    request.itemsToCreateSize = itemsSize;
+    request.itemsToCreate = items;
+
+    /* Process and return */
+    UA_CreateMonitoredItemsResponse response =
+        UA_Client_MonitoredItems_createEvents(client, request, (void**)wrappers,
+                                              wrapperCbs, deleteCbs);
+
+    UA_StatusCode retval = response.responseHeader.serviceResult;
+    if(retval == UA_STATUSCODE_GOOD && response.resultsSize != itemsSize)
+        retval = UA_STATUSCODE_BADINTERNALERROR;
+
+    if(retval == UA_STATUSCODE_GOOD) {
+        for(size_t i = 0; i < itemsSize; i++)
+            itemResults[i] = response.results[i].statusCode;
+    }
+
+    UA_CreateMonitoredItemsResponse_deleteMembers(&response);
+    return retval;
+}
+
+UA_StatusCode
+UA_Client_Subscriptions_addMonitoredEvents(UA_Client *client, const UA_UInt32 subscriptionId,
+                                           UA_MonitoredItemCreateRequest *items, size_t itemsSize,
+                                           UA_MonitoredEventHandlingFunction *hfs,
+                                           void **hfContexts, UA_StatusCode *itemResults,
+                                           UA_UInt32 *newMonitoredItemIds) {
+    return addMonitoredEvents(client, subscriptionId, items, itemsSize, hfs,
+                              hfContexts, itemResults, newMonitoredItemIds);
+}
+
+UA_StatusCode
+UA_Client_Subscriptions_addMonitoredEvent(UA_Client *client, UA_UInt32 subscriptionId,
+                                          const UA_NodeId nodeId, UA_UInt32 attributeID,
+                                          const UA_SimpleAttributeOperand *selectClauses,
+                                          size_t selectClausesSize,
+                                          const UA_ContentFilterElement *whereClauses,
+                                          size_t whereClausesSize,
+                                          const UA_MonitoredEventHandlingFunction hf,
+                                          void *hfContext, UA_UInt32 *newMonitoredItemId) {
+    UA_MonitoredItemCreateRequest item;
+    UA_MonitoredItemCreateRequest_init(&item);
+    item.itemToMonitor.nodeId = nodeId;
+    item.itemToMonitor.attributeId = attributeID;
+    item.monitoringMode = UA_MONITORINGMODE_REPORTING;
+    item.requestedParameters.samplingInterval = 0;
+    item.requestedParameters.discardOldest = false;
+
+    UA_EventFilter *evFilter = UA_EventFilter_new();
+    if(!evFilter)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    UA_EventFilter_init(evFilter);
+    evFilter->selectClausesSize = selectClausesSize;
+    evFilter->selectClauses = (UA_SimpleAttributeOperand*)(uintptr_t)selectClauses;
+    evFilter->whereClause.elementsSize = whereClausesSize;
+    evFilter->whereClause.elements = (UA_ContentFilterElement*)(uintptr_t)whereClauses;
+
+    item.requestedParameters.filter.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
+    item.requestedParameters.filter.content.decoded.type = &UA_TYPES[UA_TYPES_EVENTFILTER];
+    item.requestedParameters.filter.content.decoded.data = evFilter;
+    UA_StatusCode retval_item = UA_STATUSCODE_GOOD;
+    UA_StatusCode retval = addMonitoredEvents(client, subscriptionId, &item, 1,
+                                             (UA_MonitoredEventHandlingFunction*)(uintptr_t)&hf,
+                                              &hfContext, &retval_item, newMonitoredItemId);
+    UA_free(evFilter);
+    return retval | retval_item;
+}
+
+static UA_StatusCode
+removeMonitoredItems(UA_Client *client, UA_UInt32 subscriptionId,
+                     UA_UInt32 *monitoredItemIds, size_t itemsSize,
+                     UA_StatusCode *itemResults) {
+    UA_DeleteMonitoredItemsRequest request;
+    UA_DeleteMonitoredItemsRequest_init(&request);
+    request.subscriptionId = subscriptionId;
+    request.monitoredItemIdsSize = itemsSize;
+    request.monitoredItemIds = monitoredItemIds;
+
+    UA_DeleteMonitoredItemsResponse response = UA_Client_MonitoredItems_delete(client, request);
+    UA_StatusCode retval = response.responseHeader.serviceResult;
+    if(retval == UA_STATUSCODE_GOOD) {
+        if(response.resultsSize != itemsSize) {
+            retval = UA_STATUSCODE_BADINTERNALERROR;
+        } else {
+            for(size_t i = 0; i < itemsSize; i++)
+                itemResults[i] = response.results[i];
+        }
+    }
+    UA_DeleteMonitoredItemsResponse_deleteMembers(&response);
+    return retval;
+}
+
+UA_StatusCode
+UA_Client_Subscriptions_removeMonitoredItems(UA_Client *client, UA_UInt32 subscriptionId,
+                                             UA_UInt32 *monitoredItemIds, size_t itemsSize,
+                                             UA_StatusCode *itemResults) {
+    return removeMonitoredItems(client, subscriptionId, monitoredItemIds, itemsSize, itemResults);
+}
+
+UA_StatusCode
+UA_Client_Subscriptions_removeMonitoredItem(UA_Client *client, UA_UInt32 subscriptionId,
+                                            UA_UInt32 monitoredItemId) {
+    UA_StatusCode retval_item = UA_STATUSCODE_GOOD;
+    UA_StatusCode retval = removeMonitoredItems(client, subscriptionId, &monitoredItemId, 1, &retval_item);
+    return retval | retval_item;
+}
+
+#endif /* UA_ENABLE_SUBSCRIPTIONS */

+ 23 - 19
src/server/ua_mdns.c

@@ -1,6 +1,10 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ *    Copyright 2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ */
 
 /* Enable POSIX features */
 #if !defined(_XOPEN_SOURCE) && !defined(_WRS_KERNEL)
@@ -78,14 +82,14 @@ mdns_record_add_or_get(UA_Server *server, const char *record, const char *server
     int hashIdx = mdns_hash_record(record) % SERVER_ON_NETWORK_HASH_PRIME;
     struct serverOnNetwork_hash_entry *hash_entry = server->serverOnNetworkHash[hashIdx];
 
-    while (hash_entry) {
+    while(hash_entry) {
         size_t maxLen;
-        if (serverNameLen > hash_entry->entry->serverOnNetwork.serverName.length)
+        if(serverNameLen > hash_entry->entry->serverOnNetwork.serverName.length)
             maxLen = hash_entry->entry->serverOnNetwork.serverName.length;
         else
             maxLen = serverNameLen;
 
-        if (strncmp((char *) hash_entry->entry->serverOnNetwork.serverName.data, serverName, maxLen) == 0)
+        if(strncmp((char *) hash_entry->entry->serverOnNetwork.serverName.data, serverName, maxLen) == 0)
             return hash_entry->entry;
         hash_entry = hash_entry->next;
     }
@@ -107,8 +111,8 @@ mdns_record_add_or_get(UA_Server *server, const char *record, const char *server
     // todo: malloc may fail: return a statuscode
     listEntry->serverOnNetwork.serverName.data = (UA_Byte*)UA_malloc(serverNameLen);
     memcpy(listEntry->serverOnNetwork.serverName.data, serverName, serverNameLen);
-    server->serverOnNetworkRecordIdCounter = UA_atomic_add(&server->serverOnNetworkRecordIdCounter, 1);
-    if (server->serverOnNetworkRecordIdCounter == 0)
+    UA_atomic_addUInt32(&server->serverOnNetworkRecordIdCounter, 1);
+    if(server->serverOnNetworkRecordIdCounter == 0)
         server->serverOnNetworkRecordIdLastReset = UA_DateTime_now();
 
     // add to hash
@@ -165,7 +169,7 @@ mdns_record_remove(UA_Server *server, const char *record,
     server->serverOnNetworkSize--;
     UA_free(entry);
 #else
-    server->serverOnNetworkSize = uatomic_add_return(&server->serverOnNetworkSize, -1);
+    UA_atomic_subSize(&server->serverOnNetworkSize, 1);
     UA_Server_delayedCallback(server, delayedFree, entry);
 #endif
 }
@@ -190,7 +194,7 @@ setTxt(const struct resource *r,
     char *caps = (char *) xht_get(x, "caps");
 
     if(path && strlen(path) > 1) {
-        if (!entry->srvSet) {
+        if(!entry->srvSet) {
             /* txt arrived before SRV, thus cache path entry */
             // todo: malloc in strdup may fail: return a statuscode
             entry->pathTmp = UA_STRDUP(path);
@@ -220,7 +224,7 @@ setTxt(const struct resource *r,
             // todo: malloc may fail: return a statuscode
             entry->serverOnNetwork.serverCapabilities[i].data = (UA_Byte*)UA_malloc(len);
             memcpy(entry->serverOnNetwork.serverCapabilities[i].data, caps, len);
-            if (nextStr)
+            if(nextStr)
                 caps = nextStr + 1;
             else
                 break;
@@ -324,11 +328,11 @@ void mdns_create_txt(UA_Server *server, const char *fullServiceDomain, const cha
                                     600, conflict, server);
     xht_t *h = xht_new(11);
     char *allocPath = NULL;
-    if (!path || strlen(path) == 0) {
+    if(!path || strlen(path) == 0) {
         xht_set(h, "path", "/");
     } else {
         // path does not contain slash, so add it here
-        if (path[0] == '/')
+        if(path[0] == '/')
             // todo: malloc in strdup may fail: return a statuscode
             allocPath = UA_STRDUP(path);
         else {
@@ -343,7 +347,7 @@ void mdns_create_txt(UA_Server *server, const char *fullServiceDomain, const cha
 
     // calculate max string length:
     size_t capsLen = 0;
-    for (size_t i = 0; i < *capabilitiesSize; i++) {
+    for(size_t i = 0; i < *capabilitiesSize; i++) {
         // add comma or last \0
         capsLen += capabilites[i].length + 1;
     }
@@ -354,7 +358,7 @@ void mdns_create_txt(UA_Server *server, const char *fullServiceDomain, const cha
         // todo: malloc may fail: return a statuscode
         caps = (char*)UA_malloc(sizeof(char) * capsLen);
         size_t idx = 0;
-        for (size_t i = 0; i < *capabilitiesSize; i++) {
+        for(size_t i = 0; i < *capabilitiesSize; i++) {
             memcpy(caps + idx, (const char *) capabilites[i].data, capabilites[i].length);
             idx += capabilites[i].length + 1;
             caps[idx - 1] = ',';
@@ -401,7 +405,7 @@ mdns_set_address_record_if(UA_Server *server, const char *fullServiceDomain,
     // [servername]-[hostname]._opcua-tcp._tcp.local. A [ip].
     mdns_record_t *r = mdnsd_shared(server->mdnsDaemon, fullServiceDomain, QTYPE_A, 600);
     mdnsd_set_raw(server->mdnsDaemon, r, addr, addr_len);
-    
+
     // [hostname]. A [ip].
     r = mdnsd_shared(server->mdnsDaemon, localDomain, QTYPE_A, 600);
     mdnsd_set_raw(server->mdnsDaemon, r, addr, addr_len);
@@ -421,7 +425,7 @@ getInterfaces(UA_Server *server) {
     for(size_t attempts = 0; attempts != 3; ++attempts) {
         // todo: malloc may fail: return a statuscode
         adapter_addresses = (IP_ADAPTER_ADDRESSES*)UA_malloc(adapter_addresses_buffer_size);
-        if (!adapter_addresses) {
+        if(!adapter_addresses) {
             UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
                          "GetAdaptersAddresses out of memory");
             adapter_addresses = NULL;
@@ -442,7 +446,7 @@ getInterfaces(UA_Server *server) {
             adapter_addresses = NULL;
             continue;
         }
-        
+
         /* Unexpected error */
         UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
                      "GetAdaptersAddresses returned an unexpected error. "
@@ -457,7 +461,7 @@ getInterfaces(UA_Server *server) {
 void mdns_set_address_record(UA_Server *server, const char *fullServiceDomain,
                              const char *localDomain) {
     IP_ADAPTER_ADDRESSES* adapter_addresses = getInterfaces(server);
-    if (!adapter_addresses)
+    if(!adapter_addresses)
         return;
 
     /* Iterate through all of the adapters */
@@ -476,7 +480,7 @@ void mdns_set_address_record(UA_Server *server, const char *fullServiceDomain,
                 mdns_set_address_record_if(server, fullServiceDomain, localDomain,
                                            (char *)&ipv4->sin_addr, 4);
             }
-            /*else if (AF_INET6 == family) {
+            /*else if(AF_INET6 == family) {
             // IPv6
             SOCKADDR_IN6* ipv6 = (SOCKADDR_IN6*)(address->Address.lpSockaddr);
 
@@ -491,7 +495,7 @@ void mdns_set_address_record(UA_Server *server, const char *fullServiceDomain,
 
             if(0 == ipv6_str.find("fe")) {
             char c = ipv6_str[2];
-            if (c == '8' || c == '9' || c == 'a' || c == 'b')
+            if(c == '8' || c == '9' || c == 'a' || c == 'b')
             is_link_local = true;
             } else if (0 == ipv6_str.find("2001:0:")) {
             is_special_use = true;

+ 4 - 1
src/server/ua_mdns_internal.h

@@ -1,6 +1,9 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ */
 
 #ifndef UA_MDNS_INTERNAL_H
 #define UA_MDNS_INTERNAL_H

+ 38 - 13
src/server/ua_nodes.c

@@ -1,6 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2015-2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2015-2016 (c) Sten Grüner
+ *    Copyright 2015 (c) Chris Iatrou
+ *    Copyright 2015, 2017 (c) Florian Palm
+ *    Copyright 2015 (c) Oleksiy Vasylyev
+ *    Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH
+ *    Copyright 2017 (c) Julian Grothoff
+ */
 
 #include "ua_server_internal.h"
 #include "ua_types_encoding_binary.h"
@@ -254,7 +263,7 @@ UA_Node_copy_alloc(const UA_Node *src) {
     dst->nodeClass = src->nodeClass;
 
     UA_StatusCode retval = UA_Node_copy(src, dst);
-    if (retval != UA_STATUSCODE_GOOD){
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_free(dst);
         return NULL;
     }
@@ -296,24 +305,30 @@ copyCommonVariableAttributes(UA_VariableNode *node,
     /* if we have an extension object which is still encoded (e.g. from the nodeset compiler)
      * we need to decode it and set the decoded value instead of the encoded object */
     UA_Boolean valueSet = false;
-    if (attr->value.type != NULL && UA_NodeId_equal(&attr->value.type->typeId, &extensionObject)) {
+    if(attr->value.type != NULL && UA_NodeId_equal(&attr->value.type->typeId, &extensionObject)) {
+
+        if (attr->value.data == UA_EMPTY_ARRAY_SENTINEL) {
+            /* do nothing since we got an empty array of extension objects */
+            return UA_STATUSCODE_GOOD;
+        }
+
         const UA_ExtensionObject *obj = (const UA_ExtensionObject *)attr->value.data;
-        if (obj->encoding == UA_EXTENSIONOBJECT_ENCODED_BYTESTRING) {
+        if(obj && obj->encoding == UA_EXTENSIONOBJECT_ENCODED_BYTESTRING) {
 
             /* TODO: Once we generate type description in the nodeset compiler,
              * UA_findDatatypeByBinary can be made internal to the decoding
              * layer. */
             const UA_DataType *type = UA_findDataTypeByBinary(&obj->content.encoded.typeId);
 
-            if (type) {
+            if(type) {
                 void *dst = UA_Array_new(attr->value.arrayLength, type);
                 uint8_t *tmpPos = (uint8_t *)dst;
 
-                for (size_t i=0; i<attr->value.arrayLength; i++) {
+                for(size_t i=0; i<attr->value.arrayLength; i++) {
                     size_t offset =0;
                     const UA_ExtensionObject *curr = &((const UA_ExtensionObject *)attr->value.data)[i];
                     UA_StatusCode ret = UA_decodeBinary(&curr->content.encoded.body, &offset, tmpPos, type, 0, NULL);
-                    if (ret != UA_STATUSCODE_GOOD) {
+                    if(ret != UA_STATUSCODE_GOOD) {
                         return ret;
                     }
                     tmpPos += type->memSize;
@@ -325,7 +340,7 @@ copyCommonVariableAttributes(UA_VariableNode *node,
         }
     }
 
-    if (!valueSet)
+    if(!valueSet)
         retval |= UA_Variant_copy(&attr->value, &node->value.data.value.value);
 
     node->value.data.value.hasValue = true;
@@ -512,13 +527,23 @@ addReferenceKind(UA_Node *node, const UA_AddReferencesItem *item) {
 
 UA_StatusCode
 UA_Node_addReference(UA_Node *node, const UA_AddReferencesItem *item) {
+    UA_NodeReferenceKind *existingRefs = NULL;
     for(size_t i = 0; i < node->referencesSize; ++i) {
         UA_NodeReferenceKind *refs = &node->references[i];
-        if(refs->isInverse == item->isForward)
-            continue;
-        if(!UA_NodeId_equal(&refs->referenceTypeId, &item->referenceTypeId))
-            continue;
-        return addReferenceTarget(refs, &item->targetNodeId);
+        if(refs->isInverse != item->isForward
+                && UA_NodeId_equal(&refs->referenceTypeId, &item->referenceTypeId)) {
+            existingRefs = refs;
+            break;
+        }
+    }
+    if(existingRefs != NULL) {
+        for(size_t i = 0; i < existingRefs->targetIdsSize; i++) {
+            if(UA_ExpandedNodeId_equal(&existingRefs->targetIds[i],
+                                       &item->targetNodeId)) {
+                return UA_STATUSCODE_BADDUPLICATEREFERENCENOTALLOWED;
+            }
+        }
+        return addReferenceTarget(existingRefs, &item->targetNodeId);
     }
     return addReferenceKind(node, item);
 }

+ 57 - 44
src/server/ua_securechannel_manager.c

@@ -1,6 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2014-2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2014-2017 (c) Florian Palm
+ *    Copyright 2015-2016 (c) Sten Grüner
+ *    Copyright 2015 (c) Oleksiy Vasylyev
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ *    Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB
+ */
 
 #include "ua_securechannel_manager.h"
 #include "ua_session.h"
@@ -11,7 +19,7 @@
 #define STARTTOKENID 1
 
 UA_StatusCode
-UA_SecureChannelManager_init(UA_SecureChannelManager* cm, UA_Server* server) {
+UA_SecureChannelManager_init(UA_SecureChannelManager *cm, UA_Server *server) {
     LIST_INIT(&cm->channels);
     // TODO: use an ID that is likely to be unique after a restart
     cm->lastChannelId = STARTCHANNELID;
@@ -21,7 +29,8 @@ UA_SecureChannelManager_init(UA_SecureChannelManager* cm, UA_Server* server) {
     return UA_STATUSCODE_GOOD;
 }
 
-void UA_SecureChannelManager_deleteMembers(UA_SecureChannelManager* cm) {
+void
+UA_SecureChannelManager_deleteMembers(UA_SecureChannelManager *cm) {
     channel_list_entry *entry, *temp;
     LIST_FOREACH_SAFE(entry, &cm->channels, pointers, temp) {
         LIST_REMOVE(entry, pointers);
@@ -32,7 +41,7 @@ void UA_SecureChannelManager_deleteMembers(UA_SecureChannelManager* cm) {
 
 static void
 removeSecureChannelCallback(UA_Server *server, void *entry) {
-    channel_list_entry *centry = (channel_list_entry*)entry;
+    channel_list_entry *centry = (channel_list_entry *)entry;
     UA_SecureChannel_deleteMembersCleanup(&centry->channel);
     UA_free(entry);
 }
@@ -51,7 +60,7 @@ removeSecureChannel(UA_SecureChannelManager *cm, channel_list_entry *entry) {
 
     /* Detach the channel and make the capacity available */
     LIST_REMOVE(entry, pointers);
-    UA_atomic_add(&cm->currentChannelCount, (UA_UInt32)-1);
+    UA_atomic_subUInt32(&cm->currentChannelCount, 1);
     return UA_STATUSCODE_GOOD;
 }
 
@@ -61,7 +70,7 @@ UA_SecureChannelManager_cleanupTimedOut(UA_SecureChannelManager *cm, UA_DateTime
     channel_list_entry *entry, *temp;
     LIST_FOREACH_SAFE(entry, &cm->channels, pointers, temp) {
         UA_DateTime timeout = entry->channel.securityToken.createdAt +
-            (UA_DateTime)(entry->channel.securityToken.revisedLifetime * UA_MSEC_TO_DATETIME);
+                              (UA_DateTime)(entry->channel.securityToken.revisedLifetime * UA_DATETIME_MSEC);
         if(timeout < nowMonotonic || !entry->channel.connection) {
             UA_LOG_INFO_CHANNEL(cm->server->config.logger, &entry->channel,
                                 "SecureChannel has timed out");
@@ -73,8 +82,9 @@ UA_SecureChannelManager_cleanupTimedOut(UA_SecureChannelManager *cm, UA_DateTime
 }
 
 /* remove the first channel that has no session attached */
-static UA_Boolean purgeFirstChannelWithoutSession(UA_SecureChannelManager* cm) {
-    channel_list_entry* entry;
+static UA_Boolean
+purgeFirstChannelWithoutSession(UA_SecureChannelManager *cm) {
+    channel_list_entry *entry;
     LIST_FOREACH(entry, &cm->channels, pointers) {
         if(LIST_EMPTY(&(entry->channel.sessions))) {
             UA_LOG_DEBUG_CHANNEL(cm->server->config.logger, &entry->channel,
@@ -106,7 +116,7 @@ UA_SecureChannelManager_create(UA_SecureChannelManager *const cm, UA_Connection
     UA_LOG_INFO(cm->server->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
                 "Creating a new SecureChannel");
 
-    channel_list_entry* entry = (channel_list_entry*)UA_malloc(sizeof(channel_list_entry));
+    channel_list_entry *entry = (channel_list_entry *)UA_malloc(sizeof(channel_list_entry));
     if(!entry)
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
@@ -126,15 +136,15 @@ UA_SecureChannelManager_create(UA_SecureChannelManager *const cm, UA_Connection
     entry->channel.securityToken.revisedLifetime = cm->server->config.maxSecurityTokenLifetime;
 
     LIST_INSERT_HEAD(&cm->channels, entry, pointers);
-    UA_atomic_add(&cm->currentChannelCount, 1);
+    UA_atomic_addUInt32(&cm->currentChannelCount, 1);
     UA_Connection_attachSecureChannel(connection, &entry->channel);
     return UA_STATUSCODE_GOOD;
 }
 
 UA_StatusCode
-UA_SecureChannelManager_open(UA_SecureChannelManager* cm, UA_SecureChannel *channel,
-                             const UA_OpenSecureChannelRequest* request,
-                             UA_OpenSecureChannelResponse* response) {
+UA_SecureChannelManager_open(UA_SecureChannelManager *cm, UA_SecureChannel *channel,
+                             const UA_OpenSecureChannelRequest *request,
+                             UA_OpenSecureChannelResponse *response) {
     if(channel->state != UA_SECURECHANNELSTATE_FRESH) {
         UA_LOG_ERROR_CHANNEL(cm->server->config.logger, channel,
                              "Called open on already open or closed channel");
@@ -146,40 +156,43 @@ UA_SecureChannelManager_open(UA_SecureChannelManager* cm, UA_SecureChannel *chan
         return UA_STATUSCODE_BADSECURITYMODEREJECTED;
     }
 
+    channel->securityMode = request->securityMode;
+    channel->securityToken.createdAt = UA_DateTime_nowMonotonic();
     channel->securityToken.channelId = cm->lastChannelId++;
     channel->securityToken.createdAt = UA_DateTime_now();
+
+    /* Set the lifetime. Lifetime 0 -> set the maximum possible */
     channel->securityToken.revisedLifetime =
         (request->requestedLifetime > cm->server->config.maxSecurityTokenLifetime) ?
         cm->server->config.maxSecurityTokenLifetime : request->requestedLifetime;
-    if(channel->securityToken.revisedLifetime == 0) // lifetime 0 -> set the maximum possible
+    if(channel->securityToken.revisedLifetime == 0)
         channel->securityToken.revisedLifetime = cm->server->config.maxSecurityTokenLifetime;
-    UA_ByteString_copy(&request->clientNonce, &channel->remoteNonce);
-    channel->securityMode = request->securityMode;
-    const size_t keyLength = channel->securityPolicy->symmetricModule.cryptoModule.
-        getLocalEncryptionKeyLength(channel->securityPolicy, channel->channelContext);
-    UA_SecureChannel_generateNonce(channel,
-                                   keyLength,
-                                   &channel->localNonce);
 
-    UA_SecureChannel_generateNewKeys(channel);
+    /* Set the nonces and generate the keys */
+    UA_StatusCode retval = UA_ByteString_copy(&request->clientNonce, &channel->remoteNonce);
+    retval |= UA_SecureChannel_generateLocalNonce(channel);
+    retval |= UA_SecureChannel_generateNewKeys(channel);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
 
-    // Set the response
-    UA_ByteString_copy(&channel->localNonce, &response->serverNonce);
-    UA_ChannelSecurityToken_copy(&channel->securityToken, &response->securityToken);
+    /* Set the response */
+    retval = UA_ByteString_copy(&channel->localNonce, &response->serverNonce);
+    retval |= UA_ChannelSecurityToken_copy(&channel->securityToken, &response->securityToken);
     response->responseHeader.timestamp = UA_DateTime_now();
     response->responseHeader.requestHandle = request->requestHeader.requestHandle;
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
 
-    // Now overwrite the creation date with the internal monotonic clock
-    channel->securityToken.createdAt = UA_DateTime_nowMonotonic();
-
+    /* The channel is open */
     channel->state = UA_SECURECHANNELSTATE_OPEN;
+
     return UA_STATUSCODE_GOOD;
 }
 
 UA_StatusCode
-UA_SecureChannelManager_renew(UA_SecureChannelManager* cm, UA_SecureChannel *channel,
-                              const UA_OpenSecureChannelRequest* request,
-                              UA_OpenSecureChannelResponse* response) {
+UA_SecureChannelManager_renew(UA_SecureChannelManager *cm, UA_SecureChannel *channel,
+                              const UA_OpenSecureChannelRequest *request,
+                              UA_OpenSecureChannelResponse *response) {
     if(channel->state != UA_SECURECHANNELSTATE_OPEN) {
         UA_LOG_ERROR_CHANNEL(cm->server->config.logger, channel,
                              "Called renew on channel which is not open");
@@ -200,26 +213,26 @@ UA_SecureChannelManager_renew(UA_SecureChannelManager* cm, UA_SecureChannel *cha
 
     /* Replace the nonces */
     UA_ByteString_deleteMembers(&channel->remoteNonce);
-    UA_ByteString_copy(&request->clientNonce, &channel->remoteNonce);
-
-    const size_t keyLength = channel->securityPolicy->symmetricModule.cryptoModule.
-        getLocalEncryptionKeyLength(channel->securityPolicy, channel->channelContext);
-    UA_ByteString_deleteMembers(&channel->localNonce);
-    UA_SecureChannel_generateNonce(channel, keyLength, &channel->localNonce);
+    UA_StatusCode retval = UA_ByteString_copy(&request->clientNonce, &channel->remoteNonce);
+    retval |= UA_SecureChannel_generateLocalNonce(channel);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
 
     /* Set the response */
     response->responseHeader.requestHandle = request->requestHeader.requestHandle;
-    UA_ByteString_copy(&channel->localNonce, &response->serverNonce);
-    UA_ChannelSecurityToken_copy(&channel->nextSecurityToken, &response->securityToken);
+    retval = UA_ByteString_copy(&channel->localNonce, &response->serverNonce);
+    retval |= UA_ChannelSecurityToken_copy(&channel->nextSecurityToken, &response->securityToken);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
 
     /* Reset the internal creation date to the monotonic clock */
     channel->nextSecurityToken.createdAt = UA_DateTime_nowMonotonic();
     return UA_STATUSCODE_GOOD;
 }
 
-UA_SecureChannel*
-UA_SecureChannelManager_get(UA_SecureChannelManager* cm, UA_UInt32 channelId) {
-    channel_list_entry* entry;
+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)
             return &entry->channel;
@@ -228,8 +241,8 @@ UA_SecureChannelManager_get(UA_SecureChannelManager* cm, UA_UInt32 channelId) {
 }
 
 UA_StatusCode
-UA_SecureChannelManager_close(UA_SecureChannelManager* cm, UA_UInt32 channelId) {
-    channel_list_entry* entry;
+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)
             break;

+ 8 - 2
src/server/ua_securechannel_manager.h

@@ -1,6 +1,12 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2014-2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2014, 2017 (c) Florian Palm
+ *    Copyright 2015 (c) Oleksiy Vasylyev
+ *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
+ */
 
 #ifndef UA_CHANNEL_MANAGER_H_
 #define UA_CHANNEL_MANAGER_H_
@@ -12,7 +18,7 @@ extern "C" {
 #include "ua_util.h"
 #include "ua_server.h"
 #include "ua_securechannel.h"
-#include "queue.h"
+#include "../../deps/queue.h"
 
 typedef struct channel_list_entry {
     UA_SecureChannel channel;

+ 37 - 13
src/server/ua_server.c

@@ -1,6 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2014-2018 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2014-2017 (c) Florian Palm
+ *    Copyright 2015-2016 (c) Sten Grüner
+ *    Copyright 2015-2016 (c) Chris Iatrou
+ *    Copyright 2015 (c) LEvertz
+ *    Copyright 2015-2016 (c) Oleksiy Vasylyev
+ *    Copyright 2016 (c) Julian Grothoff
+ *    Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH
+ *    Copyright 2016 (c) Lorenz Haas
+ *    Copyright 2017 (c) frax2222
+ *    Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB
+ */
 
 #include "ua_types.h"
 #include "ua_server_internal.h"
@@ -70,7 +83,7 @@ UA_Server_forEachChildNodeCall(UA_Server *server, UA_NodeId parentNodeId,
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     for(size_t i = parentCopy->referencesSize; i > 0; --i) {
         UA_NodeReferenceKind *ref = &parentCopy->references[i - 1];
-        for (size_t j = 0; j<ref->targetIdsSize; j++)
+        for(size_t j = 0; j<ref->targetIdsSize; j++)
             retval |= callback(ref->targetIds[j].nodeId, ref->isInverse,
                                ref->referenceTypeId, handle);
     }
@@ -132,8 +145,14 @@ void UA_Server_delete(UA_Server *server) {
 #endif
 
 #ifdef UA_ENABLE_MULTITHREADING
+    /* Process new delayed callbacks from the cleanup */
+    UA_Server_cleanupDispatchQueue(server);
+    pthread_mutex_destroy(&server->dispatchQueue_accessMutex);
     pthread_cond_destroy(&server->dispatchQueue_condition);
-    pthread_mutex_destroy(&server->dispatchQueue_mutex);
+    pthread_mutex_destroy(&server->dispatchQueue_conditionMutex);
+#else
+    /* Process new delayed callbacks from the cleanup */
+    UA_Server_cleanupDelayedCallbacks(server);
 #endif
 
     /* Delete the timed work */
@@ -160,18 +179,23 @@ UA_Server_cleanup(UA_Server *server, void *_) {
 
 UA_Server *
 UA_Server_new(const UA_ServerConfig *config) {
-    UA_Server *server = (UA_Server *)UA_calloc(1, sizeof(UA_Server));
-    if(!server)
+    /* A config is required */
+    if(!config)
         return NULL;
 
+    /* At least one endpoint has to be configured */
     if(config->endpointsSize == 0) {
-        UA_LOG_FATAL(config->logger,
-                     UA_LOGCATEGORY_SERVER,
+        UA_LOG_FATAL(config->logger, UA_LOGCATEGORY_SERVER,
                      "There has to be at least one endpoint.");
-        UA_free(server);
         return NULL;
     }
 
+    /* Allocate the server */
+    UA_Server *server = (UA_Server *)UA_calloc(1, sizeof(UA_Server));
+    if(!server)
+        return NULL;
+
+    /* Set the config */
     server->config = *config;
 
     /* Init start time to zero, the actual start time will be sampled in
@@ -193,7 +217,7 @@ UA_Server_new(const UA_ServerConfig *config) {
 
     /* Initialized the dispatch queue for worker threads */
 #ifdef UA_ENABLE_MULTITHREADING
-    cds_wfcq_init(&server->dispatchQueue_head, &server->dispatchQueue_tail);
+    SIMPLEQ_INIT(&server->dispatchQueue);
 #endif
 
     /* Create Namespaces 0 and 1 */
@@ -244,10 +268,10 @@ UA_Server_new(const UA_ServerConfig *config) {
 
     /* Initialize namespace 0*/
     UA_StatusCode retVal = UA_Server_initNS0(server);
-    if (retVal != UA_STATUSCODE_GOOD) {
-        UA_LOG_ERROR(config->logger,
-                     UA_LOGCATEGORY_SERVER,
-                     "Initialization of Namespace 0 failed with %s. See previous outputs for any error messages.",
+    if(retVal != UA_STATUSCODE_GOOD) {
+        UA_LOG_ERROR(config->logger, UA_LOGCATEGORY_SERVER,
+                     "Initialization of Namespace 0 failed with %s. "
+                     "See previous outputs for any error messages.",
                      UA_StatusCode_name(retVal));
         UA_Server_delete(server);
         return NULL;

+ 81 - 23
src/server/ua_server_binary.c

@@ -1,6 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
+ *
+ *    Copyright 2014-2017 (c) Julius Pfrommer, Fraunhofer IOSB
+ *    Copyright 2014-2016 (c) Sten Grüner
+ *    Copyright 2014-2015, 2017 (c) Florian Palm
+ *    Copyright 2015-2016 (c) Chris Iatrou
+ *    Copyright 2015-2016 (c) Oleksiy Vasylyev
+ *    Copyright 2016 (c) Joakim L. Gilje
+ *    Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH
+ *    Copyright 2016 (c) TorbenD
+ *    Copyright 2017 (c) frax2222
+ *    Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB
+ */
 
 #include "ua_util.h"
 #include "ua_server_internal.h"
@@ -14,7 +26,6 @@
 #include "ua_types_generated_handling.h"
 #include "ua_securitypolicy_none.h"
 
-
 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
 // store the authentication token and session ID so we can help fuzzing by setting
 // these values in the next request automatically
@@ -40,7 +51,7 @@ sendServiceFault(UA_SecureChannel *channel, const UA_ByteString *msg,
     UA_StatusCode retval = UA_RequestHeader_decodeBinary(msg, &offset, &requestHeader);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
-    void *response = UA_alloca(responseType->memSize);
+    UA_STACKARRAY(UA_Byte, response, responseType->memSize);
     UA_init(response, responseType);
     UA_ResponseHeader *responseHeader = (UA_ResponseHeader*)response;
     responseHeader->requestHandle = requestHeader.requestHandle;
@@ -50,16 +61,23 @@ sendServiceFault(UA_SecureChannel *channel, const UA_ByteString *msg,
     // Send error message. Message type is MSG and not ERR, since we are on a securechannel!
     retval = UA_SecureChannel_sendSymmetricMessage(channel, requestId, UA_MESSAGETYPE_MSG,
                                                    response, responseType);
+
     UA_RequestHeader_deleteMembers(&requestHeader);
     UA_LOG_DEBUG(channel->securityPolicy->logger, UA_LOGCATEGORY_SERVER,
                  "Sent ServiceFault with error code %s", UA_StatusCode_name(error));
     return retval;
 }
 
+typedef enum {
+    UA_SERVICETYPE_NORMAL,
+    UA_SERVICETYPE_INSITU,
+    UA_SERVICETYPE_CUSTOM
+} UA_ServiceType;
+
 static void
 getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
                    const UA_DataType **responseType, UA_Service *service,
-                   UA_Boolean *requiresSession) {
+                   UA_Boolean *requiresSession, UA_ServiceType *serviceType) {
     switch(requestTypeId) {
     case UA_NS0ID_GETENDPOINTSREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_GetEndpoints;
@@ -100,11 +118,13 @@ getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
         *requestType = &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE];
         *requiresSession = false;
+        *serviceType = UA_SERVICETYPE_CUSTOM;
         break;
     case UA_NS0ID_ACTIVATESESSIONREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_ActivateSession;
         *requestType = &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE];
+        *serviceType = UA_SERVICETYPE_CUSTOM;
         break;
     case UA_NS0ID_CLOSESESSIONREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_CloseSession;
@@ -115,6 +135,7 @@ getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
         *service = (UA_Service)Service_Read;
         *requestType = &UA_TYPES[UA_TYPES_READREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_READRESPONSE];
+        *serviceType = UA_SERVICETYPE_INSITU;
         break;
     case UA_NS0ID_WRITEREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_Write;
@@ -320,7 +341,7 @@ processOPN(UA_Server *server, UA_SecureChannel *channel,
     retval |= UA_NodeId_decodeBinary(msg, &offset, &requestType);
     retval |= UA_OpenSecureChannelRequest_decodeBinary(msg, &offset, &openSecureChannelRequest);
 
-    /* Error occured */
+    /* Error occurred */
     if(retval != UA_STATUSCODE_GOOD ||
        requestType.identifier.numeric != UA_TYPES[UA_TYPES_OPENSECURECHANNELREQUEST].binaryEncodingId) {
         UA_NodeId_deleteMembers(&requestType);
@@ -370,7 +391,8 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
     UA_StatusCode retval = UA_NodeId_decodeBinary(msg, &offset, &requestTypeId);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
-    if(requestTypeId.identifierType != UA_NODEIDTYPE_NUMERIC)
+    if(requestTypeId.namespaceIndex != 0 ||
+       requestTypeId.identifierType != UA_NODEIDTYPE_NUMERIC)
         UA_NodeId_deleteMembers(&requestTypeId); /* leads to badserviceunsupported */
 
     /* Store the start-position of the request */
@@ -381,8 +403,9 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
     const UA_DataType *requestType = NULL;
     const UA_DataType *responseType = NULL;
     UA_Boolean sessionRequired = true;
+    UA_ServiceType serviceType = UA_SERVICETYPE_NORMAL;
     getServicePointers(requestTypeId.identifier.numeric, &requestType,
-                       &responseType, &service, &sessionRequired);
+                       &responseType, &service, &sessionRequired, &serviceType);
     if(!requestType) {
         if(requestTypeId.identifier.numeric == 787) {
             UA_LOG_INFO_CHANNEL(server->config.logger, channel,
@@ -399,7 +422,7 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
     UA_assert(responseType);
 
     /* Decode the request */
-    void *request = UA_alloca(requestType->memSize);
+    UA_STACKARRAY(UA_Byte, request, requestType->memSize);
     UA_RequestHeader *requestHeader = (UA_RequestHeader*)request;
     retval = UA_decodeBinary(msg, &offset, request, requestType,
                              server->config.customDataTypesSize,
@@ -411,7 +434,8 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
     }
 
     /* Prepare the respone */
-    void *response = UA_alloca(responseType->memSize);
+    UA_STACKARRAY(UA_Byte, responseBuf, responseType->memSize);
+    void *response = (void*)(uintptr_t)&responseBuf[0]; /* Get around aliasing rules */
     UA_init(response, responseType);
     UA_Session *session = NULL; /* must be initialized before goto send_response */
 
@@ -431,12 +455,13 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
 
     #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
     // set the authenticationToken from the create session request to help fuzzing cover more lines
-    if (!UA_NodeId_isNull(&unsafe_fuzz_authenticationToken))
+    UA_NodeId_deleteMembers(&requestHeader->authenticationToken);
+    if(!UA_NodeId_isNull(&unsafe_fuzz_authenticationToken))
         UA_NodeId_copy(&unsafe_fuzz_authenticationToken, &requestHeader->authenticationToken);
     #endif
 
     /* Find the matching session */
-    session = UA_SecureChannel_getSession(channel, &requestHeader->authenticationToken);
+    session = (UA_Session*)UA_SecureChannel_getSession(channel, &requestHeader->authenticationToken);
     if(!session && !UA_NodeId_isNull(&requestHeader->authenticationToken))
         session = UA_SessionManager_getSessionByToken(&server->sessionManager,
                                                       &requestHeader->authenticationToken);
@@ -470,7 +495,7 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
 
         UA_Session_init(&anonymousSession);
         anonymousSession.sessionId = UA_NODEID_GUID(0, UA_GUID_NULL);
-        anonymousSession.channel = channel;
+        anonymousSession.header.channel = channel;
         session = &anonymousSession;
     }
 
@@ -480,14 +505,14 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
                                "Calling service %i on a non-activated session",
                                requestType->binaryEncodingId);
         UA_SessionManager_removeSession(&server->sessionManager,
-                                        &session->authenticationToken);
+                                        &session->header.authenticationToken);
         UA_deleteMembers(request, requestType);
         return sendServiceFault(channel, msg, requestPos, responseType,
                                 requestId, UA_STATUSCODE_BADSESSIONNOTACTIVATED);
     }
 
     /* The session is bound to another channel */
-    if(session != &anonymousSession && session->channel != channel) {
+    if(session != &anonymousSession && session->header.channel != channel) {
         UA_LOG_WARNING_CHANNEL(server->config.logger, channel,
                                "Client tries to use a Session that is not "
                                "bound to this SecureChannel");
@@ -509,26 +534,59 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
     }
 #endif
 
-    /* Call the service */
-    UA_assert(service); /* For all services besides publish, the service pointer is non-NULL*/
-    service(server, session, request, response);
+    send_response:
 
-send_response:
-    /* Send the response */
+    /* Prepare the ResponseHeader */
     ((UA_ResponseHeader*)response)->requestHandle = requestHeader->requestHandle;
     ((UA_ResponseHeader*)response)->timestamp = UA_DateTime_now();
-    retval = UA_SecureChannel_sendSymmetricMessage(channel, requestId, UA_MESSAGETYPE_MSG,
-                                                   response, responseType);
 
+    /* Start the message */
+    UA_NodeId typeId = UA_NODEID_NUMERIC(0, responseType->binaryEncodingId);
+    UA_MessageContext mc;
+    retval = UA_MessageContext_begin(&mc, channel, requestId, UA_MESSAGETYPE_MSG);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto cleanup;
+
+    /* Assert's required for clang-analyzer */
+    UA_assert(mc.buf_pos == &mc.messageBuffer.data[UA_SECURE_MESSAGE_HEADER_LENGTH]);
+    UA_assert(mc.buf_end <= &mc.messageBuffer.data[mc.messageBuffer.length]);
+
+    retval = UA_MessageContext_encode(&mc, &typeId, &UA_TYPES[UA_TYPES_NODEID]);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto cleanup;
+
+    switch(serviceType) {
+    case UA_SERVICETYPE_CUSTOM:
+        /* Was processed before...*/
+        retval = UA_MessageContext_encode(&mc, response, responseType);
+        break;
+    case UA_SERVICETYPE_INSITU:
+        retval = ((UA_InSituService)service)
+            (server, session, &mc, request, (UA_ResponseHeader*)response);
+        break;
+    case UA_SERVICETYPE_NORMAL:
+    default:
+        service(server, session, request, response);
+        retval = UA_MessageContext_encode(&mc, response, responseType);
+        break;
+    }
+
+    /* Finish sending the message */
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_MessageContext_abort(&mc);
+        goto cleanup;
+    }
+
+    retval = UA_MessageContext_finish(&mc);
+
+ cleanup:
     if(retval != UA_STATUSCODE_GOOD)
         UA_LOG_INFO_CHANNEL(server->config.logger, channel,
                             "Could not send the message over the SecureChannel "
                             "with StatusCode %s", UA_StatusCode_name(retval));
-
     /* Clean up */
     UA_deleteMembers(request, requestType);
     UA_deleteMembers(response, responseType);
-
     return retval;
 }
 

+ 0 - 0
src/server/ua_server_discovery.c


Some files were not shown because too many files changed in this diff