ua_connection.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. #include "ua_util.h"
  5. #include "ua_connection_internal.h"
  6. #include "ua_types_encoding_binary.h"
  7. #include "ua_types_generated_encoding_binary.h"
  8. #include "ua_types_generated_handling.h"
  9. #include "ua_transport_generated_encoding_binary.h"
  10. #include "ua_securechannel.h"
  11. void UA_Connection_deleteMembers(UA_Connection *connection) {
  12. UA_ByteString_deleteMembers(&connection->incompleteMessage);
  13. }
  14. /* Hides somme errors before sending them to a client according to the
  15. * standard. */
  16. static void
  17. hideErrors(UA_TcpErrorMessage *const error) {
  18. switch(error->error) {
  19. case UA_STATUSCODE_BADCERTIFICATEUNTRUSTED:
  20. error->error = UA_STATUSCODE_BADSECURITYCHECKSFAILED;
  21. error->reason = UA_STRING_NULL;
  22. break;
  23. case UA_STATUSCODE_BADCERTIFICATEREVOKED:
  24. error->error = UA_STATUSCODE_BADSECURITYCHECKSFAILED;
  25. error->reason = UA_STRING_NULL;
  26. break;
  27. // TODO: Check if these are all cases that need to be covered.
  28. default:
  29. break;
  30. }
  31. }
  32. void
  33. UA_Connection_sendError(UA_Connection *connection, UA_TcpErrorMessage *error) {
  34. hideErrors(error);
  35. UA_TcpMessageHeader header;
  36. header.messageTypeAndChunkType = UA_MESSAGETYPE_ERR + UA_CHUNKTYPE_FINAL;
  37. // Header + ErrorMessage (error + reasonLength_field + length)
  38. header.messageSize = 8 + (4 + 4 + (UA_UInt32)error->reason.length);
  39. /* Get the send buffer from the network layer */
  40. UA_ByteString msg = UA_BYTESTRING_NULL;
  41. UA_StatusCode retval = connection->getSendBuffer(connection, header.messageSize, &msg);
  42. if(retval != UA_STATUSCODE_GOOD)
  43. return;
  44. /* Encode and send the response */
  45. UA_Byte *bufPos = msg.data;
  46. const UA_Byte *bufEnd = &msg.data[msg.length];
  47. UA_TcpMessageHeader_encodeBinary(&header, &bufPos, &bufEnd);
  48. UA_TcpErrorMessage_encodeBinary(error, &bufPos, &bufEnd);
  49. msg.length = header.messageSize;
  50. connection->send(connection, &msg);
  51. }
  52. static UA_StatusCode
  53. prependIncompleteChunk(UA_Connection *connection, UA_ByteString *message) {
  54. /* Allocate the new message buffer */
  55. size_t length = connection->incompleteMessage.length + message->length;
  56. UA_Byte *data = (UA_Byte*)UA_realloc(connection->incompleteMessage.data, length);
  57. if(!data) {
  58. UA_ByteString_deleteMembers(&connection->incompleteMessage);
  59. return UA_STATUSCODE_BADOUTOFMEMORY;
  60. }
  61. /* Copy / release the current message buffer */
  62. memcpy(&data[connection->incompleteMessage.length], message->data, message->length);
  63. message->length = length;
  64. message->data = data;
  65. connection->incompleteMessage = UA_BYTESTRING_NULL;
  66. return UA_STATUSCODE_GOOD;
  67. }
  68. static UA_StatusCode
  69. bufferIncompleteChunk(UA_Connection *connection, const UA_Byte *pos, const UA_Byte *end) {
  70. size_t length = (uintptr_t)end - (uintptr_t)pos;
  71. UA_StatusCode retval = UA_ByteString_allocBuffer(&connection->incompleteMessage, length);
  72. if(retval != UA_STATUSCODE_GOOD)
  73. return retval;
  74. memcpy(connection->incompleteMessage.data, pos, length);
  75. return UA_STATUSCODE_GOOD;
  76. }
  77. static UA_StatusCode
  78. processChunk(UA_Connection *connection, void *application,
  79. UA_Connection_processChunk processCallback,
  80. const UA_Byte **posp, const UA_Byte *end, UA_Boolean *done) {
  81. const UA_Byte *pos = *posp;
  82. size_t length = (uintptr_t)end - (uintptr_t)pos;
  83. /* At least 8 byte needed for the header. Wait for the next chunk. */
  84. if(length < 8) {
  85. bufferIncompleteChunk(connection, pos, end);
  86. *done = true;
  87. return UA_STATUSCODE_GOOD;
  88. }
  89. /* Check the message type */
  90. UA_MessageType msgtype = (UA_MessageType)((UA_UInt32)pos[0] + ((UA_UInt32)pos[1] << 8) +
  91. ((UA_UInt32)pos[2] << 16));
  92. if(msgtype != UA_MESSAGETYPE_MSG && msgtype != UA_MESSAGETYPE_ERR &&
  93. msgtype != UA_MESSAGETYPE_OPN && msgtype != UA_MESSAGETYPE_HEL &&
  94. msgtype != UA_MESSAGETYPE_ACK && msgtype != UA_MESSAGETYPE_CLO) {
  95. /* The message type is not recognized */
  96. return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID;
  97. }
  98. UA_Byte isFinal = pos[3];
  99. if(isFinal != 'C' && isFinal != 'F' && isFinal != 'A') {
  100. /* The message type is not recognized */
  101. return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID;
  102. }
  103. UA_UInt32 chunk_length = 0;
  104. UA_ByteString temp = { 8, (UA_Byte*)(uintptr_t)pos }; /* At least 8 byte left */
  105. size_t temp_offset = 4;
  106. /* Decoding the UInt32 cannot fail */
  107. UA_UInt32_decodeBinary(&temp, &temp_offset, &chunk_length);
  108. /* The message size is not allowed */
  109. if(chunk_length < 16 || chunk_length > connection->localConf.recvBufferSize)
  110. return UA_STATUSCODE_BADTCPMESSAGETOOLARGE;
  111. /* Wait for the next packet to process the complete chunk */
  112. if(chunk_length > length) {
  113. bufferIncompleteChunk(connection, pos, end);
  114. *done = true;
  115. return UA_STATUSCODE_GOOD;
  116. }
  117. /* Process the chunk; forward the position pointer */
  118. temp.length = chunk_length;
  119. *posp += chunk_length;
  120. *done = false;
  121. return processCallback(application, connection, &temp);
  122. }
  123. UA_StatusCode
  124. UA_Connection_processChunks(UA_Connection *connection, void *application,
  125. UA_Connection_processChunk processCallback,
  126. const UA_ByteString *packet) {
  127. /* If we have stored an incomplete chunk, prefix to the received message.
  128. * After this block, connection->incompleteMessage is always empty. The
  129. * message and the buffer is released if allocating the memory fails. */
  130. UA_Boolean realloced = false;
  131. UA_ByteString message = *packet;
  132. UA_StatusCode retval;
  133. if(connection->incompleteMessage.length > 0) {
  134. retval = prependIncompleteChunk(connection, &message);
  135. if(retval != UA_STATUSCODE_GOOD)
  136. return retval;
  137. realloced = true;
  138. }
  139. /* Loop over the received chunks. pos is increased with each chunk. */
  140. const UA_Byte *pos = message.data;
  141. const UA_Byte *end = &message.data[message.length];
  142. UA_Boolean done = true;
  143. do {
  144. retval = processChunk(connection, application, processCallback,
  145. &pos, end, &done);
  146. } while(!done && retval == UA_STATUSCODE_GOOD);
  147. if(realloced)
  148. UA_ByteString_deleteMembers(&message);
  149. return retval;
  150. }
  151. /* In order to know whether a chunk was processed, we insert an indirection into
  152. * the callback. */
  153. struct completeChunkTrampolineData {
  154. UA_Boolean called;
  155. void *application;
  156. UA_Connection_processChunk processCallback;
  157. };
  158. static UA_StatusCode
  159. completeChunkTrampoline(void *application, UA_Connection *connection,
  160. UA_ByteString *chunk) {
  161. struct completeChunkTrampolineData *data =
  162. (struct completeChunkTrampolineData*)application;
  163. data->called = true;
  164. return data->processCallback(data->application, connection, chunk);
  165. }
  166. UA_StatusCode
  167. UA_Connection_receiveChunksBlocking(UA_Connection *connection, void *application,
  168. UA_Connection_processChunk processCallback,
  169. UA_UInt32 timeout) {
  170. UA_DateTime now = UA_DateTime_nowMonotonic();
  171. UA_DateTime maxDate = now + (timeout * UA_MSEC_TO_DATETIME);
  172. struct completeChunkTrampolineData data;
  173. data.called = false;
  174. data.application = application;
  175. data.processCallback = processCallback;
  176. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  177. while(true) {
  178. /* Listen for messages to arrive */
  179. UA_ByteString packet = UA_BYTESTRING_NULL;
  180. retval = connection->recv(connection, &packet, timeout);
  181. if(retval != UA_STATUSCODE_GOOD)
  182. break;
  183. /* Try to process one complete chunk */
  184. retval = UA_Connection_processChunks(connection, &data,
  185. completeChunkTrampoline, &packet);
  186. connection->releaseRecvBuffer(connection, &packet);
  187. if(data.called)
  188. break;
  189. /* We received a message. But the chunk is incomplete. Compute the
  190. * remaining timeout. */
  191. now = UA_DateTime_nowMonotonic();
  192. if(now > maxDate)
  193. return UA_STATUSCODE_GOODNONCRITICALTIMEOUT;
  194. timeout = (UA_UInt32)((maxDate - now) / UA_MSEC_TO_DATETIME);
  195. }
  196. return retval;
  197. }
  198. void UA_Connection_detachSecureChannel(UA_Connection *connection) {
  199. UA_SecureChannel *channel = connection->channel;
  200. if(channel)
  201. /* only replace when the channel points to this connection */
  202. UA_atomic_cmpxchg((void**)&channel->connection, connection, NULL);
  203. UA_atomic_xchg((void**)&connection->channel, NULL);
  204. }
  205. // TODO: Return an error code
  206. void
  207. UA_Connection_attachSecureChannel(UA_Connection *connection, UA_SecureChannel *channel) {
  208. if(UA_atomic_cmpxchg((void**)&channel->connection, NULL, connection) == NULL)
  209. UA_atomic_xchg((void**)&connection->channel, (void*)channel);
  210. }