Browse Source

Merge pull request #208 from acplt/client

Merging the client branch
Sten 10 years ago
parent
commit
922f4ee01b

+ 9 - 9
.travis.yml

@@ -33,22 +33,22 @@ script:
 - mkdir -p build
 - cd build
 - echo "Cross compile release build for MinGW"
-- cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-mingw32.cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLESERVER=ON ..
+- cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-mingw32.cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLESERVER=ON -DBUILD_EXAMPLECLIENT=ON ..
 - make -j
 - cp ../README.md .
-- zip open62541-win32.zip README.md exampleServer_datasource.exe libopen62541.dll libopen62541.dll.a open62541.h open62541.c
+- 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 32-bit linux"
-- cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-gcc-m32.cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLESERVER=ON ..
+- cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-gcc-m32.cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLESERVER=ON -DBUILD_EXAMPLECLIENT=ON ..
 - make -j
-- tar -pczf open62541-linux32.tar.gz ../README.md exampleServer_datasource libopen62541.so open62541.h open62541.c
+- tar -pczf open62541-linux32.tar.gz ../README.md exampleServer exampleClient libopen62541.so open62541.h open62541.c
 - cp open62541-linux32.tar.gz ..
 - cd .. && rm build -rf && mkdir -p build && cd build
 - echo "Compile release build for 64-bit linux"
-- cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLESERVER=ON ..
+- cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLESERVER=ON -DBUILD_EXAMPLECLIENT=ON ..
 - make -j
-- tar -pczf open62541-linux64.tar.gz ../README.md exampleServer_datasource libopen62541.so open62541.h open62541.c
+- tar -pczf open62541-linux64.tar.gz ../README.md exampleServer exampleClient libopen62541.so open62541.h open62541.c
 - cp open62541-linux64.tar.gz ..
 - cd .. && rm build -rf && mkdir -p build && cd build
 - echo "Upgrade to gcc 4.8"
@@ -82,9 +82,9 @@ before_deploy:
 - git clone https://github.com/raspberrypi/tools
 - cd -
 - export PATH=$PATH:~/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/
-- cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-rpi64.cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLESERVER=ON ..
+- cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-rpi64.cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLESERVER=ON -DBUILD_EXAMPLECLIENT=ON ..
 - make -j
-- tar -pczf open62541-raspberrypi.tar.gz ../README.md exampleServer_datasource libopen62541.so open62541.h open62541.c
+- tar -pczf open62541-raspberrypi.tar.gz ../README.md exampleServer exampleClient libopen62541.so open62541.h open62541.c
 - cp open62541-raspberrypi.tar.gz ..
 - cd ..
 deploy:
@@ -98,4 +98,4 @@ deploy:
     - open62541-raspberrypi.tar.gz
   skip_cleanup: true
   on:
-    tags: true
+    tags: true

+ 30 - 14
CMakeLists.txt

@@ -43,7 +43,8 @@ set(exported_headers ${PROJECT_BINARY_DIR}/src_generated/ua_config.h
                      ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.h
                      ${PROJECT_SOURCE_DIR}/include/ua_connection.h
                      ${PROJECT_SOURCE_DIR}/include/ua_log.h
-                     ${PROJECT_SOURCE_DIR}/include/ua_server.h)
+                     ${PROJECT_SOURCE_DIR}/include/ua_server.h
+                     ${PROJECT_SOURCE_DIR}/include/ua_client.h)
 set(internal_headers ${PROJECT_SOURCE_DIR}/src/ua_util.h
                      ${PROJECT_SOURCE_DIR}/deps/queue.h
                      ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated.h
@@ -74,7 +75,9 @@ set(lib_sources ${PROJECT_SOURCE_DIR}/src/ua_types.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_session.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_attribute.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_nodemanagement.c
-                ${PROJECT_SOURCE_DIR}/src/server/ua_services_view.c)
+                ${PROJECT_SOURCE_DIR}/src/server/ua_services_view.c
+                ${PROJECT_SOURCE_DIR}/src/client/ua_client.c)
+                ##TODO: make client stuff optional
 
 ## generate code from xml definitions
 file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/src_generated")
@@ -136,6 +139,7 @@ if(BUILD_DEMO_NODESET)
 endif()
 
 if(CMAKE_BUILD_TYPE STREQUAL "MinSizeRel" OR CMAKE_BUILD_TYPE STREQUAL "Release")
+	set(AMALGATED ON)
     ## build amalgamated source files
     add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/open62541.h ${PROJECT_BINARY_DIR}/open62541.c
                    PRE_BUILD
@@ -178,23 +182,23 @@ endif()
 option(BUILD_EXAMPLESERVER "Build the example server" OFF)
 if(BUILD_EXAMPLESERVER)
     add_executable(exampleServer examples/server.c $<TARGET_OBJECTS:open62541-object> examples/networklayer_tcp.c examples/logger_stdout.c)
-    add_executable(exampleServer_datasource examples/server_datasource.c $<TARGET_OBJECTS:open62541-object> examples/networklayer_tcp.c examples/logger_stdout.c)
+    add_executable(exampleServer_simple examples/server_simple.c $<TARGET_OBJECTS:open62541-object> examples/networklayer_tcp.c examples/logger_stdout.c)
     target_include_directories(exampleServer PRIVATE ${PROJECT_BINARY_DIR})
-    target_include_directories(exampleServer_datasource PRIVATE ${PROJECT_BINARY_DIR})
+    target_include_directories(exampleServer_simple PRIVATE ${PROJECT_BINARY_DIR})
     if(WIN32)
         target_link_libraries(exampleServer ws2_32)
-        target_link_libraries(exampleServer_datasource ws2_32)
+        target_link_libraries(exampleServer_simple ws2_32)
     else()
         target_link_libraries(exampleServer rt)
-        target_link_libraries(exampleServer_datasource rt)
+        target_link_libraries(exampleServer_simple rt)
     endif()
     if(ENABLE_MULTITHREADING)
         target_link_libraries(exampleServer urcu-cds urcu urcu-common pthread)
-        target_link_libraries(exampleServer_datasource urcu-cds urcu urcu-common pthread)
+        target_link_libraries(exampleServer_simple urcu-cds urcu urcu-common pthread)
     endif()
     if((CMAKE_COMPILER_IS_GNUCC OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") AND (CMAKE_BUILD_TYPE STREQUAL "MinSizeRel" OR CMAKE_BUILD_TYPE STREQUAL "Release"))
     	add_custom_command(TARGET exampleServer POST_BUILD COMMAND ${CMAKE_STRIP} $<TARGET_FILE:exampleServer>)
-		add_custom_command(TARGET exampleServer_datasource POST_BUILD COMMAND ${CMAKE_STRIP} $<TARGET_FILE:exampleServer_datasource>)
+		add_custom_command(TARGET exampleServer_simple POST_BUILD COMMAND ${CMAKE_STRIP} $<TARGET_FILE:exampleServer_simple>)
 	endif()
 endif()
 
@@ -215,18 +219,30 @@ option(BUILD_EXAMPLECLIENT "Build a test client" OFF)
 if(BUILD_EXAMPLECLIENT)
 	message(STATUS "Extensions: enabling client")
 	add_definitions( -DBENCHMARK=1 )
-	add_executable(exampleClient $<TARGET_OBJECTS:open62541-object> examples/client.c)
-    target_include_directories(exampleClient PRIVATE ${PROJECT_BINARY_DIR})
-    if(ENABLE_MULTITHREADING)
-        target_link_libraries(exampleClient urcu-cds urcu urcu-common pthread)
+	if(NOT AMALGATED)
+		add_executable(exampleClient_legacy $<TARGET_OBJECTS:open62541-object> examples/client_legacy.c)
+	endif()
+	add_executable(exampleClient $<TARGET_OBJECTS:open62541-object> examples/client.c examples/networklayer_tcp.c)
+    if(NOT AMALGATED)
+    	target_include_directories(exampleClient_legacy PRIVATE ${PROJECT_BINARY_DIR})
     endif()
+    target_include_directories(exampleClient PRIVATE ${PROJECT_BINARY_DIR})
     if(WIN32)
+    	if(NOT AMALGATED)
+        	target_link_libraries(exampleClient_legacy ws2_32)
+        endif()
         target_link_libraries(exampleClient ws2_32)
     else()
+   		if(NOT AMALGATED)
+        	target_link_libraries(exampleClient_legacy rt)
+        endif()
         target_link_libraries(exampleClient rt)
     endif()
     if ((CMAKE_COMPILER_IS_GNUCC OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") AND (CMAKE_BUILD_TYPE STREQUAL "MinSizeRel" OR CMAKE_BUILD_TYPE STREQUAL "Release"))
-    	add_custom_command(TARGET exampleClient POST_BUILD COMMAND ${CMAKE_STRIP} $<TARGET_FILE:exampleClient>)
+       if(NOT AMALGATED)
+       		add_custom_command(TARGET exampleClient_legacy POST_BUILD COMMAND ${CMAKE_STRIP} $<TARGET_FILE:exampleClient_legacy>)
+	   endif()
+	   add_custom_command(TARGET exampleClient POST_BUILD COMMAND ${CMAKE_STRIP} $<TARGET_FILE:exampleClient>)
 	endif()
     if(EXTENSION_STATELESS)
         add_executable(statelessClient ${PROJECT_BINARY_DIR}/open62541.c examples/client_stateless.c)
@@ -259,4 +275,4 @@ if(BUILD_DOCUMENTATION)
                       ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
                       WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
                       COMMENT "Generating API documentation with Doxygen")
-endif()
+endif()

+ 33 - 581
examples/client.c

@@ -1,588 +1,40 @@
-/*
- C ECHO client example using sockets
-
- This is an example client for internal benchmarks. It works, but is not ready
- for serious use. We do not really check any of the returns from the server.
- */
-#include <stdio.h> //printf
-#include <string.h> //strlen
-#include <sys/socket.h> //socket
-#include <arpa/inet.h> //inet_addr
-#include <unistd.h> // for close
-#include <stdlib.h> // pulls in declaration of malloc, free
-
 #ifdef NOT_AMALGATED
-    #include "ua_transport_generated.h"
-    #include "ua_types_encoding_binary.h"
-    #include "ua_util.h"
+    #include "ua_types.h"
+    #include "ua_client.h"
 #else
     #include "open62541.h"
 #endif
 
-typedef struct ConnectionInfo {
-	UA_Int32 socket;
-	UA_UInt32 channelId;
-	UA_SequenceHeader sequenceHdr;
-	UA_NodeId authenticationToken;
-	UA_UInt32 tokenId;
-} ConnectionInfo;
-
-static UA_Int32 sendHello(UA_Int32 sock, UA_String *endpointURL) {
-
-	UA_TcpMessageHeader messageHeader;
-	messageHeader.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_HELF;
-
-	UA_TcpHelloMessage hello;
-	UA_String_copy(endpointURL, &hello.endpointUrl);
-	hello.maxChunkCount = 1;
-	hello.maxMessageSize = 16777216;
-	hello.protocolVersion = 0;
-	hello.receiveBufferSize = 65536;
-	hello.sendBufferSize = 65536;
-
-	messageHeader.messageSize = UA_TcpHelloMessage_calcSizeBinary((UA_TcpHelloMessage const*) &hello) +
-                                UA_TcpMessageHeader_calcSizeBinary((UA_TcpMessageHeader const*) &messageHeader);
-	UA_ByteString message;
-	UA_ByteString_newMembers(&message, messageHeader.messageSize);
-
-	size_t offset = 0;
-	UA_TcpMessageHeader_encodeBinary((UA_TcpMessageHeader const*) &messageHeader, &message, &offset);
-	UA_TcpHelloMessage_encodeBinary((UA_TcpHelloMessage const*) &hello, &message, &offset);
-
-	UA_Int32 sendret = send(sock, message.data, offset, 0);
-
-	UA_ByteString_deleteMembers(&message);
-	free(hello.endpointUrl.data);
-	if (sendret < 0)
-		return 1;
-	return 0;
-}
-
-static int sendOpenSecureChannel(UA_Int32 sock) {
-	UA_TcpMessageHeader msghdr;
-	msghdr.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_OPNF;
-
-	UA_UInt32 secureChannelId = 0;
-	UA_String securityPolicy;
-	UA_String_copycstring("http://opcfoundation.org/UA/SecurityPolicy#None", &securityPolicy);
-
-	UA_String senderCert;
-	senderCert.data = UA_NULL;
-	senderCert.length = -1;
-
-	UA_String receiverCertThumb;
-	receiverCertThumb.data = UA_NULL;
-	receiverCertThumb.length = -1;
-
-	UA_UInt32 sequenceNumber = 51;
-
-	UA_UInt32 requestId = 1;
-
-	UA_NodeId type;
-	type.identifier.numeric = 446; // id of opensecurechannelrequest
-	type.identifierType = UA_NODEIDTYPE_NUMERIC;
-	type.namespaceIndex = 0;
-
-	UA_OpenSecureChannelRequest opnSecRq;
-	UA_OpenSecureChannelRequest_init(&opnSecRq);
-	opnSecRq.requestHeader.timestamp = UA_DateTime_now();
-	UA_ByteString_newMembers(&opnSecRq.clientNonce, 1);
-	opnSecRq.clientNonce.data[0] = 0;
-	opnSecRq.clientProtocolVersion = 0;
-	opnSecRq.requestedLifetime = 30000;
-	opnSecRq.securityMode = UA_MESSAGESECURITYMODE_NONE;
-	opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_ISSUE;
-	opnSecRq.requestHeader.authenticationToken.identifier.numeric = 10;
-	opnSecRq.requestHeader.authenticationToken.identifierType = UA_NODEIDTYPE_NUMERIC;
-	opnSecRq.requestHeader.authenticationToken.namespaceIndex = 10;
-
-	msghdr.messageSize = 135; // todo: compute the message size from the actual content
-
-	UA_ByteString message;
-	UA_ByteString_newMembers(&message, 1000);
-	size_t offset = 0;
-	UA_TcpMessageHeader_encodeBinary(&msghdr, &message, &offset);
-	UA_UInt32_encodeBinary(&secureChannelId, &message, &offset);
-	UA_String_encodeBinary(&securityPolicy, &message, &offset);
-	UA_String_encodeBinary(&senderCert, &message, &offset);
-	UA_String_encodeBinary(&receiverCertThumb, &message, &offset);
-	UA_UInt32_encodeBinary(&sequenceNumber, &message, &offset);
-	UA_UInt32_encodeBinary(&requestId, &message, &offset);
-	UA_NodeId_encodeBinary(&type, &message, &offset);
-	UA_OpenSecureChannelRequest_encodeBinary(&opnSecRq, &message, &offset);
-
-    UA_OpenSecureChannelRequest_deleteMembers(&opnSecRq);
-	UA_String_deleteMembers(&securityPolicy);
-
-	UA_Int32 sendret = send(sock, message.data, offset, 0);
-	UA_ByteString_deleteMembers(&message);
-	if (sendret < 0) {
-		printf("send opensecurechannel failed");
-		return 1;
-	}
-	return 0;
-}
-
-static UA_Int32 sendCreateSession(UA_Int32 sock, UA_UInt32 channelId, UA_UInt32 tokenId, UA_UInt32 sequenceNumber,
-                                  UA_UInt32 requestId, UA_String *endpointUrl) {
-    UA_ByteString message;
-	UA_ByteString_newMembers(&message, 65536);
-	UA_UInt32 tmpChannelId = channelId;
-	size_t offset = 0;
-
-	UA_TcpMessageHeader msghdr;
-	msghdr.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_MSGF;
-
-	UA_NodeId type;
-	type.identifier.numeric = 461;
-	type.identifierType = UA_NODEIDTYPE_NUMERIC;
-	type.namespaceIndex = 0;
-
-	UA_CreateSessionRequest rq;
-    UA_CreateSessionRequest_init(&rq);
-	rq.requestHeader.requestHandle = 1;
-	rq.requestHeader.timestamp = UA_DateTime_now();
-	rq.requestHeader.timeoutHint = 10000;
-	rq.requestHeader.authenticationToken.identifier.numeric = 10;
-	rq.requestHeader.authenticationToken.identifierType = UA_NODEIDTYPE_NUMERIC;
-	rq.requestHeader.authenticationToken.namespaceIndex = 10;
-	UA_String_copy(endpointUrl, &rq.endpointUrl);
-	UA_String_copycstring("mysession", &rq.sessionName);
-	UA_String_copycstring("abcd", &rq.clientCertificate);
-	UA_ByteString_newMembers(&rq.clientNonce, 1);
-	rq.clientNonce.data[0] = 0;
-	rq.requestedSessionTimeout = 1200000;
-	rq.maxResponseMessageSize = UA_INT32_MAX;
-
-	msghdr.messageSize = 16 + UA_TcpMessageHeader_calcSizeBinary(&msghdr) + UA_NodeId_calcSizeBinary(&type) +
-                         UA_CreateSessionRequest_calcSizeBinary(&rq);
-
-	UA_TcpMessageHeader_encodeBinary(&msghdr, &message, &offset);
-	UA_UInt32_encodeBinary(&tmpChannelId, &message, &offset);
-	UA_UInt32_encodeBinary(&tokenId, &message, &offset);
-	UA_UInt32_encodeBinary(&sequenceNumber, &message, &offset);
-	UA_UInt32_encodeBinary(&requestId, &message, &offset);
-	UA_NodeId_encodeBinary(&type, &message, &offset);
-	UA_CreateSessionRequest_encodeBinary(&rq, &message, &offset);
-
-	UA_Int32 sendret = send(sock, message.data, offset, 0);
-	UA_ByteString_deleteMembers(&message);
-	UA_CreateSessionRequest_deleteMembers(&rq);
-	if (sendret < 0) {
-		printf("send opensecurechannel failed");
-		return 1;
-	}
-	return 0;
-}
-
-static UA_Int32 closeSession(ConnectionInfo *connectionInfo) {
-	size_t offset = 0;
-
-	UA_ByteString message;
-	UA_ByteString_newMembers(&message, 65536);
-
-	UA_CloseSessionRequest rq;
-    UA_CloseSessionRequest_init(&rq);
-
-	rq.requestHeader.requestHandle = 1;
-	rq.requestHeader.timestamp = UA_DateTime_now();
-	rq.requestHeader.timeoutHint = 10000;
-	rq.requestHeader.authenticationToken.identifier.numeric = 10;
-	rq.requestHeader.authenticationToken.identifierType = UA_NODEIDTYPE_NUMERIC;
-	rq.requestHeader.authenticationToken.namespaceIndex = 10;
-    rq.deleteSubscriptions = UA_TRUE;
-
-	UA_TcpMessageHeader msghdr;
-	msghdr.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_MSGF;
-
-	UA_NodeId type;
-	type.identifier.numeric = 473;
-	type.identifierType = UA_NODEIDTYPE_NUMERIC;
-	type.namespaceIndex = 0;
-
-	msghdr.messageSize = 16 + UA_TcpMessageHeader_calcSizeBinary(&msghdr) + UA_NodeId_calcSizeBinary(&type) +
-                         UA_CloseSessionRequest_calcSizeBinary(&rq);
-
-	UA_TcpMessageHeader_encodeBinary(&msghdr, &message, &offset);
-	UA_UInt32_encodeBinary(&connectionInfo->channelId, &message, &offset);
-	UA_UInt32_encodeBinary(&connectionInfo->tokenId, &message, &offset);
-	UA_UInt32_encodeBinary(&connectionInfo->sequenceHdr.sequenceNumber, &message, &offset);
-	UA_UInt32_encodeBinary(&connectionInfo->sequenceHdr.requestId, &message, &offset);
-	UA_NodeId_encodeBinary(&type, &message, &offset);
-	UA_CloseSessionRequest_encodeBinary(&rq, &message, &offset);
-
-	UA_Int32 sendret = send(connectionInfo->socket, message.data, offset, 0);
-	UA_ByteString_deleteMembers(&message);
-	UA_CloseSessionRequest_deleteMembers(&rq);
-	if(sendret < 0) {
-		printf("send closesessionrequest failed");
-		return 1;
-	}
-
-    return 0;
-}
-
-static UA_Int32 closeSecureChannel(ConnectionInfo *connectionInfo) {
-	size_t offset = 0;
-
-	UA_ByteString message;
-	UA_ByteString_newMembers(&message, 65536);
-
-	UA_CloseSecureChannelRequest rq;
-    UA_CloseSecureChannelRequest_init(&rq);
-
-	rq.requestHeader.requestHandle = 1;
-	rq.requestHeader.timestamp = UA_DateTime_now();
-	rq.requestHeader.timeoutHint = 10000;
-	rq.requestHeader.authenticationToken.identifier.numeric = 10;
-	rq.requestHeader.authenticationToken.identifierType = UA_NODEIDTYPE_NUMERIC;
-	rq.requestHeader.authenticationToken.namespaceIndex = 10;
-
-	UA_TcpMessageHeader msghdr;
-	msghdr.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_CLOF;
-
-	msghdr.messageSize = 4 + UA_TcpMessageHeader_calcSizeBinary(&msghdr) +
-                         UA_CloseSecureChannelRequest_calcSizeBinary(&rq);
-
-	UA_TcpMessageHeader_encodeBinary(&msghdr, &message, &offset);
-	UA_UInt32_encodeBinary(&connectionInfo->channelId, &message, &offset);
-	UA_CloseSecureChannelRequest_encodeBinary(&rq, &message, &offset);
-
-	UA_Int32 sendret = send(connectionInfo->socket, message.data, offset, 0);
-	UA_ByteString_deleteMembers(&message);
-	UA_CloseSecureChannelRequest_deleteMembers(&rq);
-	if(sendret < 0) {
-		printf("send CloseSecureChannelRequest failed");
-		return 1;
-	}
-
-    return 0;
-}
-
-static UA_Int32 sendActivateSession(UA_Int32 sock, UA_UInt32 channelId, UA_UInt32 tokenId, UA_UInt32 sequenceNumber,
-                                    UA_UInt32 requestId, UA_NodeId authenticationToken) {
-	UA_ByteString *message = UA_ByteString_new();
-	UA_ByteString_newMembers(message, 65536);
-	UA_UInt32 tmpChannelId = channelId;
-	size_t offset = 0;
-
-	UA_TcpMessageHeader msghdr;
-	msghdr.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_MSGF;
-	msghdr.messageSize = 86;
-
-	UA_NodeId type;
-	type.identifier.numeric = 467;
-	type.identifierType = UA_NODEIDTYPE_NUMERIC;
-	type.namespaceIndex = 0;
-
-	UA_ActivateSessionRequest rq;
-	UA_ActivateSessionRequest_init(&rq);
-	rq.requestHeader.requestHandle = 2;
-	rq.requestHeader.authenticationToken = authenticationToken;
-	rq.requestHeader.timestamp = UA_DateTime_now();
-	rq.requestHeader.timeoutHint = 10000;
-    
-	msghdr.messageSize  = 16 + UA_TcpMessageHeader_calcSizeBinary(&msghdr) + UA_NodeId_calcSizeBinary(&type) +
-                          UA_ActivateSessionRequest_calcSizeBinary(&rq);
-
-	UA_TcpMessageHeader_encodeBinary(&msghdr, message, &offset);
-	UA_UInt32_encodeBinary(&tmpChannelId, message, &offset);
-	UA_UInt32_encodeBinary(&tokenId, message, &offset);
-	UA_UInt32_encodeBinary(&sequenceNumber, message, &offset);
-	UA_UInt32_encodeBinary(&requestId, message, &offset);
-	UA_NodeId_encodeBinary(&type, message, &offset);
-	UA_ActivateSessionRequest_encodeBinary(&rq, message, &offset);
-
-	UA_Int32 sendret = send(sock, message->data, offset, 0);
-	UA_ByteString_delete(message);
-
-	if (sendret < 0) {
-		printf("send opensecurechannel failed");
-		return 1;
-	}
-	return 0;
-
-}
-
-static UA_Int64 sendReadRequest(ConnectionInfo *connectionInfo, UA_Int32 nodeIds_size,UA_NodeId* nodeIds){
-		/*UA_Int32 sock, UA_UInt32 channelId, UA_UInt32 tokenId, UA_UInt32 sequenceNumber, UA_UInt32 requestId,
-                         UA_NodeId authenticationToken, UA_Int32 nodeIds_size,UA_NodeId* nodeIds) {
-                         */
-	UA_ByteString *message = UA_ByteString_new();
-	UA_ByteString_newMembers(message, 65536);
-	UA_UInt32 tmpChannelId = connectionInfo->channelId;
-	size_t offset = 0;
-
-	UA_TcpMessageHeader msghdr;
-	msghdr.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_MSGF;
-
-	UA_NodeId type;
-	type.identifier.numeric = 631;
-	type.identifierType = UA_NODEIDTYPE_NUMERIC;
-	type.namespaceIndex = 0;
-
-	UA_ReadRequest rq;
-	UA_ReadRequest_init(&rq);
-	rq.maxAge = 0;
-	rq.nodesToRead = UA_Array_new(&UA_TYPES[UA_TYPES_READVALUEID], nodeIds_size);
-	rq.nodesToReadSize = nodeIds_size;
-	for(UA_Int32 i=0;i<nodeIds_size;i++) {
-		UA_ReadValueId_init(&(rq.nodesToRead[i]));
-		rq.nodesToRead[i].attributeId = 6; //WriteMask
-		UA_NodeId_init(&(rq.nodesToRead[i].nodeId));
-		rq.nodesToRead[i].nodeId = nodeIds[i];
-		UA_QualifiedName_init(&(rq.nodesToRead[0].dataEncoding));
-	}
-	rq.requestHeader.timeoutHint = 10000;
-	rq.requestHeader.timestamp = UA_DateTime_now();
-	rq.requestHeader.authenticationToken = connectionInfo->authenticationToken;
-	rq.timestampsToReturn = 0x03;
-	rq.requestHeader.requestHandle = 1 + connectionInfo->sequenceHdr.requestId;
-
-	msghdr.messageSize = 16 + UA_TcpMessageHeader_calcSizeBinary(&msghdr) + UA_NodeId_calcSizeBinary(&type) +
-                         UA_ReadRequest_calcSizeBinary(&rq);
-
-	UA_TcpMessageHeader_encodeBinary(&msghdr,message,&offset);
-	UA_UInt32_encodeBinary(&tmpChannelId, message, &offset);
-	UA_UInt32_encodeBinary(&connectionInfo->tokenId, message, &offset);
-	UA_UInt32_encodeBinary(&connectionInfo->sequenceHdr.sequenceNumber, message, &offset);
-	UA_UInt32_encodeBinary(&connectionInfo->sequenceHdr.requestId, message, &offset);
-	UA_NodeId_encodeBinary(&type,message,&offset);
-	UA_ReadRequest_encodeBinary(&rq, message, &offset);
-
-	UA_DateTime tic = UA_DateTime_now();
-	UA_Int32 sendret = send(connectionInfo->socket, message->data, offset, 0);
-	UA_Array_delete(rq.nodesToRead, &UA_TYPES[UA_TYPES_READVALUEID], nodeIds_size);
-	UA_ByteString_delete(message);
-
-	if (sendret < 0) {
-		printf("send readrequest failed");
-		return 1;
-	}
-	return tic;
-}
-
-static int ua_client_connectUA(char* ipaddress,int port, UA_String *endpointUrl, ConnectionInfo *connectionInfo,
-                               UA_Boolean stateless, UA_Boolean udp) {
-	UA_ByteString reply;
-	UA_ByteString_newMembers(&reply, 65536);
-	int sock;
-	struct sockaddr_in server;
-	//Create socket
-	if(udp==UA_TRUE){
-		sock = socket(AF_INET, SOCK_DGRAM, 0);
-	}else{
-		sock = socket(AF_INET, SOCK_STREAM, 0);
-	}
-	if(sock == -1) {
-		printf("Could not create socket");
-        return 1;
-    }
-	server.sin_addr.s_addr = inet_addr(ipaddress);
-	server.sin_family = AF_INET;
-	server.sin_port = htons(port);
-
-	if(connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) {
-			perror("connect failed. Error");
-			return 1;
-		}
-		connectionInfo->socket = sock;
-
-		if(stateless){
-			UA_NodeId_init(&connectionInfo->authenticationToken);
-			connectionInfo->channelId=0;
-			UA_SequenceHeader_init(&connectionInfo->sequenceHdr);
-			connectionInfo->tokenId=0;
-			return 0;
-		}else{
-			sendHello(sock, endpointUrl);
-			recv(sock, reply.data, reply.length, 0);
-			sendOpenSecureChannel(sock);
-			recv(sock, reply.data, reply.length, 0);
-
-			size_t recvOffset = 0;
-			UA_TcpMessageHeader msghdr;
-			UA_TcpMessageHeader_decodeBinary(&reply, &recvOffset, &msghdr);
-
-			UA_AsymmetricAlgorithmSecurityHeader asymHeader;
-			UA_NodeId rspType;
-			UA_OpenSecureChannelResponse openSecChannelRsp;
-			UA_UInt32_decodeBinary(&reply, &recvOffset, &connectionInfo->channelId);
-			UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(&reply,&recvOffset,&asymHeader);
-			UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
-			UA_SequenceHeader_decodeBinary(&reply,&recvOffset,&connectionInfo->sequenceHdr);
-			UA_NodeId_decodeBinary(&reply,&recvOffset,&rspType);
-			UA_OpenSecureChannelResponse_decodeBinary(&reply,&recvOffset,&openSecChannelRsp);
-			connectionInfo->tokenId = openSecChannelRsp.securityToken.tokenId;
-
-			sendCreateSession(sock, connectionInfo->channelId, openSecChannelRsp.securityToken.tokenId, 52, 2, endpointUrl);
-			recv(sock, reply.data, reply.length, 0);
-
-			UA_NodeId messageType;
-			recvOffset = 24;
-			UA_NodeId_decodeBinary(&reply,&recvOffset,&messageType);
-			UA_CreateSessionResponse createSessionResponse;
-			UA_CreateSessionResponse_decodeBinary(&reply,&recvOffset,&createSessionResponse);
-			connectionInfo->authenticationToken = createSessionResponse.authenticationToken;
-			sendActivateSession(sock, connectionInfo->channelId, connectionInfo->tokenId, 53, 3,
-					connectionInfo->authenticationToken);
-			recv(sock, reply.data, reply.length, 0);
-
-			UA_OpenSecureChannelResponse_deleteMembers(&openSecChannelRsp);
-
-			UA_String_deleteMembers(&reply);
-			UA_CreateSessionResponse_deleteMembers(&createSessionResponse);
-			return 0;
-		}
-}
+#include <stdio.h>
+#include "networklayer_tcp.h"
 
 int main(int argc, char *argv[]) {
-	int defaultParams = argc < 8;
-
-	//start parameters
-	if(defaultParams) {
-		printf("1st parameter: number of nodes to read \n");
-		printf("2nd parameter: number of read-tries \n");
-		printf("3rd parameter: name of the file to save measurement data \n");
-		printf("4th parameter: 1 = read same node, 0 = read different nodes \n");
-		printf("5th parameter: ip adress \n");
-		printf("6th parameter: port \n");
-		printf("7th parameter: 0=stateful, 1=stateless\n");
-		printf("8th parameter: 0=tcp, 1=udp (only with stateless calls)\n");
-		printf("\nUsing default parameters. \n");
-	}
-
-	UA_UInt32 nodesToReadSize;
-	UA_UInt32 tries;
-	UA_Boolean alwaysSameNode;
-	UA_ByteString reply;
-	UA_ByteString_newMembers(&reply, 65536);
-	UA_Boolean stateless;
-	UA_Boolean udp;
-
-	if(defaultParams)
-		nodesToReadSize = 1;
-	else
-		nodesToReadSize = atoi(argv[1]);
-
-	if(defaultParams)
-		tries= 2;
-	else
-		tries = (UA_UInt32) atoi(argv[2]);
-
-	if(defaultParams){
-		alwaysSameNode = UA_TRUE;
-	}else{
-		if(atoi(argv[4]) != 0)
-			alwaysSameNode = UA_TRUE;
-		else
-			alwaysSameNode = UA_FALSE;
-	}
-
-	if(defaultParams){
-		stateless = UA_FALSE;
-	}else{
-		if(atoi(argv[7]) != 0)
-			stateless = UA_TRUE;
-		else
-			stateless = UA_FALSE;
-	}
-
-	if(defaultParams){
-		udp = UA_FALSE;
-	}else{
-		if(atoi(argv[8]) != 0)
-			udp = UA_TRUE;
-		else
-			udp = UA_FALSE;
-	}
-
-
-
-    //Connect to remote server
-	UA_String endpoint;
-	UA_String_copycstring("none",&endpoint);
-	ConnectionInfo connectionInfo;
-
-
-/* REQUEST START*/
-    UA_NodeId *nodesToRead;
-    nodesToRead = UA_Array_new(&UA_TYPES[UA_TYPES_NODEID], nodesToReadSize);
-
-	for(UA_UInt32 i = 0; i<nodesToReadSize; i++) {
-		if(alwaysSameNode)
-			nodesToRead[i].identifier.numeric = 2253; //ask always the same node
-		else
-			nodesToRead[i].identifier.numeric = 19000 +i;
-		nodesToRead[i].identifierType = UA_NODEIDTYPE_NUMERIC;
-		nodesToRead[i].namespaceIndex = 0;
-	}
-
-	UA_DateTime tic, toc;
-	UA_Double *timeDiffs;
-	UA_Int32 received;
-	timeDiffs = UA_Array_new(&UA_TYPES[UA_TYPES_DOUBLE], tries);
-	UA_Double sum = 0;
-
-	for(UA_UInt32 i = 0; i < tries; i++) {
-		//if(stateless || (!stateless && i==0)){
-		tic = UA_DateTime_now();
-			if(defaultParams){
-				if(ua_client_connectUA("127.0.0.1",atoi("16664"),&endpoint,&connectionInfo,stateless,udp) != 0){
-					return 0;
-				}
-			}else{
-				if(ua_client_connectUA(argv[5],atoi(argv[6]),&endpoint,&connectionInfo,stateless,udp) != 0){
-					return 0;
-				}
-			}
-		//}
-		sendReadRequest(&connectionInfo,nodesToReadSize,nodesToRead);
-		received = recv(connectionInfo.socket, reply.data, 2000, 0);
-		toc = UA_DateTime_now() - tic;
-		timeDiffs[i] = (UA_Double)toc/(UA_Double)1e4;
-		sum = sum + timeDiffs[i];
-
-		closeSession(&connectionInfo);
-		recv(connectionInfo.socket, reply.data, 2000, 0);
-
-		closeSecureChannel(&connectionInfo);
-
-		//if(stateless || (!stateless && i==tries-1)){
-			close(connectionInfo.socket);
-		//}
-	}
-/* REQUEST END*/
-
-
-	UA_Double mean = sum / tries;
-	printf("mean time for handling request: %16.10f ms \n",mean);
-
-	if(received>0)
-		printf("received: %i\n",received); // dummy
-
-	//save to file
-	char data[100];
-	const char flag = 'a';
-	FILE* fHandle = UA_NULL;
-	if (defaultParams) {
-		fHandle =  fopen("client.log", &flag);
-	}else{
-		fHandle =  fopen(argv[3], &flag);
-	}
-	//header
-
-	UA_Int32 bytesToWrite = sprintf(data, "measurement %s in ms, nodesToRead %d \n", argv[3], nodesToReadSize);
-	fwrite(data,1,bytesToWrite,fHandle);
-	for(UA_UInt32 i=0;i<tries;i++) {
-		bytesToWrite = sprintf(data,"%16.10f \n",timeDiffs[i]);
-		fwrite(data,1,bytesToWrite,fHandle);
-	}
-	fclose(fHandle);
-
-	UA_String_deleteMembers(&reply);
-	UA_Array_delete(nodesToRead,&UA_TYPES[UA_TYPES_NODEID], nodesToReadSize);
-    UA_free(timeDiffs);
-
-	return 0;
-}
+	UA_Client *client = UA_Client_new();
+	UA_ClientNetworkLayer nl = ClientNetworkLayerTCP_new(UA_ConnectionConfig_standard);
+    //if(UA_Client_connect(client, UA_ConnectionConfig_standard, nl, "opc.tcp://localhost:48020") != UA_STATUSCODE_GOOD)
+	if(UA_Client_connect(client, UA_ConnectionConfig_standard, nl, "opc.tcp://localhost:16664") != UA_STATUSCODE_GOOD)
+    	return 0;
+
+    UA_NodeId node;
+    //node.namespaceIndex = 4;
+    //node.identifierType = UA_NODEIDTYPE_STRING;
+    //UA_String_copycstring("Demo.Static.Scalar.Int32", &node.identifier.string);
+    node.namespaceIndex = 1;
+    node.identifierType = UA_NODEIDTYPE_NUMERIC;
+    node.identifier.numeric = 76;
+
+    UA_ReadRequest read_req;
+    UA_ReadRequest_init(&read_req);
+
+    read_req.nodesToRead = UA_ReadValueId_new();
+    read_req.nodesToReadSize = 1;
+    read_req.nodesToRead[0].nodeId = node;
+    read_req.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE;
+    UA_ReadResponse read_resp = UA_Client_read(client, &read_req);
+    printf("the answer is: %i\n", *(UA_Int32*)read_resp.results[0].value.dataPtr);
+    UA_ReadRequest_deleteMembers(&read_req);
+    UA_ReadResponse_deleteMembers(&read_resp);
+
+    UA_Client_disconnect(client);
+    UA_Client_delete(client);
+}

+ 591 - 0
examples/client_legacy.c

@@ -0,0 +1,591 @@
+/*
+ C ECHO client example using sockets
+
+ This is an example client for internal benchmarks. It works, but is not ready
+ for serious use. We do not really check any of the returns from the server.
+ */
+#include <stdio.h> //printf
+#include <string.h> //strlen
+#include <sys/socket.h> //socket
+#include <arpa/inet.h> //inet_addr
+#include <unistd.h> // for close
+#include <stdlib.h> // pulls in declaration of malloc, free
+
+#ifdef NOT_AMALGATED
+    #include "ua_transport_generated.h"
+    #include "ua_types_encoding_binary.h"
+    #include "ua_util.h"
+#else
+    #include "open62541.h"
+#endif
+
+typedef struct ConnectionInfo {
+	UA_Int32 socket;
+	UA_UInt32 channelId;
+	UA_SequenceHeader sequenceHdr;
+	UA_NodeId authenticationToken;
+	UA_UInt32 tokenId;
+} ConnectionInfo;
+
+static UA_Int32 sendHello(UA_Int32 sock, UA_String *endpointURL) {
+
+	UA_TcpMessageHeader messageHeader;
+	messageHeader.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_HELF;
+
+	UA_TcpHelloMessage hello;
+	UA_String_copy(endpointURL, &hello.endpointUrl);
+	hello.maxChunkCount = 1;
+	hello.maxMessageSize = 16777216;
+	hello.protocolVersion = 0;
+	hello.receiveBufferSize = 65536;
+	hello.sendBufferSize = 65536;
+
+	messageHeader.messageSize = UA_TcpHelloMessage_calcSizeBinary((UA_TcpHelloMessage const*) &hello) +
+                                UA_TcpMessageHeader_calcSizeBinary((UA_TcpMessageHeader const*) &messageHeader);
+	UA_ByteString message;
+	UA_ByteString_newMembers(&message, messageHeader.messageSize);
+
+	size_t offset = 0;
+	UA_TcpMessageHeader_encodeBinary((UA_TcpMessageHeader const*) &messageHeader, &message, &offset);
+	UA_TcpHelloMessage_encodeBinary((UA_TcpHelloMessage const*) &hello, &message, &offset);
+
+	UA_Int32 sendret = send(sock, message.data, offset, 0);
+
+	UA_ByteString_deleteMembers(&message);
+	free(hello.endpointUrl.data);
+	if (sendret < 0)
+		return 1;
+	return 0;
+}
+
+static int sendOpenSecureChannel(UA_Int32 sock) {
+	UA_TcpMessageHeader msghdr;
+	msghdr.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_OPNF;
+
+	UA_UInt32 secureChannelId = 0;
+	UA_String securityPolicy;
+	UA_String_copycstring("http://opcfoundation.org/UA/SecurityPolicy#None", &securityPolicy);
+
+	UA_String senderCert;
+	senderCert.data = UA_NULL;
+	senderCert.length = -1;
+
+	UA_String receiverCertThumb;
+	receiverCertThumb.data = UA_NULL;
+	receiverCertThumb.length = -1;
+
+	UA_UInt32 sequenceNumber = 51;
+
+	UA_UInt32 requestId = 1;
+
+	UA_NodeId type;
+	type.identifier.numeric = 446; // id of opensecurechannelrequest
+	type.identifierType = UA_NODEIDTYPE_NUMERIC;
+	type.namespaceIndex = 0;
+
+	UA_OpenSecureChannelRequest opnSecRq;
+	UA_OpenSecureChannelRequest_init(&opnSecRq);
+	opnSecRq.requestHeader.timestamp = UA_DateTime_now();
+	UA_ByteString_newMembers(&opnSecRq.clientNonce, 1);
+	opnSecRq.clientNonce.data[0] = 0;
+	opnSecRq.clientProtocolVersion = 0;
+	opnSecRq.requestedLifetime = 30000;
+	opnSecRq.securityMode = UA_MESSAGESECURITYMODE_NONE;
+	opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_ISSUE;
+	opnSecRq.requestHeader.authenticationToken.identifier.numeric = 10;
+	opnSecRq.requestHeader.authenticationToken.identifierType = UA_NODEIDTYPE_NUMERIC;
+	opnSecRq.requestHeader.authenticationToken.namespaceIndex = 10;
+
+	msghdr.messageSize = 135; // todo: compute the message size from the actual content
+
+	UA_ByteString message;
+	UA_ByteString_newMembers(&message, 1000);
+	size_t offset = 0;
+	UA_TcpMessageHeader_encodeBinary(&msghdr, &message, &offset);
+	UA_UInt32_encodeBinary(&secureChannelId, &message, &offset);
+	UA_String_encodeBinary(&securityPolicy, &message, &offset);
+	UA_String_encodeBinary(&senderCert, &message, &offset);
+	UA_String_encodeBinary(&receiverCertThumb, &message, &offset);
+	UA_UInt32_encodeBinary(&sequenceNumber, &message, &offset);
+	UA_UInt32_encodeBinary(&requestId, &message, &offset);
+	UA_NodeId_encodeBinary(&type, &message, &offset);
+	UA_OpenSecureChannelRequest_encodeBinary(&opnSecRq, &message, &offset);
+
+    UA_OpenSecureChannelRequest_deleteMembers(&opnSecRq);
+	UA_String_deleteMembers(&securityPolicy);
+
+	UA_Int32 sendret = send(sock, message.data, offset, 0);
+	UA_ByteString_deleteMembers(&message);
+	if (sendret < 0) {
+		printf("send opensecurechannel failed");
+		return 1;
+	}
+	return 0;
+}
+
+static UA_Int32 sendCreateSession(UA_Int32 sock, UA_UInt32 channelId, UA_UInt32 tokenId, UA_UInt32 sequenceNumber,
+                                  UA_UInt32 requestId, UA_String *endpointUrl) {
+    UA_ByteString message;
+	UA_ByteString_newMembers(&message, 65536);
+	UA_UInt32 tmpChannelId = channelId;
+	size_t offset = 0;
+
+	UA_TcpMessageHeader msghdr;
+	msghdr.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_MSGF;
+
+	UA_NodeId type;
+	type.identifier.numeric = 461;
+	type.identifierType = UA_NODEIDTYPE_NUMERIC;
+	type.namespaceIndex = 0;
+
+	UA_CreateSessionRequest rq;
+    UA_CreateSessionRequest_init(&rq);
+	rq.requestHeader.requestHandle = 1;
+	rq.requestHeader.timestamp = UA_DateTime_now();
+	rq.requestHeader.timeoutHint = 10000;
+	rq.requestHeader.authenticationToken.identifier.numeric = 10;
+	rq.requestHeader.authenticationToken.identifierType = UA_NODEIDTYPE_NUMERIC;
+	rq.requestHeader.authenticationToken.namespaceIndex = 10;
+	UA_String_copy(endpointUrl, &rq.endpointUrl);
+	UA_String_copycstring("mysession", &rq.sessionName);
+	UA_String_copycstring("abcd", &rq.clientCertificate);
+	UA_ByteString_newMembers(&rq.clientNonce, 1);
+	rq.clientNonce.data[0] = 0;
+	rq.requestedSessionTimeout = 1200000;
+	rq.maxResponseMessageSize = UA_INT32_MAX;
+
+	msghdr.messageSize = 16 + UA_TcpMessageHeader_calcSizeBinary(&msghdr) + UA_NodeId_calcSizeBinary(&type) +
+                         UA_CreateSessionRequest_calcSizeBinary(&rq);
+
+	UA_TcpMessageHeader_encodeBinary(&msghdr, &message, &offset);
+	UA_UInt32_encodeBinary(&tmpChannelId, &message, &offset);
+	UA_UInt32_encodeBinary(&tokenId, &message, &offset);
+	UA_UInt32_encodeBinary(&sequenceNumber, &message, &offset);
+	UA_UInt32_encodeBinary(&requestId, &message, &offset);
+	UA_NodeId_encodeBinary(&type, &message, &offset);
+	UA_CreateSessionRequest_encodeBinary(&rq, &message, &offset);
+
+	UA_Int32 sendret = send(sock, message.data, offset, 0);
+	UA_ByteString_deleteMembers(&message);
+	UA_CreateSessionRequest_deleteMembers(&rq);
+	if (sendret < 0) {
+		printf("send opensecurechannel failed");
+		return 1;
+	}
+	return 0;
+}
+
+static UA_Int32 closeSession(ConnectionInfo *connectionInfo) {
+	size_t offset = 0;
+
+	UA_ByteString message;
+	UA_ByteString_newMembers(&message, 65536);
+
+	UA_CloseSessionRequest rq;
+    UA_CloseSessionRequest_init(&rq);
+
+	rq.requestHeader.requestHandle = 1;
+	rq.requestHeader.timestamp = UA_DateTime_now();
+	rq.requestHeader.timeoutHint = 10000;
+	rq.requestHeader.authenticationToken.identifier.numeric = 10;
+	rq.requestHeader.authenticationToken.identifierType = UA_NODEIDTYPE_NUMERIC;
+	rq.requestHeader.authenticationToken.namespaceIndex = 10;
+    rq.deleteSubscriptions = UA_TRUE;
+
+	UA_TcpMessageHeader msghdr;
+	msghdr.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_MSGF;
+
+	UA_NodeId type;
+	type.identifier.numeric = 473;
+	type.identifierType = UA_NODEIDTYPE_NUMERIC;
+	type.namespaceIndex = 0;
+
+	msghdr.messageSize = 16 + UA_TcpMessageHeader_calcSizeBinary(&msghdr) + UA_NodeId_calcSizeBinary(&type) +
+                         UA_CloseSessionRequest_calcSizeBinary(&rq);
+
+	UA_TcpMessageHeader_encodeBinary(&msghdr, &message, &offset);
+	UA_UInt32_encodeBinary(&connectionInfo->channelId, &message, &offset);
+	UA_UInt32_encodeBinary(&connectionInfo->tokenId, &message, &offset);
+	UA_UInt32_encodeBinary(&connectionInfo->sequenceHdr.sequenceNumber, &message, &offset);
+	UA_UInt32_encodeBinary(&connectionInfo->sequenceHdr.requestId, &message, &offset);
+	UA_NodeId_encodeBinary(&type, &message, &offset);
+	UA_CloseSessionRequest_encodeBinary(&rq, &message, &offset);
+
+	UA_Int32 sendret = send(connectionInfo->socket, message.data, offset, 0);
+	UA_ByteString_deleteMembers(&message);
+	UA_CloseSessionRequest_deleteMembers(&rq);
+	if(sendret < 0) {
+		printf("send closesessionrequest failed");
+		return 1;
+	}
+
+    return 0;
+}
+
+static UA_Int32 closeSecureChannel(ConnectionInfo *connectionInfo) {
+	size_t offset = 0;
+
+	UA_ByteString message;
+	UA_ByteString_newMembers(&message, 65536);
+
+	UA_CloseSecureChannelRequest rq;
+    UA_CloseSecureChannelRequest_init(&rq);
+
+	rq.requestHeader.requestHandle = 1;
+	rq.requestHeader.timestamp = UA_DateTime_now();
+	rq.requestHeader.timeoutHint = 10000;
+	rq.requestHeader.authenticationToken.identifier.numeric = 10;
+	rq.requestHeader.authenticationToken.identifierType = UA_NODEIDTYPE_NUMERIC;
+	rq.requestHeader.authenticationToken.namespaceIndex = 10;
+
+	UA_TcpMessageHeader msghdr;
+	msghdr.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_CLOF;
+
+	msghdr.messageSize = 4 + UA_TcpMessageHeader_calcSizeBinary(&msghdr) +
+                         UA_CloseSecureChannelRequest_calcSizeBinary(&rq);
+
+	UA_TcpMessageHeader_encodeBinary(&msghdr, &message, &offset);
+	UA_UInt32_encodeBinary(&connectionInfo->channelId, &message, &offset);
+	UA_CloseSecureChannelRequest_encodeBinary(&rq, &message, &offset);
+
+	UA_Int32 sendret = send(connectionInfo->socket, message.data, offset, 0);
+	UA_ByteString_deleteMembers(&message);
+	UA_CloseSecureChannelRequest_deleteMembers(&rq);
+	if(sendret < 0) {
+		printf("send CloseSecureChannelRequest failed");
+		return 1;
+	}
+
+    return 0;
+}
+
+static UA_Int32 sendActivateSession(UA_Int32 sock, UA_UInt32 channelId, UA_UInt32 tokenId, UA_UInt32 sequenceNumber,
+                                    UA_UInt32 requestId, UA_NodeId authenticationToken) {
+	UA_ByteString *message = UA_ByteString_new();
+	UA_ByteString_newMembers(message, 65536);
+	UA_UInt32 tmpChannelId = channelId;
+	size_t offset = 0;
+
+	UA_TcpMessageHeader msghdr;
+	msghdr.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_MSGF;
+	msghdr.messageSize = 86;
+
+	UA_NodeId type;
+	type.identifier.numeric = 467;
+	type.identifierType = UA_NODEIDTYPE_NUMERIC;
+	type.namespaceIndex = 0;
+
+	UA_ActivateSessionRequest rq;
+	UA_ActivateSessionRequest_init(&rq);
+	rq.requestHeader.requestHandle = 2;
+	rq.requestHeader.authenticationToken = authenticationToken;
+	rq.requestHeader.timestamp = UA_DateTime_now();
+	rq.requestHeader.timeoutHint = 10000;
+    
+	msghdr.messageSize  = 16 + UA_TcpMessageHeader_calcSizeBinary(&msghdr) + UA_NodeId_calcSizeBinary(&type) +
+                          UA_ActivateSessionRequest_calcSizeBinary(&rq);
+
+	UA_TcpMessageHeader_encodeBinary(&msghdr, message, &offset);
+	UA_UInt32_encodeBinary(&tmpChannelId, message, &offset);
+	UA_UInt32_encodeBinary(&tokenId, message, &offset);
+	UA_UInt32_encodeBinary(&sequenceNumber, message, &offset);
+	UA_UInt32_encodeBinary(&requestId, message, &offset);
+	UA_NodeId_encodeBinary(&type, message, &offset);
+	UA_ActivateSessionRequest_encodeBinary(&rq, message, &offset);
+
+	UA_Int32 sendret = send(sock, message->data, offset, 0);
+	UA_ByteString_delete(message);
+
+	if (sendret < 0) {
+		printf("send opensecurechannel failed");
+		return 1;
+	}
+	return 0;
+
+}
+
+static UA_Int64 sendReadRequest(ConnectionInfo *connectionInfo, UA_Int32 nodeIds_size,UA_NodeId* nodeIds){
+		/*UA_Int32 sock, UA_UInt32 channelId, UA_UInt32 tokenId, UA_UInt32 sequenceNumber, UA_UInt32 requestId,
+                         UA_NodeId authenticationToken, UA_Int32 nodeIds_size,UA_NodeId* nodeIds) {
+                         */
+	UA_ByteString *message = UA_ByteString_new();
+	UA_ByteString_newMembers(message, 65536);
+	UA_UInt32 tmpChannelId = connectionInfo->channelId;
+	size_t offset = 0;
+
+	UA_TcpMessageHeader msghdr;
+	msghdr.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_MSGF;
+
+	UA_NodeId type;
+	type.identifier.numeric = 631;
+	type.identifierType = UA_NODEIDTYPE_NUMERIC;
+	type.namespaceIndex = 0;
+
+	UA_ReadRequest rq;
+	UA_ReadRequest_init(&rq);
+	rq.maxAge = 0;
+	rq.nodesToRead = UA_Array_new(&UA_TYPES[UA_TYPES_READVALUEID], nodeIds_size);
+	rq.nodesToReadSize = 1;
+	for(UA_Int32 i=0;i<nodeIds_size;i++) {
+		UA_ReadValueId_init(&(rq.nodesToRead[i]));
+		rq.nodesToRead[i].attributeId = 6; //WriteMask
+		UA_NodeId_init(&(rq.nodesToRead[i].nodeId));
+		rq.nodesToRead[i].nodeId = nodeIds[i];
+		UA_QualifiedName_init(&(rq.nodesToRead[0].dataEncoding));
+	}
+	rq.requestHeader.timeoutHint = 10000;
+	rq.requestHeader.timestamp = UA_DateTime_now();
+	rq.requestHeader.authenticationToken = connectionInfo->authenticationToken;
+	rq.timestampsToReturn = 0x03;
+	rq.requestHeader.requestHandle = 1 + connectionInfo->sequenceHdr.requestId;
+
+	msghdr.messageSize = 16 + UA_TcpMessageHeader_calcSizeBinary(&msghdr) + UA_NodeId_calcSizeBinary(&type) +
+                         UA_ReadRequest_calcSizeBinary(&rq);
+
+	UA_TcpMessageHeader_encodeBinary(&msghdr,message,&offset);
+	UA_UInt32_encodeBinary(&tmpChannelId, message, &offset);
+	UA_UInt32_encodeBinary(&connectionInfo->tokenId, message, &offset);
+	UA_UInt32_encodeBinary(&connectionInfo->sequenceHdr.sequenceNumber, message, &offset);
+	UA_UInt32_encodeBinary(&connectionInfo->sequenceHdr.requestId, message, &offset);
+	UA_NodeId_encodeBinary(&type,message,&offset);
+	UA_ReadRequest_encodeBinary(&rq, message, &offset);
+
+	UA_DateTime tic = UA_DateTime_now();
+	UA_Int32 sendret = send(connectionInfo->socket, message->data, offset, 0);
+	UA_Array_delete(rq.nodesToRead, &UA_TYPES[UA_TYPES_READVALUEID], nodeIds_size);
+	UA_ByteString_delete(message);
+
+	if (sendret < 0) {
+		printf("send readrequest failed");
+		return 1;
+	}
+	return tic;
+}
+
+static int ua_client_connectUA(char* ipaddress,int port, UA_String *endpointUrl, ConnectionInfo *connectionInfo,
+                               UA_Boolean stateless, UA_Boolean udp) {
+	UA_ByteString reply;
+	UA_ByteString_newMembers(&reply, 65536);
+	int sock;
+	struct sockaddr_in server;
+	//Create socket
+	if(udp==UA_TRUE){
+		sock = socket(AF_INET, SOCK_DGRAM, 0);
+	}else{
+		sock = socket(AF_INET, SOCK_STREAM, 0);
+	}
+	if(sock == -1) {
+		printf("Could not create socket");
+        return 1;
+    }
+	server.sin_addr.s_addr = inet_addr(ipaddress);
+	server.sin_family = AF_INET;
+	server.sin_port = htons(port);
+
+	if(connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) {
+			perror("connect failed. Error");
+			return 1;
+		}
+		connectionInfo->socket = sock;
+
+		if(stateless){
+			UA_NodeId_init(&connectionInfo->authenticationToken);
+			connectionInfo->channelId=0;
+			UA_SequenceHeader_init(&connectionInfo->sequenceHdr);
+			connectionInfo->tokenId=0;
+			return 0;
+		}else{
+			sendHello(sock, endpointUrl);
+			recv(sock, reply.data, reply.length, 0);
+			sendOpenSecureChannel(sock);
+			recv(sock, reply.data, reply.length, 0);
+
+			size_t recvOffset = 0;
+			UA_TcpMessageHeader msghdr;
+			UA_TcpMessageHeader_decodeBinary(&reply, &recvOffset, &msghdr);
+
+			UA_AsymmetricAlgorithmSecurityHeader asymHeader;
+			UA_NodeId rspType;
+			UA_OpenSecureChannelResponse openSecChannelRsp;
+			UA_UInt32_decodeBinary(&reply, &recvOffset, &connectionInfo->channelId);
+			UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(&reply,&recvOffset,&asymHeader);
+			UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
+			UA_SequenceHeader_decodeBinary(&reply,&recvOffset,&connectionInfo->sequenceHdr);
+			UA_NodeId_decodeBinary(&reply,&recvOffset,&rspType);
+			UA_OpenSecureChannelResponse_decodeBinary(&reply,&recvOffset,&openSecChannelRsp);
+			connectionInfo->tokenId = openSecChannelRsp.securityToken.tokenId;
+
+			sendCreateSession(sock, connectionInfo->channelId, openSecChannelRsp.securityToken.tokenId, 52, 2, endpointUrl);
+			recv(sock, reply.data, reply.length, 0);
+
+			UA_NodeId messageType;
+			recvOffset = 24;
+			UA_NodeId_decodeBinary(&reply,&recvOffset,&messageType);
+			UA_CreateSessionResponse createSessionResponse;
+			UA_CreateSessionResponse_decodeBinary(&reply,&recvOffset,&createSessionResponse);
+			connectionInfo->authenticationToken = createSessionResponse.authenticationToken;
+			sendActivateSession(sock, connectionInfo->channelId, connectionInfo->tokenId, 53, 3,
+					connectionInfo->authenticationToken);
+			recv(sock, reply.data, reply.length, 0);
+
+			UA_OpenSecureChannelResponse_deleteMembers(&openSecChannelRsp);
+
+			UA_String_deleteMembers(&reply);
+			UA_CreateSessionResponse_deleteMembers(&createSessionResponse);
+			return 0;
+		}
+}
+
+int main(int argc, char *argv[]) {
+	int defaultParams = argc < 8;
+
+	//start parameters
+	if(defaultParams) {
+		printf("1st parameter: number of nodes to read \n");
+		printf("2nd parameter: number of read-tries \n");
+		printf("3rd parameter: name of the file to save measurement data \n");
+		printf("4th parameter: 1 = read same node, 0 = read different nodes \n");
+		printf("5th parameter: ip adress \n");
+		printf("6th parameter: port \n");
+		printf("7th parameter: 0=stateful, 1=stateless\n");
+		printf("8th parameter: 0=tcp, 1=udp (only with stateless calls)\n");
+		printf("\nUsing default parameters. \n");
+	}
+
+	UA_UInt32 nodesToReadSize;
+	UA_UInt32 tries;
+	UA_Boolean alwaysSameNode;
+	UA_ByteString reply;
+	UA_ByteString_newMembers(&reply, 65536);
+	UA_Boolean stateless;
+	UA_Boolean udp;
+
+	if(defaultParams)
+		nodesToReadSize = 1;
+	else
+		nodesToReadSize = atoi(argv[1]);
+
+	if(defaultParams)
+		tries= 2;
+	else
+		tries = (UA_UInt32) atoi(argv[2]);
+
+	if(defaultParams){
+		alwaysSameNode = UA_TRUE;
+	}else{
+		if(atoi(argv[4]) != 0)
+			alwaysSameNode = UA_TRUE;
+		else
+			alwaysSameNode = UA_FALSE;
+	}
+
+	if(defaultParams){
+		stateless = UA_FALSE;
+	}else{
+		if(atoi(argv[7]) != 0)
+			stateless = UA_TRUE;
+		else
+			stateless = UA_FALSE;
+	}
+
+	if(defaultParams){
+		udp = UA_FALSE;
+	}else{
+		if(atoi(argv[8]) != 0)
+			udp = UA_TRUE;
+		else
+			udp = UA_FALSE;
+	}
+
+
+
+    //Connect to remote server
+	UA_String endpoint;
+	UA_String_copycstring("none",&endpoint);
+	ConnectionInfo connectionInfo;
+
+
+/* REQUEST START*/
+    UA_NodeId *nodesToRead;
+    nodesToRead = UA_Array_new(&UA_TYPES[UA_TYPES_NODEID], 1);
+
+	for(UA_UInt32 i = 0; i<1; i++) {
+		if(alwaysSameNode)
+			nodesToRead[i].identifier.numeric = 2253; //ask always the same node
+		else
+			nodesToRead[i].identifier.numeric = 19000 +i;
+		nodesToRead[i].identifierType = UA_NODEIDTYPE_NUMERIC;
+		nodesToRead[i].namespaceIndex = 0;
+	}
+
+	UA_DateTime tic, toc;
+	UA_Double *timeDiffs;
+	UA_Int32 received;
+	timeDiffs = UA_Array_new(&UA_TYPES[UA_TYPES_DOUBLE], tries);
+	UA_Double sum = 0;
+
+	for(UA_UInt32 i = 0; i < tries; i++) {
+		//if(stateless || (!stateless && i==0)){
+		tic = UA_DateTime_now();
+			if(defaultParams){
+				if(ua_client_connectUA("127.0.0.1",atoi("16664"),&endpoint,&connectionInfo,stateless,udp) != 0){
+					return 0;
+				}
+			}else{
+				if(ua_client_connectUA(argv[5],atoi(argv[6]),&endpoint,&connectionInfo,stateless,udp) != 0){
+					return 0;
+				}
+			}
+		//}
+		for(UA_UInt32 i = 0; i < nodesToReadSize; i++) {
+		sendReadRequest(&connectionInfo,1,nodesToRead);
+		received = recv(connectionInfo.socket, reply.data, 2000, 0);
+		}
+
+		if(!stateless){
+		closeSession(&connectionInfo);
+		recv(connectionInfo.socket, reply.data, 2000, 0);
+
+		closeSecureChannel(&connectionInfo);
+		}
+		//if(stateless || (!stateless && i==tries-1)){
+			close(connectionInfo.socket);
+		//}
+		toc = UA_DateTime_now() - tic;
+		timeDiffs[i] = (UA_Double)toc/(UA_Double)1e4;
+		sum = sum + timeDiffs[i];
+	}
+/* REQUEST END*/
+
+
+	UA_Double mean = sum / tries;
+	printf("mean time for handling request: %16.10f ms \n",mean);
+
+	if(received>0)
+		printf("received: %i\n",received); // dummy
+
+	//save to file
+	char data[100];
+	const char flag = 'a';
+	FILE* fHandle = UA_NULL;
+	if (defaultParams) {
+		fHandle =  fopen("client.log", &flag);
+	}else{
+		fHandle =  fopen(argv[3], &flag);
+	}
+	//header
+
+	UA_Int32 bytesToWrite = sprintf(data, "measurement %s in ms, nodesToRead %d \n", argv[3], 1);
+	fwrite(data,1,bytesToWrite,fHandle);
+	for(UA_UInt32 i=0;i<tries;i++) {
+		bytesToWrite = sprintf(data,"%16.10f \n",timeDiffs[i]);
+		fwrite(data,1,bytesToWrite,fHandle);
+	}
+	fclose(fHandle);
+
+	UA_String_deleteMembers(&reply);
+	UA_Array_delete(nodesToRead,&UA_TYPES[UA_TYPES_NODEID], 1);
+    UA_free(timeDiffs);
+
+	return 0;
+}

+ 219 - 26
examples/networklayer_tcp.c

@@ -17,6 +17,7 @@
 #include <netinet/tcp.h>
 #include <sys/socketvar.h>
 #include <sys/ioctl.h>
+#include <netdb.h> //gethostbyname for the client
 #define __USE_BSD
 #include <unistd.h> // read, write, close
 #include <arpa/inet.h>
@@ -34,18 +35,24 @@
 #include <urcu/uatomic.h>
 #endif
 
-#define MAXBACKLOG 100
 
-struct Networklayer_TCP;
+
+struct ServerNetworklayer_TCP;
 
 /* Forwarded to the server as a (UA_Connection) and used for callbacks back into
    the networklayer */
 typedef struct {
 	UA_Connection connection;
 	UA_Int32 sockfd;
-	struct NetworkLayerTCP *layer;
+	void *layer;
 } TCPConnection;
 
+/***************************/
+/* Server NetworkLayer TCP */
+/***************************/
+
+#define MAXBACKLOG 100
+
 /* Internal mapping of sockets to connections */
 typedef struct {
     TCPConnection *connection;
@@ -56,7 +63,7 @@ typedef struct {
 #endif
 } ConnectionLink;
 
-typedef struct NetworkLayerTCP {
+typedef struct ServerNetworkLayerTCP {
 	UA_ConnectionConfig conf;
 	fd_set fdset;
 #ifdef _WIN32
@@ -81,7 +88,16 @@ typedef struct NetworkLayerTCP {
         struct deleteLink *next;
 
     } *deleteLinkList;
-} NetworkLayerTCP;
+} ServerNetworkLayerTCP;
+
+typedef struct ClientNetworkLayerTCP {
+	fd_set read_fds;
+#ifdef _WIN32
+	UA_UInt32 sockfd;
+#else
+	UA_Int32 sockfd;
+#endif
+} ClientNetworkLayerTCP;
 
 static UA_StatusCode setNonBlocking(int sockid) {
 #ifdef _WIN32
@@ -101,7 +117,7 @@ static void freeConnectionCallback(UA_Server *server, TCPConnection *connection)
 }
 
 // after every select, reset the set of sockets we want to listen on
-static void setFDSet(NetworkLayerTCP *layer) {
+static void setFDSet(ServerNetworkLayerTCP *layer) {
 	FD_ZERO(&layer->fdset);
 	FD_SET(layer->serversockfd, &layer->fdset);
 	layer->highestfd = layer->serversockfd;
@@ -116,7 +132,7 @@ static void setFDSet(NetworkLayerTCP *layer) {
 void closeConnection(TCPConnection *handle);
 void writeCallback(TCPConnection *handle, UA_ByteStringArray gather_buf);
 
-static UA_StatusCode NetworkLayerTCP_add(NetworkLayerTCP *layer, UA_Int32 newsockfd) {
+static UA_StatusCode ServerNetworkLayerTCP_add(ServerNetworkLayerTCP *layer, UA_Int32 newsockfd) {
     setNonBlocking(newsockfd);
     TCPConnection *c = malloc(sizeof(TCPConnection));
 	if(!c)
@@ -141,7 +157,7 @@ static UA_StatusCode NetworkLayerTCP_add(NetworkLayerTCP *layer, UA_Int32 newsoc
 }
 
 /* Removes all connections from the network layer. Returns the work items to close them properly. */
-static UA_UInt32 removeAllConnections(NetworkLayerTCP *layer, UA_WorkItem **returnWork) {
+static UA_UInt32 removeAllConnections(ServerNetworkLayerTCP *layer, UA_WorkItem **returnWork) {
     UA_WorkItem *work;
 	if (layer->conLinksSize <= 0 || !(work = malloc(sizeof(UA_WorkItem)*layer->conLinksSize))) {
 		*returnWork = NULL;
@@ -187,12 +203,14 @@ void closeConnection(TCPConnection *handle) {
 	shutdown(handle->sockfd,2);
 	CLOSESOCKET(handle->sockfd);
 
+    ServerNetworkLayerTCP *layer = (ServerNetworkLayerTCP*)handle->layer;
+
     // Remove the link later in the main thread
     struct deleteLink *d = malloc(sizeof(struct deleteLink));
     d->sockfd = handle->sockfd;
     while(1) {
-        d->next = handle->layer->deleteLinkList;
-        if(uatomic_cmpxchg(&handle->layer->deleteLinkList, d->next, d) == d->next)
+        d->next = layer->deleteLinkList;
+        if(uatomic_cmpxchg(&layer->deleteLinkList, d->next, d) == d->next)
             break;
     }
 }
@@ -212,8 +230,9 @@ void closeConnection(TCPConnection *handle) {
 
     // Remove the link later in the main thread
     d->sockfd = handle->sockfd;
-    d->next = handle->layer->deleteLinkList;
-    handle->layer->deleteLinkList = d;
+    ServerNetworkLayerTCP *layer = (ServerNetworkLayerTCP*)handle->layer;
+    d->next = layer->deleteLinkList;
+    layer->deleteLinkList = d;
 }
 #endif
 
@@ -261,7 +280,7 @@ void writeCallback(TCPConnection *handle, UA_ByteStringArray gather_buf) {
 #endif
 }
 
-static UA_StatusCode NetworkLayerTCP_start(NetworkLayerTCP *layer, UA_Logger *logger) {
+static UA_StatusCode ServerNetworkLayerTCP_start(ServerNetworkLayerTCP *layer, UA_Logger *logger) {
 #ifdef _WIN32
 	WORD wVersionRequested;
 	WSADATA wsaData;
@@ -306,7 +325,7 @@ static UA_StatusCode NetworkLayerTCP_start(NetworkLayerTCP *layer, UA_Logger *lo
     return UA_STATUSCODE_GOOD;
 }
 
-static UA_Int32 NetworkLayerTCP_getWork(NetworkLayerTCP *layer, UA_WorkItem **workItems,
+static UA_Int32 ServerNetworkLayerTCP_getWork(ServerNetworkLayerTCP *layer, UA_WorkItem **workItems,
                                         UA_UInt16 timeout) {
     UA_WorkItem *items = (void*)0;
     UA_Int32 itemsCount = removeAllConnections(layer, &items);
@@ -325,10 +344,10 @@ static UA_Int32 NetworkLayerTCP_getWork(NetworkLayerTCP *layer, UA_WorkItem **wo
 		struct sockaddr_in cli_addr;
 		socklen_t cli_len = sizeof(cli_addr);
 		int newsockfd = accept(layer->serversockfd, (struct sockaddr *) &cli_addr, &cli_len);
-		int i = 1;
+		int i = 1;
 		setsockopt(newsockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&i, sizeof(i));
 		if (newsockfd >= 0)
-			NetworkLayerTCP_add(layer, newsockfd);
+			ServerNetworkLayerTCP_add(layer, newsockfd);
 	}
     
     items = realloc(items, sizeof(UA_WorkItem)*(itemsCount+resultsize));
@@ -374,16 +393,16 @@ static UA_Int32 NetworkLayerTCP_getWork(NetworkLayerTCP *layer, UA_WorkItem **wo
     return j;
 }
 
-static UA_Int32 NetworkLayerTCP_stop(NetworkLayerTCP * layer, UA_WorkItem **workItems) {
-	for(UA_Int32 i = 0;i < layer->conLinksSize;i++)
-        closeConnection(layer->conLinks[i].connection);
+static UA_Int32 ServerNetworkLayerTCP_stop(ServerNetworkLayerTCP * layer, UA_WorkItem **workItems) {
+	for(UA_Int32 index = 0;index < layer->conLinksSize;index++)
+        closeConnection(layer->conLinks[index].connection);
 #ifdef _WIN32
 	WSACleanup();
 #endif
     return removeAllConnections(layer, workItems);
 }
 
-static void NetworkLayerTCP_delete(NetworkLayerTCP *layer) {
+static void ServerNetworkLayerTCP_delete(ServerNetworkLayerTCP *layer) {
 	UA_String_deleteMembers(&layer->discoveryUrl);
 	for(UA_Int32 i=0;i<layer->conLinksSize;++i){
 		free(layer->conLinks[i].connection);
@@ -393,7 +412,7 @@ static void NetworkLayerTCP_delete(NetworkLayerTCP *layer) {
 }
 
 UA_ServerNetworkLayer ServerNetworkLayerTCP_new(UA_ConnectionConfig conf, UA_UInt32 port) {
-    NetworkLayerTCP *tcplayer = malloc(sizeof(NetworkLayerTCP));
+    ServerNetworkLayerTCP *tcplayer = malloc(sizeof(ServerNetworkLayerTCP));
 	tcplayer->conf = conf;
 	tcplayer->conLinksSize = 0;
 	tcplayer->conLinks = NULL;
@@ -405,11 +424,185 @@ UA_ServerNetworkLayer ServerNetworkLayerTCP_new(UA_ConnectionConfig conf, UA_UIn
 
     UA_ServerNetworkLayer nl;
     nl.nlHandle = tcplayer;
-    nl.start = (UA_StatusCode (*)(void*, UA_Logger *logger))NetworkLayerTCP_start;
-    nl.getWork = (UA_Int32 (*)(void*, UA_WorkItem**, UA_UInt16)) NetworkLayerTCP_getWork;
-    nl.stop = (UA_Int32 (*)(void*, UA_WorkItem**)) NetworkLayerTCP_stop;
-    nl.free = (void (*)(void*))NetworkLayerTCP_delete;
-    nl.discoveryUrl = &tcplayer->discoveryUrl;
+    nl.start = (UA_StatusCode (*)(void*, UA_Logger *logger))ServerNetworkLayerTCP_start;
+    nl.getWork = (UA_Int32 (*)(void*, UA_WorkItem**, UA_UInt16))ServerNetworkLayerTCP_getWork;
+    nl.stop = (UA_Int32 (*)(void*, UA_WorkItem**))ServerNetworkLayerTCP_stop;
+    nl.free = (void (*)(void*))ServerNetworkLayerTCP_delete;
+	nl.discoveryUrl = &tcplayer->discoveryUrl;
 
     return nl;
 }
+
+/***************************/
+/* Client NetworkLayer TCP */
+/***************************/
+
+static UA_StatusCode ClientNetworkLayerTCP_connect(const UA_String endpointUrl, ClientNetworkLayerTCP *resultHandle) {
+	if(endpointUrl.length < 11 || endpointUrl.length >= 512) {
+        printf("server url size invalid\n");
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
+
+    if(strncmp((char*)endpointUrl.data, "opc.tcp://", 10) != 0) {
+        printf("server url does not begin with opc.tcp://\n");
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
+
+    //this is somewhat ugly, but atoi needs a c string
+    char cstringEndpointUrl[endpointUrl.length+1];
+    memcpy(cstringEndpointUrl, endpointUrl.data, endpointUrl.length);
+    cstringEndpointUrl[endpointUrl.length+1] = '0';
+
+    UA_UInt16 portpos = 9;
+    UA_UInt16 port = 0;
+    for(;portpos < endpointUrl.length; portpos++) {
+        if(endpointUrl.data[portpos] == ':') {
+            port = atoi(&cstringEndpointUrl[portpos+1]);
+            break;
+        }
+    }
+    if(port == 0) {
+        printf("port invalid");
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
+    
+    char hostname[512];
+    for(int i=10; i < portpos; i++)
+        hostname[i-10] = endpointUrl.data[i];
+    hostname[portpos-10] = 0;
+
+#ifdef _WIN32
+    UA_UInt32 sock = 0;
+#else
+    UA_Int32 sock = 0;
+#endif
+#ifdef _WIN32
+	WORD wVersionRequested;
+	WSADATA wsaData;
+	wVersionRequested = MAKEWORD(2, 2);
+	WSAStartup(wVersionRequested, &wsaData);
+    if((sock = socket(PF_INET, SOCK_STREAM,0)) == INVALID_SOCKET) {
+#else
+    if((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+#endif
+		printf("Could not create socket\n");
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
+
+    struct hostent *server;
+    server = gethostbyname(hostname);
+    if (server == NULL) {
+        printf("DNS lookup of %s failed\n", hostname);
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
+
+	struct sockaddr_in server_addr;
+
+    memset(&server_addr, 0, sizeof(server_addr));
+    memcpy((char *)&server_addr.sin_addr.s_addr,
+    	 (char *)server->h_addr_list[0],
+         server->h_length);
+
+	server_addr.sin_family = AF_INET;
+	server_addr.sin_port = htons(port);
+
+	if(connect(sock, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
+        printf("Connect failed.\n");
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
+
+    //if(setNonBlocking(*sock) != UA_STATUSCODE_GOOD) {
+    //    printf("Could not switch to nonblocking.\n");
+    //	FINALLY
+    //    return UA_STATUSCODE_BADINTERNALERROR;
+    //}
+
+    resultHandle->sockfd = sock;
+    return UA_STATUSCODE_GOOD;
+}
+
+static void ClientNetworkLayerTCP_disconnect(ClientNetworkLayerTCP* handle) {
+	CLOSESOCKET(handle->sockfd);
+}
+
+static UA_StatusCode ClientNetworkLayerTCP_send(ClientNetworkLayerTCP *handle, UA_ByteStringArray gather_buf) {
+	UA_UInt32 total_len = 0, nWritten = 0;
+#ifdef _WIN32
+	LPWSABUF buf = _alloca(gather_buf.stringsSize * sizeof(WSABUF));
+	int result = 0;
+	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;
+	}
+	while(nWritten < total_len) {
+		UA_UInt32 n = 0;
+		do {
+			result = WSASend(handle->sockfd, buf, gather_buf.stringsSize ,
+                             (LPDWORD)&n, 0, NULL, NULL);
+			if(result != 0)
+				printf("Error WSASend, code: %d \n", WSAGetLastError());
+		} while(errno == EINTR);
+		nWritten += n;
+	}
+#else
+	struct iovec iov[gather_buf.stringsSize];
+	for(UA_UInt32 i=0;i<gather_buf.stringsSize;i++) {
+		iov[i] = (struct iovec) {.iov_base = gather_buf.strings[i].data,
+                                 .iov_len = gather_buf.strings[i].length};
+		total_len += gather_buf.strings[i].length;
+	}
+	struct msghdr message = {.msg_name = NULL, .msg_namelen = 0, .msg_iov = iov,
+							 .msg_iovlen = gather_buf.stringsSize, .msg_control = NULL,
+							 .msg_controllen = 0, .msg_flags = 0};
+	while (nWritten < total_len) {
+        int n = sendmsg(handle->sockfd, &message, 0);
+        if(n <= -1)
+            return UA_STATUSCODE_BADINTERNALERROR;
+        nWritten += n;
+	}
+#endif
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode ClientNetworkLayerTCP_awaitResponse(ClientNetworkLayerTCP *handle, UA_ByteString *response,
+                                                         UA_UInt32 timeout) {
+    FD_ZERO(&handle->read_fds);
+    FD_SET(handle->sockfd, &handle->read_fds);//tcp socket
+    struct timeval tmptv = {0, timeout};
+    tmptv.tv_sec = 10;
+    int ret = select(handle->sockfd+1, &handle->read_fds, NULL, NULL, &tmptv);
+    if(ret <= -1)
+        return UA_STATUSCODE_BADINTERNALERROR;
+    if(ret == 0)
+        return UA_STATUSCODE_BADTIMEOUT;
+
+    ret = recv(handle->sockfd, (char*)response->data, response->length, 0);
+
+    if(ret <= -1)
+        return UA_STATUSCODE_BADINTERNALERROR;
+    if(ret == 0)
+        return UA_STATUSCODE_BADSERVERNOTCONNECTED;
+
+    response->length = ret;
+    return UA_STATUSCODE_GOOD;
+}
+
+static void ClientNetworkLayerTCP_delete(ClientNetworkLayerTCP *layer) {
+	if(layer)
+		free(layer);
+}
+
+UA_ClientNetworkLayer ClientNetworkLayerTCP_new(UA_ConnectionConfig conf) {
+	ClientNetworkLayerTCP *tcplayer = malloc(sizeof(ClientNetworkLayerTCP));
+	tcplayer->sockfd = 0;
+
+    UA_ClientNetworkLayer layer;
+    layer.nlHandle = tcplayer;
+    layer.connect = (UA_StatusCode (*)(const UA_String, void**)) ClientNetworkLayerTCP_connect;
+    layer.disconnect = (void (*)(void*)) ClientNetworkLayerTCP_disconnect;
+    layer.delete = (void (*)(void*)) ClientNetworkLayerTCP_delete;
+    layer.send = (UA_StatusCode (*)(void*, UA_ByteStringArray)) ClientNetworkLayerTCP_send;
+    layer.awaitResponse = (UA_StatusCode (*)(void*, UA_ByteString *, UA_UInt32))ClientNetworkLayerTCP_awaitResponse;
+    return layer;
+}

+ 2 - 0
examples/networklayer_tcp.h

@@ -12,12 +12,14 @@ extern "C" {
 
 #ifdef NOT_AMALGATED
     #include "ua_server.h"
+	#include "ua_client.h"
 #else
     #include "open62541.h"
 #endif
 
 /** @brief Create the TCP networklayer and listen to the specified port */
 UA_ServerNetworkLayer ServerNetworkLayerTCP_new(UA_ConnectionConfig conf, UA_UInt32 port);
+UA_ClientNetworkLayer ClientNetworkLayerTCP_new(UA_ConnectionConfig conf);
 
 #ifdef __cplusplus
 } // extern "C"

+ 224 - 59
examples/server.c

@@ -2,12 +2,6 @@
  * This work is licensed under a Creative Commons CCZero 1.0 Universal License.
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
  */
-#include <time.h>
-#include <stdio.h>
-#include <stdlib.h> 
-#include <signal.h>
-#include <errno.h> // errno, EINTR
-
 #ifdef NOT_AMALGATED
     #include "ua_types.h"
     #include "ua_server.h"
@@ -15,60 +9,227 @@
     #include "open62541.h"
 #endif
 
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h> 
+#include <signal.h>
+#define __USE_XOPEN2K
+#ifdef UA_MULTITHREADING
+#include <pthread.h>
+#endif
+
 // provided by the user, implementations available in the /examples folder
 #include "logger_stdout.h"
 #include "networklayer_tcp.h"
 
+/****************************/
+/* Server-related variables */
+/****************************/
+
 UA_Boolean running = 1;
 UA_Logger logger;
 
-static void stopHandler(int sign) {
-    printf("Received Ctrl-C\n");
-	running = 0;
+/*************************/
+/* Read-only data source */
+/*************************/
+static UA_StatusCode readTimeData(const void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
+	UA_DateTime *currentTime = UA_DateTime_new();
+	if(!currentTime)
+		return UA_STATUSCODE_BADOUTOFMEMORY;
+	*currentTime = UA_DateTime_now();
+	value->value.type = &UA_TYPES[UA_TYPES_DATETIME];
+	value->value.arrayLength = -1;
+	value->value.dataPtr = currentTime;
+	value->value.arrayDimensionsSize = -1;
+	value->value.arrayDimensions = NULL;
+	value->hasVariant = UA_TRUE;
+	if(sourceTimeStamp) {
+		value->hasSourceTimestamp = UA_TRUE;
+		value->sourceTimestamp = *currentTime;
+	}
+	return UA_STATUSCODE_GOOD;
+}
+
+static void releaseTimeData(const void *handle, UA_DataValue *value) {
+	UA_DateTime_delete((UA_DateTime*)value->value.dataPtr);
+}
+
+/*****************************/
+/* Read-only CPU temperature */
+/*      Only on Linux        */
+/*****************************/
+FILE* temperatureFile = NULL;
+static UA_StatusCode readTemperature(const void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
+	UA_Double* currentTemperature = UA_Double_new();
+
+	if(!currentTemperature)
+		return UA_STATUSCODE_BADOUTOFMEMORY;
+
+	fseek(temperatureFile, 0, SEEK_SET);
+
+	if(fscanf(temperatureFile, "%lf", currentTemperature) != 1){
+		UA_LOG_WARNING(logger, UA_LOGGERCATEGORY_USERLAND, "Can not parse temperature");
+		exit(1);
+	}
+
+	*currentTemperature /= 1000.0;
+
+	value->value.type = &UA_TYPES[UA_TYPES_DOUBLE];
+	value->value.arrayLength = -1;
+	value->value.dataPtr = currentTemperature;
+	value->value.arrayDimensionsSize = -1;
+	value->value.arrayDimensions = NULL;
+	value->hasVariant = UA_TRUE;
+	return UA_STATUSCODE_GOOD;
+}
+
+static void releaseTemperature(const void *handle, UA_DataValue *value) {
+	UA_Double_delete((UA_Double*)value->value.dataPtr);
+}
+
+/*************************/
+/* Read-write status led */
+/*************************/
+#ifdef UA_MULTITHREADING
+pthread_rwlock_t writeLock;
+#endif
+FILE* triggerFile = NULL;
+FILE* ledFile = NULL;
+UA_Boolean ledStatus = 0;
+
+static UA_StatusCode readLedStatus(const void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
+	/* In order to reduce blocking time, we could alloc memory for every read
+       and return a copy of the data. */
+#ifdef UA_MULTITHREADING
+	pthread_rwlock_rdlock(&writeLock);
+#endif
+	value->value.type = &UA_TYPES[UA_TYPES_BOOLEAN];
+	value->value.arrayLength = -1;
+	value->value.dataPtr = &ledStatus;
+	value->value.arrayDimensionsSize = -1;
+	value->value.arrayDimensions = NULL;
+	value->hasVariant = UA_TRUE;
+	if(sourceTimeStamp) {
+		value->sourceTimestamp = UA_DateTime_now();
+		value->hasSourceTimestamp = UA_TRUE;
+	}
+	return UA_STATUSCODE_GOOD;
+}
+
+static void releaseLedStatus(const void *handle, UA_DataValue *value) {
+	/* If we allocated memory for a specific read, free the content of the
+       variantdata. */
+	value->value.arrayLength = -1;
+	value->value.dataPtr = NULL;
+#ifdef UA_MULTITHREADING
+	pthread_rwlock_unlock(&writeLock);
+#endif
+}
+
+static UA_StatusCode writeLedStatus(const void *handle, const UA_Variant *data) {
+#ifdef UA_MULTITHREADING
+	pthread_rwlock_wrlock(&writeLock);
+#endif
+	if(data->dataPtr)
+		ledStatus = *(UA_Boolean*)data->dataPtr;
+
+	if(triggerFile)
+		fseek(triggerFile, 0, SEEK_SET);
+
+	if(ledFile){
+		if(ledStatus == 1){
+			fprintf(ledFile, "%s", "1");
+		} else {
+			fprintf(ledFile, "%s", "0");
+		}
+		fflush(ledFile);
+	}
+#ifdef UA_MULTITHREADING
+	pthread_rwlock_unlock(&writeLock);
+#endif
+	return UA_STATUSCODE_GOOD;
 }
 
-static UA_ByteString loadCertificate(void) {
-    UA_ByteString certificate = UA_STRING_NULL;
-	FILE *fp = NULL;
-	//FIXME: a potiential bug of locating the certificate, we need to get the path from the server's config
-	fp=fopen("localhost.der", "rb");
-
-	if(!fp) {
-        errno = 0; // we read errno also from the tcp layer...
-        return certificate;
-    }
-
-    fseek(fp, 0, SEEK_END);
-    certificate.length = ftell(fp);
-    certificate.data = malloc(certificate.length*sizeof(UA_Byte));
-	if(!certificate.data)
-		return certificate;
-
-    fseek(fp, 0, SEEK_SET);
-    if(fread(certificate.data, sizeof(UA_Byte), certificate.length, fp) < (size_t)certificate.length)
-        UA_ByteString_deleteMembers(&certificate); // error reading the cert
-    fclose(fp);
-
-    return certificate;
+static void printLedStatus(UA_Server *server, void *data) {
+	UA_LOG_INFO(logger, UA_LOGGERCATEGORY_SERVER, ledStatus ? "LED is on" : "LED is off");
 }
 
-static void testCallback(UA_Server *server, void *data) {
-    logger.log_info(UA_LOGGERCATEGORY_USERLAND, "testcallback");
+static void stopHandler(int sign) {
+	printf("Received Ctrl-C\n");
+	running = 0;
 }
 
 int main(int argc, char** argv) {
 	signal(SIGINT, stopHandler); /* catches ctrl-c */
+#ifdef UA_MULTITHREADING
+	pthread_rwlock_init(&writeLock, 0);
+#endif
 
 	UA_Server *server = UA_Server_new();
-    logger = Logger_Stdout_new();
-    UA_Server_setLogger(server, logger);
-    UA_Server_setServerCertificate(server, loadCertificate());
-    UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
+	logger = Logger_Stdout_new();
+	UA_Server_setLogger(server, logger);
+	UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
+
+	// print the status every 2 sec
+	UA_WorkItem work = {.type = UA_WORKITEMTYPE_METHODCALL,
+			.work.methodCall = {.method = printLedStatus, .data = NULL} };
+	UA_Server_addRepeatedWorkItem(server, &work, 20000000, NULL);
+
+	// add node with the datetime data source
+	UA_DataSource dateDataSource = (UA_DataSource)
+        {.handle = NULL,
+		.read = readTimeData,
+		.release = releaseTimeData,
+		.write = NULL};
+	UA_QualifiedName dateName;
+	UA_QUALIFIEDNAME_ASSIGN(dateName, "current time");
+	UA_Server_addDataSourceVariableNode(server, dateDataSource, dateName, UA_NODEID_NULL,
+                                        UA_NODEID_STATIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                        UA_NODEID_STATIC(0, UA_NS0ID_ORGANIZES));
+
+	if(!(temperatureFile = fopen("/sys/class/thermal/thermal_zone0/temp", "r"))){
+		UA_LOG_WARNING(logger, UA_LOGGERCATEGORY_USERLAND, "[Linux specific] Can not open temperature file, no temperature node will be added");
+	} else {
+		// add node with the datetime data source
+		UA_DataSource temperatureDataSource = (UA_DataSource)
+    	    {.handle = NULL,
+			.read = readTemperature,
+			.release = releaseTemperature,
+			.write = NULL};
+		UA_QualifiedName ledName;
+		UA_QUALIFIEDNAME_ASSIGN(ledName, "cpu temperature");
+		UA_Server_addDataSourceVariableNode(server, temperatureDataSource, ledName, UA_NODEID_NULL, 
+                                            UA_NODEID_STATIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                            UA_NODEID_STATIC(0, UA_NS0ID_ORGANIZES));
+	}
 
-    UA_WorkItem work = {.type = UA_WORKITEMTYPE_METHODCALL, .work.methodCall = {.method = testCallback, .data = NULL} };
-    UA_Server_addRepeatedWorkItem(server, &work, 20000000, NULL); // call every 2 sec
+	if (	!(triggerFile = fopen("/sys/class/leds/led0/trigger", "w"))
+		|| 	!(ledFile = fopen("/sys/class/leds/led0/brightness", "w"))) {
+		UA_LOG_WARNING(logger, UA_LOGGERCATEGORY_USERLAND, "[Raspberry Pi specific] Can not open trigger or LED file (try to run server with sudo if on a Raspberry PI)");
+		UA_LOG_WARNING(logger, UA_LOGGERCATEGORY_USERLAND, "An LED node will be added but no physical LED will be operated");
+	} else {
+		//setting led mode to manual
+		fprintf(triggerFile, "%s", "none");
+		fflush(triggerFile);
 
-	// add a variable node to the adresspace
+		//turning off led initially
+		fprintf(ledFile, "%s", "1");
+		fflush(ledFile);
+	}
+
+	// add node with the LED status data source
+	UA_DataSource ledStatusDataSource = (UA_DataSource)
+   		{.handle = NULL,
+		.read = readLedStatus,
+		.release = releaseLedStatus,
+		.write = writeLedStatus};
+	UA_QualifiedName statusName;
+	UA_QUALIFIEDNAME_ASSIGN(statusName, "status LED");
+	UA_Server_addDataSourceVariableNode(server, ledStatusDataSource, statusName, UA_NODEID_NULL,
+                                        UA_NODEID_STATIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                        UA_NODEID_STATIC(0, UA_NS0ID_ORGANIZES));
+
+	// add a static variable node to the adresspace
     UA_Variant *myIntegerVariant = UA_Variant_new();
     UA_Int32 myInteger = 42;
     UA_Variant_setScalarCopy(myIntegerVariant, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
@@ -79,26 +240,30 @@ int main(int argc, char** argv) {
     UA_NodeId parentReferenceNodeId = UA_NODEID_STATIC(0, UA_NS0ID_ORGANIZES);
     UA_Server_addVariableNode(server, myIntegerVariant, myIntegerName,
                               myIntegerNodeId, parentNodeId, parentReferenceNodeId);
-    
-#ifdef BENCHMARK
-    UA_UInt32 nodeCount = 500;
-    char str[15];
-    for(UA_UInt32 i = 0;i<nodeCount;i++) {
-        UA_Int32 *data = UA_Int32_new();
-        *data = 42;
-        UA_Variant *variant = UA_Variant_new();
-        UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_INT32]);
-        UA_QualifiedName *nodeName = UA_QualifiedName_new();
-        sprintf(str,"%d",i);
-        UA_QualifiedName_copycstring(str, nodeName);
-        UA_Server_addVariableNode(server, variant, nodeName, UA_NODEID_NULL, 
-                                  UA_NODEID_STATIC(0, UA_NS0ID_OBJECTSFOLDER),
-                                  UA_NODEID_STATIC(0, UA_NS0ID_ORGANIZES));
-    }
-#endif
 
-    UA_StatusCode retval = UA_Server_run(server, 1, &running);
+	//start server
+	UA_StatusCode retval = UA_Server_run(server, 1, &running); //blocks until running=false
+
+	//ctrl-c received -> clean up
 	UA_Server_delete(server);
 
+	if(temperatureFile)
+		fclose(temperatureFile);
+
+	if(triggerFile){
+		fseek(triggerFile, 0, SEEK_SET);
+		//setting led mode to default
+		fprintf(triggerFile, "%s", "mmc0");
+		fclose(triggerFile);
+	}
+
+	if(ledFile){
+		fclose(ledFile);
+	}
+
+#ifdef UA_MULTITHREADING
+	pthread_rwlock_destroy(&writeLock);
+#endif
+
 	return retval;
 }

+ 0 - 257
examples/server_datasource.c

@@ -1,257 +0,0 @@
-/*
- * This work is licensed under a Creative Commons CCZero 1.0 Universal License.
- * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
- */
-#ifdef NOT_AMALGATED
-    #include "ua_types.h"
-    #include "ua_server.h"
-#else
-    #include "open62541.h"
-#endif
-
-#include <time.h>
-#include <stdio.h>
-#include <stdlib.h> 
-#include <signal.h>
-#define __USE_XOPEN2K
-#ifdef UA_MULTITHREADING
-#include <pthread.h>
-#endif
-
-// provided by the user, implementations available in the /examples folder
-#include "logger_stdout.h"
-#include "networklayer_tcp.h"
-
-/****************************/
-/* Server-related variables */
-/****************************/
-
-UA_Boolean running = 1;
-UA_Logger logger;
-
-/*************************/
-/* Read-only data source */
-/*************************/
-static UA_StatusCode readTimeData(const void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
-	UA_DateTime *currentTime = UA_DateTime_new();
-	if(!currentTime)
-		return UA_STATUSCODE_BADOUTOFMEMORY;
-	*currentTime = UA_DateTime_now();
-	value->value.type = &UA_TYPES[UA_TYPES_DATETIME];
-	value->value.arrayLength = -1;
-	value->value.dataPtr = currentTime;
-	value->value.arrayDimensionsSize = -1;
-	value->value.arrayDimensions = NULL;
-	value->hasVariant = UA_TRUE;
-	if(sourceTimeStamp) {
-		value->hasSourceTimestamp = UA_TRUE;
-		value->sourceTimestamp = *currentTime;
-	}
-	return UA_STATUSCODE_GOOD;
-}
-
-static void releaseTimeData(const void *handle, UA_DataValue *value) {
-	UA_DateTime_delete((UA_DateTime*)value->value.dataPtr);
-}
-
-/*****************************/
-/* Read-only CPU temperature */
-/*      Only on Linux        */
-/*****************************/
-FILE* temperatureFile = NULL;
-static UA_StatusCode readTemperature(const void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
-	UA_Double* currentTemperature = UA_Double_new();
-
-	if(!currentTemperature)
-		return UA_STATUSCODE_BADOUTOFMEMORY;
-
-	fseek(temperatureFile, 0, SEEK_SET);
-
-	if(fscanf(temperatureFile, "%lf", currentTemperature) != 1){
-		UA_LOG_WARNING(logger, UA_LOGGERCATEGORY_USERLAND, "Can not parse temperature");
-		exit(1);
-	}
-
-	*currentTemperature /= 1000.0;
-
-	value->value.type = &UA_TYPES[UA_TYPES_DOUBLE];
-	value->value.arrayLength = -1;
-	value->value.dataPtr = currentTemperature;
-	value->value.arrayDimensionsSize = -1;
-	value->value.arrayDimensions = NULL;
-	value->hasVariant = UA_TRUE;
-	return UA_STATUSCODE_GOOD;
-}
-
-static void releaseTemperature(const void *handle, UA_DataValue *value) {
-	UA_Double_delete((UA_Double*)value->value.dataPtr);
-}
-
-/*************************/
-/* Read-write status led */
-/*************************/
-#ifdef UA_MULTITHREADING
-pthread_rwlock_t writeLock;
-#endif
-FILE* triggerFile = NULL;
-FILE* ledFile = NULL;
-UA_Boolean ledStatus = 0;
-
-static UA_StatusCode readLedStatus(const void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
-	/* In order to reduce blocking time, we could alloc memory for every read
-       and return a copy of the data. */
-#ifdef UA_MULTITHREADING
-	pthread_rwlock_rdlock(&writeLock);
-#endif
-	value->value.type = &UA_TYPES[UA_TYPES_BOOLEAN];
-	value->value.arrayLength = -1;
-	value->value.dataPtr = &ledStatus;
-	value->value.arrayDimensionsSize = -1;
-	value->value.arrayDimensions = NULL;
-	value->hasVariant = UA_TRUE;
-	if(sourceTimeStamp) {
-		value->sourceTimestamp = UA_DateTime_now();
-		value->hasSourceTimestamp = UA_TRUE;
-	}
-	return UA_STATUSCODE_GOOD;
-}
-
-static void releaseLedStatus(const void *handle, UA_DataValue *value) {
-	/* If we allocated memory for a specific read, free the content of the
-       variantdata. */
-	value->value.arrayLength = -1;
-	value->value.dataPtr = NULL;
-#ifdef UA_MULTITHREADING
-	pthread_rwlock_unlock(&writeLock);
-#endif
-}
-
-static UA_StatusCode writeLedStatus(const void *handle, const UA_Variant *data) {
-#ifdef UA_MULTITHREADING
-	pthread_rwlock_wrlock(&writeLock);
-#endif
-	if(data->dataPtr)
-		ledStatus = *(UA_Boolean*)data->dataPtr;
-
-	if(triggerFile)
-		fseek(triggerFile, 0, SEEK_SET);
-
-	if(ledFile){
-		if(ledStatus == 1){
-			fprintf(ledFile, "%s", "1");
-		} else {
-			fprintf(ledFile, "%s", "0");
-		}
-		fflush(ledFile);
-	}
-#ifdef UA_MULTITHREADING
-	pthread_rwlock_unlock(&writeLock);
-#endif
-	return UA_STATUSCODE_GOOD;
-}
-
-static void printLedStatus(UA_Server *server, void *data) {
-	UA_LOG_INFO(logger, UA_LOGGERCATEGORY_SERVER, ledStatus ? "LED is on" : "LED is off");
-}
-
-static void stopHandler(int sign) {
-	printf("Received Ctrl-C\n");
-	running = 0;
-}
-
-int main(int argc, char** argv) {
-	signal(SIGINT, stopHandler); /* catches ctrl-c */
-#ifdef UA_MULTITHREADING
-	pthread_rwlock_init(&writeLock, 0);
-#endif
-
-	UA_Server *server = UA_Server_new();
-	logger = Logger_Stdout_new();
-	UA_Server_setLogger(server, logger);
-	UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
-
-	// print the status every 2 sec
-	UA_WorkItem work = {.type = UA_WORKITEMTYPE_METHODCALL,
-			.work.methodCall = {.method = printLedStatus, .data = NULL} };
-	UA_Server_addRepeatedWorkItem(server, &work, 20000000, NULL);
-
-	// add node with the datetime data source
-	UA_DataSource dateDataSource = (UA_DataSource)
-        {.handle = NULL,
-		.read = readTimeData,
-		.release = releaseTimeData,
-		.write = NULL};
-	UA_QualifiedName dateName;
-	UA_QUALIFIEDNAME_ASSIGN(dateName, "current time");
-	UA_Server_addDataSourceVariableNode(server, dateDataSource, dateName, UA_NODEID_NULL,
-                                        UA_NODEID_STATIC(0, UA_NS0ID_OBJECTSFOLDER),
-                                        UA_NODEID_STATIC(0, UA_NS0ID_ORGANIZES));
-
-	if(!(temperatureFile = fopen("/sys/class/thermal/thermal_zone0/temp", "r"))){
-		UA_LOG_WARNING(logger, UA_LOGGERCATEGORY_USERLAND, "[Linux specific] Can not open temperature file, no temperature node will be added");
-	} else {
-		// add node with the datetime data source
-		UA_DataSource temperatureDataSource = (UA_DataSource)
-    	    {.handle = NULL,
-			.read = readTemperature,
-			.release = releaseTemperature,
-			.write = NULL};
-		UA_QualifiedName ledName;
-		UA_QUALIFIEDNAME_ASSIGN(ledName, "cpu temperature");
-		UA_Server_addDataSourceVariableNode(server, temperatureDataSource, ledName, UA_NODEID_NULL, 
-                                            UA_NODEID_STATIC(0, UA_NS0ID_OBJECTSFOLDER),
-                                            UA_NODEID_STATIC(0, UA_NS0ID_ORGANIZES));
-	}
-
-	if (	!(triggerFile = fopen("/sys/class/leds/led0/trigger", "w"))
-		|| 	!(ledFile = fopen("/sys/class/leds/led0/brightness", "w"))) {
-		UA_LOG_WARNING(logger, UA_LOGGERCATEGORY_USERLAND, "[Raspberry Pi specific] Can not open trigger or LED file (try to run server with sudo if on a Raspberry PI)");
-		UA_LOG_WARNING(logger, UA_LOGGERCATEGORY_USERLAND, "An LED node will be added but no physical LED will be operated");
-	} else {
-		//setting led mode to manual
-		fprintf(triggerFile, "%s", "none");
-		fflush(triggerFile);
-
-		//turning off led initially
-		fprintf(ledFile, "%s", "1");
-		fflush(ledFile);
-	}
-
-	// add node with the LED status data source
-	UA_DataSource ledStatusDataSource = (UA_DataSource)
-   		{.handle = NULL,
-		.read = readLedStatus,
-		.release = releaseLedStatus,
-		.write = writeLedStatus};
-	UA_QualifiedName statusName;
-	UA_QUALIFIEDNAME_ASSIGN(statusName, "status LED");
-	UA_Server_addDataSourceVariableNode(server, ledStatusDataSource, statusName, UA_NODEID_NULL,
-                                        UA_NODEID_STATIC(0, UA_NS0ID_OBJECTSFOLDER),
-                                        UA_NODEID_STATIC(0, UA_NS0ID_ORGANIZES));
-
-	//start server
-	UA_StatusCode retval = UA_Server_run(server, 1, &running); //blocks until running=false
-
-	//ctrl-c received -> clean up
-	UA_Server_delete(server);
-
-	if(temperatureFile)
-		fclose(temperatureFile);
-
-	if(triggerFile){
-		fseek(triggerFile, 0, SEEK_SET);
-		//setting led mode to default
-		fprintf(triggerFile, "%s", "mmc0");
-		fclose(triggerFile);
-	}
-
-	if(ledFile){
-		fclose(ledFile);
-	}
-
-#ifdef UA_MULTITHREADING
-	pthread_rwlock_destroy(&writeLock);
-#endif
-
-	return retval;
-}

+ 104 - 0
examples/server_simple.c

@@ -0,0 +1,104 @@
+/*
+ * This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
+ */
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h> 
+#include <signal.h>
+#include <errno.h> // errno, EINTR
+
+#ifdef NOT_AMALGATED
+    #include "ua_types.h"
+    #include "ua_server.h"
+#else
+    #include "open62541.h"
+#endif
+
+// provided by the user, implementations available in the /examples folder
+#include "logger_stdout.h"
+#include "networklayer_tcp.h"
+
+UA_Boolean running = 1;
+UA_Logger logger;
+
+static void stopHandler(int sign) {
+    printf("Received Ctrl-C\n");
+	running = 0;
+}
+
+static UA_ByteString loadCertificate(void) {
+    UA_ByteString certificate = UA_STRING_NULL;
+	FILE *fp = NULL;
+	//FIXME: a potiential bug of locating the certificate, we need to get the path from the server's config
+	fp=fopen("localhost.der", "rb");
+
+	if(!fp) {
+        errno = 0; // we read errno also from the tcp layer...
+        return certificate;
+    }
+
+    fseek(fp, 0, SEEK_END);
+    certificate.length = ftell(fp);
+    certificate.data = malloc(certificate.length*sizeof(UA_Byte));
+	if(!certificate.data)
+		return certificate;
+
+    fseek(fp, 0, SEEK_SET);
+    if(fread(certificate.data, sizeof(UA_Byte), certificate.length, fp) < (size_t)certificate.length)
+        UA_ByteString_deleteMembers(&certificate); // error reading the cert
+    fclose(fp);
+
+    return certificate;
+}
+
+static void testCallback(UA_Server *server, void *data) {
+    logger.log_info(UA_LOGGERCATEGORY_USERLAND, "testcallback");
+}
+
+int main(int argc, char** argv) {
+	signal(SIGINT, stopHandler); /* catches ctrl-c */
+
+	UA_Server *server = UA_Server_new();
+    logger = Logger_Stdout_new();
+    UA_Server_setLogger(server, logger);
+    UA_Server_setServerCertificate(server, loadCertificate());
+    UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
+
+    UA_WorkItem work = {.type = UA_WORKITEMTYPE_METHODCALL, .work.methodCall = {.method = testCallback, .data = NULL} };
+    UA_Server_addRepeatedWorkItem(server, &work, 20000000, NULL); // call every 2 sec
+
+	// add a variable node to the adresspace
+    UA_Variant *myIntegerVariant = UA_Variant_new();
+    UA_Int32 myInteger = 42;
+    UA_Variant_setScalarCopy(myIntegerVariant, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
+    UA_QualifiedName myIntegerName;
+    UA_QUALIFIEDNAME_ASSIGN(myIntegerName, "the answer");
+    UA_NodeId myIntegerNodeId = UA_NODEID_NULL; /* assign a random free nodeid */
+    UA_NodeId parentNodeId = UA_NODEID_STATIC(0, UA_NS0ID_OBJECTSFOLDER);
+    UA_NodeId parentReferenceNodeId = UA_NODEID_STATIC(0, UA_NS0ID_ORGANIZES);
+    UA_Server_addVariableNode(server, myIntegerVariant, myIntegerName,
+                              myIntegerNodeId, parentNodeId, parentReferenceNodeId);
+    
+#ifdef BENCHMARK
+    UA_UInt32 nodeCount = 500;
+    char str[15];
+    for(UA_UInt32 i = 0;i<nodeCount;i++) {
+        UA_Int32 *data = UA_Int32_new();
+        *data = 42;
+        UA_Variant *variant = UA_Variant_new();
+        UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_INT32]);
+        UA_QualifiedName *nodeName = UA_QualifiedName_new();
+        sprintf(str,"%d",i);
+        UA_QualifiedName_copycstring(str, nodeName);
+        UA_Server_addVariableNode(server, variant, *nodeName, UA_NODEID_NULL,
+                                  UA_NODEID_STATIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                  UA_NODEID_STATIC(0, UA_NS0ID_ORGANIZES));
+    }
+#endif
+
+    UA_StatusCode retval = UA_Server_run(server, 1, &running);
+	UA_Server_delete(server);
+
+	return retval;
+}

+ 61 - 0
include/ua_client.h

@@ -0,0 +1,61 @@
+#ifndef UA_CLIENT_H_
+#define UA_CLIENT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ua_util.h"
+#include "ua_types.h"
+#include "ua_connection.h"
+#include "ua_types_generated.h"
+
+/**
+ * The client networklayer can handle only a single connection. The networklayer
+ * is only concerned with getting messages to the client and receiving them.
+ */
+typedef struct {
+	void *nlHandle;
+
+    UA_StatusCode (*connect)(const UA_String endpointUrl, void **resultHandle);
+    void (*disconnect)(void *handle);
+    void (*delete)(void *handle);
+    UA_StatusCode (*send)(void *handle, UA_ByteStringArray gather_buf);
+    // the response buffer exists on the heap. the size shall correspond the the connection settings
+    UA_StatusCode (*awaitResponse)(void *handle, UA_ByteString *response, UA_UInt32 timeout);
+} UA_ClientNetworkLayer;
+
+struct UA_Client;
+typedef struct UA_Client UA_Client;
+
+UA_Client UA_EXPORT * UA_Client_new(void);
+void UA_Client_delete(UA_Client* client);
+
+UA_StatusCode UA_EXPORT UA_Client_connect(UA_Client *client, UA_ConnectionConfig conf,
+                                          UA_ClientNetworkLayer networkLayer, char *endpointUrl);
+
+UA_StatusCode UA_EXPORT UA_Client_disconnect(UA_Client *client);
+
+/* Attribute Service Set */
+//TODO: had to remove const from the 2nd argument
+UA_ReadResponse UA_EXPORT UA_Client_read(UA_Client *client, UA_ReadRequest *request);
+UA_WriteResponse UA_EXPORT UA_Client_write(UA_Client *client, const UA_WriteRequest *request);
+
+/* View Service Set */    
+UA_BrowseResponse UA_EXPORT UA_Client_browse(UA_Client *client, const UA_BrowseRequest *request);
+UA_TranslateBrowsePathsToNodeIdsResponse UA_EXPORT UA_Client_translateBrowsePathsToNodeIds(UA_Client *client,
+                                                        const UA_TranslateBrowsePathsToNodeIdsRequest *request);
+
+/* NodeManagement Service Set */
+UA_AddNodesResponse UA_EXPORT UA_Client_addNodes(UA_Client *client, const UA_AddNodesRequest *request);
+UA_AddReferencesResponse UA_EXPORT
+    UA_Client_addReferences(UA_Client *client, const UA_AddReferencesRequest *request);
+UA_DeleteNodesResponse UA_EXPORT UA_Client_deleteNodes(UA_Client *client, const UA_DeleteNodesRequest *request);
+UA_DeleteReferencesResponse UA_EXPORT
+    UA_Client_deleteReferences(UA_Client *client, const UA_DeleteReferencesRequest *request);
+    
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* UA_CLIENT_H_ */

+ 26 - 0
include/ua_types.h

@@ -586,6 +586,32 @@ void UA_EXPORT UA_Array_delete(void *p, const UA_DataType *dataType, UA_Int32 no
 
 /** @} */
 
+/* These are not generated from XML. Server *and* client need them. */
+typedef enum {
+    UA_ATTRIBUTEID_NODEID                  = 1,
+    UA_ATTRIBUTEID_NODECLASS               = 2,
+    UA_ATTRIBUTEID_BROWSENAME              = 3,
+    UA_ATTRIBUTEID_DISPLAYNAME             = 4,
+    UA_ATTRIBUTEID_DESCRIPTION             = 5,
+    UA_ATTRIBUTEID_WRITEMASK               = 6,
+    UA_ATTRIBUTEID_USERWRITEMASK           = 7,
+    UA_ATTRIBUTEID_ISABSTRACT              = 8,
+    UA_ATTRIBUTEID_SYMMETRIC               = 9,
+    UA_ATTRIBUTEID_INVERSENAME             = 10,
+    UA_ATTRIBUTEID_CONTAINSNOLOOPS         = 11,
+    UA_ATTRIBUTEID_EVENTNOTIFIER           = 12,
+    UA_ATTRIBUTEID_VALUE                   = 13,
+    UA_ATTRIBUTEID_DATATYPE                = 14,
+    UA_ATTRIBUTEID_VALUERANK               = 15,
+    UA_ATTRIBUTEID_ARRAYDIMENSIONS         = 16,
+    UA_ATTRIBUTEID_ACCESSLEVEL             = 17,
+    UA_ATTRIBUTEID_USERACCESSLEVEL         = 18,
+    UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL = 19,
+    UA_ATTRIBUTEID_HISTORIZING             = 20,
+    UA_ATTRIBUTEID_EXECUTABLE              = 21,
+    UA_ATTRIBUTEID_USEREXECUTABLE          = 22
+} UA_AttributeId;
+
 #ifdef __cplusplus
 } // extern "C"
 #endif

+ 489 - 0
src/client/ua_client.c

@@ -0,0 +1,489 @@
+#include "ua_client.h"
+#include "ua_nodeids.h"
+#include "ua_types.h"
+#include "ua_types_encoding_binary.h"
+#include "ua_transport_generated.h"
+
+struct UA_Client {
+    UA_ClientNetworkLayer networkLayer;
+    UA_String endpointUrl;
+    UA_Connection connection;
+
+    UA_UInt32 sequenceNumber;
+    UA_UInt32 requestId;
+
+    /* Secure Channel */
+    UA_ChannelSecurityToken securityToken;
+    UA_ByteString clientNonce;
+    UA_ByteString serverNonce;
+	/* UA_SequenceHeader sequenceHdr; */
+	/* UA_NodeId authenticationToken; */
+
+    /* Session */
+    UA_NodeId sessionId;
+    UA_NodeId authenticationToken;
+};
+
+UA_Client * UA_Client_new(void) {
+    UA_Client *client = UA_malloc(sizeof(UA_Client));
+    if(!client)
+        return UA_NULL;
+    UA_String_init(&client->endpointUrl);
+    client->connection.state = UA_CONNECTION_OPENING;
+
+    client->sequenceNumber = 0;
+    client->requestId = 0;
+
+    /* Secure Channel */
+    UA_ChannelSecurityToken_deleteMembers(&client->securityToken);
+    UA_ByteString_init(&client->clientNonce);
+    UA_ByteString_init(&client->serverNonce);
+    
+    UA_NodeId_init(&client->authenticationToken);
+    return client;
+}
+
+void UA_Client_delete(UA_Client* client){
+    client->networkLayer.delete(client->networkLayer.nlHandle);
+    UA_String_deleteMembers(&client->endpointUrl);
+    // client->connection
+
+    /* Secure Channel */
+    UA_ByteString_deleteMembers(&client->clientNonce);
+    UA_ByteString_deleteMembers(&client->serverNonce);
+
+    free(client);
+}
+
+static UA_StatusCode SecureChannelHandshake(UA_Client *client) {
+    UA_ByteString_deleteMembers(&client->clientNonce); // if the handshake is repeated
+	UA_ByteString_newMembers(&client->clientNonce, 1);
+	client->clientNonce.data[0] = 0;
+
+	UA_SecureConversationMessageHeader messageHeader;
+	messageHeader.messageHeader.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_OPNF;
+	messageHeader.secureChannelId = 0;
+
+	UA_SequenceHeader seqHeader;
+    seqHeader.sequenceNumber = ++client->sequenceNumber;
+    seqHeader.requestId = ++client->requestId;
+
+	UA_AsymmetricAlgorithmSecurityHeader asymHeader;
+	UA_AsymmetricAlgorithmSecurityHeader_init(&asymHeader);
+	UA_String_copycstring("http://opcfoundation.org/UA/SecurityPolicy#None", &asymHeader.securityPolicyUri);
+
+    /* id of opensecurechannelrequest */
+	UA_NodeId requestType = UA_NODEID_STATIC(0, UA_NS0ID_OPENSECURECHANNELREQUEST + UA_ENCODINGOFFSET_BINARY);
+
+	UA_OpenSecureChannelRequest opnSecRq;
+	UA_OpenSecureChannelRequest_init(&opnSecRq);
+	opnSecRq.requestHeader.timestamp = UA_DateTime_now();
+    UA_ByteString_copy(&client->clientNonce, &opnSecRq.clientNonce);
+	opnSecRq.requestedLifetime = 30000;
+	opnSecRq.securityMode = UA_MESSAGESECURITYMODE_NONE;
+	opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_ISSUE;
+	opnSecRq.requestHeader.authenticationToken.identifier.numeric = 10;
+	opnSecRq.requestHeader.authenticationToken.identifierType = UA_NODEIDTYPE_NUMERIC;
+	opnSecRq.requestHeader.authenticationToken.namespaceIndex = 10;
+
+	messageHeader.messageHeader.messageSize =
+        UA_SecureConversationMessageHeader_calcSizeBinary(&messageHeader) +
+        UA_AsymmetricAlgorithmSecurityHeader_calcSizeBinary(&asymHeader) +
+        UA_SequenceHeader_calcSizeBinary(&seqHeader) +
+        UA_NodeId_calcSizeBinary(&requestType) +
+        UA_OpenSecureChannelRequest_calcSizeBinary(&opnSecRq);
+
+	UA_ByteString message;
+    message.data = UA_alloca(messageHeader.messageHeader.messageSize);
+    message.length = messageHeader.messageHeader.messageSize;
+
+	size_t offset = 0;
+	UA_SecureConversationMessageHeader_encodeBinary(&messageHeader, &message, &offset);
+	UA_AsymmetricAlgorithmSecurityHeader_encodeBinary(&asymHeader, &message, &offset);
+	UA_SequenceHeader_encodeBinary(&seqHeader, &message, &offset);
+	UA_NodeId_encodeBinary(&requestType, &message, &offset);
+	UA_OpenSecureChannelRequest_encodeBinary(&opnSecRq, &message, &offset);
+
+	UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
+    UA_OpenSecureChannelRequest_deleteMembers(&opnSecRq);
+
+    UA_ByteStringArray buf = {.stringsSize = 1, .strings = &message};
+    UA_StatusCode retval = client->networkLayer.send(client->networkLayer.nlHandle, buf);
+    if(retval)
+        return retval;
+
+    // parse the response
+    UA_ByteString reply;
+    UA_ByteString_newMembers(&reply, client->connection.localConf.recvBufferSize);
+    retval = client->networkLayer.awaitResponse(client->networkLayer.nlHandle, &reply, 1000);
+    if(retval) {
+        UA_ByteString_deleteMembers(&reply);
+        return retval;
+    }
+
+	offset = 0;
+
+	UA_SecureConversationMessageHeader_decodeBinary(&reply, &offset, &messageHeader);
+	UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(&reply, &offset, &asymHeader);
+	UA_SequenceHeader_decodeBinary(&reply, &offset, &seqHeader);
+	UA_NodeId_decodeBinary(&reply, &offset, &requestType);
+
+	if(!UA_NodeId_equal(&requestType, &UA_NODEID_STATIC(0, UA_NS0ID_OPENSECURECHANNELRESPONSE +
+                                                        UA_ENCODINGOFFSET_BINARY))) {
+        UA_ByteString_deleteMembers(&reply);
+        UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
+
+	UA_OpenSecureChannelResponse response;
+	UA_OpenSecureChannelResponse_decodeBinary(&reply, &offset, &response);
+    UA_ByteString_deleteMembers(&reply);
+    retval = response.responseHeader.serviceResult;
+
+    if(retval == UA_STATUSCODE_GOOD) {
+        UA_ChannelSecurityToken_copy(&response.securityToken, &client->securityToken);
+        UA_ByteString_deleteMembers(&client->serverNonce); // if the handshake is repeated
+        UA_ByteString_copy(&response.serverNonce, &client->serverNonce);
+    }
+
+	UA_OpenSecureChannelResponse_deleteMembers(&response);
+	UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
+    return retval;
+
+}
+
+// The tcp connection is established. Now do the handshake
+static UA_StatusCode HelAckHandshake(UA_Client *c) {
+	UA_TcpMessageHeader messageHeader;
+    messageHeader.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_HELF;
+
+	UA_TcpHelloMessage hello;
+	UA_String_copy(&c->endpointUrl, &hello.endpointUrl);
+
+    UA_Connection *conn = &c->connection;
+	hello.maxChunkCount = conn->localConf.maxChunkCount;
+	hello.maxMessageSize = conn->localConf.maxMessageSize;
+	hello.protocolVersion = conn->localConf.protocolVersion;
+	hello.receiveBufferSize = conn->localConf.recvBufferSize;
+	hello.sendBufferSize = conn->localConf.sendBufferSize;
+
+	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;
+
+	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->networkLayer.send(c->networkLayer.nlHandle, buf);
+    if(retval)
+        return retval;
+
+    UA_Byte replybuf[1024];
+    UA_ByteString reply = {.data = replybuf, .length = 1024};
+    retval = c->networkLayer.awaitResponse(c->networkLayer.nlHandle, &reply, 0);
+	if (retval)
+		return retval;
+
+    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);
+        return retval;
+    }
+    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;
+}
+
+/** If the request fails, then the response is cast to UA_ResponseHeader (at the beginning of every
+    response) and filled with the appropriate error code */
+static void sendReceiveRequest(const void *request, const UA_DataType *requestType,
+                               void *response, const UA_DataType *responseType, UA_Client *client, UA_Boolean sendOnly)
+{
+
+    UA_SecureConversationMessageHeader msgHeader;
+    if(sendOnly){
+    	msgHeader.messageHeader.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_CLOF;
+    }else{
+    	msgHeader.messageHeader.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_MSGF;
+    }
+    msgHeader.secureChannelId = client->securityToken.channelId;
+
+    UA_SymmetricAlgorithmSecurityHeader symHeader;
+    symHeader.tokenId = client->securityToken.tokenId;
+    
+	UA_SequenceHeader seqHeader;
+    seqHeader.sequenceNumber = ++client->sequenceNumber;
+    seqHeader.requestId = ++client->requestId;
+
+	UA_NodeId requestId = UA_NODEID_STATIC(0, requestType->typeId.identifier.numeric +
+                                           UA_ENCODINGOFFSET_BINARY);
+
+	msgHeader.messageHeader.messageSize =
+        UA_SecureConversationMessageHeader_calcSizeBinary(&msgHeader) +
+        UA_SymmetricAlgorithmSecurityHeader_calcSizeBinary(&symHeader) +
+        UA_SequenceHeader_calcSizeBinary(&seqHeader) +
+        UA_NodeId_calcSizeBinary(&requestId) +
+        UA_calcSizeBinary(request, requestType);
+
+	UA_ByteString message;
+    UA_StatusCode retval = UA_ByteString_newMembers(&message, msgHeader.messageHeader.messageSize);
+    if(retval != UA_STATUSCODE_GOOD) {
+        // todo
+    }
+
+    size_t offset = 0;
+    retval |= UA_SecureConversationMessageHeader_encodeBinary(&msgHeader, &message, &offset);
+    retval |= UA_SymmetricAlgorithmSecurityHeader_encodeBinary(&symHeader, &message, &offset);
+    retval |= UA_SequenceHeader_encodeBinary(&seqHeader, &message, &offset);
+
+    retval |= UA_NodeId_encodeBinary(&requestId, &message, &offset);
+    retval |= UA_encodeBinary(request, requestType, &message, &offset);
+
+    UA_ByteStringArray buf = {.stringsSize = 1, .strings = &message};
+    retval = client->networkLayer.send(client->networkLayer.nlHandle, buf);
+    UA_ByteString_deleteMembers(&message);
+
+    //TODO: rework to get return value
+    if(sendOnly)
+    	return;
+
+    UA_ResponseHeader *respHeader = (UA_ResponseHeader*)response;
+    if(retval != UA_STATUSCODE_GOOD) {
+        respHeader->serviceResult = retval;
+        return;
+    }
+
+    /* Response */
+    UA_ByteString reply;
+    UA_ByteString_newMembers(&reply, client->connection.localConf.recvBufferSize);
+    retval = client->networkLayer.awaitResponse(client->networkLayer.nlHandle, &reply, 1000);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ByteString_deleteMembers(&reply);
+        respHeader->serviceResult = retval;
+        return;
+    }
+
+	offset = 0;
+	retval |= UA_SecureConversationMessageHeader_decodeBinary(&reply, &offset, &msgHeader);
+	retval |= UA_SymmetricAlgorithmSecurityHeader_decodeBinary(&reply, &offset, &symHeader);
+	retval |= UA_SequenceHeader_decodeBinary(&reply, &offset, &seqHeader);
+    UA_NodeId responseId;
+	retval |= UA_NodeId_decodeBinary(&reply, &offset, &responseId);
+
+	if(!UA_NodeId_equal(&responseId, &UA_NODEID_STATIC(0, responseType->typeId.identifier.numeric +
+                                                       UA_ENCODINGOFFSET_BINARY))) {
+        UA_ByteString_deleteMembers(&reply);
+        UA_SymmetricAlgorithmSecurityHeader_deleteMembers(&symHeader);
+        respHeader->serviceResult = retval;
+        return;
+    }
+
+	retval = UA_decodeBinary(&reply, &offset, response, responseType);
+    UA_ByteString_deleteMembers(&reply);
+    if(retval != UA_STATUSCODE_GOOD)
+        respHeader->serviceResult = retval;
+}
+
+static void synchronousRequest(const void *request, const UA_DataType *requestType,
+                               void *response, const UA_DataType *responseType, UA_Client *client){
+	sendReceiveRequest(request, requestType, response, responseType, client, UA_FALSE);
+}
+
+static void sendOnlyRequest(const void *request, const UA_DataType *requestType, UA_Client *client){
+	sendReceiveRequest(request, requestType, UA_NULL, UA_NULL, client, UA_TRUE);
+}
+
+static UA_StatusCode ActivateSession(UA_Client *client) {
+	UA_ActivateSessionRequest request;
+	UA_ActivateSessionRequest_init(&request);
+
+    request.requestHeader.requestHandle = 2; //TODO: is it a magic number?
+    request.requestHeader.authenticationToken = client->authenticationToken;
+    request.requestHeader.timestamp = UA_DateTime_now();
+    request.requestHeader.timeoutHint = 10000;
+
+    UA_ActivateSessionResponse response;
+    synchronousRequest(&request, &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST],
+                       &response, &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE],
+                       client);
+
+    UA_ActivateSessionRequest_deleteMembers(&request);
+    UA_ActivateSessionResponse_deleteMembers(&response);
+    return response.responseHeader.serviceResult; // not deleted
+}
+
+static UA_StatusCode SessionHandshake(UA_Client *client) {
+
+    UA_CreateSessionRequest request;
+    UA_CreateSessionRequest_init(&request);
+
+    // todo: is this needed for all requests?
+    UA_NodeId_copy(&client->authenticationToken, &request.requestHeader.authenticationToken);
+
+    request.requestHeader.timestamp = UA_DateTime_now();
+	request.requestHeader.timeoutHint = 10000;
+    UA_ByteString_copy(&client->clientNonce, &request.clientNonce);
+	request.requestedSessionTimeout = 1200000;
+	request.maxResponseMessageSize = UA_INT32_MAX;
+
+	/* UA_String_copy(endpointUrl, &rq.endpointUrl); */
+	/* UA_String_copycstring("mysession", &rq.sessionName); */
+	/* UA_String_copycstring("abcd", &rq.clientCertificate); */
+
+    UA_CreateSessionResponse response;
+    synchronousRequest(&request, &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST],
+                       &response, &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE],
+                       client);
+
+    UA_NodeId_copy(&response.authenticationToken, &client->authenticationToken);
+
+    UA_CreateSessionRequest_deleteMembers(&request);
+    UA_CreateSessionResponse_deleteMembers(&response);
+    return response.responseHeader.serviceResult; // not deleted
+}
+
+static UA_StatusCode CloseSession(UA_Client *client) {
+	UA_CloseSessionRequest request;
+	UA_CloseSessionRequest_init(&request);
+
+	request.requestHeader.timestamp = UA_DateTime_now();
+	request.requestHeader.timeoutHint = 10000;
+	request.deleteSubscriptions = UA_TRUE;
+	UA_NodeId_copy(&client->authenticationToken, &request.requestHeader.authenticationToken);
+    UA_CreateSessionResponse response;
+    synchronousRequest(&request, &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST],
+                       &response, &UA_TYPES[UA_TYPES_CLOSESESSIONRESPONSE],
+                       client);
+
+    UA_CloseSessionRequest_deleteMembers(&request);
+    UA_CloseSessionResponse_deleteMembers(&response);
+    return response.responseHeader.serviceResult; // not deleted
+}
+
+static UA_StatusCode CloseSecureChannel(UA_Client *client) {
+	UA_CloseSecureChannelRequest request;
+    UA_CloseSecureChannelRequest_init(&request);
+
+    request.requestHeader.requestHandle = 1; //TODO: magic number?
+    request.requestHeader.timestamp = UA_DateTime_now();
+    request.requestHeader.timeoutHint = 10000;
+
+    request.requestHeader.authenticationToken = client->authenticationToken;
+
+    sendOnlyRequest(&request, &UA_TYPES[UA_TYPES_CLOSESECURECHANNELREQUEST], client);
+
+    return UA_STATUSCODE_GOOD;
+
+}
+
+/*************************/
+/* User-Facing Functions */
+/*************************/
+
+
+UA_StatusCode UA_Client_connect(UA_Client *client, UA_ConnectionConfig conf,
+                                UA_ClientNetworkLayer networkLayer, char *endpointUrl)
+{
+    UA_StatusCode retval = UA_String_copycstring(endpointUrl, &client->endpointUrl);
+    if(retval != UA_STATUSCODE_GOOD)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+
+    client->networkLayer = networkLayer;
+    client->connection.localConf = conf;
+    retval = networkLayer.connect(client->endpointUrl, client->networkLayer.nlHandle);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    retval = HelAckHandshake(client);
+    if(retval == UA_STATUSCODE_GOOD)
+        retval = SecureChannelHandshake(client);
+    if(retval == UA_STATUSCODE_GOOD)
+        retval = SessionHandshake(client);
+    if(retval == UA_STATUSCODE_GOOD)
+        retval = ActivateSession(client);
+    return retval;
+}
+
+
+UA_StatusCode UA_EXPORT UA_Client_disconnect(UA_Client *client) {
+	UA_StatusCode retval;
+	retval = CloseSession(client);
+    if(retval == UA_STATUSCODE_GOOD)
+        retval = CloseSecureChannel(client);
+    return retval;
+}
+
+UA_ReadResponse UA_Client_read(UA_Client *client, UA_ReadRequest *request) {
+    UA_ReadResponse response;
+    //todo: probably move to synchronousRequest
+    UA_NodeId_copy(&client->authenticationToken, &request->requestHeader.authenticationToken);
+    synchronousRequest(request, &UA_TYPES[UA_TYPES_READREQUEST], &response,
+                       &UA_TYPES[UA_TYPES_READRESPONSE], client);
+    return response;
+}
+
+UA_WriteResponse UA_Client_write(UA_Client *client, const UA_WriteRequest *request) {
+    UA_WriteResponse response;
+    synchronousRequest(request, &UA_TYPES[UA_TYPES_WRITEREQUEST], &response,
+                       &UA_TYPES[UA_TYPES_WRITERESPONSE], client);
+    return response;
+}
+
+UA_BrowseResponse UA_Client_browse(UA_Client *client, const UA_BrowseRequest *request) {
+    UA_BrowseResponse response;
+    synchronousRequest(request, &UA_TYPES[UA_TYPES_BROWSEREQUEST], &response,
+                       &UA_TYPES[UA_TYPES_BROWSERESPONSE], client);
+    return response;
+}
+
+UA_TranslateBrowsePathsToNodeIdsResponse
+UA_Client_translateBrowsePathsToNodeIds(UA_Client *client,
+                                        const UA_TranslateBrowsePathsToNodeIdsRequest *request) {
+    UA_TranslateBrowsePathsToNodeIdsResponse response;
+    synchronousRequest(request, &UA_TYPES[UA_TYPES_BROWSEREQUEST], &response,
+                       &UA_TYPES[UA_TYPES_BROWSERESPONSE], client);
+    return response;
+}
+
+UA_AddNodesResponse UA_Client_addNodes(UA_Client *client, const UA_AddNodesRequest *request) {
+    UA_AddNodesResponse response;
+    synchronousRequest(request, &UA_TYPES[UA_TYPES_BROWSEREQUEST], &response,
+                       &UA_TYPES[UA_TYPES_BROWSERESPONSE], client);
+    return response;
+}
+
+UA_AddReferencesResponse UA_Client_addReferences(UA_Client *client, const UA_AddReferencesRequest *request) {
+    UA_AddReferencesResponse response;
+    synchronousRequest(request, &UA_TYPES[UA_TYPES_BROWSEREQUEST], &response,
+                       &UA_TYPES[UA_TYPES_BROWSERESPONSE], client);
+    return response;
+}
+
+UA_DeleteNodesResponse UA_Client_deleteNodes(UA_Client *client, const UA_DeleteNodesRequest *request) {
+    UA_DeleteNodesResponse response;
+    synchronousRequest(request, &UA_TYPES[UA_TYPES_BROWSEREQUEST], &response,
+                       &UA_TYPES[UA_TYPES_BROWSERESPONSE], client);
+    return response;
+}
+
+UA_DeleteReferencesResponse UA_Client_deleteReferences(UA_Client *client,
+                                                       const UA_DeleteReferencesRequest *request) {
+    UA_DeleteReferencesResponse response;
+    synchronousRequest(request, &UA_TYPES[UA_TYPES_BROWSEREQUEST], &response,
+                       &UA_TYPES[UA_TYPES_BROWSERESPONSE], client);
+    return response;
+}

+ 1 - 27
src/server/ua_server_internal.h

@@ -72,32 +72,6 @@ UA_StatusCode UA_Server_addReferenceWithSession(UA_Server *server, UA_Session *s
 
 void UA_Server_deleteTimedWork(UA_Server *server);
 
-/** The (nodes) AttributeIds are defined in part 6, table A1 of the standard */
-typedef enum {
-    UA_ATTRIBUTEID_NODEID                  = 1,
-    UA_ATTRIBUTEID_NODECLASS               = 2,
-    UA_ATTRIBUTEID_BROWSENAME              = 3,
-    UA_ATTRIBUTEID_DISPLAYNAME             = 4,
-    UA_ATTRIBUTEID_DESCRIPTION             = 5,
-    UA_ATTRIBUTEID_WRITEMASK               = 6,
-    UA_ATTRIBUTEID_USERWRITEMASK           = 7,
-    UA_ATTRIBUTEID_ISABSTRACT              = 8,
-    UA_ATTRIBUTEID_SYMMETRIC               = 9,
-    UA_ATTRIBUTEID_INVERSENAME             = 10,
-    UA_ATTRIBUTEID_CONTAINSNOLOOPS         = 11,
-    UA_ATTRIBUTEID_EVENTNOTIFIER           = 12,
-    UA_ATTRIBUTEID_VALUE                   = 13,
-    UA_ATTRIBUTEID_DATATYPE                = 14,
-    UA_ATTRIBUTEID_VALUERANK               = 15,
-    UA_ATTRIBUTEID_ARRAYDIMENSIONS         = 16,
-    UA_ATTRIBUTEID_ACCESSLEVEL             = 17,
-    UA_ATTRIBUTEID_USERACCESSLEVEL         = 18,
-    UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL = 19,
-    UA_ATTRIBUTEID_HISTORIZING             = 20,
-    UA_ATTRIBUTEID_EXECUTABLE              = 21,
-    UA_ATTRIBUTEID_USEREXECUTABLE          = 22
-} UA_AttributeId;
-
 #define ADDREFERENCE(NODEID, REFTYPE_NODEID, TARGET_EXPNODEID) do {     \
         UA_AddReferencesItem item;                                      \
         UA_AddReferencesItem_init(&item);                               \
@@ -108,4 +82,4 @@ typedef enum {
         UA_Server_addReference(server, &item);                          \
     } while(0)
 
-#endif /* UA_SERVER_INTERNAL_H_ */
+#endif /* UA_SERVER_INTERNAL_H_ */

+ 20 - 14
src/server/ua_services.h

@@ -31,7 +31,8 @@ void Service_FindServers(UA_Server                    *server,
  * Returns the Endpoints supported by a Server and all of the configuration
  * information required to establish a SecureChannel and a Session.
  */
-void Service_GetEndpoints(UA_Server *server, const UA_GetEndpointsRequest *request, UA_GetEndpointsResponse *response);
+void Service_GetEndpoints(UA_Server *server, const UA_GetEndpointsRequest *request,
+                          UA_GetEndpointsResponse *response);
 // Service_RegisterServer
 /** @} */
 
@@ -88,7 +89,8 @@ void Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
                              const UA_ActivateSessionRequest *request, UA_ActivateSessionResponse *response);
 
 /** Used to terminate a Session. */
-void Service_CloseSession(UA_Server *server, const UA_CloseSessionRequest *request, UA_CloseSessionResponse *response);
+void Service_CloseSession(UA_Server *server, const UA_CloseSessionRequest *request,
+                          UA_CloseSessionResponse *response);
 // Service_Cancel
 /** @} */
 
@@ -103,16 +105,20 @@ void Service_CloseSession(UA_Server *server, const UA_CloseSessionRequest *reque
  */
 
 /** Used to add one or more Nodes into the AddressSpace hierarchy. */
-void Service_AddNodes(UA_Server *server, UA_Session *session, const UA_AddNodesRequest *request, UA_AddNodesResponse *response);
+void Service_AddNodes(UA_Server *server, UA_Session *session, const UA_AddNodesRequest *request,
+                      UA_AddNodesResponse *response);
 
 /** Used to add one or more References to one or more Nodes. */
-void Service_AddReferences(UA_Server *server, UA_Session *session, const UA_AddReferencesRequest *request, UA_AddReferencesResponse *response);
+void Service_AddReferences(UA_Server *server, UA_Session *session, const UA_AddReferencesRequest *request,
+                           UA_AddReferencesResponse *response);
 
 /** Used to delete one or more Nodes from the AddressSpace. */
-void Service_DeleteNodes(UA_Server *server, UA_Session *session, const UA_DeleteNodesRequest *request, UA_DeleteNodesResponse *response);
+void Service_DeleteNodes(UA_Server *server, UA_Session *session, const UA_DeleteNodesRequest *request,
+                         UA_DeleteNodesResponse *response);
 
 /** Used to delete one or more References of a Node. */
-void Service_DeleteReferences(UA_Server *server, UA_Session *session, const UA_DeleteReferencesRequest *request, UA_DeleteReferencesResponse *response);
+void Service_DeleteReferences(UA_Server *server, UA_Session *session, const UA_DeleteReferencesRequest *request,
+                              UA_DeleteReferencesResponse *response);
 
 /** @} */
 
@@ -140,13 +146,11 @@ void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *sessio
 // Service_BrowseNext
 
 // Service_RegisterNodes
-void Service_RegisterNodes(UA_Server *server, UA_Session *session,
-                                           const UA_RegisterNodesRequest *request,
-                                           UA_RegisterNodesResponse *response);
+void Service_RegisterNodes(UA_Server *server, UA_Session *session, const UA_RegisterNodesRequest *request,
+                           UA_RegisterNodesResponse *response);
 // Service_UnregisterNodes
-void Service_UnregisterNodes(UA_Server *server, UA_Session *session,
-                                           const UA_UnregisterNodesRequest *request,
-                                           UA_UnregisterNodesResponse *response);
+void Service_UnregisterNodes(UA_Server *server, UA_Session *session, const UA_UnregisterNodesRequest *request,
+                             UA_UnregisterNodesResponse *response);
 /** @} */
 
 /**
@@ -181,7 +185,8 @@ void Service_UnregisterNodes(UA_Server *server, UA_Session *session,
  * allows Clients to read the entire set of indexed values as a composite, to
  * read individual elements or to read ranges of elements of the composite.
  */
-void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *request, UA_ReadResponse *response);
+void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *request,
+                  UA_ReadResponse *response);
 // Service_HistoryRead
 
 /**
@@ -190,7 +195,8 @@ void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *
  * allows Clients to write the entire set of indexed values as a composite, to
  * write individual elements or to write ranges of elements of the composite.
  */
-void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request, UA_WriteResponse *response);
+void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request,
+                   UA_WriteResponse *response);
 // Service_HistoryUpdate
 /** @} */