/* This work is licensed under a Creative Commons CCZero 1.0 Universal License. * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */ #include #include #include #include #include #include #include #include #include "common.h" /* This is a test server to the ci script. It can be used for some of the examples that need a server to connect. * It allows to connect with the username "peter" and "paula" and the password "peter123" and "paula123" or "user1" and "password". Anonymus login is also allowed. * The server has a method "hello world" and a variable "the answer" that can be written to. * The server certificate and private key are loaded from the command line arguments. */ #define MIN_ARGS 4 static UA_UsernamePasswordLogin logins[3] = { {UA_STRING_STATIC("peter"), UA_STRING_STATIC("peter123")}, {UA_STRING_STATIC("paula"), UA_STRING_STATIC("paula123")}, {UA_STRING_STATIC("user1"), UA_STRING_STATIC("password")} }; static UA_StatusCode helloWorldMethodCallback(UA_Server *server, const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId, void *objectContext, size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output) { UA_String *inputStr = (UA_String*)input->data; UA_String tmp = UA_STRING_ALLOC("Hello "); if(inputStr->length > 0) { tmp.data = (UA_Byte *)UA_realloc(tmp.data, tmp.length + inputStr->length); memcpy(&tmp.data[tmp.length], inputStr->data, inputStr->length); tmp.length += inputStr->length; } UA_Variant_setScalarCopy(output, &tmp, &UA_TYPES[UA_TYPES_STRING]); UA_String_clear(&tmp); UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Hello World was called"); return UA_STATUSCODE_GOOD; } static void addHelloWorldMethod(UA_Server *server) { UA_Argument inputArgument; UA_Argument_init(&inputArgument); inputArgument.description = UA_LOCALIZEDTEXT("en-US", "A String"); inputArgument.name = UA_STRING("MyInput"); inputArgument.dataType = UA_TYPES[UA_TYPES_STRING].typeId; inputArgument.valueRank = UA_VALUERANK_SCALAR; UA_Argument outputArgument; UA_Argument_init(&outputArgument); outputArgument.description = UA_LOCALIZEDTEXT("en-US", "A String"); outputArgument.name = UA_STRING("MyOutput"); outputArgument.dataType = UA_TYPES[UA_TYPES_STRING].typeId; outputArgument.valueRank = UA_VALUERANK_SCALAR; UA_MethodAttributes helloAttr = UA_MethodAttributes_default; helloAttr.description = UA_LOCALIZEDTEXT("en-US","Say `Hello World`"); helloAttr.displayName = UA_LOCALIZEDTEXT("en-US","Hello World"); helloAttr.executable = true; helloAttr.userExecutable = true; UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1,62541), UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(1, "hello world"), helloAttr, &helloWorldMethodCallback, 1, &inputArgument, 1, &outputArgument, NULL, NULL); } static void addVariable(UA_Server *server) { /* Define the attribute of the myInteger variable node */ UA_VariableAttributes attr = UA_VariableAttributes_default; UA_Int32 myInteger = 42; UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]); attr.description = UA_LOCALIZEDTEXT("en-US","the answer"); attr.displayName = UA_LOCALIZEDTEXT("en-US","the answer"); attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId; attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE; /* Add the variable node to the information model */ UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer"); UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer"); UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER); UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES); UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId, parentReferenceNodeId, myIntegerName, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr, NULL, NULL); } static void writeVariable(UA_Server *server) { UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer"); /* Write a different integer value */ UA_Int32 myInteger = 43; UA_Variant myVar; UA_Variant_init(&myVar); UA_Variant_setScalar(&myVar, &myInteger, &UA_TYPES[UA_TYPES_INT32]); UA_Server_writeValue(server, myIntegerNodeId, myVar); /* Set the status code of the value to an error code. The function * UA_Server_write provides access to the raw service. The above * UA_Server_writeValue is syntactic sugar for writing a specific node * attribute with the write service. */ UA_WriteValue wv; UA_WriteValue_init(&wv); wv.nodeId = myIntegerNodeId; wv.attributeId = UA_ATTRIBUTEID_VALUE; wv.value.status = UA_STATUSCODE_BADNOTCONNECTED; wv.value.hasStatus = true; UA_Server_write(server, &wv); /* Reset the variable to a good statuscode with a value */ wv.value.hasStatus = false; wv.value.value = myVar; wv.value.hasValue = true; UA_Server_write(server, &wv); } int main(int argc, char* argv[]) { UA_StatusCode retval = 0; UA_Server *server = UA_Server_new(); UA_ServerConfig *config = UA_Server_getConfig(server); #ifdef UA_ENABLE_ENCRYPTION UA_ByteString certificate = UA_BYTESTRING_NULL; UA_ByteString privateKey = UA_BYTESTRING_NULL; UA_UInt16 port = 0; if(argc >= 4) { /* Load port, certificate and private key */ port = (UA_UInt16) atoi(argv[1]); certificate = loadFile(argv[2]); privateKey = loadFile(argv[3]); // print the certificat and private key printf("certificate: %.*s\n", (int)certificate.length, certificate.data); } else { UA_LOG_FATAL(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Missing arguments. Arguments are " " " "[, ...]"); return EXIT_SUCCESS; } /* Load the trustlist */ size_t trustListSize = 0; if(argc > 4) trustListSize = (size_t)argc-4; UA_STACKARRAY(UA_ByteString, trustList, trustListSize+1); for(size_t i = 0; i < trustListSize; i++) trustList[i] = loadFile(argv[i+4]); /* Loading of an issuer list, not used in this application */ size_t issuerListSize = 0; UA_ByteString *issuerList = NULL; /* Loading of a revocation list currently unsupported */ UA_ByteString *revocationList = NULL; size_t revocationListSize = 0; retval = UA_ServerConfig_setDefaultWithSecurityPolicies(config, port, &certificate, &privateKey, trustList, trustListSize, issuerList, issuerListSize, revocationList, revocationListSize); UA_ByteString_clear(&certificate); UA_ByteString_clear(&privateKey); for(size_t i = 0; i < trustListSize; i++) UA_ByteString_clear(&trustList[i]); if(retval != UA_STATUSCODE_GOOD) goto cleanup; #endif retval = UA_AccessControl_default(config, true, &config->securityPolicies[config->securityPoliciesSize-1].policyUri, 3, logins); if(retval != UA_STATUSCODE_GOOD) goto cleanup; addHelloWorldMethod(server); addVariable(server); writeVariable(server); retval = UA_Server_runUntilInterrupt(server); if(retval != UA_STATUSCODE_GOOD) goto cleanup; cleanup: UA_Server_delete(server); return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE; }