Browse Source

Stack: Re-check for buffered incomplete chunks in UA_Connection_processChunks

Julius Pfrommer 5 years ago
parent
commit
7cae67d83a
2 changed files with 34 additions and 14 deletions
  1. 25 4
      src/ua_connection.c
  2. 9 10
      src/ua_connection_internal.h

+ 25 - 4
src/ua_connection.c

@@ -95,6 +95,7 @@ UA_Connection_sendError(UA_Connection *connection, UA_TcpErrorMessage *error) {
 static UA_StatusCode
 bufferIncompleteChunk(UA_Connection *connection, const UA_Byte *pos,
                       const UA_Byte *end) {
+    UA_assert(connection->incompleteChunk.length == 0);
     UA_assert(pos < end);
     size_t length = (uintptr_t)end - (uintptr_t)pos;
     UA_StatusCode retval = UA_ByteString_allocBuffer(&connection->incompleteChunk, length);
@@ -160,24 +161,44 @@ UA_StatusCode
 UA_Connection_processChunks(UA_Connection *connection, void *application,
                             UA_Connection_processChunk processCallback,
                             const UA_ByteString *packet) {
-    /* The connection has already prepended any incomplete chunk during recv */
+    const UA_Byte *pos = packet->data;
+    const UA_Byte *end = &packet->data[packet->length];
+    UA_ByteString appended = connection->incompleteChunk;
+
+    /* Prepend the incomplete last chunk. This is usually done in the
+     * networklayer. But we test for a buffered incomplete chunk here again to
+     * work around "lazy" network layers. */
+    if(appended.length > 0) {
+        connection->incompleteChunk = UA_BYTESTRING_NULL;
+        UA_Byte *t = (UA_Byte*)UA_realloc(appended.data, appended.length + packet->length);
+        if(!t) {
+            UA_ByteString_deleteMembers(&appended);
+            return UA_STATUSCODE_BADOUTOFMEMORY;
+        }
+        memcpy(&t[appended.length], pos, packet->length);
+        appended.data = t;
+        appended.length += packet->length;
+        pos = t;
+        end = &t[appended.length];
+    }
+
     UA_assert(connection->incompleteChunk.length == 0);
 
     /* Loop over the received chunks. pos is increased with each chunk. */
-    const UA_Byte *pos = packet->data;
-    const UA_Byte *end = &packet->data[packet->length];
     UA_Boolean done = false;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     while(!done) {
         retval = processChunk(connection, application, processCallback, &pos, end, &done);
         /* If an irrecoverable error happens: do not buffer incomplete chunk */
         if(retval != UA_STATUSCODE_GOOD)
-            return retval;
+            goto cleanup;
     }
 
     if(end > pos)
         retval = bufferIncompleteChunk(connection, pos, end);
 
+ cleanup:
+    UA_ByteString_deleteMembers(&appended);
     return retval;
 }
 

+ 9 - 10
src/ua_connection_internal.h

@@ -28,22 +28,21 @@ typedef UA_StatusCode (*UA_Connection_processChunk)(void *application,
                                                     UA_Connection *connection,
                                                     UA_ByteString *chunk);
 
-/* The network layer may receive chopped up messages since TCP is a streaming
- * protocol. This method calls the processChunk callback on all full chunks that
- * were received. Dangling half-complete chunks are buffered in the connection
- * and considered for the next received packet.
+/* The network layer may receive several chunks in one packet since TCP is a
+ * streaming protocol. The last chunk in the packet may be only partial. This
+ * method calls the processChunk callback on all full chunks that were received.
+ * The last incomplete chunk is buffered in the connection for the next
+ * iteration.
  *
- * If an entire chunk is received, it is forwarded directly. But the memory
- * needs to be freed with the networklayer-specific mechanism. If a half message
- * is received, we copy it into a local buffer. Then, the stack-specific free
- * needs to be used.
+ * The packet itself is not edited in this method. But possibly in the callback
+ * that is executed on complete chunks.
  *
  * @param connection The connection
  * @param application The client or server application
  * @param processCallback The function pointer for processing each chunk
  * @param packet The received packet.
  * @return Returns UA_STATUSCODE_GOOD or an error code. When an error occurs,
- *         the ingoing message and the current buffer in the connection are
+ *         the current buffer in the connection are
  *         freed. */
 UA_StatusCode
 UA_Connection_processChunks(UA_Connection *connection, void *application,
@@ -66,7 +65,7 @@ UA_Connection_receiveChunksBlocking(UA_Connection *connection, void *application
 
 UA_StatusCode
 UA_Connection_receiveChunksNonBlocking(UA_Connection *connection, void *application,
-                                    UA_Connection_processChunk processCallback);
+                                       UA_Connection_processChunk processCallback);
 
 /* When a fatal error occurs the Server shall send an Error Message to the
  * Client and close the socket. When a Client encounters one of these errors, it