ua_client.c 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796
  1. #include "ua_types_generated.h"
  2. #include "ua_client.h"
  3. #include "ua_nodeids.h"
  4. #include "ua_securechannel.h"
  5. #include "ua_types_encoding_binary.h"
  6. #include "ua_transport_generated.h"
  7. struct UA_Client {
  8. /* Connection */
  9. UA_Connection connection;
  10. UA_SecureChannel channel;
  11. UA_String endpointUrl;
  12. UA_UInt32 requestId;
  13. /* Session */
  14. UA_UserTokenPolicy token;
  15. UA_NodeId sessionId;
  16. UA_NodeId authenticationToken;
  17. /* Config */
  18. UA_Logger logger;
  19. UA_ClientConfig config;
  20. UA_DateTime scExpiresAt;
  21. };
  22. const UA_EXPORT UA_ClientConfig UA_ClientConfig_standard =
  23. { 5 /* ms receive timout */, 30000, 2000,
  24. {.protocolVersion = 0, .sendBufferSize = 65536, .recvBufferSize = 65536,
  25. .maxMessageSize = 65536, .maxChunkCount = 1}};
  26. UA_Client * UA_Client_new(UA_ClientConfig config, UA_Logger logger) {
  27. UA_Client *client = UA_calloc(1, sizeof(UA_Client));
  28. if(!client)
  29. return UA_NULL;
  30. UA_Connection_init(&client->connection);
  31. UA_SecureChannel_init(&client->channel);
  32. client->channel.connection = &client->connection;
  33. UA_String_init(&client->endpointUrl);
  34. client->requestId = 0;
  35. UA_NodeId_init(&client->authenticationToken);
  36. client->logger = logger;
  37. client->config = config;
  38. client->scExpiresAt = 0;
  39. return client;
  40. }
  41. void UA_Client_delete(UA_Client* client){
  42. UA_Connection_deleteMembers(&client->connection);
  43. UA_SecureChannel_deleteMembersCleanup(&client->channel);
  44. UA_String_deleteMembers(&client->endpointUrl);
  45. UA_UserTokenPolicy_deleteMembers(&client->token);
  46. free(client);
  47. }
  48. static UA_StatusCode HelAckHandshake(UA_Client *c) {
  49. UA_TcpMessageHeader messageHeader;
  50. messageHeader.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_HELF;
  51. UA_TcpHelloMessage hello;
  52. UA_String_copy(&c->endpointUrl, &hello.endpointUrl); /* must be less than 4096 bytes */
  53. UA_Connection *conn = &c->connection;
  54. hello.maxChunkCount = conn->localConf.maxChunkCount;
  55. hello.maxMessageSize = conn->localConf.maxMessageSize;
  56. hello.protocolVersion = conn->localConf.protocolVersion;
  57. hello.receiveBufferSize = conn->localConf.recvBufferSize;
  58. hello.sendBufferSize = conn->localConf.sendBufferSize;
  59. UA_ByteString message;
  60. UA_StatusCode retval = c->connection.getBuffer(&c->connection, &message);
  61. if(retval != UA_STATUSCODE_GOOD)
  62. return retval;
  63. size_t offset = 8;
  64. retval |= UA_TcpHelloMessage_encodeBinary(&hello, &message, &offset);
  65. messageHeader.messageSize = offset;
  66. offset = 0;
  67. retval |= UA_TcpMessageHeader_encodeBinary(&messageHeader, &message, &offset);
  68. UA_TcpHelloMessage_deleteMembers(&hello);
  69. if(retval != UA_STATUSCODE_GOOD) {
  70. c->connection.releaseBuffer(&c->connection, &message);
  71. return retval;
  72. }
  73. retval = c->connection.write(&c->connection, &message, messageHeader.messageSize);
  74. if(retval != UA_STATUSCODE_GOOD) {
  75. c->connection.releaseBuffer(&c->connection, &message);
  76. return retval;
  77. }
  78. UA_ByteString reply;
  79. UA_ByteString_init(&reply);
  80. do {
  81. retval = c->connection.recv(&c->connection, &reply, c->config.timeout);
  82. if(retval != UA_STATUSCODE_GOOD)
  83. return retval;
  84. } while(!reply.data);
  85. offset = 0;
  86. UA_TcpMessageHeader_decodeBinary(&reply, &offset, &messageHeader);
  87. UA_TcpAcknowledgeMessage ackMessage;
  88. retval = UA_TcpAcknowledgeMessage_decodeBinary(&reply, &offset, &ackMessage);
  89. UA_ByteString_deleteMembers(&reply);
  90. if(retval != UA_STATUSCODE_GOOD)
  91. return retval;
  92. conn->remoteConf.maxChunkCount = ackMessage.maxChunkCount;
  93. conn->remoteConf.maxMessageSize = ackMessage.maxMessageSize;
  94. conn->remoteConf.protocolVersion = ackMessage.protocolVersion;
  95. conn->remoteConf.recvBufferSize = ackMessage.receiveBufferSize;
  96. conn->remoteConf.sendBufferSize = ackMessage.sendBufferSize;
  97. conn->state = UA_CONNECTION_ESTABLISHED;
  98. return UA_STATUSCODE_GOOD;
  99. }
  100. static UA_StatusCode SecureChannelHandshake(UA_Client *client, UA_Boolean renew) {
  101. UA_SecureConversationMessageHeader messageHeader;
  102. messageHeader.messageHeader.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_OPNF;
  103. messageHeader.secureChannelId = 0;
  104. UA_SequenceHeader seqHeader;
  105. seqHeader.sequenceNumber = ++client->channel.sequenceNumber;
  106. seqHeader.requestId = ++client->requestId;
  107. UA_AsymmetricAlgorithmSecurityHeader asymHeader;
  108. UA_AsymmetricAlgorithmSecurityHeader_init(&asymHeader);
  109. asymHeader.securityPolicyUri = UA_STRING_ALLOC("http://opcfoundation.org/UA/SecurityPolicy#None");
  110. /* id of opensecurechannelrequest */
  111. UA_NodeId requestType = UA_NODEID_NUMERIC(0, UA_NS0ID_OPENSECURECHANNELREQUEST + UA_ENCODINGOFFSET_BINARY);
  112. UA_OpenSecureChannelRequest opnSecRq;
  113. UA_OpenSecureChannelRequest_init(&opnSecRq);
  114. opnSecRq.requestHeader.timestamp = UA_DateTime_now();
  115. opnSecRq.requestHeader.authenticationToken = client->authenticationToken;
  116. opnSecRq.requestedLifetime = client->config.secureChannelLifeTime;
  117. if(renew) {
  118. opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_RENEW;
  119. } else {
  120. opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_ISSUE;
  121. UA_SecureChannel_generateNonce(&client->channel.clientNonce);
  122. UA_ByteString_copy(&client->channel.clientNonce, &opnSecRq.clientNonce);
  123. opnSecRq.securityMode = UA_MESSAGESECURITYMODE_NONE;
  124. }
  125. UA_ByteString message;
  126. UA_StatusCode retval = client->connection.getBuffer(&client->connection, &message);
  127. if(retval != UA_STATUSCODE_GOOD) {
  128. UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
  129. UA_OpenSecureChannelRequest_deleteMembers(&opnSecRq);
  130. return retval;
  131. }
  132. size_t offset = 12;
  133. retval = UA_AsymmetricAlgorithmSecurityHeader_encodeBinary(&asymHeader, &message, &offset);
  134. retval |= UA_SequenceHeader_encodeBinary(&seqHeader, &message, &offset);
  135. retval |= UA_NodeId_encodeBinary(&requestType, &message, &offset);
  136. retval |= UA_OpenSecureChannelRequest_encodeBinary(&opnSecRq, &message, &offset);
  137. messageHeader.messageHeader.messageSize = offset;
  138. offset = 0;
  139. retval |= UA_SecureConversationMessageHeader_encodeBinary(&messageHeader, &message, &offset);
  140. UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
  141. UA_OpenSecureChannelRequest_deleteMembers(&opnSecRq);
  142. if(retval != UA_STATUSCODE_GOOD) {
  143. client->connection.releaseBuffer(&client->connection, &message);
  144. return retval;
  145. }
  146. retval = client->connection.write(&client->connection, &message, messageHeader.messageHeader.messageSize);
  147. if(retval != UA_STATUSCODE_GOOD) {
  148. client->connection.releaseBuffer(&client->connection, &message);
  149. return retval;
  150. }
  151. UA_ByteString reply;
  152. UA_ByteString_init(&reply);
  153. do {
  154. retval = client->connection.recv(&client->connection, &reply, client->config.timeout);
  155. if(retval != UA_STATUSCODE_GOOD)
  156. return retval;
  157. } while(!reply.data);
  158. offset = 0;
  159. UA_SecureConversationMessageHeader_decodeBinary(&reply, &offset, &messageHeader);
  160. UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(&reply, &offset, &asymHeader);
  161. UA_SequenceHeader_decodeBinary(&reply, &offset, &seqHeader);
  162. UA_NodeId_decodeBinary(&reply, &offset, &requestType);
  163. UA_NodeId expectedRequest = UA_NODEID_NUMERIC(0, UA_NS0ID_OPENSECURECHANNELRESPONSE +
  164. UA_ENCODINGOFFSET_BINARY);
  165. if(!UA_NodeId_equal(&requestType, &expectedRequest)) {
  166. UA_ByteString_deleteMembers(&reply);
  167. UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
  168. UA_NodeId_deleteMembers(&requestType);
  169. UA_LOG_ERROR(client->logger, UA_LOGCATEGORY_CLIENT,
  170. "Reply answers the wrong request. Expected OpenSecureChannelResponse.");
  171. return UA_STATUSCODE_BADINTERNALERROR;
  172. }
  173. UA_OpenSecureChannelResponse response;
  174. UA_OpenSecureChannelResponse_decodeBinary(&reply, &offset, &response);
  175. client->scExpiresAt = UA_DateTime_now() + response.securityToken.revisedLifetime * 10000;
  176. UA_ByteString_deleteMembers(&reply);
  177. retval = response.responseHeader.serviceResult;
  178. if(!renew && retval == UA_STATUSCODE_GOOD) {
  179. UA_ChannelSecurityToken_copy(&response.securityToken, &client->channel.securityToken);
  180. UA_ByteString_deleteMembers(&client->channel.serverNonce); // if the handshake is repeated
  181. UA_ByteString_copy(&response.serverNonce, &client->channel.serverNonce);
  182. }
  183. UA_OpenSecureChannelResponse_deleteMembers(&response);
  184. UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
  185. return retval;
  186. }
  187. /** If the request fails, then the response is cast to UA_ResponseHeader (at the beginning of every
  188. response) and filled with the appropriate error code */
  189. static void synchronousRequest(UA_Client *client, void *request, const UA_DataType *requestType,
  190. void *response, const UA_DataType *responseType) {
  191. /* Check if sc needs to be renewed */
  192. if(client->scExpiresAt - UA_DateTime_now() <= client->config.timeToRenewSecureChannel * 10000 )
  193. UA_Client_renewSecureChannel(client);
  194. /* Copy authenticationToken token to request header */
  195. typedef struct {
  196. UA_RequestHeader requestHeader;
  197. } headerOnlyRequest;
  198. /* The cast is valid, since all requests start with a requestHeader */
  199. UA_NodeId_copy(&client->authenticationToken, &((headerOnlyRequest*)request)->requestHeader.authenticationToken);
  200. if(!response)
  201. return;
  202. UA_init(response, responseType);
  203. /* Send the request */
  204. UA_UInt32 requestId = ++client->requestId;
  205. UA_StatusCode retval = UA_SecureChannel_sendBinaryMessage(&client->channel, requestId,
  206. request, requestType);
  207. UA_ResponseHeader *respHeader = (UA_ResponseHeader*)response;
  208. if(retval) {
  209. if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED)
  210. respHeader->serviceResult = UA_STATUSCODE_BADREQUESTTOOLARGE;
  211. else
  212. respHeader->serviceResult = retval;
  213. return;
  214. }
  215. /* Retrieve the response */
  216. // Todo: push this into the generic securechannel implementation for client and server
  217. UA_ByteString reply;
  218. UA_ByteString_init(&reply);
  219. do {
  220. retval = client->connection.recv(&client->connection, &reply, client->config.timeout);
  221. if(retval != UA_STATUSCODE_GOOD) {
  222. respHeader->serviceResult = retval;
  223. return;
  224. }
  225. } while(!reply.data);
  226. size_t offset = 0;
  227. UA_SecureConversationMessageHeader msgHeader;
  228. retval |= UA_SecureConversationMessageHeader_decodeBinary(&reply, &offset, &msgHeader);
  229. UA_SymmetricAlgorithmSecurityHeader symHeader;
  230. retval |= UA_SymmetricAlgorithmSecurityHeader_decodeBinary(&reply, &offset, &symHeader);
  231. UA_SequenceHeader seqHeader;
  232. retval |= UA_SequenceHeader_decodeBinary(&reply, &offset, &seqHeader);
  233. UA_NodeId responseId;
  234. retval |= UA_NodeId_decodeBinary(&reply, &offset, &responseId);
  235. UA_NodeId expectedNodeId = UA_NODEID_NUMERIC(0, responseType->typeId.identifier.numeric +
  236. UA_ENCODINGOFFSET_BINARY);
  237. if(retval != UA_STATUSCODE_GOOD)
  238. goto finish;
  239. /* Todo: we need to demux responses since a publish responses may come at any time */
  240. if(!UA_NodeId_equal(&responseId, &expectedNodeId) || seqHeader.requestId != requestId) {
  241. if(responseId.identifier.numeric != UA_NS0ID_SERVICEFAULT + UA_ENCODINGOFFSET_BINARY) {
  242. UA_LOG_ERROR(client->logger, UA_LOGCATEGORY_CLIENT,
  243. "Reply answers the wrong request. Expected ns=%i,i=%i. But retrieved ns=%i,i=%i",
  244. expectedNodeId.namespaceIndex, expectedNodeId.identifier.numeric,
  245. responseId.namespaceIndex, responseId.identifier.numeric);
  246. respHeader->serviceResult = UA_STATUSCODE_BADINTERNALERROR;
  247. } else
  248. retval = UA_decodeBinary(&reply, &offset, respHeader, &UA_TYPES[UA_TYPES_SERVICEFAULT]);
  249. goto finish;
  250. }
  251. retval = UA_decodeBinary(&reply, &offset, response, responseType);
  252. if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED)
  253. retval = UA_STATUSCODE_BADRESPONSETOOLARGE;
  254. finish:
  255. UA_SymmetricAlgorithmSecurityHeader_deleteMembers(&symHeader);
  256. UA_ByteString_deleteMembers(&reply);
  257. if(retval != UA_STATUSCODE_GOOD)
  258. respHeader->serviceResult = retval;
  259. }
  260. static UA_StatusCode ActivateSession(UA_Client *client) {
  261. UA_ActivateSessionRequest request;
  262. UA_ActivateSessionRequest_init(&request);
  263. request.requestHeader.requestHandle = 2; //TODO: is it a magic number?
  264. request.requestHeader.authenticationToken = client->authenticationToken;
  265. request.requestHeader.timestamp = UA_DateTime_now();
  266. request.requestHeader.timeoutHint = 10000;
  267. UA_AnonymousIdentityToken identityToken;
  268. UA_AnonymousIdentityToken_init(&identityToken);
  269. UA_String_copy(&client->token.policyId, &identityToken.policyId);
  270. //manual ExtensionObject encoding of the identityToken
  271. request.userIdentityToken.encoding = UA_EXTENSIONOBJECT_ENCODINGMASK_BODYISBYTESTRING;
  272. request.userIdentityToken.typeId = UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN].typeId;
  273. request.userIdentityToken.typeId.identifier.numeric+=UA_ENCODINGOFFSET_BINARY;
  274. UA_ByteString_newMembers(&request.userIdentityToken.body, identityToken.policyId.length+4);
  275. size_t offset = 0;
  276. UA_ByteString_encodeBinary(&identityToken.policyId,&request.userIdentityToken.body,&offset);
  277. UA_ActivateSessionResponse response;
  278. synchronousRequest(client, &request, &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST],
  279. &response, &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE]);
  280. UA_AnonymousIdentityToken_deleteMembers(&identityToken);
  281. UA_ActivateSessionRequest_deleteMembers(&request);
  282. UA_ActivateSessionResponse_deleteMembers(&response);
  283. return response.responseHeader.serviceResult; // not deleted
  284. }
  285. static UA_StatusCode EndpointsHandshake(UA_Client *client) {
  286. UA_GetEndpointsRequest request;
  287. UA_GetEndpointsRequest_init(&request);
  288. UA_NodeId_copy(&client->authenticationToken, &request.requestHeader.authenticationToken);
  289. request.requestHeader.timestamp = UA_DateTime_now();
  290. request.requestHeader.timeoutHint = 10000;
  291. UA_String_copy(&client->endpointUrl, &request.endpointUrl);
  292. request.profileUrisSize = 1;
  293. request.profileUris = UA_Array_new(&UA_TYPES[UA_TYPES_STRING], request.profileUrisSize);
  294. *request.profileUris = UA_STRING_ALLOC("http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary");
  295. UA_GetEndpointsResponse response;
  296. UA_GetEndpointsResponse_init(&response);
  297. synchronousRequest(client, &request, &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST],
  298. &response, &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE]);
  299. UA_Boolean endpointFound = UA_FALSE;
  300. UA_Boolean tokenFound = UA_FALSE;
  301. for(UA_Int32 i=0; i<response.endpointsSize; ++i){
  302. UA_EndpointDescription* endpoint = &response.endpoints[i];
  303. /* look out for an endpoint without security */
  304. if(!UA_String_equal(&endpoint->securityPolicyUri,
  305. &UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None")))
  306. continue;
  307. endpointFound = UA_TRUE;
  308. /* endpoint with no security found */
  309. /* look for a user token policy with an anonymous token */
  310. for(UA_Int32 j=0; j<endpoint->userIdentityTokensSize; ++j) {
  311. UA_UserTokenPolicy* userToken = &endpoint->userIdentityTokens[j];
  312. if(userToken->tokenType != UA_USERTOKENTYPE_ANONYMOUS)
  313. continue;
  314. tokenFound = UA_TRUE;
  315. UA_UserTokenPolicy_copy(userToken, &client->token);
  316. break;
  317. }
  318. }
  319. UA_GetEndpointsRequest_deleteMembers(&request);
  320. UA_GetEndpointsResponse_deleteMembers(&response);
  321. if(!endpointFound){
  322. UA_LOG_ERROR(client->logger, UA_LOGCATEGORY_CLIENT, "No suitable endpoint found");
  323. return UA_STATUSCODE_BADINTERNALERROR;
  324. }
  325. if(!tokenFound){
  326. UA_LOG_ERROR(client->logger, UA_LOGCATEGORY_CLIENT, "No anonymous token found");
  327. return UA_STATUSCODE_BADINTERNALERROR;
  328. }
  329. return response.responseHeader.serviceResult;
  330. }
  331. static UA_StatusCode SessionHandshake(UA_Client *client) {
  332. UA_CreateSessionRequest request;
  333. UA_CreateSessionRequest_init(&request);
  334. // todo: is this needed for all requests?
  335. UA_NodeId_copy(&client->authenticationToken, &request.requestHeader.authenticationToken);
  336. request.requestHeader.timestamp = UA_DateTime_now();
  337. request.requestHeader.timeoutHint = 10000;
  338. UA_ByteString_copy(&client->channel.clientNonce, &request.clientNonce);
  339. request.requestedSessionTimeout = 1200000;
  340. request.maxResponseMessageSize = UA_INT32_MAX;
  341. UA_CreateSessionResponse response;
  342. UA_CreateSessionResponse_init(&response);
  343. synchronousRequest(client, &request, &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST],
  344. &response, &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE]);
  345. UA_NodeId_copy(&response.authenticationToken, &client->authenticationToken);
  346. UA_CreateSessionRequest_deleteMembers(&request);
  347. UA_CreateSessionResponse_deleteMembers(&response);
  348. return response.responseHeader.serviceResult; // not deleted
  349. }
  350. static UA_StatusCode CloseSession(UA_Client *client) {
  351. UA_CloseSessionRequest request;
  352. UA_CloseSessionRequest_init(&request);
  353. request.requestHeader.timestamp = UA_DateTime_now();
  354. request.requestHeader.timeoutHint = 10000;
  355. request.deleteSubscriptions = UA_TRUE;
  356. UA_NodeId_copy(&client->authenticationToken, &request.requestHeader.authenticationToken);
  357. UA_CreateSessionResponse response;
  358. synchronousRequest(client, &request, &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST],
  359. &response, &UA_TYPES[UA_TYPES_CLOSESESSIONRESPONSE]);
  360. UA_CloseSessionRequest_deleteMembers(&request);
  361. UA_CloseSessionResponse_deleteMembers(&response);
  362. return response.responseHeader.serviceResult; // not deleted
  363. }
  364. static UA_StatusCode CloseSecureChannel(UA_Client *client) {
  365. UA_SecureChannel *channel = &client->channel;
  366. UA_CloseSecureChannelRequest request;
  367. UA_CloseSecureChannelRequest_init(&request);
  368. request.requestHeader.requestHandle = 1; //TODO: magic number?
  369. request.requestHeader.timestamp = UA_DateTime_now();
  370. request.requestHeader.timeoutHint = 10000;
  371. request.requestHeader.authenticationToken = client->authenticationToken;
  372. UA_SecureConversationMessageHeader msgHeader;
  373. msgHeader.messageHeader.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_CLOF;
  374. msgHeader.secureChannelId = client->channel.securityToken.channelId;
  375. UA_SymmetricAlgorithmSecurityHeader symHeader;
  376. symHeader.tokenId = channel->securityToken.tokenId;
  377. UA_SequenceHeader seqHeader;
  378. seqHeader.sequenceNumber = ++channel->sequenceNumber;
  379. seqHeader.requestId = ++client->requestId;
  380. UA_NodeId typeId = UA_NODEID_NUMERIC(0, UA_NS0ID_CLOSESECURECHANNELREQUEST + UA_ENCODINGOFFSET_BINARY);
  381. UA_ByteString message;
  382. UA_StatusCode retval = client->connection.getBuffer(&client->connection, &message);
  383. if(retval != UA_STATUSCODE_GOOD)
  384. return retval;
  385. size_t offset = 12;
  386. retval |= UA_SymmetricAlgorithmSecurityHeader_encodeBinary(&symHeader, &message, &offset);
  387. retval |= UA_SequenceHeader_encodeBinary(&seqHeader, &message, &offset);
  388. retval |= UA_NodeId_encodeBinary(&typeId, &message, &offset);
  389. retval |= UA_encodeBinary(&request, &UA_TYPES[UA_TYPES_CLOSESECURECHANNELREQUEST], &message, &offset);
  390. msgHeader.messageHeader.messageSize = offset;
  391. offset = 0;
  392. retval |= UA_SecureConversationMessageHeader_encodeBinary(&msgHeader, &message, &offset);
  393. if(retval != UA_STATUSCODE_GOOD) {
  394. client->connection.releaseBuffer(&client->connection, &message);
  395. return retval;
  396. }
  397. retval = client->connection.write(&client->connection, &message, msgHeader.messageHeader.messageSize);
  398. if(retval != UA_STATUSCODE_GOOD)
  399. client->connection.releaseBuffer(&client->connection, &message);
  400. return retval;
  401. }
  402. /*************************/
  403. /* User-Facing Functions */
  404. /*************************/
  405. UA_StatusCode UA_Client_connect(UA_Client *client, UA_ConnectClientConnection connectFunc, char *endpointUrl) {
  406. client->connection = connectFunc(UA_ConnectionConfig_standard, endpointUrl, &client->logger);
  407. if(client->connection.state != UA_CONNECTION_OPENING)
  408. return UA_STATUSCODE_BADCONNECTIONCLOSED;
  409. client->endpointUrl = UA_STRING_ALLOC(endpointUrl);
  410. if(client->endpointUrl.length < 0)
  411. return UA_STATUSCODE_BADOUTOFMEMORY;
  412. client->connection.localConf = client->config.localConnectionConfig;
  413. UA_StatusCode retval = HelAckHandshake(client);
  414. if(retval == UA_STATUSCODE_GOOD)
  415. retval = SecureChannelHandshake(client, UA_FALSE);
  416. if(retval == UA_STATUSCODE_GOOD)
  417. retval = EndpointsHandshake(client);
  418. if(retval == UA_STATUSCODE_GOOD)
  419. retval = SessionHandshake(client);
  420. if(retval == UA_STATUSCODE_GOOD)
  421. retval = ActivateSession(client);
  422. if(retval == UA_STATUSCODE_GOOD)
  423. client->connection.state = UA_CONNECTION_ESTABLISHED;
  424. return retval;
  425. }
  426. UA_StatusCode UA_Client_disconnect(UA_Client *client) {
  427. UA_StatusCode retval;
  428. if(client->channel.connection->state != UA_CONNECTION_ESTABLISHED)
  429. return UA_STATUSCODE_GOOD;
  430. retval = CloseSession(client);
  431. if(retval == UA_STATUSCODE_GOOD)
  432. retval = CloseSecureChannel(client);
  433. return retval;
  434. }
  435. UA_StatusCode UA_Client_renewSecureChannel(UA_Client *client) {
  436. return SecureChannelHandshake(client, UA_TRUE);
  437. }
  438. UA_ReadResponse UA_Client_read(UA_Client *client, UA_ReadRequest *request) {
  439. UA_ReadResponse response;
  440. synchronousRequest(client, request, &UA_TYPES[UA_TYPES_READREQUEST], &response,
  441. &UA_TYPES[UA_TYPES_READRESPONSE]);
  442. return response;
  443. }
  444. UA_WriteResponse UA_Client_write(UA_Client *client, UA_WriteRequest *request) {
  445. UA_WriteResponse response;
  446. synchronousRequest(client, request, &UA_TYPES[UA_TYPES_WRITEREQUEST], &response,
  447. &UA_TYPES[UA_TYPES_WRITERESPONSE]);
  448. return response;
  449. }
  450. UA_BrowseResponse UA_Client_browse(UA_Client *client, UA_BrowseRequest *request) {
  451. UA_BrowseResponse response;
  452. synchronousRequest(client, request, &UA_TYPES[UA_TYPES_BROWSEREQUEST], &response,
  453. &UA_TYPES[UA_TYPES_BROWSERESPONSE]);
  454. return response;
  455. }
  456. UA_BrowseNextResponse UA_Client_browseNext(UA_Client *client, UA_BrowseNextRequest *request) {
  457. UA_BrowseNextResponse response;
  458. synchronousRequest(client, request, &UA_TYPES[UA_TYPES_BROWSENEXTREQUEST], &response,
  459. &UA_TYPES[UA_TYPES_BROWSENEXTRESPONSE]);
  460. return response;
  461. }
  462. UA_TranslateBrowsePathsToNodeIdsResponse
  463. UA_Client_translateTranslateBrowsePathsToNodeIds(UA_Client *client,
  464. UA_TranslateBrowsePathsToNodeIdsRequest *request) {
  465. UA_TranslateBrowsePathsToNodeIdsResponse response;
  466. synchronousRequest(client, request, &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSREQUEST],
  467. &response, &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE]);
  468. return response;
  469. }
  470. UA_AddNodesResponse UA_Client_addNodes(UA_Client *client, UA_AddNodesRequest *request) {
  471. UA_AddNodesResponse response;
  472. synchronousRequest(client, request, &UA_TYPES[UA_TYPES_ADDNODESREQUEST],
  473. &response, &UA_TYPES[UA_TYPES_ADDNODESRESPONSE]);
  474. return response;
  475. }
  476. UA_AddReferencesResponse UA_Client_addReferences(UA_Client *client, UA_AddReferencesRequest *request) {
  477. UA_AddReferencesResponse response;
  478. synchronousRequest(client, request, &UA_TYPES[UA_TYPES_ADDREFERENCESREQUEST],
  479. &response, &UA_TYPES[UA_TYPES_ADDREFERENCESRESPONSE]);
  480. return response;
  481. }
  482. UA_DeleteNodesResponse UA_Client_deleteNodes(UA_Client *client, UA_DeleteNodesRequest *request) {
  483. UA_DeleteNodesResponse response;
  484. synchronousRequest(client, request, &UA_TYPES[UA_TYPES_DELETENODESREQUEST],
  485. &response, &UA_TYPES[UA_TYPES_DELETENODESRESPONSE]);
  486. return response;
  487. }
  488. UA_DeleteReferencesResponse UA_Client_deleteReferences(UA_Client *client, UA_DeleteReferencesRequest *request) {
  489. UA_DeleteReferencesResponse response;
  490. synchronousRequest(client, request, &UA_TYPES[UA_TYPES_DELETEREFERENCESREQUEST],
  491. &response, &UA_TYPES[UA_TYPES_DELETEREFERENCESRESPONSE]);
  492. return response;
  493. }
  494. /**********************************/
  495. /* User-Facing Macros-Function */
  496. /**********************************/
  497. #ifdef ENABLE_METHODCALLS
  498. UA_CallResponse UA_Client_call(UA_Client *client, UA_CallRequest *request) {
  499. UA_CallResponse response;
  500. synchronousRequest(client, request, &UA_TYPES[UA_TYPES_CALLREQUEST],
  501. &response, &UA_TYPES[UA_TYPES_CALLRESPONSE]);
  502. return response;
  503. }
  504. UA_StatusCode UA_Client_CallServerMethod(UA_Client *client, UA_NodeId objectNodeId, UA_NodeId methodNodeId,
  505. UA_Int32 inputSize, const UA_Variant *input,
  506. UA_Int32 *outputSize, UA_Variant **output) {
  507. UA_CallRequest request;
  508. UA_CallRequest_init(&request);
  509. request.methodsToCallSize = 1;
  510. request.methodsToCall = UA_CallMethodRequest_new();
  511. if(!request.methodsToCall)
  512. return UA_STATUSCODE_BADOUTOFMEMORY;
  513. UA_CallMethodRequest *rq = &request.methodsToCall[0];
  514. UA_NodeId_copy(&methodNodeId, &rq->methodId);
  515. UA_NodeId_copy(&objectNodeId, &rq->objectId);
  516. rq->inputArguments = (void*)(uintptr_t)input; // cast const...
  517. rq->inputArgumentsSize = inputSize;
  518. UA_CallResponse response;
  519. response = UA_Client_call(client, &request);
  520. rq->inputArguments = UA_NULL;
  521. rq->inputArgumentsSize = -1;
  522. UA_CallRequest_deleteMembers(&request);
  523. UA_StatusCode retval = response.responseHeader.serviceResult;
  524. retval |= response.results[0].statusCode;
  525. if(retval == UA_STATUSCODE_GOOD) {
  526. *output = response.results[0].outputArguments;
  527. *outputSize = response.results[0].outputArgumentsSize;
  528. response.results[0].outputArguments = UA_NULL;
  529. response.results[0].outputArgumentsSize = -1;
  530. }
  531. UA_CallResponse_deleteMembers(&response);
  532. return retval;
  533. }
  534. #endif
  535. #define ADDNODES_COPYDEFAULTATTRIBUTES(REQUEST,ATTRIBUTES) do { \
  536. ATTRIBUTES.specifiedAttributes = 0; \
  537. if(! UA_LocalizedText_copy(&description, &(ATTRIBUTES.description))) \
  538. ATTRIBUTES.specifiedAttributes |= UA_NODEATTRIBUTESMASK_DESCRIPTION; \
  539. if(! UA_LocalizedText_copy(&displayName, &(ATTRIBUTES.displayName))) \
  540. ATTRIBUTES.specifiedAttributes |= UA_NODEATTRIBUTESMASK_DISPLAYNAME; \
  541. ATTRIBUTES.userWriteMask = userWriteMask; \
  542. ATTRIBUTES.specifiedAttributes |= UA_NODEATTRIBUTESMASK_USERWRITEMASK; \
  543. ATTRIBUTES.writeMask = writeMask; \
  544. ATTRIBUTES.specifiedAttributes |= UA_NODEATTRIBUTESMASK_WRITEMASK; \
  545. UA_QualifiedName_copy(&browseName, &(REQUEST.nodesToAdd[0].browseName)); \
  546. UA_ExpandedNodeId_copy(&parentNodeId, &(REQUEST.nodesToAdd[0].parentNodeId)); \
  547. UA_NodeId_copy(&referenceTypeId, &(REQUEST.nodesToAdd[0].referenceTypeId)); \
  548. UA_ExpandedNodeId_copy(&typeDefinition, &(REQUEST.nodesToAdd[0].typeDefinition)); \
  549. UA_ExpandedNodeId_copy(&reqId, &(REQUEST.nodesToAdd[0].requestedNewNodeId )); \
  550. REQUEST.nodesToAddSize = 1; \
  551. } while(0)
  552. #define ADDNODES_PACK_AND_SEND(PREQUEST,PATTRIBUTES,PNODETYPE) do { \
  553. PREQUEST.nodesToAdd[0].nodeAttributes.encoding = UA_EXTENSIONOBJECT_ENCODINGMASK_BODYISBYTESTRING; \
  554. PREQUEST.nodesToAdd[0].nodeAttributes.typeId = UA_NODEID_NUMERIC(0, UA_NS0ID_##PNODETYPE##ATTRIBUTES + UA_ENCODINGOFFSET_BINARY); \
  555. size_t encOffset = 0; \
  556. UA_ByteString_newMembers(&PREQUEST.nodesToAdd[0].nodeAttributes.body, client->connection.remoteConf.maxMessageSize); \
  557. UA_encodeBinary(&PATTRIBUTES,&UA_TYPES[UA_TYPES_##PNODETYPE##ATTRIBUTES], &(PREQUEST.nodesToAdd[0].nodeAttributes.body), &encOffset); \
  558. PREQUEST.nodesToAdd[0].nodeAttributes.body.length = encOffset; \
  559. *(adRes) = UA_Client_addNodes(client, &PREQUEST); \
  560. UA_AddNodesRequest_deleteMembers(&PREQUEST); \
  561. } while(0)
  562. /* NodeManagement */
  563. UA_AddNodesResponse *UA_Client_createObjectNode(UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName,
  564. UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
  565. UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition ) {
  566. UA_AddNodesRequest adReq;
  567. UA_AddNodesRequest_init(&adReq);
  568. UA_AddNodesResponse *adRes;
  569. adRes = UA_AddNodesResponse_new();
  570. UA_AddNodesResponse_init(adRes);
  571. UA_ObjectAttributes vAtt;
  572. UA_ObjectAttributes_init(&vAtt);
  573. adReq.nodesToAdd = (UA_AddNodesItem *) UA_AddNodesItem_new();
  574. UA_AddNodesItem_init(adReq.nodesToAdd);
  575. // Default node properties and attributes
  576. ADDNODES_COPYDEFAULTATTRIBUTES(adReq, vAtt);
  577. // Specific to objects
  578. adReq.nodesToAdd[0].nodeClass = UA_NODECLASS_OBJECT;
  579. vAtt.eventNotifier = 0;
  580. vAtt.specifiedAttributes |= UA_NODEATTRIBUTESMASK_EVENTNOTIFIER;
  581. ADDNODES_PACK_AND_SEND(adReq,vAtt,OBJECT);
  582. return adRes;
  583. }
  584. UA_AddNodesResponse *UA_Client_createVariableNode(UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName,
  585. UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
  586. UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition,
  587. UA_NodeId dataType, UA_Variant *value) {
  588. UA_AddNodesRequest adReq;
  589. UA_AddNodesRequest_init(&adReq);
  590. UA_AddNodesResponse *adRes;
  591. adRes = UA_AddNodesResponse_new();
  592. UA_AddNodesResponse_init(adRes);
  593. UA_VariableAttributes vAtt;
  594. UA_VariableAttributes_init(&vAtt);
  595. adReq.nodesToAdd = (UA_AddNodesItem *) UA_AddNodesItem_new();
  596. UA_AddNodesItem_init(adReq.nodesToAdd);
  597. // Default node properties and attributes
  598. ADDNODES_COPYDEFAULTATTRIBUTES(adReq, vAtt);
  599. // Specific to variables
  600. adReq.nodesToAdd[0].nodeClass = UA_NODECLASS_VARIABLE;
  601. vAtt.accessLevel = 0;
  602. vAtt.specifiedAttributes |= UA_NODEATTRIBUTESMASK_ACCESSLEVEL;
  603. vAtt.userAccessLevel = 0;
  604. vAtt.specifiedAttributes |= UA_NODEATTRIBUTESMASK_USERACCESSLEVEL;
  605. vAtt.minimumSamplingInterval = 100;
  606. vAtt.specifiedAttributes |= UA_NODEATTRIBUTESMASK_MINIMUMSAMPLINGINTERVAL;
  607. vAtt.historizing = UA_FALSE;
  608. vAtt.specifiedAttributes |= UA_NODEATTRIBUTESMASK_HISTORIZING;
  609. if (value != NULL) {
  610. UA_Variant_copy(value, &(vAtt.value));
  611. vAtt.specifiedAttributes |= UA_NODEATTRIBUTESMASK_VALUE;
  612. vAtt.valueRank = -2;
  613. vAtt.specifiedAttributes |= UA_NODEATTRIBUTESMASK_VALUERANK;
  614. // These are defined by the variant
  615. //vAtt.arrayDimensionsSize = value->arrayDimensionsSize;
  616. //vAtt.arrayDimensions = NULL;
  617. }
  618. UA_NodeId_copy(&dataType, &(vAtt.dataType));
  619. ADDNODES_PACK_AND_SEND(adReq,vAtt,VARIABLE);
  620. return adRes;
  621. }
  622. UA_AddNodesResponse *UA_Client_createReferenceTypeNode(UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName,
  623. UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
  624. UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition,
  625. UA_LocalizedText inverseName ) {
  626. UA_AddNodesRequest adReq;
  627. UA_AddNodesRequest_init(&adReq);
  628. UA_AddNodesResponse *adRes;
  629. adRes = UA_AddNodesResponse_new();
  630. UA_AddNodesResponse_init(adRes);
  631. UA_ReferenceTypeAttributes vAtt;
  632. UA_ReferenceTypeAttributes_init(&vAtt);
  633. adReq.nodesToAdd = (UA_AddNodesItem *) UA_AddNodesItem_new();
  634. UA_AddNodesItem_init(adReq.nodesToAdd);
  635. // Default node properties and attributes
  636. ADDNODES_COPYDEFAULTATTRIBUTES(adReq, vAtt);
  637. // Specific to referencetypes
  638. adReq.nodesToAdd[0].nodeClass = UA_NODECLASS_REFERENCETYPE;
  639. UA_LocalizedText_copy(&inverseName, &(vAtt.inverseName));
  640. vAtt.specifiedAttributes |= UA_NODEATTRIBUTESMASK_INVERSENAME;
  641. vAtt.symmetric = UA_FALSE;
  642. vAtt.specifiedAttributes |= UA_NODEATTRIBUTESMASK_SYMMETRIC;
  643. vAtt.isAbstract = UA_FALSE;
  644. vAtt.specifiedAttributes |= UA_NODEATTRIBUTESMASK_ISABSTRACT;
  645. ADDNODES_PACK_AND_SEND(adReq,vAtt,REFERENCETYPE);
  646. return adRes;
  647. }
  648. UA_AddNodesResponse *UA_Client_createObjectTypeNode(UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName,
  649. UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
  650. UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition) {
  651. UA_AddNodesRequest adReq;
  652. UA_AddNodesRequest_init(&adReq);
  653. UA_AddNodesResponse *adRes;
  654. adRes = UA_AddNodesResponse_new();
  655. UA_AddNodesResponse_init(adRes);
  656. UA_ObjectTypeAttributes vAtt;
  657. UA_ObjectTypeAttributes_init(&vAtt);
  658. adReq.nodesToAdd = (UA_AddNodesItem *) UA_AddNodesItem_new();
  659. UA_AddNodesItem_init(adReq.nodesToAdd);
  660. // Default node properties and attributes
  661. ADDNODES_COPYDEFAULTATTRIBUTES(adReq, vAtt);
  662. // Specific to referencetypes
  663. adReq.nodesToAdd[0].nodeClass = UA_NODECLASS_OBJECTTYPE;
  664. vAtt.isAbstract = UA_FALSE;
  665. vAtt.specifiedAttributes |= UA_NODEATTRIBUTESMASK_ISABSTRACT;
  666. ADDNODES_PACK_AND_SEND(adReq,vAtt,OBJECTTYPE);
  667. return adRes;
  668. }