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