forked from open62541/open62541
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ci_server.c
200 lines (172 loc) · 8.14 KB
/
ci_server.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
/* 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 <open62541/server_config_default.h>
#include <open62541/plugin/accesscontrol_default.h>
#include <open62541/client_highlevel.h>
#include <open62541/plugin/log_stdout.h>
#include <open62541/plugin/securitypolicy.h>
#include <open62541/server.h>
#include <open62541/server_config_default.h>
#include <stdlib.h>
#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_NS0ID(OBJECTSFOLDER), 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_NS0ID(OBJECTSFOLDER);
UA_NodeId parentReferenceNodeId = UA_NS0ID(ORGANIZES);
UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
parentReferenceNodeId, myIntegerName,
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 "
"<port> <server-certificate.der> <private-key.der> "
"[<trustlist1.crl>, ...]");
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;
/* Revocation lists are supported, but not used for the example here */
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;
}