123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "ua_util.h"
- #include "ua_connection_internal.h"
- #include "ua_types_encoding_binary.h"
- #include "ua_types_generated_encoding_binary.h"
- #include "ua_types_generated_handling.h"
- #include "ua_transport_generated_encoding_binary.h"
- #include "ua_securechannel.h"
- void UA_Connection_deleteMembers(UA_Connection *connection) {
- UA_ByteString_deleteMembers(&connection->incompleteMessage);
- }
- /* Hides somme errors before sending them to a client according to the
- * standard. */
- static void
- hideErrors(UA_TcpErrorMessage *const error) {
- switch(error->error) {
- case UA_STATUSCODE_BADCERTIFICATEUNTRUSTED:
- error->error = UA_STATUSCODE_BADSECURITYCHECKSFAILED;
- error->reason = UA_STRING_NULL;
- break;
- case UA_STATUSCODE_BADCERTIFICATEREVOKED:
- error->error = UA_STATUSCODE_BADSECURITYCHECKSFAILED;
- error->reason = UA_STRING_NULL;
- break;
- // TODO: Check if these are all cases that need to be covered.
- default:
- break;
- }
- }
- void
- UA_Connection_sendError(UA_Connection *connection, UA_TcpErrorMessage *error) {
- hideErrors(error);
- UA_TcpMessageHeader header;
- header.messageTypeAndChunkType = UA_MESSAGETYPE_ERR + UA_CHUNKTYPE_FINAL;
- // Header + ErrorMessage (error + reasonLength_field + length)
- header.messageSize = 8 + (4 + 4 + (UA_UInt32)error->reason.length);
- /* Get the send buffer from the network layer */
- UA_ByteString msg = UA_BYTESTRING_NULL;
- UA_StatusCode retval = connection->getSendBuffer(connection, header.messageSize, &msg);
- if(retval != UA_STATUSCODE_GOOD)
- return;
- /* Encode and send the response */
- UA_Byte *bufPos = msg.data;
- const UA_Byte *bufEnd = &msg.data[msg.length];
- UA_TcpMessageHeader_encodeBinary(&header, &bufPos, &bufEnd);
- UA_TcpErrorMessage_encodeBinary(error, &bufPos, &bufEnd);
- msg.length = header.messageSize;
- connection->send(connection, &msg);
- }
- static UA_StatusCode
- prependIncompleteChunk(UA_Connection *connection, UA_ByteString *message) {
- /* Allocate the new message buffer */
- size_t length = connection->incompleteMessage.length + message->length;
- UA_Byte *data = (UA_Byte*)UA_realloc(connection->incompleteMessage.data, length);
- if(!data) {
- UA_ByteString_deleteMembers(&connection->incompleteMessage);
- return UA_STATUSCODE_BADOUTOFMEMORY;
- }
- /* Copy / release the current message buffer */
- memcpy(&data[connection->incompleteMessage.length], message->data, message->length);
- message->length = length;
- message->data = data;
- connection->incompleteMessage = UA_BYTESTRING_NULL;
- return UA_STATUSCODE_GOOD;
- }
- static UA_StatusCode
- bufferIncompleteChunk(UA_Connection *connection, const UA_Byte *pos, const UA_Byte *end) {
- size_t length = (uintptr_t)end - (uintptr_t)pos;
- UA_StatusCode retval = UA_ByteString_allocBuffer(&connection->incompleteMessage, length);
- if(retval != UA_STATUSCODE_GOOD)
- return retval;
- memcpy(connection->incompleteMessage.data, pos, length);
- return UA_STATUSCODE_GOOD;
- }
- static UA_StatusCode
- processChunk(UA_Connection *connection, void *application,
- UA_Connection_processChunk processCallback,
- const UA_Byte **posp, const UA_Byte *end, UA_Boolean *done) {
- const UA_Byte *pos = *posp;
- size_t length = (uintptr_t)end - (uintptr_t)pos;
- /* At least 8 byte needed for the header. Wait for the next chunk. */
- if(length < 8) {
- bufferIncompleteChunk(connection, pos, end);
- *done = true;
- return UA_STATUSCODE_GOOD;
- }
- /* Check the message type */
- UA_MessageType msgtype = (UA_MessageType)((UA_UInt32)pos[0] + ((UA_UInt32)pos[1] << 8) +
- ((UA_UInt32)pos[2] << 16));
- if(msgtype != UA_MESSAGETYPE_MSG && msgtype != UA_MESSAGETYPE_ERR &&
- msgtype != UA_MESSAGETYPE_OPN && msgtype != UA_MESSAGETYPE_HEL &&
- msgtype != UA_MESSAGETYPE_ACK && msgtype != UA_MESSAGETYPE_CLO) {
- /* The message type is not recognized */
- return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID;
- }
- UA_Byte isFinal = pos[3];
- if(isFinal != 'C' && isFinal != 'F' && isFinal != 'A') {
- /* The message type is not recognized */
- return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID;
- }
- UA_UInt32 chunk_length = 0;
- UA_ByteString temp = { 8, (UA_Byte*)(uintptr_t)pos }; /* At least 8 byte left */
- size_t temp_offset = 4;
- /* Decoding the UInt32 cannot fail */
- UA_UInt32_decodeBinary(&temp, &temp_offset, &chunk_length);
- /* The message size is not allowed */
- if(chunk_length < 16 || chunk_length > connection->localConf.recvBufferSize)
- return UA_STATUSCODE_BADTCPMESSAGETOOLARGE;
- /* Wait for the next packet to process the complete chunk */
- if(chunk_length > length) {
- bufferIncompleteChunk(connection, pos, end);
- *done = true;
- return UA_STATUSCODE_GOOD;
- }
- /* Process the chunk; forward the position pointer */
- temp.length = chunk_length;
- *posp += chunk_length;
- *done = false;
- return processCallback(application, connection, &temp);
- }
- UA_StatusCode
- UA_Connection_processChunks(UA_Connection *connection, void *application,
- UA_Connection_processChunk processCallback,
- const UA_ByteString *packet) {
- /* If we have stored an incomplete chunk, prefix to the received message.
- * After this block, connection->incompleteMessage is always empty. The
- * message and the buffer is released if allocating the memory fails. */
- UA_Boolean realloced = false;
- UA_ByteString message = *packet;
- UA_StatusCode retval;
- if(connection->incompleteMessage.length > 0) {
- retval = prependIncompleteChunk(connection, &message);
- if(retval != UA_STATUSCODE_GOOD)
- return retval;
- realloced = true;
- }
- /* Loop over the received chunks. pos is increased with each chunk. */
- const UA_Byte *pos = message.data;
- const UA_Byte *end = &message.data[message.length];
- UA_Boolean done = true;
- do {
- retval = processChunk(connection, application, processCallback,
- &pos, end, &done);
- } while(!done && retval == UA_STATUSCODE_GOOD);
- if(realloced)
- UA_ByteString_deleteMembers(&message);
- return retval;
- }
- /* In order to know whether a chunk was processed, we insert an indirection into
- * the callback. */
- struct completeChunkTrampolineData {
- UA_Boolean called;
- void *application;
- UA_Connection_processChunk processCallback;
- };
- static UA_StatusCode
- completeChunkTrampoline(void *application, UA_Connection *connection,
- UA_ByteString *chunk) {
- struct completeChunkTrampolineData *data =
- (struct completeChunkTrampolineData*)application;
- data->called = true;
- return data->processCallback(data->application, connection, chunk);
- }
- UA_StatusCode
- UA_Connection_receiveChunksBlocking(UA_Connection *connection, void *application,
- UA_Connection_processChunk processCallback,
- UA_UInt32 timeout) {
- UA_DateTime now = UA_DateTime_nowMonotonic();
- UA_DateTime maxDate = now + (timeout * UA_MSEC_TO_DATETIME);
- struct completeChunkTrampolineData data;
- data.called = false;
- data.application = application;
- data.processCallback = processCallback;
- UA_StatusCode retval = UA_STATUSCODE_GOOD;
- while(true) {
- /* Listen for messages to arrive */
- UA_ByteString packet = UA_BYTESTRING_NULL;
- retval = connection->recv(connection, &packet, timeout);
- if(retval != UA_STATUSCODE_GOOD)
- break;
- /* Try to process one complete chunk */
- retval = UA_Connection_processChunks(connection, &data,
- completeChunkTrampoline, &packet);
- connection->releaseRecvBuffer(connection, &packet);
- if(data.called)
- break;
- /* We received a message. But the chunk is incomplete. Compute the
- * remaining timeout. */
- now = UA_DateTime_nowMonotonic();
- if(now > maxDate)
- return UA_STATUSCODE_GOODNONCRITICALTIMEOUT;
- timeout = (UA_UInt32)((maxDate - now) / UA_MSEC_TO_DATETIME);
- }
- return retval;
- }
- void UA_Connection_detachSecureChannel(UA_Connection *connection) {
- UA_SecureChannel *channel = connection->channel;
- if(channel)
- /* only replace when the channel points to this connection */
- UA_atomic_cmpxchg((void**)&channel->connection, connection, NULL);
- UA_atomic_xchg((void**)&connection->channel, NULL);
- }
- // TODO: Return an error code
- void
- UA_Connection_attachSecureChannel(UA_Connection *connection, UA_SecureChannel *channel) {
- if(UA_atomic_cmpxchg((void**)&channel->connection, NULL, connection) == NULL)
- UA_atomic_xchg((void**)&connection->channel, (void*)channel);
- }
|