|
@@ -0,0 +1,707 @@
|
|
|
+/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
+
|
|
|
+/**
|
|
|
+ * This code is used to generate a binary file for every request type
|
|
|
+ * which can be sent from a client to the server.
|
|
|
+ * These files form the basic corpus for fuzzing the server.
|
|
|
+ * This script is intended to be executed manually and then commit the new
|
|
|
+ * corpus to the repository.
|
|
|
+ */
|
|
|
+
|
|
|
+#ifndef UA_DEBUG_DUMP_PKGS_FILE
|
|
|
+#error UA_DEBUG_DUMP_PKGS_FILE must be defined
|
|
|
+#endif
|
|
|
+
|
|
|
+#include <stdio.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <pthread.h>
|
|
|
+#include <ua_types.h>
|
|
|
+#include <sys/stat.h>
|
|
|
+#include <server/ua_server_internal.h>
|
|
|
+#include <dirent.h>
|
|
|
+#include <src_generated/ua_types_generated.h>
|
|
|
+#include <fcntl.h>
|
|
|
+#include <unistd.h>
|
|
|
+#include <ua_client_highlevel.h>
|
|
|
+
|
|
|
+#include "ua_server.h"
|
|
|
+#include "ua_client.h"
|
|
|
+#include "ua_config_default.h"
|
|
|
+#include "ua_client_highlevel.h"
|
|
|
+#include "ua_network_tcp.h"
|
|
|
+#include "ua_transport_generated.h"
|
|
|
+#include "ua_transport_generated_encoding_binary.h"
|
|
|
+#include "ua_securechannel.h"
|
|
|
+#include "ua_types_generated_encoding_binary.h"
|
|
|
+#include "server/ua_services.h"
|
|
|
+
|
|
|
+unsigned int chunkCount = 0;
|
|
|
+
|
|
|
+char dumpOutputFile[255];
|
|
|
+
|
|
|
+char *messageTypes[] = {"ack", "hel", "msg", "opn", "clo", "err", "unk"};
|
|
|
+char *messageTypePrefix = NULL;
|
|
|
+char requestServiceName[200];
|
|
|
+
|
|
|
+void UA_debug_dumpCompleteChunk(UA_Server *const server, UA_Connection *const connection, UA_ByteString *messageBuffer);
|
|
|
+
|
|
|
+static void setMessageTypePrefix(UA_UInt32 messageType) {
|
|
|
+ switch(messageType & 0x00ffffff) {
|
|
|
+ case UA_MESSAGETYPE_ACK:
|
|
|
+ messageTypePrefix = messageTypes[0];
|
|
|
+ break;
|
|
|
+ case UA_MESSAGETYPE_HEL:
|
|
|
+ messageTypePrefix = messageTypes[1];
|
|
|
+ break;
|
|
|
+ case UA_MESSAGETYPE_MSG:
|
|
|
+ messageTypePrefix = messageTypes[2];
|
|
|
+ break;
|
|
|
+ case UA_MESSAGETYPE_OPN:
|
|
|
+ messageTypePrefix = messageTypes[3];
|
|
|
+ break;
|
|
|
+ case UA_MESSAGETYPE_CLO:
|
|
|
+ messageTypePrefix = messageTypes[4];
|
|
|
+ break;
|
|
|
+ case UA_MESSAGETYPE_ERR:
|
|
|
+ messageTypePrefix = messageTypes[5];
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ messageTypePrefix = messageTypes[6];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static UA_StatusCode setRequestedServiceName(const UA_ByteString *msg) {
|
|
|
+ /* At 0, the nodeid starts... */
|
|
|
+ size_t offset = 0;
|
|
|
+
|
|
|
+ /* Decode the nodeid */
|
|
|
+ UA_NodeId requestTypeId;
|
|
|
+ UA_StatusCode retval = UA_NodeId_decodeBinary(msg, &offset, &requestTypeId);
|
|
|
+ if(retval != UA_STATUSCODE_GOOD)
|
|
|
+ return retval;
|
|
|
+ if(requestTypeId.identifierType != UA_NODEIDTYPE_NUMERIC || requestTypeId.namespaceIndex != 0) {
|
|
|
+ snprintf(requestServiceName, 200, "invalid_request_id");
|
|
|
+ return UA_STATUSCODE_BADUNEXPECTEDERROR;
|
|
|
+ }
|
|
|
+
|
|
|
+ const UA_DataType *requestType = NULL;
|
|
|
+
|
|
|
+ for (size_t i=0; i<UA_TYPES_COUNT; i++) {
|
|
|
+ if (UA_TYPES[i].binaryEncodingId == requestTypeId.identifier.numeric) {
|
|
|
+ requestType = &UA_TYPES[i];
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (requestType == NULL) {
|
|
|
+ snprintf(requestServiceName, 200, "invalid_request_no_type");
|
|
|
+ return UA_STATUSCODE_BADUNEXPECTEDERROR;
|
|
|
+ }
|
|
|
+
|
|
|
+ snprintf(requestServiceName, 200, "_%s", requestType->typeName);
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|
|
|
+
|
|
|
+static UA_StatusCode
|
|
|
+processCompleteChunkWithoutChannel(UA_Server *server, UA_Connection *connection,
|
|
|
+ UA_ByteString *message) {
|
|
|
+ size_t offset = 0;
|
|
|
+ UA_TcpMessageHeader tcpMessageHeader;
|
|
|
+ UA_StatusCode retval =
|
|
|
+ UA_TcpMessageHeader_decodeBinary(message, &offset, &tcpMessageHeader);
|
|
|
+ if(retval != UA_STATUSCODE_GOOD)
|
|
|
+ return retval;
|
|
|
+
|
|
|
+ setMessageTypePrefix(tcpMessageHeader.messageTypeAndChunkType & 0x00ffffff);
|
|
|
+
|
|
|
+ if ((tcpMessageHeader.messageTypeAndChunkType & 0x00ffffff) == UA_MESSAGETYPE_MSG) {
|
|
|
+ // this should not happen in normal operation
|
|
|
+ UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER, "Got MSG package without channel.");
|
|
|
+ return UA_STATUSCODE_BADUNEXPECTEDERROR;
|
|
|
+ }
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|
|
|
+
|
|
|
+/* Takes decoded messages starting at the nodeid of the content type. */
|
|
|
+static UA_StatusCode
|
|
|
+processSecureChannelMessage(void *application, UA_SecureChannel *channel,
|
|
|
+ UA_MessageType messagetype, UA_UInt32 requestId,
|
|
|
+ const UA_ByteString *message) {
|
|
|
+ //UA_Server *server = (UA_Server*)application;
|
|
|
+ UA_StatusCode retval = UA_STATUSCODE_GOOD;
|
|
|
+ setMessageTypePrefix(messagetype);
|
|
|
+ if (messagetype == UA_MESSAGETYPE_MSG) {
|
|
|
+ setRequestedServiceName(message);
|
|
|
+ }
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void UA_debug_dumpCompleteChunk(UA_Server *const server, UA_Connection *const connection, UA_ByteString *messageBuffer) {
|
|
|
+
|
|
|
+ messageTypePrefix = NULL;
|
|
|
+ requestServiceName[0] = 0;
|
|
|
+
|
|
|
+ UA_StatusCode retval;
|
|
|
+ if(!connection->channel)
|
|
|
+ retval = processCompleteChunkWithoutChannel(server, connection, messageBuffer);
|
|
|
+ else {
|
|
|
+ // make a backup of the sequence number and reset it, because processChunk increases it
|
|
|
+ UA_UInt32 seqBackup = connection->channel->receiveSequenceNumber;
|
|
|
+ retval = UA_SecureChannel_processChunk(connection->channel, messageBuffer,
|
|
|
+ processSecureChannelMessage,
|
|
|
+ server);
|
|
|
+ connection->channel->receiveSequenceNumber = seqBackup;
|
|
|
+ }
|
|
|
+
|
|
|
+ snprintf(dumpOutputFile, 255, "%s/%05d_%s%s.bin", UA_CORPUS_OUTPUT_DIR, ++chunkCount, messageTypePrefix ? messageTypePrefix : "", requestServiceName);
|
|
|
+ UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER, "Dumping package %s", dumpOutputFile);
|
|
|
+
|
|
|
+ FILE *write_ptr;
|
|
|
+
|
|
|
+ write_ptr = fopen(dumpOutputFile, "ab");
|
|
|
+
|
|
|
+ fwrite(messageBuffer->data, messageBuffer->length, 1, write_ptr); // write 10 bytes from our buffer
|
|
|
+ fclose(write_ptr);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+UA_Server *server;
|
|
|
+UA_ServerConfig *config;
|
|
|
+UA_Boolean *running;
|
|
|
+pthread_t server_thread;
|
|
|
+
|
|
|
+static void * serverloop(void *_) {
|
|
|
+ while(*running)
|
|
|
+ UA_Server_run_iterate(server, true);
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static void start_server(void) {
|
|
|
+ running = UA_Boolean_new();
|
|
|
+ *running = true;
|
|
|
+ config = UA_ServerConfig_new_default();
|
|
|
+ server = UA_Server_new(config);
|
|
|
+ UA_Server_run_startup(server);
|
|
|
+ pthread_create(&server_thread, NULL, serverloop, NULL);
|
|
|
+}
|
|
|
+
|
|
|
+static void teardown_server(void) {
|
|
|
+ *running = false;
|
|
|
+ pthread_join(server_thread, NULL);
|
|
|
+ UA_Server_run_shutdown(server);
|
|
|
+ UA_Boolean_delete(running);
|
|
|
+ UA_Server_delete(server);
|
|
|
+ UA_ServerConfig_delete(config);
|
|
|
+}
|
|
|
+
|
|
|
+static void emptyCorpusDir() {
|
|
|
+ DIR *theFolder = opendir(UA_CORPUS_OUTPUT_DIR);
|
|
|
+ struct dirent *next_file;
|
|
|
+ char filepath[256];
|
|
|
+
|
|
|
+ while ( (next_file = readdir(theFolder)) != NULL )
|
|
|
+ {
|
|
|
+ // build the path for each file in the folder
|
|
|
+ sprintf(filepath, "%s/%s", UA_CORPUS_OUTPUT_DIR, next_file->d_name);
|
|
|
+ remove(filepath);
|
|
|
+ }
|
|
|
+ closedir(theFolder);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#define ASSERT_GOOD(X) if (X != UA_STATUSCODE_GOOD) return X;
|
|
|
+
|
|
|
+/*************************************************
|
|
|
+ * The following list of client requests is based
|
|
|
+ * on ua_server_binary.c:getServicePointers to
|
|
|
+ * cover all possible services and their inputs
|
|
|
+ ************************************************/
|
|
|
+
|
|
|
+static UA_StatusCode findServersRequest(UA_Client *client) {
|
|
|
+ UA_ApplicationDescription* applicationDescriptionArray = NULL;
|
|
|
+ size_t applicationDescriptionArraySize = 0;
|
|
|
+
|
|
|
+ size_t serverUrisSize = 1;
|
|
|
+ UA_String *serverUris = UA_String_new();
|
|
|
+ serverUris[0] = UA_String_fromChars("urn:some:server:uri");
|
|
|
+
|
|
|
+ size_t localeIdsSize = 1;
|
|
|
+ UA_String *localeIds = UA_String_new();
|
|
|
+ localeIds[0] = UA_String_fromChars("en");
|
|
|
+
|
|
|
+ ASSERT_GOOD(UA_Client_findServers(client, "opc.tcp://localhost:4840",
|
|
|
+ serverUrisSize, serverUris, localeIdsSize, localeIds,
|
|
|
+ &applicationDescriptionArraySize, &applicationDescriptionArray));
|
|
|
+
|
|
|
+ UA_Array_delete(serverUris, serverUrisSize, &UA_TYPES[UA_TYPES_STRING]);
|
|
|
+ UA_Array_delete(localeIds, localeIdsSize, &UA_TYPES[UA_TYPES_STRING]);
|
|
|
+ UA_Array_delete(applicationDescriptionArray, applicationDescriptionArraySize,
|
|
|
+ &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]);
|
|
|
+
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+static UA_StatusCode findServersOnNetworkRequest(UA_Client *client) {
|
|
|
+ UA_ServerOnNetwork* serverOnNetwork = NULL;
|
|
|
+ size_t serverOnNetworkSize = 0;
|
|
|
+
|
|
|
+
|
|
|
+ size_t serverCapabilityFilterSize = 2;
|
|
|
+ UA_String *serverCapabilityFilter = (UA_String*)UA_malloc(sizeof(UA_String) * serverCapabilityFilterSize);
|
|
|
+ serverCapabilityFilter[0] = UA_String_fromChars("LDS");
|
|
|
+ serverCapabilityFilter[1] = UA_String_fromChars("NA");
|
|
|
+
|
|
|
+
|
|
|
+ ASSERT_GOOD(UA_Client_findServersOnNetwork(client, "opc.tcp://localhost:4840", 0, 0,
|
|
|
+ serverCapabilityFilterSize, serverCapabilityFilter,
|
|
|
+ &serverOnNetworkSize, &serverOnNetwork));
|
|
|
+
|
|
|
+ UA_Array_delete(serverCapabilityFilter, serverCapabilityFilterSize,
|
|
|
+ &UA_TYPES[UA_TYPES_STRING]);
|
|
|
+ UA_Array_delete(serverOnNetwork, serverOnNetworkSize, &UA_TYPES[UA_TYPES_SERVERONNETWORK]);
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|
|
|
+
|
|
|
+static void initUaRegisterServer(UA_RegisteredServer *requestServer) {
|
|
|
+ requestServer->isOnline = UA_TRUE;
|
|
|
+ requestServer->serverUri = server->config.applicationDescription.applicationUri;
|
|
|
+ requestServer->productUri = server->config.applicationDescription.productUri;
|
|
|
+ requestServer->serverType = server->config.applicationDescription.applicationType;
|
|
|
+ requestServer->gatewayServerUri = server->config.applicationDescription.gatewayServerUri;
|
|
|
+
|
|
|
+ // create the semaphore
|
|
|
+ int fd = open("/tmp/open62541-corpus-semaphore", O_RDWR|O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO);
|
|
|
+ close(fd);
|
|
|
+ requestServer->semaphoreFilePath = UA_STRING_ALLOC("/tmp/open62541-corpus-semaphore");
|
|
|
+
|
|
|
+ requestServer->serverNames = &server->config.applicationDescription.applicationName;
|
|
|
+ requestServer->serverNamesSize = 1;
|
|
|
+
|
|
|
+ size_t nl_discurls = server->config.networkLayersSize;
|
|
|
+ requestServer->discoveryUrls = (UA_String*)UA_malloc(sizeof(UA_String) * nl_discurls);
|
|
|
+ requestServer->discoveryUrlsSize = nl_discurls;
|
|
|
+ for(size_t i = 0; i < nl_discurls; ++i) {
|
|
|
+ UA_ServerNetworkLayer *nl = &server->config.networkLayers[i];
|
|
|
+ requestServer->discoveryUrls[i] = nl->discoveryUrl;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+static UA_StatusCode registerServerRequest(UA_Client *client) {
|
|
|
+
|
|
|
+ /* Prepare the request. Do not cleanup the request after the service call,
|
|
|
+ * as the members are stack-allocated or point into the server config. */
|
|
|
+ UA_RegisterServerRequest request;
|
|
|
+ UA_RegisterServerRequest_init(&request);
|
|
|
+ /* Copy from RegisterServer2 request */
|
|
|
+ request.requestHeader.timestamp = UA_DateTime_now();
|
|
|
+ request.requestHeader.timeoutHint = 10000;
|
|
|
+
|
|
|
+ initUaRegisterServer(&request.server);
|
|
|
+
|
|
|
+
|
|
|
+ UA_RegisterServerResponse response;
|
|
|
+ UA_RegisterServerResponse_init(&response);
|
|
|
+
|
|
|
+ __UA_Client_Service(client, &request,
|
|
|
+ &UA_TYPES[UA_TYPES_REGISTERSERVERREQUEST],
|
|
|
+ &response,
|
|
|
+ &UA_TYPES[UA_TYPES_REGISTERSERVERRESPONSE]);
|
|
|
+
|
|
|
+ UA_free(request.server.discoveryUrls);
|
|
|
+ ASSERT_GOOD(response.responseHeader.serviceResult);
|
|
|
+
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|
|
|
+
|
|
|
+static UA_StatusCode registerServer2Request(UA_Client *client) {
|
|
|
+ /* Prepare the request. Do not cleanup the request after the service call,
|
|
|
+ * as the members are stack-allocated or point into the server config. */
|
|
|
+ UA_RegisterServer2Request request;
|
|
|
+ UA_RegisterServer2Request_init(&request);
|
|
|
+ request.requestHeader.timestamp = UA_DateTime_now();
|
|
|
+ request.requestHeader.timeoutHint = 10000;
|
|
|
+
|
|
|
+ initUaRegisterServer(&request.server);
|
|
|
+
|
|
|
+ UA_MdnsDiscoveryConfiguration mdnsConfig;
|
|
|
+ UA_MdnsDiscoveryConfiguration_init(&mdnsConfig);
|
|
|
+
|
|
|
+ request.discoveryConfigurationSize = 1;
|
|
|
+ request.discoveryConfiguration = UA_ExtensionObject_new();
|
|
|
+ UA_ExtensionObject_init(&request.discoveryConfiguration[0]);
|
|
|
+ request.discoveryConfiguration[0].encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
|
|
|
+ request.discoveryConfiguration[0].content.decoded.type = &UA_TYPES[UA_TYPES_MDNSDISCOVERYCONFIGURATION];
|
|
|
+ request.discoveryConfiguration[0].content.decoded.data = &mdnsConfig;
|
|
|
+
|
|
|
+ mdnsConfig.mdnsServerName = server->config.mdnsServerName;
|
|
|
+ mdnsConfig.serverCapabilities = server->config.serverCapabilities;
|
|
|
+ mdnsConfig.serverCapabilitiesSize = server->config.serverCapabilitiesSize;
|
|
|
+
|
|
|
+ // First try with RegisterServer2, if that isn't implemented, use RegisterServer
|
|
|
+ UA_RegisterServer2Response response;
|
|
|
+ UA_RegisterServer2Response_init(&response);
|
|
|
+ __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_REGISTERSERVER2REQUEST],
|
|
|
+ &response, &UA_TYPES[UA_TYPES_REGISTERSERVER2RESPONSE]);
|
|
|
+
|
|
|
+ ASSERT_GOOD(response.responseHeader.serviceResult);
|
|
|
+ UA_free(request.server.discoveryUrls);
|
|
|
+ UA_ExtensionObject_delete(request.discoveryConfiguration);
|
|
|
+
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|
|
|
+
|
|
|
+static UA_StatusCode readValueRequest(UA_Client *client) {
|
|
|
+ UA_ReadValueId rvi;
|
|
|
+ UA_ReadValueId_init(&rvi);
|
|
|
+ rvi.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STARTTIME);
|
|
|
+ rvi.attributeId = UA_ATTRIBUTEID_VALUE;
|
|
|
+
|
|
|
+ UA_DataValue resp = UA_Server_read(server, &rvi, UA_TIMESTAMPSTORETURN_BOTH);
|
|
|
+ ASSERT_GOOD(resp.status);
|
|
|
+
|
|
|
+ UA_DataValue_deleteMembers(&resp);
|
|
|
+
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static UA_StatusCode writeValueRequest(UA_Client *client) {
|
|
|
+ UA_WriteValue wValue;
|
|
|
+ UA_WriteValue_init(&wValue);
|
|
|
+ UA_LocalizedText testValue = UA_LOCALIZEDTEXT("en-EN", "MyServer");
|
|
|
+ UA_Variant_setScalar(&wValue.value.value, &testValue, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
|
|
|
+ wValue.value.hasValue = true;
|
|
|
+ wValue.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER);
|
|
|
+ wValue.attributeId = UA_ATTRIBUTEID_DISPLAYNAME;
|
|
|
+ ASSERT_GOOD(UA_Server_write(server, &wValue));
|
|
|
+
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|
|
|
+
|
|
|
+static UA_StatusCode browseAndBrowseNextRequest(UA_Client *client) {
|
|
|
+ // Browse node in server folder
|
|
|
+ UA_BrowseRequest bReq;
|
|
|
+ UA_BrowseRequest_init(&bReq);
|
|
|
+ // normally is set to 0, to get all the nodes, but we want to test browse next
|
|
|
+ bReq.requestedMaxReferencesPerNode = 1;
|
|
|
+ bReq.nodesToBrowse = UA_BrowseDescription_new();
|
|
|
+ bReq.nodesToBrowseSize = 1;
|
|
|
+ bReq.nodesToBrowse[0].nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER);
|
|
|
+ bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL;
|
|
|
+
|
|
|
+ UA_BrowseResponse bResp = UA_Client_Service_browse(client, bReq);
|
|
|
+ ASSERT_GOOD(bResp.responseHeader.serviceResult);
|
|
|
+
|
|
|
+ // browse next
|
|
|
+ UA_BrowseNextRequest bNextReq;
|
|
|
+ UA_BrowseNextRequest_init(&bNextReq);
|
|
|
+ // normally is set to 0, to get all the nodes, but we want to test browse next
|
|
|
+ bNextReq.releaseContinuationPoints = UA_FALSE;
|
|
|
+ bNextReq.continuationPoints = &bResp.results[0].continuationPoint;
|
|
|
+ bNextReq.continuationPointsSize = 1;
|
|
|
+
|
|
|
+ UA_BrowseNextResponse bNextResp = UA_Client_Service_browseNext(client, bNextReq);
|
|
|
+ ASSERT_GOOD(bNextResp.responseHeader.serviceResult);
|
|
|
+
|
|
|
+ UA_BrowseNextResponse_deleteMembers(&bNextResp);
|
|
|
+
|
|
|
+ bNextResp = UA_Client_Service_browseNext(client, bNextReq);
|
|
|
+ ASSERT_GOOD(bNextResp.responseHeader.serviceResult);
|
|
|
+
|
|
|
+ UA_BrowseNextResponse_deleteMembers(&bNextResp);
|
|
|
+
|
|
|
+ // release continuation point. Result is then empty
|
|
|
+ bNextReq.releaseContinuationPoints = UA_TRUE;
|
|
|
+ bNextResp = UA_Client_Service_browseNext(client, bNextReq);
|
|
|
+ UA_BrowseNextResponse_deleteMembers(&bNextResp);
|
|
|
+ ASSERT_GOOD(bNextResp.responseHeader.serviceResult);
|
|
|
+
|
|
|
+ UA_BrowseRequest_deleteMembers(&bReq);
|
|
|
+ UA_BrowseResponse_deleteMembers(&bResp);
|
|
|
+ // already deleted by browse request
|
|
|
+ bNextReq.continuationPoints = NULL;
|
|
|
+ bNextReq.continuationPointsSize = 0;
|
|
|
+ UA_BrowseNextRequest_deleteMembers(&bNextReq);
|
|
|
+
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|
|
|
+
|
|
|
+static UA_StatusCode registerUnregisterNodesRequest(UA_Client *client) {
|
|
|
+ UA_RegisterNodesRequest req;
|
|
|
+ UA_RegisterNodesRequest_init(&req);
|
|
|
+
|
|
|
+ req.nodesToRegister = UA_NodeId_new();
|
|
|
+ req.nodesToRegister[0] = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
|
|
|
+ req.nodesToRegisterSize = 1;
|
|
|
+
|
|
|
+ UA_RegisterNodesResponse res = UA_Client_Service_registerNodes(client, req);
|
|
|
+ ASSERT_GOOD(res.responseHeader.serviceResult);
|
|
|
+
|
|
|
+ UA_UnregisterNodesRequest reqUn;
|
|
|
+ UA_UnregisterNodesRequest_init(&reqUn);
|
|
|
+
|
|
|
+ reqUn.nodesToUnregister = UA_NodeId_new();
|
|
|
+ reqUn.nodesToUnregister[0] = res.registeredNodeIds[0];
|
|
|
+ reqUn.nodesToUnregisterSize = 1;
|
|
|
+
|
|
|
+ UA_UnregisterNodesResponse resUn = UA_Client_Service_unregisterNodes(client, reqUn);
|
|
|
+ ASSERT_GOOD(resUn.responseHeader.serviceResult);
|
|
|
+
|
|
|
+ UA_UnregisterNodesRequest_deleteMembers(&reqUn);
|
|
|
+ UA_UnregisterNodesResponse_deleteMembers(&resUn);
|
|
|
+ UA_RegisterNodesRequest_deleteMembers(&req);
|
|
|
+ UA_RegisterNodesResponse_deleteMembers(&res);
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|
|
|
+
|
|
|
+static UA_StatusCode translateBrowsePathsToNodeIdsRequest(UA_Client *client) {
|
|
|
+ // Just for testing we want to translate the following path to its corresponding node id
|
|
|
+ // /Objects/Server/ServerStatus/State
|
|
|
+ // Equals the following node IDs:
|
|
|
+ // /85/2253/2256/2259
|
|
|
+
|
|
|
+ #define BROWSE_PATHS_SIZE 3
|
|
|
+ char *paths[BROWSE_PATHS_SIZE] = {"Server", "ServerStatus", "State"};
|
|
|
+ UA_UInt32 ids[BROWSE_PATHS_SIZE] = {UA_NS0ID_ORGANIZES, UA_NS0ID_HASCOMPONENT, UA_NS0ID_HASCOMPONENT};
|
|
|
+ UA_BrowsePath browsePath;
|
|
|
+ UA_BrowsePath_init(&browsePath);
|
|
|
+ browsePath.startingNode = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
|
|
|
+ browsePath.relativePath.elements = (UA_RelativePathElement*)UA_Array_new(BROWSE_PATHS_SIZE, &UA_TYPES[UA_TYPES_RELATIVEPATHELEMENT]);
|
|
|
+ browsePath.relativePath.elementsSize = BROWSE_PATHS_SIZE;
|
|
|
+
|
|
|
+ for(size_t i = 0; i < BROWSE_PATHS_SIZE; i++) {
|
|
|
+ UA_RelativePathElement *elem = &browsePath.relativePath.elements[i];
|
|
|
+ elem->referenceTypeId = UA_NODEID_NUMERIC(0, ids[i]);
|
|
|
+ elem->targetName = UA_QUALIFIEDNAME_ALLOC(0, paths[i]);
|
|
|
+ }
|
|
|
+
|
|
|
+ UA_TranslateBrowsePathsToNodeIdsRequest request;
|
|
|
+ UA_TranslateBrowsePathsToNodeIdsRequest_init(&request);
|
|
|
+ request.browsePaths = &browsePath;
|
|
|
+ request.browsePathsSize = 1;
|
|
|
+
|
|
|
+ UA_TranslateBrowsePathsToNodeIdsResponse response = UA_Client_Service_translateBrowsePathsToNodeIds(client, request);
|
|
|
+ ASSERT_GOOD(response.responseHeader.serviceResult);
|
|
|
+
|
|
|
+ UA_BrowsePath_deleteMembers(&browsePath);
|
|
|
+ UA_TranslateBrowsePathsToNodeIdsResponse_deleteMembers(&response);
|
|
|
+
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static void monitoredItemHandler(UA_UInt32 monId, UA_DataValue *value, void *context) {
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+static UA_StatusCode subscriptionRequests(UA_Client *client) {
|
|
|
+ UA_UInt32 subId;
|
|
|
+ // createSubscriptionRequest
|
|
|
+ ASSERT_GOOD(UA_Client_Subscriptions_new(client, UA_SubscriptionSettings_default, &subId));
|
|
|
+
|
|
|
+
|
|
|
+ // modifySubscription
|
|
|
+ UA_ModifySubscriptionRequest modifySubscriptionRequest;
|
|
|
+ UA_ModifySubscriptionRequest_init(&modifySubscriptionRequest);
|
|
|
+ modifySubscriptionRequest.subscriptionId = subId;
|
|
|
+ modifySubscriptionRequest.maxNotificationsPerPublish = UA_SubscriptionSettings_default.maxNotificationsPerPublish;
|
|
|
+ modifySubscriptionRequest.priority = UA_SubscriptionSettings_default.priority;
|
|
|
+ modifySubscriptionRequest.requestedLifetimeCount = UA_SubscriptionSettings_default.requestedLifetimeCount;
|
|
|
+ modifySubscriptionRequest.requestedMaxKeepAliveCount = UA_SubscriptionSettings_default.requestedMaxKeepAliveCount;
|
|
|
+ modifySubscriptionRequest.requestedPublishingInterval = UA_SubscriptionSettings_default.requestedPublishingInterval;
|
|
|
+ UA_ModifySubscriptionResponse modifySubscriptionResponse;
|
|
|
+ __UA_Client_Service(client, &modifySubscriptionRequest, &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONREQUEST],
|
|
|
+ &modifySubscriptionResponse, &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONRESPONSE]);
|
|
|
+ ASSERT_GOOD(modifySubscriptionResponse.responseHeader.serviceResult);
|
|
|
+ UA_ModifySubscriptionRequest_deleteMembers(&modifySubscriptionRequest);
|
|
|
+ UA_ModifySubscriptionResponse_deleteMembers(&modifySubscriptionResponse);
|
|
|
+
|
|
|
+ // setPublishingMode
|
|
|
+ UA_SetPublishingModeRequest setPublishingModeRequest;
|
|
|
+ UA_SetPublishingModeRequest_init(&setPublishingModeRequest);
|
|
|
+ setPublishingModeRequest.subscriptionIdsSize = 1;
|
|
|
+ setPublishingModeRequest.subscriptionIds = UA_malloc(sizeof(UA_UInt32));
|
|
|
+ setPublishingModeRequest.subscriptionIds[0] = subId;
|
|
|
+ setPublishingModeRequest.publishingEnabled = UA_TRUE;
|
|
|
+ UA_SetPublishingModeResponse setPublishingModeResponse;
|
|
|
+ __UA_Client_Service(client, &setPublishingModeRequest, &UA_TYPES[UA_TYPES_SETPUBLISHINGMODEREQUEST],
|
|
|
+ &setPublishingModeResponse, &UA_TYPES[UA_TYPES_SETPUBLISHINGMODERESPONSE]);
|
|
|
+ ASSERT_GOOD(setPublishingModeResponse.responseHeader.serviceResult);
|
|
|
+ UA_SetPublishingModeRequest_deleteMembers(&setPublishingModeRequest);
|
|
|
+ UA_SetPublishingModeResponse_deleteMembers(&setPublishingModeResponse);
|
|
|
+
|
|
|
+
|
|
|
+ // createMonitoredItemsRequest
|
|
|
+ UA_UInt32 monId;
|
|
|
+ ASSERT_GOOD(UA_Client_Subscriptions_addMonitoredItem(client, subId, UA_NODEID_NUMERIC(0, 2259),
|
|
|
+ UA_ATTRIBUTEID_VALUE, monitoredItemHandler,
|
|
|
+ NULL, &monId));
|
|
|
+
|
|
|
+ // publishRequest
|
|
|
+ ASSERT_GOOD(UA_Client_Subscriptions_manuallySendPublishRequest(client));
|
|
|
+
|
|
|
+ // republishRequest
|
|
|
+ UA_RepublishRequest republishRequest;
|
|
|
+ UA_RepublishRequest_init(&republishRequest);
|
|
|
+ republishRequest.retransmitSequenceNumber = 0;
|
|
|
+ republishRequest.subscriptionId = subId;
|
|
|
+ UA_RepublishResponse republishResponse;
|
|
|
+ __UA_Client_Service(client, &republishRequest, &UA_TYPES[UA_TYPES_REPUBLISHREQUEST],
|
|
|
+ &republishResponse, &UA_TYPES[UA_TYPES_REPUBLISHRESPONSE]);
|
|
|
+ // here we don't care about the return value since it may be UA_STATUSCODE_BADMESSAGENOTAVAILABLE
|
|
|
+ // ASSERT_GOOD(republishResponse.responseHeader.serviceResult);
|
|
|
+ UA_RepublishRequest_deleteMembers(&republishRequest);
|
|
|
+ UA_RepublishResponse_deleteMembers(&republishResponse);
|
|
|
+
|
|
|
+ // modifyMonitoredItems
|
|
|
+ UA_ModifyMonitoredItemsRequest modifyMonitoredItemsRequest;
|
|
|
+ UA_ModifyMonitoredItemsRequest_init(&modifyMonitoredItemsRequest);
|
|
|
+ modifyMonitoredItemsRequest.subscriptionId = subId;
|
|
|
+ modifyMonitoredItemsRequest.itemsToModifySize = 1;
|
|
|
+ modifyMonitoredItemsRequest.itemsToModify = UA_malloc(sizeof(UA_MonitoredItemModifyRequest));
|
|
|
+ modifyMonitoredItemsRequest.itemsToModify[0].monitoredItemId = monId;
|
|
|
+ UA_MonitoringParameters_init(&modifyMonitoredItemsRequest.itemsToModify[0].requestedParameters);
|
|
|
+ UA_ModifyMonitoredItemsResponse modifyMonitoredItemsResponse;
|
|
|
+ __UA_Client_Service(client, &modifyMonitoredItemsRequest, &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSREQUEST],
|
|
|
+ &modifyMonitoredItemsResponse, &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSRESPONSE]);
|
|
|
+ ASSERT_GOOD(modifyMonitoredItemsResponse.responseHeader.serviceResult);
|
|
|
+ UA_ModifyMonitoredItemsRequest_deleteMembers(&modifyMonitoredItemsRequest);
|
|
|
+ UA_ModifyMonitoredItemsResponse_deleteMembers(&modifyMonitoredItemsResponse);
|
|
|
+
|
|
|
+ // setMonitoringMode
|
|
|
+ UA_SetMonitoringModeRequest setMonitoringModeRequest;
|
|
|
+ UA_SetMonitoringModeRequest_init(&setMonitoringModeRequest);
|
|
|
+ setMonitoringModeRequest.subscriptionId = subId;
|
|
|
+ setMonitoringModeRequest.monitoredItemIdsSize = 1;
|
|
|
+ setMonitoringModeRequest.monitoredItemIds = UA_malloc(sizeof(UA_UInt32));
|
|
|
+ setMonitoringModeRequest.monitoredItemIds[0] = monId;
|
|
|
+ setMonitoringModeRequest.monitoringMode = UA_MONITORINGMODE_REPORTING;
|
|
|
+ UA_SetMonitoringModeResponse setMonitoringModeResponse;
|
|
|
+ __UA_Client_Service(client, &setMonitoringModeRequest, &UA_TYPES[UA_TYPES_SETMONITORINGMODEREQUEST],
|
|
|
+ &setMonitoringModeResponse, &UA_TYPES[UA_TYPES_SETMONITORINGMODERESPONSE]);
|
|
|
+ ASSERT_GOOD(setMonitoringModeResponse.responseHeader.serviceResult);
|
|
|
+ UA_SetMonitoringModeRequest_deleteMembers(&setMonitoringModeRequest);
|
|
|
+ UA_SetMonitoringModeResponse_deleteMembers(&setMonitoringModeResponse);
|
|
|
+
|
|
|
+ // deleteMonitoredItemsRequest
|
|
|
+ ASSERT_GOOD(UA_Client_Subscriptions_removeMonitoredItem(client, subId, monId));
|
|
|
+
|
|
|
+
|
|
|
+ // deleteSubscriptionRequest
|
|
|
+ ASSERT_GOOD(UA_Client_Subscriptions_remove(client, subId));
|
|
|
+
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|
|
|
+
|
|
|
+static UA_StatusCode callRequest(UA_Client *client) {
|
|
|
+ /* Set up the request */
|
|
|
+ UA_CallRequest request;
|
|
|
+ UA_CallRequest_init(&request);
|
|
|
+ UA_CallMethodRequest item;
|
|
|
+ UA_CallMethodRequest_init(&item);
|
|
|
+ item.methodId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS);
|
|
|
+ item.objectId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER);
|
|
|
+
|
|
|
+
|
|
|
+ UA_Variant input;
|
|
|
+ UA_UInt32 subId = 12345;
|
|
|
+ UA_Variant_init(&input);
|
|
|
+ UA_Variant_setScalarCopy(&input, &subId, &UA_TYPES[UA_TYPES_UINT32]);
|
|
|
+ item.inputArgumentsSize = 1;
|
|
|
+ item.inputArguments = &input;
|
|
|
+
|
|
|
+ request.methodsToCall = &item;
|
|
|
+ request.methodsToCallSize = 1;
|
|
|
+
|
|
|
+ /* Call the service */
|
|
|
+ UA_CallResponse response = UA_Client_Service_call(client, request);
|
|
|
+ ASSERT_GOOD(response.responseHeader.serviceResult);
|
|
|
+
|
|
|
+ UA_CallResponse_deleteMembers(&response);
|
|
|
+
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|
|
|
+
|
|
|
+static UA_StatusCode nodemanagementRequests(UA_Client *client) {
|
|
|
+ UA_ObjectAttributes attr = UA_ObjectAttributes_default;
|
|
|
+ attr.description = UA_LOCALIZEDTEXT("en-US", "Some Coordinates");
|
|
|
+ attr.displayName = UA_LOCALIZEDTEXT("en-US", "Coordinates");
|
|
|
+
|
|
|
+ UA_NodeId newObjectId;
|
|
|
+
|
|
|
+ ASSERT_GOOD(UA_Client_addObjectNode(client, UA_NODEID_NULL,
|
|
|
+ UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
|
|
+ UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
|
|
|
+ UA_QUALIFIEDNAME(1, "Coordinates"),
|
|
|
+ UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), attr, &newObjectId));
|
|
|
+
|
|
|
+
|
|
|
+ UA_ExpandedNodeId target = UA_EXPANDEDNODEID_NULL;
|
|
|
+ target.nodeId = newObjectId;
|
|
|
+ ASSERT_GOOD(UA_Client_addReference(client, UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
|
|
|
+ UA_TRUE, UA_STRING_NULL, target, UA_NODECLASS_OBJECT));
|
|
|
+
|
|
|
+ ASSERT_GOOD(UA_Client_deleteReference(client, UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
|
|
|
+ UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
|
|
|
+ true, target, true));
|
|
|
+
|
|
|
+ ASSERT_GOOD(UA_Client_deleteNode(client, newObjectId, UA_TRUE));
|
|
|
+
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|
|
|
+
|
|
|
+static UA_StatusCode executeClientServices(UA_Client *client) {
|
|
|
+
|
|
|
+ ASSERT_GOOD(findServersRequest(client));
|
|
|
+ ASSERT_GOOD(findServersOnNetworkRequest(client));
|
|
|
+ ASSERT_GOOD(registerServerRequest(client));
|
|
|
+ ASSERT_GOOD(registerServer2Request(client));
|
|
|
+ ASSERT_GOOD(readValueRequest(client));
|
|
|
+ ASSERT_GOOD(writeValueRequest(client));
|
|
|
+ ASSERT_GOOD(browseAndBrowseNextRequest(client));
|
|
|
+ ASSERT_GOOD(registerUnregisterNodesRequest(client));
|
|
|
+ ASSERT_GOOD(translateBrowsePathsToNodeIdsRequest(client));
|
|
|
+ ASSERT_GOOD(subscriptionRequests(client));
|
|
|
+ ASSERT_GOOD(callRequest(client));
|
|
|
+ ASSERT_GOOD(nodemanagementRequests(client));
|
|
|
+
|
|
|
+
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|
|
|
+
|
|
|
+int main(void) {
|
|
|
+ emptyCorpusDir();
|
|
|
+ start_server();
|
|
|
+ UA_StatusCode retval;
|
|
|
+
|
|
|
+ UA_Client *client = UA_Client_new(UA_ClientConfig_default);
|
|
|
+ // this will also call getEndpointsRequest
|
|
|
+ retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
|
|
|
+ if (retval == UA_STATUSCODE_GOOD)
|
|
|
+ retval = executeClientServices(client);
|
|
|
+ UA_Client_disconnect(client);
|
|
|
+ UA_Client_delete(client);
|
|
|
+
|
|
|
+ if (retval == UA_STATUSCODE_GOOD) {
|
|
|
+ // now also connect with user/pass so that fuzzer also knows how to do that
|
|
|
+ client = UA_Client_new(UA_ClientConfig_default);
|
|
|
+ retval = UA_Client_connect_username(client, "opc.tcp://localhost:4840", "user", "password");
|
|
|
+ retval = retval == UA_STATUSCODE_BADUSERACCESSDENIED ? UA_STATUSCODE_GOOD : retval;
|
|
|
+ UA_Client_disconnect(client);
|
|
|
+ UA_Client_delete(client);
|
|
|
+ }
|
|
|
+
|
|
|
+ teardown_server();
|
|
|
+
|
|
|
+ if (retval != UA_STATUSCODE_GOOD) {
|
|
|
+ printf("\n--------- AN ERROR OCCURED ----------\nStatus = %s\n", UA_StatusCode_name(retval));
|
|
|
+ } else {
|
|
|
+ printf("\n--------- SUCCESS -------\nThe corpus is stored in %s\nYou manually need to copy them into the direcotry tests/fuss/fuzz_binary_message_corpus/generated", UA_CORPUS_OUTPUT_DIR);
|
|
|
+ }
|
|
|
+ return (int) retval;
|
|
|
+}
|