/src/open62541_15/src/server/ua_server_utils.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 2016-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) |
6 | | * Copyright 2016 (c) Lorenz Haas |
7 | | * Copyright 2017 (c) frax2222 |
8 | | * Copyright 2017 (c) Florian Palm |
9 | | * Copyright 2017-2018 (c) Stefan Profanter, fortiss GmbH |
10 | | * Copyright 2017 (c) Julian Grothoff |
11 | | */ |
12 | | |
13 | | #include "ua_server_internal.h" |
14 | | |
15 | | /****************************/ |
16 | | /* Custom DataType Handling */ |
17 | | /****************************/ |
18 | | |
19 | | const UA_DataTypeArray * |
20 | 3.18M | serverCustomTypes(UA_Server *server) { |
21 | 3.18M | if(server->customTypes_internalSize == 0) |
22 | 3.18M | return server->config.customDataTypes; |
23 | 0 | server->customTypes_internal[server->customTypes_internalSize-1].next = server->config.customDataTypes; |
24 | 0 | return server->customTypes_internal; |
25 | 3.18M | } |
26 | | |
27 | | const UA_DataTypeArray * |
28 | 1.53M | UA_Server_getDataTypes(UA_Server *server) { |
29 | 1.53M | lockServer(server); |
30 | 1.53M | const UA_DataTypeArray * out = serverCustomTypes(server); |
31 | 1.53M | unlockServer(server); |
32 | 1.53M | return out; |
33 | 1.53M | } |
34 | | |
35 | | const UA_DataType * |
36 | 1.60M | UA_Server_findDataType(UA_Server *server, const UA_NodeId *typeId) { |
37 | 1.60M | return UA_findDataTypeWithCustom(typeId, serverCustomTypes(server)); |
38 | 1.60M | } |
39 | | |
40 | | /* DataTypes need a stable pointer. So we allocate an array of 64 datatypes. |
41 | | * When that is full we move the server->customTypes_internal into a |
42 | | * heap-structure that gets cleaned up with the server lifecycle. */ |
43 | | static UA_StatusCode |
44 | 0 | addDataType(UA_Server *server, UA_DataType *dt) { |
45 | | /* Allocate the space for the new DataType */ |
46 | 0 | #define TYPES_LIST_SIZE 64 |
47 | 0 | UA_DataTypeArray *current = NULL; |
48 | 0 | if(server->customTypes_internalSize > 0) |
49 | 0 | current = &server->customTypes_internal[server->customTypes_internalSize-1]; |
50 | 0 | if(!current || current->typesSize == TYPES_LIST_SIZE) { |
51 | | /* Increase the list-of-lists size */ |
52 | 0 | UA_DataTypeArray *lol = (UA_DataTypeArray*) |
53 | 0 | UA_realloc(server->customTypes_internal, |
54 | 0 | sizeof(UA_DataTypeArray) * (server->customTypes_internalSize+1)); |
55 | 0 | if(!lol) |
56 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
57 | 0 | memset(&lol[server->customTypes_internalSize], 0, sizeof(UA_DataTypeArray)); |
58 | 0 | server->customTypes_internal = lol; |
59 | 0 | server->customTypes_internalSize++; |
60 | | |
61 | | /* Update the next-pointers for the internal DataTypeArray */ |
62 | 0 | for(size_t i = 0; i < server->customTypes_internalSize-1; i++) |
63 | 0 | lol[i].next = &lol[i+1]; |
64 | | |
65 | | /* Add a new types list. With the space for the datatypes already appended */ |
66 | 0 | current = &server->customTypes_internal[server->customTypes_internalSize-1]; |
67 | 0 | current->types = (UA_DataType*)UA_calloc(TYPES_LIST_SIZE, sizeof(UA_DataType)); |
68 | 0 | current->typesSize = 0; |
69 | 0 | if(!current->types) { |
70 | 0 | server->customTypes_internalSize--; |
71 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
72 | 0 | } |
73 | 0 | } |
74 | | |
75 | | /* Move the datatype into the stable location in the server. Repair |
76 | | * self-referential members because their source pointer is about to go |
77 | | * out of scope. */ |
78 | 0 | UA_DataType *target = ¤t->types[current->typesSize]; |
79 | 0 | *target = *dt; |
80 | 0 | for(size_t i = 0; i < target->membersSize; i++) { |
81 | 0 | if(target->members[i].memberType == dt) |
82 | 0 | target->members[i].memberType = target; |
83 | 0 | } |
84 | 0 | current->typesSize++; |
85 | 0 | return UA_STATUSCODE_GOOD; |
86 | 0 | } |
87 | | |
88 | | UA_StatusCode |
89 | | UA_Server_addDataType(UA_Server *server, const UA_NodeId parentNodeId, |
90 | 0 | const UA_DataType *type) { |
91 | | /* Check that the type does not already exist. We do not allow changes to |
92 | | * DataTypes once they are set. */ |
93 | 0 | if(UA_Server_findDataType(server, &type->typeId)) |
94 | 0 | return UA_STATUSCODE_BADNODEIDEXISTS; |
95 | | |
96 | | /* Make a copy of the UA_DataType */ |
97 | 0 | UA_DataType dt2; |
98 | 0 | UA_StatusCode res = UA_DataType_copy(type, &dt2); |
99 | 0 | if(res != UA_STATUSCODE_GOOD) |
100 | 0 | return res; |
101 | | |
102 | | /* Add the UA_DataType to the server */ |
103 | 0 | res = addDataType(server, &dt2); |
104 | 0 | if(res != UA_STATUSCODE_GOOD) |
105 | 0 | UA_DataType_clear(&dt2); |
106 | 0 | return res; |
107 | 0 | } |
108 | | |
109 | | #ifdef UA_TYPES_STRUCTUREDESCRIPTION |
110 | | /* Keep exact lookups distinct from resolving a simple subtype's storage. |
111 | | * In particular, an unregistered structure must never inherit its parent's |
112 | | * layout implicitly: it may introduce additional fields or encodings. */ |
113 | | static const UA_DataType * |
114 | 0 | resolveDataTypeRepresentation(UA_Server *server, const UA_NodeId *typeId) { |
115 | 0 | lockServer(server); |
116 | 0 | const UA_DataType *type = UA_Server_findDataType(server, typeId); |
117 | 0 | if(type) { |
118 | 0 | unlockServer(server); |
119 | 0 | return type; |
120 | 0 | } |
121 | | |
122 | 0 | RefTree visited; |
123 | 0 | if(RefTree_init(&visited) != UA_STATUSCODE_GOOD) { |
124 | 0 | unlockServer(server); |
125 | 0 | return NULL; |
126 | 0 | } |
127 | | |
128 | 0 | const UA_Node *node = UA_NODESTORE_GET_SELECTIVE( |
129 | 0 | server, typeId, UA_NODEATTRIBUTESMASK_NONE, |
130 | 0 | UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASSUBTYPE), |
131 | 0 | UA_BROWSEDIRECTION_INVERSE); |
132 | 0 | while(node) { |
133 | 0 | UA_Boolean duplicate = false; |
134 | 0 | UA_StatusCode res = RefTree_addNodeId(&visited, &node->head.nodeId, &duplicate); |
135 | 0 | if(res != UA_STATUSCODE_GOOD || duplicate || |
136 | 0 | node->head.nodeClass != UA_NODECLASS_DATATYPE) { |
137 | 0 | UA_NODESTORE_RELEASE(server, node); |
138 | 0 | break; |
139 | 0 | } |
140 | | |
141 | 0 | const UA_DataType *candidate = |
142 | 0 | UA_Server_findDataType(server, &node->head.nodeId); |
143 | 0 | if(candidate) { |
144 | 0 | if(candidate->typeKind <= UA_DATATYPEKIND_DIAGNOSTICINFO && |
145 | 0 | candidate->typeKind != UA_DATATYPEKIND_EXTENSIONOBJECT) |
146 | 0 | type = candidate; |
147 | 0 | UA_NODESTORE_RELEASE(server, node); |
148 | 0 | break; |
149 | 0 | } |
150 | 0 | const UA_Node *parent = |
151 | 0 | getNodeType(server, &node->head, UA_NODEATTRIBUTESMASK_NODEID, |
152 | 0 | UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASSUBTYPE), |
153 | 0 | UA_BROWSEDIRECTION_INVERSE); |
154 | 0 | UA_NODESTORE_RELEASE(server, node); |
155 | 0 | node = parent; |
156 | 0 | } |
157 | 0 | RefTree_clear(&visited); |
158 | 0 | unlockServer(server); |
159 | 0 | return type; |
160 | 0 | } |
161 | | |
162 | | /* Replace an alias NodeId with the concrete datatype that defines its layout. */ |
163 | | static UA_StatusCode |
164 | 0 | normalizeDataTypeId(UA_Server *server, UA_NodeId *typeId) { |
165 | 0 | if(UA_NodeId_isNull(typeId)) |
166 | 0 | return UA_STATUSCODE_GOOD; |
167 | 0 | if(UA_Server_findDataType(server, typeId)) |
168 | 0 | return UA_STATUSCODE_GOOD; |
169 | 0 | const UA_DataType *representation = |
170 | 0 | resolveDataTypeRepresentation(server, typeId); |
171 | 0 | if(!representation) |
172 | 0 | return UA_STATUSCODE_BADNOTFOUND; |
173 | 0 | UA_NodeId_clear(typeId); |
174 | 0 | return UA_NodeId_copy(&representation->typeId, typeId); |
175 | 0 | } |
176 | | |
177 | | #endif |
178 | | |
179 | | UA_StatusCode |
180 | | UA_Server_addDataTypeFromDescription(UA_Server *server, |
181 | 0 | const UA_ExtensionObject *description) { |
182 | 0 | #ifdef UA_TYPES_STRUCTUREDESCRIPTION |
183 | | /* Normalize simple aliases in a temporary copy for memory-layout |
184 | | * construction. */ |
185 | 0 | UA_ExtensionObject layoutDescription; |
186 | 0 | UA_StatusCode res = UA_ExtensionObject_copy(description, &layoutDescription); |
187 | 0 | if(res != UA_STATUSCODE_GOOD) |
188 | 0 | return res; |
189 | 0 | if(UA_ExtensionObject_hasDecodedType( |
190 | 0 | &layoutDescription, &UA_TYPES[UA_TYPES_STRUCTUREDESCRIPTION])) { |
191 | 0 | UA_StructureDescription *structureDescription = |
192 | 0 | (UA_StructureDescription*)layoutDescription.content.decoded.data; |
193 | 0 | UA_StructureDefinition *definition = |
194 | 0 | &structureDescription->structureDefinition; |
195 | 0 | for(size_t i = 0; res == UA_STATUSCODE_GOOD && |
196 | 0 | i < definition->fieldsSize; i++) { |
197 | 0 | if(UA_NodeId_equal(&definition->fields[i].dataType, |
198 | 0 | &structureDescription->dataTypeId)) |
199 | 0 | continue; |
200 | 0 | res = normalizeDataTypeId(server, &definition->fields[i].dataType); |
201 | 0 | } |
202 | 0 | } |
203 | 0 | if(res != UA_STATUSCODE_GOOD) { |
204 | 0 | UA_ExtensionObject_clear(&layoutDescription); |
205 | 0 | return res; |
206 | 0 | } |
207 | | |
208 | | /* Translate into a new UA_DataType */ |
209 | 0 | UA_DataType dt; |
210 | 0 | res = UA_DataType_fromDescription(&dt, &layoutDescription, |
211 | 0 | serverCustomTypes(server)); |
212 | 0 | UA_ExtensionObject_clear(&layoutDescription); |
213 | 0 | if(res != UA_STATUSCODE_GOOD) |
214 | 0 | return res; |
215 | | |
216 | | #else |
217 | | /* Translate into a new UA_DataType */ |
218 | | UA_DataType dt; |
219 | | UA_StatusCode res = |
220 | | UA_DataType_fromDescription(&dt, description, serverCustomTypes(server)); |
221 | | if(res != UA_STATUSCODE_GOOD) |
222 | | return res; |
223 | | |
224 | | #endif |
225 | | |
226 | | /* Check that the type does not already exist. We do not allow changes to |
227 | | * DataTypes once they are set. */ |
228 | 0 | if(UA_Server_findDataType(server, &dt.typeId)) { |
229 | 0 | UA_DataType_clear(&dt); |
230 | 0 | return UA_STATUSCODE_BADNODEIDEXISTS; |
231 | 0 | } |
232 | | |
233 | | /* Add the UA_DataType to the server */ |
234 | 0 | res = addDataType(server, &dt); |
235 | 0 | if(res != UA_STATUSCODE_GOOD) |
236 | 0 | UA_DataType_clear(&dt); |
237 | 0 | return res; |
238 | 0 | } |
239 | | |
240 | | /********************************/ |
241 | | /* Information Model Operations */ |
242 | | /********************************/ |
243 | | |
244 | | struct ReturnTypeContext { |
245 | | UA_Server *server; |
246 | | UA_UInt32 attributeMask; |
247 | | UA_ReferenceTypeSet references; |
248 | | UA_BrowseDirection referenceDirections; |
249 | | }; |
250 | | |
251 | | static void * |
252 | 32.8M | returnFirstType(void *context, UA_ReferenceTarget *t) { |
253 | 32.8M | struct ReturnTypeContext *ctx = (struct ReturnTypeContext*)context; |
254 | | /* Don't release the node that is returned. |
255 | | * Continues to iterate if NULL is returned. */ |
256 | 32.8M | return (void *)(uintptr_t)UA_NODESTORE_GETFROMREF_SELECTIVE( |
257 | 32.8M | ctx->server, t->targetId, ctx->attributeMask, ctx->references, |
258 | 32.8M | ctx->referenceDirections); |
259 | 32.8M | } |
260 | | |
261 | | const UA_Node * |
262 | | getNodeType(UA_Server *server, const UA_NodeHead *head, |
263 | | UA_UInt32 attributeMask, UA_ReferenceTypeSet references, |
264 | 32.9M | UA_BrowseDirection referenceDirections) { |
265 | | /* The reference to the parent is different for variable and variabletype */ |
266 | 32.9M | UA_Byte parentRefIndex; |
267 | 32.9M | UA_Boolean inverse; |
268 | 32.9M | switch(head->nodeClass) { |
269 | 2.92M | case UA_NODECLASS_OBJECT: |
270 | 32.4M | case UA_NODECLASS_VARIABLE: |
271 | 32.4M | parentRefIndex = UA_REFERENCETYPEINDEX_HASTYPEDEFINITION; |
272 | 32.4M | inverse = false; |
273 | 32.4M | break; |
274 | 0 | case UA_NODECLASS_OBJECTTYPE: |
275 | 491k | case UA_NODECLASS_VARIABLETYPE: |
276 | 491k | case UA_NODECLASS_REFERENCETYPE: |
277 | 491k | case UA_NODECLASS_DATATYPE: |
278 | 491k | parentRefIndex = UA_REFERENCETYPEINDEX_HASSUBTYPE; |
279 | 491k | inverse = true; |
280 | 491k | break; |
281 | 0 | default: |
282 | 0 | return NULL; |
283 | 32.9M | } |
284 | | |
285 | 32.9M | struct ReturnTypeContext ctx; |
286 | 32.9M | ctx.server = server; |
287 | 32.9M | ctx.attributeMask = attributeMask; |
288 | 32.9M | ctx.references = references; |
289 | 32.9M | ctx.referenceDirections = referenceDirections; |
290 | | |
291 | | /* Return the first matching candidate */ |
292 | 67.3M | for(size_t i = 0; i < head->referencesSize; ++i) { |
293 | 67.1M | UA_NodeReferenceKind *rk = &head->references[i]; |
294 | 67.1M | if(rk->isInverse != inverse) |
295 | 32.1M | continue; |
296 | 35.0M | if(rk->referenceTypeIndex != parentRefIndex) |
297 | 2.21M | continue; |
298 | 32.8M | const UA_Node *type = (const UA_Node*) |
299 | 32.8M | UA_NodeReferenceKind_iterate(rk, returnFirstType, &ctx); |
300 | 32.8M | if(type) |
301 | 32.8M | return type; |
302 | 32.8M | } |
303 | | |
304 | 135k | return NULL; |
305 | 32.9M | } |
306 | | |
307 | | UA_Boolean |
308 | 19.7k | UA_Node_hasSubTypeOrInstances(const UA_NodeHead *head) { |
309 | 60.6k | for(size_t i = 0; i < head->referencesSize; ++i) { |
310 | 40.9k | if(head->references[i].isInverse == false && |
311 | 23.6k | head->references[i].referenceTypeIndex == UA_REFERENCETYPEINDEX_HASSUBTYPE) |
312 | 67 | return true; |
313 | 40.9k | if(head->references[i].isInverse == true && |
314 | 17.3k | head->references[i].referenceTypeIndex == UA_REFERENCETYPEINDEX_HASTYPEDEFINITION) |
315 | 27 | return true; |
316 | 40.9k | } |
317 | 19.6k | return false; |
318 | 19.7k | } |
319 | | |
320 | | UA_StatusCode |
321 | | getTypeAndInterfaceHierarchy(UA_Server *server, const UA_NodeId *leafNode, |
322 | | UA_Boolean includeLeaf, UA_NodeId **typeHierarchy, |
323 | 13.6M | size_t *typeHierarchySize) { |
324 | 13.6M | UA_ReferenceTypeSet hastype = UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASTYPEDEFINITION); |
325 | 13.6M | UA_ReferenceTypeSet hassubtype = UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASSUBTYPE); |
326 | 13.6M | UA_ReferenceTypeSet hasinterface = UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASINTERFACE); |
327 | | |
328 | | /* Initialize the tree and add the leaf */ |
329 | 13.6M | RefTree rt; |
330 | 13.6M | UA_StatusCode res = RefTree_init(&rt); |
331 | 13.6M | if(res != UA_STATUSCODE_GOOD) |
332 | 0 | return res; |
333 | 13.6M | res = RefTree_addNodeId(&rt, leafNode, NULL); |
334 | 13.6M | if(res != UA_STATUSCODE_GOOD) |
335 | 0 | goto errout; |
336 | | |
337 | | /* Get all types */ |
338 | 13.6M | res = browseRecursiveRefTree(server, &rt, UA_BROWSEDIRECTION_FORWARD, &hastype, |
339 | 13.6M | UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_VARIABLETYPE); |
340 | 13.6M | if(res != UA_STATUSCODE_GOOD) |
341 | 0 | goto errout; |
342 | | |
343 | | /* Get all super types */ |
344 | 13.6M | res = browseRecursiveRefTree(server, &rt, UA_BROWSEDIRECTION_INVERSE, &hassubtype, |
345 | 13.6M | UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_VARIABLETYPE); |
346 | 13.6M | if(res != UA_STATUSCODE_GOOD) |
347 | 0 | goto errout; |
348 | | |
349 | | /* Get all interfaces */ |
350 | 13.6M | res = browseRecursiveRefTree(server, &rt, UA_BROWSEDIRECTION_FORWARD, &hasinterface, |
351 | 13.6M | UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_VARIABLETYPE); |
352 | | |
353 | 13.6M | errout: |
354 | 13.6M | if(res != UA_STATUSCODE_GOOD || rt.size == 0) { |
355 | 0 | RefTree_clear(&rt); |
356 | 0 | return res; |
357 | 0 | } |
358 | | |
359 | | /* Make the array of ExpandedNodeId into an array of NodeId */ |
360 | 13.6M | UA_NodeId *outArray = (UA_NodeId*)rt.targets; |
361 | 13.6M | size_t pos = 0; |
362 | 41.6M | for(size_t i = 0; i < rt.size; i++) { |
363 | 28.0M | UA_NodeId *n = &outArray[pos]; |
364 | 28.0M | UA_ExpandedNodeId *e = &rt.targets[i]; |
365 | 28.0M | if(!UA_ExpandedNodeId_isLocal(e)) { |
366 | 0 | UA_ExpandedNodeId_clear(e); |
367 | 0 | continue; |
368 | 0 | } |
369 | 28.0M | *n = e->nodeId; |
370 | 28.0M | UA_String_clear(&e->namespaceUri); |
371 | 28.0M | pos++; |
372 | 28.0M | } |
373 | | |
374 | 13.6M | *typeHierarchySize = pos; |
375 | 13.6M | *typeHierarchy = outArray; |
376 | 13.6M | return UA_STATUSCODE_GOOD; |
377 | 13.6M | } |
378 | | |
379 | | UA_StatusCode |
380 | | getAllInterfaces(UA_Server *server, const UA_NodeId *objectNode, |
381 | 1.42M | UA_NodeId **interfaceNodes, size_t *interfaceNodesSize) { |
382 | 1.42M | UA_ReferenceTypeSet hastype = UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASTYPEDEFINITION); |
383 | 1.42M | UA_ReferenceTypeSet hassubtype = UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASSUBTYPE); |
384 | 1.42M | UA_ReferenceTypeSet hasinterface = UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASINTERFACE); |
385 | | |
386 | | /* Initialize the tree and add the leaf */ |
387 | 1.42M | size_t beforeInterfaces = 0; |
388 | 1.42M | RefTree rt; |
389 | 1.42M | UA_StatusCode res = RefTree_init(&rt); |
390 | 1.42M | if(res != UA_STATUSCODE_GOOD) |
391 | 0 | return res; |
392 | 1.42M | res = RefTree_addNodeId(&rt, objectNode, NULL); |
393 | 1.42M | if(res != UA_STATUSCODE_GOOD) |
394 | 0 | goto errout; |
395 | | |
396 | | /* Get all types */ |
397 | 1.42M | res = browseRecursiveRefTree(server, &rt, UA_BROWSEDIRECTION_FORWARD, &hastype, |
398 | 1.42M | UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_VARIABLETYPE); |
399 | 1.42M | if(res != UA_STATUSCODE_GOOD) |
400 | 0 | goto errout; |
401 | | |
402 | | /* Get all super types */ |
403 | 1.42M | res = browseRecursiveRefTree(server, &rt, UA_BROWSEDIRECTION_INVERSE, &hassubtype, |
404 | 1.42M | UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_VARIABLETYPE); |
405 | 1.42M | if(res != UA_STATUSCODE_GOOD) |
406 | 0 | goto errout; |
407 | | |
408 | | /* Get all interfaces */ |
409 | 1.42M | beforeInterfaces = rt.size; /* Return only the interfaces */ |
410 | 1.42M | res = browseRecursiveRefTree(server, &rt, UA_BROWSEDIRECTION_FORWARD, &hasinterface, |
411 | 1.42M | UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_VARIABLETYPE); |
412 | | |
413 | 1.42M | errout: |
414 | 1.42M | if(res != UA_STATUSCODE_GOOD || rt.size == 0) { |
415 | 0 | RefTree_clear(&rt); |
416 | 0 | return res; |
417 | 0 | } |
418 | | |
419 | | /* Make the array of ExpandedNodeId into an array of NodeId */ |
420 | 1.42M | UA_NodeId *outArray = (UA_NodeId*)rt.targets; |
421 | 1.42M | size_t pos = 0; |
422 | 5.84M | for(size_t i = 0; i < rt.size; i++) { |
423 | 4.41M | UA_NodeId *n = &outArray[pos]; |
424 | 4.41M | UA_ExpandedNodeId *e = &rt.targets[i]; |
425 | 4.41M | if(i < beforeInterfaces || !UA_ExpandedNodeId_isLocal(e)) { |
426 | 4.41M | UA_ExpandedNodeId_clear(e); |
427 | 4.41M | continue; |
428 | 4.41M | } |
429 | 0 | *n = e->nodeId; |
430 | 0 | UA_String_clear(&e->namespaceUri); |
431 | 0 | pos++; |
432 | 0 | } |
433 | | |
434 | | /* No interfaces found */ |
435 | 1.42M | if(pos == 0) { |
436 | 1.42M | RefTree_clear(&rt); |
437 | 1.42M | outArray = NULL; |
438 | 1.42M | } |
439 | | |
440 | 1.42M | *interfaceNodesSize = pos; |
441 | 1.42M | *interfaceNodes = outArray; |
442 | 1.42M | return UA_STATUSCODE_GOOD; |
443 | 1.42M | } |
444 | | |
445 | | /* Get the node, make the changes and release */ |
446 | | UA_StatusCode |
447 | | editNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId, |
448 | | UA_UInt32 attributeMask, UA_ReferenceTypeSet references, |
449 | | UA_BrowseDirection referenceDirections, |
450 | 23.3M | UA_EditNodeCallback callback, void *data) { |
451 | 23.3M | UA_Node *node = |
452 | 23.3M | UA_NODESTORE_GET_EDIT_SELECTIVE(server, nodeId, attributeMask, |
453 | 23.3M | references, referenceDirections); |
454 | 23.3M | if(!node) |
455 | 19.8k | return UA_STATUSCODE_BADNODEIDUNKNOWN; |
456 | 23.2M | UA_StatusCode retval = callback(server, session, node, data); |
457 | 23.2M | UA_NODESTORE_RELEASE(server, node); |
458 | 23.2M | return retval; |
459 | 23.3M | } |
460 | | |
461 | | /**************************/ |
462 | | /* Certificate Validation */ |
463 | | /**************************/ |
464 | | |
465 | | UA_StatusCode |
466 | | validateCertificate(UA_Server *server, UA_CertificateGroup *cg, |
467 | | const UA_SecurityPolicy *securityPolicy, |
468 | | UA_SecureChannel *channel, UA_Session *session, |
469 | | const char *logPrefix, |
470 | | const UA_ApplicationDescription *ad, |
471 | 0 | const UA_ByteString certificate) { |
472 | | /* Verify the ApplicationUri */ |
473 | 0 | UA_StatusCode res = UA_STATUSCODE_GOOD; |
474 | 0 | if(ad) { |
475 | 0 | res = UA_CertificateUtils_verifyApplicationUri(&certificate, |
476 | 0 | &ad->applicationUri); |
477 | 0 | if(res != UA_STATUSCODE_GOOD) { |
478 | 0 | if(server->config.allowAllCertificateUris <= UA_RULEHANDLING_WARN) { |
479 | 0 | if(session) { |
480 | 0 | UA_LOG_ERROR_SESSION(server->config.logging, session, |
481 | 0 | "%s: The client's ApplicationUri " |
482 | 0 | "could not be verified against the " |
483 | 0 | "ApplicationUri %S from the client's " |
484 | 0 | "ApplicationDescription", logPrefix, |
485 | 0 | ad->applicationUri); |
486 | 0 | } else if(channel) { |
487 | 0 | UA_LOG_ERROR_CHANNEL(server->config.logging, channel, |
488 | 0 | "%s: The client certificate's ApplicationUri " |
489 | 0 | "could not be verified against the " |
490 | 0 | "ApplicationUri %S from the client's " |
491 | 0 | "ApplicationDescription", logPrefix, |
492 | 0 | ad->applicationUri); |
493 | 0 | } else { |
494 | 0 | UA_LOG_ERROR(server->config.logging, UA_LOGCATEGORY_SERVER, |
495 | 0 | "%s: The server certificate's ApplicationUri " |
496 | 0 | "could not be verified against the " |
497 | 0 | "ApplicationUri %S from its " |
498 | 0 | "ApplicationDescription", logPrefix, |
499 | 0 | ad->applicationUri); |
500 | 0 | } |
501 | 0 | } |
502 | 0 | if(server->config.allowAllCertificateUris <= UA_RULEHANDLING_ABORT) { |
503 | 0 | if(res == UA_STATUSCODE_BADCERTIFICATEURIINVALID) { |
504 | 0 | return UA_STATUSCODE_BADCERTIFICATEURIINVALID; |
505 | 0 | } else { |
506 | 0 | return UA_STATUSCODE_BADCERTIFICATEINVALID; |
507 | 0 | } |
508 | 0 | } |
509 | 0 | } |
510 | 0 | } |
511 | | |
512 | | /* The server receives client application and user certificates. RSA |
513 | | * application certificates require clientAuth. For ECC application and user |
514 | | * certificates the extension is optional, but restrictive values apply. */ |
515 | 0 | if(securityPolicy && securityPolicy->policyType != UA_SECURITYPOLICYTYPE_NONE) { |
516 | 0 | UA_Boolean applicationCertificate = |
517 | 0 | (cg == &server->config.secureChannelPKI); |
518 | 0 | UA_Boolean ekuRequired = applicationCertificate && |
519 | 0 | securityPolicy->policyType == UA_SECURITYPOLICYTYPE_RSA; |
520 | 0 | res = UA_CertificateUtils_checkExtendedKeyUsage( |
521 | 0 | &certificate, UA_CERTIFICATEEKU_CLIENTAUTH, ekuRequired); |
522 | 0 | if(res == UA_STATUSCODE_BADCERTIFICATEUSENOTALLOWED) { |
523 | 0 | if(server->config.certificateEkuRule <= UA_RULEHANDLING_WARN) { |
524 | 0 | UA_LOG_WARNING(server->config.logging, UA_LOGCATEGORY_SECURITYPOLICY, |
525 | 0 | "%s: The client certificate does not permit clientAuth", |
526 | 0 | logPrefix); |
527 | 0 | } |
528 | 0 | if(server->config.certificateEkuRule == UA_RULEHANDLING_ABORT) |
529 | 0 | return res; |
530 | 0 | } else if(res != UA_STATUSCODE_GOOD) { |
531 | 0 | return res; |
532 | 0 | } |
533 | 0 | } |
534 | | |
535 | 0 | if(!cg->verifyCertificate) { |
536 | 0 | UA_LOG_ERROR(server->config.logging, UA_LOGCATEGORY_SERVER, |
537 | 0 | "%s: Could not validate the certificate " |
538 | 0 | "as the CertificateGroup is not configured", logPrefix); |
539 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
540 | 0 | } |
541 | | |
542 | | /* Validate in the CertificateGroup */ |
543 | 0 | res = cg->verifyCertificate(cg, &certificate); |
544 | 0 | if(res != UA_STATUSCODE_GOOD) { |
545 | 0 | const char *descr = UA_StatusCode_name(res); |
546 | 0 | if(session) { |
547 | 0 | UA_LOG_ERROR_SESSION(server->config.logging, session, |
548 | 0 | "%s: The client certificate failed the verification " |
549 | 0 | "in the CertificateGroup with StatusCode %s", |
550 | 0 | logPrefix, descr); |
551 | 0 | } else if(channel) { |
552 | 0 | UA_LOG_ERROR_CHANNEL(server->config.logging, channel, |
553 | 0 | "%s: The client certificate failed the verification " |
554 | 0 | "in the CertificateGroup with StatusCode %s", |
555 | 0 | logPrefix, descr); |
556 | 0 | } else { |
557 | 0 | UA_LOG_ERROR(server->config.logging, UA_LOGCATEGORY_SERVER, |
558 | 0 | "%s: The client certificate failed the verification " |
559 | 0 | "in the CertificateGroup with StatusCode %s", |
560 | 0 | logPrefix, descr); |
561 | 0 | } |
562 | 0 | } |
563 | 0 | return res; |
564 | 0 | } |
565 | | |
566 | | /*********************************/ |
567 | | /* Default attribute definitions */ |
568 | | /*********************************/ |
569 | | |
570 | | const UA_ObjectAttributes UA_ObjectAttributes_default = { |
571 | | 0, /* specifiedAttributes */ |
572 | | {{0, NULL}, {0, NULL}}, /* displayName */ |
573 | | {{0, NULL}, {0, NULL}}, /* description */ |
574 | | 0, 0, /* writeMask (userWriteMask) */ |
575 | | 0 /* eventNotifier */ |
576 | | }; |
577 | | |
578 | | const UA_VariableAttributes UA_VariableAttributes_default = { |
579 | | 0, /* specifiedAttributes */ |
580 | | {{0, NULL}, {0, NULL}}, /* displayName */ |
581 | | {{0, NULL}, {0, NULL}}, /* description */ |
582 | | 0, 0, /* writeMask (userWriteMask) */ |
583 | | {NULL, UA_VARIANT_DATA, |
584 | | 0, NULL, 0, NULL}, /* value */ |
585 | | {0, UA_NODEIDTYPE_NUMERIC, |
586 | | {UA_NS0ID_BASEDATATYPE}}, /* dataType */ |
587 | | UA_VALUERANK_ANY, /* valueRank */ |
588 | | 0, NULL, /* arrayDimensions */ |
589 | | UA_ACCESSLEVELMASK_READ | /* accessLevel */ |
590 | | UA_ACCESSLEVELMASK_STATUSWRITE | |
591 | | UA_ACCESSLEVELMASK_TIMESTAMPWRITE, |
592 | | 0, /* userAccessLevel */ |
593 | | 0.0, /* minimumSamplingInterval */ |
594 | | false /* historizing */ |
595 | | }; |
596 | | |
597 | | const UA_MethodAttributes UA_MethodAttributes_default = { |
598 | | 0, /* specifiedAttributes */ |
599 | | {{0, NULL}, {0, NULL}}, /* displayName */ |
600 | | {{0, NULL}, {0, NULL}}, /* description */ |
601 | | 0, 0, /* writeMask (userWriteMask) */ |
602 | | true, true /* executable (userExecutable) */ |
603 | | }; |
604 | | |
605 | | const UA_ObjectTypeAttributes UA_ObjectTypeAttributes_default = { |
606 | | 0, /* specifiedAttributes */ |
607 | | {{0, NULL}, {0, NULL}}, /* displayName */ |
608 | | {{0, NULL}, {0, NULL}}, /* description */ |
609 | | 0, 0, /* writeMask (userWriteMask) */ |
610 | | false /* isAbstract */ |
611 | | }; |
612 | | |
613 | | const UA_VariableTypeAttributes UA_VariableTypeAttributes_default = { |
614 | | 0, /* specifiedAttributes */ |
615 | | {{0, NULL}, {0, NULL}}, /* displayName */ |
616 | | {{0, NULL}, {0, NULL}}, /* description */ |
617 | | 0, 0, /* writeMask (userWriteMask) */ |
618 | | {NULL, UA_VARIANT_DATA, |
619 | | 0, NULL, 0, NULL}, /* value */ |
620 | | {0, UA_NODEIDTYPE_NUMERIC, |
621 | | {UA_NS0ID_BASEDATATYPE}}, /* dataType */ |
622 | | UA_VALUERANK_ANY, /* valueRank */ |
623 | | 0, NULL, /* arrayDimensions */ |
624 | | false /* isAbstract */ |
625 | | }; |
626 | | |
627 | | const UA_ReferenceTypeAttributes UA_ReferenceTypeAttributes_default = { |
628 | | 0, /* specifiedAttributes */ |
629 | | {{0, NULL}, {0, NULL}}, /* displayName */ |
630 | | {{0, NULL}, {0, NULL}}, /* description */ |
631 | | 0, 0, /* writeMask (userWriteMask) */ |
632 | | false, /* isAbstract */ |
633 | | false, /* symmetric */ |
634 | | {{0, NULL}, {0, NULL}} /* inverseName */ |
635 | | }; |
636 | | |
637 | | const UA_DataTypeAttributes UA_DataTypeAttributes_default = { |
638 | | 0, /* specifiedAttributes */ |
639 | | {{0, NULL}, {0, NULL}}, /* displayName */ |
640 | | {{0, NULL}, {0, NULL}}, /* description */ |
641 | | 0, 0, /* writeMask (userWriteMask) */ |
642 | | false /* isAbstract */ |
643 | | }; |
644 | | |
645 | | const UA_ViewAttributes UA_ViewAttributes_default = { |
646 | | 0, /* specifiedAttributes */ |
647 | | {{0, NULL}, {0, NULL}}, /* displayName */ |
648 | | {{0, NULL}, {0, NULL}}, /* description */ |
649 | | 0, 0, /* writeMask (userWriteMask) */ |
650 | | false, /* containsNoLoops */ |
651 | | 0 /* eventNotifier */ |
652 | | }; |