Browse Source

adding stateless features that can be turned on with EXTENSION_STATELESS
if doing so a simplest client "statelessClient" will be compiled to test

Stasik0 10 years ago
parent
commit
8e4c7b9277
3 changed files with 213 additions and 3 deletions
  1. 8 0
      CMakeLists.txt
  2. 123 0
      examples/statelessClient.c
  3. 82 3
      src/server/ua_server_binary.c

+ 8 - 0
CMakeLists.txt

@@ -58,6 +58,14 @@ endif()
 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMakeModules")
 set(generate_src_options) # the options for the tools that generate code from xml-schema definitions
 
+## extensions
+option(EXTENSION_STATELESS "Enable stateless extension" OFF)
+if(EXTENSION_STATELESS)
+	message(STATUS "Extensions: enabling stateless")
+	add_definitions(-DEXTENSION_STATELESS)
+	add_executable(statelessClient $<TARGET_OBJECTS:open62541-objects> examples/statelessClient.c)
+endif()
+
 ## self-signed certificates
 option(ENABLE_SELFSIGNED "Enable self-signed certificates" OFF)
 if(ENABLE_SELFSIGNED)

+ 123 - 0
examples/statelessClient.c

@@ -0,0 +1,123 @@
+/*
+	C ECHO client example using sockets
+ */
+#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
+
+#include "ua_transport_generated.h"
+#include "ua_namespace_0.h"
+#include "ua_util.h"
+
+
+int main(int argc , char *argv[])
+{
+	int sock;
+	struct sockaddr_in server;
+	UA_ByteString message;
+	message.data = (UA_Byte*)malloc(1000*sizeof(UA_Byte));
+	message.length = 1000;
+	UA_UInt32 messageEncodedLength = 0;
+	UA_Byte server_reply[2000];
+	unsigned int messagepos = 0;
+
+	//Create socket
+	sock = socket(AF_INET , SOCK_STREAM , 0);
+	if (sock == -1)
+	{
+		printf("Could not create socket");
+	}
+	server.sin_addr.s_addr = inet_addr("127.0.0.1");
+	server.sin_family = AF_INET;
+	server.sin_port = htons( 16664 );
+
+	//Connect to remote server
+	if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0)
+	{
+		perror("connect failed. Error");
+		return 1;
+	}
+
+
+	UA_TcpMessageHeader reqTcpHeader;
+	UA_UInt32 reqSecureChannelId = 0;
+	UA_UInt32 reqTokenId = 0;
+	UA_SequenceHeader reqSequenceHeader;
+	UA_NodeId reqRequestType;
+	UA_ReadRequest req;
+	UA_RequestHeader reqHeader;
+	UA_NodeId reqHeaderAuthToken;
+	UA_ExtensionObject reqHeaderAdditionalHeader;
+
+	UA_NodeId_init(&reqRequestType);
+	reqRequestType.identifierType = UA_NODEIDTYPE_NUMERIC;
+	reqRequestType.identifier.numeric = 631; //read request
+
+	UA_ReadRequest_init(&req);
+	req.requestHeader = reqHeader;
+	UA_RequestHeader_init(&(req.requestHeader));
+	req.requestHeader.authenticationToken = reqHeaderAuthToken;
+	UA_NodeId_init(&(req.requestHeader.authenticationToken));
+	req.requestHeader.additionalHeader = reqHeaderAdditionalHeader;
+	UA_ExtensionObject_init(&(req.requestHeader.additionalHeader));
+
+	UA_Array_new((void **)&req.nodesToRead, 1, &UA_TYPES[UA_READVALUEID]);
+	req.nodesToReadSize = 1;
+
+	UA_ReadValueId_init(&(req.nodesToRead[0]));
+	req.nodesToRead[0].attributeId = 13; //UA_ATTRIBUTEID_VALUE
+	UA_NodeId_init(&(req.nodesToRead[0].nodeId));
+	req.nodesToRead[0].nodeId.identifierType = UA_NODEIDTYPE_NUMERIC;
+	req.nodesToRead[0].nodeId.identifier.numeric = 2255;
+	UA_QualifiedName_init(&(req.nodesToRead[0].dataEncoding));
+
+
+	messageEncodedLength = UA_TcpMessageHeader_calcSizeBinary(&reqTcpHeader) +
+			UA_UInt32_calcSizeBinary(&reqSecureChannelId)+
+			UA_UInt32_calcSizeBinary(&reqTokenId)+
+			UA_SequenceHeader_calcSizeBinary(&reqSequenceHeader)+
+			UA_NodeId_calcSizeBinary(&reqRequestType) +
+			UA_ReadRequest_calcSizeBinary(&req);
+
+	UA_TcpMessageHeader_init(&reqTcpHeader);
+	reqTcpHeader.messageType = UA_MESSAGETYPE_MSG;
+	reqTcpHeader.messageSize = messageEncodedLength;
+	reqTcpHeader.isFinal = 'F';
+
+
+	UA_TcpMessageHeader_encodeBinary(&reqTcpHeader, &message, &messagepos);
+	UA_UInt32_encodeBinary(&reqSecureChannelId, &message, &messagepos);
+	UA_UInt32_encodeBinary(&reqTokenId, &message, &messagepos);
+	UA_SequenceHeader_encodeBinary(&reqSequenceHeader, &message, &messagepos);
+	UA_NodeId_encodeBinary(&reqRequestType, &message, &messagepos);
+	UA_ReadRequest_encodeBinary(&req, &message, &messagepos);
+
+
+	//Send some data
+	if( send(sock , message.data, messagepos , 0) < 0)
+	{
+		puts("Send failed");
+		return 1;
+	}
+
+	//Receive a reply from the server
+	int received = recv(sock , server_reply , 2000 , 0);
+	if(received < 0)
+	{
+		puts("recv failed");
+		return 1;
+	}
+
+
+	for(int i=0;i<received;i++){
+		  //show only printable ascii
+		  if(server_reply[i] >= 32 && server_reply[i]<= 126)
+			  printf("%c",server_reply[i]);
+	}
+	printf("\n");
+	close(sock);
+	return 0;
+}

+ 82 - 3
src/server/ua_server_binary.c

@@ -146,13 +146,37 @@ static void init_response_header(const UA_RequestHeader *p, UA_ResponseHeader *r
         responseType = requestType.nodeId.identifier.numeric + 3;                                         \
 } while(0)
 
+#ifdef EXTENSION_STATELESS
+#define INVOKE_STATELESS_SERVICE(TYPE) do { 															  \
+        UA_##TYPE##Request p;                                                                             \
+        UA_##TYPE##Response r;                                                                            \
+        CHECK_PROCESS(UA_##TYPE##Request_decodeBinary(msg, pos, &p),; );                                  \
+        UA_##TYPE##Response_init(&r);                                                                     \
+        init_response_header(&p.requestHeader, &r.responseHeader);                                        \
+        DBG_VERBOSE(printf("Anonymous Invoke Service: %s\n", # TYPE));                                    \
+        Service_##TYPE(server, &anonymousSession, &p, &r);                                                \
+        DBG_VERBOSE(printf("Finished Anonymous Service: %s\n", # TYPE));                                  \
+        UA_ByteString_newMembers(message, UA_##TYPE##Response_calcSizeBinary(&r));                        \
+        UA_##TYPE##Response_encodeBinary(&r, message, &sendOffset);                                       \
+        UA_##TYPE##Request_deleteMembers(&p);                                                             \
+        UA_##TYPE##Response_deleteMembers(&r);                                                            \
+        responseType = requestType.nodeId.identifier.numeric + 3;                                         \
+} while(0)
+#endif
+
 static void processMessage(UA_Connection *connection, UA_Server *server, const UA_ByteString *msg, UA_UInt32 *pos) {
     // 1) Read in the securechannel
     UA_UInt32 secureChannelId;
     UA_UInt32_decodeBinary(msg, pos, &secureChannelId);
 
-    UA_SecureChannel *channel;
+    UA_SecureChannel *channel = UA_NULL;
+#ifdef EXTENSION_STATELESS
+    if(connection->channel != UA_NULL && secureChannelId != 0){
+#endif
     UA_SecureChannelManager_get(&server->secureChannelManager, secureChannelId, &channel);
+#ifdef EXTENSION_STATELESS
+    }
+#endif
 
     // 2) Read the security header
     UA_UInt32 tokenId;
@@ -160,11 +184,17 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
     UA_SequenceHeader sequenceHeader;
     CHECK_PROCESS(UA_SequenceHeader_decodeBinary(msg, pos, &sequenceHeader),; );
 
+#ifdef EXTENSION_STATELESS
+    if(connection->channel != UA_NULL && secureChannelId != 0){
+#endif
     channel->sequenceNumber = sequenceHeader.sequenceNumber;
     channel->requestId = sequenceHeader.requestId;
     // todo
     //UA_SecureChannel_checkSequenceNumber(channel,sequenceHeader.sequenceNumber);
     //UA_SecureChannel_checkRequestId(channel,sequenceHeader.requestId);
+#ifdef EXTENSION_STATELESS
+    }
+#endif
 
     // 3) Read the nodeid of the request
     UA_ExpandedNodeId requestType;
@@ -181,6 +211,26 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
     UA_ByteString *message = &responseBufs[1];
 
     UA_UInt32 sendOffset = 0;
+#ifdef EXTENSION_STATELESS
+    if(connection->channel == UA_NULL && secureChannelId == 0){
+     //stateless service calls - will pass UA_NULL as session
+     //fixme: maybe we need to pass a magic number instead of UA_NULL e.g. just a 42
+     switch(requestType.nodeId.identifier.numeric - 2) {
+     case UA_READREQUEST_NS0:
+     INVOKE_STATELESS_SERVICE(Read);
+     break;
+
+     case UA_WRITEREQUEST_NS0:
+     INVOKE_STATELESS_SERVICE(Write);
+     break;
+
+     case UA_BROWSEREQUEST_NS0:
+     INVOKE_STATELESS_SERVICE(Browse);
+     break;
+     }
+     //FIXME: a copy-pasted default case, but I did not want any duplications
+     }else{
+#endif
     //subtract UA_ENCODINGOFFSET_BINARY for binary encoding
     switch(requestType.nodeId.identifier.numeric - UA_ENCODINGOFFSET_BINARY) {
     case UA_GETENDPOINTSREQUEST_NS0: {
@@ -296,7 +346,9 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
         }
     	break;
     }
-
+#ifdef EXTENSION_STATELESS
+    }
+#endif
     // 5) Build the header
     UA_NodeId response_nodeid = { .namespaceIndex     = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
                                   .identifier.numeric = responseType }; // add 2 for binary encoding
@@ -313,6 +365,11 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
     UA_UInt32 rpos = 0;
     UA_TcpMessageHeader_encodeBinary(&respHeader, header, &rpos);
 
+
+#ifdef EXTENSION_STATELESS
+	if(connection->channel != UA_NULL && secureChannelId != 0){
+#endif
+
     UA_UInt32_encodeBinary(&channel->securityToken.channelId, header, &rpos); // channel id
     UA_UInt32_encodeBinary(&channel->securityToken.tokenId, header, &rpos); // algorithm security header
 
@@ -320,7 +377,15 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
     UA_UInt32_encodeBinary(&channel->requestId, header, &rpos);
 
     UA_NodeId_encodeBinary(&response_nodeid, header, &rpos); // add payload type
-
+#ifdef EXTENSION_STATELESS
+	}else{
+	UA_UInt32 zeroInt = 0;
+	UA_UInt32_encodeBinary(&zeroInt, header, &rpos);
+	UA_UInt32_encodeBinary(&zeroInt, header, &rpos);
+	UA_UInt32_encodeBinary(&zeroInt, header, &rpos);
+	UA_UInt32_encodeBinary(&zeroInt, header, &rpos);
+	}
+#endif
     // sign data
 
     // encrypt data
@@ -349,6 +414,10 @@ void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection
     // todo: test how far pos advanced must be equal to what is said in the messageheader
     do {
         retval = UA_TcpMessageHeader_decodeBinary(msg, &pos, &tcpMessageHeader);
+        if(tcpMessageHeader.messageSize < 8){
+        	printf("The announced size of the message is illegal (smaller than 8), skipping the whole packet\n");
+        	return;
+        }
         UA_UInt32 targetpos = pos - 8 + tcpMessageHeader.messageSize;
         if(retval == UA_STATUSCODE_GOOD) {
             // none of the process-functions returns an error its all contained inside.
@@ -369,6 +438,16 @@ void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection
                     processMessage(connection, server, msg, &pos);
                     break;
                 }
+#ifdef EXTENSION_STATELESS
+                //process messages with session zero
+                if(connection->state == UA_CONNECTION_OPENING &&
+                		connection->channel == UA_NULL) {
+                	processMessage(connection, server, msg, &pos);
+                	//fixme: we need to think about keepalive
+                	connection->close(connection->callbackHandle);
+                	break;
+                }
+#endif
 
             case UA_MESSAGETYPE_CLO:
                 connection->state = UA_CONNECTION_CLOSING;