ua_connection.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  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. *
  5. * Copyright 2014-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
  6. * Copyright 2014, 2016-2017 (c) Florian Palm
  7. * Copyright 2015-2016 (c) Sten Grüner
  8. * Copyright 2015 (c) Oleksiy Vasylyev
  9. * Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH
  10. * Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB
  11. */
  12. #include "ua_util_internal.h"
  13. #include "ua_connection_internal.h"
  14. #include "ua_types_encoding_binary.h"
  15. #include "ua_types_generated_encoding_binary.h"
  16. #include "ua_types_generated_handling.h"
  17. #include "ua_transport_generated_encoding_binary.h"
  18. #include "ua_securechannel.h"
  19. void UA_Connection_deleteMembers(UA_Connection *connection) {
  20. UA_ByteString_deleteMembers(&connection->incompleteChunk);
  21. }
  22. UA_StatusCode
  23. UA_Connection_processHELACK(UA_Connection *connection,
  24. const UA_ConnectionConfig *localConfig,
  25. const UA_ConnectionConfig *remoteConfig) {
  26. connection->config = *remoteConfig;
  27. /* The lowest common version is used by both sides */
  28. if(connection->config.protocolVersion > localConfig->protocolVersion)
  29. connection->config.protocolVersion = localConfig->protocolVersion;
  30. /* Can we receive the max send size? */
  31. if(connection->config.sendBufferSize > localConfig->recvBufferSize)
  32. connection->config.sendBufferSize = localConfig->recvBufferSize;
  33. /* Can we send the max receive size? */
  34. if(connection->config.recvBufferSize > localConfig->sendBufferSize)
  35. connection->config.recvBufferSize = localConfig->sendBufferSize;
  36. /* Chunks of at least 8192 bytes must be permissible.
  37. * See Part 6, Clause 6.7.1 */
  38. if(connection->config.recvBufferSize < 8192 ||
  39. connection->config.sendBufferSize < 8192 ||
  40. (connection->config.maxMessageSize != 0 &&
  41. connection->config.maxMessageSize < 8192))
  42. return UA_STATUSCODE_BADINTERNALERROR;
  43. connection->state = UA_CONNECTION_ESTABLISHED;
  44. return UA_STATUSCODE_GOOD;
  45. }
  46. /* Hides somme errors before sending them to a client according to the
  47. * standard. */
  48. static void
  49. hideErrors(UA_TcpErrorMessage *const error) {
  50. switch(error->error) {
  51. case UA_STATUSCODE_BADCERTIFICATEUNTRUSTED:
  52. error->error = UA_STATUSCODE_BADSECURITYCHECKSFAILED;
  53. error->reason = UA_STRING_NULL;
  54. break;
  55. // TODO: Check if these are all cases that need to be covered.
  56. default:
  57. break;
  58. }
  59. }
  60. void
  61. UA_Connection_sendError(UA_Connection *connection, UA_TcpErrorMessage *error) {
  62. hideErrors(error);
  63. UA_TcpMessageHeader header;
  64. header.messageTypeAndChunkType = UA_MESSAGETYPE_ERR + UA_CHUNKTYPE_FINAL;
  65. // Header + ErrorMessage (error + reasonLength_field + length)
  66. header.messageSize = 8 + (4 + 4 + (UA_UInt32)error->reason.length);
  67. /* Get the send buffer from the network layer */
  68. UA_ByteString msg = UA_BYTESTRING_NULL;
  69. UA_StatusCode retval = connection->getSendBuffer(connection, header.messageSize, &msg);
  70. if(retval != UA_STATUSCODE_GOOD)
  71. return;
  72. /* Encode and send the response */
  73. UA_Byte *bufPos = msg.data;
  74. const UA_Byte *bufEnd = &msg.data[msg.length];
  75. UA_TcpMessageHeader_encodeBinary(&header, &bufPos, bufEnd);
  76. UA_TcpErrorMessage_encodeBinary(error, &bufPos, bufEnd);
  77. msg.length = header.messageSize;
  78. connection->send(connection, &msg);
  79. }
  80. static UA_StatusCode
  81. bufferIncompleteChunk(UA_Connection *connection, const UA_Byte *pos,
  82. const UA_Byte *end) {
  83. UA_assert(connection->incompleteChunk.length == 0);
  84. UA_assert(pos < end);
  85. size_t length = (uintptr_t)end - (uintptr_t)pos;
  86. UA_StatusCode retval = UA_ByteString_allocBuffer(&connection->incompleteChunk, length);
  87. if(retval != UA_STATUSCODE_GOOD)
  88. return retval;
  89. memcpy(connection->incompleteChunk.data, pos, length);
  90. return UA_STATUSCODE_GOOD;
  91. }
  92. static UA_StatusCode
  93. processChunk(UA_Connection *connection, void *application,
  94. UA_Connection_processChunk processCallback,
  95. const UA_Byte **posp, const UA_Byte *end, UA_Boolean *done) {
  96. const UA_Byte *pos = *posp;
  97. const size_t remaining = (uintptr_t)end - (uintptr_t)pos;
  98. /* At least 8 byte needed for the header. Wait for the next chunk. */
  99. if(remaining < 8) {
  100. *done = true;
  101. return UA_STATUSCODE_GOOD;
  102. }
  103. /* Check the message type */
  104. UA_MessageType msgtype = (UA_MessageType)
  105. ((UA_UInt32)pos[0] + ((UA_UInt32)pos[1] << 8) + ((UA_UInt32)pos[2] << 16));
  106. if(msgtype != UA_MESSAGETYPE_MSG && msgtype != UA_MESSAGETYPE_ERR &&
  107. msgtype != UA_MESSAGETYPE_OPN && msgtype != UA_MESSAGETYPE_HEL &&
  108. msgtype != UA_MESSAGETYPE_ACK && msgtype != UA_MESSAGETYPE_CLO) {
  109. /* The message type is not recognized */
  110. return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID;
  111. }
  112. UA_Byte isFinal = pos[3];
  113. if(isFinal != 'C' && isFinal != 'F' && isFinal != 'A') {
  114. /* The message type is not recognized */
  115. return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID;
  116. }
  117. UA_UInt32 chunk_length = 0;
  118. UA_ByteString temp = { 8, (UA_Byte*)(uintptr_t)pos }; /* At least 8 byte left */
  119. size_t temp_offset = 4;
  120. /* Decoding the UInt32 cannot fail */
  121. UA_UInt32_decodeBinary(&temp, &temp_offset, &chunk_length);
  122. /* The message size is not allowed */
  123. if(chunk_length < 16 || chunk_length > connection->config.recvBufferSize)
  124. return UA_STATUSCODE_BADTCPMESSAGETOOLARGE;
  125. /* Have an the complete chunk */
  126. if(chunk_length > remaining) {
  127. *done = true;
  128. return UA_STATUSCODE_GOOD;
  129. }
  130. /* Process the chunk; forward the position pointer */
  131. temp.length = chunk_length;
  132. *posp += chunk_length;
  133. *done = false;
  134. return processCallback(application, connection, &temp);
  135. }
  136. UA_StatusCode
  137. UA_Connection_processChunks(UA_Connection *connection, void *application,
  138. UA_Connection_processChunk processCallback,
  139. const UA_ByteString *packet) {
  140. const UA_Byte *pos = packet->data;
  141. const UA_Byte *end = &packet->data[packet->length];
  142. UA_ByteString appended = connection->incompleteChunk;
  143. /* Prepend the incomplete last chunk. This is usually done in the
  144. * networklayer. But we test for a buffered incomplete chunk here again to
  145. * work around "lazy" network layers. */
  146. if(appended.length > 0) {
  147. connection->incompleteChunk = UA_BYTESTRING_NULL;
  148. UA_Byte *t = (UA_Byte*)UA_realloc(appended.data, appended.length + packet->length);
  149. if(!t) {
  150. UA_ByteString_deleteMembers(&appended);
  151. return UA_STATUSCODE_BADOUTOFMEMORY;
  152. }
  153. memcpy(&t[appended.length], pos, packet->length);
  154. appended.data = t;
  155. appended.length += packet->length;
  156. pos = t;
  157. end = &t[appended.length];
  158. }
  159. UA_assert(connection->incompleteChunk.length == 0);
  160. /* Loop over the received chunks. pos is increased with each chunk. */
  161. UA_Boolean done = false;
  162. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  163. while(!done) {
  164. retval = processChunk(connection, application, processCallback, &pos, end, &done);
  165. /* If an irrecoverable error happens: do not buffer incomplete chunk */
  166. if(retval != UA_STATUSCODE_GOOD)
  167. goto cleanup;
  168. }
  169. if(end > pos)
  170. retval = bufferIncompleteChunk(connection, pos, end);
  171. cleanup:
  172. UA_ByteString_deleteMembers(&appended);
  173. return retval;
  174. }
  175. /* In order to know whether a chunk was processed, we insert an redirection into
  176. * the callback. */
  177. struct completeChunkTrampolineData {
  178. UA_Boolean called;
  179. void *application;
  180. UA_Connection_processChunk processCallback;
  181. };
  182. static UA_StatusCode
  183. completeChunkTrampoline(void *application, UA_Connection *connection,
  184. UA_ByteString *chunk) {
  185. struct completeChunkTrampolineData *data =
  186. (struct completeChunkTrampolineData*)application;
  187. data->called = true;
  188. return data->processCallback(data->application, connection, chunk);
  189. }
  190. UA_StatusCode
  191. UA_Connection_receiveChunksBlocking(UA_Connection *connection, void *application,
  192. UA_Connection_processChunk processCallback,
  193. UA_UInt32 timeout) {
  194. UA_DateTime now = UA_DateTime_nowMonotonic();
  195. UA_DateTime maxDate = now + (timeout * UA_DATETIME_MSEC);
  196. struct completeChunkTrampolineData data;
  197. data.called = false;
  198. data.application = application;
  199. data.processCallback = processCallback;
  200. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  201. while(true) {
  202. /* Listen for messages to arrive */
  203. UA_ByteString packet = UA_BYTESTRING_NULL;
  204. retval = connection->recv(connection, &packet, timeout);
  205. if(retval != UA_STATUSCODE_GOOD)
  206. break;
  207. /* Try to process one complete chunk */
  208. retval = UA_Connection_processChunks(connection, &data,
  209. completeChunkTrampoline, &packet);
  210. connection->releaseRecvBuffer(connection, &packet);
  211. if(data.called)
  212. break;
  213. /* We received a message. But the chunk is incomplete. Compute the
  214. * remaining timeout. */
  215. now = UA_DateTime_nowMonotonic();
  216. /* >= avoid timeout to be set to 0 */
  217. if(now >= maxDate)
  218. return UA_STATUSCODE_GOODNONCRITICALTIMEOUT;
  219. /* round always to upper value to avoid timeout to be set to 0
  220. * if(maxDate - now) < (UA_DATETIME_MSEC/2) */
  221. timeout = (UA_UInt32)(((maxDate - now) + (UA_DATETIME_MSEC - 1)) / UA_DATETIME_MSEC);
  222. }
  223. return retval;
  224. }
  225. UA_StatusCode
  226. UA_Connection_receiveChunksNonBlocking(UA_Connection *connection, void *application,
  227. UA_Connection_processChunk processCallback) {
  228. struct completeChunkTrampolineData data;
  229. data.called = false;
  230. data.application = application;
  231. data.processCallback = processCallback;
  232. /* Listen for messages to arrive */
  233. UA_ByteString packet = UA_BYTESTRING_NULL;
  234. UA_StatusCode retval = connection->recv(connection, &packet, 1);
  235. if((retval != UA_STATUSCODE_GOOD) && (retval != UA_STATUSCODE_GOODNONCRITICALTIMEOUT))
  236. return retval;
  237. /* Try to process one complete chunk */
  238. retval = UA_Connection_processChunks(connection, &data, completeChunkTrampoline, &packet);
  239. connection->releaseRecvBuffer(connection, &packet);
  240. return retval;
  241. }
  242. void UA_Connection_detachSecureChannel(UA_Connection *connection) {
  243. UA_SecureChannel *channel = connection->channel;
  244. if(channel)
  245. /* only replace when the channel points to this connection */
  246. UA_atomic_cmpxchg((void**)&channel->connection, connection, NULL);
  247. UA_atomic_xchg((void**)&connection->channel, NULL);
  248. }
  249. // TODO: Return an error code
  250. void
  251. UA_Connection_attachSecureChannel(UA_Connection *connection, UA_SecureChannel *channel) {
  252. if(UA_atomic_cmpxchg((void**)&channel->connection, NULL, connection) == NULL)
  253. UA_atomic_xchg((void**)&connection->channel, (void*)channel);
  254. }