/src/open62541_15/src/server/ua_server.c
Line | Count | Source |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
4 | | * |
5 | | * Copyright 2014-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) |
6 | | * Copyright 2014-2017 (c) Florian Palm |
7 | | * Copyright 2015-2016 (c) Sten Grüner |
8 | | * Copyright 2015-2016 (c) Chris Iatrou |
9 | | * Copyright 2015 (c) LEvertz |
10 | | * Copyright 2015-2016 (c) Oleksiy Vasylyev |
11 | | * Copyright 2016 (c) Julian Grothoff |
12 | | * Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH |
13 | | * Copyright 2016 (c) Lorenz Haas |
14 | | * Copyright 2017 (c) frax2222 |
15 | | * Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB |
16 | | * Copyright 2018 (c) Hilscher Gesellschaft für Systemautomation mbH (Author: Martin Lang) |
17 | | * Copyright 2019 (c) Kalycito Infotech Private Limited |
18 | | * Copyright 2021 (c) Fraunhofer IOSB (Author: Jan Hermes) |
19 | | * Copyright 2022-2025 (c) Fraunhofer IOSB (Author: Andreas Ebner) |
20 | | * Copyright 2024 (c) Fraunhofer IOSB (Author: Noel Graf) |
21 | | */ |
22 | | |
23 | | #include "ua_server_internal.h" |
24 | | |
25 | | #ifdef UA_ENABLE_SUBSCRIPTIONS |
26 | | #include "ua_subscription.h" |
27 | | #endif |
28 | | |
29 | | #ifdef UA_ENABLE_NODESET_INJECTOR |
30 | | #include "open62541/nodesetinjector.h" |
31 | | #endif |
32 | | |
33 | | #ifdef UA_ENABLE_ENCRYPTION |
34 | | #include "open62541/plugin/certificategroup_default.h" |
35 | | #endif |
36 | | |
37 | 6.46k | #define STARTCHANNELID 1 |
38 | 6.46k | #define STARTTOKENID 1 |
39 | | |
40 | | /**********************/ |
41 | | /* Namespace Handling */ |
42 | | /**********************/ |
43 | | |
44 | | /* The NS1 Uri can be changed by the user to some custom string. This method is |
45 | | * called to initialize the NS1 Uri if it is not set before to the default |
46 | | * Application URI. |
47 | | * |
48 | | * This is done as soon as the Namespace Array is read or written via node value |
49 | | * read / write services, or UA_Server_addNamespace, or UA_Server_getNamespaceByIndex |
50 | | * UA_Server_getNamespaceByName or UA_Server_run_startup is called. |
51 | | * |
52 | | * Therefore one has to set the custom NS1 URI before one of the previously |
53 | | * mentioned steps. */ |
54 | | |
55 | | void |
56 | 19.9k | setupNs1Uri(UA_Server *server) { |
57 | 19.9k | if(!server->namespaces[1].data) { |
58 | 19.9k | UA_String_copy(&server->config.applicationDescription.applicationUri, |
59 | 19.9k | &server->namespaces[1]); |
60 | 19.9k | } |
61 | 19.9k | } |
62 | | |
63 | 19.4k | UA_UInt16 addNamespace(UA_Server *server, const UA_String name) { |
64 | | /* ensure that the uri for ns1 is set up from the app description */ |
65 | 19.4k | setupNs1Uri(server); |
66 | | |
67 | | /* Check if the namespace already exists in the server's namespace array */ |
68 | 19.4k | for(size_t i = 0; i < server->namespacesSize; ++i) { |
69 | 19.4k | if(UA_String_equal(&name, &server->namespaces[i])) |
70 | 19.4k | return (UA_UInt16) i; |
71 | 19.4k | } |
72 | | |
73 | | /* Make the array bigger */ |
74 | 0 | UA_String *newNS = (UA_String*)UA_realloc(server->namespaces, |
75 | 0 | sizeof(UA_String) * (server->namespacesSize + 1)); |
76 | 0 | UA_CHECK_MEM(newNS, return 0); |
77 | | |
78 | 0 | server->namespaces = newNS; |
79 | | |
80 | | /* Copy the namespace string */ |
81 | 0 | UA_StatusCode retval = UA_String_copy(&name, &server->namespaces[server->namespacesSize]); |
82 | 0 | UA_CHECK_STATUS(retval, return 0); |
83 | | |
84 | | /* Announce the change (otherwise, the array appears unchanged) */ |
85 | 0 | ++server->namespacesSize; |
86 | 0 | return (UA_UInt16)(server->namespacesSize - 1); |
87 | 0 | } |
88 | | |
89 | 19.4k | UA_UInt16 UA_Server_addNamespace(UA_Server *server, const char* name) { |
90 | | /* Override const attribute to get string (dirty hack) */ |
91 | 19.4k | UA_String nameString; |
92 | 19.4k | nameString.length = strlen(name); |
93 | 19.4k | nameString.data = (UA_Byte*)(uintptr_t)name; |
94 | 19.4k | lockServer(server); |
95 | 19.4k | UA_UInt16 retVal = addNamespace(server, nameString); |
96 | 19.4k | unlockServer(server); |
97 | 19.4k | return retVal; |
98 | 19.4k | } |
99 | | |
100 | | UA_ServerConfig* |
101 | 1.36M | UA_Server_getConfig(UA_Server *server) { |
102 | 1.36M | UA_CHECK_MEM(server, return NULL); |
103 | 1.36M | return &server->config; |
104 | 1.36M | } |
105 | | |
106 | | UA_StatusCode |
107 | | getNamespaceByName(UA_Server *server, const UA_String namespaceUri, |
108 | 0 | size_t *foundIndex) { |
109 | | /* ensure that the uri for ns1 is set up from the app description */ |
110 | 0 | setupNs1Uri(server); |
111 | 0 | UA_StatusCode res = UA_STATUSCODE_BADNOTFOUND; |
112 | 0 | for(size_t idx = 0; idx < server->namespacesSize; idx++) { |
113 | 0 | if(UA_String_equal(&server->namespaces[idx], &namespaceUri)) { |
114 | 0 | (*foundIndex) = idx; |
115 | 0 | res = UA_STATUSCODE_GOOD; |
116 | 0 | break; |
117 | 0 | } |
118 | 0 | } |
119 | 0 | return res; |
120 | 0 | } |
121 | | |
122 | | UA_StatusCode |
123 | | getNamespaceByIndex(UA_Server *server, const size_t namespaceIndex, |
124 | 0 | UA_String *foundUri) { |
125 | | /* ensure that the uri for ns1 is set up from the app description */ |
126 | 0 | setupNs1Uri(server); |
127 | 0 | UA_StatusCode res = UA_STATUSCODE_BADNOTFOUND; |
128 | 0 | if(namespaceIndex >= server->namespacesSize) |
129 | 0 | return res; |
130 | 0 | res = UA_String_copy(&server->namespaces[namespaceIndex], foundUri); |
131 | 0 | return res; |
132 | 0 | } |
133 | | |
134 | | UA_StatusCode |
135 | | UA_Server_getNamespaceByName(UA_Server *server, const UA_String namespaceUri, |
136 | 0 | size_t *foundIndex) { |
137 | 0 | lockServer(server); |
138 | 0 | UA_StatusCode res = getNamespaceByName(server, namespaceUri, foundIndex); |
139 | 0 | unlockServer(server); |
140 | 0 | return res; |
141 | 0 | } |
142 | | |
143 | | UA_StatusCode |
144 | | UA_Server_getNamespaceByIndex(UA_Server *server, const size_t namespaceIndex, |
145 | 0 | UA_String *foundUri) { |
146 | 0 | lockServer(server); |
147 | 0 | UA_StatusCode res = getNamespaceByIndex(server, namespaceIndex, foundUri); |
148 | 0 | unlockServer(server); |
149 | 0 | return res; |
150 | 0 | } |
151 | | |
152 | | UA_StatusCode |
153 | | UA_Server_forEachChildNodeCall(UA_Server *server, UA_NodeId parentNodeId, |
154 | 0 | UA_NodeIteratorCallback callback, void *handle) { |
155 | 0 | UA_BrowseDescription bd; |
156 | 0 | UA_BrowseDescription_init(&bd); |
157 | 0 | bd.nodeId = parentNodeId; |
158 | 0 | bd.browseDirection = UA_BROWSEDIRECTION_BOTH; |
159 | 0 | bd.resultMask = UA_BROWSERESULTMASK_REFERENCETYPEID | UA_BROWSERESULTMASK_ISFORWARD; |
160 | |
|
161 | 0 | UA_BrowseResult br = UA_Server_browse(server, 0, &bd); |
162 | 0 | UA_StatusCode res = br.statusCode; |
163 | 0 | UA_CHECK_STATUS(res, goto cleanup); |
164 | | |
165 | 0 | for(size_t i = 0; i < br.referencesSize; i++) { |
166 | 0 | if(!UA_ExpandedNodeId_isLocal(&br.references[i].nodeId)) |
167 | 0 | continue; |
168 | 0 | res = callback(br.references[i].nodeId.nodeId, !br.references[i].isForward, |
169 | 0 | br.references[i].referenceTypeId, handle); |
170 | 0 | UA_CHECK_STATUS(res, goto cleanup); |
171 | 0 | } |
172 | 0 | cleanup: |
173 | 0 | UA_BrowseResult_clear(&br); |
174 | 0 | return res; |
175 | 0 | } |
176 | | |
177 | | /********************/ |
178 | | /* GDS Transaction */ |
179 | | /********************/ |
180 | | |
181 | | UA_StatusCode |
182 | 0 | UA_GDSTransaction_init(UA_GDSTransaction *transaction, UA_Server *server, const UA_NodeId sessionId) { |
183 | 0 | if(!transaction || !server) |
184 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
185 | | |
186 | 0 | UA_ByteString csr = UA_BYTESTRING_NULL; |
187 | 0 | if(transaction->localCsrCertificate.length > 0) |
188 | 0 | csr = transaction->localCsrCertificate; |
189 | |
|
190 | 0 | memset(transaction, 0, sizeof(UA_GDSTransaction)); |
191 | |
|
192 | 0 | transaction->state = UA_GDSTRANSACTIONSTATE_PENDING; |
193 | 0 | UA_NodeId_copy(&sessionId, &transaction->sessionId); |
194 | 0 | transaction->server = server; |
195 | 0 | transaction->localCsrCertificate = csr; |
196 | 0 | transaction->applyChangesQueued = false; |
197 | |
|
198 | 0 | return UA_STATUSCODE_GOOD; |
199 | 0 | } |
200 | | |
201 | | UA_CertificateGroup* |
202 | | UA_GDSTransaction_getCertificateGroup(UA_GDSTransaction *transaction, |
203 | 0 | const UA_CertificateGroup *certGroup) { |
204 | 0 | #ifdef UA_ENABLE_ENCRYPTION |
205 | 0 | if(!transaction || !certGroup) |
206 | 0 | return NULL; |
207 | | |
208 | | /* Check if transaction was initialized */ |
209 | 0 | if(transaction->state != UA_GDSTRANSACTIONSTATE_PENDING) |
210 | 0 | return NULL; |
211 | | |
212 | 0 | for(size_t i = 0; i < transaction->certGroupSize; i++) { |
213 | 0 | UA_CertificateGroup *group = &transaction->certGroups[i]; |
214 | 0 | if(UA_NodeId_equal(&group->certificateGroupId, &certGroup->certificateGroupId)) |
215 | 0 | return group; |
216 | 0 | } |
217 | | |
218 | | /* If the certGroup does not exist, create a new one */ |
219 | 0 | transaction->certGroups = (UA_CertificateGroup*)UA_realloc(transaction->certGroups, (transaction->certGroupSize + 1) * sizeof(UA_CertificateGroup)); |
220 | 0 | if(!transaction->certGroups) |
221 | 0 | return NULL; |
222 | | |
223 | 0 | transaction->certGroupSize++; |
224 | |
|
225 | 0 | memset(&transaction->certGroups[transaction->certGroupSize-1], 0, sizeof(UA_CertificateGroup)); |
226 | |
|
227 | 0 | UA_TrustListDataType trustList; |
228 | 0 | UA_TrustListDataType_init(&trustList); |
229 | 0 | trustList.specifiedLists = UA_TRUSTLISTMASKS_ALL; |
230 | 0 | certGroup->getTrustList((UA_CertificateGroup*)(uintptr_t)certGroup, &trustList); |
231 | | |
232 | | /* Set up the parameters */ |
233 | 0 | static UA_THREAD_LOCAL UA_KeyValuePair params[1] = { |
234 | 0 | {{0, UA_STRING_STATIC("max-trust-listsize")}, {0}} |
235 | 0 | }; |
236 | 0 | UA_KeyValueMap paramsMap = {1, params}; |
237 | |
|
238 | 0 | UA_ServerConfig *config = UA_Server_getConfig(transaction->server); |
239 | 0 | UA_Variant_setScalar(¶ms[0].value, &config->maxTrustListSize, |
240 | 0 | &UA_TYPES[UA_TYPES_UINT32]); |
241 | |
|
242 | 0 | UA_CertificateGroup_Memorystore(&transaction->certGroups[transaction->certGroupSize-1], |
243 | 0 | (UA_NodeId*)(uintptr_t)&certGroup->certificateGroupId, &trustList, certGroup->logging, ¶msMap); |
244 | |
|
245 | 0 | UA_TrustListDataType_clear(&trustList); |
246 | |
|
247 | 0 | return &transaction->certGroups[transaction->certGroupSize-1]; |
248 | | #else |
249 | | return NULL; |
250 | | #endif |
251 | 0 | } |
252 | | |
253 | | UA_StatusCode |
254 | | UA_GDSTransaction_addCertificateInfo(UA_GDSTransaction *transaction, |
255 | | const UA_NodeId certificateGroupId, |
256 | | const UA_NodeId certificateTypeId, |
257 | | const UA_ByteString *certificate, |
258 | 0 | const UA_ByteString *privateKey) { |
259 | 0 | if(!transaction || !certificate) |
260 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
261 | | |
262 | | /* Check if transaction was initialized */ |
263 | 0 | if(transaction->state != UA_GDSTRANSACTIONSTATE_PENDING) |
264 | 0 | return UA_STATUSCODE_BADINVALIDSTATE; |
265 | | |
266 | | /* Check if an entry with certificateGroupId and certificateTypeId already exists */ |
267 | 0 | for(size_t i = 0; i < transaction->certificateInfosSize; i++) { |
268 | 0 | UA_GDSCertificateInfo *certInfo = &transaction->certificateInfos[i]; |
269 | |
|
270 | 0 | if(!UA_NodeId_equal(&certInfo->certificateGroup, &certificateGroupId) || |
271 | 0 | !UA_NodeId_equal(&certInfo->certificateType, &certificateTypeId)) |
272 | 0 | continue; |
273 | | |
274 | 0 | UA_ByteString_clear(&certInfo->certificate); |
275 | 0 | UA_ByteString_clear(&certInfo->privateKey); |
276 | |
|
277 | 0 | UA_ByteString_copy(certificate, &certInfo->certificate); |
278 | 0 | certInfo->privateKey = UA_BYTESTRING_NULL; |
279 | 0 | if(privateKey) |
280 | 0 | UA_ByteString_copy(privateKey, &certInfo->privateKey); |
281 | |
|
282 | 0 | return UA_STATUSCODE_GOOD; |
283 | 0 | } |
284 | | |
285 | 0 | UA_GDSCertificateInfo *newCertInfos = (UA_GDSCertificateInfo *)UA_realloc(transaction->certificateInfos, |
286 | 0 | (transaction->certificateInfosSize + 1) * sizeof(UA_GDSCertificateInfo)); |
287 | 0 | if(!newCertInfos) |
288 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
289 | | |
290 | 0 | transaction->certificateInfos = newCertInfos; |
291 | |
|
292 | 0 | UA_GDSCertificateInfo *newCertInfo = &transaction->certificateInfos[transaction->certificateInfosSize]; |
293 | 0 | UA_ByteString_copy(certificate, &newCertInfo->certificate); |
294 | 0 | UA_NodeId_copy(&certificateGroupId, &newCertInfo->certificateGroup); |
295 | 0 | UA_NodeId_copy(&certificateTypeId, &newCertInfo->certificateType); |
296 | 0 | newCertInfo->privateKey = UA_BYTESTRING_NULL; |
297 | 0 | if(privateKey) |
298 | 0 | UA_ByteString_copy(privateKey, &newCertInfo->privateKey); |
299 | |
|
300 | 0 | transaction->certificateInfosSize++; |
301 | |
|
302 | 0 | return UA_STATUSCODE_GOOD; |
303 | 0 | } |
304 | | |
305 | 6.46k | void UA_GDSTransaction_clear(UA_GDSTransaction *transaction) { |
306 | 6.46k | if(!transaction) |
307 | 0 | return; |
308 | | |
309 | 6.46k | transaction->state = UA_GDSTRANSACTIONSTATE_FRESH; |
310 | 6.46k | transaction->server = NULL; |
311 | 6.46k | UA_NodeId_clear(&transaction->sessionId); |
312 | 6.46k | UA_ByteString_clear(&transaction->localCsrCertificate); |
313 | | |
314 | 6.46k | if(transaction->certGroups) { |
315 | 0 | for(size_t i = 0; i < transaction->certGroupSize; i++) { |
316 | 0 | transaction->certGroups[i].clear(&transaction->certGroups[i]); |
317 | 0 | } |
318 | 0 | UA_free(transaction->certGroups); |
319 | 0 | transaction->certGroupSize = 0; |
320 | 0 | transaction->certGroups = NULL; |
321 | 0 | } |
322 | | |
323 | 6.46k | if(transaction->certificateInfos) { |
324 | 0 | for(size_t i = 0; i < transaction->certificateInfosSize; i++) { |
325 | 0 | UA_ByteString_clear(&transaction->certificateInfos[i].certificate); |
326 | 0 | UA_ByteString_clear(&transaction->certificateInfos[i].privateKey); |
327 | 0 | UA_NodeId_clear(&transaction->certificateInfos[i].certificateGroup); |
328 | 0 | UA_NodeId_clear(&transaction->certificateInfos[i].certificateType); |
329 | 0 | } |
330 | 0 | UA_free(transaction->certificateInfos); |
331 | 0 | transaction->certificateInfosSize = 0; |
332 | 0 | transaction->certificateInfos = NULL; |
333 | 0 | } |
334 | 6.46k | } |
335 | | |
336 | 0 | void UA_GDSTransaction_delete(UA_GDSTransaction *transaction) { |
337 | 0 | UA_GDSTransaction_clear(transaction); |
338 | 0 | UA_free(transaction); |
339 | 0 | } |
340 | | |
341 | | #ifndef UA_ENABLE_GDS_PUSHMANAGEMENT |
342 | | /* Minimal stub: when GDS push management is not compiled in, |
343 | | * only the embedded transaction needs to be cleared. */ |
344 | | void |
345 | 6.46k | UA_GDSManager_clear(UA_GDSManager *gdsManager) { |
346 | 6.46k | if(!gdsManager) |
347 | 0 | return; |
348 | 6.46k | gdsManager->checkSessionCallbackId = 0; |
349 | 6.46k | UA_GDSTransaction_clear(&gdsManager->transaction); |
350 | 6.46k | } |
351 | | #endif |
352 | | |
353 | | /*********************/ |
354 | | /* Server Components */ |
355 | | /*********************/ |
356 | | |
357 | | enum ZIP_CMP |
358 | 15.9k | cmpServerComponent(const UA_UInt64 *a, const UA_UInt64 *b) { |
359 | 15.9k | if(*a == *b) |
360 | 0 | return ZIP_CMP_EQ; |
361 | 15.9k | return (*a < *b) ? ZIP_CMP_LESS : ZIP_CMP_MORE; |
362 | 15.9k | } |
363 | | |
364 | | void |
365 | | addServerComponent(UA_Server *server, UA_ServerComponent *sc, |
366 | 20.2k | UA_UInt64 *identifier) { |
367 | 20.2k | if(!sc) |
368 | 0 | return; |
369 | | |
370 | 20.2k | sc->identifier = ++server->serverComponentIds; |
371 | 20.2k | ZIP_INSERT(UA_ServerComponentTree, &server->serverComponents, sc); |
372 | | |
373 | | /* Start the component if the server is started */ |
374 | 20.2k | if(server->state == UA_LIFECYCLESTATE_STARTED && sc->start) |
375 | 0 | sc->start(sc, server); |
376 | | |
377 | 20.2k | if(identifier) |
378 | 0 | *identifier = sc->identifier; |
379 | 20.2k | } |
380 | | |
381 | | static void * |
382 | 760 | findServerComponent(void *context, UA_ServerComponent *sc) { |
383 | 760 | UA_String *name = (UA_String*)context; |
384 | 760 | return (UA_String_equal(&sc->name, name)) ? sc : NULL; |
385 | 760 | } |
386 | | |
387 | | UA_ServerComponent * |
388 | 516 | getServerComponentByName(UA_Server *server, UA_String name) { |
389 | 516 | return (UA_ServerComponent*) |
390 | 516 | ZIP_ITER(UA_ServerComponentTree, &server->serverComponents, |
391 | 516 | findServerComponent, &name); |
392 | 516 | } |
393 | | |
394 | | static void * |
395 | 816 | startServerComponent(void *server, UA_ServerComponent *sc) { |
396 | 816 | sc->start(sc, (UA_Server*)server); |
397 | 816 | return NULL; |
398 | 816 | } |
399 | | |
400 | | static void * |
401 | 816 | stopServerComponent(void *_, UA_ServerComponent *sc) { |
402 | 816 | sc->stop(sc); |
403 | 816 | return NULL; |
404 | 816 | } |
405 | | |
406 | | /* ZIP_ITER returns NULL only if all components are stopped */ |
407 | | static void * |
408 | 1.08k | checkServerComponent(void *_, UA_ServerComponent *sc) { |
409 | 1.08k | return (sc->state == UA_LIFECYCLESTATE_STOPPED) ? NULL : (void*)0x01; |
410 | 1.08k | } |
411 | | |
412 | | /********************/ |
413 | | /* Server Lifecycle */ |
414 | | /********************/ |
415 | | |
416 | | /* The server needs to be stopped before it can be deleted */ |
417 | | UA_StatusCode |
418 | 6.46k | UA_Server_delete(UA_Server *server) { |
419 | 6.46k | if(!server) |
420 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
421 | | |
422 | 6.46k | if(server->state != UA_LIFECYCLESTATE_STOPPED) { |
423 | 0 | UA_LOG_ERROR(server->config.logging, UA_LOGCATEGORY_SERVER, |
424 | 0 | "The server must be fully stopped before it can be deleted"); |
425 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
426 | 0 | } |
427 | | |
428 | 6.46k | lockServer(server); |
429 | | |
430 | 6.46k | session_list_entry *current, *temp; |
431 | 6.46k | LIST_FOREACH_SAFE(current, &server->sessions, pointers, temp) { |
432 | 4.04k | UA_Session_remove(server, ¤t->session, UA_SHUTDOWNREASON_CLOSE); |
433 | 4.04k | } |
434 | 6.46k | UA_Array_delete(server->namespaces, server->namespacesSize, &UA_TYPES[UA_TYPES_STRING]); |
435 | | |
436 | 6.46k | #ifdef UA_ENABLE_SUBSCRIPTIONS |
437 | | /* Remove subscriptions without a session */ |
438 | 6.46k | UA_Subscription *sub, *sub_tmp; |
439 | 6.46k | LIST_FOREACH_SAFE(sub, &server->subscriptions, serverListEntry, sub_tmp) { |
440 | 0 | UA_Subscription_delete(server, sub); |
441 | 0 | } |
442 | | |
443 | | #ifdef UA_ENABLE_SUBSCRIPTIONS_ALARMS_CONDITIONS |
444 | | UA_ConditionList_delete(server); |
445 | | #endif |
446 | | |
447 | 6.46k | #endif |
448 | | |
449 | 6.46k | #if UA_MULTITHREADING >= 100 |
450 | 6.46k | UA_AsyncManager_clear(&server->asyncManager, server); |
451 | 6.46k | #endif |
452 | | |
453 | | /* Clean up the Admin Session */ |
454 | 6.46k | UA_Session_clear(&server->adminSession, server); |
455 | 6.46k | #ifdef UA_ENABLE_SUBSCRIPTIONS |
456 | 6.46k | server->adminSubscription = NULL; |
457 | 6.46k | UA_assert(server->monitoredItemsSize == 0); |
458 | 6.46k | UA_assert(server->subscriptionsSize == 0); |
459 | 6.46k | #endif |
460 | | |
461 | | /* Remove all server components (all stopped by now) */ |
462 | 6.46k | UA_ServerComponent *top; |
463 | 24.1k | while((top = ZIP_ROOT(&server->serverComponents))) { |
464 | 17.7k | UA_assert(top->state == UA_LIFECYCLESTATE_STOPPED); |
465 | 17.7k | top->clear(top); |
466 | 17.7k | ZIP_REMOVE(UA_ServerComponentTree, &server->serverComponents, top); |
467 | 17.7k | UA_free(top); |
468 | 17.7k | } |
469 | | |
470 | 6.46k | unlockServer(server); /* The timer has its own mutex */ |
471 | | |
472 | | /* Clean up the config */ |
473 | 6.46k | UA_ServerConfig_clear(&server->config); |
474 | | |
475 | 6.46k | #if UA_MULTITHREADING >= 100 |
476 | 6.46k | UA_LOCK_DESTROY(&server->serviceMutex); |
477 | 6.46k | #endif |
478 | | |
479 | 6.46k | UA_GDSManager_clear(&server->gdsManager); |
480 | | |
481 | | /* Clean up the custom datatypes */ |
482 | 6.46k | if(server->customTypes_internal != NULL) { |
483 | 0 | for(size_t i = 0; i < server->customTypes_internalSize; i++) { |
484 | 0 | UA_DataTypeArray *curr = &server->customTypes_internal[i]; |
485 | 0 | for(size_t j = 0; j < curr->typesSize; j++) |
486 | 0 | UA_DataType_clear(&curr->types[j]); |
487 | 0 | UA_free(curr->types); |
488 | 0 | } |
489 | 0 | UA_free(server->customTypes_internal); |
490 | 0 | } |
491 | | |
492 | | /* Delete the server itself and return */ |
493 | 6.46k | UA_free(server); |
494 | 6.46k | return UA_STATUSCODE_GOOD; |
495 | 6.46k | } |
496 | | |
497 | | /* Regular house-keeping tasks. Removing unused and timed-out channels and |
498 | | * sessions. */ |
499 | | static void |
500 | 0 | serverHouseKeeping(UA_Server *server, void *_) { |
501 | 0 | lockServer(server); |
502 | 0 | UA_EventLoop *el = server->config.eventLoop; |
503 | 0 | cleanupSessions(server, el->dateTime_nowMonotonic(el)); |
504 | 0 | unlockServer(server); |
505 | 0 | } |
506 | | |
507 | | /********************/ |
508 | | /* Server Lifecycle */ |
509 | | /********************/ |
510 | | |
511 | | static |
512 | | UA_INLINE |
513 | 19.4k | UA_Boolean UA_Server_NodestoreIsConfigured(UA_Server *server) { |
514 | 19.4k | return (server->config.nodestore && server->config.nodestore->getNode); |
515 | 19.4k | } |
516 | | |
517 | | static UA_Server * |
518 | 6.46k | UA_Server_init(UA_Server *server) { |
519 | 6.46k | UA_StatusCode res = UA_STATUSCODE_GOOD; |
520 | 6.46k | UA_CHECK_FATAL(UA_Server_NodestoreIsConfigured(server), goto cleanup, |
521 | 6.46k | server->config.logging, UA_LOGCATEGORY_SERVER, |
522 | 6.46k | "No Nodestore configured in the server"); |
523 | | |
524 | | /* Init start time to zero, the actual start time will be sampled in |
525 | | * UA_Server_run_startup() */ |
526 | 6.46k | server->startTime = 0; |
527 | | |
528 | | /* Set a seed for non-cyptographic randomness */ |
529 | 6.46k | #ifndef UA_ENABLE_DETERMINISTIC_RNG |
530 | 6.46k | UA_random_seed((UA_UInt64)UA_DateTime_now()); |
531 | 6.46k | #endif |
532 | | |
533 | 6.46k | UA_LOCK_INIT(&server->serviceMutex); |
534 | 6.46k | lockServer(server); |
535 | | |
536 | | /* Initialize the adminSession */ |
537 | 6.46k | UA_Session_init(&server->adminSession); |
538 | 6.46k | server->adminSession.sessionId.identifierType = UA_NODEIDTYPE_GUID; |
539 | 6.46k | server->adminSession.sessionId.identifier.guid.data1 = 1; |
540 | 6.46k | server->adminSession.validTill = UA_INT64_MAX; |
541 | 6.46k | server->adminSession.sessionName = UA_STRING_ALLOC("Administrator"); |
542 | | |
543 | 6.46k | #ifdef UA_ENABLE_SUBSCRIPTIONS |
544 | | /* Initialize the adminSubscription */ |
545 | 6.46k | server->adminSubscription = UA_Subscription_new(); |
546 | 6.46k | UA_CHECK_MEM(server->adminSubscription, goto cleanup); |
547 | 6.46k | UA_Session_attachSubscription(&server->adminSession, server->adminSubscription); |
548 | 6.46k | #endif |
549 | | |
550 | | /* Create Namespaces 0 and 1 |
551 | | * Ns1 will be filled later with the uri from the app description */ |
552 | 6.46k | server->namespaces = (UA_String *)UA_Array_new(2, &UA_TYPES[UA_TYPES_STRING]); |
553 | 6.46k | UA_CHECK_MEM(server->namespaces, goto cleanup); |
554 | | |
555 | 6.46k | server->namespaces[0] = UA_STRING_ALLOC("http://opcfoundation.org/UA/"); |
556 | 6.46k | server->namespaces[1] = UA_STRING_NULL; |
557 | 6.46k | server->namespacesSize = 2; |
558 | | |
559 | | /* Initialize Session Management */ |
560 | 6.46k | LIST_INIT(&server->sessions); |
561 | 6.46k | server->sessionCount = 0; |
562 | | |
563 | | /* Initialize SecureChannel */ |
564 | 6.46k | TAILQ_INIT(&server->channels); |
565 | | /* TODO: use an ID that is likely to be unique after a restart */ |
566 | 6.46k | server->lastChannelId = STARTCHANNELID; |
567 | 6.46k | server->lastTokenId = STARTTOKENID; |
568 | | |
569 | 6.46k | #if UA_MULTITHREADING >= 100 |
570 | 6.46k | UA_AsyncManager_init(&server->asyncManager, server); |
571 | 6.46k | #endif |
572 | | |
573 | | /* Initialize namespace 0 */ |
574 | 6.46k | #if defined(UA_GENERATED_NAMESPACE_ZERO) || defined(UA_NAMESPACE_ZERO_MINIMAL) |
575 | | /* Generate NS0 nodes at runtime or create the minimal NS0 */ |
576 | 6.46k | res = initNS0(server); |
577 | | #else |
578 | | /* NONE configuration: NS0 pre-loaded by external nodestore (e.g., ROM). |
579 | | * Only connect data sources for dynamic values like ServerTime, ServerStatus, etc. */ |
580 | | res = initNS0_dataSources(server); |
581 | | #endif |
582 | 6.46k | UA_CHECK_STATUS(res, goto cleanup); |
583 | | |
584 | | #ifdef UA_ENABLE_GDS_PUSHMANAGEMENT |
585 | | res = initNS0PushManagement(server); |
586 | | UA_CHECK_STATUS(res, goto cleanup); |
587 | | #endif |
588 | | |
589 | | #ifdef UA_ENABLE_NODESET_INJECTOR |
590 | | res = UA_Server_injectNodesets(server); |
591 | | UA_CHECK_STATUS(res, goto cleanup); |
592 | | #endif |
593 | | |
594 | | /* Initialize the binay protocol support */ |
595 | 6.46k | addServerComponent(server, UA_BinaryProtocolManager_new(server), NULL); |
596 | | |
597 | | /* Initialized Discovery */ |
598 | 6.46k | #ifdef UA_ENABLE_DISCOVERY |
599 | 6.46k | addServerComponent(server, UA_DiscoveryManager_new(), NULL); |
600 | 6.46k | #endif |
601 | | |
602 | | /* Initialize PubSub */ |
603 | 6.46k | #ifdef UA_ENABLE_PUBSUB |
604 | 6.46k | if(server->config.pubsubEnabled) |
605 | 6.46k | addServerComponent(server, UA_PubSubManager_new(server), NULL); |
606 | 6.46k | #endif |
607 | | |
608 | | /* For all custom datatypes, check if they are represented in the |
609 | | * information model. If not, add them. */ |
610 | 6.46k | #ifdef UA_ENABLE_TYPEDESCRIPTION |
611 | 6.46k | for(const UA_DataTypeArray *custom = server->config.customDataTypes; |
612 | 6.46k | custom != NULL; custom = custom->next) { |
613 | 0 | for(size_t i = 0; i < custom->typesSize; i++) { |
614 | 0 | const UA_DataType *type = &custom->types[i]; |
615 | 0 | if(type->typeKind != UA_DATATYPEKIND_STRUCTURE && |
616 | 0 | type->typeKind != UA_DATATYPEKIND_OPTSTRUCT && |
617 | 0 | type->typeKind != UA_DATATYPEKIND_UNION) |
618 | 0 | continue; |
619 | 0 | const UA_Node *node = |
620 | 0 | UA_NODESTORE_GET_SELECTIVE(server, &type->typeId, 0, |
621 | 0 | UA_REFERENCETYPESET_NONE, |
622 | 0 | UA_BROWSEDIRECTION_INVALID); |
623 | 0 | if(node) { |
624 | 0 | UA_NODESTORE_RELEASE(server, node); |
625 | 0 | continue; |
626 | 0 | } |
627 | | |
628 | 0 | UA_QualifiedName dataTypeBrowseName = |
629 | 0 | {type->typeId.namespaceIndex, UA_STRING_STATIC((char*)(uintptr_t)type->typeName)}; |
630 | 0 | UA_DataTypeAttributes dta = UA_DataTypeAttributes_default; |
631 | 0 | dta.displayName.text = UA_STRING((char*)(uintptr_t)type->typeName); |
632 | 0 | res = UA_Server_addDataTypeNode(server, type->typeId, UA_NS0ID(STRUCTURE), |
633 | 0 | UA_NS0ID(HASSUBTYPE), dataTypeBrowseName, |
634 | 0 | dta, NULL, NULL); |
635 | 0 | if(res != UA_STATUSCODE_GOOD) { |
636 | 0 | UA_LOG_WARNING(server->config.logging, UA_LOGCATEGORY_SERVER, |
637 | 0 | "Could not add DataTypeNode for %s (%N)", |
638 | 0 | type->typeName, type->typeId); |
639 | 0 | } |
640 | 0 | } |
641 | 0 | } |
642 | 6.46k | #endif |
643 | | |
644 | 6.46k | unlockServer(server); |
645 | 6.46k | return server; |
646 | | |
647 | 0 | cleanup: |
648 | 0 | unlockServer(server); |
649 | 0 | UA_Server_delete(server); |
650 | 0 | return NULL; |
651 | 6.46k | } |
652 | | |
653 | | UA_Server * |
654 | 19.4k | UA_Server_newWithConfig(UA_ServerConfig *config) { |
655 | 19.4k | UA_CHECK_MEM(config, return NULL); |
656 | | |
657 | 19.4k | UA_CHECK_LOG(config->eventLoop != NULL, return NULL, ERROR, |
658 | 19.4k | config->logging, UA_LOGCATEGORY_SERVER, "No EventLoop configured"); |
659 | | |
660 | 19.4k | UA_Server *server = (UA_Server *)UA_calloc(1, sizeof(UA_Server)); |
661 | 19.4k | UA_CHECK_MEM(server, UA_ServerConfig_clear(config); return NULL); |
662 | | |
663 | 19.4k | server->config = *config; |
664 | | |
665 | | /* If not defined, set logging to what the server has */ |
666 | 19.4k | if(!server->config.secureChannelPKI.logging) |
667 | 0 | server->config.secureChannelPKI.logging = server->config.logging; |
668 | 19.4k | if(!server->config.sessionPKI.logging) |
669 | 0 | server->config.sessionPKI.logging = server->config.logging; |
670 | | |
671 | | /* Reset the old config */ |
672 | 19.4k | memset(config, 0, sizeof(UA_ServerConfig)); |
673 | 19.4k | return UA_Server_init(server); |
674 | 19.4k | } |
675 | | |
676 | | /* Returns if the server should be shut down immediately */ |
677 | | static UA_Boolean |
678 | 0 | setServerShutdown(UA_Server *server) { |
679 | 0 | if(server->endTime != 0) |
680 | 0 | return false; |
681 | 0 | if(server->config.shutdownDelay == 0) |
682 | 0 | return true; |
683 | | |
684 | 0 | UA_LOG_WARNING(server->config.logging, UA_LOGCATEGORY_SERVER, |
685 | 0 | "Shutting down the server with a delay of %i ms", |
686 | 0 | (int)server->config.shutdownDelay); |
687 | |
|
688 | 0 | UA_EventLoop *el = server->config.eventLoop; |
689 | 0 | server->endTime = el->dateTime_now(el) + (UA_DateTime)(server->config.shutdownDelay * UA_DATETIME_MSEC); |
690 | | |
691 | | /* Call the application notification callback */ |
692 | 0 | UA_ServerConfig *config = &server->config; |
693 | 0 | if(config->lifecycleNotificationCallback) |
694 | 0 | config->lifecycleNotificationCallback(server, UA_APPLICATIONNOTIFICATIONTYPE_LIFECYCLE_SHUTDOWN, |
695 | 0 | UA_KEYVALUEMAP_NULL); |
696 | 0 | if(config->globalNotificationCallback) |
697 | 0 | config->globalNotificationCallback(server, UA_APPLICATIONNOTIFICATIONTYPE_LIFECYCLE_SHUTDOWN, |
698 | 0 | UA_KEYVALUEMAP_NULL); |
699 | |
|
700 | 0 | return false; |
701 | 0 | } |
702 | | |
703 | | /*******************/ |
704 | | /* Timed Callbacks */ |
705 | | /*******************/ |
706 | | |
707 | | UA_StatusCode |
708 | | UA_Server_addTimedCallback(UA_Server *server, UA_ServerCallback callback, |
709 | 0 | void *data, UA_DateTime date, UA_UInt64 *callbackId) { |
710 | 0 | lockServer(server); |
711 | 0 | UA_EventLoop *el = server->config.eventLoop; |
712 | 0 | UA_StatusCode retval = el->addTimer(el, (UA_Callback)callback, server, data, |
713 | 0 | 0.0, &date, UA_TIMERPOLICY_ONCE, callbackId); |
714 | 0 | unlockServer(server); |
715 | 0 | return retval; |
716 | 0 | } |
717 | | |
718 | | UA_StatusCode |
719 | | addRepeatedCallback(UA_Server *server, UA_ServerCallback callback, |
720 | 2.64k | void *data, UA_Double interval_ms, UA_UInt64 *callbackId) { |
721 | 2.64k | UA_LOCK_ASSERT(&server->serviceMutex); |
722 | 2.64k | UA_EventLoop *el = server->config.eventLoop; |
723 | 2.64k | return el->addTimer(el, (UA_Callback)callback, server, data, interval_ms, NULL, |
724 | 2.64k | UA_TIMERPOLICY_CURRENTTIME, callbackId); |
725 | 2.64k | } |
726 | | |
727 | | UA_StatusCode |
728 | | UA_Server_addRepeatedCallback(UA_Server *server, UA_ServerCallback callback, |
729 | | void *data, UA_Double interval_ms, |
730 | 0 | UA_UInt64 *callbackId) { |
731 | 0 | lockServer(server); |
732 | 0 | UA_StatusCode res = addRepeatedCallback(server, callback, data, interval_ms, callbackId); |
733 | 0 | unlockServer(server); |
734 | 0 | return res; |
735 | 0 | } |
736 | | |
737 | | UA_StatusCode |
738 | | changeRepeatedCallbackInterval(UA_Server *server, UA_UInt64 callbackId, |
739 | 0 | UA_Double interval_ms) { |
740 | 0 | UA_LOCK_ASSERT(&server->serviceMutex); |
741 | 0 | UA_EventLoop *el = server->config.eventLoop; |
742 | 0 | return el->modifyTimer(el, callbackId, interval_ms, NULL, UA_TIMERPOLICY_CURRENTTIME); |
743 | 0 | } |
744 | | |
745 | | UA_StatusCode |
746 | | UA_Server_changeRepeatedCallbackInterval(UA_Server *server, UA_UInt64 callbackId, |
747 | 0 | UA_Double interval_ms) { |
748 | 0 | lockServer(server); |
749 | 0 | UA_StatusCode retval = changeRepeatedCallbackInterval(server, callbackId, interval_ms); |
750 | 0 | unlockServer(server); |
751 | 0 | return retval; |
752 | 0 | } |
753 | | |
754 | | void |
755 | 2.64k | removeCallback(UA_Server *server, UA_UInt64 callbackId) { |
756 | 2.64k | UA_LOCK_ASSERT(&server->serviceMutex); |
757 | 2.64k | UA_EventLoop *el = server->config.eventLoop; |
758 | 2.64k | el->removeTimer(el, callbackId); |
759 | 2.64k | } |
760 | | |
761 | | void |
762 | 0 | UA_Server_removeCallback(UA_Server *server, UA_UInt64 callbackId) { |
763 | 0 | lockServer(server); |
764 | 0 | removeCallback(server, callbackId); |
765 | 0 | unlockServer(server); |
766 | 0 | } |
767 | | |
768 | | /* When the trustlist changes, re-check the certificates of all |
769 | | * SecureChannels */ |
770 | | static void |
771 | 0 | secureChannel_delayedCloseTrustList(void *application, void *context) { |
772 | 0 | UA_DelayedCallback *dc = (UA_DelayedCallback*)context; |
773 | 0 | UA_Server *server = (UA_Server*)application; |
774 | |
|
775 | 0 | UA_CertificateGroup *certGroup = &server->config.secureChannelPKI; |
776 | 0 | UA_SecureChannel *channel; |
777 | 0 | TAILQ_FOREACH(channel, &server->channels, serverEntry) { |
778 | 0 | if(channel->state != UA_SECURECHANNELSTATE_CLOSED && |
779 | 0 | channel->state != UA_SECURECHANNELSTATE_CLOSING) |
780 | 0 | continue; |
781 | 0 | if(channel->remoteCertificate.length == 0) |
782 | 0 | continue; /* SecureChannels w/o security */ |
783 | 0 | UA_StatusCode res = |
784 | 0 | validateCertificate(server, certGroup, channel, channel->sessions, |
785 | 0 | "RenewTrustList", NULL, channel->remoteCertificate); |
786 | 0 | if(res != UA_STATUSCODE_GOOD) |
787 | 0 | UA_SecureChannel_shutdown(channel, UA_SHUTDOWNREASON_CLOSE); |
788 | 0 | } |
789 | 0 | UA_free(dc); |
790 | 0 | } |
791 | | |
792 | | static UA_CertificateGroup* |
793 | 0 | getCertificateGroup(UA_Server *server, const UA_NodeId certificateGroupId) { |
794 | 0 | UA_NodeId defaultApplicationGroup = |
795 | 0 | UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP); |
796 | 0 | UA_NodeId defaultUserTokenGroup = |
797 | 0 | UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP); |
798 | 0 | if(UA_NodeId_equal(&certificateGroupId, &defaultApplicationGroup)) { |
799 | 0 | return &server->config.secureChannelPKI; |
800 | 0 | } |
801 | 0 | if(UA_NodeId_equal(&certificateGroupId, &defaultUserTokenGroup)) { |
802 | 0 | return &server->config.sessionPKI; |
803 | 0 | } |
804 | 0 | return NULL; |
805 | 0 | } |
806 | | |
807 | | UA_StatusCode |
808 | | UA_Server_addCertificates(UA_Server *server, |
809 | | const UA_NodeId certificateGroupId, |
810 | | UA_ByteString *certificates, |
811 | | size_t certificatesSize, |
812 | | UA_ByteString *crls, |
813 | | size_t crlsSize, |
814 | | const UA_Boolean isTrusted, |
815 | 0 | const UA_Boolean appendCertificates) { |
816 | 0 | UA_CertificateGroup *certGroup = getCertificateGroup(server, certificateGroupId); |
817 | 0 | if(!certGroup) |
818 | 0 | return UA_STATUSCODE_BADINVALIDARGUMENT; |
819 | | |
820 | 0 | UA_TrustListDataType trustList; |
821 | 0 | UA_TrustListDataType_init(&trustList); |
822 | |
|
823 | 0 | if(isTrusted) { |
824 | 0 | trustList.specifiedLists = UA_TRUSTLISTMASKS_TRUSTEDCERTIFICATES | UA_TRUSTLISTMASKS_TRUSTEDCRLS; |
825 | 0 | trustList.trustedCertificates = certificates; |
826 | 0 | trustList.trustedCertificatesSize = certificatesSize; |
827 | 0 | trustList.trustedCrls = crls; |
828 | 0 | trustList.trustedCrlsSize = crlsSize; |
829 | 0 | } else { |
830 | 0 | trustList.specifiedLists = UA_TRUSTLISTMASKS_ISSUERCERTIFICATES | UA_TRUSTLISTMASKS_ISSUERCRLS; |
831 | 0 | trustList.issuerCertificates = certificates; |
832 | 0 | trustList.issuerCertificatesSize = certificatesSize; |
833 | 0 | trustList.issuerCrls = crls; |
834 | 0 | trustList.issuerCrlsSize = crlsSize; |
835 | 0 | } |
836 | | |
837 | | /* When adding certificate files to the TrustList, |
838 | | * it is not necessary to check the trust status of the existing SecureChannels. */ |
839 | 0 | if(appendCertificates) |
840 | 0 | return certGroup->addToTrustList(certGroup, &trustList); |
841 | | |
842 | 0 | UA_StatusCode retval = certGroup->setTrustList(certGroup, &trustList); |
843 | 0 | if(retval != UA_STATUSCODE_GOOD) |
844 | 0 | return retval; |
845 | | |
846 | 0 | UA_DelayedCallback *dc = (UA_DelayedCallback*)UA_calloc(1, sizeof(UA_DelayedCallback)); |
847 | 0 | if(!dc) |
848 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
849 | | |
850 | 0 | dc->callback = secureChannel_delayedCloseTrustList; |
851 | 0 | dc->application = server; |
852 | 0 | dc->context = dc; |
853 | |
|
854 | 0 | UA_EventLoop *el = server->config.eventLoop; |
855 | 0 | el->addDelayedCallback(el, dc); |
856 | |
|
857 | 0 | return UA_STATUSCODE_GOOD; |
858 | 0 | } |
859 | | |
860 | | UA_StatusCode |
861 | | UA_Server_removeCertificates(UA_Server *server, |
862 | | const UA_NodeId certificateGroupId, |
863 | | UA_ByteString *certificates, |
864 | | size_t certificatesSize, |
865 | 0 | const UA_Boolean isTrusted) { |
866 | 0 | UA_CertificateGroup *certGroup = getCertificateGroup(server, certificateGroupId); |
867 | 0 | if(!certGroup) |
868 | 0 | return UA_STATUSCODE_BADINVALIDARGUMENT; |
869 | | |
870 | 0 | UA_ByteString *crls = NULL; |
871 | 0 | size_t crlsSize = 0; |
872 | 0 | UA_StatusCode retval = UA_STATUSCODE_GOOD; |
873 | 0 | for(size_t i = 0; i < certificatesSize; i++) { |
874 | 0 | retval = certGroup->getCertificateCrls(certGroup, &certificates[i], isTrusted, &crls, &crlsSize); |
875 | | /* Tolerate "Bad_NoMatch" to support removing CA certificates that do |
876 | | * not have an associated CRL. */ |
877 | 0 | if((retval != UA_STATUSCODE_GOOD) && (retval != UA_STATUSCODE_BADNOMATCH)) { |
878 | 0 | UA_Array_delete(crls, crlsSize, &UA_TYPES[UA_TYPES_BYTESTRING]); |
879 | 0 | return retval; |
880 | 0 | } |
881 | 0 | } |
882 | | |
883 | 0 | UA_TrustListDataType trustList; |
884 | 0 | UA_TrustListDataType_init(&trustList); |
885 | 0 | if(isTrusted) { |
886 | 0 | trustList.specifiedLists = UA_TRUSTLISTMASKS_TRUSTEDCERTIFICATES | UA_TRUSTLISTMASKS_TRUSTEDCRLS; |
887 | 0 | trustList.trustedCertificates = certificates; |
888 | 0 | trustList.trustedCertificatesSize = certificatesSize; |
889 | 0 | trustList.trustedCrls = crls; |
890 | 0 | trustList.trustedCrlsSize = crlsSize; |
891 | 0 | } else { |
892 | 0 | trustList.specifiedLists = UA_TRUSTLISTMASKS_ISSUERCERTIFICATES | UA_TRUSTLISTMASKS_ISSUERCRLS; |
893 | 0 | trustList.issuerCertificates = certificates; |
894 | 0 | trustList.issuerCertificatesSize = certificatesSize; |
895 | 0 | trustList.issuerCrls = crls; |
896 | 0 | trustList.issuerCrlsSize = crlsSize; |
897 | 0 | } |
898 | |
|
899 | 0 | retval = certGroup->removeFromTrustList(certGroup, &trustList); |
900 | 0 | UA_Array_delete(crls, crlsSize, &UA_TYPES[UA_TYPES_BYTESTRING]); |
901 | 0 | if(retval != UA_STATUSCODE_GOOD) |
902 | 0 | return retval; |
903 | | |
904 | 0 | UA_DelayedCallback *dc = (UA_DelayedCallback*)UA_calloc(1, sizeof(UA_DelayedCallback)); |
905 | 0 | if(!dc) |
906 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
907 | | |
908 | 0 | dc->callback = secureChannel_delayedCloseTrustList; |
909 | 0 | dc->application = server; |
910 | 0 | dc->context = dc; |
911 | |
|
912 | 0 | UA_EventLoop *el = server->config.eventLoop; |
913 | 0 | el->addDelayedCallback(el, dc); |
914 | |
|
915 | 0 | return UA_STATUSCODE_GOOD; |
916 | 0 | } |
917 | | |
918 | | typedef struct UpdateCertInfo { |
919 | | UA_Server *server; |
920 | | UA_NodeId certificateTypeId; |
921 | | } UpdateCertInfo; |
922 | | |
923 | | static void |
924 | 0 | secureChannel_delayedClose(void *application, void *context) { |
925 | 0 | UA_DelayedCallback *dc = (UA_DelayedCallback*)context; |
926 | 0 | UpdateCertInfo *info = (UpdateCertInfo*)application; |
927 | |
|
928 | 0 | UA_SecureChannel *channel; |
929 | 0 | TAILQ_FOREACH(channel, &info->server->channels, serverEntry) { |
930 | 0 | const UA_SecurityPolicy *policy = channel->securityPolicy; |
931 | 0 | if(UA_NodeId_equal(&policy->certificateTypeId, &(info->certificateTypeId))) |
932 | 0 | UA_SecureChannel_shutdown(channel, UA_SHUTDOWNREASON_CLOSE); |
933 | 0 | } |
934 | 0 | UA_NodeId_clear(&(info->certificateTypeId)); |
935 | 0 | UA_free(info); |
936 | 0 | UA_free(dc); |
937 | 0 | } |
938 | | |
939 | | UA_StatusCode |
940 | | UA_Server_updateCertificate(UA_Server *server, |
941 | | const UA_NodeId certificateGroupId, |
942 | | const UA_NodeId certificateTypeId, |
943 | | const UA_ByteString certificate, |
944 | 0 | const UA_ByteString *privateKey) { |
945 | 0 | if(!server) |
946 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
947 | | |
948 | 0 | lockServer(server); |
949 | |
|
950 | 0 | if(server->gdsManager.transaction.state == UA_GDSTRANSACTIONSTATE_PENDING) { |
951 | 0 | unlockServer(server); |
952 | 0 | return UA_STATUSCODE_BADTRANSACTIONPENDING; |
953 | 0 | } |
954 | | |
955 | 0 | UA_NodeId defaultApplicationGroup = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP); |
956 | 0 | UA_NodeId certGroupId = certificateGroupId; |
957 | 0 | if(UA_NodeId_isNull(&certGroupId)) { |
958 | | /* Use default value if argument is empty */ |
959 | 0 | certGroupId = defaultApplicationGroup; |
960 | 0 | } |
961 | | /* The server currently only supports the DefaultApplicationGroup */ |
962 | 0 | if(!UA_NodeId_equal(&certGroupId, &defaultApplicationGroup)) { |
963 | 0 | unlockServer(server); |
964 | 0 | return UA_STATUSCODE_BADINVALIDARGUMENT; |
965 | 0 | } |
966 | | |
967 | | /* The server currently only supports the following certificate type */ |
968 | | /* UA_NodeId certTypRsaMin = UA_NODEID_NUMERIC(0, UA_NS0ID_RSAMINAPPLICATIONCERTIFICATETYPE); */ |
969 | 0 | UA_NodeId certTypRsaSha256 = UA_NODEID_NUMERIC(0, UA_NS0ID_RSASHA256APPLICATIONCERTIFICATETYPE); |
970 | 0 | if(!UA_NodeId_equal(&certificateTypeId, &certTypRsaSha256)) { |
971 | 0 | unlockServer(server); |
972 | 0 | return UA_STATUSCODE_BADINVALIDARGUMENT; |
973 | 0 | } |
974 | | |
975 | 0 | UA_ByteString newPrivateKey = UA_BYTESTRING_NULL; |
976 | 0 | if(privateKey) { |
977 | 0 | if(UA_CertificateUtils_checkKeyPair(&certificate, privateKey) != UA_STATUSCODE_GOOD) { |
978 | 0 | unlockServer(server); |
979 | 0 | return UA_STATUSCODE_BADNOTSUPPORTED; |
980 | 0 | } |
981 | 0 | newPrivateKey = *privateKey; |
982 | 0 | } else { |
983 | 0 | #ifdef UA_ENABLE_ENCRYPTION |
984 | | /* No key to pair the certificate with, so check that it parses */ |
985 | 0 | size_t keySize = 0; |
986 | 0 | if(UA_CertificateUtils_getKeySize((UA_ByteString*)(uintptr_t)&certificate, |
987 | 0 | &keySize) != UA_STATUSCODE_GOOD) { |
988 | 0 | unlockServer(server); |
989 | 0 | return UA_STATUSCODE_BADCERTIFICATEINVALID; |
990 | 0 | } |
991 | 0 | #endif |
992 | 0 | } |
993 | | |
994 | 0 | UA_StatusCode retval = UA_STATUSCODE_GOOD; |
995 | 0 | for(size_t i = 0; i < server->config.endpointsSize; i++) { |
996 | 0 | UA_EndpointDescription *ed = &server->config.endpoints[i]; |
997 | 0 | UA_SecurityPolicy *sp = getSecurityPolicyByUri(server, |
998 | 0 | &server->config.endpoints[i].securityPolicyUri); |
999 | 0 | UA_CHECK_MEM(sp, unlockServer(server); return UA_STATUSCODE_BADINTERNALERROR); |
1000 | | |
1001 | 0 | if(!UA_NodeId_equal(&sp->certificateTypeId, &certificateTypeId)) |
1002 | 0 | continue; |
1003 | | |
1004 | 0 | retval = sp->updateCertificate(sp, certificate, newPrivateKey); |
1005 | 0 | if(retval != UA_STATUSCODE_GOOD) { |
1006 | 0 | unlockServer(server); |
1007 | 0 | return retval; |
1008 | 0 | } |
1009 | | |
1010 | 0 | UA_ByteString_clear(&ed->serverCertificate); |
1011 | 0 | UA_ByteString_copy(&certificate, &ed->serverCertificate); |
1012 | 0 | } |
1013 | | |
1014 | 0 | UA_DelayedCallback *dc = (UA_DelayedCallback*)UA_calloc(1, sizeof(UA_DelayedCallback)); |
1015 | 0 | if(!dc) { |
1016 | 0 | unlockServer(server); |
1017 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
1018 | 0 | } |
1019 | | |
1020 | 0 | UpdateCertInfo *certInfo = (UpdateCertInfo*)UA_calloc(1, sizeof(UpdateCertInfo)); |
1021 | 0 | certInfo->server = server; |
1022 | 0 | UA_NodeId_copy(&certificateTypeId, &(certInfo->certificateTypeId)); |
1023 | |
|
1024 | 0 | dc->callback = secureChannel_delayedClose; |
1025 | 0 | dc->application = certInfo; |
1026 | 0 | dc->context = dc; |
1027 | |
|
1028 | 0 | UA_EventLoop *el = server->config.eventLoop; |
1029 | 0 | el->addDelayedCallback(el, dc); |
1030 | |
|
1031 | 0 | unlockServer(server); |
1032 | 0 | return UA_STATUSCODE_GOOD; |
1033 | 0 | } |
1034 | | |
1035 | | UA_StatusCode |
1036 | | UA_Server_createSigningRequest(UA_Server *server, |
1037 | | const UA_NodeId certificateGroupId, |
1038 | | const UA_NodeId certificateTypeId, |
1039 | | const UA_String *subjectName, |
1040 | | const UA_Boolean *regenerateKey, |
1041 | | const UA_ByteString *nonce, |
1042 | 0 | UA_ByteString *csr) { |
1043 | 0 | if(!server || !csr) |
1044 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
1045 | | |
1046 | 0 | UA_StatusCode retval = UA_STATUSCODE_GOOD; |
1047 | 0 | UA_NodeId defaultApplicationGroup = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP); |
1048 | 0 | UA_NodeId certGroupId = certificateGroupId; |
1049 | 0 | if(UA_NodeId_isNull(&certGroupId)) { |
1050 | | /* Use default value if argument is empty */ |
1051 | 0 | certGroupId = defaultApplicationGroup; |
1052 | 0 | } |
1053 | | /* The server currently only supports the DefaultApplicationGroup */ |
1054 | 0 | if(!UA_NodeId_equal(&certGroupId, &defaultApplicationGroup)) |
1055 | 0 | return UA_STATUSCODE_BADINVALIDARGUMENT; |
1056 | | |
1057 | | /* The server currently only supports RSA CertificateType */ |
1058 | 0 | UA_NodeId rsaShaCertificateType = UA_NODEID_NUMERIC(0, UA_NS0ID_RSASHA256APPLICATIONCERTIFICATETYPE); |
1059 | 0 | UA_NodeId rsaMinCertificateType = UA_NODEID_NUMERIC(0,UA_NS0ID_RSAMINAPPLICATIONCERTIFICATETYPE); |
1060 | 0 | if(!UA_NodeId_equal(&certificateTypeId, &rsaShaCertificateType) && |
1061 | 0 | !UA_NodeId_equal(&certificateTypeId, &rsaMinCertificateType)) |
1062 | 0 | return UA_STATUSCODE_BADINVALIDARGUMENT; |
1063 | | |
1064 | 0 | UA_CertificateGroup certGroup = server->config.secureChannelPKI; |
1065 | |
|
1066 | 0 | if(!UA_NodeId_equal(&certGroup.certificateGroupId, &defaultApplicationGroup)) |
1067 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
1068 | | |
1069 | 0 | UA_ByteString *newPrivateKey = NULL; |
1070 | 0 | if(regenerateKey && *regenerateKey == true) |
1071 | 0 | newPrivateKey = UA_ByteString_new(); |
1072 | |
|
1073 | 0 | for(size_t i = 0; i < server->config.endpointsSize; i++) { |
1074 | 0 | UA_SecurityPolicy *sp = |
1075 | 0 | getSecurityPolicyByUri(server, &server->config.endpoints[i].securityPolicyUri); |
1076 | 0 | if(!sp) { |
1077 | 0 | retval = UA_STATUSCODE_BADINTERNALERROR; |
1078 | 0 | goto cleanup; |
1079 | 0 | } |
1080 | | |
1081 | 0 | if(sp->policyType == UA_SECURITYPOLICYTYPE_NONE) |
1082 | 0 | continue; |
1083 | | |
1084 | 0 | if(UA_NodeId_equal(&certificateTypeId, &sp->certificateTypeId) && |
1085 | 0 | UA_NodeId_equal(&certGroupId, &sp->certificateGroupId)) { |
1086 | 0 | retval = sp->createSigningRequest(sp, subjectName, nonce, |
1087 | 0 | &UA_KEYVALUEMAP_NULL, csr, newPrivateKey); |
1088 | 0 | if(retval != UA_STATUSCODE_GOOD) |
1089 | 0 | goto cleanup; |
1090 | 0 | } |
1091 | 0 | } |
1092 | | |
1093 | 0 | UA_ByteString_clear(&server->gdsManager.transaction.localCsrCertificate); |
1094 | 0 | UA_ByteString_copy(csr, &server->gdsManager.transaction.localCsrCertificate); |
1095 | |
|
1096 | 0 | cleanup: |
1097 | 0 | if(newPrivateKey) |
1098 | 0 | { |
1099 | | /* wipe private key before freeing its memory */ |
1100 | 0 | UA_ByteString_memZero(newPrivateKey); |
1101 | 0 | UA_ByteString_delete(newPrivateKey); |
1102 | 0 | } |
1103 | |
|
1104 | 0 | return retval; |
1105 | 0 | } |
1106 | | |
1107 | | /***************************/ |
1108 | | /* Server lookup functions */ |
1109 | | /***************************/ |
1110 | | |
1111 | | UA_SecurityPolicy * |
1112 | 10.9k | getSecurityPolicyByUri(const UA_Server *server, const UA_String *securityPolicyUri) { |
1113 | 10.9k | for(size_t i = 0; i < server->config.securityPoliciesSize; i++) { |
1114 | 10.9k | UA_SecurityPolicy *sp = &server->config.securityPolicies[i]; |
1115 | 10.9k | if(UA_String_equal(securityPolicyUri, &sp->policyUri)) |
1116 | 10.9k | return sp; |
1117 | 10.9k | } |
1118 | 0 | return NULL; |
1119 | 10.9k | } |
1120 | | |
1121 | | UA_SecurityPolicy * |
1122 | 0 | getSecurityPolicyByPostfix(const UA_Server *server, const UA_String uriPostfix) { |
1123 | 0 | for(size_t i = 0; i < server->config.securityPoliciesSize; i++) { |
1124 | 0 | UA_SecurityPolicy *sp = &server->config.securityPolicies[i]; |
1125 | 0 | UA_String spPostfix = securityPolicyUriPostfix(sp->policyUri); |
1126 | 0 | if(UA_String_equal(&uriPostfix, &spPostfix)) |
1127 | 0 | return sp; |
1128 | 0 | } |
1129 | 0 | return NULL; |
1130 | 0 | } |
1131 | | |
1132 | | /* The local ApplicationUri has to match the certificates of the |
1133 | | * SecurityPolicies */ |
1134 | | static void |
1135 | 536 | verifyServerApplicationUri(const UA_Server *server) { |
1136 | | #if UA_LOGLEVEL <= 400 |
1137 | | const UA_ServerConfig *sc = &server->config; |
1138 | | for(size_t i = 0; i < sc->securityPoliciesSize; i++) { |
1139 | | UA_SecurityPolicy *sp = &sc->securityPolicies[i]; |
1140 | | if(sp->policyType == UA_SECURITYPOLICYTYPE_NONE && |
1141 | | sp->localCertificate.length == 0) |
1142 | | continue; |
1143 | | UA_StatusCode retval = |
1144 | | UA_CertificateUtils_verifyApplicationUri(&sp->localCertificate, |
1145 | | &sc->applicationDescription.applicationUri); |
1146 | | if(retval != UA_STATUSCODE_GOOD) { |
1147 | | UA_LOG_WARNING(sc->logging, UA_LOGCATEGORY_SERVER, |
1148 | | "The ApplicationUri %S in the server's ApplicationDescription " |
1149 | | "does not match the URI specified in the certificate " |
1150 | | "for the SecurityPolicy %S", |
1151 | | server->config.applicationDescription.applicationUri, |
1152 | | sp->policyUri); |
1153 | | } |
1154 | | } |
1155 | | #endif |
1156 | 536 | } |
1157 | | |
1158 | | UA_ServerStatistics |
1159 | 0 | UA_Server_getStatistics(UA_Server *server) { |
1160 | 0 | UA_ServerStatistics stat; |
1161 | 0 | lockServer(server); |
1162 | 0 | stat.scs = server->secureChannelStatistics; |
1163 | 0 | UA_ServerDiagnosticsSummaryDataType *sds = &server->serverDiagnosticsSummary; |
1164 | 0 | stat.ss.currentSessionCount = server->activeSessionCount; |
1165 | 0 | stat.ss.cumulatedSessionCount = sds->cumulatedSessionCount; |
1166 | 0 | stat.ss.securityRejectedSessionCount = sds->securityRejectedSessionCount; |
1167 | 0 | stat.ss.rejectedSessionCount = sds->rejectedSessionCount; |
1168 | 0 | stat.ss.sessionTimeoutCount = sds->sessionTimeoutCount; |
1169 | 0 | stat.ss.sessionAbortCount = sds->sessionAbortCount; |
1170 | 0 | unlockServer(server); |
1171 | 0 | return stat; |
1172 | 0 | } |
1173 | | |
1174 | | /********************/ |
1175 | | /* Main Server Loop */ |
1176 | | /********************/ |
1177 | | |
1178 | 476 | #define UA_MAXTIMEOUT 500 /* Max timeout in ms between main-loop iterations */ |
1179 | | |
1180 | | void |
1181 | 816 | setServerLifecycleState(UA_Server *server, UA_LifecycleState state) { |
1182 | 816 | UA_LOCK_ASSERT(&server->serviceMutex); |
1183 | | |
1184 | | /* Not state change, nothing to do */ |
1185 | 816 | if(server->state == state) |
1186 | 0 | return; |
1187 | | |
1188 | 816 | server->state = state; /* Apply the state change */ |
1189 | | |
1190 | | /* Call the application notification callback */ |
1191 | 816 | UA_ServerConfig *config = &server->config; |
1192 | 816 | if(config->globalNotificationCallback || config->lifecycleNotificationCallback) { |
1193 | 0 | UA_ApplicationNotificationType nt = UA_APPLICATIONNOTIFICATIONTYPE_LIFECYCLE_STARTED; |
1194 | 0 | switch(state) { |
1195 | 0 | case UA_LIFECYCLESTATE_STOPPED: nt = UA_APPLICATIONNOTIFICATIONTYPE_LIFECYCLE_STOPPED; break; |
1196 | 0 | case UA_LIFECYCLESTATE_STOPPING: nt = UA_APPLICATIONNOTIFICATIONTYPE_LIFECYCLE_STOPPING; break; |
1197 | 0 | default: break; |
1198 | 0 | } |
1199 | 0 | if(config->lifecycleNotificationCallback) |
1200 | 0 | config->lifecycleNotificationCallback(server, nt, UA_KEYVALUEMAP_NULL); |
1201 | 0 | if(config->globalNotificationCallback) |
1202 | 0 | config->globalNotificationCallback(server, nt, UA_KEYVALUEMAP_NULL); |
1203 | 0 | } |
1204 | | |
1205 | | /* Call the (legacy) notification callback */ |
1206 | 816 | if(server->config.notifyLifecycleState) |
1207 | 0 | server->config.notifyLifecycleState(server, state); |
1208 | 816 | } |
1209 | | |
1210 | | UA_LifecycleState |
1211 | 0 | UA_Server_getLifecycleState(UA_Server *server) { |
1212 | 0 | return server->state; |
1213 | 0 | } |
1214 | | |
1215 | | /* Start: Spin up the workers and the network layer and sample the server's |
1216 | | * start time. |
1217 | | * Iterate: Process repeated callbacks and events in the network layer. This |
1218 | | * part can be driven from an external main-loop in an event-driven |
1219 | | * single-threaded architecture. |
1220 | | * Stop: Stop workers, finish all callbacks, stop the network layer, clean up */ |
1221 | | |
1222 | | UA_StatusCode |
1223 | 272 | UA_Server_run_startup(UA_Server *server) { |
1224 | 272 | if(server == NULL) { |
1225 | 0 | return UA_STATUSCODE_BADINVALIDARGUMENT; |
1226 | 0 | } |
1227 | 272 | UA_ServerConfig *config = &server->config; |
1228 | | |
1229 | 272 | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
1230 | | /* Prominently warn user that fuzzing build is enabled. This will tamper |
1231 | | * with authentication tokens and other important variables E.g. if fuzzing |
1232 | | * is enabled, and two clients are connected, subscriptions do not work |
1233 | | * properly, since the tokens will be overridden to allow easier fuzzing. */ |
1234 | 272 | UA_LOG_FATAL(server->config.logging, UA_LOGCATEGORY_SERVER, |
1235 | 272 | "Server was built with unsafe fuzzing mode. " |
1236 | 272 | "This should only be used for specific fuzzing builds."); |
1237 | 272 | #endif |
1238 | | |
1239 | 272 | if(server->state != UA_LIFECYCLESTATE_STOPPED) { |
1240 | 0 | UA_LOG_WARNING(config->logging, UA_LOGCATEGORY_SERVER, |
1241 | 0 | "The server has already been started"); |
1242 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
1243 | 0 | } |
1244 | | |
1245 | | /* Check if UserIdentityTokens are defined */ |
1246 | 272 | bool hasUserIdentityTokens = false; |
1247 | 544 | for(size_t i = 0; i < config->endpointsSize; i++) { |
1248 | 272 | if(config->endpoints[i].userIdentityTokensSize > 0) { |
1249 | 0 | hasUserIdentityTokens = true; |
1250 | 0 | break; |
1251 | 0 | } |
1252 | 272 | } |
1253 | 272 | if(config->accessControl.userTokenPoliciesSize == 0 && hasUserIdentityTokens == false) { |
1254 | 0 | UA_LOG_ERROR(config->logging, UA_LOGCATEGORY_SERVER, |
1255 | 0 | "The server has no userIdentificationPolicies defined."); |
1256 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
1257 | 0 | } |
1258 | | |
1259 | | /* Start the EventLoop if not already started */ |
1260 | 272 | UA_StatusCode retVal = UA_STATUSCODE_GOOD; |
1261 | 272 | UA_EventLoop *el = config->eventLoop; |
1262 | 272 | UA_CHECK_MEM_ERROR(el, return UA_STATUSCODE_BADINTERNALERROR, |
1263 | 272 | config->logging, UA_LOGCATEGORY_SERVER, |
1264 | 272 | "An EventLoop must be configured"); |
1265 | | |
1266 | 272 | if(el->state != UA_EVENTLOOPSTATE_STARTED) { |
1267 | 0 | retVal = el->start(el); |
1268 | 0 | UA_CHECK_STATUS(retVal, return retVal); /* Errors are logged internally */ |
1269 | 0 | } |
1270 | | |
1271 | | /* Take the server lock */ |
1272 | 272 | lockServer(server); |
1273 | | |
1274 | | /* Does the ApplicationUri match the local certificates? */ |
1275 | 272 | verifyServerApplicationUri(server); |
1276 | | |
1277 | 272 | #if UA_MULTITHREADING >= 100 |
1278 | | /* Add regulare callback for async operation processing */ |
1279 | 272 | UA_AsyncManager_start(&server->asyncManager, server); |
1280 | 272 | #endif |
1281 | | |
1282 | | /* Are there enough SecureChannels possible for the max number of sessions? */ |
1283 | 272 | if(config->maxSecureChannels != 0 && |
1284 | 272 | (config->maxSessions == 0 || config->maxSessions > config->maxSecureChannels)) { |
1285 | 0 | UA_LOG_WARNING(config->logging, UA_LOGCATEGORY_SERVER, |
1286 | 0 | "Maximum SecureChannels count not enough for the " |
1287 | 0 | "maximum Sessions count"); |
1288 | 0 | } |
1289 | | |
1290 | | /* Add a regular callback for housekeeping tasks. With a 1s interval. */ |
1291 | 272 | retVal = addRepeatedCallback(server, serverHouseKeeping, |
1292 | 272 | NULL, 1000.0, &server->houseKeepingCallbackId); |
1293 | 272 | UA_CHECK_STATUS_ERROR(retVal, unlockServer(server); return retVal, |
1294 | 272 | config->logging, UA_LOGCATEGORY_SERVER, |
1295 | 272 | "Could not create the server housekeeping task"); |
1296 | | |
1297 | | /* Ensure that the uri for ns1 is set up from the app description */ |
1298 | 272 | UA_String_clear(&server->namespaces[1]); |
1299 | 272 | setupNs1Uri(server); |
1300 | | |
1301 | | /* At least one endpoint has to be configured */ |
1302 | 272 | if(config->endpointsSize == 0) { |
1303 | 0 | UA_LOG_WARNING(config->logging, UA_LOGCATEGORY_SERVER, |
1304 | 0 | "There has to be at least one endpoint."); |
1305 | 0 | } |
1306 | | |
1307 | | /* Update Endpoint description */ |
1308 | 544 | for(size_t i = 0; i < config->endpointsSize; ++i) { |
1309 | 272 | UA_ApplicationDescription_clear(&config->endpoints[i].server); |
1310 | 272 | UA_ApplicationDescription_copy(&config->applicationDescription, |
1311 | 272 | &config->endpoints[i].server); |
1312 | 272 | } |
1313 | | |
1314 | | /* Write ServerArray with same ApplicationUri value as NamespaceArray */ |
1315 | 272 | UA_Variant var; |
1316 | 272 | UA_Variant_init(&var); |
1317 | 272 | UA_Variant_setArray(&var, &config->applicationDescription.applicationUri, |
1318 | 272 | 1, &UA_TYPES[UA_TYPES_STRING]); |
1319 | 272 | UA_NodeId serverArray = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERARRAY); |
1320 | 272 | writeValueAttribute(server, serverArray, &var); |
1321 | | |
1322 | | /* Sample the start time and set it to the Server object */ |
1323 | 272 | server->startTime = el->dateTime_now(el); |
1324 | 272 | UA_Variant_init(&var); |
1325 | 272 | UA_Variant_setScalar(&var, &server->startTime, &UA_TYPES[UA_TYPES_DATETIME]); |
1326 | 272 | UA_NodeId startTime = |
1327 | 272 | UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STARTTIME); |
1328 | 272 | writeValueAttribute(server, startTime, &var); |
1329 | | |
1330 | | /* Start all ServerComponents */ |
1331 | 272 | ZIP_ITER(UA_ServerComponentTree, &server->serverComponents, |
1332 | 272 | startServerComponent, server); |
1333 | | |
1334 | | /* Check that the binary protocol support component have been started */ |
1335 | 272 | UA_ServerComponent *binaryProtocolManager = |
1336 | 272 | getServerComponentByName(server, UA_STRING("binary")); |
1337 | 272 | if(!binaryProtocolManager) { |
1338 | 0 | UA_LOG_ERROR(config->logging, UA_LOGCATEGORY_SERVER, |
1339 | 0 | "Binary protocol support component not found."); |
1340 | | /* Stop all server components that have already been started */ |
1341 | 0 | ZIP_ITER(UA_ServerComponentTree, &server->serverComponents, |
1342 | 0 | stopServerComponent, server); |
1343 | 0 | unlockServer(server); |
1344 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
1345 | 0 | } |
1346 | 272 | if(binaryProtocolManager->state != UA_LIFECYCLESTATE_STARTED) { |
1347 | 0 | UA_LOG_ERROR(config->logging, UA_LOGCATEGORY_SERVER, |
1348 | 0 | "The binary protocol support component could not been started."); |
1349 | | /* Stop all server components that have already been started */ |
1350 | 0 | ZIP_ITER(UA_ServerComponentTree, &server->serverComponents, |
1351 | 0 | stopServerComponent, NULL); |
1352 | 0 | unlockServer(server); |
1353 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
1354 | 0 | } |
1355 | | |
1356 | | /* Set the server to STARTED. From here on, only use |
1357 | | * UA_Server_run_shutdown(server) to stop the server. */ |
1358 | 272 | setServerLifecycleState(server, UA_LIFECYCLESTATE_STARTED); |
1359 | | |
1360 | 272 | unlockServer(server); |
1361 | 272 | return UA_STATUSCODE_GOOD; |
1362 | 272 | } |
1363 | | |
1364 | | UA_UInt16 |
1365 | 1.59k | UA_Server_run_iterate(UA_Server *server, UA_Boolean waitInternal) { |
1366 | | /* Make sure an EventLoop is configured */ |
1367 | 1.59k | UA_EventLoop *el = server->config.eventLoop; |
1368 | 1.59k | if(!el) |
1369 | 0 | return 0; |
1370 | | |
1371 | | /* Process timed and network events in the EventLoop */ |
1372 | 1.59k | UA_UInt32 timeout = (waitInternal) ? UA_MAXTIMEOUT : 0; |
1373 | 1.59k | el->run(el, timeout); |
1374 | | |
1375 | | /* Return the time until the next scheduled callback */ |
1376 | 1.59k | UA_DateTime now = el->dateTime_nowMonotonic(el); |
1377 | 1.59k | UA_DateTime nextTimeout = (el->nextTimer(el) - now) / UA_DATETIME_MSEC; |
1378 | 1.59k | if(nextTimeout < 0) |
1379 | 0 | nextTimeout = 0; |
1380 | 1.59k | if(nextTimeout > UA_UINT16_MAX) |
1381 | 272 | nextTimeout = UA_UINT16_MAX; |
1382 | 1.59k | return (UA_UInt16)nextTimeout; |
1383 | 1.59k | } |
1384 | | |
1385 | | static UA_Boolean |
1386 | 0 | testShutdownCondition(UA_Server *server) { |
1387 | | /* Was there a wait time until the shutdown configured? */ |
1388 | 0 | if(server->endTime == 0) |
1389 | 0 | return false; |
1390 | 0 | UA_EventLoop *el = server->config.eventLoop; |
1391 | 0 | return (el->dateTime_now(el) > server->endTime); |
1392 | 0 | } |
1393 | | |
1394 | | static UA_Boolean |
1395 | 544 | testStoppedCondition(UA_Server *server) { |
1396 | | /* Check if there are remaining server components that did not fully stop */ |
1397 | 544 | if(ZIP_ITER(UA_ServerComponentTree, &server->serverComponents, |
1398 | 544 | checkServerComponent, NULL) != NULL) |
1399 | 272 | return false; |
1400 | 272 | return true; |
1401 | 544 | } |
1402 | | |
1403 | | UA_StatusCode |
1404 | 272 | UA_Server_run_shutdown(UA_Server *server) { |
1405 | 272 | if(server == NULL) |
1406 | 0 | return UA_STATUSCODE_BADINVALIDARGUMENT; |
1407 | | |
1408 | 272 | lockServer(server); |
1409 | | |
1410 | 272 | if(server->state != UA_LIFECYCLESTATE_STARTED) { |
1411 | 0 | UA_LOG_ERROR(server->config.logging, UA_LOGCATEGORY_SERVER, |
1412 | 0 | "The server is not started, cannot be shut down"); |
1413 | 0 | unlockServer(server); |
1414 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
1415 | 0 | } |
1416 | | |
1417 | | /* Set to stopping and notify the application */ |
1418 | 272 | setServerLifecycleState(server, UA_LIFECYCLESTATE_STOPPING); |
1419 | | |
1420 | 272 | #if UA_MULTITHREADING >= 100 |
1421 | | /* Stop regular callback for async operation processing */ |
1422 | 272 | UA_AsyncManager_stop(&server->asyncManager, server); |
1423 | 272 | #endif |
1424 | | |
1425 | | /* Stop the regular housekeeping tasks */ |
1426 | 272 | if(server->houseKeepingCallbackId != 0) { |
1427 | 272 | removeCallback(server, server->houseKeepingCallbackId); |
1428 | 272 | server->houseKeepingCallbackId = 0; |
1429 | 272 | } |
1430 | | |
1431 | | /* Stop all ServerComponents */ |
1432 | 272 | ZIP_ITER(UA_ServerComponentTree, &server->serverComponents, |
1433 | 272 | stopServerComponent, NULL); |
1434 | | |
1435 | | /* Are we already stopped? */ |
1436 | 272 | if(testStoppedCondition(server)) { |
1437 | 0 | setServerLifecycleState(server, UA_LIFECYCLESTATE_STOPPED); |
1438 | 0 | } |
1439 | | |
1440 | | /* Only stop the EventLoop if it is coupled to the server lifecycle */ |
1441 | 272 | if(server->config.externalEventLoop) { |
1442 | 0 | unlockServer(server); |
1443 | 0 | return UA_STATUSCODE_GOOD; |
1444 | 0 | } |
1445 | | |
1446 | | /* Unlock and do one "normal" iteration. This allows threads waiting for the |
1447 | | * server lock to proceed before the server lock is destroyed. */ |
1448 | 272 | unlockServer(server); |
1449 | 272 | UA_Server_run_iterate(server, true); |
1450 | 272 | lockServer(server); |
1451 | | |
1452 | | /* Iterate the EventLoop until the server is stopped */ |
1453 | 272 | UA_StatusCode res = UA_STATUSCODE_GOOD; |
1454 | 272 | UA_EventLoop *el = server->config.eventLoop; |
1455 | 272 | while(!testStoppedCondition(server) && |
1456 | 0 | res == UA_STATUSCODE_GOOD) { |
1457 | | /* Event-loop callbacks on other threads may need the server lock. */ |
1458 | 0 | unlockServer(server); |
1459 | 0 | res = el->run(el, 100); |
1460 | 0 | lockServer(server); |
1461 | 0 | } |
1462 | | |
1463 | | /* Stop the EventLoop. Iterate until stopped. */ |
1464 | 272 | el->stop(el); |
1465 | 272 | while(el->state != UA_EVENTLOOPSTATE_STOPPED && |
1466 | 0 | el->state != UA_EVENTLOOPSTATE_FRESH && |
1467 | 0 | res == UA_STATUSCODE_GOOD) { |
1468 | | /* Event-loop callbacks on other threads may need the server lock. */ |
1469 | 0 | unlockServer(server); |
1470 | 0 | res = el->run(el, 100); |
1471 | 0 | lockServer(server); |
1472 | 0 | } |
1473 | | |
1474 | | /* Set server lifecycle state to stopped if not already the case */ |
1475 | 272 | setServerLifecycleState(server, UA_LIFECYCLESTATE_STOPPED); |
1476 | | |
1477 | 272 | unlockServer(server); |
1478 | 272 | return res; |
1479 | 272 | } |
1480 | | |
1481 | | UA_StatusCode |
1482 | 0 | UA_Server_run(UA_Server *server, const volatile UA_Boolean *running) { |
1483 | 0 | UA_StatusCode retval = UA_Server_run_startup(server); |
1484 | 0 | UA_CHECK_STATUS(retval, return retval); |
1485 | | |
1486 | 0 | while(!testShutdownCondition(server)) { |
1487 | 0 | UA_Server_run_iterate(server, true); |
1488 | 0 | if(!*running) { |
1489 | 0 | if(setServerShutdown(server)) |
1490 | 0 | break; |
1491 | 0 | } |
1492 | 0 | } |
1493 | 0 | return UA_Server_run_shutdown(server); |
1494 | 0 | } |
1495 | | |
1496 | 43.6M | void lockServer(UA_Server *server) { |
1497 | 43.6M | if(UA_LIKELY(server->config.eventLoop && server->config.eventLoop->lock)) |
1498 | 43.6M | server->config.eventLoop->lock(server->config.eventLoop); |
1499 | 43.6M | UA_LOCK(&server->serviceMutex); |
1500 | 43.6M | } |
1501 | | |
1502 | 43.6M | void unlockServer(UA_Server *server) { |
1503 | 43.6M | if(UA_LIKELY(server->config.eventLoop && server->config.eventLoop->unlock)) |
1504 | 43.6M | server->config.eventLoop->unlock(server->config.eventLoop); |
1505 | 43.6M | UA_UNLOCK(&server->serviceMutex); |
1506 | 43.6M | } |