ua_client_connect.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  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_client.h"
  5. #include "ua_client_internal.h"
  6. #include "ua_transport_generated.h"
  7. #include "ua_transport_generated_handling.h"
  8. #include "ua_transport_generated_encoding_binary.h"
  9. #include "ua_types_encoding_binary.h"
  10. #include "ua_types_generated_encoding_binary.h"
  11. #define UA_MINMESSAGESIZE 8192
  12. /***********************/
  13. /* Open the Connection */
  14. /***********************/
  15. static UA_StatusCode
  16. processACKResponse(void *application, UA_Connection *connection, UA_ByteString *chunk) {
  17. UA_Client *client = (UA_Client*)application;
  18. /* Decode the message */
  19. size_t offset = 0;
  20. UA_StatusCode retval;
  21. UA_TcpMessageHeader messageHeader;
  22. UA_TcpAcknowledgeMessage ackMessage;
  23. retval = UA_TcpMessageHeader_decodeBinary(chunk, &offset, &messageHeader);
  24. retval |= UA_TcpAcknowledgeMessage_decodeBinary(chunk, &offset, &ackMessage);
  25. if(retval != UA_STATUSCODE_GOOD) {
  26. UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_NETWORK,
  27. "Decoding ACK message failed");
  28. return retval;
  29. }
  30. /* Store remote connection settings and adjust local configuration to not
  31. * exceed the limits */
  32. UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_NETWORK, "Received ACK message");
  33. connection->remoteConf.maxChunkCount = ackMessage.maxChunkCount; /* may be zero -> unlimited */
  34. connection->remoteConf.maxMessageSize = ackMessage.maxMessageSize; /* may be zero -> unlimited */
  35. connection->remoteConf.protocolVersion = ackMessage.protocolVersion;
  36. connection->remoteConf.sendBufferSize = ackMessage.sendBufferSize;
  37. connection->remoteConf.recvBufferSize = ackMessage.receiveBufferSize;
  38. if(connection->remoteConf.recvBufferSize < connection->localConf.sendBufferSize)
  39. connection->localConf.sendBufferSize = connection->remoteConf.recvBufferSize;
  40. if(connection->remoteConf.sendBufferSize < connection->localConf.recvBufferSize)
  41. connection->localConf.recvBufferSize = connection->remoteConf.sendBufferSize;
  42. connection->state = UA_CONNECTION_ESTABLISHED;
  43. return UA_STATUSCODE_GOOD;
  44. }
  45. static UA_StatusCode
  46. HelAckHandshake(UA_Client *client) {
  47. /* Get a buffer */
  48. UA_ByteString message;
  49. UA_Connection *conn = &client->connection;
  50. UA_StatusCode retval = conn->getSendBuffer(conn, UA_MINMESSAGESIZE, &message);
  51. if(retval != UA_STATUSCODE_GOOD)
  52. return retval;
  53. /* Prepare the HEL message and encode at offset 8 */
  54. UA_TcpHelloMessage hello;
  55. UA_String_copy(&client->endpointUrl, &hello.endpointUrl); /* must be less than 4096 bytes */
  56. hello.maxChunkCount = conn->localConf.maxChunkCount;
  57. hello.maxMessageSize = conn->localConf.maxMessageSize;
  58. hello.protocolVersion = conn->localConf.protocolVersion;
  59. hello.receiveBufferSize = conn->localConf.recvBufferSize;
  60. hello.sendBufferSize = conn->localConf.sendBufferSize;
  61. UA_Byte *bufPos = &message.data[8]; /* skip the header */
  62. const UA_Byte *bufEnd = &message.data[message.length];
  63. retval = UA_TcpHelloMessage_encodeBinary(&hello, &bufPos, &bufEnd);
  64. UA_TcpHelloMessage_deleteMembers(&hello);
  65. /* Encode the message header at offset 0 */
  66. UA_TcpMessageHeader messageHeader;
  67. messageHeader.messageTypeAndChunkType = UA_CHUNKTYPE_FINAL + UA_MESSAGETYPE_HEL;
  68. messageHeader.messageSize = (UA_UInt32)((uintptr_t)bufPos - (uintptr_t)message.data);
  69. bufPos = message.data;
  70. retval |= UA_TcpMessageHeader_encodeBinary(&messageHeader, &bufPos, &bufEnd);
  71. if(retval != UA_STATUSCODE_GOOD) {
  72. conn->releaseSendBuffer(conn, &message);
  73. return retval;
  74. }
  75. /* Send the HEL message */
  76. message.length = messageHeader.messageSize;
  77. retval = conn->send(conn, &message);
  78. if(retval != UA_STATUSCODE_GOOD) {
  79. UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_NETWORK,
  80. "Sending HEL failed");
  81. return retval;
  82. }
  83. UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_NETWORK,
  84. "Sent HEL message");
  85. /* Loop until we have a complete chunk */
  86. retval = UA_Connection_receiveChunksBlocking(conn, client, processACKResponse,
  87. client->config.timeout);
  88. if(retval != UA_STATUSCODE_GOOD)
  89. UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_NETWORK,
  90. "Receiving ACK message failed");
  91. return retval;
  92. }
  93. static void
  94. processDecodedOPNResponse(UA_Client *client, UA_OpenSecureChannelResponse *response) {
  95. /* Replace the token */
  96. UA_ChannelSecurityToken_deleteMembers(&client->channel.securityToken);
  97. client->channel.securityToken = response->securityToken;
  98. UA_ChannelSecurityToken_deleteMembers(&response->securityToken);
  99. /* Replace the nonce */
  100. UA_ByteString_deleteMembers(&client->channel.remoteNonce);
  101. client->channel.remoteNonce = response->serverNonce;
  102. UA_ByteString_deleteMembers(&response->serverNonce);
  103. if(client->channel.state == UA_SECURECHANNELSTATE_OPEN)
  104. UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
  105. "SecureChannel in the server renewed");
  106. else
  107. UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
  108. "Opened SecureChannel acknowledged by the server");
  109. /* Response.securityToken.revisedLifetime is UInt32 we need to cast it to
  110. * DateTime=Int64 we take 75% of lifetime to start renewing as described in
  111. * standard */
  112. client->channel.state = UA_SECURECHANNELSTATE_OPEN;
  113. client->nextChannelRenewal = UA_DateTime_nowMonotonic() + (UA_DateTime)
  114. (response->securityToken.revisedLifetime * (UA_Double)UA_MSEC_TO_DATETIME * 0.75);
  115. }
  116. static UA_StatusCode
  117. openSecureChannel(UA_Client *client, UA_Boolean renew) {
  118. /* Check if sc is still valid */
  119. if(renew && client->nextChannelRenewal - UA_DateTime_nowMonotonic() > 0)
  120. return UA_STATUSCODE_GOOD;
  121. UA_Connection *conn = &client->connection;
  122. if(conn->state != UA_CONNECTION_ESTABLISHED)
  123. return UA_STATUSCODE_BADSERVERNOTCONNECTED;
  124. /* Prepare the OpenSecureChannelRequest */
  125. UA_OpenSecureChannelRequest opnSecRq;
  126. UA_OpenSecureChannelRequest_init(&opnSecRq);
  127. opnSecRq.requestHeader.timestamp = UA_DateTime_now();
  128. opnSecRq.requestHeader.authenticationToken = client->authenticationToken;
  129. if(renew) {
  130. opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_RENEW;
  131. UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
  132. "Requesting to renew the SecureChannel");
  133. } else {
  134. opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_ISSUE;
  135. UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
  136. "Requesting to open a SecureChannel");
  137. }
  138. opnSecRq.securityMode = UA_MESSAGESECURITYMODE_NONE;
  139. opnSecRq.clientNonce = client->channel.localNonce;
  140. opnSecRq.requestedLifetime = client->config.secureChannelLifeTime;
  141. /* Send the OPN message */
  142. UA_UInt32 requestId = ++client->requestId;
  143. UA_StatusCode retval =
  144. UA_SecureChannel_sendAsymmetricOPNMessage(&client->channel, requestId, &opnSecRq,
  145. &UA_TYPES[UA_TYPES_OPENSECURECHANNELREQUEST]);
  146. if(retval != UA_STATUSCODE_GOOD) {
  147. UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
  148. "Sending OPN message failed with error %s", UA_StatusCode_name(retval));
  149. UA_Client_disconnect(client);
  150. return retval;
  151. }
  152. UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "OPN message sent");
  153. /* Receive / decrypt / decode the OPN response. Process async services in
  154. * the background until the OPN response arrives. */
  155. UA_OpenSecureChannelResponse response;
  156. retval = receiveServiceResponse(client, &response,
  157. &UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE],
  158. UA_DateTime_nowMonotonic() +
  159. ((UA_DateTime)client->config.timeout * UA_MSEC_TO_DATETIME),
  160. &requestId);
  161. if(retval != UA_STATUSCODE_GOOD) {
  162. UA_Client_disconnect(client);
  163. return retval;
  164. }
  165. processDecodedOPNResponse(client, &response);
  166. UA_OpenSecureChannelResponse_deleteMembers(&response);
  167. return retval;
  168. }
  169. static UA_StatusCode
  170. activateSession(UA_Client *client) {
  171. UA_ActivateSessionRequest request;
  172. UA_ActivateSessionRequest_init(&request);
  173. request.requestHeader.requestHandle = ++client->requestHandle;
  174. request.requestHeader.timestamp = UA_DateTime_now();
  175. request.requestHeader.timeoutHint = 600000;
  176. //manual ExtensionObject encoding of the identityToken
  177. if(client->authenticationMethod == UA_CLIENTAUTHENTICATION_NONE) {
  178. UA_AnonymousIdentityToken* identityToken = UA_AnonymousIdentityToken_new();
  179. UA_AnonymousIdentityToken_init(identityToken);
  180. UA_String_copy(&client->token.policyId, &identityToken->policyId);
  181. request.userIdentityToken.encoding = UA_EXTENSIONOBJECT_DECODED;
  182. request.userIdentityToken.content.decoded.type = &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN];
  183. request.userIdentityToken.content.decoded.data = identityToken;
  184. } else {
  185. UA_UserNameIdentityToken* identityToken = UA_UserNameIdentityToken_new();
  186. UA_UserNameIdentityToken_init(identityToken);
  187. UA_String_copy(&client->token.policyId, &identityToken->policyId);
  188. UA_String_copy(&client->username, &identityToken->userName);
  189. UA_String_copy(&client->password, &identityToken->password);
  190. request.userIdentityToken.encoding = UA_EXTENSIONOBJECT_DECODED;
  191. request.userIdentityToken.content.decoded.type = &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN];
  192. request.userIdentityToken.content.decoded.data = identityToken;
  193. }
  194. UA_ActivateSessionResponse response;
  195. __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST],
  196. &response, &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE]);
  197. if(response.responseHeader.serviceResult) {
  198. UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
  199. "ActivateSession failed with error code %s",
  200. UA_StatusCode_name(response.responseHeader.serviceResult));
  201. }
  202. UA_StatusCode retval = response.responseHeader.serviceResult;
  203. UA_ActivateSessionRequest_deleteMembers(&request);
  204. UA_ActivateSessionResponse_deleteMembers(&response);
  205. return retval;
  206. }
  207. /* Gets a list of endpoints. Memory is allocated for endpointDescription array */
  208. UA_StatusCode
  209. UA_Client_getEndpointsInternal(UA_Client *client, size_t* endpointDescriptionsSize,
  210. UA_EndpointDescription** endpointDescriptions) {
  211. UA_GetEndpointsRequest request;
  212. UA_GetEndpointsRequest_init(&request);
  213. request.requestHeader.timestamp = UA_DateTime_now();
  214. request.requestHeader.timeoutHint = 10000;
  215. // assume the endpointurl outlives the service call
  216. request.endpointUrl = client->endpointUrl;
  217. UA_GetEndpointsResponse response;
  218. UA_GetEndpointsResponse_init(&response);
  219. __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST],
  220. &response, &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE]);
  221. if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
  222. UA_StatusCode retval = response.responseHeader.serviceResult;
  223. UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
  224. "GetEndpointRequest failed with error code %s",
  225. UA_StatusCode_name(retval));
  226. UA_GetEndpointsResponse_deleteMembers(&response);
  227. return retval;
  228. }
  229. *endpointDescriptions = response.endpoints;
  230. *endpointDescriptionsSize = response.endpointsSize;
  231. response.endpoints = NULL;
  232. response.endpointsSize = 0;
  233. UA_GetEndpointsResponse_deleteMembers(&response);
  234. return UA_STATUSCODE_GOOD;
  235. }
  236. static UA_StatusCode
  237. getEndpoints(UA_Client *client) {
  238. UA_EndpointDescription* endpointArray = NULL;
  239. size_t endpointArraySize = 0;
  240. UA_StatusCode retval =
  241. UA_Client_getEndpointsInternal(client, &endpointArraySize, &endpointArray);
  242. if(retval != UA_STATUSCODE_GOOD)
  243. return retval;
  244. UA_Boolean endpointFound = false;
  245. UA_Boolean tokenFound = false;
  246. UA_String securityNone = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None");
  247. UA_String binaryTransport = UA_STRING("http://opcfoundation.org/UA-Profile/"
  248. "Transport/uatcp-uasc-uabinary");
  249. // TODO: compare endpoint information with client->endpointUri
  250. for(size_t i = 0; i < endpointArraySize; ++i) {
  251. UA_EndpointDescription* endpoint = &endpointArray[i];
  252. /* look out for binary transport endpoints */
  253. /* Note: Siemens returns empty ProfileUrl, we will accept it as binary */
  254. if(endpoint->transportProfileUri.length != 0 &&
  255. !UA_String_equal(&endpoint->transportProfileUri, &binaryTransport))
  256. continue;
  257. /* look out for an endpoint without security */
  258. if(!UA_String_equal(&endpoint->securityPolicyUri, &securityNone))
  259. continue;
  260. /* endpoint with no security found */
  261. endpointFound = true;
  262. /* look for a user token policy with an anonymous token */
  263. for(size_t j = 0; j < endpoint->userIdentityTokensSize; ++j) {
  264. UA_UserTokenPolicy* userToken = &endpoint->userIdentityTokens[j];
  265. /* Usertokens also have a security policy... */
  266. if(userToken->securityPolicyUri.length > 0 &&
  267. !UA_String_equal(&userToken->securityPolicyUri, &securityNone))
  268. continue;
  269. /* UA_CLIENTAUTHENTICATION_NONE == UA_USERTOKENTYPE_ANONYMOUS
  270. * UA_CLIENTAUTHENTICATION_USERNAME == UA_USERTOKENTYPE_USERNAME
  271. * TODO: Check equivalence for other types when adding the support */
  272. if((int)client->authenticationMethod != (int)userToken->tokenType)
  273. continue;
  274. /* Endpoint with matching usertokenpolicy found */
  275. tokenFound = true;
  276. UA_UserTokenPolicy_deleteMembers(&client->token);
  277. UA_UserTokenPolicy_copy(userToken, &client->token);
  278. break;
  279. }
  280. }
  281. UA_Array_delete(endpointArray, endpointArraySize,
  282. &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
  283. if(!endpointFound) {
  284. UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
  285. "No suitable endpoint found");
  286. retval = UA_STATUSCODE_BADINTERNALERROR;
  287. } else if(!tokenFound) {
  288. UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
  289. "No suitable UserTokenPolicy found for the possible endpoints");
  290. retval = UA_STATUSCODE_BADINTERNALERROR;
  291. }
  292. return retval;
  293. }
  294. static UA_StatusCode
  295. createSession(UA_Client *client) {
  296. UA_CreateSessionRequest request;
  297. UA_CreateSessionRequest_init(&request);
  298. request.requestHeader.timestamp = UA_DateTime_now();
  299. request.requestHeader.timeoutHint = 10000;
  300. UA_ByteString_copy(&client->channel.localNonce, &request.clientNonce);
  301. request.requestedSessionTimeout = 1200000;
  302. request.maxResponseMessageSize = UA_INT32_MAX;
  303. UA_String_copy(&client->endpointUrl, &request.endpointUrl);
  304. UA_CreateSessionResponse response;
  305. UA_CreateSessionResponse_init(&response);
  306. __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST],
  307. &response, &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE]);
  308. UA_NodeId_copy(&response.authenticationToken, &client->authenticationToken);
  309. UA_StatusCode retval = response.responseHeader.serviceResult;
  310. UA_CreateSessionRequest_deleteMembers(&request);
  311. UA_CreateSessionResponse_deleteMembers(&response);
  312. return retval;
  313. }
  314. UA_StatusCode
  315. UA_Client_connectInternal(UA_Client *client, const char *endpointUrl,
  316. UA_Boolean endpointsHandshake, UA_Boolean createNewSession) {
  317. UA_ChannelSecurityToken_init(&client->channel.securityToken);
  318. client->channel.state = UA_SECURECHANNELSTATE_FRESH;
  319. if(client->state >= UA_CLIENTSTATE_CONNECTED)
  320. return UA_STATUSCODE_GOOD;
  321. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  322. client->connection =
  323. client->config.connectionFunc(client->config.localConnectionConfig,
  324. endpointUrl, client->config.timeout);
  325. if(client->connection.state != UA_CONNECTION_OPENING) {
  326. retval = UA_STATUSCODE_BADCONNECTIONCLOSED;
  327. goto cleanup;
  328. }
  329. UA_String_deleteMembers(&client->endpointUrl);
  330. client->endpointUrl = UA_STRING_ALLOC(endpointUrl);
  331. if(!client->endpointUrl.data) {
  332. retval = UA_STATUSCODE_BADOUTOFMEMORY;
  333. goto cleanup;
  334. }
  335. /* Open a TCP connection */
  336. client->connection.localConf = client->config.localConnectionConfig;
  337. retval = HelAckHandshake(client);
  338. if(retval != UA_STATUSCODE_GOOD)
  339. goto cleanup;
  340. client->state = UA_CLIENTSTATE_CONNECTED;
  341. /* Open a SecureChannel. TODO: Select with endpoint */
  342. client->channel.connection = &client->connection;
  343. retval = openSecureChannel(client, false);
  344. if(retval != UA_STATUSCODE_GOOD)
  345. goto cleanup;
  346. client->state = UA_CLIENTSTATE_SECURECHANNEL;
  347. /* Get Endpoints */
  348. if(endpointsHandshake) {
  349. retval = getEndpoints(client);
  350. if(retval != UA_STATUSCODE_GOOD)
  351. goto cleanup;
  352. }
  353. /* Open a Session */
  354. if(createNewSession) {
  355. retval = createSession(client);
  356. if(retval != UA_STATUSCODE_GOOD)
  357. goto cleanup;
  358. retval = activateSession(client);
  359. if(retval != UA_STATUSCODE_GOOD)
  360. goto cleanup;
  361. client->state = UA_CLIENTSTATE_SESSION;
  362. }
  363. return retval;
  364. cleanup:
  365. UA_Client_disconnect(client);
  366. return retval;
  367. }
  368. UA_StatusCode
  369. UA_Client_connect(UA_Client *client, const char *endpointUrl) {
  370. return UA_Client_connectInternal(client, endpointUrl, UA_TRUE, UA_TRUE);
  371. }
  372. UA_StatusCode
  373. UA_Client_connect_username(UA_Client *client, const char *endpointUrl,
  374. const char *username, const char *password) {
  375. client->authenticationMethod = UA_CLIENTAUTHENTICATION_USERNAME;
  376. client->username = UA_STRING_ALLOC(username);
  377. client->password = UA_STRING_ALLOC(password);
  378. return UA_Client_connect(client, endpointUrl);
  379. }
  380. UA_StatusCode
  381. UA_Client_manuallyRenewSecureChannel(UA_Client *client) {
  382. UA_StatusCode retval = openSecureChannel(client, true);
  383. if(retval != UA_STATUSCODE_GOOD)
  384. client->state = UA_CLIENTSTATE_DISCONNECTED;
  385. return retval;
  386. }
  387. /************************/
  388. /* Close the Connection */
  389. /************************/
  390. static void
  391. sendCloseSession(UA_Client *client) {
  392. UA_CloseSessionRequest request;
  393. UA_CloseSessionRequest_init(&request);
  394. request.requestHeader.timestamp = UA_DateTime_now();
  395. request.requestHeader.timeoutHint = 10000;
  396. request.deleteSubscriptions = true;
  397. UA_CloseSessionResponse response;
  398. __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST],
  399. &response, &UA_TYPES[UA_TYPES_CLOSESESSIONRESPONSE]);
  400. UA_CloseSessionRequest_deleteMembers(&request);
  401. UA_CloseSessionResponse_deleteMembers(&response);
  402. }
  403. static void
  404. sendCloseSecureChannel(UA_Client *client) {
  405. UA_SecureChannel *channel = &client->channel;
  406. UA_CloseSecureChannelRequest request;
  407. UA_CloseSecureChannelRequest_init(&request);
  408. request.requestHeader.requestHandle = ++client->requestHandle;
  409. request.requestHeader.timestamp = UA_DateTime_now();
  410. request.requestHeader.timeoutHint = 10000;
  411. request.requestHeader.authenticationToken = client->authenticationToken;
  412. UA_SecureChannel_sendSymmetricMessage(channel, ++client->requestId,
  413. UA_MESSAGETYPE_CLO, &request,
  414. &UA_TYPES[UA_TYPES_CLOSESECURECHANNELREQUEST]);
  415. UA_SecureChannel_deleteMembersCleanup(&client->channel);
  416. }
  417. UA_StatusCode
  418. UA_Client_disconnect(UA_Client *client) {
  419. /* Is a session established? */
  420. if(client->state == UA_CLIENTSTATE_SESSION){
  421. client->state = UA_CLIENTSTATE_SESSION_DISCONNECTED;
  422. sendCloseSession(client);
  423. }
  424. /* Is a secure channel established? */
  425. if(client->state >= UA_CLIENTSTATE_SECURECHANNEL)
  426. sendCloseSecureChannel(client);
  427. /* Close the TCP connection */
  428. if(client->state >= UA_CLIENTSTATE_CONNECTED)
  429. client->connection.close(&client->connection);
  430. client->state = UA_CLIENTSTATE_DISCONNECTED;
  431. return UA_STATUSCODE_GOOD;
  432. }