Переглянути джерело

move chunk processing to src/ua_securechannel.c; use in both client and server

Julius Pfrommer 8 роки тому
батько
коміт
3ce335cf22
4 змінених файлів з 291 додано та 311 видалено
  1. 79 69
      src/client/ua_client.c
  2. 85 204
      src/server/ua_server_binary.c
  3. 120 26
      src/ua_securechannel.c
  4. 7 12
      src/ua_securechannel.h

+ 79 - 69
src/client/ua_client.c

@@ -685,24 +685,84 @@ UA_StatusCode UA_Client_manuallyRenewSecureChannel(UA_Client *client) {
 /* Raw Services */
 /****************/
 
+struct ResponseDescription {
+    UA_Client *client;
+    UA_Boolean processed;
+    UA_UInt32 requestId;
+    void *response;
+    const UA_DataType *responseType;
+};
+
+static void
+processServiceResponse(struct ResponseDescription *rd, UA_SecureChannel *channel,
+                       UA_MessageType messageType, UA_UInt32 requestId, UA_ByteString *message) {
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    UA_ResponseHeader *respHeader = (UA_ResponseHeader*)rd->response;
+    rd->processed = true;
+
+    /* Check that the request id matches */
+    /* Todo: we need to demux async responses since a publish responses may come at any time */
+    if(requestId != rd->requestId) {
+        UA_LOG_ERROR(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
+                     "Reply answers the wrong requestId. Async services are not yet implemented.");
+        retval = UA_STATUSCODE_BADINTERNALERROR;
+        goto finish;
+    }
+
+    /* Check that the response type matches */
+    const UA_NodeId expectedNodeId = UA_NODEID_NUMERIC(0, rd->responseType->binaryEncodingId);
+    const UA_NodeId serviceFaultNodeId =
+        UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_SERVICEFAULT].binaryEncodingId);
+    size_t offset = 0;
+    UA_NodeId responseId;
+    retval = UA_NodeId_decodeBinary(message, &offset, &responseId);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto finish;
+    if(!UA_NodeId_equal(&responseId, &expectedNodeId)) {
+        if(UA_NodeId_equal(&responseId, &serviceFaultNodeId)) {
+            /* Take the statuscode from the servicefault */
+            retval = UA_decodeBinary(message, &offset, rd->response, &UA_TYPES[UA_TYPES_SERVICEFAULT]);
+        } else {
+            UA_LOG_ERROR(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
+                         "Reply answers the wrong request. Expected ns=%i,i=%i. But retrieved ns=%i,i=%i",
+                         expectedNodeId.namespaceIndex, expectedNodeId.identifier.numeric,
+                         responseId.namespaceIndex, responseId.identifier.numeric);
+            UA_NodeId_deleteMembers(&responseId);
+            retval = UA_STATUSCODE_BADINTERNALERROR;
+        }
+        goto finish;
+    }
+
+    /* Decode the response */
+    retval = UA_decodeBinary(message, &offset, rd->response, rd->responseType);
+
+ finish:
+    if(retval == UA_STATUSCODE_GOOD) {
+        UA_LOG_DEBUG(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
+                     "Received a response of type %i", responseId.identifier.numeric);
+    } else {
+        if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED)
+            retval = UA_STATUSCODE_BADRESPONSETOOLARGE;
+        UA_LOG_INFO(rd->client->config.logger, UA_LOGCATEGORY_CLIENT, "Error receiving the response");
+        respHeader->serviceResult = retval;
+    }
+}
+
 void __UA_Client_Service(UA_Client *client, const void *r, const UA_DataType *requestType,
                          void *response, const UA_DataType *responseType) {
-    /* Requests always begin witih a RequestHeader, therefore we can cast. */
-    UA_RequestHeader *request = (void*)(uintptr_t)r;
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    UA_init(response, responseType);
     UA_ResponseHeader *respHeader = (UA_ResponseHeader*)response;
 
-    /* make sure we have a valid session */
-    retval = UA_Client_manuallyRenewSecureChannel(client);
+    /* Make sure we have a valid session */
+    UA_StatusCode retval = UA_Client_manuallyRenewSecureChannel(client);
     if(retval != UA_STATUSCODE_GOOD) {
         respHeader->serviceResult = retval;
         client->state = UA_CLIENTSTATE_ERRORED;
         return;
     }
 
-    /* handling request parameters */
+    /* Handling request parameters */
     //here const *r is 'violated'
+    UA_RequestHeader *request = (void*)(uintptr_t)r;
     UA_NodeId_copy(&client->authenticationToken, &request->authenticationToken);
     request->timestamp = UA_DateTime_now();
     request->requestHandle = ++client->requestHandle;
@@ -718,11 +778,15 @@ void __UA_Client_Service(UA_Client *client, const void *r, const UA_DataType *re
         else
             respHeader->serviceResult = retval;
         client->state = UA_CLIENTSTATE_ERRORED;
-        return;
+        goto finish;
     }
 
+    /* Requests always begin witih a RequestHeader, therefore we can cast. */
+    UA_init(response, responseType);
+    struct ResponseDescription rd = (struct ResponseDescription){
+        client, false, requestId, response, responseType};
+
     /* Retrieve the response */
-    // Todo: push this into the generic securechannel implementation for client and server
     UA_ByteString reply;
     UA_ByteString_init(&reply);
     UA_Boolean realloced = false;
@@ -732,68 +796,14 @@ void __UA_Client_Service(UA_Client *client, const void *r, const UA_DataType *re
         if(retval != UA_STATUSCODE_GOOD) {
             respHeader->serviceResult = retval;
             client->state = UA_CLIENTSTATE_ERRORED;
-            //free token
-            UA_NodeId_deleteMembers(&request->authenticationToken);
-            return;
+            goto finish;
         }
-    } while(!reply.data);
-
-    size_t offset = 0;
-    UA_SecureConversationMessageHeader msgHeader;
-    retval |= UA_SecureConversationMessageHeader_decodeBinary(&reply, &offset, &msgHeader);
-    UA_SymmetricAlgorithmSecurityHeader symHeader;
-    retval |= UA_SymmetricAlgorithmSecurityHeader_decodeBinary(&reply, &offset, &symHeader);
-    UA_SequenceHeader seqHeader;
-    retval |= UA_SequenceHeader_decodeBinary(&reply, &offset, &seqHeader);
-    UA_NodeId responseId;
-    retval |= UA_NodeId_decodeBinary(&reply, &offset, &responseId);
-    UA_NodeId expectedNodeId = UA_NODEID_NUMERIC(0, responseType->binaryEncodingId);
-
-    if(retval != UA_STATUSCODE_GOOD)
-        goto finish;
-
-    /* Does the sequence number match? */
-    retval = UA_SecureChannel_processSequenceNumber(seqHeader.sequenceNumber, client->channel);
-    if (retval != UA_STATUSCODE_GOOD){
-        UA_LOG_INFO_CHANNEL(client->config.logger, client->channel,
-                            "The sequence number was not increased by one. Got %i, expected %i",
-                            seqHeader.sequenceNumber, client->channel->receiveSequenceNumber + 1);
-        goto finish;
-    }
-
-    /* Todo: we need to demux responses since a publish responses may come at any time */
-    if(!UA_NodeId_equal(&responseId, &expectedNodeId) || seqHeader.requestId != requestId) {
-        if(responseId.identifier.numeric != UA_TYPES[UA_TYPES_SERVICEFAULT].binaryEncodingId) {
-            UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
-                         "Reply answers the wrong request. Expected ns=%i,i=%i. But retrieved ns=%i,i=%i",
-                         expectedNodeId.namespaceIndex, expectedNodeId.identifier.numeric,
-                         responseId.namespaceIndex, responseId.identifier.numeric);
-            respHeader->serviceResult = UA_STATUSCODE_BADINTERNALERROR;
-        } else
-            retval = UA_decodeBinary(&reply, &offset, respHeader, &UA_TYPES[UA_TYPES_SERVICEFAULT]);
-        goto finish;
-    }
-
-    retval = UA_decodeBinary(&reply, &offset, response, responseType);
-    if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED)
-        retval = UA_STATUSCODE_BADRESPONSETOOLARGE;
+        if(!reply.data)
+            break;
+        UA_SecureChannel_processChunks(client->channel, &reply,
+                                       (UA_ProcessMessageCallback*)processServiceResponse, &rd);
+    } while(!rd.processed);
 
  finish:
-    UA_SymmetricAlgorithmSecurityHeader_deleteMembers(&symHeader);
-    if(!realloced)
-        client->connection->releaseRecvBuffer(client->connection, &reply);
-    else
-        UA_ByteString_deleteMembers(&reply);
-
-    if(retval != UA_STATUSCODE_GOOD){
-        UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_CLIENT, "Error receiving the response");
-        client->state = UA_CLIENTSTATE_FAULTED;
-        respHeader->serviceResult = retval;
-    } else {
-      client->state = UA_CLIENTSTATE_CONNECTED;
-    }
-    //free token
     UA_NodeId_deleteMembers(&request->authenticationToken);
-    UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_CLIENT,
-                 "Received a response of type %i", responseId.identifier.numeric);
 }

+ 85 - 204
src/server/ua_server_binary.c

@@ -35,34 +35,6 @@ sendError(UA_SecureChannel *channel, const UA_ByteString *msg,
     UA_ResponseHeader_deleteMembers(responseHeader);
 }
 
-/* Returns a complete decoded request (without securechannel headers + padding)
-   or UA_BYTESTRING_NULL */
-static UA_ByteString
-processChunk(UA_SecureChannel *channel, UA_Server *server,
-             const UA_TcpMessageHeader *messageHeader, UA_UInt32 requestId,
-             const UA_ByteString *msg, size_t offset, size_t chunksize,
-             UA_Boolean *deleteRequest) {
-    UA_ByteString bytes = UA_BYTESTRING_NULL;
-    switch(messageHeader->messageTypeAndChunkType & 0xff000000) {
-    case UA_CHUNKTYPE_INTERMEDIATE:
-        UA_LOG_TRACE_CHANNEL(server->config.logger, channel, "Chunk message");
-        UA_SecureChannel_appendChunk(channel, requestId, msg, offset, chunksize);
-        break;
-    case UA_CHUNKTYPE_FINAL:
-        UA_LOG_TRACE_CHANNEL(server->config.logger, channel, "Final chunk message");
-        bytes = UA_SecureChannel_finalizeChunk(channel, requestId, msg, offset,
-                                               chunksize, deleteRequest);
-        break;
-    case UA_CHUNKTYPE_ABORT:
-        UA_LOG_INFO_CHANNEL(server->config.logger, channel, "Chunk aborted");
-        UA_SecureChannel_removeChunk(channel, requestId);
-        break;
-    default:
-        UA_LOG_INFO_CHANNEL(server->config.logger, channel, "Unknown chunk type");
-    }
-    return bytes;
-}
-
 static void
 getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
                    const UA_DataType **responseType, UA_Service *service,
@@ -329,35 +301,31 @@ static void processHEL(UA_Connection *connection, const UA_ByteString *msg, size
 
 /* OPN -> Open up/renew the securechannel */
 static void
-processOPN(UA_Connection *connection, UA_Server *server,
-           const UA_ByteString *msg, size_t *offset) {
-    if(connection->state != UA_CONNECTION_ESTABLISHED) {
-        connection->close(connection);
-        return;
-    }
-
-    UA_UInt32 channelId;
-    UA_StatusCode retval = UA_UInt32_decodeBinary(msg, offset, &channelId);
-
+processOPN(UA_Server *server, UA_Connection *connection,
+           UA_UInt32 channelId, const UA_ByteString *msg) {
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    /* Called before HEL */
+    if(connection->state != UA_CONNECTION_ESTABLISHED)
+        retval = UA_STATUSCODE_BADCOMMUNICATIONERROR;
     /* Opening up a channel with a channelid already set */
     if(!connection->channel && channelId != 0)
-        retval |= UA_STATUSCODE_BADREQUESTTYPEINVALID;
+        retval = UA_STATUSCODE_BADCOMMUNICATIONERROR;
     /* Renew a channel with the wrong channelid */
     if(connection->channel && channelId != connection->channel->securityToken.channelId)
-        retval |= UA_STATUSCODE_BADREQUESTTYPEINVALID;
+        retval = UA_STATUSCODE_BADCOMMUNICATIONERROR;
 
     /* Decode the request */
     UA_AsymmetricAlgorithmSecurityHeader asymHeader;
     UA_SequenceHeader seqHeader;
     UA_NodeId requestType;
     UA_OpenSecureChannelRequest r;
+    size_t offset = 0;
+    retval |= UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(msg, &offset, &asymHeader);
+    retval |= UA_SequenceHeader_decodeBinary(msg, &offset, &seqHeader);
+    retval |= UA_NodeId_decodeBinary(msg, &offset, &requestType);
+    retval |= UA_OpenSecureChannelRequest_decodeBinary(msg, &offset, &r);
 
-    retval |= UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(msg, offset, &asymHeader);
-    retval |= UA_SequenceHeader_decodeBinary(msg, offset, &seqHeader);
-    retval |= UA_NodeId_decodeBinary(msg, offset, &requestType);
-    retval |= UA_OpenSecureChannelRequest_decodeBinary(msg, offset, &r);
-
-    /* Could not decode or wrong service type */
+    /* Error occured */
     if(retval != UA_STATUSCODE_GOOD || requestType.identifier.numeric != 446) {
         UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
         UA_NodeId_deleteMembers(&requestType);
@@ -372,9 +340,8 @@ processOPN(UA_Connection *connection, UA_Server *server,
     Service_OpenSecureChannel(server, connection, &r, &p);
     UA_OpenSecureChannelRequest_deleteMembers(&r);
 
-    UA_SecureChannel *channel = connection->channel;
-
     /* Opening the channel failed */
+    UA_SecureChannel *channel = connection->channel;
     if(!channel) {
         UA_OpenSecureChannelResponse_deleteMembers(&p);
         UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
@@ -433,8 +400,8 @@ processOPN(UA_Connection *connection, UA_Server *server,
 }
 
 static void
-processRequest(UA_SecureChannel *channel, UA_Server *server,
-               UA_UInt32 requestId, const UA_ByteString *msg) {
+processMSG(UA_Server *server, UA_SecureChannel *channel,
+           UA_UInt32 requestId, const UA_ByteString *msg) {
     /* At 0, the nodeid starts... */
     size_t ppos = 0;
     size_t *offset = &ppos;
@@ -596,182 +563,96 @@ processRequest(UA_SecureChannel *channel, UA_Server *server,
     UA_deleteMembers(response, responseType);
 }
 
-/* MSG -> Normal request */
+/* Takes decoded messages starting at the nodeid of the content type. Only OPN
+ * messages start at the asymmetricalgorithmsecurityheader and are not
+ * decoded. */
 static void
-processMSG(UA_Connection *connection, UA_Server *server,
-           const UA_TcpMessageHeader *messageHeader,
-           const UA_ByteString *msg, size_t *offset) {
-    /* Decode the header */
-    UA_UInt32 channelId = 0;
-    UA_UInt32 tokenId = 0;
-    UA_SequenceHeader sequenceHeader;
-    UA_StatusCode retval = UA_UInt32_decodeBinary(msg, offset, &channelId);
-    retval |= UA_UInt32_decodeBinary(msg, offset, &tokenId);
-    retval |= UA_SequenceHeader_decodeBinary(msg, offset, &sequenceHeader);
-    if(retval != UA_STATUSCODE_GOOD)
-        return;
-
-    /* Get the SecureChannel */
-    UA_SecureChannel *channel = connection->channel;
-    UA_SecureChannel anonymousChannel; /* use if no channel specified */
-    if(!channel) {
-        UA_SecureChannel_init(&anonymousChannel);
-        anonymousChannel.connection = connection;
-        channel = &anonymousChannel;
-    }
-
-    /* Is the channel attached to connection? */
-    if(channelId != channel->securityToken.channelId) {
-        UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                    "Connection %i | Received MSG with the channel id %i not bound to the connection",
-                    connection->sockfd, channelId);
-        Service_CloseSecureChannel(server, channel);
-        connection->close(connection);
-        return;
-    }
-
-    /* Does the sequence number match? */
-    retval = UA_SecureChannel_processSequenceNumber(sequenceHeader.sequenceNumber, channel);
-    if (retval != UA_STATUSCODE_GOOD){
-        UA_LOG_INFO_CHANNEL(server->config.logger, channel,
-                            "The sequence number was not increased by one. Got %i, expected %i",
-                            sequenceHeader.sequenceNumber, channel->receiveSequenceNumber + 1);
-        sendError(channel, msg, *offset, &UA_TYPES[UA_TYPES_SERVICEFAULT],
-                  sequenceHeader.requestId, UA_STATUSCODE_BADSECURITYCHECKSFAILED);
+UA_Server_processSecureChannelMessage(UA_Server *server, UA_SecureChannel *channel,
+                                      UA_MessageType messagetype, UA_UInt32 requestId,
+                                      const UA_ByteString *message) {
+    switch(messagetype) {
+    case UA_MESSAGETYPE_HEL:
+        UA_LOG_TRACE_CHANNEL(server->config.logger, channel,
+                             "Cannot process a HEL on an open channel");
+        break;
+    case UA_MESSAGETYPE_OPN:
+        UA_LOG_TRACE_CHANNEL(server->config.logger, channel,
+                             "Process an OPN on an open channel");
+        processOPN(server, channel->connection, channel->securityToken.channelId, message);
+        break;
+    case UA_MESSAGETYPE_MSG:
+        UA_LOG_TRACE_CHANNEL(server->config.logger, channel,
+                             "Process a MSG", connection->sockfd);
+        processMSG(server, channel, requestId, message);
+        break;
+    case UA_MESSAGETYPE_CLO:
+        UA_LOG_TRACE_CHANNEL(server->config.logger, channel,
+                             "Process a CLO", connection->sockfd);
         Service_CloseSecureChannel(server, channel);
-        connection->close(connection);
-        return;
-    }
-
-    /* Does the token match? */
-    if(tokenId != channel->securityToken.tokenId) {
-        if(tokenId != channel->nextSecurityToken.tokenId) {
-            UA_LOG_INFO_CHANNEL(server->config.logger, channel,
-                                "Request with a wrong security token. Closing the SecureChannel.");
-            Service_CloseSecureChannel(server, channel);
-            connection->close(connection);
-            return;
-        }
-        UA_SecureChannel_revolveTokens(channel);
-    }
-
-    /* Process chunk to get complete request */
-    UA_Boolean deleteRequest = false;
-    UA_ByteString request = processChunk(channel, server, messageHeader, sequenceHeader.requestId,
-                                         msg, *offset, messageHeader->messageSize - 24, &deleteRequest);
-    *offset += (messageHeader->messageSize - 24);
-    if(request.length > 0) {
-        /* Process the request */
-        processRequest(channel, server, sequenceHeader.requestId, &request);
-        if(deleteRequest)
-            UA_ByteString_deleteMembers(&request);
+        break;
+    default:
+        UA_LOG_TRACE_CHANNEL(server->config.logger, channel,
+                             "Unknown message type");
     }
-
-    /* Clean up a possible anonymous channel */
-    if(channel == &anonymousChannel)
-        UA_SecureChannel_deleteMembersCleanup(channel);
 }
 
-/* CLO -> Close the secure channel */
-static void
-processCLO(UA_Connection *connection, UA_Server *server,
-           const UA_ByteString *msg, size_t *offset) {
-    UA_UInt32 channelId;
-    UA_UInt32 tokenId = 0;
-    UA_SequenceHeader sequenceHeader;
-    UA_StatusCode retval = UA_UInt32_decodeBinary(msg, offset, &channelId);
-    retval |= UA_UInt32_decodeBinary(msg, offset, &tokenId);
-    retval |= UA_SequenceHeader_decodeBinary(msg, offset, &sequenceHeader);
-    if(retval != UA_STATUSCODE_GOOD)
-        return;
-
-    UA_SecureChannel *channel = connection->channel;
-    if(!channel || channel->securityToken.channelId != channelId ||
-       channel->securityToken.tokenId != tokenId)
-        return;
-
-    if(sequenceHeader.sequenceNumber != channel->receiveSequenceNumber + 1)
-        return;
-
-    Service_CloseSecureChannel(server, connection->channel);
-}
-
-/* Process binary message received from Connection dose not modify UA_ByteString
- * you have to free it youself. use of connection->getSendBuffer() and
- * connection->send() to answer Message */
+/* Takes the raw message from the network layer */
 void
 UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection,
-                               const UA_ByteString *msg) {
-    size_t offset= 0;
-    UA_TcpMessageHeader tcpMessageHeader;
-    do {
-        /* Decode the message header */
-        UA_StatusCode retval = UA_TcpMessageHeader_decodeBinary(msg, &offset, &tcpMessageHeader);
+                               const UA_ByteString *message) {
+    UA_SecureChannel *channel = connection->channel;
+    if(channel) {
+        /* Assemble chunks in the securechannel and process complete messages */
+        UA_StatusCode retval = 
+            UA_SecureChannel_processChunks(channel, message,
+                 (UA_ProcessMessageCallback*)UA_Server_processSecureChannelMessage, server);
+        if(retval != UA_STATUSCODE_GOOD)
+            UA_LOG_TRACE_CHANNEL(server->config.logger, channel,
+                                 "Procesing chunkgs resulted in error code 0x%08x", retval);
+    } else {
+        /* Process messages without a channel and no chunking */
+        size_t offset = 0;
+        UA_TcpMessageHeader tcpMessageHeader;
+        UA_StatusCode retval = UA_TcpMessageHeader_decodeBinary(message, &offset, &tcpMessageHeader);
         if(retval != UA_STATUSCODE_GOOD) {
-            UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                        "Decoding of message header failed on Connection %i",
-                        connection->sockfd);
-            connection->close(connection);
-            break;
-        }
-        if(tcpMessageHeader.messageSize < 16) {
-            UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                        "The message is suspiciously small on Connection %i",
-                        connection->sockfd);
             connection->close(connection);
-            break;
+            return;
         }
 
-        /* Set the expected position after processing the chunk */
-        size_t targetpos = offset - 8 + tcpMessageHeader.messageSize;
-
-        /* Process the message */
+        /* Dispatch according to the message type */
         switch(tcpMessageHeader.messageTypeAndChunkType & 0x00ffffff) {
         case UA_MESSAGETYPE_HEL:
             UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                         "Connection %i | Process a HEL", connection->sockfd);
-            processHEL(connection, msg, &offset);
+                         "Connection %i | Process HEL message", connection->sockfd);
+            processHEL(connection, message, &offset);
             break;
-
-        case UA_MESSAGETYPE_OPN:
+        case UA_MESSAGETYPE_OPN: {
             UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                         "Connection %i | Process a OPN", connection->sockfd);
-            processOPN(connection, server, msg, &offset);
-            break;
-
-        case UA_MESSAGETYPE_MSG:
-#ifndef UA_ENABLE_NONSTANDARD_STATELESS
-            if(connection->state != UA_CONNECTION_ESTABLISHED) {
-                UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                             "Connection %i | Received a MSG, but the connection " \
-                             "is not established", connection->sockfd);
+                         "Connection %i | Process OPN message", connection->sockfd);
+            UA_UInt32 channelId = 0;
+            retval = UA_UInt32_decodeBinary(message, &offset, &channelId);
+            if(retval != UA_STATUSCODE_GOOD)
                 connection->close(connection);
-                return;
-            }
-#endif
+            UA_ByteString offsetMessage = (UA_ByteString){
+                .data = message->data + 12, .length = message->length - 12};
+            processOPN(server, connection, channelId, &offsetMessage);
+            break; }
+        case UA_MESSAGETYPE_MSG:
             UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                         "Connection %i | Process a MSG", connection->sockfd);
-            processMSG(connection, server, &tcpMessageHeader, msg, &offset);
+                         "Connection %i | Processing a MSG message not possible "
+                         "without a SecureChannel", connection->sockfd);
+            connection->close(connection);
             break;
-
         case UA_MESSAGETYPE_CLO:
             UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                         "Connection %i | Process a CLO", connection->sockfd);
-            processCLO(connection, server, msg, &offset);
-            return;
-
+                         "Connection %i | Processing a CLO message not possible "
+                         "without a SecureChannel", connection->sockfd);
+            connection->close(connection);
+            break;
         default:
             UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                         "Connection %i | Unknown chunk type", connection->sockfd);
-        }
-
-        /* Loop to process the next message in the stream */
-        if(offset != targetpos) {
-            UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                         "Connection %i | Message was not entirely processed. " \
-                         "Skip from position %i to position %i; message length is %i",
-                         connection->sockfd, offset, targetpos, msg->length);
-            offset = targetpos;
+                         "Connection %i | Unknown message type", connection->sockfd);
+            connection->close(connection);
         }
-    } while(msg->length > offset);
+    }
 }

+ 120 - 26
src/ua_securechannel.c

@@ -129,6 +129,10 @@ void UA_SecureChannel_revolveTokens(UA_SecureChannel *channel) {
     UA_ChannelSecurityToken_init(&channel->nextSecurityToken);
 }
 
+/***********************/
+/* Send Binary Message */
+/***********************/
+
 static UA_StatusCode
 UA_SecureChannel_sendChunk(UA_ChunkInfo *ci, UA_ByteString *dst, size_t offset) {
     UA_SecureChannel *channel = ci->channel;
@@ -253,9 +257,27 @@ UA_SecureChannel_sendBinaryMessage(UA_SecureChannel *channel, UA_UInt32 requestI
     return UA_SecureChannel_sendChunk(&ci, &message, messagePos);
 }
 
+/***************************/
+/* Process Received Chunks */
+/***************************/
+
+static void
+UA_SecureChannel_removeChunk(UA_SecureChannel *channel, UA_UInt32 requestId) {
+    struct ChunkEntry *ch;
+    LIST_FOREACH(ch, &channel->chunks, pointers) {
+        if(ch->requestId == requestId) {
+            UA_ByteString_deleteMembers(&ch->bytes);
+            LIST_REMOVE(ch, pointers);
+            UA_free(ch);
+            return;
+        }
+    }
+}
+
 /* assume that chunklength fits */
-static void appendChunk(struct ChunkEntry *ch, const UA_ByteString *msg,
-                        size_t offset, size_t chunklength) {
+static void
+appendChunk(struct ChunkEntry *ch, const UA_ByteString *msg,
+            size_t offset, size_t chunklength) {
     UA_Byte* new_bytes = UA_realloc(ch->bytes.data, ch->bytes.length + chunklength);
     if(!new_bytes) {
         UA_ByteString_deleteMembers(&ch->bytes);
@@ -266,11 +288,14 @@ static void appendChunk(struct ChunkEntry *ch, const UA_ByteString *msg,
     ch->bytes.length += chunklength;
 }
 
-void UA_SecureChannel_appendChunk(UA_SecureChannel *channel, UA_UInt32 requestId,
-                                  const UA_ByteString *msg, size_t offset, size_t chunklength) {
+static void
+UA_SecureChannel_appendChunk(UA_SecureChannel *channel, UA_UInt32 requestId,
+                             const UA_ByteString *msg, size_t offset,
+                             size_t chunklength) {
     /* Check if the chunk fits into the message */
     if(msg->length - offset < chunklength) {
-        UA_SecureChannel_removeChunk(channel, requestId); /* can't process all chunks for that request */
+        /* can't process all chunks for that request */
+        UA_SecureChannel_removeChunk(channel, requestId);
         return;
     }
 
@@ -294,11 +319,13 @@ void UA_SecureChannel_appendChunk(UA_SecureChannel *channel, UA_UInt32 requestId
     appendChunk(ch, msg, offset, chunklength);
 }
 
-UA_ByteString UA_SecureChannel_finalizeChunk(UA_SecureChannel *channel, UA_UInt32 requestId,
-                                             const UA_ByteString *msg, size_t offset, size_t chunklength,
-                                             UA_Boolean *deleteChunk) {
+static UA_ByteString
+UA_SecureChannel_finalizeChunk(UA_SecureChannel *channel, UA_UInt32 requestId,
+                               const UA_ByteString *msg, size_t offset,
+                               size_t chunklength, UA_Boolean *deleteChunk) {
     if(msg->length - offset < chunklength) {
-        UA_SecureChannel_removeChunk(channel, requestId); /* can't process all chunks for that request */
+        /* can't process all chunks for that request */
+        UA_SecureChannel_removeChunk(channel, requestId);
         return UA_BYTESTRING_NULL;
     }
 
@@ -323,27 +350,94 @@ UA_ByteString UA_SecureChannel_finalizeChunk(UA_SecureChannel *channel, UA_UInt3
     return bytes;
 }
 
-void UA_SecureChannel_removeChunk(UA_SecureChannel *channel, UA_UInt32 requestId) {
-    struct ChunkEntry *ch;
-    LIST_FOREACH(ch, &channel->chunks, pointers) {
-        if(ch->requestId == requestId) {
-            UA_ByteString_deleteMembers(&ch->bytes);
-            LIST_REMOVE(ch, pointers);
-            UA_free(ch);
-            return;
-        }
-    }
-}
-
-UA_StatusCode UA_SecureChannel_processSequenceNumber (UA_UInt32 SequenceNumber, UA_SecureChannel *channel){
-/* Does the sequence number match? */
+static UA_StatusCode
+UA_SecureChannel_processSequenceNumber(UA_SecureChannel *channel, UA_UInt32 SequenceNumber) {
+    /* Does the sequence number match? */
     if(SequenceNumber != channel->receiveSequenceNumber + 1) {
-        if(channel->receiveSequenceNumber + 1 > 4294966271 && SequenceNumber < 1024) {
+        if(channel->receiveSequenceNumber + 1 > 4294966271 && SequenceNumber < 1024)
             channel->receiveSequenceNumber = SequenceNumber - 1; /* Roll over */
-        } else {
+        else
             return UA_STATUSCODE_BADSECURITYCHECKSFAILED;
-        }
     }
     channel->receiveSequenceNumber++;
     return UA_STATUSCODE_GOOD;
 }
+
+UA_StatusCode
+UA_SecureChannel_processChunks(UA_SecureChannel *channel, const UA_ByteString *chunks,
+                               UA_ProcessMessageCallback callback, void *application) {
+    size_t offset= 0;
+    do {
+        size_t initial_offset = offset;
+        
+        /* Decode header */
+        UA_SecureConversationMessageHeader header;
+        UA_StatusCode retval = UA_SecureConversationMessageHeader_decodeBinary(chunks, &offset, &header);
+        if(retval != UA_STATUSCODE_GOOD)
+            return retval;
+
+        /* Is the channel attached to connection? */
+        if(header.secureChannelId != channel->securityToken.channelId) {
+            //Service_CloseSecureChannel(server, channel);
+            //connection->close(connection);
+            return UA_STATUSCODE_BADCOMMUNICATIONERROR;
+        }
+
+        /* Use requestId = 0 with OPN as argument for the callback */
+        UA_SequenceHeader sequenceHeader;
+        UA_SequenceHeader_init(&sequenceHeader);
+
+        if((header.messageHeader.messageTypeAndChunkType & 0x00ffffff) != UA_MESSAGETYPE_OPN) {
+            /* Check the symmetric security header (not for OPN) */
+            UA_UInt32 tokenId = 0;
+            retval |= UA_UInt32_decodeBinary(chunks, &offset, &tokenId);
+            retval |= UA_SequenceHeader_decodeBinary(chunks, &offset, &sequenceHeader);
+            if(retval != UA_STATUSCODE_GOOD)
+                return UA_STATUSCODE_BADCOMMUNICATIONERROR;
+
+            /* Does the token match? */
+            if(tokenId != channel->securityToken.tokenId) {
+                if(tokenId != channel->nextSecurityToken.tokenId)
+                    return UA_STATUSCODE_BADCOMMUNICATIONERROR;
+                UA_SecureChannel_revolveTokens(channel);
+            }
+
+            /* Does the sequence number match? */
+            retval = UA_SecureChannel_processSequenceNumber(channel, sequenceHeader.sequenceNumber);
+            if(retval != UA_STATUSCODE_GOOD)
+                return UA_STATUSCODE_BADCOMMUNICATIONERROR;
+        }
+
+        /* Process chunk */
+        size_t processed_header = offset - initial_offset;
+        switch(header.messageHeader.messageTypeAndChunkType & 0xff000000) {
+        case UA_CHUNKTYPE_INTERMEDIATE:
+            UA_SecureChannel_appendChunk(channel, sequenceHeader.requestId, chunks, offset,
+                                         header.messageHeader.messageSize - processed_header);
+            break;
+        case UA_CHUNKTYPE_FINAL: {
+            UA_Boolean deleteMessage = false;
+            UA_ByteString message =
+                UA_SecureChannel_finalizeChunk(channel, sequenceHeader.requestId, chunks, offset,
+                                               header.messageHeader.messageSize - processed_header,
+                                               &deleteMessage);
+            if(message.length > 0) {
+                callback(application, channel, header.messageHeader.messageTypeAndChunkType & 0x00ffffff,
+                         sequenceHeader.requestId, &message);
+                if(deleteMessage)
+                    UA_ByteString_deleteMembers(&message);
+            }
+            break; }
+        case UA_CHUNKTYPE_ABORT:
+            UA_SecureChannel_removeChunk(channel, sequenceHeader.requestId);
+            break;
+        default:
+            return UA_STATUSCODE_BADDECODINGERROR;
+        }
+
+        /* Jump to the end of the chunk */
+        offset += (header.messageHeader.messageSize - processed_header);
+    } while(chunks->length > offset);
+
+    return UA_STATUSCODE_GOOD;
+}

+ 7 - 12
src/ua_securechannel.h

@@ -64,20 +64,15 @@ void UA_SecureChannel_revolveTokens(UA_SecureChannel *channel);
 /**
  * Chunking
  * -------- */
-/* Offset is initially set to the beginning of the chunk content. chunklength is
-   the length of the decoded chunk content (minus header, padding, etc.) */
-void UA_SecureChannel_appendChunk(UA_SecureChannel *channel, UA_UInt32 requestId,
-                                  const UA_ByteString *msg, size_t offset, size_t chunklength);
+typedef void
+(UA_ProcessMessageCallback)(void *application, UA_SecureChannel *channel,
+                             UA_MessageType messageType, UA_UInt32 requestId,
+                             const UA_ByteString *message);
 
-/* deleteChunk indicates if the returned bytestring was copied off the network
-   buffer (and needs to be freed) or points into the msg */
-UA_ByteString UA_SecureChannel_finalizeChunk(UA_SecureChannel *channel, UA_UInt32 requestId,
-                                             const UA_ByteString *msg, size_t offset, size_t chunklength,
-                                             UA_Boolean *deleteChunk);
+UA_StatusCode
+UA_SecureChannel_processChunks(UA_SecureChannel *channel, const UA_ByteString *chunks,
+                               UA_ProcessMessageCallback callback, void *application);
 
-void UA_SecureChannel_removeChunk(UA_SecureChannel *channel, UA_UInt32 requestId);
-
-UA_StatusCode UA_SecureChannel_processSequenceNumber (UA_UInt32 SequenceNumber, UA_SecureChannel *channel);
 /**
  * Log Helper
  * ---------- */