Browse Source

Merge branch 'master' of https://github.com/acplt/open62541

Added preliminary version of the python based namespace generator. Initializing variable nodes that contain complex contents (extensionobjects, statuscodes, etc.) is currently not supported. The cmake switch LOADGENERATEDNS will enable autogeneration of the namespace. The scripts logger is set to verbose/debug.
ichrispa 10 years ago
parent
commit
14b70cea88

+ 10 - 2
.travis.yml

@@ -15,7 +15,7 @@ addons:
     build_command: make
     branch_pattern: coverity_scan
 before_install:
-- sudo apt-get install binutils-mingw-w64-i686 gcc-mingw-w64-i686
+- sudo apt-get install binutils-mingw-w64-i686 gcc-mingw-w64-i686 mingw-w64
 - sudo add-apt-repository ppa:kalakris/cmake -y
 - sudo apt-get update -qq
 - sudo apt-get install -qq --no-install-recommends build-essential cmake python-lxml gcc-multilib graphviz doxygen wget zip
@@ -32,13 +32,20 @@ script:
 - echo "Testing builds"
 - mkdir -p build
 - cd build
-- echo "Cross compile release build for MinGW"
+- echo "Cross compile release build for MinGW 32 bit"
 - cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-mingw32.cmake -DENABLE_AMALGAMATION=ON -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLESERVER=ON -DBUILD_EXAMPLECLIENT=ON ..
 - make -j
 - cp ../README.md .
 - zip open62541-win32.zip README.md exampleServer.exe exampleClient.exe libopen62541.dll libopen62541.dll.a open62541.h open62541.c
 - cp open62541-win32.zip ..
 - cd .. && rm build -rf && mkdir -p build && cd build
+- echo "Cross compile release build for MinGW 64 bit"
+- cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-mingw64.cmake -DENABLE_AMALGAMATION=ON -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLESERVER=ON -DBUILD_EXAMPLECLIENT=ON ..
+- make -j
+- cp ../README.md .
+- zip open62541-win64.zip README.md exampleServer.exe exampleClient.exe libopen62541.dll libopen62541.dll.a open62541.h open62541.c
+- cp open62541-win64.zip ..
+- cd .. && rm build -rf && mkdir -p build && cd build
 - echo "Cross compile release build for 32-bit linux"
 - cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-gcc-m32.cmake -DENABLE_AMALGAMATION=ON -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLESERVER=ON -DBUILD_EXAMPLECLIENT=ON ..
 - make -j
@@ -95,6 +102,7 @@ deploy:
     secure: PKCoA7MBRtHloIbNF4Qj5LQyCafjekfVeFXlMBd9KmC0ynNuef+D7nn38f/jo25/b0Ii7r+hgslkQPezbKyEqo2zcCB8Pn8TWau2hbzKM/dUCPoN90HVaQcRjUi8P2Y+QkouwyPWSujBL35/X5QiAntRotCSbZx4fkyiN8cU95o=
   file: 
     - open62541-win32.zip
+    - open62541-win64.zip
     - open62541-linux32.tar.gz
     - open62541-linux64.tar.gz
     - open62541-raspberrypi.tar.gz

+ 1 - 1
README.md

@@ -46,7 +46,7 @@ int main(int argc, char** argv)
     signal(SIGINT, signalHandler);
 
     /* init the server */
-    UA_Server *server = UA_Server_new();
+    UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
     UA_Server_setLogger(server, Logger_Stdout_new());
     UA_Server_addNetworkLayer(server,
         ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, PORT));

+ 38 - 0
cmake/Toolchain-mingw64.cmake

@@ -0,0 +1,38 @@
+# the name of the target operating system
+set(CMAKE_SYSTEM_NAME Windows)
+
+#remove the runtime dependency for libgcc_s_sjlj-1.dll
+set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-libgcc")
+
+# Which compilers to use for C and C++, and location of target
+# environment.
+if(EXISTS /usr/x86_64-w64-mingw32)
+# First look in standard location as used by Debian/Ubuntu/etc.
+set(CMAKE_STRIP x86_64-w64-mingw32-strip)
+set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
+set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
+set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)
+set(CMAKE_AR:FILEPATH /usr/bin/x86_64-w64-mingw32-ar)
+set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32)
+elseif(EXISTS /usr/i586-mingw32msvc)
+# Else fill in local path which the user will likely adjust.
+set(CMAKE_STRIP /usr/local/cross-tools/bin/i386-mingw32-strip)
+set(CMAKE_C_COMPILER /usr/local/cross-tools/bin/i386-mingw32-gcc)
+set(CMAKE_CXX_COMPILER /usr/local/cross-tools/bin/i386-mingw32-g++)
+set(CMAKE_RC_COMPILER /usr/local/cross-tools/bin/i386-mingw32-windres)
+set(CMAKE_FIND_ROOT_PATH /usr/local/cross-tools)
+endif() 
+
+# Adjust the default behaviour of the FIND_XXX() commands:
+# search headers and libraries in the target environment, search
+# programs in the host environment
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+# Tell pkg-config not to look at the target environment's .pc files.
+# Setting PKG_CONFIG_LIBDIR sets the default search directory, but we have to
+# set PKG_CONFIG_PATH as well to prevent pkg-config falling back to the host's
+# path.
+set(ENV{PKG_CONFIG_LIBDIR} ${CMAKE_FIND_ROOT_PATH}/lib/pkgconfig)
+set(ENV{PKG_CONFIG_PATH} ${CMAKE_FIND_ROOT_PATH}/lib/pkgconfig)
+set(ENV{MINGDIR} ${CMAKE_FIND_ROOT_PATH}) 

+ 0 - 1
examples/client.c

@@ -9,7 +9,6 @@
 #endif
 
 #include <stdio.h>
-#include "networklayer_tcp.h"
 
 int main(int argc, char *argv[]) {
     UA_Client *client = UA_Client_new(UA_ClientConfig_standard, Logger_Stdout_new());

+ 32 - 43
examples/networklayer_tcp.c

@@ -38,62 +38,52 @@
 /* Generic Socket Functions */
 /****************************/
 
-static UA_StatusCode socket_write(UA_Connection *connection, UA_ByteStringArray gather_buf) {
-    UA_UInt32 total_len = 0, nWritten = 0;
-#ifdef _WIN32
-    LPWSABUF buf = _alloca(gather_buf.stringsSize * sizeof(WSABUF));
-    memset(buf, 0, sizeof(gather_buf.stringsSize * sizeof(WSABUF)));
-    for(UA_UInt32 i = 0; i<gather_buf.stringsSize; i++) {
-        buf[i].buf = (char*)gather_buf.strings[i].data;
-        buf[i].len = gather_buf.strings[i].length;
-        total_len += gather_buf.strings[i].length;
-    }
-    int result = 0;
-    while(nWritten < total_len) {
-        UA_UInt32 n = 0;
+static UA_StatusCode socket_write(UA_Connection *connection, const UA_ByteString *buf) {
+    if(buf->length < 0)
+        return UA_STATUSCODE_BADINTERNALERROR;
+    UA_Int32 nWritten = 0;
+    while (nWritten < buf->length) {
+        UA_Int32 n = 0;
         do {
-            result = WSASend(connection->sockfd, buf, gather_buf.stringsSize, (LPDWORD)&n, 0, NULL, NULL);
-            if(result != 0 &&WSAGetLastError() != WSAEINTR)
+#ifdef _WIN32
+            n = send((SOCKET)connection->sockfd, (const char*)buf->data, buf->length, 0);
+            if(n < 0 && WSAGetLastError() != WSAEINTR)
                 return UA_STATUSCODE_BADCONNECTIONCLOSED;
-        } while(result != 0);
-        nWritten += n;
-    }
 #else
-    struct iovec iov[gather_buf.stringsSize];
-    memset(iov, 0, sizeof(struct iovec)*gather_buf.stringsSize);
-    for(UA_UInt32 i=0;i<gather_buf.stringsSize;i++) {
-        iov[i].iov_base = gather_buf.strings[i].data;
-        iov[i].iov_len = gather_buf.strings[i].length;
-        total_len += gather_buf.strings[i].length;
-    }
-    struct msghdr message;
-    memset(&message, 0, sizeof(message));
-    message.msg_iov = iov;
-    message.msg_iovlen = gather_buf.stringsSize;
-    while (nWritten < total_len) {
-        UA_Int32 n = 0;
-        do {
-            n = sendmsg(connection->sockfd, &message, 0);
+            n = send(connection->sockfd, (const char*)buf->data, buf->length, MSG_NOSIGNAL);
             if(n == -1L && errno != EINTR)
                 return UA_STATUSCODE_BADCONNECTIONCLOSED;
+#endif
         } while (n == -1L);
         nWritten += n;
     }
-#endif
     return UA_STATUSCODE_GOOD;
 }
 
 static UA_StatusCode socket_recv(UA_Connection *connection, UA_ByteString *response, UA_UInt32 timeout) {
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+	if(response->data == UA_NULL)
+        retval = connection->getBuffer(connection, response, connection->localConf.recvBufferSize);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
     struct timeval tmptv = {0, timeout * 1000};
     setsockopt(connection->sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tmptv, sizeof(struct timeval));
     int ret = recv(connection->sockfd, (char*)response->data, response->length, 0);
-    if(ret <= -1) {
-        if(errno == EAGAIN) {
-            UA_ByteString_deleteMembers(response);
-            UA_ByteString_init(response);
-            return UA_STATUSCODE_GOOD;
+	if(ret == 0) {
+		connection->releaseBuffer(connection, response);
+		connection->close(connection);
+		return UA_STATUSCODE_BADCONNECTIONCLOSED;
+	} else if(ret < 0) {
+#ifdef _WIN32
+		if(WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEWOULDBLOCK) {
+#else
+		if (errno == EAGAIN) {
+#endif
+            return UA_STATUSCODE_BADCOMMUNICATIONERROR;
         }
-        return UA_STATUSCODE_BADINTERNALERROR;
+        connection->releaseBuffer(connection, response);
+		connection->close(connection);
+        return UA_STATUSCODE_BADCONNECTIONCLOSED;
     }
     response->length = ret;
     *response = UA_Connection_completeMessages(connection, *response);
@@ -124,8 +114,6 @@ static UA_StatusCode socket_set_nonblocking(UA_Int32 sockfd) {
 /*****************************/
 
 static UA_StatusCode GetMallocedBuffer(UA_Connection *connection, UA_ByteString *buf, size_t minSize) {
-    if(minSize > connection->remoteConf.recvBufferSize)
-        return UA_STATUSCODE_BADINTERNALERROR;
     return UA_ByteString_newMembers(buf, minSize);
 }
 
@@ -252,6 +240,7 @@ static UA_StatusCode ServerNetworkLayerTCP_add(ServerNetworkLayerTCP *layer, UA_
     c->close = ServerNetworkLayerTCP_closeConnection;
     c->getBuffer = GetMallocedBuffer;
     c->releaseBuffer = ReleaseMallocedBuffer;
+    c->state = UA_CONNECTION_OPENING;
     struct ConnectionMapping *nm =
         realloc(layer->mappings, sizeof(struct ConnectionMapping)*(layer->mappingsSize+1));
     if(!nm) {
@@ -538,7 +527,7 @@ UA_Connection ClientNetworkLayerTCP_connect(char *endpointUrl, UA_Logger *logger
         return connection;
     }
     connection.state = UA_CONNECTION_OPENING;
-    socket_set_nonblocking(connection.sockfd);
+    //socket_set_nonblocking(connection.sockfd);
     connection.write = socket_write;
     connection.recv = socket_recv;
     connection.close = socket_close;

+ 4 - 3
examples/server.c

@@ -6,7 +6,8 @@
 #include <signal.h>
 #include <errno.h> // errno, EINTR
 #include <stdio.h>
-#include <stdlib.h> 
+#include <stdlib.h>
+#include <string.h>
 #define __USE_XOPEN2K
 #ifdef UA_MULTITHREADING
 # include <pthread.h>
@@ -190,14 +191,14 @@ int main(int argc, char** argv) {
 	pthread_rwlock_init(&writeLock, 0);
 #endif
 
-	UA_Server *server = UA_Server_new();
+	UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
 	logger = Logger_Stdout_new();
 	UA_Server_setLogger(server, logger);
     UA_ByteString certificate = loadCertificate();
     UA_Server_setServerCertificate(server, certificate);
     UA_ByteString_deleteMembers(&certificate);
 	UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
-    UA_UInt16 nsIndex = UA_Server_addNamespace(server, "urn:unconfigured:open62541:open62541Server");
+    UA_UInt16 nsIndex = UA_Server_addNamespace(server, UA_ServerConfig_standard.Application_applicationURI);
 
 	// print the status every 2 sec
 	UA_WorkItem work = {.type = UA_WORKITEMTYPE_METHODCALL,

+ 3 - 2
examples/server_simple.c

@@ -7,6 +7,7 @@
 #include <stdlib.h> 
 #include <signal.h>
 #include <errno.h> // errno, EINTR
+#include <string.h>
 
 #ifdef NOT_AMALGATED
     #include "ua_types.h"
@@ -59,14 +60,14 @@ static void testCallback(UA_Server *server, void *data) {
 int main(int argc, char** argv) {
 	signal(SIGINT, stopHandler); /* catches ctrl-c */
 
-	UA_Server *server = UA_Server_new();
+	UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
     logger = Logger_Stdout_new();
     UA_Server_setLogger(server, logger);
     UA_ByteString certificate = loadCertificate();
     UA_Server_setServerCertificate(server, certificate);
     UA_ByteString_deleteMembers(&certificate);
     UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
-    UA_Server_addNamespace(server, "urn:unconfigured:open62541:open62541Server");
+    UA_Server_addNamespace(server, UA_ServerConfig_standard.Application_applicationURI);
 
     UA_WorkItem work = {.type = UA_WORKITEMTYPE_METHODCALL,
                         .work.methodCall = {.method = testCallback, .data = NULL} };

+ 10 - 9
include/ua_connection.h

@@ -28,13 +28,6 @@ extern "C" {
  * @{
  */
 
-/** Used for zero-copy communication. The array of bytestrings is sent over the
-   network as a single buffer. */
-typedef struct UA_ByteStringArray {
-    UA_UInt32      stringsSize;
-    UA_ByteString *strings;
-} UA_ByteStringArray;
-
 typedef enum UA_ConnectionState {
     UA_CONNECTION_OPENING, ///< The socket is open, but the HEL/ACK handshake is not done
     UA_CONNECTION_ESTABLISHED, ///< The socket is open and the connection configured
@@ -73,8 +66,16 @@ struct UA_Connection {
     UA_ByteString incompleteMessage; ///> Half-received messages (tcp is a streaming protocol) get stored here
     UA_StatusCode (*getBuffer)(UA_Connection *connection, UA_ByteString *buf, size_t minSize); ///> Attach the data array to the buffer. Fails if minSize is larger than remoteConf allows
     void (*releaseBuffer)(UA_Connection *connection, UA_ByteString *buf); ///> Release the buffer
-    UA_StatusCode (*write)(UA_Connection *connection, UA_ByteStringArray buf); ///> The bytestrings cannot be reused after sending!
-    UA_StatusCode (*recv)(UA_Connection *connection, UA_ByteString *response, UA_UInt32 timeout); // timeout in milliseconds
+    UA_StatusCode (*write)(UA_Connection *connection, const UA_ByteString *buf); ///> The bytestrings cannot be reused after sending!
+   /**
+     * Receive a message from the remote connection
+	 * @param connection The connection
+	 * @param response The response string. It is allocated by the connection and needs to be freed with connection->releaseBuffer
+     * @param timeout Timeout of the recv operation in milliseconds
+     * @return Returns UA_STATUSCODE_BADCOMMUNICATIONERROR if the recv operation can be repeated, UA_STATUSCODE_GOOD if it succeeded and
+     * UA_STATUSCODE_BADCONNECTIONCLOSED if the connection was closed.
+	 */
+    UA_StatusCode (*recv)(UA_Connection *connection, UA_ByteString *response, UA_UInt32 timeout);
     void (*close)(UA_Connection *connection);
 };
 

+ 15 - 2
include/ua_server.h

@@ -32,10 +32,23 @@ extern "C" {
  * @{
  */
 
+typedef struct UA_ServerConfig {
+    UA_Boolean  Login_enableAnonymous;
+
+    UA_Boolean  Login_enableUsernamePassword;
+    char**      Login_usernames;
+    char**      Login_passwords;
+    UA_UInt32   Login_loginsCount;
+
+    char*       Application_applicationURI;
+} UA_ServerConfig;
+
+extern const UA_ServerConfig UA_ServerConfig_standard;
+
 struct UA_Server;
 typedef struct UA_Server UA_Server;
 
-UA_Server UA_EXPORT * UA_Server_new(void);
+UA_Server UA_EXPORT * UA_Server_new(UA_ServerConfig config);
 void UA_EXPORT UA_Server_setServerCertificate(UA_Server *server, UA_ByteString certificate);
 void UA_EXPORT UA_Server_delete(UA_Server *server);
 
@@ -109,7 +122,7 @@ typedef struct UA_WorkItem {
             UA_ByteString message;
         } binaryNetworkMessage;
         struct {
-            void * data;
+            void *data;
             void (*method)(UA_Server *server, void *data);
         } methodCall;
     } work;

+ 4 - 1
include/ua_types.h

@@ -332,7 +332,7 @@ UA_TYPE_HANDLING_FUNCTIONS(UA_DiagnosticInfo)
     allocated. If the memory cannot be allocated, a null-string is returned. */
 UA_String UA_EXPORT UA_String_fromChars(char const *src);
 #define UA_STRING_ALLOC(CHARS) UA_String_fromChars(CHARS)
-#define UA_STRING(CHARS) (const UA_String) {sizeof(CHARS)-1, (UA_Byte*)CHARS }
+#define UA_STRING(CHARS) (const UA_String) {strlen(CHARS), (UA_Byte*)CHARS }
 #define UA_STRING_NULL (UA_String) {-1, (UA_Byte*)0 }
 
 /** Printf a char-array into a UA_String. Memory for the string data is allocated. */
@@ -341,6 +341,9 @@ UA_StatusCode UA_EXPORT UA_String_copyprintf(char const *fmt, UA_String *dst, ..
 /** Compares two strings */
 UA_Boolean UA_EXPORT UA_String_equal(const UA_String *string1, const UA_String *string2);
 
+/** Compares an UA String with a char array */
+UA_Boolean UA_EXPORT UA_String_equalchars(const UA_String *string1, char *charString);
+
 /* DateTime */
 /** Returns the current time */
 UA_DateTime UA_EXPORT UA_DateTime_now(void);

+ 52 - 42
src/client/ua_client.c

@@ -4,6 +4,8 @@
 #include "ua_types_encoding_binary.h"
 #include "ua_transport_generated.h"
 
+#define ANONYMOUS_POLICY "open62541-anonymous-policy"
+
 struct UA_Client {
     /* Connection */
     UA_Connection connection;
@@ -83,46 +85,41 @@ static UA_StatusCode HelAckHandshake(UA_Client *c) {
     messageHeader.messageSize = UA_TcpHelloMessage_calcSizeBinary((UA_TcpHelloMessage const*)&hello) +
                                 UA_TcpMessageHeader_calcSizeBinary((UA_TcpMessageHeader const*)&messageHeader);
     UA_ByteString message;
-    message.data = UA_alloca(messageHeader.messageSize);
-    message.length = messageHeader.messageSize;
+    UA_StatusCode retval = c->connection.getBuffer(&c->connection, &message, messageHeader.messageSize);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
 
     size_t offset = 0;
     UA_TcpMessageHeader_encodeBinary(&messageHeader, &message, &offset);
     UA_TcpHelloMessage_encodeBinary(&hello, &message, &offset);
     UA_TcpHelloMessage_deleteMembers(&hello);
 
-    UA_ByteStringArray buf = {.stringsSize = 1, .strings = &message};
-    UA_StatusCode retval = c->connection.write(&c->connection, buf);
+    retval = c->connection.write(&c->connection, &message);
+    c->connection.releaseBuffer(&c->connection, &message);
     if(retval)
         return retval;
 
     UA_ByteString reply;
+    UA_ByteString_init(&reply);
     do {
-        UA_ByteString_newMembers(&reply, 1024);
         retval = c->connection.recv(&c->connection, &reply, c->config.timeout);
-        if(retval != UA_STATUSCODE_GOOD) {
-            UA_ByteString_deleteMembers(&reply);
+        if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED)
             return retval;
-        }
-    } while(reply.length < 0);
+    } while(retval != UA_STATUSCODE_GOOD);
 
     offset = 0;
     UA_TcpMessageHeader_decodeBinary(&reply, &offset, &messageHeader);
     UA_TcpAcknowledgeMessage ackMessage;
     retval = UA_TcpAcknowledgeMessage_decodeBinary(&reply, &offset, &ackMessage);
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_TcpAcknowledgeMessage_deleteMembers(&ackMessage);
+    c->connection.releaseBuffer(&c->connection, &reply);
+    if(retval != UA_STATUSCODE_GOOD)
         return retval;
-    }
-    UA_ByteString_deleteMembers(&reply);
     conn->remoteConf.maxChunkCount = ackMessage.maxChunkCount;
     conn->remoteConf.maxMessageSize = ackMessage.maxMessageSize;
     conn->remoteConf.protocolVersion = ackMessage.protocolVersion;
     conn->remoteConf.recvBufferSize = ackMessage.receiveBufferSize;
     conn->remoteConf.sendBufferSize = ackMessage.sendBufferSize;
     conn->state = UA_CONNECTION_ESTABLISHED;
-
-    UA_TcpAcknowledgeMessage_deleteMembers(&ackMessage);
     return UA_STATUSCODE_GOOD;
 }
 
@@ -165,8 +162,13 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *client) {
         UA_OpenSecureChannelRequest_calcSizeBinary(&opnSecRq);
 
     UA_ByteString message;
-    message.data = UA_alloca(messageHeader.messageHeader.messageSize);
-    message.length = messageHeader.messageHeader.messageSize;
+    UA_StatusCode retval = client->connection.getBuffer(&client->connection, &message,
+                                                        messageHeader.messageHeader.messageSize);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
+        UA_OpenSecureChannelRequest_deleteMembers(&opnSecRq);
+        return retval;
+    }
 
     size_t offset = 0;
     UA_SecureConversationMessageHeader_encodeBinary(&messageHeader, &message, &offset);
@@ -178,21 +180,19 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *client) {
     UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
     UA_OpenSecureChannelRequest_deleteMembers(&opnSecRq);
 
-    UA_ByteStringArray buf = {.stringsSize = 1, .strings = &message};
-    UA_StatusCode retval = client->connection.write(&client->connection, buf);
+    retval = client->connection.write(&client->connection, &message);
+    client->connection.releaseBuffer(&client->connection, &message);
     if(retval)
         return retval;
 
     // parse the response
     UA_ByteString reply;
+    UA_ByteString_init(&reply);
     do {
-        UA_ByteString_newMembers(&reply, client->connection.localConf.recvBufferSize);
         retval = client->connection.recv(&client->connection, &reply, client->config.timeout);
-        if(retval != UA_STATUSCODE_GOOD) {
-            UA_ByteString_deleteMembers(&reply);
+        if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED)
             return retval;
-        }
-    } while(reply.length < 0);
+    } while(retval != UA_STATUSCODE_GOOD);
 
     offset = 0;
     UA_SecureConversationMessageHeader_decodeBinary(&reply, &offset, &messageHeader);
@@ -201,15 +201,16 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *client) {
     UA_NodeId_decodeBinary(&reply, &offset, &requestType);
 
     if(!UA_NodeId_equal(&requestType, &UA_NODEID_NUMERIC(0, UA_NS0ID_OPENSECURECHANNELRESPONSE +
-                                                        UA_ENCODINGOFFSET_BINARY))) {
-        UA_ByteString_deleteMembers(&reply);
+                                                         UA_ENCODINGOFFSET_BINARY))) {
+        client->connection.releaseBuffer(&client->connection, &reply);
         UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
+        UA_NodeId_deleteMembers(&requestType);
         return UA_STATUSCODE_BADINTERNALERROR;
     }
 
     UA_OpenSecureChannelResponse response;
     UA_OpenSecureChannelResponse_decodeBinary(&reply, &offset, &response);
-    UA_ByteString_deleteMembers(&reply);
+    client->connection.releaseBuffer(&client->connection, &reply);
     retval = response.responseHeader.serviceResult;
 
     if(retval == UA_STATUSCODE_GOOD) {
@@ -228,8 +229,7 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *client) {
     response) and filled with the appropriate error code */
 static void sendReceiveRequest(UA_RequestHeader *request, const UA_DataType *requestType,
                                void *response, const UA_DataType *responseType, UA_Client *client,
-                               UA_Boolean sendOnly)
-{
+                               UA_Boolean sendOnly) {
     if(response)
         UA_init(response, responseType);
 
@@ -260,7 +260,7 @@ static void sendReceiveRequest(UA_RequestHeader *request, const UA_DataType *req
         UA_calcSizeBinary(request, requestType);
 
     UA_ByteString message;
-    UA_StatusCode retval = UA_ByteString_newMembers(&message, msgHeader.messageHeader.messageSize);
+    UA_StatusCode retval = client->connection.getBuffer(&client->connection, &message, msgHeader.messageHeader.messageSize);
     if(retval != UA_STATUSCODE_GOOD) {
         // todo
     }
@@ -273,9 +273,8 @@ static void sendReceiveRequest(UA_RequestHeader *request, const UA_DataType *req
     retval |= UA_NodeId_encodeBinary(&requestId, &message, &offset);
     retval |= UA_encodeBinary(request, requestType, &message, &offset);
 
-    UA_ByteStringArray buf = {.stringsSize = 1, .strings = &message};
-    retval = client->connection.write(&client->connection, buf);
-    UA_ByteString_deleteMembers(&message);
+    retval = client->connection.write(&client->connection, &message);
+    client->connection.releaseBuffer(&client->connection, &message);
 
     //TODO: rework to get return value
     if(sendOnly)
@@ -289,15 +288,14 @@ static void sendReceiveRequest(UA_RequestHeader *request, const UA_DataType *req
 
     /* Response */
     UA_ByteString reply;
+    UA_ByteString_init(&reply);
     do {
-        UA_ByteString_newMembers(&reply, client->connection.localConf.recvBufferSize);
         retval = client->connection.recv(&client->connection, &reply, client->config.timeout);
-        if(retval != UA_STATUSCODE_GOOD) {
-            UA_ByteString_deleteMembers(&reply);
+        if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED) {
             respHeader->serviceResult = retval;
             return;
         }
-    } while(reply.length < 0);
+    } while(retval != UA_STATUSCODE_GOOD);
 
     offset = 0;
     retval |= UA_SecureConversationMessageHeader_decodeBinary(&reply, &offset, &msgHeader);
@@ -308,20 +306,20 @@ static void sendReceiveRequest(UA_RequestHeader *request, const UA_DataType *req
 
     if(!UA_NodeId_equal(&responseId, &UA_NODEID_NUMERIC(0, responseType->typeId.identifier.numeric +
                                                        UA_ENCODINGOFFSET_BINARY))) {
-        UA_ByteString_deleteMembers(&reply);
+        client->connection.releaseBuffer(&client->connection, &reply);
         UA_SymmetricAlgorithmSecurityHeader_deleteMembers(&symHeader);
         respHeader->serviceResult = retval;
         return;
     }
 
     retval = UA_decodeBinary(&reply, &offset, response, responseType);
-    UA_ByteString_deleteMembers(&reply);
+    client->connection.releaseBuffer(&client->connection, &reply);
     if(retval != UA_STATUSCODE_GOOD)
         respHeader->serviceResult = retval;
 }
 
-static void synchronousRequest(void *request, const UA_DataType *requestType,
-                               void *response, const UA_DataType *responseType, UA_Client *client){
+static void synchronousRequest(void *request, const UA_DataType *requestType, void *response,
+                               const UA_DataType *responseType, UA_Client *client) {
     sendReceiveRequest(request, requestType, response, responseType, client, UA_FALSE);
 }
 
@@ -334,6 +332,19 @@ static UA_StatusCode ActivateSession(UA_Client *client) {
     request.requestHeader.timestamp = UA_DateTime_now();
     request.requestHeader.timeoutHint = 10000;
 
+    UA_AnonymousIdentityToken identityToken;
+    UA_AnonymousIdentityToken_init(&identityToken);
+    identityToken.policyId = UA_STRING(ANONYMOUS_POLICY);
+
+    //manual ExtensionObject encoding of the identityToken
+    request.userIdentityToken.encoding = UA_EXTENSIONOBJECT_ENCODINGMASK_BODYISBYTESTRING;
+    request.userIdentityToken.typeId = UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN].typeId;
+    request.userIdentityToken.typeId.identifier.numeric+=UA_ENCODINGOFFSET_BINARY;
+
+    UA_ByteString_newMembers(&request.userIdentityToken.body, identityToken.policyId.length+4);
+    size_t offset = 0;
+    UA_ByteString_encodeBinary(&identityToken.policyId,&request.userIdentityToken.body,&offset);
+
     UA_ActivateSessionResponse response;
     synchronousRequest(&request, &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST],
                        &response, &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE],
@@ -345,7 +356,6 @@ static UA_StatusCode ActivateSession(UA_Client *client) {
 }
 
 static UA_StatusCode SessionHandshake(UA_Client *client) {
-
     UA_CreateSessionRequest request;
     UA_CreateSessionRequest_init(&request);
 

+ 5 - 11
src/server/ua_securechannel_manager.c

@@ -2,11 +2,6 @@
 #include "ua_session.h"
 #include "ua_statuscodes.h"
 
-typedef struct channel_list_entry {
-    UA_SecureChannel channel;
-    LIST_ENTRY(channel_list_entry) pointers;
-} channel_list_entry;
-
 UA_StatusCode UA_SecureChannelManager_init(UA_SecureChannelManager *cm, UA_UInt32 maxChannelCount,
                                            UA_UInt32 tokenLifetime, UA_UInt32 startChannelId,
                                            UA_UInt32 startTokenId) {
@@ -119,20 +114,19 @@ UA_SecureChannel * UA_SecureChannelManager_get(UA_SecureChannelManager *cm, UA_U
 
 UA_StatusCode UA_SecureChannelManager_close(UA_SecureChannelManager *cm, UA_UInt32 channelId) {
     // TODO lock access
-    // TODO: close the binaryconnection if it is still open. So we do not have stray pointers..
     channel_list_entry *entry;
     LIST_FOREACH(entry, &cm->channels, pointers) {
         if(entry->channel.securityToken.channelId == channelId) {
-            if(entry->channel.connection)
-                entry->channel.connection->channel = UA_NULL; // remove pointer back to the channel
-            if(entry->channel.session)
-                entry->channel.session->channel = UA_NULL; // remove ponter back to the channel
+            UA_Connection *c = entry->channel.connection;
+            if(c) {
+                UA_Connection_detachSecureChannel(c);
+                c->close(c);
+            }
             UA_SecureChannel_deleteMembers(&entry->channel);
             LIST_REMOVE(entry, pointers);
             UA_free(entry);
             return UA_STATUSCODE_GOOD;
         }
     }
-    //TODO notify server application that secureChannel has been closed part 6 - §7.1.4
     return UA_STATUSCODE_BADINTERNALERROR;
 }

+ 6 - 0
src/server/ua_securechannel_manager.h

@@ -4,6 +4,12 @@
 #include "ua_util.h"
 #include "ua_server.h"
 #include "ua_securechannel.h"
+#include "queue.h"
+
+typedef struct channel_list_entry {
+    UA_SecureChannel channel;
+    LIST_ENTRY(channel_list_entry) pointers;
+} channel_list_entry;
 
 typedef struct UA_SecureChannelManager {
     LIST_HEAD(channel_list, channel_list_entry) channels; // doubly-linked list of channels

+ 83 - 15
src/server/ua_server.c

@@ -6,6 +6,18 @@
 #include "ua_services.h"
 #include "ua_nodeids.h"
 
+
+const UA_ServerConfig UA_ServerConfig_standard = {
+        UA_TRUE,
+
+        UA_TRUE,
+        (char *[]){"username"},
+        (char *[]){"password"},
+        1,
+
+        "urn:unconfigured:open62541:open62541Server"
+};
+
 /**********************/
 /* Namespace Handling */
 /**********************/
@@ -103,6 +115,43 @@ void UA_Server_delete(UA_Server *server) {
 	UA_free(server);
 }
 
+/**
+ * Recurring cleanup. Removing unused and timed-out channels and sessions
+ * Todo: make this thread-safe
+ */
+static void UA_Server_cleanup(UA_Server *server, void *nothing) {
+    printf( "cleaning up...\n");
+    fflush(stdout);
+    UA_DateTime now = UA_DateTime_now();
+    channel_list_entry *entry;
+    /* remove channels that were not renewed or who have no connection attached */
+    LIST_FOREACH(entry, &server->secureChannelManager.channels, pointers) {
+        if(entry->channel.securityToken.createdAt +
+           (10000 * entry->channel.securityToken.revisedLifetime) > now ||
+           entry->channel.connection)
+            continue;
+        UA_Connection *c = entry->channel.connection;
+        UA_Connection_detachSecureChannel(c);
+        if(c) {
+            c->close(c);
+        }
+        UA_SecureChannel_detachSession(&entry->channel);
+        UA_SecureChannel_deleteMembers(&entry->channel);
+        LIST_REMOVE(entry, pointers);
+        UA_free(entry);
+    }
+
+    session_list_entry *sentry;
+    LIST_FOREACH(sentry, &server->sessionManager.sessions, pointers) {
+        if(sentry->session.validTill < now) {
+            LIST_REMOVE(sentry, pointers);
+            UA_SecureChannel_detachSession(sentry->session.channel);
+            UA_Session_deleteMembers(&sentry->session);
+            UA_free(sentry);
+        }
+    }
+}
+
 static UA_StatusCode readStatus(void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
     UA_ServerStatusDataType *status = UA_ServerStatusDataType_new();
     status->startTime   = ((const UA_Server*)handle)->startTime;
@@ -222,11 +271,14 @@ static void addVariableTypeNode_subtype(UA_Server *server, char* name, UA_UInt32
                       &UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 }
 
-UA_Server * UA_Server_new(void) {
+UA_Server * UA_Server_new(UA_ServerConfig config) {
     UA_Server *server = UA_malloc(sizeof(UA_Server));
     if(!server)
         return UA_NULL;
 
+    //FIXME: config contains strings, for now its okay, but consider copying them aswell
+    server->config = config;
+
     LIST_INIT(&server->timedWork);
 #ifdef UA_MULTITHREADING
     rcu_init();
@@ -246,12 +298,10 @@ UA_Server * UA_Server_new(void) {
 
     UA_ByteString_init(&server->serverCertificate);
 
-#define PRODUCT_URI "http://open62541.org"
-#define APPLICATION_URI "urn:unconfigured:open62541:open62541Server"
     // mockup application description
     UA_ApplicationDescription_init(&server->description);
     server->description.productUri = UA_STRING_ALLOC(PRODUCT_URI);
-    server->description.applicationUri = UA_STRING_ALLOC(APPLICATION_URI);
+    server->description.applicationUri = UA_STRING_ALLOC(server->config.Application_applicationURI);
     server->description.discoveryUrlsSize = 0;
 
     server->description.applicationName = UA_LOCALIZEDTEXT_ALLOC("", "Unconfigured open62541 application");
@@ -272,16 +322,30 @@ UA_Server * UA_Server_new(void) {
         endpoint->securityPolicyUri = UA_STRING_ALLOC("http://opcfoundation.org/UA/SecurityPolicy#None");
         endpoint->transportProfileUri = UA_STRING_ALLOC("http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary");
 
-        endpoint->userIdentityTokensSize = 2;
-        endpoint->userIdentityTokens = UA_Array_new(&UA_TYPES[UA_TYPES_USERTOKENPOLICY], 2);
-
-        UA_UserTokenPolicy_init(&endpoint->userIdentityTokens[0]);
-        endpoint->userIdentityTokens[0].tokenType = UA_USERTOKENTYPE_ANONYMOUS;
-        endpoint->userIdentityTokens[0].policyId = UA_STRING_ALLOC("my-anonymous-policy"); // defined per server
-
-        UA_UserTokenPolicy_init(&endpoint->userIdentityTokens[1]);
-        endpoint->userIdentityTokens[1].tokenType = UA_USERTOKENTYPE_USERNAME;
-        endpoint->userIdentityTokens[1].policyId = UA_STRING_ALLOC("my-username-policy"); // defined per server
+        int size = 0;
+        if(server->config.Login_enableAnonymous){
+            size++;
+        }
+        if(server->config.Login_enableUsernamePassword){
+            size++;
+        }
+        endpoint->userIdentityTokensSize = size;
+        endpoint->userIdentityTokens = UA_Array_new(&UA_TYPES[UA_TYPES_USERTOKENPOLICY], size);
+
+        int currentIndex = 0;
+        if(server->config.Login_enableAnonymous){
+            UA_UserTokenPolicy_init(&endpoint->userIdentityTokens[currentIndex]);
+            endpoint->userIdentityTokens[currentIndex].tokenType = UA_USERTOKENTYPE_ANONYMOUS;
+            endpoint->userIdentityTokens[currentIndex].policyId = UA_STRING_ALLOC(ANONYMOUS_POLICY); // defined per server
+            currentIndex++;
+        }
+
+        if(server->config.Login_enableUsernamePassword){
+            UA_UserTokenPolicy_init(&endpoint->userIdentityTokens[currentIndex]);
+            endpoint->userIdentityTokens[currentIndex].tokenType = UA_USERTOKENTYPE_USERNAME;
+            endpoint->userIdentityTokens[currentIndex].policyId = UA_STRING_ALLOC(USERNAME_POLICY); // defined per server
+            currentIndex++;
+        }
 
         /* UA_String_copy(endpointUrl, &endpoint->endpointUrl); */
         /* /\* The standard says "the HostName specified in the Server Certificate is the */
@@ -308,6 +372,10 @@ UA_Server * UA_Server_new(void) {
 
     server->nodestore = UA_NodeStore_new();
 
+	/* UA_WorkItem cleanup = {.type = UA_WORKITEMTYPE_METHODCALL, */
+    /*                        .work.methodCall = {.method = UA_Server_cleanup, .data = NULL} }; */
+	/* UA_Server_addRepeatedWorkItem(server, &cleanup, 100000, NULL); */
+
     /**********************/
     /* Server Information */
     /**********************/
@@ -728,7 +796,7 @@ UA_Server * UA_Server_new(void) {
    serverArray->value.variant.data = UA_Array_new(&UA_TYPES[UA_TYPES_STRING], 1);
    serverArray->value.variant.arrayLength = 1;
    serverArray->value.variant.type = &UA_TYPES[UA_TYPES_STRING];
-   *(UA_String *)serverArray->value.variant.data = UA_STRING_ALLOC(APPLICATION_URI);
+   *(UA_String *)serverArray->value.variant.data = UA_STRING_ALLOC(server->config.Application_applicationURI);
    serverArray->valueRank = 1;
    serverArray->minimumSamplingInterval = 1.0;
    serverArray->historizing = UA_FALSE;

+ 86 - 88
src/server/ua_server_binary.c

@@ -11,14 +11,6 @@
 /** Max size of messages that are allocated on the stack */
 #define MAX_STACK_MESSAGE 65536
 
-static UA_StatusCode UA_ByteStringArray_deleteMembers(UA_ByteStringArray *stringarray) {
-    if(!stringarray)
-        return UA_STATUSCODE_BADINTERNALERROR;
-    for(UA_UInt32 i = 0; i < stringarray->stringsSize; i++)
-        UA_String_deleteMembers(&stringarray->strings[i]);
-    return UA_STATUSCODE_GOOD;
-}
-
 static void processHEL(UA_Connection *connection, const UA_ByteString *msg, size_t *pos) {
     UA_TcpHelloMessage helloMessage;
     if(UA_TcpHelloMessage_decodeBinary(msg, pos, &helloMessage) != UA_STATUSCODE_GOOD) {
@@ -32,6 +24,7 @@ static void processHEL(UA_Connection *connection, const UA_ByteString *msg, size
     connection->remoteConf.recvBufferSize = helloMessage.receiveBufferSize;
     connection->remoteConf.sendBufferSize = helloMessage.sendBufferSize;
     connection->state = UA_CONNECTION_ESTABLISHED;
+    UA_TcpHelloMessage_deleteMembers(&helloMessage);
 
     // build acknowledge response
     UA_TcpAcknowledgeMessage ackMessage;
@@ -43,18 +36,18 @@ static void processHEL(UA_Connection *connection, const UA_ByteString *msg, size
 
     UA_TcpMessageHeader ackHeader;
     ackHeader.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_ACKF;
-    ackHeader.messageSize = UA_TcpAcknowledgeMessage_calcSizeBinary(&ackMessage) +
-        UA_TcpMessageHeader_calcSizeBinary(&ackHeader);
+    ackHeader.messageSize = UA_TcpMessageHeader_calcSizeBinary(&ackHeader) 
+        + UA_TcpAcknowledgeMessage_calcSizeBinary(&ackMessage);
+
+    UA_ByteString ack_msg;
+    if(connection->getBuffer(connection, &ack_msg, ackHeader.messageSize) != UA_STATUSCODE_GOOD)
+        return;
 
-    // The message is on the stack. That's ok since ack is very small.
-    UA_ByteString ack_msg = (UA_ByteString){ .length = ackHeader.messageSize,
-                                             .data = UA_alloca(ackHeader.messageSize) };
     size_t tmpPos = 0;
     UA_TcpMessageHeader_encodeBinary(&ackHeader, &ack_msg, &tmpPos);
     UA_TcpAcknowledgeMessage_encodeBinary(&ackMessage, &ack_msg, &tmpPos);
-    UA_ByteStringArray answer_buf = { .stringsSize = 1, .strings = &ack_msg };
-    connection->write(connection, answer_buf);
-    UA_TcpHelloMessage_deleteMembers(&helloMessage);
+    connection->write(connection, &ack_msg);
+    connection->releaseBuffer(connection, &ack_msg);
 }
 
 static void processOPN(UA_Connection *connection, UA_Server *server, const UA_ByteString *msg,
@@ -91,6 +84,7 @@ static void processOPN(UA_Connection *connection, UA_Server *server, const UA_By
     UA_OpenSecureChannelResponse p;
     UA_OpenSecureChannelResponse_init(&p);
     Service_OpenSecureChannel(server, connection, &r, &p);
+    UA_OpenSecureChannelRequest_deleteMembers(&r);
 
     UA_SecureConversationMessageHeader respHeader;
     respHeader.messageHeader.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_OPNF;
@@ -107,10 +101,8 @@ static void processOPN(UA_Connection *connection, UA_Server *server, const UA_By
         + UA_NodeId_calcSizeBinary(&responseType)
         + UA_OpenSecureChannelResponse_calcSizeBinary(&p);
 
-    UA_ByteString resp_msg = (UA_ByteString){
-        .length = respHeader.messageHeader.messageSize,
-        .data = UA_alloca(respHeader.messageHeader.messageSize)
-    };
+    UA_ByteString resp_msg;
+    retval = connection->getBuffer(connection, &resp_msg, respHeader.messageHeader.messageSize);
 
     size_t tmpPos = 0;
     UA_SecureConversationMessageHeader_encodeBinary(&respHeader, &resp_msg, &tmpPos);
@@ -119,10 +111,10 @@ static void processOPN(UA_Connection *connection, UA_Server *server, const UA_By
     UA_NodeId_encodeBinary(&responseType, &resp_msg, &tmpPos);
     UA_OpenSecureChannelResponse_encodeBinary(&p, &resp_msg, &tmpPos);
 
-    UA_OpenSecureChannelRequest_deleteMembers(&r);
     UA_OpenSecureChannelResponse_deleteMembers(&p);
     UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
-    connection->write(connection, (UA_ByteStringArray){ .stringsSize = 1, .strings = &resp_msg });
+    connection->write(connection, &resp_msg);
+    connection->releaseBuffer(connection, &resp_msg);
 }
 
 static void init_response_header(const UA_RequestHeader *p, UA_ResponseHeader *r) {
@@ -143,14 +135,17 @@ static void init_response_header(const UA_RequestHeader *p, UA_ResponseHeader *r
         UA_##TYPE##Response_init(&r);                                   \
         init_response_header(&p.requestHeader, &r.responseHeader);      \
         if(!clientSession)                                              \
-            r.responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID; \
+            r.responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONNOTACTIVATED; \
         else                                                            \
             Service_##TYPE(server, clientSession, &p, &r);              \
-        connection->getBuffer(connection, message, UA_##TYPE##Response_calcSizeBinary(&r)); \
-        UA_##TYPE##Response_encodeBinary(&r, message, &sendOffset);     \
         UA_##TYPE##Request_deleteMembers(&p);                           \
+        retval = connection->getBuffer(connection, &message, headerSize + UA_##TYPE##Response_calcSizeBinary(&r)); \
+        if(retval != UA_STATUSCODE_GOOD) {                              \
+            UA_##TYPE##Response_deleteMembers(&r);                      \
+            return;                                                     \
+        }                                                               \
+        UA_##TYPE##Response_encodeBinary(&r, &message, &messagePos);    \
         UA_##TYPE##Response_deleteMembers(&r);                          \
-        responseType = requestType.identifier.numeric + 3;              \
     } while(0)
 
 static void processMSG(UA_Connection *connection, UA_Server *server, const UA_ByteString *msg, size_t *pos) {
@@ -188,25 +183,38 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
         clientSession = &anonymousSession;
 #endif
 
-    // 3) Read the nodeid of the request
+    // 3) Build the header and compute the header size
+    UA_SecureConversationMessageHeader respHeader;
+    respHeader.messageHeader.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_MSGF;
+    respHeader.messageHeader.messageSize = 0;
+    respHeader.secureChannelId = clientChannel->securityToken.channelId;
+
+    UA_SymmetricAlgorithmSecurityHeader symSecHeader;
+    symSecHeader.tokenId = clientChannel->securityToken.tokenId;
+
+    UA_SequenceHeader seqHeader;
+    seqHeader.sequenceNumber = clientChannel->sequenceNumber;
+    seqHeader.requestId = clientChannel->requestId;
+
+    // 4) process the request
+    UA_ByteString message;
     UA_NodeId requestType;
     if(UA_NodeId_decodeBinary(msg, pos, &requestType))
         return;
     if(requestType.identifierType != UA_NODEIDTYPE_NUMERIC) {
-        // That must not happen. The requestType does not have to be deleted at the end.
         UA_NodeId_deleteMembers(&requestType);
         return;
     }
 
-    // 4) process the request
-    UA_ByteString responseBufs[2]; // 0->header, 1->response payload
-    UA_UInt32 responseType;
-    UA_ByteString *header = &responseBufs[0];
-    UA_ByteString *message = &responseBufs[1];
-    size_t sendOffset = 0;
-
-    //subtract UA_ENCODINGOFFSET_BINARY for binary encoding, if retval is set, this forces the default path
-    switch(requestType.identifier.numeric - UA_ENCODINGOFFSET_BINARY + retval) {
+    UA_NodeId response_nodeid = UA_NODEID_NUMERIC(0, requestType.identifier.numeric + 3);
+    UA_UInt32 headerSize = UA_SecureConversationMessageHeader_calcSizeBinary(&respHeader)
+        + UA_SymmetricAlgorithmSecurityHeader_calcSizeBinary(&symSecHeader)
+        + UA_SequenceHeader_calcSizeBinary(&seqHeader)
+        + UA_NodeId_calcSizeBinary(&response_nodeid);
+    size_t messagePos = headerSize;
+
+    //subtract UA_ENCODINGOFFSET_BINARY for binary encoding
+    switch(requestType.identifier.numeric - UA_ENCODINGOFFSET_BINARY) {
     case UA_NS0ID_GETENDPOINTSREQUEST: {
         UA_GetEndpointsRequest  p;
         UA_GetEndpointsResponse r;
@@ -215,11 +223,14 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
         UA_GetEndpointsResponse_init(&r);
         init_response_header(&p.requestHeader, &r.responseHeader);
         Service_GetEndpoints(server, &p, &r);
-        connection->getBuffer(connection, message, UA_GetEndpointsResponse_calcSizeBinary(&r));
-        UA_GetEndpointsResponse_encodeBinary(&r, message, &sendOffset);
         UA_GetEndpointsRequest_deleteMembers(&p);
+        retval = connection->getBuffer(connection, &message, headerSize + UA_GetEndpointsResponse_calcSizeBinary(&r));
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_GetEndpointsResponse_deleteMembers(&r);
+            return;
+        }
+        UA_GetEndpointsResponse_encodeBinary(&r, &message, &messagePos);
         UA_GetEndpointsResponse_deleteMembers(&r);
-        responseType = requestType.identifier.numeric + 3;
         break;
     }
 
@@ -231,11 +242,14 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
         UA_FindServersResponse_init(&r);
         init_response_header(&p.requestHeader, &r.responseHeader);
         Service_FindServers(server, &p, &r);
-        connection->getBuffer(connection, message, UA_FindServersResponse_calcSizeBinary(&r));
-        UA_FindServersResponse_encodeBinary(&r, message, &sendOffset);
         UA_FindServersRequest_deleteMembers(&p);
+        retval = connection->getBuffer(connection, &message, headerSize + UA_FindServersResponse_calcSizeBinary(&r));
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_FindServersResponse_deleteMembers(&r);
+            return;
+        }
+        UA_FindServersResponse_encodeBinary(&r, &message, &messagePos);
         UA_FindServersResponse_deleteMembers(&r);
-        responseType = requestType.identifier.numeric + 3;
         break;
     }
 
@@ -247,11 +261,14 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
         UA_CreateSessionResponse_init(&r);
         init_response_header(&p.requestHeader, &r.responseHeader);
         Service_CreateSession(server, clientChannel, &p, &r);
-        connection->getBuffer(connection, message, UA_CreateSessionResponse_calcSizeBinary(&r));
-        UA_CreateSessionResponse_encodeBinary(&r, message, &sendOffset);
         UA_CreateSessionRequest_deleteMembers(&p);
+        retval = connection->getBuffer(connection, &message, headerSize + UA_CreateSessionResponse_calcSizeBinary(&r));
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_CreateSessionResponse_deleteMembers(&r);
+            return;
+        }
+        UA_CreateSessionResponse_encodeBinary(&r, &message, &messagePos);
         UA_CreateSessionResponse_deleteMembers(&r);
-        responseType = requestType.identifier.numeric + 3;
         break;
     }
 
@@ -263,11 +280,14 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
         UA_ActivateSessionResponse_init(&r);
         init_response_header(&p.requestHeader, &r.responseHeader);
         Service_ActivateSession(server, clientChannel, &p, &r);
-        connection->getBuffer(connection, message, UA_ActivateSessionResponse_calcSizeBinary(&r));
-        UA_ActivateSessionResponse_encodeBinary(&r, message, &sendOffset);
         UA_ActivateSessionRequest_deleteMembers(&p);
+        retval = connection->getBuffer(connection, &message, headerSize + UA_ActivateSessionResponse_calcSizeBinary(&r));
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_ActivateSessionResponse_deleteMembers(&r);
+            return;
+        }
+        UA_ActivateSessionResponse_encodeBinary(&r, &message, &messagePos);
         UA_ActivateSessionResponse_deleteMembers(&r);
-        responseType = requestType.identifier.numeric + 3;
         break;
     }
 
@@ -298,6 +318,7 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
     case UA_NS0ID_TRANSLATEBROWSEPATHSTONODEIDSREQUEST:
         INVOKE_SERVICE(TranslateBrowsePathsToNodeIds);
         break;
+
     default: {
         UA_LOG_INFO(server->logger, UA_LOGGERCATEGORY_COMMUNICATION, "Unknown request: NodeId(ns=%d, i=%d)",
                     requestType.namespaceIndex, requestType.identifier.numeric);
@@ -312,54 +333,31 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
         if(retval != UA_STATUSCODE_GOOD)
             r.serviceResult = retval;
 #endif
-        connection->getBuffer(connection, message, UA_ResponseHeader_calcSizeBinary(&r));
-        UA_ResponseHeader_encodeBinary(&r, message, &sendOffset);
         UA_RequestHeader_deleteMembers(&p);
+        retval = connection->getBuffer(connection, &message, headerSize + UA_ResponseHeader_calcSizeBinary(&r));
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_ResponseHeader_deleteMembers(&r);
+            return;
+        }
+        UA_ResponseHeader_encodeBinary(&r, &message, &messagePos);
         UA_ResponseHeader_deleteMembers(&r);
-        responseType = UA_NS0ID_RESPONSEHEADER + UA_ENCODINGOFFSET_BINARY;
+        response_nodeid = UA_NODEID_NUMERIC(0, UA_NS0ID_RESPONSEHEADER + UA_ENCODINGOFFSET_BINARY);
         break;
     }
     }
 
-    // 5) Build the header
-    UA_SecureConversationMessageHeader respHeader;
-    respHeader.messageHeader.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_MSGF;
-    respHeader.messageHeader.messageSize = 0;
-    respHeader.secureChannelId = clientChannel->securityToken.channelId;
-
-    UA_SymmetricAlgorithmSecurityHeader symSecHeader;
-    symSecHeader.tokenId = clientChannel->securityToken.tokenId;
-
-    UA_SequenceHeader seqHeader;
-    seqHeader.sequenceNumber = clientChannel->sequenceNumber;
-    seqHeader.requestId = clientChannel->requestId;
-
-    UA_NodeId response_nodeid = { .namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
-        .identifier.numeric = responseType };
-
-    UA_UInt32 headerSize =
-        UA_SecureConversationMessageHeader_calcSizeBinary(&respHeader)
-        + UA_SymmetricAlgorithmSecurityHeader_calcSizeBinary(&symSecHeader)
-        + UA_SequenceHeader_calcSizeBinary(&seqHeader)
-        + UA_NodeId_calcSizeBinary(&response_nodeid);
-    respHeader.messageHeader.messageSize = headerSize + message->length;
-
-    size_t rpos = 0;
-    connection->getBuffer(connection, header, headerSize);
-    UA_SecureConversationMessageHeader_encodeBinary(&respHeader, header, &rpos);
-    UA_SymmetricAlgorithmSecurityHeader_encodeBinary(&symSecHeader, header, &rpos);
-    UA_SequenceHeader_encodeBinary(&seqHeader, header, &rpos);
-    UA_NodeId_encodeBinary(&response_nodeid, header, &rpos);
+    messagePos = 0;
+    respHeader.messageHeader.messageSize = message.length;
+    UA_SecureConversationMessageHeader_encodeBinary(&respHeader, &message, &messagePos);
+    UA_SymmetricAlgorithmSecurityHeader_encodeBinary(&symSecHeader, &message, &messagePos);
+    UA_SequenceHeader_encodeBinary(&seqHeader, &message, &messagePos);
+    UA_NodeId_encodeBinary(&response_nodeid, &message, &messagePos);
 
     // todo: sign & encrypt
 
-    // 6) Send it over the wire.
-    UA_ByteStringArray responseBufArray;
-    responseBufArray.strings = responseBufs;
-    responseBufArray.stringsSize = 2;
-    connection->write(connection, responseBufArray);
-    connection->releaseBuffer(connection, header);
-    connection->releaseBuffer(connection, message);
+    // 5) Send it over the wire.
+    connection->write(connection, &message);
+    connection->releaseBuffer(connection, &message);
 }
 
 static void processCLO(UA_Connection *connection, UA_Server *server, const UA_ByteString *msg, size_t *pos) {

+ 7 - 0
src/server/ua_server_internal.h

@@ -7,6 +7,10 @@
 #include "ua_securechannel_manager.h"
 #include "ua_nodestore.h"
 
+#define PRODUCT_URI "http://open62541.org"
+#define ANONYMOUS_POLICY "open62541-anonymous-policy"
+#define USERNAME_POLICY "open62541-username-policy"
+
 /** Mapping of namespace-id and url to an external nodestore. For namespaces
     that have no mapping defined, the internal nodestore is used by default. */
 typedef struct UA_ExternalNamespace {
@@ -59,6 +63,9 @@ struct UA_Server {
 
     UA_DateTime startTime;
     UA_DateTime buildDate;
+
+    /* Config */
+    UA_ServerConfig config;
 };
 
 void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection, UA_ByteString *msg);

+ 8 - 5
src/server/ua_server_worker.c

@@ -18,13 +18,14 @@
 #define BATCHSIZE 20 // max size of worklists that are dispatched to workers
 
 static void processWork(UA_Server *server, UA_WorkItem *work, UA_Int32 workSize) {
-    for(UA_Int32 i = 0;i<workSize;i++) {
+    for(UA_Int32 i = 0; i < workSize; i++) {
         UA_WorkItem *item = &work[i];
         switch(item->type) {
         case UA_WORKITEMTYPE_BINARYNETWORKMESSAGE:
             UA_Server_processBinaryMessage(server, item->work.binaryNetworkMessage.connection,
                                            &item->work.binaryNetworkMessage.message);
-            UA_free(item->work.binaryNetworkMessage.message.data);
+            item->work.binaryNetworkMessage.connection->releaseBuffer(item->work.binaryNetworkMessage.connection,
+                                                                      &item->work.binaryNetworkMessage.message);
             break;
 
         case UA_WORKITEMTYPE_METHODCALL:
@@ -153,9 +154,9 @@ static UA_StatusCode addTimedWork(UA_Server *server, const UA_WorkItem *item, UA
         if(tw->repetitionInterval == repetitionInterval &&
            (repetitionInterval > 0 || tw->time == firstTime))
             break; // found a matching entry
+        lastTw = tw;
         if(tw->time > firstTime) {
             tw = UA_NULL; // not matchin entry exists
-            lastTw = tw;
             break;
         }
     }
@@ -284,11 +285,13 @@ static UA_UInt16 processTimedWork(UA_Server *server) {
 
     // check if the next timed work is sooner than the usual timeout
     UA_TimedWork *first = LIST_FIRST(&server->timedWork);
-    UA_UInt16 timeout = MAXTIMEOUT;
+    UA_Int32 timeout = MAXTIMEOUT;
     if(first) {
         timeout = (first->time - current)/10;
         if(timeout > MAXTIMEOUT)
-            timeout = MAXTIMEOUT;
+            return MAXTIMEOUT;
+        if(timeout < 0)
+            return 0;
     }
     return timeout;
 }

+ 71 - 3
src/server/ua_services_session.c

@@ -38,6 +38,12 @@ void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
     }
 }
 
+#ifdef RETURN
+#undef RETURN
+#endif
+#define RETURN  UA_UserIdentityToken_deleteMembers(&token); \
+                UA_UserNameIdentityToken_deleteMembers(&username_token); \
+                return
 void Service_ActivateSession(UA_Server *server,UA_SecureChannel *channel,
                              const UA_ActivateSessionRequest *request,
                              UA_ActivateSessionResponse *response) {
@@ -47,11 +53,73 @@ void Service_ActivateSession(UA_Server *server,UA_SecureChannel *channel,
                                         (const UA_NodeId*)&request->requestHeader.authenticationToken,
                                         &foundSession);
 
-	if(foundSession == UA_NULL)
+	if(foundSession == UA_NULL){
         response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
-    else
+        return;
+	}
+
+
+
+    UA_UserIdentityToken token;
+    UA_UserIdentityToken_init(&token);
+    size_t offset = 0;
+    UA_UserIdentityToken_decodeBinary(&request->userIdentityToken.body, &offset, &token);
+
+    UA_UserNameIdentityToken username_token;
+    UA_UserNameIdentityToken_init(&username_token);
+
+    //check policies
+
+    if(token.policyId.data == UA_NULL){ //user identity token is NULL
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+        //todo cleanup session
+        RETURN;
+    }
+
+    //anonymous logins
+    if(server->config.Login_enableAnonymous && UA_String_equalchars(&token.policyId, ANONYMOUS_POLICY)){
+        //success - bind session to the channel
         channel->session = foundSession;
+        RETURN;
+    //username logins
+    }else if(server->config.Login_enableUsernamePassword && UA_String_equalchars(&token.policyId, USERNAME_POLICY)){
+        offset = 0;
+        UA_UserNameIdentityToken_decodeBinary(&request->userIdentityToken.body, &offset, &username_token);
+        if(username_token.encryptionAlgorithm.data != UA_NULL){
+            //we only support encryption
+            response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+            //todo cleanup session
+            RETURN;
+        }
+        if(username_token.userName.length == -1 && username_token.password.length == -1){
+            //empty username and password
+            response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+            //todo cleanup session
+            RETURN;
+        }
+        for(UA_UInt32 i=0;i<server->config.Login_loginsCount;++i){
+            if(UA_String_equalchars(&username_token.userName, server->config.Login_usernames[i])
+            && UA_String_equalchars(&username_token.password, server->config.Login_passwords[i])){
+                //success - bind session to the channel
+                channel->session = foundSession;
+                RETURN;
+            }
+        }
+        //no username/pass matched
+       response->responseHeader.serviceResult = UA_STATUSCODE_BADUSERACCESSDENIED;
+       //todo cleanup session
+       RETURN;
+    }
+
+    //default case - no login
+    response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+    //todo cleanup session
+    RETURN;
+
+
+
 }
+#undef RETURN
 
 void Service_CloseSession(UA_Server *server, UA_Session *session, const UA_CloseSessionRequest *request,
                           UA_CloseSessionResponse *response) {
@@ -60,7 +128,7 @@ void Service_CloseSession(UA_Server *server, UA_Session *session, const UA_Close
 			(const UA_NodeId*)&request->requestHeader.authenticationToken, &foundSession);
 
 	if(foundSession == UA_NULL){
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+		response->responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID;
 		return;
 	}
 

+ 3 - 2
src/server/ua_services_view.c

@@ -472,12 +472,13 @@ static void translateBrowsePath(UA_Server *server, UA_Session *session, const UA
 void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
                                            const UA_TranslateBrowsePathsToNodeIdsRequest *request,
                                            UA_TranslateBrowsePathsToNodeIdsResponse *response) {
-    size_t size = request->browsePathsSize;
-	if(size <= 0) {
+	if(request->browsePathsSize <= 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
         return;
     }
 
+    size_t size = request->browsePathsSize;
+
     response->results = UA_Array_new(&UA_TYPES[UA_TYPES_BROWSEPATHRESULT], size);
     if(!response->results) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;

+ 1 - 7
src/server/ua_session_manager.c

@@ -8,11 +8,6 @@
  implementation is choosen based on whether multithreading is enabled or not.
  */
 
-typedef struct session_list_entry {
-    UA_Session session;
-    LIST_ENTRY(session_list_entry) pointers;
-} session_list_entry;
-
 UA_StatusCode UA_SessionManager_init(UA_SessionManager *sessionManager, UA_UInt32 maxSessionCount,
                                     UA_UInt32 maxSessionLifeTime, UA_UInt32 startSessionId) {
     LIST_INIT(&sessionManager->sessions);
@@ -129,8 +124,7 @@ UA_SessionManager_removeSession(UA_SessionManager *sessionManager, const UA_Node
         return UA_STATUSCODE_BADINTERNALERROR;
 
     LIST_REMOVE(current, pointers);
-    if(current->session.channel)
-        current->session.channel->session = UA_NULL; // the channel is no longer attached to a session
+    UA_SecureChannel_detachSession(current->session.channel);
     UA_Session_deleteMembers(&current->session);
     UA_free(current);
     return UA_STATUSCODE_GOOD;

+ 5 - 0
src/server/ua_session_manager.h

@@ -6,6 +6,11 @@
 #include "ua_util.h"
 #include "ua_session.h"
 
+typedef struct session_list_entry {
+    UA_Session session;
+    LIST_ENTRY(session_list_entry) pointers;
+} session_list_entry;
+
 typedef struct UA_SessionManager {
     LIST_HEAD(session_list, session_list_entry) sessions; // doubly-linked list of sessions
     UA_UInt32    maxSessionCount;

+ 5 - 0
src/ua_types.c

@@ -159,6 +159,11 @@ UA_Boolean UA_String_equal(const UA_String *string1, const UA_String *string2) {
     return (is == 0) ? UA_TRUE : UA_FALSE;
 }
 
+UA_Boolean UA_String_equalchars(const UA_String *string1, char *charString) {
+    UA_String string2 = UA_STRING(charString);
+    return UA_String_equal(string1, &string2);
+}
+
 /* DateTime */
 #define UNIX_EPOCH_BIAS_SEC 11644473600LL // Number of seconds from 1 Jan. 1601 00:00 to 1 Jan 1970 00:00 UTC
 #define HUNDRED_NANOSEC_PER_USEC 10LL

+ 2 - 0
src/ua_util.h

@@ -10,6 +10,8 @@
 #endif
 #ifndef _POSIX_SOURCE
 # define _POSIX_SOURCE
+#endif
+#ifndef _POSIX_C_SOURCE
 # define _POSIX_C_SOURCE 199309L
 #endif
 #ifndef _BSD_SOURCE

+ 3 - 2
tests/check_server_interaction_fileinput.c

@@ -15,7 +15,8 @@ UA_UInt32 read_count = 0;
 UA_UInt32 max_reads;
 char **filenames;
 
-static void writeCallback(void *handle, UA_ByteStringArray buf) {
+static UA_StatusCode writeCallback(void *handle, UA_ByteString *buf) {
+    return UA_STATUSCODE_GOOD;
 }
 
 static void readCallback(void) {
@@ -25,7 +26,7 @@ static void readCallback(void) {
 }
 
 START_TEST(readVariable) {
-	UA_Server *server = UA_Server_new();
+	UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
     UA_Server_setLogger(server, Logger_Stdout_new());
     UA_Server_addNetworkLayer(server, ServerNetworkLayerFileInput_new(max_reads, filenames, readCallback,
                                                                       writeCallback, NULL));

+ 7 - 7
tests/testing_networklayers.c

@@ -4,19 +4,19 @@
 #include <assert.h>
 #include "testing_networklayers.h"
 
-typedef struct {
+typedef struct NetworkLayer_FileInput {
 	UA_Connection connection;
     UA_UInt32 files;
     char **filenames;
     UA_UInt32 files_read;
-    void (*writeCallback)(void *, UA_ByteStringArray buf);
+    UA_StatusCode (*writeCallback)(struct NetworkLayer_FileInput *handle, const UA_ByteString *buf);
     void (*readCallback)(void);
     void *callbackHandle;
 } NetworkLayer_FileInput;
 
 /** Accesses only the sockfd in the handle. Can be run from parallel threads. */
-static UA_StatusCode writeCallback(NetworkLayer_FileInput *handle, UA_ByteStringArray gather_buf) {
-    handle->writeCallback(handle->callbackHandle, gather_buf);
+static UA_StatusCode writeCallback(NetworkLayer_FileInput *handle, const UA_ByteString *buf) {
+    handle->writeCallback(handle->callbackHandle, buf);
     return UA_STATUSCODE_GOOD;
 }
 
@@ -73,7 +73,7 @@ static void NetworkLayer_FileInput_delete(NetworkLayer_FileInput *layer) {
 
 UA_ServerNetworkLayer
 ServerNetworkLayerFileInput_new(UA_UInt32 files, char **filenames, void(*readCallback)(void),
-                                void(*writeCallback) (void*, UA_ByteStringArray buf),
+                                UA_StatusCode (*writeCallback) (void*, UA_ByteString *buf),
                                 void *callbackHandle)
 {
     NetworkLayer_FileInput *layer = malloc(sizeof(NetworkLayer_FileInput));
@@ -81,13 +81,13 @@ ServerNetworkLayerFileInput_new(UA_UInt32 files, char **filenames, void(*readCal
     layer->connection.localConf = UA_ConnectionConfig_standard;
     layer->connection.channel = (void*)0;
     layer->connection.close = (void (*)(UA_Connection*))closeCallback;
-    layer->connection.write = (UA_StatusCode (*)(UA_Connection*, UA_ByteStringArray))writeCallback;
+    layer->connection.write = (UA_StatusCode (*)(UA_Connection*, const UA_ByteString*))writeCallback;
 
     layer->files = files;
     layer->filenames = filenames;
     layer->files_read = 0;
     layer->readCallback = readCallback;
-    layer->writeCallback = writeCallback;
+    layer->writeCallback = (UA_StatusCode(*)(struct NetworkLayer_FileInput *handle, const UA_ByteString *buf)) writeCallback;
     layer->callbackHandle = callbackHandle;
     
     UA_ServerNetworkLayer nl;

+ 1 - 1
tests/testing_networklayers.h

@@ -10,7 +10,7 @@
 /** @brief Create the TCP networklayer and listen to the specified port */
 UA_ServerNetworkLayer
 ServerNetworkLayerFileInput_new(UA_UInt32 files, char **filenames, void(*readCallback)(void),
-                                void(*writeCallback) (void*, UA_ByteStringArray buf),
+                                UA_StatusCode (*writeCallback) (void*, UA_ByteString *buf),
                                 void *callbackHandle);
 
 #endif /* TESTING_NETWORKLAYERS_H_ */

+ 1 - 1
tools/generate_datatypes.py

@@ -57,7 +57,7 @@ minimal_types = ["InvalidType", "Node", "NodeClass", "ReferenceNode", "Applicati
                  "NodeAttributes","ReferenceTypeAttributes", "ViewAttributes", "ObjectTypeAttributes",
                  "NodeAttributesMask","DeleteNodesItem", "DeleteNodesRequest", "DeleteNodesResponse",
                  "DeleteReferencesItem", "DeleteReferencesRequest", "DeleteReferencesResponse",
-                 "RegisterNodesRequest", "RegisterNodesResponse", "UnregisterNodesRequest", "UnregisterNodesResponse"]
+                 "RegisterNodesRequest", "RegisterNodesResponse", "UnregisterNodesRequest", "UnregisterNodesResponse", "UserIdentityToken", "UserNameIdentityToken", "AnonymousIdentityToken"]
 
 class TypeDescription(object):
     def __init__(self, name, nodeid, namespaceid):