Browse Source

added session handling and reading to the client. (still dirty, valgrind-cleaning needed)

Julius Pfrommer 10 years ago
parent
commit
575b90c8db
3 changed files with 207 additions and 50 deletions
  1. 9 0
      examples/client_proper.c
  2. 5 5
      include/ua_client.h
  3. 193 45
      src/client/ua_client.c

+ 9 - 0
examples/client_proper.c

@@ -13,5 +13,14 @@ int main(int argc, char *argv[]) {
 	UA_ClientNetworkLayer nl = ClientNetworkLayerTCP_new(UA_ConnectionConfig_standard);
     UA_Client_connect(client, UA_ConnectionConfig_standard, nl, "opc.tcp://localhost:16664");
 
+    UA_ReadRequest read_req;
+    UA_ReadRequest_init(&read_req);
+    read_req.nodesToRead = UA_ReadValueId_new();
+    read_req.nodesToReadSize = 1;
+    read_req.nodesToRead[0].nodeId = UA_NODEID_STATIC(1, 73);
+    read_req.nodesToRead[0].attributeId = 13;
+    UA_ReadResponse read_resp = UA_Client_read(client, &read_req);
+    printf("answer statuscode: %i\n", read_resp.responseHeader.serviceResult);
+    printf("answer value: %i\n", *(UA_Int32*)read_resp.results[0].value.dataPtr);
     UA_Client_delete(client);
 }

+ 5 - 5
include/ua_client.h

@@ -31,14 +31,14 @@ typedef struct UA_Client UA_Client;
 UA_Client UA_EXPORT * UA_Client_new(void);
 void UA_Client_delete(UA_Client* client);
 
-UA_StatusCode UA_EXPORT UA_Client_connect(UA_Client *c, UA_ConnectionConfig conf,
+UA_StatusCode UA_EXPORT UA_Client_connect(UA_Client *client, UA_ConnectionConfig conf,
                                           UA_ClientNetworkLayer networkLayer, char *endpointUrl);
 
-UA_StatusCode UA_EXPORT UA_Client_disconnect(UA_Client *c);
+UA_StatusCode UA_EXPORT UA_Client_disconnect(UA_Client *client);
 
-UA_ReadResponse UA_EXPORT UA_Client_read(UA_Client *c, const UA_ReadRequest *request);
-UA_WriteResponse UA_EXPORT UA_Client_write(UA_Client *c, const UA_WriteRequest *request);
-UA_BrowseResponse UA_EXPORT UA_Client_browse(UA_Client *c, const UA_BrowseRequest *request);
+UA_ReadResponse UA_EXPORT UA_Client_read(UA_Client *client, const UA_ReadRequest *request);
+UA_WriteResponse UA_EXPORT UA_Client_write(UA_Client *client, const UA_WriteRequest *request);
+UA_BrowseResponse UA_EXPORT UA_Client_browse(UA_Client *client, const UA_BrowseRequest *request);
 
 #ifdef __cplusplus
 } // extern "C"

+ 193 - 45
src/client/ua_client.c

@@ -9,84 +9,114 @@ struct UA_Client {
     UA_String endpointUrl;
     UA_Connection connection;
 
+    UA_UInt32 sequenceNumber;
     UA_UInt32 requestId;
-    UA_UInt32 sequenceId;
 
-	UA_UInt32 channelId;
+    /* Secure Channel */
+    UA_ChannelSecurityToken securityToken;
+    UA_ByteString clientNonce;
+    UA_ByteString serverNonce;
 	/* UA_SequenceHeader sequenceHdr; */
 	/* UA_NodeId authenticationToken; */
-	/* UA_UInt32 tokenId; */
-    /* UA_Connection *connection; */
+
+    /* Session */
+    UA_NodeId sessionId;
+    UA_NodeId authenticationToken;
 };
 
 UA_Client * UA_Client_new(void) {
-    UA_Client *c = UA_malloc(sizeof(UA_Client));
-    if(!c)
+    UA_Client *client = UA_malloc(sizeof(UA_Client));
+    if(!client)
         return UA_NULL;
-    UA_String_init(&c->endpointUrl);
-    c->connection.state = UA_CONNECTION_OPENING;
-    c->requestId = 1;
-    c->sequenceId = 1;
-    return c;
+    UA_String_init(&client->endpointUrl);
+    client->connection.state = UA_CONNECTION_OPENING;
+
+    client->sequenceNumber = 0;
+    client->requestId = 0;
+
+    /* Secure Channel */
+    UA_ChannelSecurityToken_deleteMembers(&client->securityToken);
+    UA_ByteString_init(&client->clientNonce);
+    UA_ByteString_init(&client->serverNonce);
+    
+    return client;
 }
 
 void UA_Client_delete(UA_Client* client){
-	if(client){
-		client->networkLayer.delete(client->networkLayer.nlHandle);
-		UA_String_deleteMembers(&client->endpointUrl);
-		free(client);
-	}
+    client->networkLayer.delete(client->networkLayer.nlHandle);
+    UA_String_deleteMembers(&client->endpointUrl);
+    // client->connection
+
+    /* Secure Channel */
+    UA_ByteString_deleteMembers(&client->clientNonce);
+    UA_ByteString_deleteMembers(&client->serverNonce);
+
+    free(client);
 }
 
-static UA_StatusCode HelAckHandshake(UA_Client *c);
-static UA_StatusCode SecureChannelHandshake(UA_Client *c);
-static UA_StatusCode SessionHandshake(UA_Client *c);
-static UA_StatusCode CloseSession(UA_Client *c);
-static UA_StatusCode CloseSecureChannel(UA_Client *c);
+static UA_StatusCode HelAckHandshake(UA_Client *client);
+static UA_StatusCode SecureChannelHandshake(UA_Client *client);
+static UA_StatusCode SessionHandshake(UA_Client *client);
+static UA_StatusCode CloseSession(UA_Client *client);
+static UA_StatusCode CloseSecureChannel(UA_Client *client);
 
-UA_StatusCode UA_Client_connect(UA_Client *c, UA_ConnectionConfig conf, UA_ClientNetworkLayer networkLayer,
-                                char *endpointUrl) {
-    UA_StatusCode retval = UA_String_copycstring(endpointUrl, &c->endpointUrl);
+UA_StatusCode UA_Client_connect(UA_Client *client, UA_ConnectionConfig conf,
+                                UA_ClientNetworkLayer networkLayer, char *endpointUrl)
+{
+    UA_StatusCode retval = UA_String_copycstring(endpointUrl, &client->endpointUrl);
     if(retval != UA_STATUSCODE_GOOD)
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
-    c->networkLayer = networkLayer;
-    c->connection.localConf = conf;
-    retval = networkLayer.connect(c->endpointUrl, c->networkLayer.nlHandle);
+    client->networkLayer = networkLayer;
+    client->connection.localConf = conf;
+    retval = networkLayer.connect(client->endpointUrl, client->networkLayer.nlHandle);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
 
-    HelAckHandshake(c);
-    SecureChannelHandshake(c);
-    // session
-    
-    return UA_STATUSCODE_GOOD;
+    retval = HelAckHandshake(client);
+    if(retval == UA_STATUSCODE_GOOD)
+        retval = SecureChannelHandshake(client);
+    if(retval == UA_STATUSCODE_GOOD)
+        retval = SessionHandshake(client);
+    printf("Connection returned %i\n", retval);
+    return retval;
 }
 
-UA_StatusCode UA_EXPORT UA_Client_disconnect(UA_Client *c) {
+UA_StatusCode UA_EXPORT UA_Client_disconnect(UA_Client *client) {
     return UA_STATUSCODE_GOOD;
 }
 
-static UA_StatusCode SecureChannelHandshake(UA_Client *c) {
+#define SETREQUESTHEADER(HEADER, CLIENT) \
+    UA_NodeId_copy(&CLIENT->authenticationToken, &HEADER.authenticationToken); \
+    HEADER.timestamp = UA_DateTime_now();                               \
+	HEADER.timeoutHint = 10000;                                         \
+    /* extend this */
+
+static UA_StatusCode SecureChannelHandshake(UA_Client *client) {
+    UA_ByteString_deleteMembers(&client->clientNonce); // if the handshake is repeated
+	UA_ByteString_newMembers(&client->clientNonce, 1);
+	client->clientNonce.data[0] = 0;
+
 	UA_SecureConversationMessageHeader messageHeader;
 	messageHeader.messageHeader.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_OPNF;
 	messageHeader.secureChannelId = 0;
 
 	UA_SequenceHeader seqHeader;
-	seqHeader.requestId = c->requestId;
-	seqHeader.sequenceNumber = c->sequenceId;
+    seqHeader.sequenceNumber = ++client->sequenceNumber;
+    seqHeader.requestId = ++client->requestId;
 
 	UA_AsymmetricAlgorithmSecurityHeader asymHeader;
 	UA_AsymmetricAlgorithmSecurityHeader_init(&asymHeader);
 	UA_String_copycstring("http://opcfoundation.org/UA/SecurityPolicy#None", &asymHeader.securityPolicyUri);
 
-	UA_NodeId requestType = UA_NODEID_STATIC(0, UA_NS0ID_OPENSECURECHANNELREQUEST + UA_ENCODINGOFFSET_BINARY);  // id of opensecurechannelrequest
+    /* id of opensecurechannelrequest */
+	UA_NodeId requestType = UA_NODEID_STATIC(0, UA_NS0ID_OPENSECURECHANNELREQUEST +
+                                             UA_ENCODINGOFFSET_BINARY);
 
 	UA_OpenSecureChannelRequest opnSecRq;
 	UA_OpenSecureChannelRequest_init(&opnSecRq);
 	opnSecRq.requestHeader.timestamp = UA_DateTime_now();
-	UA_ByteString_newMembers(&opnSecRq.clientNonce, 1);
-	opnSecRq.clientNonce.data[0] = 0;
+    UA_ByteString_copy(&client->clientNonce, &opnSecRq.clientNonce);
 	opnSecRq.clientProtocolVersion = 0;
 	opnSecRq.requestedLifetime = 30000;
 	opnSecRq.securityMode = UA_MESSAGESECURITYMODE_NONE;
@@ -117,14 +147,14 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *c) {
     UA_OpenSecureChannelRequest_deleteMembers(&opnSecRq);
 
     UA_ByteStringArray buf = {.stringsSize = 1, .strings = &message};
-    UA_StatusCode retval = c->networkLayer.send(c->networkLayer.nlHandle, buf);
+    UA_StatusCode retval = client->networkLayer.send(client->networkLayer.nlHandle, buf);
     if(retval)
         return retval;
 
     // parse the response
     UA_ByteString reply;
-    UA_ByteString_newMembers(&reply, c->connection.localConf.recvBufferSize);
-    retval = c->networkLayer.awaitResponse(c->networkLayer.nlHandle, &reply, 1000);
+    UA_ByteString_newMembers(&reply, client->connection.localConf.recvBufferSize);
+    retval = client->networkLayer.awaitResponse(client->networkLayer.nlHandle, &reply, 1000);
     if(retval) {
         UA_ByteString_deleteMembers(&reply);
         return retval;
@@ -137,13 +167,23 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *c) {
 	UA_SequenceHeader_decodeBinary(&reply, &offset, &seqHeader);
 	UA_NodeId_decodeBinary(&reply, &offset, &requestType);
 
-	c->channelId = messageHeader.secureChannelId;
-
+	if(!UA_NodeId_equal(&requestType, &UA_NODEID_STATIC(0, UA_NS0ID_OPENSECURECHANNELRESPONSE +
+                                                        UA_ENCODINGOFFSET_BINARY))) {
+        UA_ByteString_deleteMembers(&reply);
+        UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
 
-	//TODO: save other stuff
 	UA_OpenSecureChannelResponse response;
 	UA_OpenSecureChannelResponse_decodeBinary(&reply, &offset, &response);
     UA_ByteString_deleteMembers(&reply);
+    retval = response.responseHeader.serviceResult;
+
+    if(retval == UA_STATUSCODE_GOOD) {
+        UA_ChannelSecurityToken_copy(&response.securityToken, &client->securityToken);
+        UA_ByteString_deleteMembers(&client->serverNonce); // if the handshake is repeated
+        UA_ByteString_copy(&response.serverNonce, &client->serverNonce);
+    }
 
 	UA_OpenSecureChannelResponse_deleteMembers(&response);
 	UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
@@ -207,3 +247,111 @@ static UA_StatusCode HelAckHandshake(UA_Client *c) {
 	return UA_STATUSCODE_GOOD;
 }
 
+/** If the request fails, then the response is cast to UA_ResponseHeader (at the beginning of every
+    response) and filled with the appropriate error code */
+static void synchronousRequest(const void *request, const UA_DataType *requestType,
+                               void *response, const UA_DataType *responseType, UA_Client *client)
+{
+    UA_ResponseHeader *respHeader = (UA_ResponseHeader*)response;
+
+    UA_SecureConversationMessageHeader msgHeader;
+    msgHeader.messageHeader.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_MSGF;
+    msgHeader.secureChannelId = client->securityToken.channelId;
+
+    UA_SymmetricAlgorithmSecurityHeader symHeader;
+    symHeader.tokenId = client->securityToken.tokenId;
+    
+	UA_SequenceHeader seqHeader;
+    seqHeader.sequenceNumber = ++client->sequenceNumber;
+    seqHeader.requestId = ++client->requestId;
+
+	UA_NodeId requestId = UA_NODEID_STATIC(0, requestType->typeId.identifier.numeric +
+                                             UA_ENCODINGOFFSET_BINARY);
+
+	msgHeader.messageHeader.messageSize =
+        UA_SecureConversationMessageHeader_calcSizeBinary(&msgHeader) +
+        UA_SymmetricAlgorithmSecurityHeader_calcSizeBinary(&symHeader) +
+        UA_SequenceHeader_calcSizeBinary(&seqHeader) +
+        UA_NodeId_calcSizeBinary(&requestId) +
+        UA_calcSizeBinary(request, requestType);
+
+	UA_ByteString message;
+    UA_StatusCode retval = UA_ByteString_newMembers(&message, msgHeader.messageHeader.messageSize);
+    if(retval != UA_STATUSCODE_GOOD) {
+
+    }
+
+    size_t offset = 0;
+    retval |= UA_SecureConversationMessageHeader_encodeBinary(&msgHeader, &message, &offset);
+    retval |= UA_SymmetricAlgorithmSecurityHeader_encodeBinary(&symHeader, &message, &offset);
+    retval |= UA_SequenceHeader_encodeBinary(&seqHeader, &message, &offset);
+    retval |= UA_NodeId_encodeBinary(&requestId, &message, &offset);
+    retval |= UA_encodeBinary(request, requestType, &message, &offset);
+
+    UA_ByteStringArray buf = {.stringsSize = 1, .strings = &message};
+    retval = client->networkLayer.send(client->networkLayer.nlHandle, buf);
+    if(retval != UA_STATUSCODE_GOOD) {
+        respHeader->serviceResult = retval;
+        return;
+    }
+
+    /* Response */
+    UA_ByteString reply;
+    UA_ByteString_newMembers(&reply, client->connection.localConf.recvBufferSize);
+    retval = client->networkLayer.awaitResponse(client->networkLayer.nlHandle, &reply, 1000);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ByteString_deleteMembers(&reply);
+        respHeader->serviceResult = retval;
+        return;
+    }
+
+	offset = 0;
+	retval |= UA_SecureConversationMessageHeader_decodeBinary(&reply, &offset, &msgHeader);
+	retval |= UA_SymmetricAlgorithmSecurityHeader_decodeBinary(&reply, &offset, &symHeader);
+	retval |= UA_SequenceHeader_decodeBinary(&reply, &offset, &seqHeader);
+    UA_NodeId responseId;
+	retval |= UA_NodeId_decodeBinary(&reply, &offset, &responseId);
+
+	if(!UA_NodeId_equal(&responseId, &UA_NODEID_STATIC(0, responseType->typeId.identifier.numeric +
+                                                       UA_ENCODINGOFFSET_BINARY))) {
+        UA_ByteString_deleteMembers(&reply);
+        UA_SymmetricAlgorithmSecurityHeader_deleteMembers(&symHeader);
+        respHeader->serviceResult = retval;
+        return;
+    }
+
+	retval = UA_decodeBinary(&reply, &offset, response, responseType);
+    UA_ByteString_deleteMembers(&reply);
+    if(retval != UA_STATUSCODE_GOOD)
+        respHeader->serviceResult = retval;
+}
+
+static UA_StatusCode SessionHandshake(UA_Client *client) {
+
+    UA_CreateSessionRequest request;
+    UA_CreateSessionRequest_init(&request);
+
+    SETREQUESTHEADER(request.requestHeader, client);
+	/* UA_String_copy(endpointUrl, &rq.endpointUrl); */
+	/* UA_String_copycstring("mysession", &rq.sessionName); */
+	/* UA_String_copycstring("abcd", &rq.clientCertificate); */
+
+    UA_ByteString_copy(&client->clientNonce, &request.clientNonce);
+	request.requestedSessionTimeout = 1200000;
+	request.maxResponseMessageSize = UA_INT32_MAX;
+
+    UA_CreateSessionResponse response;
+    synchronousRequest(&request, &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST],
+                       &response, &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE],
+                       client);
+
+    UA_CreateSessionResponse_deleteMembers(&response);
+    return response.responseHeader.serviceResult; // not deleted
+}
+
+UA_ReadResponse UA_Client_read(UA_Client *client, const UA_ReadRequest *request) {
+    UA_ReadResponse response;
+    synchronousRequest(request, &UA_TYPES[UA_TYPES_READREQUEST], &response,
+                       &UA_TYPES[UA_TYPES_READRESPONSE], client);
+    return response;
+}