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
 /build
 /.idea
 /.idea
 /cmake-build*
 /cmake-build*
+/tools/certs/certs/*

+ 8 - 0
.travis.yml

@@ -39,12 +39,20 @@ matrix:
       env:
       env:
         - ANALYZE=false
         - ANALYZE=false
         - PYTHON=python3
         - PYTHON=python3
+    - os: linux
+      compiler: tcc
+      env:
+        - ANALYZE=false
+        - PYTHON=python2
     - os: linux
     - os: linux
       compiler: clang
       compiler: clang
       env: ANALYZE=true
       env: ANALYZE=true
     - os: linux
     - os: linux
       compiler: clang
       compiler: clang
       env: FUZZER=true
       env: FUZZER=true
+    #- os: linux
+    #  compiler: gcc
+    #  env: LINT=true
     - os: osx
     - os: osx
       compiler: clang
       compiler: clang
       # disable homebrew auto update which takes a lot of time
       # 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>
 Palm, Florian <f.palm (at) plt.rwth-aachen.de>
 Pfrommer, Julius <julius.pfrommer (at) kit.edu>
 Pfrommer, Julius <julius.pfrommer (at) kit.edu>
 Profanter, Stefan <profanter (at) fortiss.org>
 Profanter, Stefan <profanter (at) fortiss.org>
+Stalder, Thomas <t.stalder (at) bluetimeconcept.ch>
 Urbas, Leon <leon.urbas (at) tu-dresden.de>
 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
 This changelog reports changes to the public API. Internal refactorings and bug
 fixes are not reported here.
 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>
 2017-09-07 jpfr <julius.pfrommer at web.de>
 
 
  * Make Client Highlevel Interface consistent
  * 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_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
 set(CMAKE_ARCHIVE_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 #
 # 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_MAJOR 0)
 set(OPEN62541_VER_MINOR 3)
 set(OPEN62541_VER_MINOR 3)
 set(OPEN62541_VER_PATCH 0)
 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(LIB_INSTALL_DIR lib CACHE PATH "Installation path of libraries")
 
 
 # Set OPEN62541_VER_COMMIT
 # 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_SUBSCRIPTIONS "Enable subscriptions support." ON)
 option(UA_ENABLE_DISCOVERY "Enable Discovery Service (LDS)" ON)
 option(UA_ENABLE_DISCOVERY "Enable Discovery Service (LDS)" ON)
 option(UA_ENABLE_DISCOVERY_MULTICAST "Enable Discovery Service with multicast support (LDS-ME)" OFF)
 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_AMALGAMATION "Concatenate the library to a single file open62541.h/.c" OFF)
 option(UA_ENABLE_COVERAGE "Enable gcov coverage" OFF)
 option(UA_ENABLE_COVERAGE "Enable gcov coverage" OFF)
 option(BUILD_SHARED_LIBS "Enable building of shared libraries (dll/so)" 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)
 if(UA_ENABLE_COVERAGE)
   set(CMAKE_BUILD_TYPE DEBUG)
   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")
   set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fprofile-arcs -ftest-coverage")
 endif()
 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)
 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")
     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)
     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)
 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)
 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)
 option(UA_ENABLE_FULL_NS0 "Use the full NS0 instead of a minimal Namespace 0 nodeset" OFF)
 if (MSVC AND UA_ENABLE_FULL_NS0)
 if (MSVC AND UA_ENABLE_FULL_NS0)
     # For the full NS0 we need a stack size of 8MB (as it is default on linux)
     # 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")
     set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:8000000")
 endif()
 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)
 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)
 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)
        "Add hooks to force failure modes for additional unit tests. Not for production use!" OFF)
 mark_as_advanced(UA_ENABLE_UNIT_TEST_FAILURE_HOOKS)
 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
 # Build options for debugging
 option(UA_DEBUG "Enable assertions and additional functionality that should not be included in release builds" OFF)
 option(UA_DEBUG "Enable assertions and additional functionality that should not be included in release builds" OFF)
 mark_as_advanced(UA_DEBUG)
 mark_as_advanced(UA_DEBUG)
@@ -151,6 +178,12 @@ if(BUILD_SHARED_LIBS)
   endif()
   endif()
 endif()
 endif()
 
 
+######################
+# External Libraries #
+######################
+
+list(APPEND open62541_LIBRARIES "")
+
 # Force compilation with as C++
 # Force compilation with as C++
 option(UA_COMPILE_AS_CXX "Force compilation with a C++ compiler" OFF)
 option(UA_COMPILE_AS_CXX "Force compilation with a C++ compiler" OFF)
 mark_as_advanced(UA_COMPILE_AS_CXX)
 mark_as_advanced(UA_COMPILE_AS_CXX)
@@ -159,19 +192,24 @@ if (UA_COMPILE_AS_CXX)
     add_definitions(-D__STDC_CONSTANT_MACROS)
     add_definitions(-D__STDC_CONSTANT_MACROS)
 endif()
 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(NOT WIN32)
   if(QNXNTO)
   if(QNXNTO)
     list(APPEND open62541_LIBRARIES socket)
     list(APPEND open62541_LIBRARIES socket)
     list(APPEND open62541_LIBRARIES c)
     list(APPEND open62541_LIBRARIES c)
     list(APPEND open62541_LIBRARIES stdc++)
     list(APPEND open62541_LIBRARIES stdc++)
   else()
   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"))
     if(NOT APPLE AND (NOT ${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD"))
       list(APPEND open62541_LIBRARIES rt)
       list(APPEND open62541_LIBRARIES rt)
     endif()
     endif()
@@ -182,9 +220,10 @@ else()
         list(APPEND open62541_LIBRARIES iphlpapi)
         list(APPEND open62541_LIBRARIES iphlpapi)
     endif()
     endif()
 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"))
 if(NOT UA_COMPILE_AS_CXX AND (CMAKE_COMPILER_IS_GNUCC OR "x${CMAKE_C_COMPILER_ID}" STREQUAL "xClang"))
     # Compiler
     # 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)
         add_definitions(-D_WRS_KERNEL)
     endif()
     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
     # Linker
     set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") # cmake sets -rdynamic by default
     set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") # cmake sets -rdynamic by default
 
 
@@ -254,9 +303,9 @@ if(APPLE)
 endif()
 endif()
 
 
 if(MSVC)
 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
     set(CompilerFlags CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_C_FLAGS
         CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE)
         CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE)
     foreach(CompilerFlag ${CompilerFlags})
     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}/plugins # TODO: discovery depends on the default config
                     ${PROJECT_SOURCE_DIR}/deps
                     ${PROJECT_SOURCE_DIR}/deps
                     ${PROJECT_BINARY_DIR}
                     ${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
 set(exported_headers ${PROJECT_BINARY_DIR}/src_generated/ua_config.h
                      ${PROJECT_SOURCE_DIR}/deps/ms_stdint.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.h
                      ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated_handling.h
                      ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated_handling.h
                      ${PROJECT_SOURCE_DIR}/include/ua_server.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_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_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_securitypolicy.h
                      ${PROJECT_SOURCE_DIR}/include/ua_plugin_nodestore.h
                      ${PROJECT_SOURCE_DIR}/include/ua_plugin_nodestore.h
                      ${PROJECT_SOURCE_DIR}/include/ua_server_config.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.h
                      ${PROJECT_SOURCE_DIR}/include/ua_client_highlevel.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
 set(internal_headers ${PROJECT_SOURCE_DIR}/deps/queue.h
                      ${PROJECT_SOURCE_DIR}/deps/pcg_basic.h
                      ${PROJECT_SOURCE_DIR}/deps/pcg_basic.h
                      ${PROJECT_SOURCE_DIR}/deps/libc_time.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_BINARY_DIR}/src_generated/ua_transport_generated_encoding_binary.h
                      ${PROJECT_SOURCE_DIR}/src/ua_connection_internal.h
                      ${PROJECT_SOURCE_DIR}/src/ua_connection_internal.h
                      ${PROJECT_SOURCE_DIR}/src/ua_securechannel.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/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_subscription.h
                      ${PROJECT_SOURCE_DIR}/src/server/ua_session_manager.h
                      ${PROJECT_SOURCE_DIR}/src/server/ua_session_manager.h
                      ${PROJECT_SOURCE_DIR}/src/server/ua_securechannel_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_BINARY_DIR}/src_generated/ua_statuscode_descriptions.c
                 ${PROJECT_SOURCE_DIR}/src/ua_util.c
                 ${PROJECT_SOURCE_DIR}/src/ua_util.c
                 ${PROJECT_SOURCE_DIR}/src/ua_timer.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_connection.c
                 ${PROJECT_SOURCE_DIR}/src/ua_securechannel.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_nodes.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server_ns0.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_connect.c
                 ${PROJECT_SOURCE_DIR}/src/client/ua_client_discovery.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.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
                 # dependencies
                 ${PROJECT_SOURCE_DIR}/deps/libc_time.c
                 ${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
 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_accesscontrol_default.h
+                           ${PROJECT_SOURCE_DIR}/plugins/ua_pki_certificate.h
                            ${PROJECT_SOURCE_DIR}/plugins/ua_log_stdout.h
                            ${PROJECT_SOURCE_DIR}/plugins/ua_log_stdout.h
                            ${PROJECT_SOURCE_DIR}/plugins/ua_nodestore_default.h
                            ${PROJECT_SOURCE_DIR}/plugins/ua_nodestore_default.h
                            ${PROJECT_SOURCE_DIR}/plugins/ua_config_default.h
                            ${PROJECT_SOURCE_DIR}/plugins/ua_config_default.h
                            ${PROJECT_SOURCE_DIR}/plugins/ua_securitypolicy_none.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
 set(default_plugin_sources ${PROJECT_SOURCE_DIR}/plugins/ua_network_tcp.c
                            ${PROJECT_SOURCE_DIR}/plugins/ua_clock.c
                            ${PROJECT_SOURCE_DIR}/plugins/ua_clock.c
                            ${PROJECT_SOURCE_DIR}/plugins/ua_log_stdout.c
                            ${PROJECT_SOURCE_DIR}/plugins/ua_log_stdout.c
                            ${PROJECT_SOURCE_DIR}/plugins/ua_accesscontrol_default.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_nodestore_default.c
                            ${PROJECT_SOURCE_DIR}/plugins/ua_config_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)
 if(UA_DEBUG_DUMP_PKGS)
     list(APPEND lib_sources ${PROJECT_SOURCE_DIR}/plugins/ua_debug_dump_pkgs.c)
     list(APPEND lib_sources ${PROJECT_SOURCE_DIR}/plugins/ua_debug_dump_pkgs.c)
@@ -411,19 +480,24 @@ endif()
 #########################
 #########################
 
 
 if (UA_ENABLE_FULL_NS0)
 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_DATATYPES "")
     set(UA_FILE_NODEIDS ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/NodeIds.csv)
     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)
     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()
 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_DATATYPES "${PROJECT_SOURCE_DIR}/tools/schema/datatypes_minimal.txt")
     set(UA_FILE_NODEIDS ${PROJECT_SOURCE_DIR}/tools/schema/NodeIds.csv)
     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)
     set(UA_FILE_TYPES_BSD ${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd)
 endif()
 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 "")
 if (UA_FILE_DATATYPES STREQUAL "")
     set(SELECTED_TYPES_TMP "")
     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
                            ${OPEN62541_VER_COMMIT} ${CMAKE_CURRENT_BINARY_DIR}/open62541.c
                            ${internal_headers} ${lib_sources} ${default_plugin_sources}
                            ${internal_headers} ${lib_sources} ${default_plugin_sources}
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/amalgamate.py ${internal_headers}
                    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-source DEPENDS ${PROJECT_BINARY_DIR}/open62541.c)
 add_custom_target(open62541-amalgamation-header DEPENDS ${PROJECT_BINARY_DIR}/open62541.h)
 add_custom_target(open62541-amalgamation-header DEPENDS ${PROJECT_BINARY_DIR}/open62541.h)
@@ -644,9 +718,6 @@ if(UA_BUILD_UNIT_TESTS)
 endif()
 endif()
 
 
 if(UA_BUILD_FUZZING OR UA_BUILD_OSS_FUZZ OR UA_BUILD_FUZZING_CORPUS)
 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)
     add_subdirectory(tests/fuzz)
 endif()
 endif()
 
 
@@ -673,6 +744,18 @@ add_custom_target(lint ${CLANG_TIDY_PROGRAM}
                   COMMENT "Run clang-tidy on the library")
                   COMMENT "Run clang-tidy on the library")
 add_dependencies(lint open62541)
 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           #
 # Installation           #
 ##########################
 ##########################
@@ -680,7 +763,7 @@ add_dependencies(lint open62541)
 # specify install location with `-DCMAKE_INSTALL_PREFIX=xyz`
 # specify install location with `-DCMAKE_INSTALL_PREFIX=xyz`
 # Enable shared library with `-DBUILD_SHARED_LIBS=ON`
 # 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(target_install_dest_name "${cmake_configfile_install}/open62541Targets.cmake")
 set(open62541_tools_dir share/open62541/tools)
 set(open62541_tools_dir share/open62541/tools)
 set(open62541_deps_dir include/open62541/deps)
 set(open62541_deps_dir include/open62541/deps)
@@ -691,7 +774,7 @@ install(TARGETS open62541
         LIBRARY DESTINATION ${LIB_INSTALL_DIR}
         LIBRARY DESTINATION ${LIB_INSTALL_DIR}
         ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
         ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
         RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}
         RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}
-        INCLUDES DESTINATION ${INCLUDE_INSTALL_DIR}/open62541 ${open62541_deps_dir})
+        INCLUDES DESTINATION include/open62541 ${open62541_deps_dir})
 
 
 include(CMakePackageConfigHelpers)
 include(CMakePackageConfigHelpers)
 configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake/open62541-config.cmake.in"
 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(lint PROPERTIES FOLDER "CodeAnalysis")
 set_target_properties(open62541-amalgamation-header PROPERTIES FOLDER "open62541/lib")
 set_target_properties(open62541-amalgamation-header PROPERTIES FOLDER "open62541/lib")
 set_target_properties(open62541-amalgamation-source 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:      |                      |
 | SOAP-HTTP WS-SC UA XML-UA Binary        |      :new_moon:      |                      |
 | **Encryption**                          |                      |                      |
 | **Encryption**                          |                      |                      |
 | None                                    |  :white_check_mark:  |                      |
 | 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**                      |                      |                      |
 | **Authentication**                      |                      |                      |
 | Anonymous                               |  :white_check_mark:  |                      |
 | Anonymous                               |  :white_check_mark:  |                      |
 | User Name Password                      |  :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
 - Code-Generation
   - Support for generating data types from standard XML definitions
   - Support for generating data types from standard XML definitions
   - Support for generating server-side information models (nodesets) 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:
 Features currently being implemented:
 - Target 0.3 release (to be released in the coming weeks):
 - 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)
   - Access control for individual nodes (Done)
+  - Asynchronous service requests in the client (Done)
 - Target 0.4 release:
 - Target 0.4 release:
   - Events (notifications emitted by objects, data change notifications are implemented), WIP by @Pro
   - 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.
 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.
 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).
 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)
 - the [mailing list](https://groups.google.com/d/forum/open62541)
 - our [IRC channel](http://webchat.freenode.net/?channels=%23open62541)
 - our [IRC channel](http://webchat.freenode.net/?channels=%23open62541)
 - the [bugtracker](https://github.com/open62541/open62541/issues)
 - the [bugtracker](https://github.com/open62541/open62541/issues)
 
 
+or contact a member of the core development group (see below).
+
 ### Development
 ### 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):
 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)
 - Julius Pfrommer (Fraunhofer IOSB, Karlsruhe)
 - Stefan Profanter (fortiss, Munich)
 - 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)
 - [Report bugs](https://github.com/open62541/open62541/issues)
 - Improve the [documentation](http://open62541.org/doc/current)
 - 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)"
 - 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:
 cache:
   - C:\ProgramData\chocolatey\bin -> tools/appveyor/install.ps1
   - C:\ProgramData\chocolatey\bin -> tools/appveyor/install.ps1
   - C:\ProgramData\chocolatey\lib -> tools/appveyor/install.ps1
   - C:\ProgramData\chocolatey\lib -> tools/appveyor/install.ps1
+  - C:\tools\vcpkg\installed -> tools/appveyor/install.ps1
   #- 'c:\miktex'
   #- 'c:\miktex'
 
 
 init:
 init:
@@ -50,7 +51,7 @@ install:
 
 
 before_build:
 before_build:
   # Add installed tools to PATH
   # 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
   # Workaround for CMake not wanting sh.exe on PATH for MinGW
   - set PATH=%PATH:C:\Program Files\Git\usr\bin;=%
   - set PATH=%PATH:C:\Program Files\Git\usr\bin;=%
   # Miktex
   # 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_types.h ${DOC_SRC_DIR}/types.rst)
 generate_rst(${PROJECT_SOURCE_DIR}/include/ua_constants.h ${DOC_SRC_DIR}/constants.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_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_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.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_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)
 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}
 add_custom_target(doc_latex ${SPHINX_EXECUTABLE}
   -b latex "${DOC_SRC_DIR}" "${DOC_LATEX_DIR}"
   -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
   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}/plugin_log.rst ${DOC_SRC_DIR}/plugin_network.rst
           ${DOC_SRC_DIR}/services.rst ${DOC_SRC_DIR}/plugin_access_control.rst
           ${DOC_SRC_DIR}/services.rst ${DOC_SRC_DIR}/plugin_access_control.rst
           ${DOC_SRC_DIR}/nodestore.rst
           ${DOC_SRC_DIR}/nodestore.rst
+          ${DOC_SRC_DIR}/tutorials.rst
           ${DOC_SRC_DIR}/tutorial_datatypes.rst
           ${DOC_SRC_DIR}/tutorial_datatypes.rst
           ${DOC_SRC_DIR}/tutorial_client_firststeps.rst
           ${DOC_SRC_DIR}/tutorial_client_firststeps.rst
           ${DOC_SRC_DIR}/tutorial_server_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")
   COMMENT "Building LaTeX sources for documentation with Sphinx")
 add_dependencies(doc_latex open62541)
 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}
   WORKING_DIRECTORY ${DOC_LATEX_DIR}
   # compile it twice so that the contents pages are correct
   # 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}
   DEPENDS ${PDFLATEX_COMPILER}
   COMMENT "Generating PDF documentation from LaTeX sources")
   COMMENT "Generating PDF documentation from LaTeX sources")
 add_dependencies(doc_pdf doc_latex)
 add_dependencies(doc_pdf doc_latex)
@@ -68,10 +74,13 @@ add_dependencies(doc_pdf doc_latex)
 add_custom_target(doc ${SPHINX_EXECUTABLE}
 add_custom_target(doc ${SPHINX_EXECUTABLE}
   -b html "${DOC_SRC_DIR}" "${DOC_HTML_DIR}"
   -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
   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}/plugin_log.rst ${DOC_SRC_DIR}/plugin_network.rst
           ${DOC_SRC_DIR}/services.rst ${DOC_SRC_DIR}/plugin_access_control.rst
           ${DOC_SRC_DIR}/services.rst ${DOC_SRC_DIR}/plugin_access_control.rst
           ${DOC_SRC_DIR}/nodestore.rst
           ${DOC_SRC_DIR}/nodestore.rst
+          ${DOC_SRC_DIR}/tutorials.rst
           ${DOC_SRC_DIR}/tutorial_datatypes.rst
           ${DOC_SRC_DIR}/tutorial_datatypes.rst
           ${DOC_SRC_DIR}/tutorial_client_firststeps.rst
           ${DOC_SRC_DIR}/tutorial_client_firststeps.rst
           ${DOC_SRC_DIR}/tutorial_server_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
    sudo apt-get install git build-essential gcc pkg-config cmake python python-six
 
 
    # enable additional features
    # 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 check # for unit tests
    sudo apt-get install python-sphinx graphviz # for documentation generation
    sudo apt-get install python-sphinx graphviz # for documentation generation
    sudo apt-get install python-sphinx-rtd-theme # documentation style
    sudo apt-get install python-sphinx-rtd-theme # documentation style
@@ -168,7 +168,7 @@ specified by the following options:
 **UA_BUILD_EXAMPLES_NODESET_COMPILER**
 **UA_BUILD_EXAMPLES_NODESET_COMPILER**
    Generate an OPC UA information model from a nodeset XML (experimental)
    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)
    Generate a self-signed certificate for the server (openSSL required)
 
 
 UA_ENABLE_* group
 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
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
 # 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
 # The reST default role (used for this markup: `text`) to use for all
 # documents.
 # documents.

+ 10 - 14
doc/index.rst

@@ -36,7 +36,7 @@ At its core, OPC UA defines
   response message in the protocol type system.
   response message in the protocol type system.
 
 
 The standard itself can be purchased from IEC or downloaded for free on the
 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).
 register with a valid email).
 
 
 The OPC Foundation drives the continuous improvement of the standard and the
 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,
 some additional features. Server binaries can be well under 100kb in size,
 depending on the contained information model.
 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
 - Communication Stack
 
 
   - OPC UA binary protocol
   - OPC UA binary protocol
   - Chunking (splitting of large messages)
   - Chunking (splitting of large messages)
   - Exchangeable network layer (plugin) for using custom networking APIs (e.g. on embedded targets)
   - Exchangeable network layer (plugin) for using custom networking APIs (e.g. on embedded targets)
+  - Encrypted communication
+  - Asynchronous service requests in the client
 
 
 - Information model
 - Information model
 
 
   - Support for all OPC UA node types (including method nodes)
   - Support for all OPC UA node types (including method nodes)
   - Support for adding and removing nodes and references also at runtime.
   - 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)
   - Support for inheritance and instantiation of object- and variable-types (custom constructor/destructor, instantiation of child nodes)
+  - Access control for individual nodes
 
 
 - Subscriptions
 - Subscriptions
 
 
@@ -83,12 +80,11 @@ UA SDKs and services.
   - Support for generating data types from standard XML definitions
   - Support for generating data types from standard XML definitions
   - Support for generating server-side information models (nodesets) 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)
 - 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
 Getting Help
 ------------
 ------------
@@ -103,9 +99,9 @@ Contributing
 ------------
 ------------
 
 
 As an open source project, we invite new contributors to help improve open62541.
 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>`_
 - `Report bugs <https://github.com/open62541/open62541/issues>`_
 - Improve the `documentation <http://open62541.org/doc/current>`_
 - 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,
   in cleartext. Currently defined authentication mechanisms are anonymous login,
   username/password, Kerberos and x509 certificates. The latter requires that
   username/password, Kerberos and x509 certificates. The latter requires that
   the request message is accompanied by a signature to prove that the sender is
   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:
   There are two message exchanges required to establish a session:
   *CreateSession* and *ActicateSession*. The ActivateSession service can be used
   *CreateSession* and *ActicateSession*. The ActivateSession service can be used

+ 1 - 1
doc/toc.rst

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

+ 2 - 0
doc/tutorials.rst

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

+ 10 - 2
examples/CMakeLists.txt

@@ -14,7 +14,7 @@ macro(add_example EXAMPLE_NAME EXAMPLE_SOURCE)
   assign_source_group(${EXAMPLE_SOURCE})
   assign_source_group(${EXAMPLE_SOURCE})
   add_dependencies(${EXAMPLE_NAME} open62541-amalgamation-header open62541-amalgamation-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 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)
   if(UA_COMPILE_AS_CXX)
     set_source_files_properties(${EXAMPLE_SOURCE} PROPERTIES LANGUAGE CXX)
     set_source_files_properties(${EXAMPLE_SOURCE} PROPERTIES LANGUAGE CXX)
   endif()
   endif()
@@ -48,7 +48,7 @@ add_example(tutorial_client_events tutorial_client_events.c)
 # Example Server #
 # Example Server #
 ##################
 ##################
 
 
-add_example(server server.c)
+add_example(server_ctt server_ctt.c)
 
 
 ##################
 ##################
 # Example Client #
 # Example Client #
@@ -58,6 +58,10 @@ add_example(client client.c)
 
 
 add_example(client_connect_loop client_connect_loop.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 #
 # Feature Examples #
 ####################
 ####################
@@ -70,6 +74,10 @@ add_example(server_repeated_job server_repeated_job.c)
 
 
 add_example(server_inheritance server_inheritance.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_client custom_datatype/client_types_custom.c)
 add_example(custom_datatype_server custom_datatype/server_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"
 #include "open62541.h"
 
 
 static UA_Boolean
 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");
     printf("Called allowAddNode\n");
     return UA_TRUE;
     return UA_TRUE;
 }
 }
 
 
 static UA_Boolean
 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");
     printf("Called allowAddReference\n");
     return UA_TRUE;
     return UA_TRUE;
 }
 }
 
 
 static UA_Boolean
 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");
     printf("Called allowDeleteNode\n");
     return UA_FALSE; // Do not allow deletion from client
     return UA_FALSE; // Do not allow deletion from client
 }
 }
 
 
 static UA_Boolean
 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");
     printf("Called allowDeleteReference\n");
     return UA_TRUE;
     return UA_TRUE;
 }
 }

+ 28 - 19
examples/client.c

@@ -6,7 +6,8 @@
 
 
 #ifdef UA_ENABLE_SUBSCRIPTIONS
 #ifdef UA_ENABLE_SUBSCRIPTIONS
 static void
 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");
     printf("The Answer has changed!\n");
 }
 }
 #endif
 #endif
@@ -37,7 +38,7 @@ int main(int argc, char *argv[]) {
         return (int)retval;
         return (int)retval;
     }
     }
     printf("%i endpoints found\n", (int)endpointArraySize);
     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,
         printf("URL of endpoint %i is %.*s\n", (int)i,
                (int)endpointArray[i].endpointUrl.length,
                (int)endpointArray[i].endpointUrl.length,
                endpointArray[i].endpointUrl.data);
                endpointArray[i].endpointUrl.data);
@@ -63,8 +64,8 @@ int main(int argc, char *argv[]) {
     bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL; /* return everything */
     bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL; /* return everything */
     UA_BrowseResponse bResp = UA_Client_Service_browse(client, bReq);
     UA_BrowseResponse bResp = UA_Client_Service_browse(client, bReq);
     printf("%-9s %-16s %-16s %-16s\n", "NAMESPACE", "NODEID", "BROWSE NAME", "DISPLAY NAME");
     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]);
             UA_ReferenceDescription *ref = &(bResp.results[i].references[j]);
             if(ref->nodeId.nodeId.identifierType == UA_NODEIDTYPE_NUMERIC) {
             if(ref->nodeId.nodeId.identifierType == UA_NODEIDTYPE_NUMERIC) {
                 printf("%-9d %-16d %-16.*s %-16.*s\n", ref->nodeId.nodeId.namespaceIndex,
                 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
 #ifdef UA_ENABLE_SUBSCRIPTIONS
     /* Create a subscription */
     /* 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);
         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 */
     /* The first publish request should return the initial value of the variable */
-    UA_Client_Subscriptions_manuallySendPublishRequest(client);
+    UA_Client_runAsync(client, 1000);
 #endif
 #endif
 
 
     /* Read attribute */
     /* Read attribute */
@@ -148,9 +157,9 @@ int main(int argc, char *argv[]) {
 
 
 #ifdef UA_ENABLE_SUBSCRIPTIONS
 #ifdef UA_ENABLE_SUBSCRIPTIONS
     /* Take another look at the.answer */
     /* Take another look at the.answer */
-    UA_Client_Subscriptions_manuallySendPublishRequest(client);
+    UA_Client_runAsync(client, 100);
     /* Delete the subscription */
     /* Delete the subscription */
-    if(!UA_Client_Subscriptions_remove(client, subId))
+    if(UA_Client_Subscriptions_deleteSingle(client, subId) == UA_STATUSCODE_GOOD)
         printf("Subscription removed\n");
         printf("Subscription removed\n");
 #endif
 #endif
 
 
@@ -165,11 +174,11 @@ int main(int argc, char *argv[]) {
     retval = UA_Client_call(client, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
     retval = UA_Client_call(client, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                             UA_NODEID_NUMERIC(1, 62541), 1, &input, &outputSize, &output);
                             UA_NODEID_NUMERIC(1, 62541), 1, &input, &outputSize, &output);
     if(retval == UA_STATUSCODE_GOOD) {
     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);
                (unsigned long)outputSize);
         UA_Array_delete(output, outputSize, &UA_TYPES[UA_TYPES_VARIANT]);
         UA_Array_delete(output, outputSize, &UA_TYPES[UA_TYPES_VARIANT]);
     } else {
     } 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);
     UA_Variant_deleteMembers(&input);
 #endif
 #endif

+ 9 - 8
examples/client_connect_loop.c

@@ -56,12 +56,12 @@ int main(void) {
     UA_Variant_init(&value);
     UA_Variant_init(&value);
 
 
     /* Endless loop reading the server time */
     /* Endless loop reading the server time */
-    while (running) {
+    while(running) {
         /* if already connected, this will return GOOD and do nothing */
         /* if already connected, this will return GOOD and do nothing */
         /* if the connection is closed/errored, the connection will be reset and then reconnected */
         /* 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 */
         /* Alternatively you can also use UA_Client_getState to get the current state */
         UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
         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");
             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 */
             /* 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 */
             /* 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);
                 UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
 
 
         retval = UA_Client_readValueAttribute(client, nodeId, &value);
         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 ...");
             UA_LOG_ERROR(logger, UA_LOGCATEGORY_CLIENT, "Connection was closed. Reconnecting ...");
             continue;
             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_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);
         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_Client *client = UA_Client_new(config);
     UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
     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);
         UA_Client_delete(client);
         return (int)retval;
         return (int)retval;
     }
     }
@@ -29,7 +29,7 @@ int main(void) {
 
 
     retval = UA_Client_readValueAttribute(client, nodeId, &value);
     retval = UA_Client_readValueAttribute(client, nodeId, &value);
             
             
-    if (retval == UA_STATUSCODE_GOOD) {
+    if(retval == UA_STATUSCODE_GOOD) {
         Point *p = (Point *)value.data;
         Point *p = (Point *)value.data;
         printf("Point = %f, %f, %f \n", p->x, p->y, p->z);
         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;
     p.z = 0.0;
     UA_Variant_setScalar(&dattr.value, &p, &PointType);
     UA_Variant_setScalar(&dattr.value, &p, &PointType);
 
 
-
     UA_Server_addVariableTypeNode(server, PointType.typeId,
     UA_Server_addVariableTypeNode(server, PointType.typeId,
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
                                   UA_QUALIFIEDNAME(1, "3D.Point"),
                                   UA_QUALIFIEDNAME(1, "3D.Point"),
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
                                   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_OBJECTSFOLDER),
                               UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
                               UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
                               UA_QUALIFIEDNAME(1, "3D.Point"),
                               UA_QUALIFIEDNAME(1, "3D.Point"),
-                              PointType.typeId,
-                              vattr,
-                              NULL,
-                              NULL
-    );
+                              PointType.typeId, vattr, NULL, NULL);
 }
 }
 
 
 int main(void) {
 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_Client *client = UA_Client_new(UA_ClientConfig_default);
         UA_StatusCode retval = UA_Client_findServersOnNetwork(client, DISCOVERY_SERVER_ENDPOINT, 0, 0,
         UA_StatusCode retval = UA_Client_findServersOnNetwork(client, DISCOVERY_SERVER_ENDPOINT, 0, 0,
                                                               0, NULL, &serverOnNetworkSize, &serverOnNetwork);
                                                               0, NULL, &serverOnNetworkSize, &serverOnNetwork);
-        if (retval != UA_STATUSCODE_GOOD) {
+        if(retval != UA_STATUSCODE_GOOD) {
             UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER,
             UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER,
                          "Could not call FindServersOnNetwork service. "
                          "Could not call FindServersOnNetwork service. "
                          "Is the discovery server started? StatusCode %s",
                          "Is the discovery server started? StatusCode %s",
@@ -36,7 +36,7 @@ int main(void) {
         }
         }
 
 
         // output all the returned/registered servers
         // 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];
             UA_ServerOnNetwork *server = &serverOnNetwork[i];
             printf("Server[%lu]: %.*s", (unsigned long) i,
             printf("Server[%lu]: %.*s", (unsigned long) i,
                    (int) server->serverName.length, server->serverName.data);
                    (int) server->serverName.length, server->serverName.data);
@@ -44,7 +44,7 @@ int main(void) {
             printf("\n\tDiscovery URL: %.*s", (int) server->discoveryUrl.length,
             printf("\n\tDiscovery URL: %.*s", (int) server->discoveryUrl.length,
                    server->discoveryUrl.data);
                    server->discoveryUrl.data);
             printf("\n\tCapabilities: ");
             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,
                 printf("%.*s,", (int) server->serverCapabilities[j].length,
                        server->serverCapabilities[j].data);
                        server->serverCapabilities[j].data);
             }
             }
@@ -66,14 +66,14 @@ int main(void) {
                                        &applicationDescriptionArraySize, &applicationDescriptionArray);
                                        &applicationDescriptionArraySize, &applicationDescriptionArray);
         UA_Client_delete(client);
         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. "
         UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER, "Could not call FindServers service. "
                 "Is the discovery server started? StatusCode %s", UA_StatusCode_name(retval));
                 "Is the discovery server started? StatusCode %s", UA_StatusCode_name(retval));
         return (int) retval;
         return (int) retval;
     }
     }
 
 
     // output all the returned/registered servers
     // 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];
         UA_ApplicationDescription *description = &applicationDescriptionArray[i];
         printf("Server[%lu]: %.*s", (unsigned long) i, (int) description->applicationUri.length,
         printf("Server[%lu]: %.*s", (unsigned long) i, (int) description->applicationUri.length,
                description->applicationUri.data);
                description->applicationUri.data);
@@ -84,7 +84,7 @@ int main(void) {
         printf("\n\tProduct URI: %.*s", (int) description->productUri.length,
         printf("\n\tProduct URI: %.*s", (int) description->productUri.length,
                description->productUri.data);
                description->productUri.data);
         printf("\n\tType: ");
         printf("\n\tType: ");
-        switch (description->applicationType) {
+        switch(description->applicationType) {
             case UA_APPLICATIONTYPE_SERVER:
             case UA_APPLICATIONTYPE_SERVER:
                 printf("Server");
                 printf("Server");
                 break;
                 break;
@@ -101,7 +101,7 @@ int main(void) {
                 printf("Unknown");
                 printf("Unknown");
         }
         }
         printf("\n\tDiscovery URLs:");
         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,
             printf("\n\t\t[%lu]: %.*s", (unsigned long) j,
                    (int) description->discoveryUrls[j].length,
                    (int) description->discoveryUrls[j].length,
                    description->discoveryUrls[j].data);
                    description->discoveryUrls[j].data);
@@ -116,9 +116,9 @@ int main(void) {
 
 
     printf("-------- Server Endpoints --------\n");
     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];
         UA_ApplicationDescription *description = &applicationDescriptionArray[i];
-        if (description->discoveryUrlsSize == 0) {
+        if(description->discoveryUrlsSize == 0) {
             UA_LOG_INFO(logger, UA_LOGCATEGORY_CLIENT,
             UA_LOG_INFO(logger, UA_LOGCATEGORY_CLIENT,
                         "[GetEndpoints] Server %.*s did not provide any discovery urls. Skipping.",
                         "[GetEndpoints] Server %.*s did not provide any discovery urls. Skipping.",
                         (int)description->applicationUri.length, description->applicationUri.data);
                         (int)description->applicationUri.length, description->applicationUri.data);
@@ -138,20 +138,20 @@ int main(void) {
         size_t endpointArraySize = 0;
         size_t endpointArraySize = 0;
         retval = UA_Client_getEndpoints(client, discoveryUrl, &endpointArraySize, &endpointArray);
         retval = UA_Client_getEndpoints(client, discoveryUrl, &endpointArraySize, &endpointArray);
         UA_free(discoveryUrl);
         UA_free(discoveryUrl);
-        if (retval != UA_STATUSCODE_GOOD) {
+        if(retval != UA_STATUSCODE_GOOD) {
             UA_Client_disconnect(client);
             UA_Client_disconnect(client);
             UA_Client_delete(client);
             UA_Client_delete(client);
             break;
             break;
         }
         }
 
 
-        for (size_t j = 0; j < endpointArraySize; j++) {
+        for(size_t j = 0; j < endpointArraySize; j++) {
             UA_EndpointDescription *endpoint = &endpointArray[j];
             UA_EndpointDescription *endpoint = &endpointArray[j];
             printf("\n\tEndpoint[%lu]:", (unsigned long) j);
             printf("\n\tEndpoint[%lu]:", (unsigned long) j);
             printf("\n\t\tEndpoint URL: %.*s", (int) endpoint->endpointUrl.length, endpoint->endpointUrl.data);
             printf("\n\t\tEndpoint URL: %.*s", (int) endpoint->endpointUrl.length, endpoint->endpointUrl.data);
             printf("\n\t\tTransport profile URI: %.*s", (int) endpoint->transportProfileUri.length,
             printf("\n\t\tTransport profile URI: %.*s", (int) endpoint->transportProfileUri.length,
                    endpoint->transportProfileUri.data);
                    endpoint->transportProfileUri.data);
             printf("\n\t\tSecurity Mode: ");
             printf("\n\t\tSecurity Mode: ");
-            switch (endpoint->securityMode) {
+            switch(endpoint->securityMode) {
             case UA_MESSAGESECURITYMODE_INVALID:
             case UA_MESSAGESECURITYMODE_INVALID:
                 printf("Invalid");
                 printf("Invalid");
                 break;
                 break;

+ 7 - 7
examples/discovery/server_multicast.c

@@ -64,19 +64,19 @@ static void
 serverOnNetworkCallback(const UA_ServerOnNetwork *serverOnNetwork, UA_Boolean isServerAnnounce,
 serverOnNetworkCallback(const UA_ServerOnNetwork *serverOnNetwork, UA_Boolean isServerAnnounce,
                         UA_Boolean isTxtReceived, void *data) {
                         UA_Boolean isTxtReceived, void *data) {
 
 
-    if (discovery_url != NULL || !isServerAnnounce) {
+    if(discovery_url != NULL || !isServerAnnounce) {
         UA_LOG_DEBUG(logger, UA_LOGCATEGORY_SERVER,
         UA_LOG_DEBUG(logger, UA_LOGCATEGORY_SERVER,
                      "serverOnNetworkCallback called, but discovery URL "
                      "serverOnNetworkCallback called, but discovery URL "
                      "already initialized or is not announcing. Ignoring.");
                      "already initialized or is not announcing. Ignoring.");
         return; // we already have everything we need or we only want server announces
         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
         // skip self
         return;
         return;
     }
     }
 
 
-    if (!isTxtReceived)
+    if(!isTxtReceived)
         return; // we wait until the corresponding TXT record is announced.
         return; // we wait until the corresponding TXT record is announced.
                 // Problem: how to handle if a Server does not announce the
                 // Problem: how to handle if a Server does not announce the
                 // optional TXT?
                 // 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",
     UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Another server announced itself on %.*s",
                 (int)serverOnNetwork->discoveryUrl.length, serverOnNetwork->discoveryUrl.data);
                 (int)serverOnNetwork->discoveryUrl.length, serverOnNetwork->discoveryUrl.data);
 
 
-    if (discovery_url != NULL)
+    if(discovery_url != NULL)
         UA_free(discovery_url);
         UA_free(discovery_url);
     discovery_url = (char*)UA_malloc(serverOnNetwork->discoveryUrl.length + 1);
     discovery_url = (char*)UA_malloc(serverOnNetwork->discoveryUrl.length + 1);
     memcpy(discovery_url, serverOnNetwork->discoveryUrl.data, serverOnNetwork->discoveryUrl.length);
     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
     // Start the server and call iterate to wait for the multicast discovery of the LDS
     UA_StatusCode retval = UA_Server_run_startup(server);
     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,
         UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER,
                      "Could not start the server. StatusCode %s",
                      "Could not start the server. StatusCode %s",
                      UA_StatusCode_name(retval));
                      UA_StatusCode_name(retval));
@@ -147,7 +147,7 @@ int main(int argc, char **argv) {
                 "Server started. Waiting for announce of LDS Server.");
                 "Server started. Waiting for announce of LDS Server.");
     while (running && discovery_url == NULL)
     while (running && discovery_url == NULL)
         UA_Server_run_iterate(server, true);
         UA_Server_run_iterate(server, true);
-    if (!running) {
+    if(!running) {
         UA_Server_delete(server);
         UA_Server_delete(server);
         UA_ServerConfig_delete(config);
         UA_ServerConfig_delete(config);
         UA_free(discovery_url);
         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
     // periodic server register after 10 Minutes, delay first register for 500ms
     retval = UA_Server_addPeriodicServerRegisterCallback(server, discovery_url,
     retval = UA_Server_addPeriodicServerRegisterCallback(server, discovery_url,
                                                          10 * 60 * 1000, 500, NULL);
                                                          10 * 60 * 1000, 500, NULL);
-    if (retval != UA_STATUSCODE_GOOD) {
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER,
         UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER,
                      "Could not create periodic job for server register. StatusCode %s",
                      "Could not create periodic job for server register. StatusCode %s",
                      UA_StatusCode_name(retval));
                      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);
                                                     10 * 60 * 1000, 500, NULL);
     // UA_StatusCode retval = UA_Server_addPeriodicServerRegisterJob(server,
     // UA_StatusCode retval = UA_Server_addPeriodicServerRegisterJob(server,
     // "opc.tcp://localhost:4840", 10*60*1000, 500, NULL);
     // "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,
         UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER,
                      "Could not create periodic job for server register. StatusCode %s",
                      "Could not create periodic job for server register. StatusCode %s",
                      UA_StatusCode_name(retval));
                      UA_StatusCode_name(retval));
@@ -108,7 +108,7 @@ int main(int argc, char **argv) {
     }
     }
 
 
     retval = UA_Server_run(server, &running);
     retval = UA_Server_run(server, &running);
-    if (retval != UA_STATUSCODE_GOOD) {
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER,
         UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER,
                      "Could not start the server. StatusCode %s",
                      "Could not start the server. StatusCode %s",
                      UA_StatusCode_name(retval));
                      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);
     fseek(fp, 0, SEEK_END);
     certificate.length = (size_t)ftell(fp);
     certificate.length = (size_t)ftell(fp);
     certificate.data = (UA_Byte *)UA_malloc(certificate.length*sizeof(UA_Byte));
     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);
     fseek(fp, 0, SEEK_SET);
     if(fread(certificate.data, sizeof(UA_Byte), certificate.length, fp) < (size_t)certificate.length)
     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 */
 #define _CRT_SECURE_NO_WARNINGS /* disable fopen deprication warning in msvs */
 #endif
 #endif
 
 
-
 #include <signal.h>
 #include <signal.h>
 #include <errno.h> // errno, EINTR
 #include <errno.h> // errno, EINTR
 #include <stdio.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdlib.h>
 #include <string.h>
 #include <string.h>
+#include <open62541.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_Boolean running = true;
 UA_Logger logger = UA_Log_Stdout;
 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) {
     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);
     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);
     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");
     UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Received Ctrl-C");
     running = 0;
     running = 0;
 }
 }
@@ -70,6 +80,7 @@ readTimeData(UA_Server *server,
 
 
 /* Method Node Example */
 /* Method Node Example */
 #ifdef UA_ENABLE_METHODCALLS
 #ifdef UA_ENABLE_METHODCALLS
+
 static UA_StatusCode
 static UA_StatusCode
 helloWorld(UA_Server *server,
 helloWorld(UA_Server *server,
            const UA_NodeId *sessionId, void *sessionContext,
            const UA_NodeId *sessionId, void *sessionContext,
@@ -78,11 +89,11 @@ helloWorld(UA_Server *server,
            size_t inputSize, const UA_Variant *input,
            size_t inputSize, const UA_Variant *input,
            size_t outputSize, UA_Variant *output) {
            size_t outputSize, UA_Variant *output) {
     /* input is a scalar string (checked by the server) */
     /* 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 hello = UA_STRING("Hello ");
     UA_String greet;
     UA_String greet;
     greet.length = hello.length + name->length;
     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.data, hello.length);
     memcpy(greet.data + hello.length, name->data, name->length);
     memcpy(greet.data + hello.length, name->data, name->length);
     UA_Variant_setScalarCopy(output, &greet, &UA_TYPES[UA_TYPES_STRING]);
     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]);
     UA_Variant_setScalarCopy(output, &out, &UA_TYPES[UA_TYPES_INT32]);
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
+
 #endif
 #endif
 
 
-int main(int argc, char** argv) {
+int
+main(int argc, char **argv) {
     signal(SIGINT, stopHandler); /* catches ctrl-c */
     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 *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);
     UA_ByteString_deleteMembers(&certificate);
+#endif
+
     /* uncomment next line to add a custom hostname */
     /* uncomment next line to add a custom hostname */
     // UA_ServerConfig_set_customHostname(config, UA_STRING("custom"));
     // UA_ServerConfig_set_customHostname(config, UA_STRING("custom"));
-    
+
     UA_Server *server = UA_Server_new(config);
     UA_Server *server = UA_Server_new(config);
+    if(server == NULL)
+        return 1;
 
 
     /* add a static variable node to the server */
     /* add a static variable node to the server */
     UA_VariableAttributes myVar = UA_VariableAttributes_default;
     UA_VariableAttributes myVar = UA_VariableAttributes_default;
     myVar.description = UA_LOCALIZEDTEXT("en-US", "the answer");
     myVar.description = UA_LOCALIZEDTEXT("en-US", "the answer");
     myVar.displayName = UA_LOCALIZEDTEXT("en-US", "the answer");
     myVar.displayName = UA_LOCALIZEDTEXT("en-US", "the answer");
     myVar.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
     myVar.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
+    myVar.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
+    myVar.valueRank = -1;
     UA_Int32 myInteger = 42;
     UA_Int32 myInteger = 42;
     UA_Variant_setScalarCopy(&myVar.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
     UA_Variant_setScalarCopy(&myVar.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
     const UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
     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 parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
     UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId, parentReferenceNodeId,
     UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId, parentReferenceNodeId,
-        myIntegerName, UA_NODEID_NULL, myVar, NULL, NULL);
+                              myIntegerName, baseDataVariableType, myVar, NULL, NULL);
     UA_Variant_deleteMembers(&myVar.value);
     UA_Variant_deleteMembers(&myVar.value);
 
 
     /* add a variable with the datetime data source */
     /* add a variable with the datetime data source */
@@ -145,13 +209,15 @@ int main(int argc, char** argv) {
     dateDataSource.read = readTimeData;
     dateDataSource.read = readTimeData;
     dateDataSource.write = NULL;
     dateDataSource.write = NULL;
     UA_VariableAttributes v_attr = UA_VariableAttributes_default;
     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");
     const UA_QualifiedName dateName = UA_QUALIFIEDNAME(1, "current time");
     UA_Server_addDataSourceVariableNode(server, UA_NODEID_NULL, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
     UA_Server_addDataSourceVariableNode(server, UA_NODEID_NULL, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                                         UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), dateName,
                                         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 */
     /* Add HelloWorld method to the server */
 #ifdef UA_ENABLE_METHODCALLS
 #ifdef UA_ENABLE_METHODCALLS
@@ -177,11 +243,11 @@ int main(int argc, char** argv) {
     addmethodattributes.executable = true;
     addmethodattributes.executable = true;
     addmethodattributes.userExecutable = true;
     addmethodattributes.userExecutable = true;
     UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1, 62541),
     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
 #endif
 
 
     /* Add folders for demo information model */
     /* 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.description = UA_LOCALIZEDTEXT("en-US", "Demo");
     object_attr.displayName = UA_LOCALIZEDTEXT("en-US", "Demo");
     object_attr.displayName = UA_LOCALIZEDTEXT("en-US", "Demo");
     UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, DEMOID),
     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.description = UA_LOCALIZEDTEXT("en-US", "Scalar");
     object_attr.displayName = UA_LOCALIZEDTEXT("en-US", "Scalar");
     object_attr.displayName = UA_LOCALIZEDTEXT("en-US", "Scalar");
     UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, SCALARID),
     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.description = UA_LOCALIZEDTEXT("en-US", "Array");
     object_attr.displayName = UA_LOCALIZEDTEXT("en-US", "Array");
     object_attr.displayName = UA_LOCALIZEDTEXT("en-US", "Array");
     UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, ARRAYID),
     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.description = UA_LOCALIZEDTEXT("en-US", "Matrix");
     object_attr.displayName = 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_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*/
     /* Fill demo nodes for each type*/
     UA_UInt32 id = 51000; // running id in namespace 0
     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_Variant_setScalar(&attr.value, value, &UA_TYPES[type]);
         UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id),
         UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id),
                                   UA_NODEID_NUMERIC(1, SCALARID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
                                   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);
         UA_Variant_deleteMembers(&attr.value);
 
 
         /* add an array node for every built-in type */
         /* 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_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_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id), UA_NODEID_NUMERIC(1, ARRAYID),
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), qualifiedName,
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), qualifiedName,
-                                  UA_NODEID_NULL, attr, NULL, NULL);
+                                  baseDataVariableType, attr, NULL, NULL);
         UA_Variant_deleteMembers(&attr.value);
         UA_Variant_deleteMembers(&attr.value);
 
 
         /* add an matrix node for every built-in type */
         /* 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[0] = 3;
         attr.value.arrayDimensions[1] = 3;
         attr.value.arrayDimensions[1] = 3;
         attr.value.arrayDimensionsSize = 2;
         attr.value.arrayDimensionsSize = 2;
@@ -271,7 +337,7 @@ int main(int argc, char** argv) {
         attr.value.type = &UA_TYPES[type];
         attr.value.type = &UA_TYPES[type];
         UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id), UA_NODEID_NUMERIC(1, MATRIXID),
         UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id), UA_NODEID_NUMERIC(1, MATRIXID),
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), qualifiedName,
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), qualifiedName,
-                                  UA_NODEID_NULL, attr, NULL, NULL);
+                                  baseDataVariableType, attr, NULL, NULL);
         UA_Variant_deleteMembers(&attr.value);
         UA_Variant_deleteMembers(&attr.value);
 #ifdef UA_ENABLE_TYPENAMES
 #ifdef UA_ENABLE_TYPENAMES
         UA_LocalizedText_deleteMembers(&attr.displayName);
         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 */
     /* Hierarchy of depth 10 for CTT testing with forward and inverse references */
     /* Enter node "depth 9" in CTT configuration - Project->Settings->Server
     /* Enter node "depth 9" in CTT configuration - Project->Settings->Server
        Test->NodeIds->Paths->Starting Node 1 */
        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_ORGANIZES), UA_QUALIFIEDNAME(1, "DepthDemo"),
                             UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL, NULL);
                             UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL, NULL);
 
 
@@ -297,10 +362,11 @@ int main(int argc, char** argv) {
 #else
 #else
         sprintf(name, "depth%i", i);
         sprintf(name, "depth%i", i);
 #endif
 #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_QUALIFIEDNAME(1, name),
                                 UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL, NULL);
                                 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.executable = true;
     addmethodattributes.userExecutable = true;
     addmethodattributes.userExecutable = true;
     UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1, NOARGID),
     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 */
     /* Method with in arguments */
     addmethodattributes = UA_MethodAttributes_default;
     addmethodattributes = UA_MethodAttributes_default;
@@ -347,11 +413,11 @@ int main(int argc, char** argv) {
     inputArguments.valueRank = -1; //uaexpert will crash if set to 0 ;)
     inputArguments.valueRank = -1; //uaexpert will crash if set to 0 ;)
 
 
     UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1, INARGID),
     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 */
     /* Method with out arguments */
     addmethodattributes = UA_MethodAttributes_default;
     addmethodattributes = UA_MethodAttributes_default;
@@ -366,11 +432,11 @@ int main(int argc, char** argv) {
     outputArguments.valueRank = -1;
     outputArguments.valueRank = -1;
 
 
     UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1, OUTARGID),
     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 */
     /* Method with inout arguments */
     addmethodattributes = UA_MethodAttributes_default;
     addmethodattributes = UA_MethodAttributes_default;
@@ -379,11 +445,11 @@ int main(int argc, char** argv) {
     addmethodattributes.userExecutable = true;
     addmethodattributes.userExecutable = true;
 
 
     UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1, INOUTARGID),
     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
 #endif
 
 
     /* run server */
     /* run server */

+ 155 - 28
examples/server_inheritance.c

@@ -9,31 +9,28 @@ static void stopHandler(int sig) {
     running = false;
     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;
     UA_ObjectTypeAttributes otAttr = UA_ObjectTypeAttributes_default;
     otAttr.description = UA_LOCALIZEDTEXT("en-US", "A mamal");
     otAttr.description = UA_LOCALIZEDTEXT("en-US", "A mamal");
     otAttr.displayName = UA_LOCALIZEDTEXT("en-US", "MamalType");
     otAttr.displayName = UA_LOCALIZEDTEXT("en-US", "MamalType");
@@ -50,7 +47,7 @@ int main(void) {
     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(1, 10000),
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
                               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, NULL, NULL);
 
 
     vAttr = UA_VariableAttributes_default;
     vAttr = UA_VariableAttributes_default;
@@ -59,7 +56,7 @@ int main(void) {
     UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 10002),
     UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 10002),
                               UA_NODEID_NUMERIC(1, 10000),
                               UA_NODEID_NUMERIC(1, 10000),
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
                               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, NULL, NULL);
 
 
     otAttr = UA_ObjectTypeAttributes_default;
     otAttr = UA_ObjectTypeAttributes_default;
@@ -78,7 +75,7 @@ int main(void) {
     UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 20001),
     UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 20001),
                               UA_NODEID_NUMERIC(1, 20000),
                               UA_NODEID_NUMERIC(1, 20000),
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
                               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, NULL, NULL);
 
 
     vAttr = UA_VariableAttributes_default;
     vAttr = UA_VariableAttributes_default;
@@ -89,7 +86,7 @@ int main(void) {
     UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 20002),
     UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 20002),
                               UA_NODEID_NUMERIC(1, 20000),
                               UA_NODEID_NUMERIC(1, 20000),
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
                               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);
                               vAttr, NULL, NULL);
 
 
     /* Instatiate a dog named bello:
     /* Instatiate a dog named bello:
@@ -126,8 +123,138 @@ int main(void) {
                             UA_QUALIFIEDNAME(1, "Mamal1"), UA_NODEID_NUMERIC(1, 10000),
                             UA_QUALIFIEDNAME(1, "Mamal1"), UA_NODEID_NUMERIC(1, 10000),
                             oAttr, NULL, NULL);
                             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 */
     /* Run the server */
-    retval = UA_Server_run(server, &running);
+    UA_StatusCode retval = UA_Server_run(server, &running);
     UA_Server_delete(server);
     UA_Server_delete(server);
     UA_ServerConfig_delete(config);
     UA_ServerConfig_delete(config);
     return (int)retval;
     return (int)retval;

+ 12 - 12
examples/server_instantiation.c

@@ -15,7 +15,7 @@ int main(void) {
 
 
     UA_ServerConfig *config = UA_ServerConfig_new_default();
     UA_ServerConfig *config = UA_ServerConfig_new_default();
     UA_Server *server = UA_Server_new(config);
     UA_Server *server = UA_Server_new(config);
-    
+
     /* Create a rudimentary objectType
     /* Create a rudimentary objectType
      * 
      * 
      * BaseObjectType
      * BaseObjectType
@@ -30,43 +30,43 @@ int main(void) {
     UA_ObjectTypeAttributes otAttr = UA_ObjectTypeAttributes_default;
     UA_ObjectTypeAttributes otAttr = UA_ObjectTypeAttributes_default;
     otAttr.description = UA_LOCALIZEDTEXT("en-US", "A mamal");
     otAttr.description = UA_LOCALIZEDTEXT("en-US", "A mamal");
     otAttr.displayName = UA_LOCALIZEDTEXT("en-US", "MamalType");
     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_BASEOBJECTTYPE),
                                 UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
                                 UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
                                 UA_QUALIFIEDNAME(1, "MamalType"), otAttr, NULL, NULL);
                                 UA_QUALIFIEDNAME(1, "MamalType"), otAttr, NULL, NULL);
-  
+
     UA_VariableAttributes vAttr = UA_VariableAttributes_default;
     UA_VariableAttributes vAttr = UA_VariableAttributes_default;
     vAttr.description =  UA_LOCALIZEDTEXT("en-US", "This mamals Age in months");
     vAttr.description =  UA_LOCALIZEDTEXT("en-US", "This mamals Age in months");
     vAttr.displayName =  UA_LOCALIZEDTEXT("en-US", "Age");
     vAttr.displayName =  UA_LOCALIZEDTEXT("en-US", "Age");
     UA_UInt32 ageVar = 0;
     UA_UInt32 ageVar = 0;
     UA_Variant_setScalar(&vAttr.value, &ageVar, &UA_TYPES[UA_TYPES_UINT32]);
     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_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 = UA_ObjectTypeAttributes_default;
     otAttr.description = UA_LOCALIZEDTEXT("en-US", "A dog, subtype of mamal");
     otAttr.description = UA_LOCALIZEDTEXT("en-US", "A dog, subtype of mamal");
     otAttr.displayName = UA_LOCALIZEDTEXT("en-US", "DogType");
     otAttr.displayName = UA_LOCALIZEDTEXT("en-US", "DogType");
     UA_Server_addObjectTypeNode(server, UA_NODEID_NUMERIC(1, 10002),
     UA_Server_addObjectTypeNode(server, UA_NODEID_NUMERIC(1, 10002),
                                 UA_NODEID_NUMERIC(1, 10000), UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
                                 UA_NODEID_NUMERIC(1, 10000), UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
                                 UA_QUALIFIEDNAME(1, "DogType"), otAttr, NULL, NULL);
                                 UA_QUALIFIEDNAME(1, "DogType"), otAttr, NULL, NULL);
-    
+
     vAttr = UA_VariableAttributes_default;
     vAttr = UA_VariableAttributes_default;
     vAttr.description =  UA_LOCALIZEDTEXT("en-US", "This mamals Age in months");
     vAttr.description =  UA_LOCALIZEDTEXT("en-US", "This mamals Age in months");
     vAttr.displayName =  UA_LOCALIZEDTEXT("en-US", "Name");
     vAttr.displayName =  UA_LOCALIZEDTEXT("en-US", "Name");
     UA_String defaultName = UA_STRING("unnamed dog");
     UA_String defaultName = UA_STRING("unnamed dog");
     UA_Variant_setScalar(&vAttr.value, &defaultName, &UA_TYPES[UA_TYPES_STRING]);
     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_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:
     /* Instatiate a dog named bello:
      * (O) Objects
      * (O) Objects
      *   + O Bello <DogType>
      *   + O Bello <DogType>
      *     + Age 
      *     + Age 
      *     + Name
      *     + Name
      */
      */
-    
+
     UA_ObjectAttributes oAttr = UA_ObjectAttributes_default;
     UA_ObjectAttributes oAttr = UA_ObjectAttributes_default;
     oAttr.description = UA_LOCALIZEDTEXT("en-US", "A dog named Bello");
     oAttr.description = UA_LOCALIZEDTEXT("en-US", "A dog named Bello");
     oAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Bello");
     oAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Bello");
@@ -75,7 +75,7 @@ int main(void) {
                             UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                             UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                             UA_QUALIFIEDNAME(1, "Bello"), UA_NODEID_NUMERIC(1, 10002),
                             UA_QUALIFIEDNAME(1, "Bello"), UA_NODEID_NUMERIC(1, 10002),
                             oAttr, NULL, NULL);
                             oAttr, NULL, NULL);
-    
+
     retval = UA_Server_run(server, &running);
     retval = UA_Server_run(server, &running);
     UA_Server_delete(server);
     UA_Server_delete(server);
     UA_ServerConfig_delete(config);
     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_ServerConfig config = UA_ServerConfig_standard;
     UA_ServerNetworkLayer nl;
     UA_ServerNetworkLayer nl;
-    nl = UA_ServerNetworkLayerUDP(UA_ConnectionConfig_standard, 16664);
+    nl = UA_ServerNetworkLayerUDP(UA_ConnectionConfig_standard, 4840);
     config.networkLayers = &nl;
     config.networkLayers = &nl;
     config.networkLayersSize = 1;
     config.networkLayersSize = 1;
     UA_Server *server = UA_Server_new(config);
     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. */
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
 
 
 #include <signal.h>
 #include <signal.h>
+#include <stdio.h>
 #include "open62541.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 UA_Boolean running = true;
 static void stopHandler(int sig) {
 static void stopHandler(int sig) {
     UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "received ctrl-c");
     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
 #ifdef UA_ENABLE_SUBSCRIPTIONS
+
 static void
 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");
     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) {
     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_UInt16 severity = *(UA_UInt16 *)eventFields[i].data;
             UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Severity: %u", severity);
             UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Severity: %u", severity);
         } else if (UA_Variant_hasScalarType(&eventFields[i], &UA_TYPES[UA_TYPES_LOCALIZEDTEXT])) {
         } 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].typeDefinitionId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE);
     selectClauses[0].browsePathSize = 1;
     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) {
     if(!selectClauses[0].browsePath) {
         UA_SimpleAttributeOperand_delete(selectClauses);
         UA_SimpleAttributeOperand_delete(selectClauses);
         return NULL;
         return NULL;
@@ -55,7 +76,8 @@ setupSelectClauses(void) {
 
 
     selectClauses[1].typeDefinitionId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE);
     selectClauses[1].typeDefinitionId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE);
     selectClauses[1].browsePathSize = 1;
     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) {
     if(!selectClauses[1].browsePath) {
         UA_SimpleAttributeOperand_deleteMembers(selectClauses);
         UA_SimpleAttributeOperand_deleteMembers(selectClauses);
         UA_SimpleAttributeOperand_delete(selectClauses);
         UA_SimpleAttributeOperand_delete(selectClauses);
@@ -73,9 +95,16 @@ int main(int argc, char *argv[]) {
     signal(SIGINT, stopHandler);
     signal(SIGINT, stopHandler);
     signal(SIGTERM, 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_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) {
     if(retval != UA_STATUSCODE_GOOD) {
         UA_Client_delete(client);
         UA_Client_delete(client);
         return (int)retval;
         return (int)retval;
@@ -83,49 +112,62 @@ int main(int argc, char *argv[]) {
 
 
 #ifdef UA_ENABLE_SUBSCRIPTIONS
 #ifdef UA_ENABLE_SUBSCRIPTIONS
     /* Create a subscription */
     /* 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_disconnect(client);
         UA_Client_delete(client);
         UA_Client_delete(client);
         return (int)retval;
         return (int)retval;
     }
     }
+    UA_UInt32 subId = response.subscriptionId;
     UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Create subscription succeeded, id %u", subId);
     UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Create subscription succeeded, id %u", subId);
 
 
     /* Add a MonitoredItem */
     /* 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_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 */
     /* 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
 #endif
 
 
     UA_Client_disconnect(client);
     UA_Client_disconnect(client);
     UA_Client_delete(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);
     UA_Variant_init(&value);
 
 
     /* NodeId of the variable holding the current time */
     /* 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);
     retval = UA_Client_readValueAttribute(client, nodeId, &value);
+
     if(retval == UA_STATUSCODE_GOOD &&
     if(retval == UA_STATUSCODE_GOOD &&
        UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_DATETIME])) {
        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 */
     /* Clean up */
@@ -60,7 +59,7 @@ int main(void) {
  * ^^^^^^^^^^^^^
  * ^^^^^^^^^^^^^
  *
  *
  * - Try to connect to some other OPC UA server by changing
  * - 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).
  *   queried node is contained in any OPC UA server).
  *
  *
  * - Try to set the value of the variable node (ns=1,i="the.answer") containing
  * - 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);
     UA_String_deleteMembers(&s4);
     if(!eq)
     if(!eq)
         return;
         return;
-    
+
     /* Structured Type */
     /* Structured Type */
     UA_CallRequest cr;
     UA_CallRequest cr;
     UA_init(&cr, &UA_TYPES[UA_TYPES_CALLREQUEST]); /* Generic method */
     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
  * 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
  * 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,
  * 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 system clock as the underlying "process".
  *
  *
  * The following code snippets are each concerned with a different way of
  * 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_QualifiedName currentName = UA_QUALIFIEDNAME(1, "current-time");
     UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
     UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
     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,
     UA_Server_addVariableNode(server, currentNodeId, parentNodeId,
                               parentReferenceNodeId, currentName,
                               parentReferenceNodeId, currentName,
                               variableTypeNodeId, attr, NULL, NULL);
                               variableTypeNodeId, attr, NULL, NULL);
@@ -70,7 +70,7 @@ addCurrentTimeVariable(UA_Server *server) {
 static void
 static void
 beforeReadTime(UA_Server *server,
 beforeReadTime(UA_Server *server,
                const UA_NodeId *sessionId, void *sessionContext,
                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) {
                const UA_NumericRange *range, const UA_DataValue *data) {
     UA_DateTime now = UA_DateTime_now();
     UA_DateTime now = UA_DateTime_now();
     UA_Variant value;
     UA_Variant value;
@@ -140,7 +140,7 @@ addCurrentTimeDataSourceVariable(UA_Server *server) {
     UA_QualifiedName currentName = UA_QUALIFIEDNAME(1, "current-time-datasource");
     UA_QualifiedName currentName = UA_QUALIFIEDNAME(1, "current-time-datasource");
     UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
     UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
     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;
     UA_DataSource timeDataSource;
     timeDataSource.read = readCurrentTime;
     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
  * *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
  * cases and runs on all platforms. Users can then adjust the library to fit
  * their use case via configuration and by developing (platform-specific)
  * their use case via configuration and by developing (platform-specific)
  * plugins. The core library is based on C99 only and does not even require
  * 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_Server_addObjectNode(server, UA_NODEID_NULL,
                             UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                             UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                             UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
                             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);
                             oAttr, NULL, &pumpId);
 
 
     UA_VariableAttributes mnAttr = UA_VariableAttributes_default;
     UA_VariableAttributes mnAttr = UA_VariableAttributes_default;
@@ -76,7 +76,7 @@ manuallyDefinePump(UA_Server *server) {
     UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpId,
     UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpId,
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_QUALIFIEDNAME(1, "ManufacturerName"),
                               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_VariableAttributes modelAttr = UA_VariableAttributes_default;
     UA_String modelName = UA_STRING("Mega Pump 3000");
     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_Server_addVariableNode(server, UA_NODEID_NULL, pumpId,
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_QUALIFIEDNAME(1, "ModelName"),
                               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_VariableAttributes statusAttr = UA_VariableAttributes_default;
     UA_Boolean status = true;
     UA_Boolean status = true;
@@ -94,7 +94,7 @@ manuallyDefinePump(UA_Server *server) {
     UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpId,
     UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpId,
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_QUALIFIEDNAME(1, "Status"),
                               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_VariableAttributes rpmAttr = UA_VariableAttributes_default;
     UA_Double rpm = 50.0;
     UA_Double rpm = 50.0;
@@ -103,7 +103,7 @@ manuallyDefinePump(UA_Server *server) {
     UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpId,
     UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpId,
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_QUALIFIEDNAME(1, "MotorRPMs"),
                               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_Server_addVariableNode(server, UA_NODEID_NULL, deviceTypeId,
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_QUALIFIEDNAME(1, "ManufacturerName"),
                               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 */
     /* Make the manufacturer name mandatory */
     UA_Server_addReference(server, manufacturerNameId,
     UA_Server_addReference(server, manufacturerNameId,
                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE),
@@ -195,7 +195,7 @@ defineObjectTypes(UA_Server *server) {
     UA_Server_addVariableNode(server, UA_NODEID_NULL, deviceTypeId,
     UA_Server_addVariableNode(server, UA_NODEID_NULL, deviceTypeId,
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_QUALIFIEDNAME(1, "ModelName"),
                               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" */
     /* Define the object type for "Pump" */
     UA_ObjectTypeAttributes ptAttr = UA_ObjectTypeAttributes_default;
     UA_ObjectTypeAttributes ptAttr = UA_ObjectTypeAttributes_default;
@@ -212,7 +212,7 @@ defineObjectTypes(UA_Server *server) {
     UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpTypeId,
     UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpTypeId,
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_QUALIFIEDNAME(1, "Status"),
                               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 */
     /* Make the status variable mandatory */
     UA_Server_addReference(server, statusId,
     UA_Server_addReference(server, statusId,
                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE),
@@ -224,7 +224,7 @@ defineObjectTypes(UA_Server *server) {
     UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpTypeId,
     UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpTypeId,
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               UA_QUALIFIEDNAME(1, "MotorRPMs"),
                               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.isInverse = false;
     rpe.includeSubtypes = false;
     rpe.includeSubtypes = false;
     rpe.targetName = UA_QUALIFIEDNAME(1, "Status");
     rpe.targetName = UA_QUALIFIEDNAME(1, "Status");
-    
+
     UA_BrowsePath bp;
     UA_BrowsePath bp;
     UA_BrowsePath_init(&bp);
     UA_BrowsePath_init(&bp);
     bp.startingNode = *nodeId;
     bp.startingNode = *nodeId;
     bp.relativePath.elementsSize = 1;
     bp.relativePath.elementsSize = 1;
     bp.relativePath.elements = &rpe;
     bp.relativePath.elements = &rpe;
-    
+
     UA_BrowsePathResult bpr =
     UA_BrowsePathResult bpr =
         UA_Server_translateBrowsePathToNodeIds(server, &bp);
         UA_Server_translateBrowsePathToNodeIds(server, &bp);
     if(bpr.statusCode != UA_STATUSCODE_GOOD ||
     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_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
     UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
     UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
                               parentReferenceNodeId, myIntegerName,
                               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_String s = UA_STRING("2dpoint?");
     UA_Variant_setScalar(&vAttr.value, &s, &UA_TYPES[UA_TYPES_STRING]);
     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_Server_addVariableNode(server, UA_NODEID_NULL,
                               UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                               UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                               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
 /* 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
  * 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_
 #ifndef UA_CLIENT_H_
 #define UA_CLIENT_H_
 #define UA_CLIENT_H_
@@ -14,6 +25,7 @@ extern "C" {
 #include "ua_types_generated_handling.h"
 #include "ua_types_generated_handling.h"
 #include "ua_plugin_network.h"
 #include "ua_plugin_network.h"
 #include "ua_plugin_log.h"
 #include "ua_plugin_log.h"
+#include "ua_client_config.h"
 
 
 /**
 /**
  * .. _client:
  * .. _client:
@@ -32,40 +44,12 @@ extern "C" {
  * `UA_Client_Subscriptions_manuallySendPublishRequest`. See also :ref:`here
  * `UA_Client_Subscriptions_manuallySendPublishRequest`. See also :ref:`here
  * <client-subscriptions>`.
  * <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
  * 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 */
 /* Create a new client */
 UA_Client UA_EXPORT *
 UA_Client UA_EXPORT *
 UA_Client_new(UA_ClientConfig config);
 UA_Client_new(UA_ClientConfig config);
@@ -74,6 +58,10 @@ UA_Client_new(UA_ClientConfig config);
 UA_ClientState UA_EXPORT
 UA_ClientState UA_EXPORT
 UA_Client_getState(UA_Client *client);
 UA_Client_getState(UA_Client *client);
 
 
+/* Get the client context */
+void UA_EXPORT *
+UA_Client_getContext(UA_Client *client);
+
 /* Reset a client */
 /* Reset a client */
 void UA_EXPORT
 void UA_EXPORT
 UA_Client_reset(UA_Client *client);
 UA_Client_reset(UA_Client *client);
@@ -89,7 +77,7 @@ UA_Client_delete(UA_Client *client);
 /* Connect to the server
 /* Connect to the server
  *
  *
  * @param client to use
  * @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 */
  * @return Indicates whether the operation succeeded or returns an error code */
 UA_StatusCode UA_EXPORT
 UA_StatusCode UA_EXPORT
 UA_Client_connect(UA_Client *client, const char *endpointUrl);
 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
 /* Connect to the selected server with the given username and password
  *
  *
  * @param client to use
  * @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 username
  * @param password
  * @param password
  * @return Indicates whether the operation succeeded or returns an error code */
  * @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,
 UA_Client_connect_username(UA_Client *client, const char *endpointUrl,
                            const char *username, const char *password);
                            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_StatusCode UA_EXPORT
 UA_Client_disconnect(UA_Client *client);
 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 */
 /* Renew the underlying secure channel */
 UA_StatusCode UA_EXPORT
 UA_StatusCode UA_EXPORT
 UA_Client_manuallyRenewSecureChannel(UA_Client *client);
 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
  * @param client to use. Must be connected to the same endpoint given in
  *        serverUrl or otherwise in disconnected state.
  *        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 endpointDescriptionsSize size of the array of endpoint descriptions
  * @param endpointDescriptions array of endpoint descriptions that is allocated
  * @param endpointDescriptions array of endpoint descriptions that is allocated
  *        by the function (you need to free manually)
  *        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
  * @param client to use. Must be connected to the same endpoint given in
  *        serverUrl or otherwise in disconnected state.
  *        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 serverUrisSize Optional filter for specific server uris
  * @param serverUris Optional filter for specific server uris
  * @param serverUris Optional filter for specific server uris
  * @param localeIdsSize Optional indication which locale you prefer
  * @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
  * @param client to use. Must be connected to the same endpoint given in
  * serverUrl or otherwise in disconnected state.
  * 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
  * @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
  *        or equal the given. Can be used for pagination to only get a subset of
  *        the full list
  *        the full list
@@ -187,6 +179,7 @@ UA_Client_findServersOnNetwork(UA_Client *client, const char *serverUrl,
  *
  *
  * Services
  * Services
  * --------
  * --------
+ *
  * The raw OPC UA services are exposed to the client. But most of them time, it
  * 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``
  * is better to use the convenience functions from ``ua_client_highlevel.h``
  * that wrap the raw services. */
  * 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 *requestType, void *response,
                     const UA_DataType *responseType);
                     const UA_DataType *responseType);
 
 
-/**
+/*
  * Attribute Service Set
  * Attribute Service Set
  * ^^^^^^^^^^^^^^^^^^^^^ */
  * ^^^^^^^^^^^^^^^^^^^^^ */
 static UA_INLINE UA_ReadResponse
 static UA_INLINE UA_ReadResponse
@@ -215,7 +208,7 @@ UA_Client_Service_write(UA_Client *client, const UA_WriteRequest request) {
     return response;
     return response;
 }
 }
 
 
-/**
+/*
  * Method Service Set
  * Method Service Set
  * ^^^^^^^^^^^^^^^^^^ */
  * ^^^^^^^^^^^^^^^^^^ */
 #ifdef UA_ENABLE_METHODCALLS
 #ifdef UA_ENABLE_METHODCALLS
@@ -228,7 +221,7 @@ UA_Client_Service_call(UA_Client *client, const UA_CallRequest request) {
 }
 }
 #endif
 #endif
 
 
-/**
+/*
  * NodeManagement Service Set
  * NodeManagement Service Set
  * ^^^^^^^^^^^^^^^^^^^^^^^^^^ */
  * ^^^^^^^^^^^^^^^^^^^^^^^^^^ */
 static UA_INLINE UA_AddNodesResponse
 static UA_INLINE UA_AddNodesResponse
@@ -266,7 +259,7 @@ UA_Client_Service_deleteReferences(UA_Client *client,
     return response;
     return response;
 }
 }
 
 
-/**
+/*
  * View Service Set
  * View Service Set
  * ^^^^^^^^^^^^^^^^ */
  * ^^^^^^^^^^^^^^^^ */
 static UA_INLINE UA_BrowseResponse
 static UA_INLINE UA_BrowseResponse
@@ -316,7 +309,7 @@ UA_Client_Service_unregisterNodes(UA_Client *client,
     return response;
     return response;
 }
 }
 
 
-/**
+/*
  * Query Service Set
  * Query Service Set
  * ^^^^^^^^^^^^^^^^^ */
  * ^^^^^^^^^^^^^^^^^ */
 static UA_INLINE UA_QueryFirstResponse
 static UA_INLINE UA_QueryFirstResponse
@@ -337,74 +330,6 @@ UA_Client_Service_queryNext(UA_Client *client,
     return response;
     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:
  * .. _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
  * be made without waiting for a response first. Responess may come in a
  * different ordering. */
  * 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
 typedef void
 (*UA_ClientAsyncServiceCallback)(UA_Client *client, void *userdata,
 (*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_StatusCode UA_EXPORT
 __UA_Client_AsyncService(UA_Client *client, const void *request,
 __UA_Client_AsyncService(UA_Client *client, const void *request,
                          const UA_DataType *requestType,
                          const UA_DataType *requestType,
@@ -426,13 +370,51 @@ __UA_Client_AsyncService(UA_Client *client, const void *request,
                          const UA_DataType *responseType,
                          const UA_DataType *responseType,
                          void *userdata, UA_UInt32 *requestId);
                          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::
  * .. toctree::
  *
  *
- *    client_highlevel */
+ *    client_highlevel
+ *    client_subscriptions */
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 } // extern "C"
 } // 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
 /* 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
  * 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_
 #ifndef UA_CLIENT_HIGHLEVEL_H_
 #define 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);
                                &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
  * Misc Highlevel Functionality
  * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
  * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
@@ -640,10 +580,8 @@ UA_Client_NamespaceGetIndex(UA_Client *client, UA_String *namespaceUri,
 #define HAVE_NODEITER_CALLBACK
 #define HAVE_NODEITER_CALLBACK
 /* Iterate over all nodes referenced by parentNodeId by calling the callback
 /* Iterate over all nodes referenced by parentNodeId by calling the callback
    function for each child node */
    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
 #endif
 
 
 UA_StatusCode UA_EXPORT
 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_NODEMANAGEMENT
 #cmakedefine UA_ENABLE_SUBSCRIPTIONS
 #cmakedefine UA_ENABLE_SUBSCRIPTIONS
 #cmakedefine UA_ENABLE_MULTITHREADING
 #cmakedefine UA_ENABLE_MULTITHREADING
+#cmakedefine UA_ENABLE_ENCRYPTION
 
 
 /* Advanced Options */
 /* Advanced Options */
 #cmakedefine UA_ENABLE_STATUSCODE_DESCRIPTIONS
 #cmakedefine UA_ENABLE_STATUSCODE_DESCRIPTIONS
@@ -38,6 +39,8 @@ extern "C" {
 #cmakedefine UA_ENABLE_DISCOVERY_MULTICAST
 #cmakedefine UA_ENABLE_DISCOVERY_MULTICAST
 #cmakedefine UA_ENABLE_DISCOVERY_SEMAPHORE
 #cmakedefine UA_ENABLE_DISCOVERY_SEMAPHORE
 #cmakedefine UA_ENABLE_UNIT_TEST_FAILURE_HOOKS
 #cmakedefine UA_ENABLE_UNIT_TEST_FAILURE_HOOKS
+#cmakedefine UA_ENABLE_VALGRIND_INTERACTIVE
+#define UA_VALGRIND_INTERACTIVE_INTERVAL ${UA_VALGRIND_INTERACTIVE_INTERVAL}
 
 
 /* Options for Debugging */
 /* Options for Debugging */
 #cmakedefine UA_DEBUG
 #cmakedefine UA_DEBUG
@@ -90,6 +93,9 @@ extern "C" {
 #elif defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER) /* GCC, Clang, MSC */
 #elif defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER) /* GCC, Clang, MSC */
 # define UA_CTASTR2(pre,post) pre ## post
 # define UA_CTASTR2(pre,post) pre ## post
 # define UA_CTASTR(pre,post) UA_CTASTR2(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)                             \
 # define UA_STATIC_ASSERT(cond,msg)                             \
     typedef struct {                                            \
     typedef struct {                                            \
         int UA_CTASTR(static_assertion_failed_,msg) : !!(cond); \
         int UA_CTASTR(static_assertion_failed_,msg) : !!(cond); \
@@ -109,18 +115,36 @@ extern "C" {
 # include <malloc.h>
 # include <malloc.h>
 #endif
 #endif
 
 
-#define UA_free(ptr) free(ptr)
-#define UA_malloc(size) malloc(size)
-#define UA_calloc(num, size) calloc(num, size)
-#define UA_realloc(ptr, size) realloc(ptr, size)
+#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__)
 #if defined(__GNUC__) || defined(__clang__)
-# define UA_alloca(size) __builtin_alloca (size)
+# define UA_STACKARRAY(TYPE, NAME, SIZE) TYPE NAME[SIZE]
 #elif defined(_WIN32)
 #elif defined(_WIN32)
-# define UA_alloca(SIZE) _alloca(SIZE)
+# define UA_STACKARRAY(TYPE, NAME, SIZE) \
+    TYPE *NAME = (TYPE*)_alloca(sizeof(TYPE) * SIZE)
 #else
 #else
 # include <alloca.h>
 # include <alloca.h>
-# define UA_alloca(SIZE) alloca(SIZE)
+# define UA_STACKARRAY(TYPE, NAME, SIZE) \
+    TYPE *NAME = (TYPE*)alloca(sizeof(TYPE) * SIZE)
 #endif
 #endif
 
 
 /**
 /**
@@ -211,22 +235,16 @@ extern "C" {
  * ^^^^^^^^^^^^^^^^^^
  * ^^^^^^^^^^^^^^^^^^
  * The definition ``UA_BINARY_OVERLAYABLE_INTEGER`` is true when the integer
  * The definition ``UA_BINARY_OVERLAYABLE_INTEGER`` is true when the integer
  * representation of the target architecture is little-endian. */
  * 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
 # 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>
 # include <endian.h>
 # if __BYTE_ORDER == __LITTLE_ENDIAN
 # if __BYTE_ORDER == __LITTLE_ENDIAN
 #  define UA_BINARY_OVERLAYABLE_INTEGER 1
 #  define UA_BINARY_OVERLAYABLE_INTEGER 1
 # endif
 # endif
-# if __FLOAT_BYTE_ORDER == __LITTLE_ENDIAN
-#  define UA_BINARY_OVERLAYABLE_FLOAT 1
-# endif
 #elif defined(__OpenBSD__) /* OpenBSD */
 #elif defined(__OpenBSD__) /* OpenBSD */
 # include <sys/endian.h>
 # include <sys/endian.h>
 # if BYTE_ORDER == LITTLE_ENDIAN
 # if BYTE_ORDER == LITTLE_ENDIAN
@@ -259,7 +277,10 @@ extern "C" {
  * The definition ``UA_BINARY_OVERLAYABLE_FLOAT`` is true when the floating
  * The definition ``UA_BINARY_OVERLAYABLE_FLOAT`` is true when the floating
  * point number representation of the target architecture is IEEE 754. Note that
  * point number representation of the target architecture is IEEE 754. Note that
  * this cannot be reliable detected with macros for the clang compiler
  * 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)
 #if defined(_WIN32)
 # define UA_BINARY_OVERLAYABLE_FLOAT 1
 # define UA_BINARY_OVERLAYABLE_FLOAT 1
 #elif defined(__FLOAT_WORD_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
 #elif defined(__FLOAT_WORD_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
@@ -268,6 +289,15 @@ extern "C" {
 #elif defined(__FLOAT_WORD_ORDER) && defined(__LITTLE_ENDIAN) && \
 #elif defined(__FLOAT_WORD_ORDER) && defined(__LITTLE_ENDIAN) && \
     (__FLOAT_WORD_ORDER == __LITTLE_ENDIAN) /* Defined only in GCC */
     (__FLOAT_WORD_ORDER == __LITTLE_ENDIAN) /* Defined only in GCC */
 # define UA_BINARY_OVERLAYABLE_FLOAT 1
 # 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)
 #elif defined(_WRS_KERNEL)
 # define UA_BINARY_OVERLAYABLE_FLOAT 1
 # define UA_BINARY_OVERLAYABLE_FLOAT 1
 #endif
 #endif

+ 19 - 9
include/ua_constants.h

@@ -1,6 +1,12 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
 /* 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
  * 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_
 #ifndef UA_CONSTANTS_H_
 #define 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_GOODCOMPLETESASYNCHRONOUSLY 0x002e0000 // The processing will complete asynchronously.
 #define UA_STATUSCODE_GOODOVERLOAD 0x002f0000 // Sampling has slowed down due to resource limitations.
 #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_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_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_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.
 #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_BADEVENTFILTERINVALID 0x80470000 // The event filter is not valid.
 #define UA_STATUSCODE_BADCONTENTFILTERINVALID 0x80480000 // The content 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_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_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_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.
 #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_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_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_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_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_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.
 #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_BADSEQUENCENUMBERINVALID 0x80880000 // The sequence number is not valid.
 #define UA_STATUSCODE_BADPROTOCOLVERSIONUNSUPPORTED 0x80be0000 // The applications do not have compatible protocol versions.
 #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_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_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_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.
 #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_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_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_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_BADCONDITIONALREADYDISABLED 0x80980000 // This condition has already been disabled.
 #define UA_STATUSCODE_BADCONDITIONALREADYENABLED 0x80cc0000 // This condition has already been enabled.
 #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_BADEVENTIDUNKNOWN 0x809a0000 // The specified event id is not recognized.
 #define UA_STATUSCODE_BADEVENTNOTACKNOWLEDGEABLE 0x80bb0000 // The event cannot be acknowledged.
 #define UA_STATUSCODE_BADEVENTNOTACKNOWLEDGEABLE 0x80bb0000 // The event cannot be acknowledged.
 #define UA_STATUSCODE_BADDIALOGNOTACTIVE 0x80cd0000 // The dialog condition is not active.
 #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_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_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_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_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_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).
 #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_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_BADDISCONNECT 0x80ad0000 // The server has disconnected from the client.
 #define UA_STATUSCODE_BADCONNECTIONCLOSED 0x80ae0000 // The network connection has been closed.
 #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_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_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.
 #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_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.
 #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
  * 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
 /* 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
  * 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_
 #ifndef UA_PLUGIN_ACCESS_CONTROL_H_
 #define UA_PLUGIN_ACCESS_CONTROL_H_
 #define UA_PLUGIN_ACCESS_CONTROL_H_
@@ -12,60 +16,77 @@ extern "C" {
 #include "ua_types.h"
 #include "ua_types.h"
 
 
 /**
 /**
+ * .. _access-control:
+ *
  * Access Control Plugin API
  * Access Control Plugin API
  * =========================
  * =========================
  * The access control callback is used to authenticate sessions and grant access
  * The access control callback is used to authenticate sessions and grant access
  * rights accordingly. */
  * 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
     /* Authenticate a session. The session context is attached to the session and
      * later passed into the node-based access control callbacks. */
      * 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,
                                      const UA_ExtensionObject *userIdentityToken,
                                      void **sessionContext);
                                      void **sessionContext);
 
 
     /* Deauthenticate a session and cleanup */
     /* 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*/
     /* 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);
                                    const UA_NodeId *nodeId, void *nodeContext);
 
 
     /* Additional access control for variable nodes */
     /* 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);
                                   const UA_NodeId *nodeId, void *nodeContext);
 
 
     /* Additional access control for method nodes */
     /* 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);
                                     const UA_NodeId *methodId, void *methodContext);
 
 
     /* Additional access control for calling a method node in the context of a
     /* Additional access control for calling a method node in the context of a
      * specific object */
      * 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 *methodId, void *methodContext,
                                             const UA_NodeId *objectId, void *objectContext);
                                             const UA_NodeId *objectId, void *objectContext);
 
 
     /* Allow adding a node */
     /* 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);
                                const UA_AddNodesItem *item);
 
 
     /* Allow adding a reference */
     /* 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);
                                     const UA_AddReferencesItem *item);
 
 
     /* Allow deleting a node */
     /* 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);
                                   const UA_DeleteNodesItem *item);
 
 
     /* Allow deleting a reference */
     /* 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);
                                        const UA_DeleteReferencesItem *item);
-} UA_AccessControl;
+};
 
 
 #ifdef __cplusplus
 #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
 /* 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 
  * 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_
 #ifndef UA_PLUGIN_LOG_H_
 #define 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
 /* 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
  * 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_
 #ifndef UA_PLUGIN_NETWORK_H_
 #define UA_PLUGIN_NETWORK_H_
 #define UA_PLUGIN_NETWORK_H_
@@ -10,6 +14,7 @@ extern "C" {
 #endif
 #endif
 
 
 #include "ua_server.h"
 #include "ua_server.h"
+#include "ua_plugin_log.h"
 
 
 /* Forward declarations */
 /* Forward declarations */
 struct UA_Connection;
 struct UA_Connection;
@@ -46,12 +51,12 @@ typedef struct {
 } UA_ConnectionConfig;
 } UA_ConnectionConfig;
 
 
 typedef enum {
 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
     UA_CONNECTION_OPENING,     /* The socket is open, but the HEL/ACK handshake
                                 * is not done */
                                 * is not done */
-    UA_CONNECTION_ESTABLISHED, /* The socket is open and the connection
+    UA_CONNECTION_ESTABLISHED  /* The socket is open and the connection
                                 * configured */
                                 * configured */
-    UA_CONNECTION_CLOSED       /* The socket has been closed and the connection
-                                * will be deleted */
 } UA_ConnectionState;
 } UA_ConnectionState;
 
 
 struct UA_Connection {
 struct UA_Connection {
@@ -189,10 +194,11 @@ struct UA_ServerNetworkLayer {
 
 
 /* @param localConf the connection config for this client
 /* @param localConf the connection config for this client
  * @param endpointUrl to where to connect
  * @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
 typedef UA_Connection
 (*UA_ConnectClientConnection)(UA_ConnectionConfig localConf, const char *endpointUrl,
 (*UA_ConnectClientConnection)(UA_ConnectionConfig localConf, const char *endpointUrl,
-                              const UA_UInt32 timeout);
+                              const UA_UInt32 timeout, UA_Logger logger);
 
 
 /**
 /**
  * Endpoint URL Parser
  * 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
 /* 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
  * 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_
 #ifndef UA_SERVER_NODES_H_
 #define 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
 /* 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
  * 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_
 #ifndef UA_PLUGIN_SECURITYPOLICY_H_
 #define UA_PLUGIN_SECURITYPOLICY_H_
 #define UA_PLUGIN_SECURITYPOLICY_H_
@@ -12,6 +17,7 @@ extern "C" {
 #include "ua_types.h"
 #include "ua_types.h"
 #include "ua_types_generated.h"
 #include "ua_types_generated.h"
 #include "ua_plugin_log.h"
 #include "ua_plugin_log.h"
+#include "ua_plugin_pki.h"
 
 
 extern const UA_ByteString UA_SECURITY_POLICY_NONE_URI;
 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_SecurityPolicy UA_SecurityPolicy;
 
 
 typedef struct {
 typedef struct {
-    UA_String signatureAlgorithmUri;
+    UA_String uri;
 
 
     /* Verifies the signature of the message using the provided keys in the context.
     /* 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 message the message to which the signature is supposed to belong.
      * @param signature the signature of the message, that should be verified. */
      * @param signature the signature of the message, that should be verified. */
     UA_StatusCode (*verify)(const UA_SecurityPolicy *securityPolicy,
     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;
                             const UA_ByteString *signature) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 
 
     /* Signs the given message using this policys signing algorithm and the
     /* 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
      *                  necessary size can be acquired with the signatureSize
      *                  attribute of this module. */
      *                  attribute of this module. */
     UA_StatusCode (*sign)(const UA_SecurityPolicy *securityPolicy,
     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;
                           UA_ByteString *signature) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 
 
     /* Gets the signature size that depends on the local (private) key.
     /* Gets the signature size that depends on the local (private) key.
@@ -66,7 +72,27 @@ typedef struct {
     size_t (*getRemoteSignatureSize)(const UA_SecurityPolicy *securityPolicy,
     size_t (*getRemoteSignatureSize)(const UA_SecurityPolicy *securityPolicy,
                                      const void *channelContext);
                                      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.
     /* Encrypt the given data in place using an asymmetric algorithm and keys.
      *
      *
@@ -75,9 +101,9 @@ typedef struct {
      *                       the keys to encrypt data.
      *                       the keys to encrypt data.
      * @param data the data that is encrypted. The encrypted data will overwrite
      * @param data the data that is encrypted. The encrypted data will overwrite
      *             the data that was supplied. */
      *             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
     /* Decrypts the given ciphertext in place using an asymmetric algorithm and
      * key.
      * key.
@@ -86,9 +112,9 @@ typedef struct {
      * @param channelContext the channelContext which contains information about
      * @param channelContext the channelContext which contains information about
      *                       the keys needed to decrypt the message.
      *                       the keys needed to decrypt the message.
      * @param data the data to decrypt. The decryption is done in place. */
      * @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
     /* 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.
      * @param channelContext the context to retrieve data from.
      * @return the length of the local key. Returns 0 if no
      * @return the length of the local key. Returns 0 if no
      *         key length is known. */
      *         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
     /* 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.
      * @param channelContext the context to retrieve data from.
      * @return the length of the remote key. Returns 0 if no
      * @return the length of the remote key. Returns 0 if no
      *         key length is known. */
      *         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;
 } UA_SecurityPolicyCryptoModule;
 
 
 typedef struct {
 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 securityPolicy the securityPolicy the function is invoked on.
      * @param certificate the certificate to make a thumbprint of.
      * @param certificate the certificate to make a thumbprint of.
      * @param thumbprint an output buffer for the resulting thumbprint. Always
      * @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. */
      *                   asymmetricModule. */
     UA_StatusCode (*makeCertificateThumbprint)(const UA_SecurityPolicy *securityPolicy,
     UA_StatusCode (*makeCertificateThumbprint)(const UA_SecurityPolicy *securityPolicy,
                                                const UA_ByteString *certificate,
                                                const UA_ByteString *certificate,
                                                UA_ByteString *thumbprint)
                                                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.
     /* 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
      * @param certificateThumbprint the certificate thumbprint to compare to the
      *                              one stored in the context.
      *                              one stored in the context.
      * @return if the thumbprints match UA_STATUSCODE_GOOD is returned. If they
      * @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,
     UA_StatusCode (*compareCertificateThumbprint)(const UA_SecurityPolicy *securityPolicy,
                                                   const UA_ByteString *certificateThumbprint)
                                                   const UA_ByteString *certificateThumbprint)
-        UA_FUNC_ATTR_WARN_UNUSED_RESULT;
+    UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 
 
     UA_SecurityPolicyCryptoModule cryptoModule;
     UA_SecurityPolicyCryptoModule cryptoModule;
 } UA_SecurityPolicyAsymmetricModule;
 } UA_SecurityPolicyAsymmetricModule;
@@ -151,7 +226,8 @@ typedef struct {
     UA_StatusCode (*generateKey)(const UA_SecurityPolicy *securityPolicy,
     UA_StatusCode (*generateKey)(const UA_SecurityPolicy *securityPolicy,
                                  const UA_ByteString *secret,
                                  const UA_ByteString *secret,
                                  const UA_ByteString *seed, UA_ByteString *out)
                                  const UA_ByteString *seed, UA_ByteString *out)
-        UA_FUNC_ATTR_WARN_UNUSED_RESULT;
+    UA_FUNC_ATTR_WARN_UNUSED_RESULT;
+
     /* Random generator for generating nonces.
     /* Random generator for generating nonces.
      *
      *
      * @param securityPolicy the securityPolicy this function is invoked on.
      * @param securityPolicy the securityPolicy this function is invoked on.
@@ -162,17 +238,20 @@ typedef struct {
      *            data. */
      *            data. */
     UA_StatusCode (*generateNonce)(const UA_SecurityPolicy *securityPolicy,
     UA_StatusCode (*generateNonce)(const UA_SecurityPolicy *securityPolicy,
                                    UA_ByteString *out)
                                    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;
     UA_SecurityPolicyCryptoModule cryptoModule;
-    size_t encryptionBlockSize;
-    size_t signingKeyLength;
 } UA_SecurityPolicySymmetricModule;
 } UA_SecurityPolicySymmetricModule;
 
 
 typedef struct {
 typedef struct {
     /* This method creates a new context data object.
     /* 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
      * 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
      * need to manually free the memory pointed to by *channelContext or to
      * call delete in case of failure.
      * call delete in case of failure.
@@ -189,7 +268,7 @@ typedef struct {
     UA_StatusCode (*newContext)(const UA_SecurityPolicy *securityPolicy,
     UA_StatusCode (*newContext)(const UA_SecurityPolicy *securityPolicy,
                                 const UA_ByteString *remoteCertificate,
                                 const UA_ByteString *remoteCertificate,
                                 void **channelContext)
                                 void **channelContext)
-        UA_FUNC_ATTR_WARN_UNUSED_RESULT;
+    UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 
 
     /* Deletes the the security context. */
     /* Deletes the the security context. */
     void (*deleteContext)(void *channelContext);
     void (*deleteContext)(void *channelContext);
@@ -200,7 +279,7 @@ typedef struct {
      * @param key the local encrypting key to store in the context. */
      * @param key the local encrypting key to store in the context. */
     UA_StatusCode (*setLocalSymEncryptingKey)(void *channelContext,
     UA_StatusCode (*setLocalSymEncryptingKey)(void *channelContext,
                                               const UA_ByteString *key)
                                               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.
     /* 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. */
      * @param key the local signing key to store in the context. */
     UA_StatusCode (*setLocalSymSigningKey)(void *channelContext,
     UA_StatusCode (*setLocalSymSigningKey)(void *channelContext,
                                            const UA_ByteString *key)
                                            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.
     /* 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. */
      * @param iv the local initialization vector to store in the context. */
     UA_StatusCode (*setLocalSymIv)(void *channelContext,
     UA_StatusCode (*setLocalSymIv)(void *channelContext,
                                    const UA_ByteString *iv)
                                    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.
     /* 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. */
      * @param key the remote encrypting key to store in the context. */
     UA_StatusCode (*setRemoteSymEncryptingKey)(void *channelContext,
     UA_StatusCode (*setRemoteSymEncryptingKey)(void *channelContext,
                                                const UA_ByteString *key)
                                                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.
     /* 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. */
      * @param key the remote signing key to store in the context. */
     UA_StatusCode (*setRemoteSymSigningKey)(void *channelContext,
     UA_StatusCode (*setRemoteSymSigningKey)(void *channelContext,
                                             const UA_ByteString *key)
                                             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.
     /* 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. */
      * @param iv the remote initialization vector to store in the context. */
     UA_StatusCode (*setRemoteSymIv)(void *channelContext,
     UA_StatusCode (*setRemoteSymIv)(void *channelContext,
                                     const UA_ByteString *iv)
                                     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
     /* Compares the supplied certificate with the certificate in the channel
      * context.
      * context.
@@ -249,30 +328,10 @@ typedef struct {
      *                       certificate to compare to.
      *                       certificate to compare to.
      * @param certificate the certificate to compare to the one stored in the context.
      * @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
      * @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,
     UA_StatusCode (*compareCertificate)(const void *channelContext,
                                         const UA_ByteString *certificate)
                                         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;
 } UA_SecurityPolicyChannelModule;
 
 
 struct UA_SecurityPolicy {
 struct UA_SecurityPolicy {
@@ -289,7 +348,9 @@ struct UA_SecurityPolicy {
     /* Function pointers grouped into modules */
     /* Function pointers grouped into modules */
     UA_SecurityPolicyAsymmetricModule asymmetricModule;
     UA_SecurityPolicyAsymmetricModule asymmetricModule;
     UA_SecurityPolicySymmetricModule symmetricModule;
     UA_SecurityPolicySymmetricModule symmetricModule;
+    UA_SecurityPolicySignatureAlgorithm certificateSigningAlgorithm;
     UA_SecurityPolicyChannelModule channelModule;
     UA_SecurityPolicyChannelModule channelModule;
+    UA_CertificateVerification *certificateVerification;
 
 
     UA_Logger logger;
     UA_Logger logger;
 
 
@@ -302,6 +363,21 @@ typedef struct {
     UA_EndpointDescription endpointDescription;
     UA_EndpointDescription endpointDescription;
 } UA_Endpoint;
 } 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
 #ifdef __cplusplus
 }
 }
 #endif
 #endif

+ 86 - 24
include/ua_server.h

@@ -1,6 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
 /* 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
  * 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_
 #ifndef UA_SERVER_H_
 #define UA_SERVER_H_
 #define UA_SERVER_H_
@@ -19,12 +27,16 @@ typedef struct UA_ServerConfig UA_ServerConfig;
 struct UA_Server;
 struct UA_Server;
 typedef struct UA_Server UA_Server;
 typedef struct UA_Server UA_Server;
 
 
+struct UA_ClientConfig;
+
 /**
 /**
  * .. _server:
  * .. _server:
  *
  *
  * Server
  * Server
  * ======
  * ======
  *
  *
+ * .. include:: server_config.rst
+ *
  * .. _server-lifecycle:
  * .. _server-lifecycle:
  *
  *
  * Server Lifecycle
  * Server Lifecycle
@@ -92,7 +104,7 @@ UA_Server_changeRepeatedCallbackInterval(UA_Server *server, UA_UInt64 callbackId
  *
  *
  * @param server The server object.
  * @param server The server object.
  * @param callbackId The id of the callback that shall be removed.
  * @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. */
  *         An error code otherwise. */
 UA_StatusCode UA_EXPORT
 UA_StatusCode UA_EXPORT
 UA_Server_removeRepeatedCallback(UA_Server *server, UA_UInt64 callbackId);
 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_DataValue UA_EXPORT
 UA_Server_read(UA_Server *server, const UA_ReadValueId *item,
 UA_Server_read(UA_Server *server, const UA_ReadValueId *item,
                UA_TimestampsToReturn timestamps);
                UA_TimestampsToReturn timestamps);
-    
+
 /* Don't use this function. There are typed versions for every supported
 /* Don't use this function. There are typed versions for every supported
  * attribute. */
  * attribute. */
 UA_StatusCode UA_EXPORT
 UA_StatusCode UA_EXPORT
@@ -638,6 +650,18 @@ UA_Server_setNodeContext(UA_Server *server, UA_NodeId nodeId,
  * be set to a null-pointer. */
  * be set to a null-pointer. */
 typedef struct {
 typedef struct {
     /* Copies the data from the source into the provided value.
     /* 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
      * @param handle An optional pointer to user-defined data for the
      *        specific data source
      *        specific data source
@@ -659,8 +683,8 @@ typedef struct {
                           void *nodeContext, UA_Boolean includeSourceTimeStamp,
                           void *nodeContext, UA_Boolean includeSourceTimeStamp,
                           const UA_NumericRange *range, UA_DataValue *value);
                           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
      * @param handle An optional pointer to user-defined data for the
      *        specific data source
      *        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,
  * reference it later. When passing numeric NodeIds with a numeric identifier 0,
  * the stack evaluates this as "select a random unassigned numeric NodeId in
  * 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
  * 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
  * node insertion) contain the nodeId of the new node. You may also pass a
  * ``NULL`` pointer if this result is not needed.
  * ``NULL`` pointer if this result is not needed.
  *
  *
@@ -926,14 +950,33 @@ UA_Server_addDataSourceVariableNode(UA_Server *server,
                                     void *nodeContext, UA_NodeId *outNewNodeId);
                                     void *nodeContext, UA_NodeId *outNewNodeId);
 
 
 UA_StatusCode UA_EXPORT
 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,
 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
  * pseudo-random unique NodeIds. Existing children are detected during the
  * _finish part via their matching BrowseName.
  * _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.
 /* The ``attr`` argument must have a type according to the NodeClass.
  * ``VariableAttributes`` for variables, ``ObjectAttributes`` for objects, and
  * ``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_StatusCode UA_EXPORT
 UA_Server_addNode_begin(UA_Server *server, const UA_NodeClass nodeClass,
 UA_Server_addNode_begin(UA_Server *server, const UA_NodeClass nodeClass,
                         const UA_NodeId requestedNewNodeId,
                         const UA_NodeId requestedNewNodeId,
+                        const UA_NodeId parentNodeId,
+                        const UA_NodeId referenceTypeId,
                         const UA_QualifiedName browseName,
                         const UA_QualifiedName browseName,
                         const UA_NodeId typeDefinition,
                         const UA_NodeId typeDefinition,
                         const void *attr, const UA_DataType *attributeType,
                         const void *attr, const UA_DataType *attributeType,
                         void *nodeContext, UA_NodeId *outNewNodeId);
                         void *nodeContext, UA_NodeId *outNewNodeId);
 
 
 UA_StatusCode UA_EXPORT
 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. */
 /* Deletes a node and optionally all references leading to the node. */
 UA_StatusCode UA_EXPORT
 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 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
  * end-user. Please use plain UA_ServerCallbacks instead. The following UA_Job
  * definition contains just the fraction of the original struct that was useful
  * definition contains just the fraction of the original struct that was useful
  * to end-users. */
  * 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
 /* 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
  * 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_
 #ifndef UA_SERVER_CONFIG_H_
 #define UA_SERVER_CONFIG_H_
 #define UA_SERVER_CONFIG_H_
@@ -13,13 +18,31 @@ extern "C" {
 #include "ua_plugin_log.h"
 #include "ua_plugin_log.h"
 #include "ua_plugin_network.h"
 #include "ua_plugin_network.h"
 #include "ua_plugin_access_control.h"
 #include "ua_plugin_access_control.h"
+#include "ua_plugin_pki.h"
 #include "ua_plugin_securitypolicy.h"
 #include "ua_plugin_securitypolicy.h"
 #include "ua_plugin_nodestore.h"
 #include "ua_plugin_nodestore.h"
 
 
 /**
 /**
+ * .. _server-configuration:
+ *
  * 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 {
 typedef struct {
     UA_UInt32 min;
     UA_UInt32 min;
@@ -39,6 +62,8 @@ struct UA_ServerConfig {
     UA_BuildInfo buildInfo;
     UA_BuildInfo buildInfo;
     UA_ApplicationDescription applicationDescription;
     UA_ApplicationDescription applicationDescription;
     UA_ByteString serverCertificate;
     UA_ByteString serverCertificate;
+
+    /* MDNS Discovery */
 #ifdef UA_ENABLE_DISCOVERY
 #ifdef UA_ENABLE_DISCOVERY
     UA_String mdnsServerName;
     UA_String mdnsServerName;
     size_t serverCapabilitiesSize;
     size_t serverCapabilitiesSize;
@@ -48,6 +73,10 @@ struct UA_ServerConfig {
     /* Custom DataTypes */
     /* Custom DataTypes */
     size_t customDataTypesSize;
     size_t customDataTypesSize;
     UA_DataType *customDataTypes;
     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 */
     /* Nodestore */
     UA_Nodestore nodestore;
     UA_Nodestore nodestore;
@@ -56,16 +85,25 @@ struct UA_ServerConfig {
     size_t networkLayersSize;
     size_t networkLayersSize;
     UA_ServerNetworkLayer *networkLayers;
     UA_ServerNetworkLayer *networkLayers;
     UA_String customHostname;
     UA_String customHostname;
-    
+
     /* Available endpoints */
     /* Available endpoints */
     size_t endpointsSize;
     size_t endpointsSize;
     UA_Endpoint *endpoints;
     UA_Endpoint *endpoints;
 
 
-    /* Global Node Lifecycle */
+    /* Node Lifecycle callbacks */
     UA_GlobalNodeLifecycle nodeLifecycle;
     UA_GlobalNodeLifecycle nodeLifecycle;
+    /**
+     * .. note:: See the section for :ref:`node lifecycle
+     *    handling<node-lifecycle>`. */
 
 
     /* Access Control */
     /* Access Control */
     UA_AccessControl accessControl;
     UA_AccessControl accessControl;
+    /**
+     * .. note:: See the section for :ref:`access-control
+     *    handling<access-control>`. */
+
+    /* Certificate Verification */
+    UA_CertificateVerification certificateVerification;
 
 
     /* Limits for SecureChannels */
     /* Limits for SecureChannels */
     UA_UInt16 maxSecureChannels;
     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
 /* 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
  * 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_
 #ifndef UA_TYPES_H_
 #define 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 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:
  * .. _datetime:
  *
  *
@@ -170,27 +184,29 @@ UA_STRING(char *chars) {
  * ^^^^^^^^
  * ^^^^^^^^
  * An instance in time. A DateTime value is encoded as a 64-bit signed integer
  * 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
  * 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);
 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);
 UA_DateTime UA_EXPORT UA_DateTime_nowMonotonic(void);
 
 
+/* Represents a Datetime as a structure */
 typedef struct UA_DateTimeStruct {
 typedef struct UA_DateTimeStruct {
     UA_UInt16 nanoSec;
     UA_UInt16 nanoSec;
     UA_UInt16 microSec;
     UA_UInt16 microSec;
@@ -205,7 +221,23 @@ typedef struct UA_DateTimeStruct {
 
 
 UA_DateTimeStruct UA_EXPORT UA_DateTime_toStruct(UA_DateTime t);
 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
  * Guid
@@ -348,6 +380,9 @@ typedef struct {
     UA_UInt32 serverIndex;
     UA_UInt32 serverIndex;
 } UA_ExpandedNodeId;
 } 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;
 UA_EXPORT extern const UA_ExpandedNodeId UA_EXPANDEDNODEID_NULL;
 
 
 /** The following functions are shorthand for creating ExpandedNodeIds. */
 /** 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;
     qn.name = UA_STRING_ALLOC(chars); return qn;
 }
 }
 
 
+UA_Boolean UA_EXPORT
+UA_QualifiedName_equal(const UA_QualifiedName *qn1,
+                       const UA_QualifiedName *qn2);
+
 /**
 /**
  * LocalizedText
  * 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
  * 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
  * 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. */
  * target NodeId is stored instead of the decoded value. */
 typedef enum {
 typedef enum {
     UA_EXTENSIONOBJECT_ENCODED_NOBODY     = 0,
     UA_EXTENSIONOBJECT_ENCODED_NOBODY     = 0,
@@ -744,7 +783,7 @@ typedef struct {
                                      types */
                                      types */
     UA_Byte   padding;            /* How much padding is there before this
     UA_Byte   padding;            /* How much padding is there before this
                                      member element? For arrays this is the
                                      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
                                      (No padding between size_t and the
                                      following ptr.) */
                                      following ptr.) */
     UA_Boolean namespaceZero : 1; /* The type of the member is defined in
     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;
     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
 #ifdef __cplusplus
 } // extern "C"
 } // extern "C"
 #endif
 #endif

+ 2 - 2
open62541.spec

@@ -6,7 +6,7 @@ License:  MPLv2.0
 URL:      http://open62541.org
 URL:      http://open62541.org
 Source0:  https://github.com/open62541/open62541/archive/%{name}-%{version}.tar.gz
 Source0:  https://github.com/open62541/open62541/archive/%{name}-%{version}.tar.gz
 
 
-BuildRequires: cmake3, python
+BuildRequires: cmake3, python, python-six
 
 
 %description
 %description
 open62541 is a C-based library (linking with C++ projects is possible)
 open62541 is a C-based library (linking with C++ projects is possible)
@@ -49,7 +49,7 @@ rm examples/CMakeLists.txt
 %{_libdir}/pkgconfig/open62541.pc
 %{_libdir}/pkgconfig/open62541.pc
 %dir %{_includedir}/open62541
 %dir %{_includedir}/open62541
 %{_includedir}/open62541/*
 %{_includedir}/open62541/*
-%{_libdir}/cmake3/open62541*
+%{_libdir}/cmake/open62541*
 %dir %{_exec_prefix}/share/open62541
 %dir %{_exec_prefix}/share/open62541
 %{_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.
 /* 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"
 #include "ua_accesscontrol_default.h"
 
 
 /* Example access control management. Anonymous and username / password login.
 /* Example access control management. Anonymous and username / password login.
  * The access rights are maximally permissive. */
  * 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 ANONYMOUS_POLICY "open62541-anonymous-policy"
 #define USERNAME_POLICY "open62541-username-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 anonymous_policy = UA_STRING_STATIC(ANONYMOUS_POLICY);
 const UA_String username_policy = UA_STRING_STATIC(USERNAME_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,
                         const UA_ExtensionObject *userIdentityToken,
                         void **sessionContext) {
                         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? */
     /* Could the token be decoded? */
     if(userIdentityToken->encoding < UA_EXTENSIONOBJECT_DECODED)
     if(userIdentityToken->encoding < UA_EXTENSIONOBJECT_DECODED)
         return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
         return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
 
 
     /* Anonymous login */
     /* 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
         /* Compatibility notice: Siemens OPC Scout v10 provides an empty
          * policyId. This is not compliant. For compatibility, assume that empty
          * policyId. This is not compliant. For compatibility, assume that empty
          * policyId == ANONYMOUS_POLICY */
          * 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;
             return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
 
 
         /* No userdata atm */
         /* No userdata atm */
@@ -52,24 +66,26 @@ activateSession_default(const UA_NodeId *sessionId,
     }
     }
 
 
     /* Username and password */
     /* 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;
             (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;
             return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
 
 
         /* Empty username and password */
         /* 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;
             return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
 
 
         /* Try to match username/pw */
         /* Try to match username/pw */
         UA_Boolean match = false;
         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;
                 match = true;
                 break;
                 break;
             }
             }
@@ -86,56 +102,151 @@ activateSession_default(const UA_NodeId *sessionId,
     return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
     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 */
     /* 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) {
                           const UA_NodeId *nodeId, void *nodeContext) {
     return 0xFFFFFFFF;
     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) {
                            const UA_NodeId *nodeId, void *nodeContext) {
     return 0xFF;
     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) {
                           const UA_NodeId *methodId, void *methodContext) {
     return true;
     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 *methodId, void *methodContext,
                                   const UA_NodeId *objectId, void *objectContext) {
                                   const UA_NodeId *objectId, void *objectContext) {
     return true;
     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) {
                      const UA_AddNodesItem *item) {
     return true;
     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) {
                           const UA_AddReferencesItem *item) {
     return true;
     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) {
                         const UA_DeleteNodesItem *item) {
     return true;
     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) {
                              const UA_DeleteReferencesItem *item) {
     return true;
     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.
 /* 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_
 #ifndef UA_ACCESSCONTROL_DEFAULT_H_
 #define UA_ACCESSCONTROL_DEFAULT_H_
 #define UA_ACCESSCONTROL_DEFAULT_H_
 
 
 #include "ua_server.h"
 #include "ua_server.h"
+#include "ua_plugin_access_control.h"
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 extern "C" {
 extern "C" {
 #endif
 #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
 #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.
 /* 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 */
 /* Enable POSIX features */
 #if !defined(_XOPEN_SOURCE) && !defined(_WRS_KERNEL)
 #if !defined(_XOPEN_SOURCE) && !defined(_WRS_KERNEL)
@@ -40,6 +45,10 @@
 
 
 #include "ua_types.h"
 #include "ua_types.h"
 
 
+#if defined(UA_FREERTOS)
+#include <task.h>
+#endif
+
 UA_DateTime UA_DateTime_now(void) {
 UA_DateTime UA_DateTime_now(void) {
 #if defined(_WIN32)
 #if defined(_WIN32)
     /* Windows filetime has the same definition as UA_DateTime */
     /* Windows filetime has the same definition as UA_DateTime */
@@ -54,16 +63,38 @@ UA_DateTime UA_DateTime_now(void) {
 #else
 #else
     struct timeval tv;
     struct timeval tv;
     gettimeofday(&tv, NULL);
     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
 #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) {
 UA_DateTime UA_DateTime_nowMonotonic(void) {
 #if defined(_WIN32)
 #if defined(_WIN32)
     LARGE_INTEGER freq, ticks;
     LARGE_INTEGER freq, ticks;
     QueryPerformanceFrequency(&freq);
     QueryPerformanceFrequency(&freq);
     QueryPerformanceCounter(&ticks);
     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);
     return (UA_DateTime)(ticks.QuadPart * ticks2dt);
 #elif defined(__APPLE__) || defined(__MACH__)
 #elif defined(__APPLE__) || defined(__MACH__)
     /* OS X does not have clock_gettime, use clock_get_time */
     /* 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);
     host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
     clock_get_time(cclock, &mts);
     clock_get_time(cclock, &mts);
     mach_port_deallocate(mach_task_self(), cclock);
     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)
 #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;
     struct timespec ts;
     clock_gettime(CLOCK_MONOTONIC, &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
 #else
     struct timespec ts;
     struct timespec ts;
     clock_gettime(CLOCK_MONOTONIC_RAW, &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
 #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.
 /* 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_config_default.h"
+#include "ua_client_config.h"
 #include "ua_log_stdout.h"
 #include "ua_log_stdout.h"
 #include "ua_network_tcp.h"
 #include "ua_network_tcp.h"
 #include "ua_accesscontrol_default.h"
 #include "ua_accesscontrol_default.h"
+#include "ua_pki_certificate.h"
 #include "ua_nodestore_default.h"
 #include "ua_nodestore_default.h"
-#include "ua_types_generated.h"
 #include "ua_securitypolicy_none.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
 /* Struct initialization works across ANSI C/C99/C++ if it is done when the
  * variable is first declared. Assigning values to existing structs is
  * 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
 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};
     UA_DurationRange range = {min, max};
     return range;
     return range;
 }
 }
@@ -61,33 +71,24 @@ createSecurityPolicyNoneEndpoint(UA_ServerConfig *conf, UA_Endpoint *endpoint,
                                  const UA_ByteString localCertificate) {
                                  const UA_ByteString localCertificate) {
     UA_EndpointDescription_init(&endpoint->endpointDescription);
     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.securityMode = UA_MESSAGESECURITYMODE_NONE;
     endpoint->endpointDescription.securityPolicyUri =
     endpoint->endpointDescription.securityPolicyUri =
         UA_STRING_ALLOC("http://opcfoundation.org/UA/SecurityPolicy#None");
         UA_STRING_ALLOC("http://opcfoundation.org/UA/SecurityPolicy#None");
     endpoint->endpointDescription.transportProfileUri =
     endpoint->endpointDescription.transportProfileUri =
         UA_STRING_ALLOC("http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary");
         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_String_copy(&localCertificate, &endpoint->endpointDescription.serverCertificate);
-
     UA_ApplicationDescription_copy(&conf->applicationDescription,
     UA_ApplicationDescription_copy(&conf->applicationDescription,
                                    &endpoint->endpointDescription.server);
                                    &endpoint->endpointDescription.server);
 
 
@@ -95,17 +96,103 @@ createSecurityPolicyNoneEndpoint(UA_ServerConfig *conf, UA_Endpoint *endpoint,
 }
 }
 
 
 void
 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)
     if(!config)
         return;
         return;
     UA_String_deleteMembers(&config->customHostname);
     UA_String_deleteMembers(&config->customHostname);
     UA_String_copy(&customHostname, &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)
     if(!conf)
         return NULL;
         return NULL;
 
 
@@ -123,12 +210,14 @@ UA_ServerConfig_new_minimal(UA_UInt16 portNumber,
     conf->buildInfo.softwareVersion =
     conf->buildInfo.softwareVersion =
         UA_STRING_ALLOC(VERSION(UA_OPEN62541_VER_MAJOR, UA_OPEN62541_VER_MINOR,
         UA_STRING_ALLOC(VERSION(UA_OPEN62541_VER_MAJOR, UA_OPEN62541_VER_MINOR,
                                 UA_OPEN62541_VER_PATCH, UA_OPEN62541_VER_LABEL));
                                 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->buildInfo.buildDate = 0;
 
 
     conf->applicationDescription.applicationUri = UA_STRING_ALLOC(APPLICATION_URI);
     conf->applicationDescription.applicationUri = UA_STRING_ALLOC(APPLICATION_URI);
     conf->applicationDescription.productUri = UA_STRING_ALLOC(PRODUCT_URI);
     conf->applicationDescription.productUri = UA_STRING_ALLOC(PRODUCT_URI);
-    conf->applicationDescription.applicationName = 
+    conf->applicationDescription.applicationName =
         UA_LOCALIZEDTEXT_ALLOC("en", APPLICATION_NAME);
         UA_LOCALIZEDTEXT_ALLOC("en", APPLICATION_NAME);
     conf->applicationDescription.applicationType = UA_APPLICATIONTYPE_SERVER;
     conf->applicationDescription.applicationType = UA_APPLICATIONTYPE_SERVER;
     /* conf->applicationDescription.gatewayServerUri = UA_STRING_NULL; */
     /* conf->applicationDescription.gatewayServerUri = UA_STRING_NULL; */
@@ -150,27 +239,20 @@ UA_ServerConfig_new_minimal(UA_UInt16 portNumber,
     /* conf->networkLayersSize = 0; */
     /* conf->networkLayersSize = 0; */
     /* conf->networkLayers = NULL; */
     /* conf->networkLayers = NULL; */
     /* conf->customHostname = UA_STRING_NULL; */
     /* conf->customHostname = UA_STRING_NULL; */
- 
+
     /* Endpoints */
     /* Endpoints */
     /* conf->endpoints = {0, NULL}; */
     /* 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 */
     /* Global Node Lifecycle */
     conf->nodeLifecycle.constructor = NULL;
     conf->nodeLifecycle.constructor = NULL;
     conf->nodeLifecycle.destructor = 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 */
     /* Limits for SecureChannels */
     conf->maxSecureChannels = 40;
     conf->maxSecureChannels = 40;
@@ -197,26 +279,43 @@ UA_ServerConfig_new_minimal(UA_UInt16 portNumber,
 
 
     /* --> Finish setting the default static config <-- */
     /* --> 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);
     UA_StatusCode retval = UA_Nodestore_default_new(&conf->nodestore);
     if(retval != UA_STATUSCODE_GOOD) {
     if(retval != UA_STATUSCODE_GOOD) {
         UA_ServerConfig_delete(conf);
         UA_ServerConfig_delete(conf);
         return NULL;
         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);
         UA_ServerConfig_delete(conf);
         return NULL;
         return NULL;
     }
     }
-    conf->networkLayers[0] =
-        UA_ServerNetworkLayerTCP(UA_ConnectionConfig_default, portNumber);
-    conf->networkLayersSize = 1;
 
 
     /* Allocate the endpoint */
     /* Allocate the endpoint */
     conf->endpointsSize = 1;
     conf->endpointsSize = 1;
-    conf->endpoints = (UA_Endpoint*)UA_malloc(sizeof(UA_Endpoint));
+    conf->endpoints = (UA_Endpoint *)UA_malloc(sizeof(UA_Endpoint));
     if(!conf->endpoints) {
     if(!conf->endpoints) {
         UA_ServerConfig_delete(conf);
         UA_ServerConfig_delete(conf);
         return NULL;
         return NULL;
@@ -236,6 +335,239 @@ UA_ServerConfig_new_minimal(UA_UInt16 portNumber,
     return conf;
     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
 void
 UA_ServerConfig_delete(UA_ServerConfig *config) {
 UA_ServerConfig_delete(UA_ServerConfig *config) {
     if(!config)
     if(!config)
@@ -281,6 +613,12 @@ UA_ServerConfig_delete(UA_ServerConfig *config) {
     config->endpoints = NULL;
     config->endpoints = NULL;
     config->endpointsSize = 0;
     config->endpointsSize = 0;
 
 
+    /* Certificate Validation */
+    config->certificateVerification.deleteMembers(&config->certificateVerification);
+
+    /* Access Control */
+    config->accessControl.deleteMembers(&config->accessControl);
+
     UA_free(config);
     UA_free(config);
 }
 }
 
 
@@ -292,31 +630,31 @@ const UA_ClientConfig UA_ClientConfig_default = {
     5000, /* .timeout, 5 seconds */
     5000, /* .timeout, 5 seconds */
     10 * 60 * 1000, /* .secureChannelLifeTime, 10 minutes */
     10 * 60 * 1000, /* .secureChannelLifeTime, 10 minutes */
     UA_Log_Stdout, /* .logger */
     UA_Log_Stdout, /* .logger */
-    /* .localConnectionConfig */
-    {0, /* .protocolVersion */
+    { /* .localConnectionConfig */
+        0, /* .protocolVersion */
         65535, /* .sendBufferSize, 64k per chunk */
         65535, /* .sendBufferSize, 64k per chunk */
         65535, /* .recvBufferSize, 64k per chunk */
         65535, /* .recvBufferSize, 64k per chunk */
         0, /* .maxMessageSize, 0 -> unlimited */
         0, /* .maxMessageSize, 0 -> unlimited */
-        0}, /* .maxChunkCount, 0 -> unlimited */
+        0 /* .maxChunkCount, 0 -> unlimited */
+    },
     UA_ClientConnectionTCP, /* .connectionFunc */
     UA_ClientConnectionTCP, /* .connectionFunc */
 
 
     0, /* .customDataTypesSize */
     0, /* .customDataTypesSize */
-    NULL /*.customDataTypes */
-};
-
-/****************************************/
-/* Default Client Subscription Settings */
-/****************************************/
+    NULL, /*.customDataTypes */
 
 
+    NULL, /*.stateCallback */
 #ifdef UA_ENABLE_SUBSCRIPTIONS
 #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
 #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.
 /* 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_
 #ifndef UA_CONFIG_DEFAULT_H_
 #define 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
  * @param certificate Optional certificate for the server endpoint. Can be
  *        ``NULL``. */
  *        ``NULL``. */
 UA_EXPORT UA_ServerConfig *
 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
 /* Creates a server config on the default port 4840 with no server
  * certificate. */
  * certificate. */
@@ -50,7 +85,7 @@ UA_ServerConfig_new_default(void) {
 UA_EXPORT void
 UA_EXPORT void
 UA_ServerConfig_set_customHostname(UA_ServerConfig *config,
 UA_ServerConfig_set_customHostname(UA_ServerConfig *config,
                                    const UA_String customHostname);
                                    const UA_String customHostname);
-  
+
 /* Frees allocated memory in the server config */
 /* Frees allocated memory in the server config */
 UA_EXPORT void
 UA_EXPORT void
 UA_ServerConfig_delete(UA_ServerConfig *config);
 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.
 /* 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"
 #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.
 /* 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_
 #ifndef UA_LOG_SOCKET_ERROR_H_
 #define 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.
 /* 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 <stdio.h>
-#include <stdarg.h>
 #include "ua_log_stdout.h"
 #include "ua_log_stdout.h"
 #include "ua_types_generated.h"
 #include "ua_types_generated.h"
 #include "ua_types_generated_handling.h"
 #include "ua_types_generated_handling.h"
@@ -47,16 +50,21 @@ __attribute__((__format__(__printf__, 3 , 0)))
 void
 void
 UA_Log_Stdout(UA_LogLevel level, UA_LogCategory category,
 UA_Log_Stdout(UA_LogLevel level, UA_LogCategory category,
               const char *msg, va_list args) {
               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
 #ifdef UA_ENABLE_MULTITHREADING
     pthread_mutex_lock(&printf_mutex);
     pthread_mutex_lock(&printf_mutex);
 #endif
 #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);
     vprintf(msg, args);
     printf("\n");
     printf("\n");
     fflush(stdout);
     fflush(stdout);
+
 #ifdef UA_ENABLE_MULTITHREADING
 #ifdef UA_ENABLE_MULTITHREADING
     pthread_mutex_unlock(&printf_mutex);
     pthread_mutex_unlock(&printf_mutex);
 #endif
 #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.
 /* 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_
 #ifndef UA_LOG_STDOUT_H_
 #define 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.
 /* 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 */
 /* Enable POSIX features */
 #if !defined(_XOPEN_SOURCE) && !defined(_WRS_KERNEL)
 #if !defined(_XOPEN_SOURCE) && !defined(_WRS_KERNEL)
@@ -31,11 +38,19 @@
 
 
 #include "ua_network_tcp.h"
 #include "ua_network_tcp.h"
 #include "ua_log_stdout.h"
 #include "ua_log_stdout.h"
-#include "queue.h"
+#include "../deps/queue.h"
 
 
 #include <stdio.h> // snprintf
 #include <stdio.h> // snprintf
 #include <string.h> // memset
 #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
 #ifdef _WIN32
 # include <winsock2.h>
 # include <winsock2.h>
@@ -46,45 +61,68 @@
 # define OPTVAL_TYPE char
 # define OPTVAL_TYPE char
 # define ERR_CONNECTION_PROGRESS WSAEWOULDBLOCK
 # define ERR_CONNECTION_PROGRESS WSAEWOULDBLOCK
 # define UA_sleep_ms(X) Sleep(X)
 # 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 SOCKET int
 # define WIN32_INT
 # define WIN32_INT
 # define OPTVAL_TYPE int
 # define OPTVAL_TYPE int
 # define ERR_CONNECTION_PROGRESS EINPROGRESS
 # 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 <fcntl.h>
 # include <unistd.h> // read, write, close
 # include <unistd.h> // read, write, close
-# include <netdb.h>
+
 # ifdef __QNX__
 # ifdef __QNX__
 #  include <sys/socket.h>
 #  include <sys/socket.h>
 # endif
 # 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
-#endif
-# ifndef __CYGWIN__
+# if !defined(__CYGWIN__) && !defined(UA_FREERTOS)
 #  include <netinet/tcp.h>
 #  include <netinet/tcp.h>
 # endif
 # endif
-#endif
+#endif /* _WIN32 */
 
 
 /* unsigned int for windows and workaround to a glibc bug */
 /* unsigned int for windows and workaround to a glibc bug */
 /* Additionally if GNU_LIBRARY is not defined, it may be using
 /* Additionally if GNU_LIBRARY is not defined, it may be using
@@ -144,6 +182,11 @@ connection_releaserecvbuffer(UA_Connection *connection,
 
 
 static UA_StatusCode
 static UA_StatusCode
 connection_write(UA_Connection *connection, UA_ByteString *buf) {
 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 */
     /* Prevent OS signals when sending to a closed socket */
     int flags = 0;
     int flags = 0;
 #ifdef MSG_NOSIGNAL
 #ifdef MSG_NOSIGNAL
@@ -176,6 +219,9 @@ connection_write(UA_Connection *connection, UA_ByteString *buf) {
 static UA_StatusCode
 static UA_StatusCode
 connection_recv(UA_Connection *connection, UA_ByteString *response,
 connection_recv(UA_Connection *connection, UA_ByteString *response,
                 UA_UInt32 timeout) {
                 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 */
     /* Listen on the socket for the given timeout until a message arrives */
     if(timeout > 0) {
     if(timeout > 0) {
         fd_set fdset;
         fd_set fdset;
@@ -190,6 +236,17 @@ connection_recv(UA_Connection *connection, UA_ByteString *response,
         /* No result */
         /* No result */
         if(resultsize == 0)
         if(resultsize == 0)
             return UA_STATUSCODE_GOODNONCRITICALTIMEOUT;
             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*)
     response->data = (UA_Byte*)
@@ -231,7 +288,7 @@ socket_set_nonblocking(SOCKET sockfd) {
     u_long iMode = 1;
     u_long iMode = 1;
     if(ioctlsocket(sockfd, FIONBIO, &iMode) != NO_ERROR)
     if(ioctlsocket(sockfd, FIONBIO, &iMode) != NO_ERROR)
         return UA_STATUSCODE_BADINTERNALERROR;
         return UA_STATUSCODE_BADINTERNALERROR;
-#elif defined(_WRS_KERNEL)
+#elif defined(_WRS_KERNEL) || defined(UA_FREERTOS)
     int on = TRUE;
     int on = TRUE;
     if(ioctl(sockfd, FIONBIO, &on) < 0)
     if(ioctl(sockfd, FIONBIO, &on) < 0)
       return UA_STATUSCODE_BADINTERNALERROR;
       return UA_STATUSCODE_BADINTERNALERROR;
@@ -249,7 +306,7 @@ socket_set_blocking(SOCKET sockfd) {
     u_long iMode = 0;
     u_long iMode = 0;
     if(ioctlsocket(sockfd, FIONBIO, &iMode) != NO_ERROR)
     if(ioctlsocket(sockfd, FIONBIO, &iMode) != NO_ERROR)
         return UA_STATUSCODE_BADINTERNALERROR;
         return UA_STATUSCODE_BADINTERNALERROR;
-#elif defined(_WRS_KERNEL)
+#elif defined(_WRS_KERNEL) || defined(UA_FREERTOS)
     int on = FALSE;
     int on = FALSE;
     if(ioctl(sockfd, FIONBIO, &on) < 0)
     if(ioctl(sockfd, FIONBIO, &on) < 0)
       return UA_STATUSCODE_BADINTERNALERROR;
       return UA_STATUSCODE_BADINTERNALERROR;
@@ -275,6 +332,7 @@ typedef struct ConnectionEntry {
 } ConnectionEntry;
 } ConnectionEntry;
 
 
 typedef struct {
 typedef struct {
+    UA_Logger logger;
     UA_ConnectionConfig conf;
     UA_ConnectionConfig conf;
     UA_UInt16 port;
     UA_UInt16 port;
     UA_Int32 serverSockets[FD_SETSIZE];
     UA_Int32 serverSockets[FD_SETSIZE];
@@ -292,6 +350,8 @@ ServerNetworkLayerTCP_freeConnection(UA_Connection *connection) {
  * socket is returned from select. */
  * socket is returned from select. */
 static void
 static void
 ServerNetworkLayerTCP_close(UA_Connection *connection) {
 ServerNetworkLayerTCP_close(UA_Connection *connection) {
+    if (connection->state == UA_CONNECTION_CLOSED)
+        return;
     shutdown((SOCKET)connection->sockfd, 2);
     shutdown((SOCKET)connection->sockfd, 2);
     connection->state = UA_CONNECTION_CLOSED;
     connection->state = UA_CONNECTION_CLOSED;
 }
 }
@@ -307,12 +367,13 @@ ServerNetworkLayerTCP_add(ServerNetworkLayerTCP *layer, UA_Int32 newsockfd,
     if(setsockopt(newsockfd, IPPROTO_TCP, TCP_NODELAY,
     if(setsockopt(newsockfd, IPPROTO_TCP, TCP_NODELAY,
                (const char *)&dummy, sizeof(dummy)) < 0) {
                (const char *)&dummy, sizeof(dummy)) < 0) {
         UA_LOG_SOCKET_ERRNO_WRAP(
         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",
                              "Cannot set socket option TCP_NODELAY. Error: %s",
                              errno_str));
                              errno_str));
         return UA_STATUSCODE_BADUNEXPECTEDERROR;
         return UA_STATUSCODE_BADUNEXPECTEDERROR;
     }
     }
 
 
+#if !defined(UA_FREERTOS)
     /* Get the peer name for logging */
     /* Get the peer name for logging */
     char remote_name[100];
     char remote_name[100];
     int res = getnameinfo((struct sockaddr*)remote,
     int res = getnameinfo((struct sockaddr*)remote,
@@ -320,16 +381,16 @@ ServerNetworkLayerTCP_add(ServerNetworkLayerTCP *layer, UA_Int32 newsockfd,
                           remote_name, sizeof(remote_name),
                           remote_name, sizeof(remote_name),
                           NULL, 0, NI_NUMERICHOST);
                           NULL, 0, NI_NUMERICHOST);
     if(res == 0) {
     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",
                     "Connection %i | New connection over TCP from %s",
                     (int)newsockfd, remote_name);
                     (int)newsockfd, remote_name);
     } else {
     } 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, "
                                                 "Connection %i | New connection over TCP, "
                                                         "getnameinfo failed with error: %s",
                                                         "getnameinfo failed with error: %s",
                                                 (int)newsockfd, errno_str));
                                                 (int)newsockfd, errno_str));
     }
     }
-
+#endif
     /* Allocate and initialize the connection */
     /* Allocate and initialize the connection */
     ConnectionEntry *e = (ConnectionEntry*)UA_malloc(sizeof(ConnectionEntry));
     ConnectionEntry *e = (ConnectionEntry*)UA_malloc(sizeof(ConnectionEntry));
     if(!e){
     if(!e){
@@ -367,7 +428,7 @@ addServerSocket(ServerNetworkLayerTCP *layer, struct addrinfo *ai) {
     if(newsock < 0)
     if(newsock < 0)
 #endif
 #endif
     {
     {
-        UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+        UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK,
                        "Error opening the server socket");
                        "Error opening the server socket");
         return;
         return;
     }
     }
@@ -375,26 +436,29 @@ addServerSocket(ServerNetworkLayerTCP *layer, struct addrinfo *ai) {
     /* Some Linux distributions have net.ipv6.bindv6only not activated. So
     /* Some Linux distributions have net.ipv6.bindv6only not activated. So
      * sockets can double-bind to IPv4 and IPv6. This leads to problems. Use
      * sockets can double-bind to IPv4 and IPv6. This leads to problems. Use
      * AF_INET6 sockets only for IPv6. */
      * AF_INET6 sockets only for IPv6. */
+
     int optval = 1;
     int optval = 1;
+#if !defined(UA_FREERTOS)
     if(ai->ai_family == AF_INET6 &&
     if(ai->ai_family == AF_INET6 &&
        setsockopt(newsock, IPPROTO_IPV6, IPV6_V6ONLY,
        setsockopt(newsock, IPPROTO_IPV6, IPV6_V6ONLY,
                   (const char*)&optval, sizeof(optval)) == -1) {
                   (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");
                        "Could not set an IPv6 socket to IPv6 only");
         CLOSESOCKET(newsock);
         CLOSESOCKET(newsock);
         return;
         return;
     }
     }
-
+#endif
     if(setsockopt(newsock, SOL_SOCKET, SO_REUSEADDR,
     if(setsockopt(newsock, SOL_SOCKET, SO_REUSEADDR,
                   (const char *)&optval, sizeof(optval)) == -1) {
                   (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");
                        "Could not make the socket reusable");
         CLOSESOCKET(newsock);
         CLOSESOCKET(newsock);
         return;
         return;
     }
     }
 
 
+
     if(socket_set_nonblocking(newsock) != UA_STATUSCODE_GOOD) {
     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");
                        "Could not set the server socket to nonblocking");
         CLOSESOCKET(newsock);
         CLOSESOCKET(newsock);
         return;
         return;
@@ -403,7 +467,7 @@ addServerSocket(ServerNetworkLayerTCP *layer, struct addrinfo *ai) {
     /* Bind socket to address */
     /* Bind socket to address */
     if(bind(newsock, ai->ai_addr, WIN32_INT ai->ai_addrlen) < 0) {
     if(bind(newsock, ai->ai_addr, WIN32_INT ai->ai_addrlen) < 0) {
         UA_LOG_SOCKET_ERRNO_WRAP(
         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));
                            "Error binding a server socket: %s", errno_str));
         CLOSESOCKET(newsock);
         CLOSESOCKET(newsock);
         return;
         return;
@@ -412,7 +476,7 @@ addServerSocket(ServerNetworkLayerTCP *layer, struct addrinfo *ai) {
     /* Start listening */
     /* Start listening */
     if(listen(newsock, MAXBACKLOG) < 0) {
     if(listen(newsock, MAXBACKLOG) < 0) {
         UA_LOG_SOCKET_ERRNO_WRAP(
         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));
                        "Error listening on server socket: %s", errno_str));
         CLOSESOCKET(newsock);
         CLOSESOCKET(newsock);
         return;
         return;
@@ -449,7 +513,7 @@ ServerNetworkLayerTCP_start(UA_ServerNetworkLayer *nl, const UA_String *customHo
                                         layer->port);
                                         layer->port);
 #endif
 #endif
         du.data = (UA_Byte*)discoveryUrl;
         du.data = (UA_Byte*)discoveryUrl;
-    }else{    
+    }else{
         char hostname[256];
         char hostname[256];
         if(gethostname(hostname, 255) == 0) {
         if(gethostname(hostname, 255) == 0) {
             char discoveryUrl[256];
             char discoveryUrl[256];
@@ -478,7 +542,13 @@ ServerNetworkLayerTCP_start(UA_ServerNetworkLayer *nl, const UA_String *customHo
     hints.ai_family = AF_UNSPEC;
     hints.ai_family = AF_UNSPEC;
     hints.ai_socktype = SOCK_STREAM;
     hints.ai_socktype = SOCK_STREAM;
     hints.ai_flags = AI_PASSIVE;
     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)
     if(getaddrinfo(NULL, portno, &hints, &res) != 0)
+#endif
         return UA_STATUSCODE_BADINTERNALERROR;
         return UA_STATUSCODE_BADINTERNALERROR;
 
 
     /* There might be serveral addrinfos (for different network cards,
     /* 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);
         addServerSocket(layer, ai);
     freeaddrinfo(res);
     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",
                 "TCP network layer listening on %.*s",
                 (int)nl->discoveryUrl.length, nl->discoveryUrl.data);
                 (int)nl->discoveryUrl.length, nl->discoveryUrl.data);
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
@@ -533,7 +603,7 @@ ServerNetworkLayerTCP_listen(UA_ServerNetworkLayer *nl, UA_Server *server,
     struct timeval tmptv = {0, timeout * 1000};
     struct timeval tmptv = {0, timeout * 1000};
     if (select(highestfd+1, &fdset, NULL, &errset, &tmptv) < 0) {
     if (select(highestfd+1, &fdset, NULL, &errset, &tmptv) < 0) {
         UA_LOG_SOCKET_ERRNO_WRAP(
         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));
                                   "Socket select failed with %s", errno_str));
         // we will retry, so do not return bad
         // we will retry, so do not return bad
         return UA_STATUSCODE_GOOD;
         return UA_STATUSCODE_GOOD;
@@ -555,7 +625,7 @@ ServerNetworkLayerTCP_listen(UA_ServerNetworkLayer *nl, UA_Server *server,
 #endif
 #endif
             continue;
             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",
                     "Connection %i | New TCP connection on server socket %i",
                     (int)newsockfd, layer->serverSockets[i]);
                     (int)newsockfd, layer->serverSockets[i]);
 
 
@@ -567,8 +637,8 @@ ServerNetworkLayerTCP_listen(UA_ServerNetworkLayer *nl, UA_Server *server,
     UA_DateTime now = UA_DateTime_nowMonotonic();
     UA_DateTime now = UA_DateTime_nowMonotonic();
     LIST_FOREACH_SAFE(e, &layer->connections, pointers, e_tmp) {
     LIST_FOREACH_SAFE(e, &layer->connections, pointers, e_tmp) {
         if ((e->connection.state == UA_CONNECTION_OPENING) &&
         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)",
                         "Connection %i | Closed by the server (no Hello Message)",
                          e->connection.sockfd);
                          e->connection.sockfd);
             LIST_REMOVE(e, pointers);
             LIST_REMOVE(e, pointers);
@@ -581,7 +651,7 @@ ServerNetworkLayerTCP_listen(UA_ServerNetworkLayer *nl, UA_Server *server,
            !UA_fd_isset(e->connection.sockfd, &fdset))
            !UA_fd_isset(e->connection.sockfd, &fdset))
           continue;
           continue;
 
 
-        UA_LOG_TRACE(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+        UA_LOG_TRACE(layer->logger, UA_LOGCATEGORY_NETWORK,
                     "Connection %i | Activity on the socket",
                     "Connection %i | Activity on the socket",
                     e->connection.sockfd);
                     e->connection.sockfd);
 
 
@@ -594,15 +664,9 @@ ServerNetworkLayerTCP_listen(UA_ServerNetworkLayer *nl, UA_Server *server,
             connection_releaserecvbuffer(&e->connection, &buf);
             connection_releaserecvbuffer(&e->connection, &buf);
         } else if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED) {
         } else if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED) {
             /* The socket is shutdown but not closed */
             /* 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);
             LIST_REMOVE(e, pointers);
             CLOSESOCKET(e->connection.sockfd);
             CLOSESOCKET(e->connection.sockfd);
             UA_Server_removeConnection(server, &e->connection);
             UA_Server_removeConnection(server, &e->connection);
@@ -614,7 +678,7 @@ ServerNetworkLayerTCP_listen(UA_ServerNetworkLayer *nl, UA_Server *server,
 static void
 static void
 ServerNetworkLayerTCP_stop(UA_ServerNetworkLayer *nl, UA_Server *server) {
 ServerNetworkLayerTCP_stop(UA_ServerNetworkLayer *nl, UA_Server *server) {
     ServerNetworkLayerTCP *layer = (ServerNetworkLayerTCP *)nl->handle;
     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");
                 "Shutting down the TCP network layer");
 
 
     /* Close the server sockets */
     /* Close the server sockets */
@@ -649,7 +713,6 @@ ServerNetworkLayerTCP_deleteMembers(UA_ServerNetworkLayer *nl) {
     ConnectionEntry *e, *e_tmp;
     ConnectionEntry *e, *e_tmp;
     LIST_FOREACH_SAFE(e, &layer->connections, pointers, e_tmp) {
     LIST_FOREACH_SAFE(e, &layer->connections, pointers, e_tmp) {
         LIST_REMOVE(e, pointers);
         LIST_REMOVE(e, pointers);
-        ServerNetworkLayerTCP_close(&e->connection);
         CLOSESOCKET(e->connection.sockfd);
         CLOSESOCKET(e->connection.sockfd);
         UA_free(e);
         UA_free(e);
     }
     }
@@ -659,7 +722,7 @@ ServerNetworkLayerTCP_deleteMembers(UA_ServerNetworkLayer *nl) {
 }
 }
 
 
 UA_ServerNetworkLayer
 UA_ServerNetworkLayer
-UA_ServerNetworkLayerTCP(UA_ConnectionConfig conf, UA_UInt16 port) {
+UA_ServerNetworkLayerTCP(UA_ConnectionConfig conf, UA_UInt16 port, UA_Logger logger) {
     UA_ServerNetworkLayer nl;
     UA_ServerNetworkLayer nl;
     memset(&nl, 0, sizeof(UA_ServerNetworkLayer));
     memset(&nl, 0, sizeof(UA_ServerNetworkLayer));
     ServerNetworkLayerTCP *layer = (ServerNetworkLayerTCP*)
     ServerNetworkLayerTCP *layer = (ServerNetworkLayerTCP*)
@@ -667,6 +730,7 @@ UA_ServerNetworkLayerTCP(UA_ConnectionConfig conf, UA_UInt16 port) {
     if(!layer)
     if(!layer)
         return nl;
         return nl;
 
 
+    layer->logger = (logger != NULL ? logger : UA_Log_Stdout);
     layer->conf = conf;
     layer->conf = conf;
     layer->port = port;
     layer->port = port;
 
 
@@ -684,6 +748,8 @@ UA_ServerNetworkLayerTCP(UA_ConnectionConfig conf, UA_UInt16 port) {
 
 
 static void
 static void
 ClientNetworkLayerTCP_close(UA_Connection *connection) {
 ClientNetworkLayerTCP_close(UA_Connection *connection) {
+    if (connection->state == UA_CONNECTION_CLOSED)
+        return;
     shutdown((SOCKET)connection->sockfd, 2);
     shutdown((SOCKET)connection->sockfd, 2);
     CLOSESOCKET(connection->sockfd);
     CLOSESOCKET(connection->sockfd);
     connection->state = UA_CONNECTION_CLOSED;
     connection->state = UA_CONNECTION_CLOSED;
@@ -691,7 +757,8 @@ ClientNetworkLayerTCP_close(UA_Connection *connection) {
 
 
 UA_Connection
 UA_Connection
 UA_ClientConnectionTCP(UA_ConnectionConfig conf,
 UA_ClientConnectionTCP(UA_ConnectionConfig conf,
-                       const char *endpointUrl, const UA_UInt32 timeout) {
+                       const char *endpointUrl, const UA_UInt32 timeout,
+                       UA_Logger logger) {
 #ifdef _WIN32
 #ifdef _WIN32
     WORD wVersionRequested;
     WORD wVersionRequested;
     WSADATA wsaData;
     WSADATA wsaData;
@@ -699,9 +766,13 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
     WSAStartup(wVersionRequested, &wsaData);
     WSAStartup(wVersionRequested, &wsaData);
 #endif
 #endif
 
 
+    if(logger == NULL) {
+        logger = UA_Log_Stdout;
+    }
+
     UA_Connection connection;
     UA_Connection connection;
     memset(&connection, 0, sizeof(UA_Connection));
     memset(&connection, 0, sizeof(UA_Connection));
-    connection.state = UA_CONNECTION_OPENING;
+    connection.state = UA_CONNECTION_CLOSED;
     connection.localConf = conf;
     connection.localConf = conf;
     connection.remoteConf = conf;
     connection.remoteConf = conf;
     connection.send = connection_write;
     connection.send = connection_write;
@@ -722,7 +793,7 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
         UA_parseEndpointUrl(&endpointUrlString, &hostnameString,
         UA_parseEndpointUrl(&endpointUrlString, &hostnameString,
                             &port, &pathString);
                             &port, &pathString);
     if(parse_retval != UA_STATUSCODE_GOOD || hostnameString.length > 511) {
     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);
                        "Server url is invalid: %s", endpointUrl);
         return connection;
         return connection;
     }
     }
@@ -731,7 +802,7 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
 
 
     if(port == 0) {
     if(port == 0) {
         port = 4840;
         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);
                     "No port defined, using default port %d", port);
     }
     }
 
 
@@ -739,6 +810,9 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
     memset(&hints, 0, sizeof(hints));
     memset(&hints, 0, sizeof(hints));
     hints.ai_family = AF_UNSPEC;
     hints.ai_family = AF_UNSPEC;
     hints.ai_socktype = SOCK_STREAM;
     hints.ai_socktype = SOCK_STREAM;
+#if defined(UA_FREERTOS)
+    hints.ai_protocol = IPPROTO_TCP;
+#endif
     char portStr[6];
     char portStr[6];
 #ifndef _MSC_VER
 #ifndef _MSC_VER
     snprintf(portStr, 6, "%d", port);
     snprintf(portStr, 6, "%d", port);
@@ -747,15 +821,21 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
 #endif
 #endif
     int error = getaddrinfo(hostname, portStr, &hints, &server);
     int error = getaddrinfo(hostname, portStr, &hints, &server);
     if(error != 0 || !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",
                        "DNS lookup of %s failed with error %s",
                        hostname, gai_strerror(error));
                        hostname, gai_strerror(error));
+#else
+        UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK,
+                        "DNS lookup of %s failed with error",
+                        hostname);
+#endif
         return connection;
         return connection;
     }
     }
 
 
-
     UA_Boolean connected = UA_FALSE;
     UA_Boolean connected = UA_FALSE;
-
+    UA_DateTime dtTimeout = timeout * UA_DATETIME_MSEC;
     UA_DateTime connStart = UA_DateTime_nowMonotonic();
     UA_DateTime connStart = UA_DateTime_nowMonotonic();
     SOCKET clientsockfd;
     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
      * want to try to connect. So use a loop and retry until timeout is
      * reached. */
      * reached. */
     do {
     do {
-
-        connection.state = UA_CONNECTION_OPENING;
         /* Get a socket */
         /* Get a socket */
         clientsockfd = socket(server->ai_family,
         clientsockfd = socket(server->ai_family,
                               server->ai_socktype,
                               server->ai_socktype,
@@ -774,19 +852,20 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
     #else
     #else
         if(clientsockfd < 0) {
         if(clientsockfd < 0) {
     #endif
     #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);
             freeaddrinfo(server);
             return connection;
             return connection;
         }
         }
 
 
+        connection.state = UA_CONNECTION_OPENING;
+
         /* Connect to the server */
         /* Connect to the server */
         connection.sockfd = (UA_Int32) clientsockfd; /* cast for win32 */
         connection.sockfd = (UA_Int32) clientsockfd; /* cast for win32 */
 
 
         /* Non blocking connect to be able to timeout */
         /* Non blocking connect to be able to timeout */
         if (socket_set_nonblocking(clientsockfd) != UA_STATUSCODE_GOOD) {
         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");
                            "Could not set the client socket to nonblocking");
             ClientNetworkLayerTCP_close(&connection);
             ClientNetworkLayerTCP_close(&connection);
             freeaddrinfo(server);
             freeaddrinfo(server);
@@ -794,15 +873,14 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
         }
         }
 
 
         /* Non blocking connect */
         /* 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)) {
         if ((error == -1) && (errno__ != ERR_CONNECTION_PROGRESS)) {
             ClientNetworkLayerTCP_close(&connection);
             ClientNetworkLayerTCP_close(&connection);
             UA_LOG_SOCKET_ERRNO_WRAP(
             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);
             freeaddrinfo(server);
             return connection;
             return connection;
         }
         }
@@ -810,25 +888,23 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
         /* Use select to wait and check if connected */
         /* Use select to wait and check if connected */
         if (error == -1 && (errno__ == ERR_CONNECTION_PROGRESS)) {
         if (error == -1 && (errno__ == ERR_CONNECTION_PROGRESS)) {
             /* connection in progress. Wait until connected using select */
             /* 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;
                 break;
 
 
             fd_set fdset;
             fd_set fdset;
             FD_ZERO(&fdset);
             FD_ZERO(&fdset);
             UA_fd_set(clientsockfd, &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),
             struct timeval tmptv = {(long int) (timeout_usec / 1000000),
                                     (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
                 /* Windows does not have any getsockopt equivalent and it is not
                  * needed there */
                  * needed there */
-#ifdef _WIN32
                 connected = true;
                 connected = true;
                 break;
                 break;
 #else
 #else
@@ -842,7 +918,7 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
                     /* connection refused happens on localhost or local ip without timeout */
                     /* connection refused happens on localhost or local ip without timeout */
                     if (so_error != ECONNREFUSED) {
                     if (so_error != ECONNREFUSED) {
                         ClientNetworkLayerTCP_close(&connection);
                         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",
                                        "Connection to %s failed with error: %s",
                                        endpointUrl, strerror(ret == 0 ? so_error : errno__));
                                        endpointUrl, strerror(ret == 0 ? so_error : errno__));
                         freeaddrinfo(server);
                         freeaddrinfo(server);
@@ -863,13 +939,15 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
         }
         }
         ClientNetworkLayerTCP_close(&connection);
         ClientNetworkLayerTCP_close(&connection);
 
 
-    } while ((UA_Double)(UA_DateTime_nowMonotonic() - connStart)*UA_DATETIME_TO_MSEC < timeout);
+    } while ((UA_DateTime_nowMonotonic() - connStart) < dtTimeout);
 
 
     freeaddrinfo(server);
     freeaddrinfo(server);
-    if (!connected) {
+
+    if(!connected) {
         /* connection timeout */
         /* 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",
                        "Trying to connect to %s timed out",
                        endpointUrl);
                        endpointUrl);
         return connection;
         return connection;
@@ -878,7 +956,7 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
 
 
     /* We are connected. Reset socket to blocking */
     /* We are connected. Reset socket to blocking */
     if(socket_set_blocking(clientsockfd) != UA_STATUSCODE_GOOD) {
     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");
                        "Could not set the client socket to blocking");
         ClientNetworkLayerTCP_close(&connection);
         ClientNetworkLayerTCP_close(&connection);
         return connection;
         return connection;
@@ -889,7 +967,7 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
     int sso_result = setsockopt(connection.sockfd, SOL_SOCKET,
     int sso_result = setsockopt(connection.sockfd, SOL_SOCKET,
                                 SO_NOSIGPIPE, (void*)&val, sizeof(val));
                                 SO_NOSIGPIPE, (void*)&val, sizeof(val));
     if(sso_result < 0)
     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");
                        "Couldn't set SO_NOSIGPIPE");
 #endif
 #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.
 /* 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_
 #ifndef UA_NETWORK_TCP_H_
 #define UA_NETWORK_TCP_H_
 #define UA_NETWORK_TCP_H_
@@ -10,12 +13,13 @@ extern "C" {
 
 
 #include "ua_server.h"
 #include "ua_server.h"
 #include "ua_client.h"
 #include "ua_client.h"
+#include "ua_plugin_log.h"
 
 
 UA_ServerNetworkLayer UA_EXPORT
 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_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
 #ifdef __cplusplus
 } // extern "C"
 } // 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.
 /* 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 "ua_network_udp.h"
 #include <stdio.h>
 #include <stdio.h>
 #include <string.h> // memset
 #include <string.h> // memset
 
 
-#ifdef UA_ENABLE_MULTITHREADING
-# include <urcu/uatomic.h>
-#endif
-
 /* with a space so amalgamation does not remove the includes */
 /* with a space so amalgamation does not remove the includes */
 # include <errno.h> // errno, EINTR
 # include <errno.h> // errno, EINTR
 # include <fcntl.h> // fcntl
 # include <fcntl.h> // fcntl
 # include <strings.h> //bzero
 # include <strings.h> //bzero
+
+#if defined(UA_FREERTOS)
+ # include <lwip/udp.h>
+ # include <lwip/tcpip.h>
+#else
 # ifndef _WRS_KERNEL
 # ifndef _WRS_KERNEL
 #  include <sys/select.h>
 #  include <sys/select.h>
 # else
 # else
-# include <selectLib.h>
+#  include <selectLib.h>
 # endif
 # 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__
 #ifdef __QNX__
 #include <sys/socket.h>
 #include <sys/socket.h>
 #endif
 #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.
 /* 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_
 #ifndef UA_NETWORK_UDP_H_
 #define 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.
 /* 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"
 #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;
     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 */
         /* create a random nodeid */
         /* start at least with 50,000 to make sure we don not conflict with nodes from the spec */
         /* 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 */
         /* 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.
 /* 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_
 #ifndef UA_NODESTORE_DEFAULT_H_
 #define 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.
 /* 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_types.h"
 #include "ua_securitypolicy_none.h"
 #include "ua_securitypolicy_none.h"
@@ -7,7 +11,7 @@
 
 
 static UA_StatusCode
 static UA_StatusCode
 verify_none(const UA_SecurityPolicy *securityPolicy,
 verify_none(const UA_SecurityPolicy *securityPolicy,
-            const void *channelContext,
+            void *channelContext,
             const UA_ByteString *message,
             const UA_ByteString *message,
             const UA_ByteString *signature) {
             const UA_ByteString *signature) {
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
@@ -15,7 +19,7 @@ verify_none(const UA_SecurityPolicy *securityPolicy,
 
 
 static UA_StatusCode
 static UA_StatusCode
 sign_none(const UA_SecurityPolicy *securityPolicy,
 sign_none(const UA_SecurityPolicy *securityPolicy,
-          const void *channelContext,
+          void *channelContext,
           const UA_ByteString *message,
           const UA_ByteString *message,
           UA_ByteString *signature) {
           UA_ByteString *signature) {
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
@@ -29,14 +33,14 @@ length_none(const UA_SecurityPolicy *securityPolicy,
 
 
 static UA_StatusCode
 static UA_StatusCode
 encrypt_none(const UA_SecurityPolicy *securityPolicy,
 encrypt_none(const UA_SecurityPolicy *securityPolicy,
-             const void *channelContext,
+             void *channelContext,
              UA_ByteString *data) {
              UA_ByteString *data) {
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
 
 
 static UA_StatusCode
 static UA_StatusCode
 decrypt_none(const UA_SecurityPolicy *securityPolicy,
 decrypt_none(const UA_SecurityPolicy *securityPolicy,
-             const void *channelContext,
+             void *channelContext,
              UA_ByteString *data) {
              UA_ByteString *data) {
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
@@ -62,18 +66,24 @@ generateKey_none(const UA_SecurityPolicy *securityPolicy,
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
 
 
+/* Use the non-cryptographic RNG to set the nonce */
 static UA_StatusCode
 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)
     if(securityPolicy == NULL || out == NULL)
         return UA_STATUSCODE_BADINTERNALERROR;
         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;
     return UA_STATUSCODE_GOOD;
 }
 }
 
 
@@ -94,17 +104,6 @@ setContextValue_none(void *channelContext,
     return UA_STATUSCODE_GOOD;
     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
 static UA_StatusCode
 compareCertificate_none(const void *channelContext,
 compareCertificate_none(const void *channelContext,
                         const UA_ByteString *certificate) {
                         const UA_ByteString *certificate) {
@@ -117,38 +116,48 @@ policy_deletemembers_none(UA_SecurityPolicy *policy) {
 }
 }
 
 
 UA_StatusCode
 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->policyUri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None");
     policy->logger = logger;
     policy->logger = logger;
     UA_ByteString_copy(&localCertificate, &policy->localCertificate);
     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.generateKey = generateKey_none;
     policy->symmetricModule.generateNonce = generateNonce_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.newContext = newContext_none;
     policy->channelModule.deleteContext = deleteContext_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.setRemoteSymSigningKey = setContextValue_none;
     policy->channelModule.setRemoteSymIv = setContextValue_none;
     policy->channelModule.setRemoteSymIv = setContextValue_none;
     policy->channelModule.compareCertificate = compareCertificate_none;
     policy->channelModule.compareCertificate = compareCertificate_none;
-    policy->channelModule.getRemoteAsymPlainTextBlockSize = getRemoteAsymPlainTextBlockSize_none;
-    policy->channelModule.getRemoteAsymEncryptionBufferLengthOverhead =
-        getRemoteAsymEncryptionBufferLengthOverhead_none;
     policy->deleteMembers = policy_deletemembers_none;
     policy->deleteMembers = policy_deletemembers_none;
 
 
     return UA_STATUSCODE_GOOD;
     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.
 /* 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_
 #ifndef UA_SECURITYPOLICY_NONE_H_
 #define UA_SECURITYPOLICY_NONE_H_
 #define UA_SECURITYPOLICY_NONE_H_
@@ -12,8 +16,8 @@ extern "C" {
 #include "ua_plugin_log.h"
 #include "ua_plugin_log.h"
 
 
 UA_StatusCode UA_EXPORT
 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
 #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
 /* 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
  * 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.h"
 #include "ua_client_internal.h"
 #include "ua_client_internal.h"
@@ -10,18 +24,20 @@
 #include "ua_util.h"
 #include "ua_util.h"
 #include "ua_securitypolicy_none.h"
 #include "ua_securitypolicy_none.h"
 
 
- /********************/
- /* Client Lifecycle */
- /********************/
+/********************/
+/* Client Lifecycle */
+/********************/
 
 
 static void
 static void
 UA_Client_init(UA_Client* client, UA_ClientConfig config) {
 UA_Client_init(UA_Client* client, UA_ClientConfig config) {
     memset(client, 0, sizeof(UA_Client));
     memset(client, 0, sizeof(UA_Client));
     /* TODO: Select policy according to the endpoint */
     /* 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.securityPolicy = &client->securityPolicy;
     client->channel.securityMode = UA_MESSAGESECURITYMODE_NONE;
     client->channel.securityMode = UA_MESSAGESECURITYMODE_NONE;
     client->config = config;
     client->config = config;
+    if(client->config.stateCallback)
+        client->config.stateCallback(client, client->state);
 }
 }
 
 
 UA_Client *
 UA_Client *
@@ -49,22 +65,11 @@ UA_Client_deleteMembers(UA_Client* client) {
         UA_String_deleteMembers(&client->password);
         UA_String_deleteMembers(&client->password);
 
 
     /* Delete the async service calls */
     /* 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 */
     /* Delete the subscriptions */
 #ifdef UA_ENABLE_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
 #endif
 }
 }
 
 
@@ -85,6 +90,13 @@ UA_Client_getState(UA_Client *client) {
     return client->state;
     return client->state;
 }
 }
 
 
+void *
+UA_Client_getContext(UA_Client *client) {
+    if(!client)
+        return NULL;
+    return client->config.clientContext;
+}
+
 /****************/
 /****************/
 /* Raw Services */
 /* Raw Services */
 /****************/
 /****************/
@@ -130,9 +142,12 @@ sendSymmetricServiceRequest(UA_Client *client, const void *request,
     return UA_STATUSCODE_GOOD;
     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 */
 /* Look for the async callback in the linked list, execute and delete it */
 static UA_StatusCode
 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) {
                      const UA_ByteString *responseMessage, size_t *offset) {
     /* Find the callback */
     /* Find the callback */
     AsyncServiceCall *ac;
     AsyncServiceCall *ac;
@@ -143,20 +158,46 @@ processAsyncResponse(UA_Client *client, UA_UInt32 requestId, UA_NodeId *response
     if(!ac)
     if(!ac)
         return UA_STATUSCODE_BADREQUESTHEADERINVALID;
         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 */
     /* 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,
         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 */
     /* Remove the callback */
     LIST_REMOVE(ac, pointers);
     LIST_REMOVE(ac, pointers);
     UA_free(ac);
     UA_free(ac);
@@ -184,15 +225,10 @@ processServiceResponse(void *application, UA_SecureChannel *channel,
      * TODO: Solve this for client and server together */
      * TODO: Solve this for client and server together */
     if(rd->client->state >= UA_CLIENTSTATE_SECURECHANNEL &&
     if(rd->client->state >= UA_CLIENTSTATE_SECURECHANNEL &&
        (channel->securityToken.createdAt +
        (channel->securityToken.createdAt +
-        (channel->securityToken.revisedLifetime * UA_MSEC_TO_DATETIME))
+        (channel->securityToken.revisedLifetime * UA_DATETIME_MSEC))
        < UA_DateTime_nowMonotonic())
        < UA_DateTime_nowMonotonic())
         return UA_STATUSCODE_BADSECURECHANNELCLOSED;
         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 */
     /* Decode the data type identifier of the response */
     size_t offset = 0;
     size_t offset = 0;
     UA_NodeId responseId;
     UA_NodeId responseId;
@@ -210,32 +246,37 @@ processServiceResponse(void *application, UA_SecureChannel *channel,
     /* Got the synchronous response */
     /* Got the synchronous response */
     rd->received = true;
     rd->received = true;
 
 
+    /* Forward declaration for the goto */
+    UA_NodeId expectedNodeId = UA_NODEID_NUMERIC(0, rd->responseType->binaryEncodingId);
+
     /* Check that the response type matches */
     /* 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,
             retval = UA_decodeBinary(message, &offset, rd->response,
                                      &UA_TYPES[UA_TYPES_SERVICEFAULT], 0, NULL);
                                      &UA_TYPES[UA_TYPES_SERVICEFAULT], 0, NULL);
         } else {
         } else {
             /* Close the connection */
             /* Close the connection */
+            UA_LOG_ERROR(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
+                         "Reply contains the wrong service response");
             retval = UA_STATUSCODE_BADCOMMUNICATIONERROR;
             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:
 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)
         if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED)
             retval = UA_STATUSCODE_BADRESPONSETOOLARGE;
             retval = UA_STATUSCODE_BADRESPONSETOOLARGE;
         UA_LOG_INFO(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
         UA_LOG_INFO(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
@@ -247,8 +288,6 @@ finish:
             respHeader->serviceResult = retval;
             respHeader->serviceResult = retval;
         }
         }
     }
     }
-    UA_NodeId_deleteMembers(&responseId);
-
     return retval;
     return retval;
 }
 }
 
 
@@ -283,17 +322,15 @@ receiveServiceResponse(UA_Client *client, void *response, const UA_DataType *res
             return UA_STATUSCODE_GOODNONCRITICALTIMEOUT;
             return UA_STATUSCODE_GOODNONCRITICALTIMEOUT;
 
 
         /* round always to upper value to avoid timeout to be set to 0
         /* 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_GOOD && retval != UA_STATUSCODE_GOODNONCRITICALTIMEOUT) {
             if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED)
             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;
             break;
         }
         }
     } while(!rd.received);
     } while(!rd.received);
@@ -315,23 +352,47 @@ __UA_Client_Service(UA_Client *client, const void *request,
             respHeader->serviceResult = UA_STATUSCODE_BADREQUESTTOOLARGE;
             respHeader->serviceResult = UA_STATUSCODE_BADREQUESTTOOLARGE;
         else
         else
             respHeader->serviceResult = retval;
             respHeader->serviceResult = retval;
-        UA_Client_disconnect(client);
+        UA_Client_close(client);
         return;
         return;
     }
     }
 
 
     /* Retrieve the response */
     /* Retrieve the response */
     UA_DateTime maxDate = UA_DateTime_nowMonotonic() +
     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);
     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 */
         /* 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;
         retval = UA_STATUSCODE_BADCONNECTIONCLOSED;
     }
     }
     if(retval != UA_STATUSCODE_GOOD)
     if(retval != UA_STATUSCODE_GOOD)
         respHeader->serviceResult = retval;
         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_StatusCode
 __UA_Client_AsyncService(UA_Client *client, const void *request,
 __UA_Client_AsyncService(UA_Client *client, const void *request,
                          const UA_DataType *requestType,
                          const UA_DataType *requestType,
@@ -363,10 +424,22 @@ __UA_Client_AsyncService(UA_Client *client, const void *request,
 UA_StatusCode
 UA_StatusCode
 UA_Client_runAsync(UA_Client *client, UA_UInt16 timeout) {
 UA_Client_runAsync(UA_Client *client, UA_UInt16 timeout) {
     /* TODO: Call repeated jobs that are scheduled */
     /* 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)
     if(retval == UA_STATUSCODE_GOODNONCRITICALTIMEOUT)
         retval = UA_STATUSCODE_GOOD;
         retval = UA_STATUSCODE_GOOD;
+#ifdef UA_ENABLE_SUBSCRIPTIONS
+    /* The inactivity check must be done after receiveServiceResponse */
+    UA_Client_Subscriptions_backgroundPublishInactivityCheck(client);
+#endif
     return retval;
     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
 /* 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
  * 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.h"
 #include "ua_client_internal.h"
 #include "ua_client_internal.h"
@@ -12,9 +18,22 @@
 
 
 #define UA_MINMESSAGESIZE 8192
 #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
 static UA_StatusCode
 processACKResponse(void *application, UA_Connection *connection, UA_ByteString *chunk) {
 processACKResponse(void *application, UA_Connection *connection, UA_ByteString *chunk) {
@@ -97,9 +116,13 @@ HelAckHandshake(UA_Client *client) {
     /* Loop until we have a complete chunk */
     /* Loop until we have a complete chunk */
     retval = UA_Connection_receiveChunksBlocking(conn, client, processACKResponse,
     retval = UA_Connection_receiveChunksBlocking(conn, client, processACKResponse,
                                                  client->config.timeout);
                                                  client->config.timeout);
-    if(retval != UA_STATUSCODE_GOOD)
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_NETWORK,
         UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_NETWORK,
                     "Receiving ACK message failed");
                     "Receiving ACK message failed");
+        if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED)
+            client->state = UA_CLIENTSTATE_DISCONNECTED;
+        UA_Client_close(client);
+    }
     return retval;
     return retval;
 }
 }
 
 
@@ -108,12 +131,12 @@ processDecodedOPNResponse(UA_Client *client, UA_OpenSecureChannelResponse *respo
     /* Replace the token */
     /* Replace the token */
     UA_ChannelSecurityToken_deleteMembers(&client->channel.securityToken);
     UA_ChannelSecurityToken_deleteMembers(&client->channel.securityToken);
     client->channel.securityToken = response->securityToken;
     client->channel.securityToken = response->securityToken;
-    UA_ChannelSecurityToken_deleteMembers(&response->securityToken);
+    UA_ChannelSecurityToken_init(&response->securityToken);
 
 
     /* Replace the nonce */
     /* Replace the nonce */
     UA_ByteString_deleteMembers(&client->channel.remoteNonce);
     UA_ByteString_deleteMembers(&client->channel.remoteNonce);
     client->channel.remoteNonce = response->serverNonce;
     client->channel.remoteNonce = response->serverNonce;
-    UA_ByteString_deleteMembers(&response->serverNonce);
+    UA_ByteString_init(&response->serverNonce);
 
 
     if(client->channel.state == UA_SECURECHANNELSTATE_OPEN)
     if(client->channel.state == UA_SECURECHANNELSTATE_OPEN)
         UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
         UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
@@ -127,7 +150,7 @@ processDecodedOPNResponse(UA_Client *client, UA_OpenSecureChannelResponse *respo
      * standard */
      * standard */
     client->channel.state = UA_SECURECHANNELSTATE_OPEN;
     client->channel.state = UA_SECURECHANNELSTATE_OPEN;
     client->nextChannelRenewal = UA_DateTime_nowMonotonic() + (UA_DateTime)
     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
 static UA_StatusCode
@@ -166,7 +189,7 @@ openSecureChannel(UA_Client *client, UA_Boolean renew) {
     if(retval != UA_STATUSCODE_GOOD) {
     if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
         UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
                      "Sending OPN message failed with error %s", UA_StatusCode_name(retval));
                      "Sending OPN message failed with error %s", UA_StatusCode_name(retval));
-        UA_Client_disconnect(client);
+        UA_Client_close(client);
         return retval;
         return retval;
     }
     }
 
 
@@ -178,11 +201,11 @@ openSecureChannel(UA_Client *client, UA_Boolean renew) {
     retval = receiveServiceResponse(client, &response,
     retval = receiveServiceResponse(client, &response,
                                     &UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE],
                                     &UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE],
                                     UA_DateTime_nowMonotonic() +
                                     UA_DateTime_nowMonotonic() +
-                                    ((UA_DateTime)client->config.timeout * UA_MSEC_TO_DATETIME),
+                                    ((UA_DateTime)client->config.timeout * UA_DATETIME_MSEC),
                                     &requestId);
                                     &requestId);
-                                    
+
     if(retval != UA_STATUSCODE_GOOD) {
     if(retval != UA_STATUSCODE_GOOD) {
-        UA_Client_disconnect(client);
+        UA_Client_close(client);
         return retval;
         return retval;
     }
     }
 
 
@@ -222,7 +245,7 @@ activateSession(UA_Client *client) {
     __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST],
     __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST],
                         &response, &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE]);
                         &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,
         UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
                      "ActivateSession failed with error code %s",
                      "ActivateSession failed with error code %s",
                      UA_StatusCode_name(response.responseHeader.serviceResult));
                      UA_StatusCode_name(response.responseHeader.serviceResult));
@@ -246,7 +269,6 @@ UA_Client_getEndpointsInternal(UA_Client *client, size_t* endpointDescriptionsSi
     request.endpointUrl = client->endpointUrl;
     request.endpointUrl = client->endpointUrl;
 
 
     UA_GetEndpointsResponse response;
     UA_GetEndpointsResponse response;
-    UA_GetEndpointsResponse_init(&response);
     __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST],
     __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST],
                         &response, &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE]);
                         &response, &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE]);
 
 
@@ -347,7 +369,6 @@ createSession(UA_Client *client) {
     UA_String_copy(&client->endpointUrl, &request.endpointUrl);
     UA_String_copy(&client->endpointUrl, &request.endpointUrl);
 
 
     UA_CreateSessionResponse response;
     UA_CreateSessionResponse response;
-    UA_CreateSessionResponse_init(&response);
     __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST],
     __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST],
                         &response, &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE]);
                         &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) {
                           UA_Boolean endpointsHandshake, UA_Boolean createNewSession) {
     if(client->state >= UA_CLIENTSTATE_CONNECTED)
     if(client->state >= UA_CLIENTSTATE_CONNECTED)
         return UA_STATUSCODE_GOOD;
         return UA_STATUSCODE_GOOD;
-
     UA_ChannelSecurityToken_init(&client->channel.securityToken);
     UA_ChannelSecurityToken_init(&client->channel.securityToken);
     client->channel.state = UA_SECURECHANNELSTATE_FRESH;
     client->channel.state = UA_SECURECHANNELSTATE_FRESH;
 
 
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     client->connection =
     client->connection =
         client->config.connectionFunc(client->config.localConnectionConfig,
         client->config.connectionFunc(client->config.localConnectionConfig,
-                                      endpointUrl, client->config.timeout);
+                                      endpointUrl, client->config.timeout,
+                                      client->config.logger);
     if(client->connection.state != UA_CONNECTION_OPENING) {
     if(client->connection.state != UA_CONNECTION_OPENING) {
         retval = UA_STATUSCODE_BADCONNECTIONCLOSED;
         retval = UA_STATUSCODE_BADCONNECTIONCLOSED;
         goto cleanup;
         goto cleanup;
@@ -389,49 +410,72 @@ UA_Client_connectInternal(UA_Client *client, const char *endpointUrl,
     retval = HelAckHandshake(client);
     retval = HelAckHandshake(client);
     if(retval != UA_STATUSCODE_GOOD)
     if(retval != UA_STATUSCODE_GOOD)
         goto cleanup;
         goto cleanup;
-    client->state = UA_CLIENTSTATE_CONNECTED;
+    setClientState(client, UA_CLIENTSTATE_CONNECTED);
 
 
     /* Open a SecureChannel. TODO: Select with endpoint  */
     /* Open a SecureChannel. TODO: Select with endpoint  */
     client->channel.connection = &client->connection;
     client->channel.connection = &client->connection;
     retval = openSecureChannel(client, false);
     retval = openSecureChannel(client, false);
     if(retval != UA_STATUSCODE_GOOD)
     if(retval != UA_STATUSCODE_GOOD)
         goto cleanup;
         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)
             if(retval != UA_STATUSCODE_GOOD)
                 goto cleanup;
                 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)
         if(retval != UA_STATUSCODE_GOOD)
             goto cleanup;
             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);
         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)
         if(retval != UA_STATUSCODE_GOOD)
             goto cleanup;
             goto cleanup;
-        client->state = UA_CLIENTSTATE_SESSION;
+        setClientState(client, UA_CLIENTSTATE_SESSION);
     }
     }
+
     return retval;
     return retval;
 
 
 cleanup:
 cleanup:
-    UA_Client_disconnect(client);
+    UA_Client_close(client);
     return retval;
     return retval;
 }
 }
 
 
@@ -453,7 +497,8 @@ UA_StatusCode
 UA_Client_manuallyRenewSecureChannel(UA_Client *client) {
 UA_Client_manuallyRenewSecureChannel(UA_Client *client) {
     UA_StatusCode retval = openSecureChannel(client, true);
     UA_StatusCode retval = openSecureChannel(client, true);
     if(retval != UA_STATUSCODE_GOOD)
     if(retval != UA_STATUSCODE_GOOD)
-        client->state = UA_CLIENTSTATE_DISCONNECTED;
+        UA_Client_close(client);
+
     return retval;
     return retval;
 }
 }
 
 
@@ -488,27 +533,57 @@ sendCloseSecureChannel(UA_Client *client) {
     UA_SecureChannel_sendSymmetricMessage(channel, ++client->requestId,
     UA_SecureChannel_sendSymmetricMessage(channel, ++client->requestId,
                                           UA_MESSAGETYPE_CLO, &request,
                                           UA_MESSAGETYPE_CLO, &request,
                                           &UA_TYPES[UA_TYPES_CLOSESECURECHANNELREQUEST]);
                                           &UA_TYPES[UA_TYPES_CLOSESECURECHANNELREQUEST]);
+    UA_CloseSecureChannelRequest_deleteMembers(&request);
     UA_SecureChannel_deleteMembersCleanup(&client->channel);
     UA_SecureChannel_deleteMembersCleanup(&client->channel);
 }
 }
 
 
 UA_StatusCode
 UA_StatusCode
 UA_Client_disconnect(UA_Client *client) {
 UA_Client_disconnect(UA_Client *client) {
     /* Is a session established? */
     /* 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);
         sendCloseSession(client);
     }
     }
     UA_NodeId_deleteMembers(&client->authenticationToken);
     UA_NodeId_deleteMembers(&client->authenticationToken);
     client->requestHandle = 0;
     client->requestHandle = 0;
 
 
     /* Is a secure channel established? */
     /* Is a secure channel established? */
-    if(client->state >= UA_CLIENTSTATE_SECURECHANNEL)
+    if(client->state >= UA_CLIENTSTATE_SECURECHANNEL) {
+        client->state = UA_CLIENTSTATE_CONNECTED;
         sendCloseSecureChannel(client);
         sendCloseSecureChannel(client);
+    }
 
 
     /* Close the TCP connection */
     /* 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->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;
     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
 /* 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
  * 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.h"
 #include "ua_client_internal.h"
 #include "ua_client_internal.h"
@@ -58,7 +63,6 @@ UA_Client_findServers(UA_Client *client, const char *serverUrl,
 
 
     /* Send the request */
     /* Send the request */
     UA_FindServersResponse response;
     UA_FindServersResponse response;
-    UA_FindServersResponse_init(&response);
     __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_FINDSERVERSREQUEST],
     __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_FINDSERVERSREQUEST],
                         &response, &UA_TYPES[UA_TYPES_FINDSERVERSRESPONSE]);
                         &response, &UA_TYPES[UA_TYPES_FINDSERVERSRESPONSE]);
 
 
@@ -109,7 +113,6 @@ UA_Client_findServersOnNetwork(UA_Client *client, const char *serverUrl,
 
 
     /* Send the request */
     /* Send the request */
     UA_FindServersOnNetworkResponse response;
     UA_FindServersOnNetworkResponse response;
-    UA_FindServersOnNetworkResponse_init(&response);
     __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKREQUEST],
     __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKREQUEST],
                         &response, &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKRESPONSE]);
                         &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
 /* 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
  * 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.h"
 #include "ua_client_highlevel.h"
 #include "ua_client_highlevel.h"
@@ -35,7 +42,7 @@ UA_Client_NamespaceGetIndex(UA_Client *client, UA_String *namespaceUri,
 
 
     retval = UA_STATUSCODE_BADNOTFOUND;
     retval = UA_STATUSCODE_BADNOTFOUND;
     UA_String *ns = (UA_String *)response.results[0].value.data;
     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])) {
         if(UA_String_equal(namespaceUri, &ns[i])) {
             *namespaceIndex = (UA_UInt16)i;
             *namespaceIndex = (UA_UInt16)i;
             retval = UA_STATUSCODE_GOOD;
             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
 /* 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
  * 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_
 #ifndef UA_CLIENT_INTERNAL_H_
 #define UA_CLIENT_INTERNAL_H_
 #define UA_CLIENT_INTERNAL_H_
 
 
 #include "ua_securechannel.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
 #ifdef UA_ENABLE_SUBSCRIPTIONS
 
 
@@ -20,35 +30,53 @@ typedef struct UA_Client_NotificationsAckNumber {
 } UA_Client_NotificationsAckNumber;
 } UA_Client_NotificationsAckNumber;
 
 
 typedef struct UA_Client_MonitoredItem {
 typedef struct UA_Client_MonitoredItem {
-    LIST_ENTRY(UA_Client_MonitoredItem)  listEntry;
+    LIST_ENTRY(UA_Client_MonitoredItem) listEntry;
     UA_UInt32 monitoredItemId;
     UA_UInt32 monitoredItemId;
-    UA_UInt32 monitoringMode;
-    UA_NodeId monitoredNodeId;
-    UA_UInt32 attributeID;
     UA_UInt32 clientHandle;
     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;
 } UA_Client_MonitoredItem;
 
 
 typedef struct UA_Client_Subscription {
 typedef struct UA_Client_Subscription {
     LIST_ENTRY(UA_Client_Subscription) listEntry;
     LIST_ENTRY(UA_Client_Subscription) listEntry;
-    UA_UInt32 lifeTime;
-    UA_UInt32 keepAliveCount;
+    UA_UInt32 subscriptionId;
+    void *context;
     UA_Double publishingInterval;
     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;
     LIST_HEAD(UA_ListOfClientMonitoredItems, UA_Client_MonitoredItem) monitoredItems;
 } UA_Client_Subscription;
 } 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 */
 /* Client */
@@ -62,6 +90,11 @@ typedef struct AsyncServiceCall {
     void *userdata;
     void *userdata;
 } AsyncServiceCall;
 } 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 {
 typedef enum {
     UA_CLIENTAUTHENTICATION_NONE,
     UA_CLIENTAUTHENTICATION_NONE,
     UA_CLIENTAUTHENTICATION_USERNAME
     UA_CLIENTAUTHENTICATION_USERNAME
@@ -70,6 +103,7 @@ typedef enum {
 struct UA_Client {
 struct UA_Client {
     /* State */
     /* State */
     UA_ClientState state;
     UA_ClientState state;
+
     UA_ClientConfig config;
     UA_ClientConfig config;
 
 
     /* Connection */
     /* Connection */
@@ -77,7 +111,7 @@ struct UA_Client {
     UA_String endpointUrl;
     UA_String endpointUrl;
 
 
     /* SecureChannel */
     /* SecureChannel */
-    UA_SecurityPolicy securityPolicy;
+    UA_SecurityPolicy securityPolicy; /* TODO: Move supported policies to the config */
     UA_SecureChannel channel;
     UA_SecureChannel channel;
     UA_UInt32 requestId;
     UA_UInt32 requestId;
     UA_DateTime nextChannelRenewal;
     UA_DateTime nextChannelRenewal;
@@ -100,9 +134,13 @@ struct UA_Client {
     UA_UInt32 monitoredItemHandles;
     UA_UInt32 monitoredItemHandles;
     LIST_HEAD(ListOfUnacknowledgedNotifications, UA_Client_NotificationsAckNumber) pendingNotificationsAcks;
     LIST_HEAD(ListOfUnacknowledgedNotifications, UA_Client_NotificationsAckNumber) pendingNotificationsAcks;
     LIST_HEAD(ListOfClientSubscriptionItems, UA_Client_Subscription) subscriptions;
     LIST_HEAD(ListOfClientSubscriptionItems, UA_Client_Subscription) subscriptions;
+    UA_UInt16 currentlyOutStandingPublishRequests;
 #endif
 #endif
 };
 };
 
 
+void
+setClientState(UA_Client *client, UA_ClientState state);
+
 UA_StatusCode
 UA_StatusCode
 UA_Client_connectInternal(UA_Client *client, const char *endpointUrl,
 UA_Client_connectInternal(UA_Client *client, const char *endpointUrl,
                           UA_Boolean endpointsHandshake, UA_Boolean createNewSession);
                           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
 /* 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
  * 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 */
 /* Enable POSIX features */
 #if !defined(_XOPEN_SOURCE) && !defined(_WRS_KERNEL)
 #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;
     int hashIdx = mdns_hash_record(record) % SERVER_ON_NETWORK_HASH_PRIME;
     struct serverOnNetwork_hash_entry *hash_entry = server->serverOnNetworkHash[hashIdx];
     struct serverOnNetwork_hash_entry *hash_entry = server->serverOnNetworkHash[hashIdx];
 
 
-    while (hash_entry) {
+    while(hash_entry) {
         size_t maxLen;
         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;
             maxLen = hash_entry->entry->serverOnNetwork.serverName.length;
         else
         else
             maxLen = serverNameLen;
             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;
             return hash_entry->entry;
         hash_entry = hash_entry->next;
         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
     // todo: malloc may fail: return a statuscode
     listEntry->serverOnNetwork.serverName.data = (UA_Byte*)UA_malloc(serverNameLen);
     listEntry->serverOnNetwork.serverName.data = (UA_Byte*)UA_malloc(serverNameLen);
     memcpy(listEntry->serverOnNetwork.serverName.data, serverName, 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();
         server->serverOnNetworkRecordIdLastReset = UA_DateTime_now();
 
 
     // add to hash
     // add to hash
@@ -165,7 +169,7 @@ mdns_record_remove(UA_Server *server, const char *record,
     server->serverOnNetworkSize--;
     server->serverOnNetworkSize--;
     UA_free(entry);
     UA_free(entry);
 #else
 #else
-    server->serverOnNetworkSize = uatomic_add_return(&server->serverOnNetworkSize, -1);
+    UA_atomic_subSize(&server->serverOnNetworkSize, 1);
     UA_Server_delayedCallback(server, delayedFree, entry);
     UA_Server_delayedCallback(server, delayedFree, entry);
 #endif
 #endif
 }
 }
@@ -190,7 +194,7 @@ setTxt(const struct resource *r,
     char *caps = (char *) xht_get(x, "caps");
     char *caps = (char *) xht_get(x, "caps");
 
 
     if(path && strlen(path) > 1) {
     if(path && strlen(path) > 1) {
-        if (!entry->srvSet) {
+        if(!entry->srvSet) {
             /* txt arrived before SRV, thus cache path entry */
             /* txt arrived before SRV, thus cache path entry */
             // todo: malloc in strdup may fail: return a statuscode
             // todo: malloc in strdup may fail: return a statuscode
             entry->pathTmp = UA_STRDUP(path);
             entry->pathTmp = UA_STRDUP(path);
@@ -220,7 +224,7 @@ setTxt(const struct resource *r,
             // todo: malloc may fail: return a statuscode
             // todo: malloc may fail: return a statuscode
             entry->serverOnNetwork.serverCapabilities[i].data = (UA_Byte*)UA_malloc(len);
             entry->serverOnNetwork.serverCapabilities[i].data = (UA_Byte*)UA_malloc(len);
             memcpy(entry->serverOnNetwork.serverCapabilities[i].data, caps, len);
             memcpy(entry->serverOnNetwork.serverCapabilities[i].data, caps, len);
-            if (nextStr)
+            if(nextStr)
                 caps = nextStr + 1;
                 caps = nextStr + 1;
             else
             else
                 break;
                 break;
@@ -324,11 +328,11 @@ void mdns_create_txt(UA_Server *server, const char *fullServiceDomain, const cha
                                     600, conflict, server);
                                     600, conflict, server);
     xht_t *h = xht_new(11);
     xht_t *h = xht_new(11);
     char *allocPath = NULL;
     char *allocPath = NULL;
-    if (!path || strlen(path) == 0) {
+    if(!path || strlen(path) == 0) {
         xht_set(h, "path", "/");
         xht_set(h, "path", "/");
     } else {
     } else {
         // path does not contain slash, so add it here
         // path does not contain slash, so add it here
-        if (path[0] == '/')
+        if(path[0] == '/')
             // todo: malloc in strdup may fail: return a statuscode
             // todo: malloc in strdup may fail: return a statuscode
             allocPath = UA_STRDUP(path);
             allocPath = UA_STRDUP(path);
         else {
         else {
@@ -343,7 +347,7 @@ void mdns_create_txt(UA_Server *server, const char *fullServiceDomain, const cha
 
 
     // calculate max string length:
     // calculate max string length:
     size_t capsLen = 0;
     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
         // add comma or last \0
         capsLen += capabilites[i].length + 1;
         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
         // todo: malloc may fail: return a statuscode
         caps = (char*)UA_malloc(sizeof(char) * capsLen);
         caps = (char*)UA_malloc(sizeof(char) * capsLen);
         size_t idx = 0;
         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);
             memcpy(caps + idx, (const char *) capabilites[i].data, capabilites[i].length);
             idx += capabilites[i].length + 1;
             idx += capabilites[i].length + 1;
             caps[idx - 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].
     // [servername]-[hostname]._opcua-tcp._tcp.local. A [ip].
     mdns_record_t *r = mdnsd_shared(server->mdnsDaemon, fullServiceDomain, QTYPE_A, 600);
     mdns_record_t *r = mdnsd_shared(server->mdnsDaemon, fullServiceDomain, QTYPE_A, 600);
     mdnsd_set_raw(server->mdnsDaemon, r, addr, addr_len);
     mdnsd_set_raw(server->mdnsDaemon, r, addr, addr_len);
-    
+
     // [hostname]. A [ip].
     // [hostname]. A [ip].
     r = mdnsd_shared(server->mdnsDaemon, localDomain, QTYPE_A, 600);
     r = mdnsd_shared(server->mdnsDaemon, localDomain, QTYPE_A, 600);
     mdnsd_set_raw(server->mdnsDaemon, r, addr, addr_len);
     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) {
     for(size_t attempts = 0; attempts != 3; ++attempts) {
         // todo: malloc may fail: return a statuscode
         // todo: malloc may fail: return a statuscode
         adapter_addresses = (IP_ADAPTER_ADDRESSES*)UA_malloc(adapter_addresses_buffer_size);
         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,
             UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
                          "GetAdaptersAddresses out of memory");
                          "GetAdaptersAddresses out of memory");
             adapter_addresses = NULL;
             adapter_addresses = NULL;
@@ -442,7 +446,7 @@ getInterfaces(UA_Server *server) {
             adapter_addresses = NULL;
             adapter_addresses = NULL;
             continue;
             continue;
         }
         }
-        
+
         /* Unexpected error */
         /* Unexpected error */
         UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
         UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
                      "GetAdaptersAddresses returned an unexpected error. "
                      "GetAdaptersAddresses returned an unexpected error. "
@@ -457,7 +461,7 @@ getInterfaces(UA_Server *server) {
 void mdns_set_address_record(UA_Server *server, const char *fullServiceDomain,
 void mdns_set_address_record(UA_Server *server, const char *fullServiceDomain,
                              const char *localDomain) {
                              const char *localDomain) {
     IP_ADAPTER_ADDRESSES* adapter_addresses = getInterfaces(server);
     IP_ADAPTER_ADDRESSES* adapter_addresses = getInterfaces(server);
-    if (!adapter_addresses)
+    if(!adapter_addresses)
         return;
         return;
 
 
     /* Iterate through all of the adapters */
     /* 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,
                 mdns_set_address_record_if(server, fullServiceDomain, localDomain,
                                            (char *)&ipv4->sin_addr, 4);
                                            (char *)&ipv4->sin_addr, 4);
             }
             }
-            /*else if (AF_INET6 == family) {
+            /*else if(AF_INET6 == family) {
             // IPv6
             // IPv6
             SOCKADDR_IN6* ipv6 = (SOCKADDR_IN6*)(address->Address.lpSockaddr);
             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")) {
             if(0 == ipv6_str.find("fe")) {
             char c = ipv6_str[2];
             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;
             is_link_local = true;
             } else if (0 == ipv6_str.find("2001:0:")) {
             } else if (0 == ipv6_str.find("2001:0:")) {
             is_special_use = true;
             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
 /* 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
  * 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
 #ifndef UA_MDNS_INTERNAL_H
 #define 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
 /* 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
  * 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_server_internal.h"
 #include "ua_types_encoding_binary.h"
 #include "ua_types_encoding_binary.h"
@@ -254,7 +263,7 @@ UA_Node_copy_alloc(const UA_Node *src) {
     dst->nodeClass = src->nodeClass;
     dst->nodeClass = src->nodeClass;
 
 
     UA_StatusCode retval = UA_Node_copy(src, dst);
     UA_StatusCode retval = UA_Node_copy(src, dst);
-    if (retval != UA_STATUSCODE_GOOD){
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_free(dst);
         UA_free(dst);
         return NULL;
         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)
     /* 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 */
      * we need to decode it and set the decoded value instead of the encoded object */
     UA_Boolean valueSet = false;
     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;
         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,
             /* TODO: Once we generate type description in the nodeset compiler,
              * UA_findDatatypeByBinary can be made internal to the decoding
              * UA_findDatatypeByBinary can be made internal to the decoding
              * layer. */
              * layer. */
             const UA_DataType *type = UA_findDataTypeByBinary(&obj->content.encoded.typeId);
             const UA_DataType *type = UA_findDataTypeByBinary(&obj->content.encoded.typeId);
 
 
-            if (type) {
+            if(type) {
                 void *dst = UA_Array_new(attr->value.arrayLength, type);
                 void *dst = UA_Array_new(attr->value.arrayLength, type);
                 uint8_t *tmpPos = (uint8_t *)dst;
                 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;
                     size_t offset =0;
                     const UA_ExtensionObject *curr = &((const UA_ExtensionObject *)attr->value.data)[i];
                     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);
                     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;
                         return ret;
                     }
                     }
                     tmpPos += type->memSize;
                     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);
         retval |= UA_Variant_copy(&attr->value, &node->value.data.value.value);
 
 
     node->value.data.value.hasValue = true;
     node->value.data.value.hasValue = true;
@@ -512,13 +527,23 @@ addReferenceKind(UA_Node *node, const UA_AddReferencesItem *item) {
 
 
 UA_StatusCode
 UA_StatusCode
 UA_Node_addReference(UA_Node *node, const UA_AddReferencesItem *item) {
 UA_Node_addReference(UA_Node *node, const UA_AddReferencesItem *item) {
+    UA_NodeReferenceKind *existingRefs = NULL;
     for(size_t i = 0; i < node->referencesSize; ++i) {
     for(size_t i = 0; i < node->referencesSize; ++i) {
         UA_NodeReferenceKind *refs = &node->references[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);
     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
 /* 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
  * 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_securechannel_manager.h"
 #include "ua_session.h"
 #include "ua_session.h"
@@ -11,7 +19,7 @@
 #define STARTTOKENID 1
 #define STARTTOKENID 1
 
 
 UA_StatusCode
 UA_StatusCode
-UA_SecureChannelManager_init(UA_SecureChannelManager* cm, UA_Server* server) {
+UA_SecureChannelManager_init(UA_SecureChannelManager *cm, UA_Server *server) {
     LIST_INIT(&cm->channels);
     LIST_INIT(&cm->channels);
     // TODO: use an ID that is likely to be unique after a restart
     // TODO: use an ID that is likely to be unique after a restart
     cm->lastChannelId = STARTCHANNELID;
     cm->lastChannelId = STARTCHANNELID;
@@ -21,7 +29,8 @@ UA_SecureChannelManager_init(UA_SecureChannelManager* cm, UA_Server* server) {
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
 
 
-void UA_SecureChannelManager_deleteMembers(UA_SecureChannelManager* cm) {
+void
+UA_SecureChannelManager_deleteMembers(UA_SecureChannelManager *cm) {
     channel_list_entry *entry, *temp;
     channel_list_entry *entry, *temp;
     LIST_FOREACH_SAFE(entry, &cm->channels, pointers, temp) {
     LIST_FOREACH_SAFE(entry, &cm->channels, pointers, temp) {
         LIST_REMOVE(entry, pointers);
         LIST_REMOVE(entry, pointers);
@@ -32,7 +41,7 @@ void UA_SecureChannelManager_deleteMembers(UA_SecureChannelManager* cm) {
 
 
 static void
 static void
 removeSecureChannelCallback(UA_Server *server, void *entry) {
 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_SecureChannel_deleteMembersCleanup(&centry->channel);
     UA_free(entry);
     UA_free(entry);
 }
 }
@@ -51,7 +60,7 @@ removeSecureChannel(UA_SecureChannelManager *cm, channel_list_entry *entry) {
 
 
     /* Detach the channel and make the capacity available */
     /* Detach the channel and make the capacity available */
     LIST_REMOVE(entry, pointers);
     LIST_REMOVE(entry, pointers);
-    UA_atomic_add(&cm->currentChannelCount, (UA_UInt32)-1);
+    UA_atomic_subUInt32(&cm->currentChannelCount, 1);
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
 
 
@@ -61,7 +70,7 @@ UA_SecureChannelManager_cleanupTimedOut(UA_SecureChannelManager *cm, UA_DateTime
     channel_list_entry *entry, *temp;
     channel_list_entry *entry, *temp;
     LIST_FOREACH_SAFE(entry, &cm->channels, pointers, temp) {
     LIST_FOREACH_SAFE(entry, &cm->channels, pointers, temp) {
         UA_DateTime timeout = entry->channel.securityToken.createdAt +
         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) {
         if(timeout < nowMonotonic || !entry->channel.connection) {
             UA_LOG_INFO_CHANNEL(cm->server->config.logger, &entry->channel,
             UA_LOG_INFO_CHANNEL(cm->server->config.logger, &entry->channel,
                                 "SecureChannel has timed out");
                                 "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 */
 /* 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) {
     LIST_FOREACH(entry, &cm->channels, pointers) {
         if(LIST_EMPTY(&(entry->channel.sessions))) {
         if(LIST_EMPTY(&(entry->channel.sessions))) {
             UA_LOG_DEBUG_CHANNEL(cm->server->config.logger, &entry->channel,
             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,
     UA_LOG_INFO(cm->server->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
                 "Creating a new 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)
     if(!entry)
         return UA_STATUSCODE_BADOUTOFMEMORY;
         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;
     entry->channel.securityToken.revisedLifetime = cm->server->config.maxSecurityTokenLifetime;
 
 
     LIST_INSERT_HEAD(&cm->channels, entry, pointers);
     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);
     UA_Connection_attachSecureChannel(connection, &entry->channel);
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
 
 
 UA_StatusCode
 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) {
     if(channel->state != UA_SECURECHANNELSTATE_FRESH) {
         UA_LOG_ERROR_CHANNEL(cm->server->config.logger, channel,
         UA_LOG_ERROR_CHANNEL(cm->server->config.logger, channel,
                              "Called open on already open or closed 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;
         return UA_STATUSCODE_BADSECURITYMODEREJECTED;
     }
     }
 
 
+    channel->securityMode = request->securityMode;
+    channel->securityToken.createdAt = UA_DateTime_nowMonotonic();
     channel->securityToken.channelId = cm->lastChannelId++;
     channel->securityToken.channelId = cm->lastChannelId++;
     channel->securityToken.createdAt = UA_DateTime_now();
     channel->securityToken.createdAt = UA_DateTime_now();
+
+    /* Set the lifetime. Lifetime 0 -> set the maximum possible */
     channel->securityToken.revisedLifetime =
     channel->securityToken.revisedLifetime =
         (request->requestedLifetime > cm->server->config.maxSecurityTokenLifetime) ?
         (request->requestedLifetime > cm->server->config.maxSecurityTokenLifetime) ?
         cm->server->config.maxSecurityTokenLifetime : request->requestedLifetime;
         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;
         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.timestamp = UA_DateTime_now();
     response->responseHeader.requestHandle = request->requestHeader.requestHandle;
     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;
     channel->state = UA_SECURECHANNELSTATE_OPEN;
+
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
 
 
 UA_StatusCode
 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) {
     if(channel->state != UA_SECURECHANNELSTATE_OPEN) {
         UA_LOG_ERROR_CHANNEL(cm->server->config.logger, channel,
         UA_LOG_ERROR_CHANNEL(cm->server->config.logger, channel,
                              "Called renew on channel which is not open");
                              "Called renew on channel which is not open");
@@ -200,26 +213,26 @@ UA_SecureChannelManager_renew(UA_SecureChannelManager* cm, UA_SecureChannel *cha
 
 
     /* Replace the nonces */
     /* Replace the nonces */
     UA_ByteString_deleteMembers(&channel->remoteNonce);
     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 */
     /* Set the response */
     response->responseHeader.requestHandle = request->requestHeader.requestHandle;
     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 */
     /* Reset the internal creation date to the monotonic clock */
     channel->nextSecurityToken.createdAt = UA_DateTime_nowMonotonic();
     channel->nextSecurityToken.createdAt = UA_DateTime_nowMonotonic();
     return UA_STATUSCODE_GOOD;
     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) {
     LIST_FOREACH(entry, &cm->channels, pointers) {
         if(entry->channel.securityToken.channelId == channelId)
         if(entry->channel.securityToken.channelId == channelId)
             return &entry->channel;
             return &entry->channel;
@@ -228,8 +241,8 @@ UA_SecureChannelManager_get(UA_SecureChannelManager* cm, UA_UInt32 channelId) {
 }
 }
 
 
 UA_StatusCode
 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) {
     LIST_FOREACH(entry, &cm->channels, pointers) {
         if(entry->channel.securityToken.channelId == channelId)
         if(entry->channel.securityToken.channelId == channelId)
             break;
             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
 /* 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
  * 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_
 #ifndef UA_CHANNEL_MANAGER_H_
 #define UA_CHANNEL_MANAGER_H_
 #define UA_CHANNEL_MANAGER_H_
@@ -12,7 +18,7 @@ extern "C" {
 #include "ua_util.h"
 #include "ua_util.h"
 #include "ua_server.h"
 #include "ua_server.h"
 #include "ua_securechannel.h"
 #include "ua_securechannel.h"
-#include "queue.h"
+#include "../../deps/queue.h"
 
 
 typedef struct channel_list_entry {
 typedef struct channel_list_entry {
     UA_SecureChannel channel;
     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
 /* 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
  * 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_types.h"
 #include "ua_server_internal.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;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     for(size_t i = parentCopy->referencesSize; i > 0; --i) {
     for(size_t i = parentCopy->referencesSize; i > 0; --i) {
         UA_NodeReferenceKind *ref = &parentCopy->references[i - 1];
         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,
             retval |= callback(ref->targetIds[j].nodeId, ref->isInverse,
                                ref->referenceTypeId, handle);
                                ref->referenceTypeId, handle);
     }
     }
@@ -132,8 +145,14 @@ void UA_Server_delete(UA_Server *server) {
 #endif
 #endif
 
 
 #ifdef UA_ENABLE_MULTITHREADING
 #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_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
 #endif
 
 
     /* Delete the timed work */
     /* Delete the timed work */
@@ -160,18 +179,23 @@ UA_Server_cleanup(UA_Server *server, void *_) {
 
 
 UA_Server *
 UA_Server *
 UA_Server_new(const UA_ServerConfig *config) {
 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;
         return NULL;
 
 
+    /* At least one endpoint has to be configured */
     if(config->endpointsSize == 0) {
     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.");
                      "There has to be at least one endpoint.");
-        UA_free(server);
         return NULL;
         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;
     server->config = *config;
 
 
     /* Init start time to zero, the actual start time will be sampled in
     /* 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 */
     /* Initialized the dispatch queue for worker threads */
 #ifdef UA_ENABLE_MULTITHREADING
 #ifdef UA_ENABLE_MULTITHREADING
-    cds_wfcq_init(&server->dispatchQueue_head, &server->dispatchQueue_tail);
+    SIMPLEQ_INIT(&server->dispatchQueue);
 #endif
 #endif
 
 
     /* Create Namespaces 0 and 1 */
     /* Create Namespaces 0 and 1 */
@@ -244,10 +268,10 @@ UA_Server_new(const UA_ServerConfig *config) {
 
 
     /* Initialize namespace 0*/
     /* Initialize namespace 0*/
     UA_StatusCode retVal = UA_Server_initNS0(server);
     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_StatusCode_name(retVal));
         UA_Server_delete(server);
         UA_Server_delete(server);
         return NULL;
         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
 /* 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
  * 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_util.h"
 #include "ua_server_internal.h"
 #include "ua_server_internal.h"
@@ -14,7 +26,6 @@
 #include "ua_types_generated_handling.h"
 #include "ua_types_generated_handling.h"
 #include "ua_securitypolicy_none.h"
 #include "ua_securitypolicy_none.h"
 
 
-
 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
 // store the authentication token and session ID so we can help fuzzing by setting
 // store the authentication token and session ID so we can help fuzzing by setting
 // these values in the next request automatically
 // 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);
     UA_StatusCode retval = UA_RequestHeader_decodeBinary(msg, &offset, &requestHeader);
     if(retval != UA_STATUSCODE_GOOD)
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
         return retval;
-    void *response = UA_alloca(responseType->memSize);
+    UA_STACKARRAY(UA_Byte, response, responseType->memSize);
     UA_init(response, responseType);
     UA_init(response, responseType);
     UA_ResponseHeader *responseHeader = (UA_ResponseHeader*)response;
     UA_ResponseHeader *responseHeader = (UA_ResponseHeader*)response;
     responseHeader->requestHandle = requestHeader.requestHandle;
     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!
     // 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,
     retval = UA_SecureChannel_sendSymmetricMessage(channel, requestId, UA_MESSAGETYPE_MSG,
                                                    response, responseType);
                                                    response, responseType);
+
     UA_RequestHeader_deleteMembers(&requestHeader);
     UA_RequestHeader_deleteMembers(&requestHeader);
     UA_LOG_DEBUG(channel->securityPolicy->logger, UA_LOGCATEGORY_SERVER,
     UA_LOG_DEBUG(channel->securityPolicy->logger, UA_LOGCATEGORY_SERVER,
                  "Sent ServiceFault with error code %s", UA_StatusCode_name(error));
                  "Sent ServiceFault with error code %s", UA_StatusCode_name(error));
     return retval;
     return retval;
 }
 }
 
 
+typedef enum {
+    UA_SERVICETYPE_NORMAL,
+    UA_SERVICETYPE_INSITU,
+    UA_SERVICETYPE_CUSTOM
+} UA_ServiceType;
+
 static void
 static void
 getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
 getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
                    const UA_DataType **responseType, UA_Service *service,
                    const UA_DataType **responseType, UA_Service *service,
-                   UA_Boolean *requiresSession) {
+                   UA_Boolean *requiresSession, UA_ServiceType *serviceType) {
     switch(requestTypeId) {
     switch(requestTypeId) {
     case UA_NS0ID_GETENDPOINTSREQUEST_ENCODING_DEFAULTBINARY:
     case UA_NS0ID_GETENDPOINTSREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_GetEndpoints;
         *service = (UA_Service)Service_GetEndpoints;
@@ -100,11 +118,13 @@ getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
         *requestType = &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST];
         *requestType = &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE];
         *responseType = &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE];
         *requiresSession = false;
         *requiresSession = false;
+        *serviceType = UA_SERVICETYPE_CUSTOM;
         break;
         break;
     case UA_NS0ID_ACTIVATESESSIONREQUEST_ENCODING_DEFAULTBINARY:
     case UA_NS0ID_ACTIVATESESSIONREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_ActivateSession;
         *service = (UA_Service)Service_ActivateSession;
         *requestType = &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST];
         *requestType = &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE];
         *responseType = &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE];
+        *serviceType = UA_SERVICETYPE_CUSTOM;
         break;
         break;
     case UA_NS0ID_CLOSESESSIONREQUEST_ENCODING_DEFAULTBINARY:
     case UA_NS0ID_CLOSESESSIONREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_CloseSession;
         *service = (UA_Service)Service_CloseSession;
@@ -115,6 +135,7 @@ getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
         *service = (UA_Service)Service_Read;
         *service = (UA_Service)Service_Read;
         *requestType = &UA_TYPES[UA_TYPES_READREQUEST];
         *requestType = &UA_TYPES[UA_TYPES_READREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_READRESPONSE];
         *responseType = &UA_TYPES[UA_TYPES_READRESPONSE];
+        *serviceType = UA_SERVICETYPE_INSITU;
         break;
         break;
     case UA_NS0ID_WRITEREQUEST_ENCODING_DEFAULTBINARY:
     case UA_NS0ID_WRITEREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_Write;
         *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_NodeId_decodeBinary(msg, &offset, &requestType);
     retval |= UA_OpenSecureChannelRequest_decodeBinary(msg, &offset, &openSecureChannelRequest);
     retval |= UA_OpenSecureChannelRequest_decodeBinary(msg, &offset, &openSecureChannelRequest);
 
 
-    /* Error occured */
+    /* Error occurred */
     if(retval != UA_STATUSCODE_GOOD ||
     if(retval != UA_STATUSCODE_GOOD ||
        requestType.identifier.numeric != UA_TYPES[UA_TYPES_OPENSECURECHANNELREQUEST].binaryEncodingId) {
        requestType.identifier.numeric != UA_TYPES[UA_TYPES_OPENSECURECHANNELREQUEST].binaryEncodingId) {
         UA_NodeId_deleteMembers(&requestType);
         UA_NodeId_deleteMembers(&requestType);
@@ -370,7 +391,8 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
     UA_StatusCode retval = UA_NodeId_decodeBinary(msg, &offset, &requestTypeId);
     UA_StatusCode retval = UA_NodeId_decodeBinary(msg, &offset, &requestTypeId);
     if(retval != UA_STATUSCODE_GOOD)
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
         return retval;
-    if(requestTypeId.identifierType != UA_NODEIDTYPE_NUMERIC)
+    if(requestTypeId.namespaceIndex != 0 ||
+       requestTypeId.identifierType != UA_NODEIDTYPE_NUMERIC)
         UA_NodeId_deleteMembers(&requestTypeId); /* leads to badserviceunsupported */
         UA_NodeId_deleteMembers(&requestTypeId); /* leads to badserviceunsupported */
 
 
     /* Store the start-position of the request */
     /* 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 *requestType = NULL;
     const UA_DataType *responseType = NULL;
     const UA_DataType *responseType = NULL;
     UA_Boolean sessionRequired = true;
     UA_Boolean sessionRequired = true;
+    UA_ServiceType serviceType = UA_SERVICETYPE_NORMAL;
     getServicePointers(requestTypeId.identifier.numeric, &requestType,
     getServicePointers(requestTypeId.identifier.numeric, &requestType,
-                       &responseType, &service, &sessionRequired);
+                       &responseType, &service, &sessionRequired, &serviceType);
     if(!requestType) {
     if(!requestType) {
         if(requestTypeId.identifier.numeric == 787) {
         if(requestTypeId.identifier.numeric == 787) {
             UA_LOG_INFO_CHANNEL(server->config.logger, channel,
             UA_LOG_INFO_CHANNEL(server->config.logger, channel,
@@ -399,7 +422,7 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
     UA_assert(responseType);
     UA_assert(responseType);
 
 
     /* Decode the request */
     /* Decode the request */
-    void *request = UA_alloca(requestType->memSize);
+    UA_STACKARRAY(UA_Byte, request, requestType->memSize);
     UA_RequestHeader *requestHeader = (UA_RequestHeader*)request;
     UA_RequestHeader *requestHeader = (UA_RequestHeader*)request;
     retval = UA_decodeBinary(msg, &offset, request, requestType,
     retval = UA_decodeBinary(msg, &offset, request, requestType,
                              server->config.customDataTypesSize,
                              server->config.customDataTypesSize,
@@ -411,7 +434,8 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
     }
     }
 
 
     /* Prepare the respone */
     /* 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_init(response, responseType);
     UA_Session *session = NULL; /* must be initialized before goto send_response */
     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
     #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
     // set the authenticationToken from the create session request to help fuzzing cover more lines
     // 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);
         UA_NodeId_copy(&unsafe_fuzz_authenticationToken, &requestHeader->authenticationToken);
     #endif
     #endif
 
 
     /* Find the matching session */
     /* 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))
     if(!session && !UA_NodeId_isNull(&requestHeader->authenticationToken))
         session = UA_SessionManager_getSessionByToken(&server->sessionManager,
         session = UA_SessionManager_getSessionByToken(&server->sessionManager,
                                                       &requestHeader->authenticationToken);
                                                       &requestHeader->authenticationToken);
@@ -470,7 +495,7 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
 
 
         UA_Session_init(&anonymousSession);
         UA_Session_init(&anonymousSession);
         anonymousSession.sessionId = UA_NODEID_GUID(0, UA_GUID_NULL);
         anonymousSession.sessionId = UA_NODEID_GUID(0, UA_GUID_NULL);
-        anonymousSession.channel = channel;
+        anonymousSession.header.channel = channel;
         session = &anonymousSession;
         session = &anonymousSession;
     }
     }
 
 
@@ -480,14 +505,14 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
                                "Calling service %i on a non-activated session",
                                "Calling service %i on a non-activated session",
                                requestType->binaryEncodingId);
                                requestType->binaryEncodingId);
         UA_SessionManager_removeSession(&server->sessionManager,
         UA_SessionManager_removeSession(&server->sessionManager,
-                                        &session->authenticationToken);
+                                        &session->header.authenticationToken);
         UA_deleteMembers(request, requestType);
         UA_deleteMembers(request, requestType);
         return sendServiceFault(channel, msg, requestPos, responseType,
         return sendServiceFault(channel, msg, requestPos, responseType,
                                 requestId, UA_STATUSCODE_BADSESSIONNOTACTIVATED);
                                 requestId, UA_STATUSCODE_BADSESSIONNOTACTIVATED);
     }
     }
 
 
     /* The session is bound to another channel */
     /* 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,
         UA_LOG_WARNING_CHANNEL(server->config.logger, channel,
                                "Client tries to use a Session that is not "
                                "Client tries to use a Session that is not "
                                "bound to this SecureChannel");
                                "bound to this SecureChannel");
@@ -509,26 +534,59 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
     }
     }
 #endif
 #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)->requestHandle = requestHeader->requestHandle;
     ((UA_ResponseHeader*)response)->timestamp = UA_DateTime_now();
     ((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)
     if(retval != UA_STATUSCODE_GOOD)
         UA_LOG_INFO_CHANNEL(server->config.logger, channel,
         UA_LOG_INFO_CHANNEL(server->config.logger, channel,
                             "Could not send the message over the SecureChannel "
                             "Could not send the message over the SecureChannel "
                             "with StatusCode %s", UA_StatusCode_name(retval));
                             "with StatusCode %s", UA_StatusCode_name(retval));
-
     /* Clean up */
     /* Clean up */
     UA_deleteMembers(request, requestType);
     UA_deleteMembers(request, requestType);
     UA_deleteMembers(response, responseType);
     UA_deleteMembers(response, responseType);
-
     return retval;
     return retval;
 }
 }
 
 

+ 0 - 0
src/server/ua_server_discovery.c


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