Selaa lähdekoodia

Merge branch 'master' into feature/architectures

Jose Cabral 6 vuotta sitten
vanhempi
commit
253612baf6

+ 10 - 7
CMakeLists.txt

@@ -406,6 +406,7 @@ set(exported_headers ${exported_headers}
                      ${PROJECT_SOURCE_DIR}/include/ua_types.h
                      ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.h
                      ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated_handling.h
+                     ${PROJECT_SOURCE_DIR}/include/ua_util.h
                      ${PROJECT_SOURCE_DIR}/include/ua_server.h
                      ${PROJECT_SOURCE_DIR}/include/ua_plugin_log.h
                      ${PROJECT_SOURCE_DIR}/include/ua_plugin_network.h
@@ -425,7 +426,8 @@ set(exported_headers ${exported_headers}
 set(internal_headers ${PROJECT_SOURCE_DIR}/deps/queue.h
                      ${PROJECT_SOURCE_DIR}/deps/pcg_basic.h
                      ${PROJECT_SOURCE_DIR}/deps/libc_time.h
-                     ${PROJECT_SOURCE_DIR}/src/ua_util.h
+                     ${PROJECT_SOURCE_DIR}/deps/base64.h
+                     ${PROJECT_SOURCE_DIR}/src/ua_util_internal.h
                      ${PROJECT_SOURCE_DIR}/src/ua_types_encoding_binary.h
                      ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated_encoding_binary.h
                      ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated.h
@@ -496,7 +498,8 @@ set(lib_sources ${PROJECT_SOURCE_DIR}/src/ua_types.c
 
                 # dependencies
                 ${PROJECT_SOURCE_DIR}/deps/libc_time.c
-                ${PROJECT_SOURCE_DIR}/deps/pcg_basic.c)
+                ${PROJECT_SOURCE_DIR}/deps/pcg_basic.c
+                ${PROJECT_SOURCE_DIR}/deps/base64.c)
 
 set(default_plugin_headers ${PROJECT_SOURCE_DIR}/plugins/ua_accesscontrol_default.h
                            ${PROJECT_SOURCE_DIR}/plugins/ua_pki_certificate.h
@@ -545,11 +548,11 @@ if(UA_ENABLE_DISCOVERY_MULTICAST)
                          ${internal_headers} )
     list(APPEND internal_headers ${PROJECT_SOURCE_DIR}/src/server/ua_mdns_internal.h)
     set(lib_sources ${PROJECT_SOURCE_DIR}/src/server/ua_mdns.c
-                    ${PROJECT_SOURCE_DIR}/deps/mdnsd/libmdnsd/1035.c
-                    ${PROJECT_SOURCE_DIR}/deps/mdnsd/libmdnsd/xht.c
-                    ${PROJECT_SOURCE_DIR}/deps/mdnsd/libmdnsd/sdtxt.c
-                    ${PROJECT_SOURCE_DIR}/deps/mdnsd/libmdnsd/mdnsd.c
-                    ${lib_sources})
+        ${PROJECT_SOURCE_DIR}/deps/mdnsd/libmdnsd/1035.c
+        ${PROJECT_SOURCE_DIR}/deps/mdnsd/libmdnsd/xht.c
+        ${PROJECT_SOURCE_DIR}/deps/mdnsd/libmdnsd/sdtxt.c
+        ${PROJECT_SOURCE_DIR}/deps/mdnsd/libmdnsd/mdnsd.c
+        ${lib_sources})
 endif()
 
 #########################

+ 1 - 0
arch/ua_network_tcp.c

@@ -11,6 +11,7 @@
 #include "ua_network_tcp.h"
 #include "ua_log_stdout.h"
 #include "../deps/queue.h"
+#include "ua_util.h"
 
 #include <string.h> // memset
 

+ 179 - 0
deps/base64.c

@@ -0,0 +1,179 @@
+/*
+
+  https://github.com/superwills/NibbleAndAHalf
+  base64.h -- Fast base64 encoding and decoding.
+  version 1.0.0, April 17, 2013 143a
+
+  Copyright (C) 2013 William Sherif
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  William Sherif
+  will.sherif@gmail.com
+
+  YWxsIHlvdXIgYmFzZSBhcmUgYmVsb25nIHRvIHVz
+
+*/
+
+#include "base64.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static const char* b64="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ;
+
+// maps A=>0,B=>1..
+static const unsigned char unb64[]={
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //10
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //20
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //30
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //40
+    0,   0,   0,  62,   0,   0,   0,  63,  52,  53, //50
+    54,  55,  56,  57,  58,  59,  60,  61,   0,   0, //60
+    0,   0,   0,   0,   0,   0,   1,   2,   3,   4, //70
+    5,   6,   7,   8,   9,  10,  11,  12,  13,  14, //80
+    15,  16,  17,  18,  19,  20,  21,  22,  23,  24, //90
+    25,   0,   0,   0,   0,   0,   0,  26,  27,  28, //100
+    29,  30,  31,  32,  33,  34,  35,  36,  37,  38, //110
+    39,  40,  41,  42,  43,  44,  45,  46,  47,  48, //120
+    49,  50,  51,   0,   0,   0,   0,   0,   0,   0, //130
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //140
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //150
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //160
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //170
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //180
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //190
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //200
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //210
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //220
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //230
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //240
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //250
+    0,   0,   0,   0,   0,   0,
+}; // This array has 256 elements
+
+// Converts binary data of length=len to base64 characters.
+// Length of the resultant string is stored in flen
+// (you must pass pointer flen).
+char* UA_base64( const void* binaryData, int len, int *flen )
+{
+    const unsigned char* bin = (const unsigned char*) binaryData ;
+    char* res ;
+
+    int rc = 0 ; // result counter
+    int byteNo ; // I need this after the loop
+
+    int modulusLen = len % 3 ;
+    int pad = ((modulusLen&1)<<1) + ((modulusLen&2)>>1) ; // 2 gives 1 and 1 gives 2, but 0 gives 0.
+
+    *flen = 4*(len + pad)/3 ;
+    res = (char*) malloc( (size_t)(*flen + 1) ) ; // and one for the null
+    if( !res )
+    {
+        puts( "ERROR: base64 could not allocate enough memory." ) ;
+        puts( "I must stop because I could not get enough" ) ;
+        return 0;
+    }
+
+    for( byteNo = 0 ; byteNo <= len-3 ; byteNo+=3 )
+    {
+        unsigned char BYTE0=bin[byteNo];
+        unsigned char BYTE1=bin[byteNo+1];
+        unsigned char BYTE2=bin[byteNo+2];
+        res[rc++]  = b64[ BYTE0 >> 2 ] ;
+        res[rc++]  = b64[ ((0x3&BYTE0)<<4) + (BYTE1 >> 4) ] ;
+        res[rc++]  = b64[ ((0x0f&BYTE1)<<2) + (BYTE2>>6) ] ;
+        res[rc++]  = b64[ 0x3f&BYTE2 ] ;
+    }
+
+    if( pad==2 )
+    {
+        res[rc++] = b64[ bin[byteNo] >> 2 ] ;
+        res[rc++] = b64[ (0x3&bin[byteNo])<<4 ] ;
+        res[rc++] = '=';
+        res[rc++] = '=';
+    }
+    else if( pad==1 )
+    {
+        res[rc++]  = b64[ bin[byteNo] >> 2 ] ;
+        res[rc++]  = b64[ ((0x3&bin[byteNo])<<4)   +   (bin[byteNo+1] >> 4) ] ;
+        res[rc++]  = b64[ (0x0f&bin[byteNo+1])<<2 ] ;
+        res[rc++] = '=';
+    }
+
+    res[rc]=0; // NULL TERMINATOR! ;)
+    return res ;
+}
+
+unsigned char* UA_unbase64( const char* ascii, int len, int *flen )
+{
+    const unsigned char *safeAsciiPtr = (const unsigned char*)ascii ;
+    unsigned char *bin ;
+    int cb=0;
+    int charNo;
+    int pad = 0 ;
+
+    if( len < 2 ) { // 2 accesses below would be OOB.
+        // catch empty string, return NULL as result.
+        puts( "ERROR: You passed an invalid base64 string (too short). You get NULL back." ) ;
+        *flen=0;
+        return 0 ;
+    }
+    if( safeAsciiPtr[ len-1 ]=='=' )  ++pad ;
+    if( safeAsciiPtr[ len-2 ]=='=' )  ++pad ;
+
+    *flen = 3*len/4 - pad ;
+    bin = (unsigned char*)malloc( (size_t) (*flen) ) ;
+    if( !bin )
+    {
+        puts( "ERROR: unbase64 could not allocate enough memory." ) ;
+        puts( "I must stop because I could not get enough" ) ;
+        return 0;
+    }
+
+    for( charNo=0; charNo <= len - 4 - pad ; charNo+=4 )
+    {
+        int A=unb64[safeAsciiPtr[charNo]];
+        int B=unb64[safeAsciiPtr[charNo+1]];
+        int C=unb64[safeAsciiPtr[charNo+2]];
+        int D=unb64[safeAsciiPtr[charNo+3]];
+
+        bin[cb++] = (unsigned char)((A<<2) | (B>>4)) ;
+        bin[cb++] = (unsigned char)((B<<4) | (C>>2)) ;
+        bin[cb++] = (unsigned char)((C<<6) | (D)) ;
+    }
+
+    if( pad==1 )
+    {
+        int A=unb64[safeAsciiPtr[charNo]];
+        int B=unb64[safeAsciiPtr[charNo+1]];
+        int C=unb64[safeAsciiPtr[charNo+2]];
+
+        bin[cb++] = (unsigned char)((A<<2) | (B>>4)) ;
+        bin[cb++] = (unsigned char)((B<<4) | (C>>2)) ;
+    }
+    else if( pad==2 )
+    {
+        int A=unb64[safeAsciiPtr[charNo]];
+        int B=unb64[safeAsciiPtr[charNo+1]];
+
+        bin[cb++] = (unsigned char)((A<<2) | (B>>4)) ;
+    }
+
+    return bin ;
+}
+

+ 46 - 0
deps/base64.h

@@ -0,0 +1,46 @@
+/*
+
+  https://github.com/superwills/NibbleAndAHalf
+  base64.h -- Fast base64 encoding and decoding.
+  version 1.0.0, April 17, 2013 143a
+
+  Copyright (C) 2013 William Sherif
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  William Sherif
+  will.sherif@gmail.com
+
+  YWxsIHlvdXIgYmFzZSBhcmUgYmVsb25nIHRvIHVz
+
+*/
+#ifndef UA_BASE64_H_
+#define UA_BASE64_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+char* UA_base64( const void* binaryData, int len, int *flen );
+
+unsigned char* UA_unbase64( const char* ascii, int len, int *flen );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UA_BASE64_H_ */

+ 7 - 1
include/ua_config.h.in

@@ -74,7 +74,7 @@ extern "C" {
 #else
 # include "ms_stdint.h"
 # if !defined(__bool_true_false_are_defined)
-#  define bool short
+#  define bool unsigned char
 #  define true 1
 #  define false 0
 #  define __bool_true_false_are_defined
@@ -240,6 +240,12 @@ extern "C" {
 # define UA_BINARY_OVERLAYABLE_INTEGER 0
 #endif
 
+/* Some platforms (e.g. QNX) have sizeof(bool) > 1. Disable overlayed integer
+ * encoding if that is the case. */
+#if UA_BINARY_OVERLAYABLE_INTEGER == 1
+UA_STATIC_ASSERT(sizeof(bool) == 1, cannot_overlay_integers_with_large_bool);
+#endif
+
 /**
  * Float Endianness
  * ^^^^^^^^^^^^^^^^

+ 6 - 2
include/ua_plugin_access_control.h

@@ -34,9 +34,13 @@ struct UA_AccessControl {
     size_t userTokenPoliciesSize;
     UA_UserTokenPolicy *userTokenPolicies;
     
-    /* Authenticate a session. The session context is attached to the session and
-     * later passed into the node-based access control callbacks. */
+    /* Authenticate a session. The session context is attached to the session
+     * and later passed into the node-based access control callbacks. The new
+     * session is rejected if a StatusCode other than UA_STATUSCODE_GOOD is
+     * returned. */
     UA_StatusCode (*activateSession)(UA_Server *server, UA_AccessControl *ac,
+                                     const UA_EndpointDescription *endpointDescription,
+                                     const UA_ByteString *secureChannelRemoteCertificate,
                                      const UA_NodeId *sessionId,
                                      const UA_ExtensionObject *userIdentityToken,
                                      void **sessionContext);

+ 4 - 11
include/ua_plugin_log.h

@@ -16,6 +16,10 @@ extern "C" {
 #include <stdarg.h>
 #include "ua_config.h"
 
+#include "ua_types.h"
+#include "ua_types_generated_handling.h"
+#include "base64.h"
+
 /**
  * Logging Plugin API
  * ==================
@@ -107,17 +111,6 @@ UA_LOG_FATAL(UA_Logger logger, UA_LogCategory category, const char *msg, ...) {
 #endif
 }
 
-/**
- * Convenience macros for complex types
- * ------------------------------------ */
-#define UA_PRINTF_GUID_FORMAT "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x"
-#define UA_PRINTF_GUID_DATA(GUID) (GUID).data1, (GUID).data2, (GUID).data3, \
-        (GUID).data4[0], (GUID).data4[1], (GUID).data4[2], (GUID).data4[3], \
-        (GUID).data4[4], (GUID).data4[5], (GUID).data4[6], (GUID).data4[7]
-
-#define UA_PRINTF_STRING_FORMAT "\"%.*s\""
-#define UA_PRINTF_STRING_DATA(STRING) (int)(STRING).length, (STRING).data
-
 
 #ifdef __cplusplus
 } // extern "C"

+ 0 - 23
include/ua_plugin_network.h

@@ -201,29 +201,6 @@ typedef UA_Connection
 (*UA_ConnectClientConnection)(UA_ConnectionConfig localConf, const char *endpointUrl,
                               const UA_UInt32 timeout, UA_Logger logger);
 
-/**
- * Endpoint URL Parser
- * -------------------
- * The endpoint URL parser is generally useful for the implementation of network
- * layer plugins. */
-
-/* Split the given endpoint url into hostname, port and path. All arguments must
- * be non-NULL. EndpointUrls have the form "opc.tcp://hostname:port/path", port
- * and path may be omitted (together with the prefix colon and slash).
- *
- * @param endpointUrl The endpoint URL.
- * @param outHostname Set to the parsed hostname. The string points into the
- *        original endpointUrl, so no memory is allocated. If an IPv6 address is
- *        given, hostname contains e.g. '[2001:0db8:85a3::8a2e:0370:7334]'
- * @param outPort Set to the port of the url or left unchanged.
- * @param outPath Set to the path if one is present in the endpointUrl.
- *        Starting or trailing '/' are NOT included in the path. The string
- *        points into the original endpointUrl, so no memory is allocated.
- * @return Returns UA_STATUSCODE_BADTCPENDPOINTURLINVALID if parsing failed. */
-UA_StatusCode UA_EXPORT
-UA_parseEndpointUrl(const UA_String *endpointUrl, UA_String *outHostname,
-                    UA_UInt16 *outPort, UA_String *outPath);
-
 #ifdef __cplusplus
 } // extern "C"
 #endif

+ 84 - 0
include/ua_util.h

@@ -0,0 +1,84 @@
+/* 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_HELPER_H_
+#define UA_HELPER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ua_config.h"
+#include "ua_types.h"
+
+/**
+* Endpoint URL Parser
+* -------------------
+* The endpoint URL parser is generally useful for the implementation of network
+* layer plugins. */
+
+/* Split the given endpoint url into hostname, port and path. All arguments must
+ * be non-NULL. EndpointUrls have the form "opc.tcp://hostname:port/path", port
+ * and path may be omitted (together with the prefix colon and slash).
+ *
+ * @param endpointUrl The endpoint URL.
+ * @param outHostname Set to the parsed hostname. The string points into the
+ *        original endpointUrl, so no memory is allocated. If an IPv6 address is
+ *        given, hostname contains e.g. '[2001:0db8:85a3::8a2e:0370:7334]'
+ * @param outPort Set to the port of the url or left unchanged.
+ * @param outPath Set to the path if one is present in the endpointUrl.
+ *        Starting or trailing '/' are NOT included in the path. The string
+ *        points into the original endpointUrl, so no memory is allocated.
+ * @return Returns UA_STATUSCODE_BADTCPENDPOINTURLINVALID if parsing failed. */
+UA_StatusCode UA_EXPORT
+UA_parseEndpointUrl(const UA_String *endpointUrl, UA_String *outHostname,
+                    UA_UInt16 *outPort, UA_String *outPath);
+
+/**
+ * Convenience macros for complex types
+ * ------------------------------------ */
+#define UA_PRINTF_GUID_FORMAT "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x"
+#define UA_PRINTF_GUID_DATA(GUID) (GUID).data1, (GUID).data2, (GUID).data3, \
+        (GUID).data4[0], (GUID).data4[1], (GUID).data4[2], (GUID).data4[3], \
+        (GUID).data4[4], (GUID).data4[5], (GUID).data4[6], (GUID).data4[7]
+
+#define UA_PRINTF_STRING_FORMAT "\"%.*s\""
+#define UA_PRINTF_STRING_DATA(STRING) (int)(STRING).length, (STRING).data
+
+/**
+ * Helper functions for converting data types
+ * ------------------------------------ */
+/*
+ * Converts a bytestring to the corresponding base64 encoded string representation.
+ *
+ * @param byteString the original byte string
+ * @param str the resulting base64 encoded byte string
+ *
+ * @return UA_STATUSCODE_GOOD on success.
+ */
+UA_StatusCode UA_EXPORT
+UA_ByteString_toBase64String(const UA_ByteString *byteString, UA_String *str);
+
+/*
+ * Converts a node id to the corresponding string representation.
+ * It can be one of:
+ * - Numeric: ns=0;i=123
+ * - String: ns=0;s=Some String
+ * - Guid: ns=0;g=A123456C-0ABC-1A2B-815F-687212AAEE1B
+ * - ByteString: ns=0;b=AA==
+ *
+ */
+UA_StatusCode UA_EXPORT
+UA_NodeId_toString(const UA_NodeId *nodeId, UA_String *nodeIdStr);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UA_HELPER_H_ */

+ 2 - 0
plugins/ua_accesscontrol_default.c

@@ -27,6 +27,8 @@ const UA_String username_policy = UA_STRING_STATIC(USERNAME_POLICY);
 
 static UA_StatusCode
 activateSession_default(UA_Server *server, UA_AccessControl *ac,
+                        const UA_EndpointDescription *endpointDescription,
+                        const UA_ByteString *secureChannelRemoteCertificate,
                         const UA_NodeId *sessionId,
                         const UA_ExtensionObject *userIdentityToken,
                         void **sessionContext) {

+ 1 - 1
plugins/ua_debug_dump_pkgs.c

@@ -5,7 +5,7 @@
  *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
  */
 
-#include "ua_util.h"
+#include "ua_util_internal.h"
 
 #include <ctype.h>
 #include <stdio.h>

+ 1 - 0
plugins/ua_network_pubsub_udp.c

@@ -8,6 +8,7 @@
 
 #include "ua_plugin_network.h"
 #include "ua_network_pubsub_udp.h"
+#include "ua_util.h"
 #include "ua_log_stdout.h"
 
 //UDP multicast network layer specific internal data

+ 1 - 1
plugins/ua_nodestore_default.c

@@ -8,7 +8,7 @@
 
 #include "ua_nodestore_default.h"
 
-#include "../src/ua_util.h" /* TOOO: Move atomic operations to arch definitions */
+#include "../src/ua_util_internal.h" /* TOOO: Move atomic operations to arch definitions */
 
 /* container_of */
 #define container_of(ptr, type, member) \

+ 21 - 22
src/server/ua_securechannel_manager.c

@@ -20,7 +20,7 @@
 
 UA_StatusCode
 UA_SecureChannelManager_init(UA_SecureChannelManager *cm, UA_Server *server) {
-    LIST_INIT(&cm->channels);
+    TAILQ_INIT(&cm->channels);
     // TODO: use an ID that is likely to be unique after a restart
     cm->lastChannelId = STARTCHANNELID;
     cm->lastTokenId = STARTTOKENID;
@@ -31,9 +31,9 @@ UA_SecureChannelManager_init(UA_SecureChannelManager *cm, UA_Server *server) {
 
 void
 UA_SecureChannelManager_deleteMembers(UA_SecureChannelManager *cm) {
-    channel_list_entry *entry, *temp;
-    LIST_FOREACH_SAFE(entry, &cm->channels, pointers, temp) {
-        LIST_REMOVE(entry, pointers);
+    channel_entry *entry, *temp;
+    TAILQ_FOREACH_SAFE(entry, &cm->channels, pointers, temp) {
+        TAILQ_REMOVE(&cm->channels, entry, pointers);
         UA_SecureChannel_deleteMembersCleanup(&entry->channel);
         UA_free(entry);
     }
@@ -41,13 +41,13 @@ UA_SecureChannelManager_deleteMembers(UA_SecureChannelManager *cm) {
 
 static void
 removeSecureChannelCallback(UA_Server *server, void *entry) {
-    channel_list_entry *centry = (channel_list_entry *)entry;
+    channel_entry *centry = (channel_entry *)entry;
     UA_SecureChannel_deleteMembersCleanup(&centry->channel);
     UA_free(entry);
 }
 
 static UA_StatusCode
-removeSecureChannel(UA_SecureChannelManager *cm, channel_list_entry *entry) {
+removeSecureChannel(UA_SecureChannelManager *cm, channel_entry *entry) {
     /* Add a delayed callback to remove the channel when the currently
      * scheduled jobs have completed */
     UA_StatusCode retval = UA_Server_delayedCallback(cm->server, removeSecureChannelCallback, entry);
@@ -59,7 +59,7 @@ removeSecureChannel(UA_SecureChannelManager *cm, channel_list_entry *entry) {
     }
 
     /* Detach the channel and make the capacity available */
-    LIST_REMOVE(entry, pointers);
+    TAILQ_REMOVE(&cm->channels, entry, pointers);
     UA_atomic_subUInt32(&cm->currentChannelCount, 1);
     return UA_STATUSCODE_GOOD;
 }
@@ -67,8 +67,8 @@ removeSecureChannel(UA_SecureChannelManager *cm, channel_list_entry *entry) {
 /* remove channels that were not renewed or who have no connection attached */
 void
 UA_SecureChannelManager_cleanupTimedOut(UA_SecureChannelManager *cm, UA_DateTime nowMonotonic) {
-    channel_list_entry *entry, *temp;
-    LIST_FOREACH_SAFE(entry, &cm->channels, pointers, temp) {
+    channel_entry *entry, *temp;
+    TAILQ_FOREACH_SAFE(entry, &cm->channels, pointers, temp) {
         UA_DateTime timeout = entry->channel.securityToken.createdAt +
                               (UA_DateTime)(entry->channel.securityToken.revisedLifetime * UA_DATETIME_MSEC);
         if(timeout < nowMonotonic || !entry->channel.connection) {
@@ -84,14 +84,13 @@ UA_SecureChannelManager_cleanupTimedOut(UA_SecureChannelManager *cm, UA_DateTime
 /* remove the first channel that has no session attached */
 static UA_Boolean
 purgeFirstChannelWithoutSession(UA_SecureChannelManager *cm) {
-    channel_list_entry *entry;
-    LIST_FOREACH(entry, &cm->channels, pointers) {
-        if(LIST_EMPTY(&(entry->channel.sessions))) {
-            UA_LOG_DEBUG_CHANNEL(cm->server->config.logger, &entry->channel,
-                                 "Channel was purged since maxSecureChannels was "
-                                 "reached and channel had no session attached");
+    channel_entry *entry;
+    TAILQ_FOREACH(entry, &cm->channels, pointers) {
+        if(LIST_EMPTY(&entry->channel.sessions)) {
+            UA_LOG_INFO_CHANNEL(cm->server->config.logger, &entry->channel,
+                                "Channel was purged since maxSecureChannels was "
+                                "reached and channel had no session attached");
             removeSecureChannel(cm, entry);
-            UA_assert(entry != LIST_FIRST(&cm->channels));
             return true;
         }
     }
@@ -116,7 +115,7 @@ UA_SecureChannelManager_create(UA_SecureChannelManager *const cm, UA_Connection
     UA_LOG_INFO(cm->server->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
                 "Creating a new SecureChannel");
 
-    channel_list_entry *entry = (channel_list_entry *)UA_malloc(sizeof(channel_list_entry));
+    channel_entry *entry = (channel_entry *)UA_malloc(sizeof(channel_entry));
     if(!entry)
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
@@ -135,7 +134,7 @@ UA_SecureChannelManager_create(UA_SecureChannelManager *const cm, UA_Connection
     entry->channel.securityToken.createdAt = UA_DateTime_now();
     entry->channel.securityToken.revisedLifetime = cm->server->config.maxSecurityTokenLifetime;
 
-    LIST_INSERT_HEAD(&cm->channels, entry, pointers);
+    TAILQ_INSERT_TAIL(&cm->channels, entry, pointers);
     UA_atomic_addUInt32(&cm->currentChannelCount, 1);
     UA_Connection_attachSecureChannel(connection, &entry->channel);
     return UA_STATUSCODE_GOOD;
@@ -232,8 +231,8 @@ UA_SecureChannelManager_renew(UA_SecureChannelManager *cm, UA_SecureChannel *cha
 
 UA_SecureChannel *
 UA_SecureChannelManager_get(UA_SecureChannelManager *cm, UA_UInt32 channelId) {
-    channel_list_entry *entry;
-    LIST_FOREACH(entry, &cm->channels, pointers) {
+    channel_entry *entry;
+    TAILQ_FOREACH(entry, &cm->channels, pointers) {
         if(entry->channel.securityToken.channelId == channelId)
             return &entry->channel;
     }
@@ -242,8 +241,8 @@ UA_SecureChannelManager_get(UA_SecureChannelManager *cm, UA_UInt32 channelId) {
 
 UA_StatusCode
 UA_SecureChannelManager_close(UA_SecureChannelManager *cm, UA_UInt32 channelId) {
-    channel_list_entry *entry;
-    LIST_FOREACH(entry, &cm->channels, pointers) {
+    channel_entry *entry;
+    TAILQ_FOREACH(entry, &cm->channels, pointers) {
         if(entry->channel.securityToken.channelId == channelId)
             break;
     }

+ 6 - 6
src/server/ua_securechannel_manager.h

@@ -15,18 +15,18 @@
 extern "C" {
 #endif
 
-#include "ua_util.h"
+#include "ua_util_internal.h"
 #include "ua_server.h"
 #include "ua_securechannel.h"
 #include "../../deps/queue.h"
 
-typedef struct channel_list_entry {
+typedef struct channel_entry {
     UA_SecureChannel channel;
-    LIST_ENTRY(channel_list_entry) pointers;
-} channel_list_entry;
+    TAILQ_ENTRY(channel_entry) pointers;
+} channel_entry;
 
-typedef struct UA_SecureChannelManager {
-    LIST_HEAD(channel_list, channel_list_entry) channels; // doubly-linked list of channels
+typedef struct {
+    TAILQ_HEAD(, channel_entry) channels; // doubly-linked list of channels
     UA_UInt32 currentChannelCount;
     UA_UInt32 lastChannelId;
     UA_UInt32 lastTokenId;

+ 1 - 1
src/server/ua_server_internal.h

@@ -18,7 +18,7 @@
 extern "C" {
 #endif
 
-#include "ua_util.h"
+#include "ua_util_internal.h"
 #include "ua_server.h"
 #include "ua_server_config.h"
 #include "ua_timer.h"

+ 50 - 1
src/server/ua_services_session.c

@@ -242,10 +242,59 @@ Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
         return;
     }
 
+    /* Find the matching endpoint */
+    const UA_EndpointDescription *ed = NULL;
+    for(size_t i = 0; ed == NULL && i < server->config.endpointsSize; ++i) {
+        const UA_Endpoint *e = &server->config.endpoints[i];
+
+        /* Match the Security Mode */
+        if(e->endpointDescription.securityMode != channel->securityMode)
+            continue;
+
+        /* Match the SecurityPolicy */
+        if(!UA_String_equal(&e->securityPolicy.policyUri,
+                            &channel->securityPolicy->policyUri))
+            continue;
+
+        /* Match the UserTokenType */
+        for(size_t j = 0; j < e->endpointDescription.userIdentityTokensSize; j++) {
+            const UA_UserTokenPolicy *u = &e->endpointDescription.userIdentityTokens[j];
+            if(u->tokenType == UA_USERTOKENTYPE_ANONYMOUS) {
+                if(request->userIdentityToken.content.decoded.type != &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN])
+                    continue;
+            } else if(u->tokenType == UA_USERTOKENTYPE_USERNAME) {
+                if(request->userIdentityToken.content.decoded.type != &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN])
+                    continue;
+            } else if(u->tokenType == UA_USERTOKENTYPE_CERTIFICATE) {
+                if(request->userIdentityToken.content.decoded.type != &UA_TYPES[UA_TYPES_X509IDENTITYTOKEN])
+                    continue;
+            } else if(u->tokenType == UA_USERTOKENTYPE_ISSUEDTOKEN) {
+                if(request->userIdentityToken.content.decoded.type != &UA_TYPES[UA_TYPES_ISSUEDIDENTITYTOKEN])
+                    continue;
+            } else {
+                response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+                return;
+            }
+
+            /* Match found */
+            ed = &e->endpointDescription;
+            break;
+        }
+
+    }
+
+    /* No matching endpoint found */
+    if(!ed) {
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENREJECTED;
+        return;
+    }
+
     /* Callback into userland access control */
     response->responseHeader.serviceResult =
         server->config.accessControl.activateSession(server, &server->config.accessControl,
-                                                     &session->sessionId, &request->userIdentityToken,
+                                                     ed, &channel->remoteCertificate,
+                                                     &session->sessionId,
+                                                     &request->userIdentityToken,
                                                      &session->sessionHandle);
     if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
         UA_LOG_INFO_SESSION(server->config.logger, session,

+ 1 - 1
src/server/ua_session.h

@@ -13,7 +13,7 @@ extern "C" {
 #endif
 
 #include "ua_securechannel.h"
-#include "../deps/queue.h"
+#include "ua_util.h"
 
 #define UA_MAXCONTINUATIONPOINTS 5
 

+ 1 - 1
src/server/ua_session_manager.h

@@ -17,7 +17,7 @@ extern "C" {
 #endif
 
 #include "ua_server.h"
-#include "ua_util.h"
+#include "ua_util_internal.h"
 #include "ua_session.h"
 #include "../../deps/queue.h"
 

+ 1 - 1
src/server/ua_subscription.h

@@ -14,7 +14,7 @@
 #ifndef UA_SUBSCRIPTION_H_
 #define UA_SUBSCRIPTION_H_
 
-#include "ua_util.h"
+#include "ua_util_internal.h"
 #include "ua_types.h"
 #include "ua_types_generated.h"
 #include "ua_session.h"

+ 1 - 1
src/ua_connection.c

@@ -10,7 +10,7 @@
  *    Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB
  */
 
-#include "ua_util.h"
+#include "ua_util_internal.h"
 #include "ua_connection_internal.h"
 #include "ua_types_encoding_binary.h"
 #include "ua_types_generated_encoding_binary.h"

+ 1 - 1
src/ua_securechannel.c

@@ -11,7 +11,7 @@
  *    Copyright 2017-2018 (c) Mark Giraud, Fraunhofer IOSB
  */
 
-#include "ua_util.h"
+#include "ua_util_internal.h"
 #include "ua_securechannel.h"
 #include "ua_types_encoding_binary.h"
 #include "ua_types_generated_encoding_binary.h"

+ 18 - 7
src/ua_timer.c

@@ -6,7 +6,7 @@
  *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
  */
 
-#include "ua_util.h"
+#include "ua_util_internal.h"
 #include "ua_timer.h"
 
 /* Only one thread operates on the repeated jobs. This is usually the "main"
@@ -128,16 +128,25 @@ addTimerCallbackEntry(UA_Timer *t, UA_TimerCallbackEntry * UA_RESTRICT tc) {
     SLIST_FOREACH(tmpTc, &t->repeatedCallbacks, next) {
         if(tmpTc->nextTime >= tc->nextTime)
             break;
-        afterTc = tmpTc;
 
         /* The goal is to have many repeated callbacks with the same repetition
          * interval in a "block" in order to reduce linear search for re-entry
          * to the sorted list after processing. Allow the first execution to lie
          * between "nextTime - 1s" and "nextTime" if this adjustment groups
-         * callbacks with the same repetition interval. */
+         * callbacks with the same repetition interval.
+         * Callbacks of a block are added in reversed order. This design allows
+         * the monitored items of a subscription (if created in a sequence with the
+         * same publish/sample interval) to be executed before the subscription
+         * publish the notifications */
         if(tmpTc->interval == tc->interval &&
-           tmpTc->nextTime > (tc->nextTime - UA_DATETIME_SEC))
+           tmpTc->nextTime > (tc->nextTime - UA_DATETIME_SEC)) {
             tc->nextTime = tmpTc->nextTime;
+            break;
+        }
+
+        /* tc is neither in the same interval nor supposed to be executed sooner
+         * than tmpTc. Update afterTc to push tc further back in the timer list. */
+        afterTc = tmpTc;
     }
 
     /* Add the repeated callback */
@@ -324,11 +333,13 @@ UA_Timer_process(UA_Timer *t, UA_DateTime nowMonotonic,
                     break;
                 prev_tc = n;
             }
-
-            /* Update last_dispatched */
-            last_dispatched = tc;
         }
 
+        /* Update last_dispatched to make sure batched callbacks are added in the
+         * same sequence as before they were executed and to save some iterations
+         * of the linear search for callbacks to be added further back in the list. */
+        last_dispatched = tc;
+
         /* Add entry to the new position in the sorted list */
         SLIST_INSERT_AFTER(prev_tc, tc, next);
     }

+ 9 - 2
src/ua_timer.h

@@ -13,12 +13,19 @@
 extern "C" {
 #endif
 
-#include "ua_util.h"
+#include "ua_util_internal.h"
 
 /* An (event) timer triggers callbacks with a recurring interval. Adding,
  * removing and changing repeated callbacks can be done from independent
  * threads. Processing the changes and dispatching callbacks must be done by a
- * single "mainloop" process. */
+ * single "mainloop" process.
+ * Timer callbacks with the same recurring interval are batched into blocks in
+ * order to reduce linear search for re-entry to the sorted list after processing.
+ * Callbacks are inserted in reversed order (last callback are put first in the block)
+ * to allow the monitored items of a subscription (if created in a sequence with the
+ * same publish/sample interval) to be executed before the subscription publish the
+ * notifications. When callbacks are entered to the timer list after execution they
+ * are added in the same order as before execution. */
 
 /* Forward declaration */
 struct UA_TimerCallbackEntry;

+ 1 - 1
src/ua_types.c

@@ -14,7 +14,7 @@
  *    Copyright 2016 (c) Lorenz Haas
  */
 
-#include "ua_util.h"
+#include "ua_util_internal.h"
 #include "ua_types.h"
 #include "ua_types_generated.h"
 #include "ua_types_generated_handling.h"

+ 22 - 20
src/ua_types_encoding_binary.c

@@ -15,7 +15,7 @@
  *    Copyright 2017 (c) Henrik Norrman
  */
 
-#include "ua_util.h"
+#include "ua_util_internal.h"
 #include "ua_types_encoding_binary.h"
 #include "ua_types_generated.h"
 #include "ua_types_generated_handling.h"
@@ -182,8 +182,10 @@ UA_decode64(const u8 buf[8], u64 *v) {
 #endif /* !UA_BINARY_OVERLAYABLE_INTEGER */
 
 /* Boolean */
+/* Note that sizeof(bool) != 1 on some platforms. Overlayable integer encoding
+ * is disabled in those cases. */
 ENCODE_BINARY(Boolean) {
-    if(ctx->pos + sizeof(UA_Boolean) > ctx->end)
+    if(ctx->pos + 1 > ctx->end)
         return UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED;
     *ctx->pos = *(const u8*)src;
     ++ctx->pos;
@@ -191,7 +193,7 @@ ENCODE_BINARY(Boolean) {
 }
 
 DECODE_BINARY(Boolean) {
-    if(ctx->pos + sizeof(UA_Boolean) > ctx->end)
+    if(ctx->pos + 1 > ctx->end)
         return UA_STATUSCODE_BADDECODINGERROR;
     *dst = (*ctx->pos > 0) ? true : false;
     ++ctx->pos;
@@ -1550,10 +1552,10 @@ Array_calcSizeBinary(const void *src, size_t length, const UA_DataType *type) {
     return s;
 }
 
-static size_t
-calcSizeBinaryMemSize(const void *UA_RESTRICT p, const UA_DataType *type) {
-    return type->memSize;
-}
+static size_t calcSizeBinary1(const void *_, const UA_DataType *__) { return 1; }
+static size_t calcSizeBinary2(const void *_, const UA_DataType *__) { return 2; }
+static size_t calcSizeBinary4(const void *_, const UA_DataType *__) { return 4; }
+static size_t calcSizeBinary8(const void *_, const UA_DataType *__) { return 8; }
 
 CALCSIZE_BINARY(String) {
     return 4 + src->length;
@@ -1712,25 +1714,25 @@ CALCSIZE_BINARY(DiagnosticInfo) {
 }
 
 const calcSizeBinarySignature calcSizeBinaryJumpTable[UA_BUILTIN_TYPES_COUNT + 1] = {
-    (calcSizeBinarySignature)calcSizeBinaryMemSize, /* Boolean */
-    (calcSizeBinarySignature)calcSizeBinaryMemSize, /* Byte */
-    (calcSizeBinarySignature)calcSizeBinaryMemSize,
-    (calcSizeBinarySignature)calcSizeBinaryMemSize, /* Int16 */
-    (calcSizeBinarySignature)calcSizeBinaryMemSize,
-    (calcSizeBinarySignature)calcSizeBinaryMemSize, /* Int32 */
-    (calcSizeBinarySignature)calcSizeBinaryMemSize,
-    (calcSizeBinarySignature)calcSizeBinaryMemSize, /* Int64 */
-    (calcSizeBinarySignature)calcSizeBinaryMemSize,
-    (calcSizeBinarySignature)calcSizeBinaryMemSize, /* Float */
-    (calcSizeBinarySignature)calcSizeBinaryMemSize, /* Double */
+    (calcSizeBinarySignature)calcSizeBinary1, /* Boolean */
+    (calcSizeBinarySignature)calcSizeBinary1, /* SByte */
+    (calcSizeBinarySignature)calcSizeBinary1, /* Byte */
+    (calcSizeBinarySignature)calcSizeBinary2, /* Int16 */
+    (calcSizeBinarySignature)calcSizeBinary2, /* UInt16 */
+    (calcSizeBinarySignature)calcSizeBinary4, /* Int32 */
+    (calcSizeBinarySignature)calcSizeBinary4, /* UInt32 */
+    (calcSizeBinarySignature)calcSizeBinary8, /* Int64 */
+    (calcSizeBinarySignature)calcSizeBinary8, /* UInt64 */
+    (calcSizeBinarySignature)calcSizeBinary4, /* Float */
+    (calcSizeBinarySignature)calcSizeBinary8, /* Double */
     (calcSizeBinarySignature)String_calcSizeBinary,
-    (calcSizeBinarySignature)calcSizeBinaryMemSize, /* DateTime */
+    (calcSizeBinarySignature)calcSizeBinary8, /* DateTime */
     (calcSizeBinarySignature)Guid_calcSizeBinary,
     (calcSizeBinarySignature)String_calcSizeBinary, /* ByteString */
     (calcSizeBinarySignature)String_calcSizeBinary, /* XmlElement */
     (calcSizeBinarySignature)NodeId_calcSizeBinary,
     (calcSizeBinarySignature)ExpandedNodeId_calcSizeBinary,
-    (calcSizeBinarySignature)calcSizeBinaryMemSize, /* StatusCode */
+    (calcSizeBinarySignature)calcSizeBinary4, /* StatusCode */
     (calcSizeBinarySignature)QualifiedName_calcSizeBinary,
     (calcSizeBinarySignature)LocalizedText_calcSizeBinary,
     (calcSizeBinarySignature)ExtensionObject_calcSizeBinary,

+ 100 - 0
src/ua_util.c

@@ -8,7 +8,9 @@
  */
 
 #include "ua_util.h"
+#include "ua_util_internal.h"
 #include "ua_plugin_network.h"
+#include "base64.h"
 
 size_t
 UA_readNumber(u8 *buf, size_t buflen, u32 *number) {
@@ -100,3 +102,101 @@ UA_parseEndpointUrl(const UA_String *endpointUrl, UA_String *outHostname,
 
     return UA_STATUSCODE_GOOD;
 }
+
+UA_StatusCode UA_ByteString_toBase64String(const UA_ByteString *byteString, UA_String *str) {
+    if (str->length != 0) {
+        UA_free(str->data);
+        str->data = NULL;
+        str->length = 0;
+    }
+    if (byteString == NULL || byteString->data == NULL)
+        return UA_STATUSCODE_GOOD;
+    if (byteString == str)
+        return UA_STATUSCODE_BADINVALIDARGUMENT;
+
+    int resSize = 0;
+    str->data = (UA_Byte*)UA_base64(byteString->data, (int)byteString->length, &resSize);
+    str->length = (size_t) resSize;
+    if (str->data == NULL)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+
+    return UA_STATUSCODE_GOOD;
+}
+
+UA_StatusCode
+UA_NodeId_toString(const UA_NodeId *nodeId, UA_String *nodeIdStr) {
+    if (nodeIdStr->length != 0) {
+        UA_free(nodeIdStr->data);
+        nodeIdStr->data = NULL;
+        nodeIdStr->length = 0;
+    }
+    if (nodeId == NULL)
+        return UA_STATUSCODE_GOOD;
+
+    char *nsStr = NULL;
+    size_t nsLen = 0;
+    if (nodeId->namespaceIndex != 0) {
+        nsStr = (char*)UA_malloc(9+1); // strlen("ns=XXXXX;") = 9 + Nullbyte
+        UA_snprintf(nsStr, 10, "ns=%d;", nodeId->namespaceIndex);
+        nsLen = strlen(nsStr);
+    }
+
+
+    UA_ByteString byteStr = UA_BYTESTRING_NULL;
+    switch (nodeId->identifierType) {
+        case UA_NODEIDTYPE_NUMERIC:
+            /* ns (2 byte, 65535) = 5 chars, numeric (4 byte, 4294967295) = 10 chars, delim = 1 , nullbyte = 1-> 17 chars */
+            nodeIdStr->length = nsLen + 2 + 10 + 1;
+            nodeIdStr->data = (UA_Byte*)UA_malloc(nodeIdStr->length);
+            if (nodeIdStr->data == NULL) {
+                UA_free(nsStr);
+                return UA_STATUSCODE_BADOUTOFMEMORY;
+            }
+            UA_snprintf((char*)nodeIdStr->data, nodeIdStr->length, "%si=%lu",
+                        nsLen > 0 ? nsStr : "",
+                        (unsigned long )nodeId->identifier.numeric);
+            break;
+        case UA_NODEIDTYPE_STRING:
+            /* ns (16bit) = 5 chars, strlen + nullbyte */
+            nodeIdStr->length = nsLen + 2 + nodeId->identifier.string.length + 1;
+            nodeIdStr->data = (UA_Byte*)UA_malloc(nodeIdStr->length);
+            if (nodeIdStr->data == NULL) {
+                UA_free(nsStr);
+                return UA_STATUSCODE_BADOUTOFMEMORY;
+            }
+            UA_snprintf((char*)nodeIdStr->data, nodeIdStr->length, "%ss=%.*s",
+                        nsLen > 0 ? nsStr : "",
+                        (int)nodeId->identifier.string.length, nodeId->identifier.string.data);
+            break;
+        case UA_NODEIDTYPE_GUID:
+            /* ns (16bit) = 5 chars + strlen(A123456C-0ABC-1A2B-815F-687212AAEE1B)=36 + nullbyte */
+            nodeIdStr->length = nsLen + 2 + 36 + 1;
+            nodeIdStr->data = (UA_Byte*)UA_malloc(nodeIdStr->length);
+            if (nodeIdStr->data == NULL) {
+                UA_free(nsStr);
+                return UA_STATUSCODE_BADOUTOFMEMORY;
+            }
+            UA_snprintf((char*)nodeIdStr->data, nodeIdStr->length, "%sg=" UA_PRINTF_GUID_FORMAT,
+                        nsLen > 0 ? nsStr : "",
+                        UA_PRINTF_GUID_DATA(nodeId->identifier.guid));
+            break;
+        case UA_NODEIDTYPE_BYTESTRING:
+            UA_ByteString_toBase64String(&nodeId->identifier.byteString, &byteStr);
+            /* ns (16bit) = 5 chars + LEN + nullbyte */
+            nodeIdStr->length = nsLen + 2 + byteStr.length + 1;
+            nodeIdStr->data = (UA_Byte*)UA_malloc(nodeIdStr->length);
+            if (nodeIdStr->data == NULL) {
+                UA_String_deleteMembers(&byteStr);
+                UA_free(nsStr);
+                return UA_STATUSCODE_BADOUTOFMEMORY;
+            }
+            UA_snprintf((char*)nodeIdStr->data, nodeIdStr->length, "%sb=%.*s",
+                        nsLen > 0 ? nsStr : "",
+                        (int)byteStr.length, byteStr.data);
+            UA_String_deleteMembers(&byteStr);
+            break;
+    }
+    UA_free(nsStr);
+    return UA_STATUSCODE_GOOD;
+}
+

src/ua_util.h → src/ua_util_internal.h


+ 153 - 0
tests/check_utils.c

@@ -7,6 +7,7 @@
 #include "ua_types.h"
 #include "ua_client.h"
 #include "ua_util.h"
+#include "ua_util_internal.h"
 #include "check.h"
 
 START_TEST(EndpointUrl_split) {
@@ -155,6 +156,149 @@ START_TEST(StatusCode_msg) {
 }
 END_TEST
 
+
+static void assertNodeIdString(const UA_String *gotStr, const char* expectedStr) {
+    size_t expectedStringLength = strlen(expectedStr);
+    ck_assert_uint_ge(gotStr->length, expectedStringLength);
+    char *gotChars = (char*)UA_malloc(gotStr->length+1);
+    memcpy(gotChars, gotStr->data, gotStr->length);
+    gotChars[gotStr->length] = 0;
+    ck_assert_str_eq(gotChars, expectedStr);
+    UA_free(gotChars);
+}
+
+START_TEST(idToStringNumeric) {
+    UA_NodeId n;
+    UA_String str = UA_STRING_NULL;
+
+    n = UA_NODEID_NUMERIC(0,0);
+    UA_NodeId_toString(&n, &str);
+    assertNodeIdString(&str, "i=0");
+
+    n = UA_NODEID_NUMERIC(12345,1234567890);
+    UA_NodeId_toString(&n, &str);
+    assertNodeIdString(&str, "ns=12345;i=1234567890");
+
+    n = UA_NODEID_NUMERIC(0xFFFF,0xFFFFFFFF);
+    UA_NodeId_toString(&n, &str);
+    assertNodeIdString(&str, "ns=65535;i=4294967295");
+
+    UA_String_deleteMembers(&str);
+} END_TEST
+
+START_TEST(idToStringString) {
+    UA_NodeId n;
+    UA_String str = UA_STRING_NULL;
+
+    n = UA_NODEID_STRING(0,"");
+    UA_NodeId_toString(&n, &str);
+    assertNodeIdString(&str, "s=");
+
+    n = UA_NODEID_STRING(54321,"Some String");
+    UA_NodeId_toString(&n, &str);
+    assertNodeIdString(&str, "ns=54321;s=Some String");
+
+    n = UA_NODEID_STRING(0,"Some String");
+    UA_NodeId_toString(&n, &str);
+    assertNodeIdString(&str, "s=Some String");
+
+    UA_String_deleteMembers(&str);
+} END_TEST
+
+START_TEST(idToStringGuid) {
+    UA_NodeId n;
+    UA_String str = UA_STRING_NULL;
+
+    UA_Guid g = UA_GUID_NULL;
+
+    n = UA_NODEID_GUID(0,UA_GUID_NULL);
+    UA_NodeId_toString(&n, &str);
+    assertNodeIdString(&str, "g=00000000-0000-0000-0000-000000000000");
+
+    g.data1 = 0xA123456C;
+    g.data2 = 0x0ABC;
+    g.data3 = 0x1A2B;
+    g.data4[0] = 0x81;
+    g.data4[1] = 0x5F;
+    g.data4[2] = 0x68;
+    g.data4[3] = 0x72;
+    g.data4[4] = 0x12;
+    g.data4[5] = 0xAA;
+    g.data4[6] = 0xEE;
+    g.data4[7] = 0x1B;
+
+    n = UA_NODEID_GUID(65535,g);
+    UA_NodeId_toString(&n, &str);
+    assertNodeIdString(&str, "ns=65535;g=a123456c-0abc-1a2b-815f-687212aaee1b");
+
+    g.data1 = 0xFFFFFFFF;
+    g.data2 = 0xFFFF;
+    g.data3 = 0xFFFF;
+    g.data4[0] = 0xFF;
+    g.data4[1] = 0xFF;
+    g.data4[2] = 0xFF;
+    g.data4[3] = 0xFF;
+    g.data4[4] = 0xFF;
+    g.data4[5] = 0xFF;
+    g.data4[6] = 0xFF;
+    g.data4[7] = 0xFF;
+
+    n = UA_NODEID_GUID(65535,g);
+    UA_NodeId_toString(&n, &str);
+    assertNodeIdString(&str, "ns=65535;g=ffffffff-ffff-ffff-ffff-ffffffffffff");
+
+    UA_String_deleteMembers(&str);
+} END_TEST
+
+START_TEST(idToStringByte) {
+    UA_NodeId n;
+    UA_String str = UA_STRING_NULL;
+
+    n.namespaceIndex = 0;
+    n.identifierType = UA_NODEIDTYPE_BYTESTRING;
+    n.identifier.byteString.data = NULL;
+    n.identifier.byteString.length = 0;
+    UA_NodeId_toString(&n, &str);
+    assertNodeIdString(&str, "b=");
+
+    UA_ByteString bs = UA_BYTESTRING_NULL;
+
+    bs.length = 1;
+    bs.data = (UA_Byte*)UA_malloc(bs.length);
+    bs.data[0] = 0x00;
+    n.identifier.byteString = bs;
+    n.namespaceIndex = 123;
+    UA_NodeId_toString(&n, &str);
+    assertNodeIdString(&str, "ns=123;b=AA==");
+    UA_free(bs.data);
+
+    bs.length = 1;
+    bs.data = (UA_Byte*)UA_malloc(bs.length);
+    bs.data[0] = 0x2C;
+    n.identifier.byteString = bs;
+    n.namespaceIndex = 123;
+    UA_NodeId_toString(&n, &str);
+    assertNodeIdString(&str, "ns=123;b=LA==");
+    UA_free(bs.data);
+
+    bs.length = 5;
+    bs.data = (UA_Byte*)UA_malloc(bs.length);
+    bs.data[0] = 0x21;
+    bs.data[1] = 0x83;
+    bs.data[2] = 0xE0;
+    bs.data[3] = 0x54;
+    bs.data[4] = 0x78;
+    n.identifier.byteString = bs;
+    n.namespaceIndex = 599;
+    UA_NodeId_toString(&n, &str);
+    assertNodeIdString(&str, "ns=599;b=IYPgVHg=");
+    UA_free(bs.data);
+
+    UA_String_deleteMembers(&str);
+} END_TEST
+
+
+
 static Suite* testSuite_Utils(void) {
     Suite *s = suite_create("Utils");
     TCase *tc_endpointUrl_split = tcase_create("EndpointUrl_split");
@@ -164,6 +308,15 @@ static Suite* testSuite_Utils(void) {
     tcase_add_test(tc_utils, readNumber);
     tcase_add_test(tc_utils, StatusCode_msg);
     suite_add_tcase(s,tc_utils);
+
+
+    TCase *tc1 = tcase_create("test nodeid string");
+    tcase_add_test(tc1, idToStringNumeric);
+    tcase_add_test(tc1, idToStringString);
+    tcase_add_test(tc1, idToStringGuid);
+    tcase_add_test(tc1, idToStringByte);
+    suite_add_tcase(s, tc1);
+
     return s;
 }
 

+ 2 - 0
tools/schema/datatypes_minimal.txt

@@ -45,6 +45,8 @@ ServiceFault
 UserIdentityToken
 UserNameIdentityToken
 AnonymousIdentityToken
+X509IdentityToken
+IssuedIdentityToken
 CreateSessionResponse
 CreateSessionRequest
 EndpointDescription