/src/open62541/src/util/ua_util.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, 2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) |
6 | | * Copyright 2014 (c) Florian Palm |
7 | | * Copyright 2017 (c) Stefan Profanter, fortiss GmbH |
8 | | */ |
9 | | |
10 | | /* If UA_ENABLE_INLINABLE_EXPORT is enabled, then this file is the compilation |
11 | | * unit for the generated code from UA_INLINABLE definitions. */ |
12 | | #define UA_INLINABLE_IMPL 1 |
13 | | |
14 | | #include <open62541/types.h> |
15 | | #include <open62541/server.h> |
16 | | #include <open62541/util.h> |
17 | | #include <open62541/common.h> |
18 | | // Not used in this translation unit, but exposes symbols that are used in other translation units |
19 | | #include <open62541/server_config_default.h> |
20 | | #include <open62541/transport_generated.h> |
21 | | |
22 | | #include "ua_util_internal.h" |
23 | | #include "pcg_basic.h" |
24 | | #include "base64.h" |
25 | | #include "itoa.h" |
26 | | #include "../../deps/parse_num.h" |
27 | | #include "../../deps/libc_time.h" |
28 | | |
29 | | static const char * attributeIdNames[28] = { |
30 | | "Invalid", "NodeId", "NodeClass", "BrowseName", "DisplayName", "Description", |
31 | | "WriteMask", "UserWriteMask", "IsAbstract", "Symmetric", "InverseName", |
32 | | "ContainsNoLoops", "EventNotifier", "Value", "DataType", "ValueRank", |
33 | | "ArrayDimensions", "AccessLevel", "UserAccessLevel", "MinimumSamplingInterval", |
34 | | "Historizing", "Executable", "UserExecutable", "DataTypeDefinition", |
35 | | "RolePermissions", "UserRolePermissions", "AccessRestrictions", "AccessLevelEx" |
36 | | }; |
37 | | |
38 | | const char * |
39 | 0 | UA_AttributeId_name(UA_AttributeId attrId) { |
40 | 0 | if(attrId < 0 || attrId > UA_ATTRIBUTEID_ACCESSLEVELEX) |
41 | 0 | return attributeIdNames[0]; |
42 | 0 | return attributeIdNames[attrId]; |
43 | 0 | } |
44 | | |
45 | | /* OR-ing 32 goes from upper-case to lower-case */ |
46 | | UA_AttributeId |
47 | 14 | UA_AttributeId_fromName(const UA_String name) { |
48 | 406 | for(size_t i = 0; i < 28; i++) { |
49 | 392 | if(strlen(attributeIdNames[i]) != name.length) |
50 | 392 | continue; |
51 | 0 | for(size_t j = 0; j < name.length; j++) { |
52 | 0 | if((attributeIdNames[i][j] | 32) != (name.data[j] | 32)) |
53 | 0 | goto next; |
54 | 0 | } |
55 | 0 | return (UA_AttributeId)i; |
56 | 0 | next: |
57 | 0 | continue; |
58 | 0 | } |
59 | 14 | return UA_ATTRIBUTEID_INVALID; |
60 | 14 | } |
61 | | |
62 | | static UA_DataTypeKind |
63 | 462 | typeEquivalence(const UA_DataType *t) { |
64 | 462 | UA_DataTypeKind k = (UA_DataTypeKind)t->typeKind; |
65 | 462 | if(k == UA_DATATYPEKIND_ENUM) |
66 | 0 | return UA_DATATYPEKIND_INT32; |
67 | 462 | return k; |
68 | 462 | } |
69 | | |
70 | | void |
71 | 231 | adjustType(UA_Variant *value, const UA_DataType *targetType) { |
72 | | /* If the value is empty, there is nothing we can do here */ |
73 | 231 | const UA_DataType *type = value->type; |
74 | 231 | if(!type || !targetType) |
75 | 0 | return; |
76 | | |
77 | | /* A string is written to a byte array. the valuerank and array dimensions |
78 | | * are checked later */ |
79 | 231 | if(targetType == &UA_TYPES[UA_TYPES_BYTE] && |
80 | 0 | type == &UA_TYPES[UA_TYPES_BYTESTRING] && |
81 | 0 | UA_Variant_isScalar(value)) { |
82 | 0 | UA_ByteString *str = (UA_ByteString*)value->data; |
83 | 0 | value->type = &UA_TYPES[UA_TYPES_BYTE]; |
84 | 0 | value->arrayLength = str->length; |
85 | 0 | value->data = str->data; |
86 | 0 | return; |
87 | 0 | } |
88 | | |
89 | | /* An enum was sent as an int32, or an opaque type as a bytestring. This |
90 | | * is detected with the typeKind indicating the "true" datatype. */ |
91 | 231 | UA_DataTypeKind te1 = typeEquivalence(targetType); |
92 | 231 | UA_DataTypeKind te2 = typeEquivalence(type); |
93 | 231 | if(te1 == te2 && te1 <= UA_DATATYPEKIND_ENUM) { |
94 | 231 | value->type = targetType; |
95 | 231 | return; |
96 | 231 | } |
97 | | |
98 | | /* Add more possible type adjustments here. What are they? */ |
99 | 231 | } |
100 | | |
101 | | size_t |
102 | 171k | UA_readNumberWithBase(const UA_Byte *buf, size_t buflen, UA_UInt32 *number, UA_Byte base) { |
103 | 171k | UA_assert(buf); |
104 | 171k | UA_assert(number); |
105 | 171k | u32 n = 0; |
106 | 171k | size_t progress = 0; |
107 | | /* read numbers until the end or a non-number character appears */ |
108 | 9.47M | while(progress < buflen) { |
109 | 9.30M | u8 c = buf[progress]; |
110 | 9.30M | if(c >= '0' && c <= '9' && c <= '0' + (base-1)) |
111 | 9.28M | n = (n * base) + c - '0'; |
112 | 13.6k | else if(base > 9 && c >= 'a' && c <= 'z' && c <= 'a' + (base-11)) |
113 | 8.52k | n = (n * base) + c-'a' + 10; |
114 | 5.08k | else if(base > 9 && c >= 'A' && c <= 'Z' && c <= 'A' + (base-11)) |
115 | 4.59k | n = (n * base) + c-'A' + 10; |
116 | 493 | else |
117 | 493 | break; |
118 | 9.29M | ++progress; |
119 | 9.29M | } |
120 | 171k | *number = n; |
121 | 171k | return progress; |
122 | 171k | } |
123 | | |
124 | | size_t |
125 | 149k | UA_readNumber(const UA_Byte *buf, size_t buflen, UA_UInt32 *number) { |
126 | 149k | return UA_readNumberWithBase(buf, buflen, number, 10); |
127 | 149k | } |
128 | | |
129 | 3.55k | #define UA_SCHEMAS_SIZE 4 |
130 | 1.11k | #define UA_ETH_SCHEMA_INDEX 2 |
131 | | |
132 | | static const char* schemas[UA_SCHEMAS_SIZE] = { |
133 | | "opc.tcp://", "opc.udp://", "opc.eth://", "opc.mqtt://" |
134 | | }; |
135 | | |
136 | | UA_StatusCode |
137 | | UA_parseEndpointUrl(const UA_String *endpointUrl, UA_String *outHostname, |
138 | 1.40k | UA_UInt16 *outPort, UA_String *outPath) { |
139 | | /* Url must begin with "opc.tcp://" or opc.udp:// (if pubsub enabled) */ |
140 | 1.40k | if(endpointUrl->length < 11) { |
141 | 8 | return UA_STATUSCODE_BADTCPENDPOINTURLINVALID; |
142 | 8 | } |
143 | | |
144 | | /* Which type of schema is this? */ |
145 | 1.39k | unsigned schemaType = 0; |
146 | 2.15k | for(; schemaType < UA_SCHEMAS_SIZE; schemaType++) { |
147 | 2.06k | if(strncmp((char*)endpointUrl->data, |
148 | 2.06k | schemas[schemaType], |
149 | 2.06k | strlen(schemas[schemaType])) == 0) |
150 | 1.30k | break; |
151 | 2.06k | } |
152 | 1.39k | if(schemaType == UA_SCHEMAS_SIZE) |
153 | 91 | return UA_STATUSCODE_BADTCPENDPOINTURLINVALID; |
154 | | |
155 | | /* Forward the current position until the first colon or slash */ |
156 | 1.30k | size_t start = strlen(schemas[schemaType]); |
157 | 1.30k | size_t curr = start; |
158 | 1.30k | UA_Boolean ipv6 = false; |
159 | 1.30k | if(endpointUrl->length > curr && endpointUrl->data[curr] == '[') { |
160 | | /* IPv6: opc.tcp://[2001:0db8:85a3::8a2e:0370:7334]:1234/path */ |
161 | 10.1M | for(; curr < endpointUrl->length; ++curr) { |
162 | 10.1M | if(endpointUrl->data[curr] == ']') |
163 | 98 | break; |
164 | 10.1M | } |
165 | 116 | if(curr == endpointUrl->length) |
166 | 18 | return UA_STATUSCODE_BADTCPENDPOINTURLINVALID; |
167 | 98 | curr++; |
168 | 98 | ipv6 = true; |
169 | 1.19k | } else { |
170 | | /* IPv4 or hostname: opc.tcp://something.something:1234/path */ |
171 | 1.07M | for(; curr < endpointUrl->length; ++curr) { |
172 | 1.07M | if(endpointUrl->data[curr] == ':' || endpointUrl->data[curr] == '/') |
173 | 1.14k | break; |
174 | 1.07M | } |
175 | 1.19k | } |
176 | | |
177 | | /* Set the hostname */ |
178 | 1.29k | if(ipv6) { |
179 | | /* Skip the ipv6 '[]' container for getaddrinfo() later */ |
180 | 98 | outHostname->data = &endpointUrl->data[start+1]; |
181 | 98 | outHostname->length = curr - (start+2); |
182 | 1.19k | } else { |
183 | 1.19k | outHostname->data = &endpointUrl->data[start]; |
184 | 1.19k | outHostname->length = curr - start; |
185 | 1.19k | } |
186 | | |
187 | | /* Empty string? */ |
188 | 1.29k | if(outHostname->length == 0) |
189 | 1.11k | outHostname->data = NULL; |
190 | | |
191 | | /* Already at the end */ |
192 | 1.29k | if(curr == endpointUrl->length) |
193 | 86 | return UA_STATUSCODE_GOOD; |
194 | | |
195 | | /* Set the port - and for ETH set the VID.PCP postfix in the outpath string. |
196 | | * We have to parse that externally. */ |
197 | 1.20k | if(endpointUrl->data[curr] == ':') { |
198 | 1.11k | if(++curr == endpointUrl->length) |
199 | 2 | return UA_STATUSCODE_BADTCPENDPOINTURLINVALID; |
200 | | |
201 | | /* ETH schema */ |
202 | 1.11k | if(schemaType == UA_ETH_SCHEMA_INDEX) { |
203 | 1 | if(outPath != NULL) { |
204 | 1 | outPath->data = &endpointUrl->data[curr]; |
205 | 1 | outPath->length = endpointUrl->length - curr; |
206 | 1 | } |
207 | 1 | return UA_STATUSCODE_GOOD; |
208 | 1 | } |
209 | | |
210 | 1.11k | u32 largeNum; |
211 | 1.11k | size_t progress = UA_readNumber(&endpointUrl->data[curr], |
212 | 1.11k | endpointUrl->length - curr, &largeNum); |
213 | 1.11k | if(progress == 0 || largeNum > 65535) |
214 | 87 | return UA_STATUSCODE_BADTCPENDPOINTURLINVALID; |
215 | | /* Test if the end of a valid port was reached */ |
216 | 1.02k | curr += progress; |
217 | 1.02k | if(curr == endpointUrl->length || endpointUrl->data[curr] == '/') |
218 | 992 | *outPort = (u16)largeNum; |
219 | 1.02k | if(curr == endpointUrl->length) |
220 | 979 | return UA_STATUSCODE_GOOD; |
221 | 1.02k | } |
222 | | |
223 | | /* Set the path */ |
224 | 135 | UA_assert(curr < endpointUrl->length); |
225 | 135 | if(endpointUrl->data[curr] != '/') |
226 | 63 | return UA_STATUSCODE_BADTCPENDPOINTURLINVALID; |
227 | 72 | if(++curr == endpointUrl->length) |
228 | 13 | return UA_STATUSCODE_GOOD; |
229 | 59 | if(outPath != NULL) { |
230 | 59 | outPath->data = &endpointUrl->data[curr]; |
231 | 59 | outPath->length = endpointUrl->length - curr; |
232 | | |
233 | | /* Remove trailing slash from the path */ |
234 | 59 | if(endpointUrl->data[endpointUrl->length - 1] == '/') |
235 | 2 | outPath->length--; |
236 | | |
237 | | /* Empty string? */ |
238 | 59 | if(outPath->length == 0) |
239 | 2 | outPath->data = NULL; |
240 | 59 | } |
241 | | |
242 | 59 | return UA_STATUSCODE_GOOD; |
243 | 72 | } |
244 | | |
245 | | UA_StatusCode |
246 | | UA_parseEndpointUrlEthernet(const UA_String *endpointUrl, UA_String *target, |
247 | 349 | UA_UInt16 *vid, UA_Byte *pcp) { |
248 | | /* Url must begin with "opc.eth://" */ |
249 | 349 | if(endpointUrl->length < 11) { |
250 | 6 | return UA_STATUSCODE_BADINTERNALERROR; |
251 | 6 | } |
252 | 343 | if(strncmp((char*) endpointUrl->data, "opc.eth://", 10) != 0) { |
253 | 89 | return UA_STATUSCODE_BADINTERNALERROR; |
254 | 89 | } |
255 | | |
256 | | /* Where does the host address end? */ |
257 | 254 | size_t curr = 10; |
258 | 1.10M | for(; curr < endpointUrl->length; ++curr) { |
259 | 1.10M | if(endpointUrl->data[curr] == ':') { |
260 | 239 | break; |
261 | 239 | } |
262 | 1.10M | } |
263 | | |
264 | | /* set host address */ |
265 | 254 | target->data = &endpointUrl->data[10]; |
266 | 254 | target->length = curr - 10; |
267 | 254 | if(curr == endpointUrl->length) { |
268 | 15 | return UA_STATUSCODE_GOOD; |
269 | 15 | } |
270 | | |
271 | | /* Set VLAN */ |
272 | 239 | u32 value = 0; |
273 | 239 | curr++; /* skip ':' */ |
274 | 239 | size_t progress = UA_readNumber(&endpointUrl->data[curr], |
275 | 239 | endpointUrl->length - curr, &value); |
276 | 239 | if(progress == 0 || value > 4096) { |
277 | 82 | return UA_STATUSCODE_BADINTERNALERROR; |
278 | 82 | } |
279 | 157 | curr += progress; |
280 | 157 | if(curr == endpointUrl->length || endpointUrl->data[curr] == '.') { |
281 | 121 | *vid = (UA_UInt16) value; |
282 | 121 | } |
283 | 157 | if(curr == endpointUrl->length) { |
284 | 33 | return UA_STATUSCODE_GOOD; |
285 | 33 | } |
286 | | |
287 | | /* Set priority */ |
288 | 124 | if(endpointUrl->data[curr] != '.') { |
289 | 36 | return UA_STATUSCODE_BADINTERNALERROR; |
290 | 36 | } |
291 | 88 | curr++; /* skip '.' */ |
292 | 88 | progress = UA_readNumber(&endpointUrl->data[curr], |
293 | 88 | endpointUrl->length - curr, &value); |
294 | 88 | if(progress == 0 || value > 7) { |
295 | 59 | return UA_STATUSCODE_BADINTERNALERROR; |
296 | 59 | } |
297 | 29 | curr += progress; |
298 | 29 | if(curr != endpointUrl->length) { |
299 | 24 | return UA_STATUSCODE_BADINTERNALERROR; |
300 | 24 | } |
301 | 5 | *pcp = (UA_Byte) value; |
302 | | |
303 | 5 | return UA_STATUSCODE_GOOD; |
304 | 29 | } |
305 | | |
306 | | UA_StatusCode |
307 | | UA_ByteString_toBase64(const UA_ByteString *byteString, |
308 | 0 | UA_String *str) { |
309 | 0 | UA_String_init(str); |
310 | 0 | if(!byteString || !byteString->data) |
311 | 0 | return UA_STATUSCODE_GOOD; |
312 | | |
313 | 0 | str->data = (UA_Byte*) |
314 | 0 | UA_base64(byteString->data, byteString->length, &str->length); |
315 | 0 | if(!str->data) |
316 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
317 | | |
318 | 0 | return UA_STATUSCODE_GOOD; |
319 | 0 | } |
320 | | |
321 | | UA_StatusCode |
322 | | UA_ByteString_fromBase64(UA_ByteString *bs, |
323 | 0 | const UA_String *input) { |
324 | 0 | UA_ByteString_init(bs); |
325 | 0 | if(input->length == 0) |
326 | 0 | return UA_STATUSCODE_GOOD; |
327 | 0 | bs->data = UA_unbase64((const unsigned char*)input->data, |
328 | 0 | input->length, &bs->length); |
329 | | /* TODO: Differentiate between encoding and memory errors */ |
330 | 0 | if(!bs->data) |
331 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
332 | 0 | return UA_STATUSCODE_GOOD; |
333 | 0 | } |
334 | | |
335 | | static u8 |
336 | 96.8k | printNum(i32 n, char *pos, u8 min_digits) { |
337 | 96.8k | char digits[10]; |
338 | 96.8k | u8 len = 0; |
339 | | /* Handle negative values */ |
340 | 96.8k | if(n < 0) { |
341 | 810 | pos[len++] = '-'; |
342 | 810 | n = -n; |
343 | 810 | } |
344 | | |
345 | | /* Extract the digits */ |
346 | 96.8k | u8 i = 0; |
347 | 344k | for(; i < min_digits || n > 0; i++) { |
348 | 247k | digits[i] = (char)((n % 10) + '0'); |
349 | 247k | n /= 10; |
350 | 247k | } |
351 | | |
352 | | /* Print in reverse order and return */ |
353 | 344k | for(; i > 0; i--) |
354 | 247k | pos[len++] = digits[i-1]; |
355 | 96.8k | return len; |
356 | 96.8k | } |
357 | | |
358 | 10.7k | #define UA_DATETIME_LENGTH 40 |
359 | | |
360 | | UA_StatusCode |
361 | 10.7k | encodeDateTime(const UA_DateTime dt, UA_String *output) { |
362 | 10.7k | char buffer[UA_DATETIME_LENGTH]; |
363 | 10.7k | char *pos = buffer; |
364 | | |
365 | 10.7k | if(output->length > 0) { |
366 | 10.7k | if(output->length < UA_DATETIME_LENGTH) |
367 | 0 | return UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED; |
368 | 10.7k | pos = (char*)output->data; |
369 | 10.7k | } |
370 | | |
371 | | /* Format: -yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSS'Z' is used. max 31 bytes. |
372 | | * Note the optional minus for negative years. */ |
373 | 10.7k | UA_DateTimeStruct tSt = UA_DateTime_toStruct(dt); |
374 | 10.7k | pos += printNum(tSt.year, pos, 4); |
375 | 10.7k | *(pos++) = '-'; |
376 | 10.7k | pos += printNum(tSt.month, pos, 2); |
377 | 10.7k | *(pos++) = '-'; |
378 | 10.7k | pos += printNum(tSt.day, pos, 2); |
379 | 10.7k | *(pos++) = 'T'; |
380 | 10.7k | pos += printNum(tSt.hour, pos, 2); |
381 | 10.7k | *(pos++) = ':'; |
382 | 10.7k | pos += printNum(tSt.min, pos, 2); |
383 | 10.7k | *(pos++) = ':'; |
384 | 10.7k | pos += printNum(tSt.sec, pos, 2); |
385 | 10.7k | *(pos++) = '.'; |
386 | 10.7k | pos += printNum(tSt.milliSec, pos, 3); |
387 | 10.7k | pos += printNum(tSt.microSec, pos, 3); |
388 | 10.7k | pos += printNum(tSt.nanoSec, pos, 3); |
389 | | |
390 | | /* Remove trailing zeros */ |
391 | 10.7k | pos--; |
392 | 106k | while(*pos == '0') |
393 | 96.1k | pos--; |
394 | 10.7k | if(*pos == '.') |
395 | 10.5k | pos--; |
396 | | |
397 | 10.7k | pos++; |
398 | 10.7k | *(pos++) = 'Z'; |
399 | | |
400 | 10.7k | if(output->length > 0) { |
401 | 10.7k | output->length = (size_t)(pos - (char*)output->data); |
402 | 10.7k | } else { |
403 | 0 | UA_String str = {(size_t)(pos - buffer), (UA_Byte*)buffer}; |
404 | 0 | return UA_String_copy(&str, output); |
405 | 0 | } |
406 | | |
407 | 10.7k | return UA_STATUSCODE_GOOD; |
408 | 10.7k | } |
409 | | |
410 | | /* Key Value Map */ |
411 | | |
412 | | const UA_KeyValueMap UA_KEYVALUEMAP_NULL = {0, NULL}; |
413 | | |
414 | | UA_KeyValueMap * |
415 | 0 | UA_KeyValueMap_new(void) { |
416 | 0 | return (UA_KeyValueMap*)UA_calloc(1, sizeof(UA_KeyValueMap)); |
417 | 0 | } |
418 | | |
419 | | UA_StatusCode |
420 | | UA_KeyValueMap_set(UA_KeyValueMap *map, |
421 | | const UA_QualifiedName key, |
422 | 0 | const UA_Variant *value) { |
423 | 0 | if(map == NULL || value == NULL) |
424 | 0 | return UA_STATUSCODE_BADINVALIDARGUMENT; |
425 | | |
426 | | /* Key exists already */ |
427 | 0 | const UA_Variant *v = UA_KeyValueMap_get(map, key); |
428 | 0 | if(v) { |
429 | 0 | UA_Variant copyV; |
430 | 0 | UA_StatusCode res = UA_Variant_copy(value, ©V); |
431 | 0 | if(res != UA_STATUSCODE_GOOD) |
432 | 0 | return res; |
433 | 0 | UA_Variant *target = (UA_Variant*)(uintptr_t)v; |
434 | 0 | UA_Variant_clear(target); |
435 | 0 | *target = copyV; |
436 | 0 | return UA_STATUSCODE_GOOD; |
437 | 0 | } |
438 | | |
439 | | /* Append to the array */ |
440 | 0 | UA_KeyValuePair pair; |
441 | 0 | pair.key = key; |
442 | 0 | pair.value = *value; |
443 | 0 | return UA_Array_appendCopy((void**)&map->map, &map->mapSize, &pair, |
444 | 0 | &UA_TYPES[UA_TYPES_KEYVALUEPAIR]); |
445 | 0 | } |
446 | | |
447 | | UA_EXPORT UA_StatusCode |
448 | | UA_KeyValueMap_setShallow(UA_KeyValueMap *map, |
449 | | const UA_QualifiedName key, |
450 | 0 | UA_Variant *value) { |
451 | 0 | if(map == NULL || value == NULL) |
452 | 0 | return UA_STATUSCODE_BADINVALIDARGUMENT; |
453 | | |
454 | | /* Key exists already */ |
455 | 0 | UA_Variant *target; |
456 | 0 | const UA_Variant *v = UA_KeyValueMap_get(map, key); |
457 | 0 | if(v) { |
458 | 0 | target = (UA_Variant*)(uintptr_t)v; |
459 | 0 | UA_Variant_clear(target); |
460 | 0 | } else { |
461 | | /* Append to the array */ |
462 | 0 | UA_KeyValuePair pair; |
463 | 0 | pair.key = key; |
464 | 0 | UA_Variant_init(&pair.value); |
465 | 0 | UA_StatusCode res = |
466 | 0 | UA_Array_appendCopy((void**)&map->map, &map->mapSize, &pair, |
467 | 0 | &UA_TYPES[UA_TYPES_KEYVALUEPAIR]); |
468 | 0 | if(res != UA_STATUSCODE_GOOD) |
469 | 0 | return res; |
470 | 0 | target = &map->map[map->mapSize-1].value; |
471 | 0 | } |
472 | | |
473 | 0 | *target = *value; |
474 | 0 | return UA_STATUSCODE_GOOD; |
475 | 0 | } |
476 | | |
477 | | UA_StatusCode |
478 | | UA_KeyValueMap_setScalar(UA_KeyValueMap *map, |
479 | | const UA_QualifiedName key, |
480 | | const void * UA_RESTRICT p, |
481 | 0 | const UA_DataType *type) { |
482 | 0 | if(p == NULL || type == NULL) |
483 | 0 | return UA_STATUSCODE_BADINVALIDARGUMENT; |
484 | 0 | UA_Variant v; |
485 | 0 | UA_Variant_init(&v); |
486 | 0 | v.type = type; |
487 | 0 | v.arrayLength = 0; |
488 | 0 | v.data = (void*)(uintptr_t)p; |
489 | 0 | return UA_KeyValueMap_set(map, key, &v); |
490 | 0 | } |
491 | | |
492 | | UA_StatusCode |
493 | | UA_KeyValueMap_setScalarShallow(UA_KeyValueMap *map, const UA_QualifiedName key, |
494 | 0 | void *UA_RESTRICT p, const UA_DataType *type) { |
495 | 0 | if(p == NULL || type == NULL) |
496 | 0 | return UA_STATUSCODE_BADINVALIDARGUMENT; |
497 | 0 | UA_Variant v; |
498 | 0 | UA_Variant_init(&v); |
499 | 0 | v.type = type; |
500 | 0 | v.arrayLength = 0; |
501 | 0 | v.data = p; |
502 | 0 | return UA_KeyValueMap_setShallow(map, key, &v); |
503 | 0 | } |
504 | | |
505 | | const UA_Variant * |
506 | | UA_KeyValueMap_get(const UA_KeyValueMap *map, |
507 | 19.2k | const UA_QualifiedName key) { |
508 | 19.2k | if(!map) |
509 | 0 | return NULL; |
510 | 51.5k | for(size_t i = 0; i < map->mapSize; i++) { |
511 | 39.2k | if(map->map[i].key.namespaceIndex == key.namespaceIndex && |
512 | 39.2k | UA_String_equal(&map->map[i].key.name, &key.name)) |
513 | 6.93k | return &map->map[i].value; |
514 | | |
515 | 39.2k | } |
516 | 12.3k | return NULL; |
517 | 19.2k | } |
518 | | |
519 | | UA_Boolean |
520 | 0 | UA_KeyValueMap_isEmpty(const UA_KeyValueMap *map) { |
521 | 0 | if(!map) |
522 | 0 | return true; |
523 | 0 | return map->mapSize == 0; |
524 | 0 | } |
525 | | |
526 | | const void * |
527 | | UA_KeyValueMap_getScalar(const UA_KeyValueMap *map, |
528 | | const UA_QualifiedName key, |
529 | 10.6k | const UA_DataType *type) { |
530 | 10.6k | const UA_Variant *v = UA_KeyValueMap_get(map, key); |
531 | 10.6k | if(!v || !UA_Variant_hasScalarType(v, type)) |
532 | 6.92k | return NULL; |
533 | 3.69k | return v->data; |
534 | 10.6k | } |
535 | | |
536 | | void |
537 | 2.36k | UA_KeyValueMap_clear(UA_KeyValueMap *map) { |
538 | 2.36k | if(!map) |
539 | 473 | return; |
540 | 1.89k | if(map->mapSize > 0) { |
541 | 0 | UA_Array_delete(map->map, map->mapSize, &UA_TYPES[UA_TYPES_KEYVALUEPAIR]); |
542 | 0 | map->mapSize = 0; |
543 | 0 | } |
544 | 1.89k | } |
545 | | |
546 | | void |
547 | 473 | UA_KeyValueMap_delete(UA_KeyValueMap *map) { |
548 | 473 | UA_KeyValueMap_clear(map); |
549 | 473 | UA_free(map); |
550 | 473 | } |
551 | | |
552 | | UA_StatusCode |
553 | | UA_KeyValueMap_remove(UA_KeyValueMap *map, |
554 | 0 | const UA_QualifiedName key) { |
555 | 0 | if(!map) |
556 | 0 | return UA_STATUSCODE_BADINVALIDARGUMENT; |
557 | | |
558 | 0 | UA_KeyValuePair *m = map->map; |
559 | 0 | size_t s = map->mapSize; |
560 | 0 | size_t i = 0; |
561 | 0 | for(; i < s; i++) { |
562 | 0 | if(m[i].key.namespaceIndex == key.namespaceIndex && |
563 | 0 | UA_String_equal(&m[i].key.name, &key.name)) |
564 | 0 | break; |
565 | 0 | } |
566 | 0 | if(i == s) |
567 | 0 | return UA_STATUSCODE_BADNOTFOUND; |
568 | | |
569 | | /* Clean the slot and move the last entry to fill the slot */ |
570 | 0 | UA_KeyValuePair_clear(&m[i]); |
571 | 0 | if(s > 1 && i < s - 1) { |
572 | 0 | m[i] = m[s-1]; |
573 | 0 | UA_KeyValuePair_init(&m[s-1]); |
574 | 0 | } |
575 | | |
576 | | /* In case resize fails, keep the longer original array around. Resize never |
577 | | * fails when reducing the size to zero. */ |
578 | 0 | UA_StatusCode res = |
579 | 0 | UA_Array_resize((void**)&map->map, &map->mapSize, map->mapSize - 1, |
580 | 0 | &UA_TYPES[UA_TYPES_KEYVALUEPAIR]); |
581 | | /* Adjust map->mapSize only when UA_Array_resize() failed. On success, the |
582 | | * value has already been decremented by UA_Array_resize(). */ |
583 | 0 | if(res != UA_STATUSCODE_GOOD) |
584 | 0 | map->mapSize--; |
585 | 0 | return UA_STATUSCODE_GOOD; |
586 | 0 | } |
587 | | |
588 | | UA_StatusCode |
589 | 0 | UA_KeyValueMap_copy(const UA_KeyValueMap *src, UA_KeyValueMap *dst) { |
590 | 0 | if(!dst) |
591 | 0 | return UA_STATUSCODE_BADINVALIDARGUMENT; |
592 | 0 | if(!src) { |
593 | 0 | dst->map = NULL; |
594 | 0 | dst->mapSize = 0; |
595 | 0 | return UA_STATUSCODE_GOOD; |
596 | 0 | } |
597 | 0 | UA_StatusCode res = UA_Array_copy(src->map, src->mapSize, (void**)&dst->map, |
598 | 0 | &UA_TYPES[UA_TYPES_KEYVALUEPAIR]); |
599 | 0 | if(res == UA_STATUSCODE_GOOD) |
600 | 0 | dst->mapSize = src->mapSize; |
601 | 0 | return res; |
602 | 0 | } |
603 | | |
604 | | UA_Boolean |
605 | 0 | UA_KeyValueMap_contains(const UA_KeyValueMap *map, const UA_QualifiedName key) { |
606 | 0 | if(!map) |
607 | 0 | return false; |
608 | 0 | for(size_t i = 0; i < map->mapSize; ++i) { |
609 | 0 | if(UA_QualifiedName_equal(&map->map[i].key, &key)) |
610 | 0 | return true; |
611 | 0 | } |
612 | 0 | return false; |
613 | 0 | } |
614 | | |
615 | | UA_StatusCode |
616 | 0 | UA_KeyValueMap_merge(UA_KeyValueMap *lhs, const UA_KeyValueMap *rhs) { |
617 | 0 | if(!lhs) |
618 | 0 | return UA_STATUSCODE_BADINVALIDARGUMENT; |
619 | 0 | if(!rhs) |
620 | 0 | return UA_STATUSCODE_GOOD; |
621 | | |
622 | 0 | UA_KeyValueMap merge; |
623 | 0 | UA_StatusCode res = UA_KeyValueMap_copy(lhs, &merge); |
624 | 0 | if(res != UA_STATUSCODE_GOOD) |
625 | 0 | return res; |
626 | | |
627 | 0 | for(size_t i = 0; i < rhs->mapSize; ++i) { |
628 | 0 | res = UA_KeyValueMap_set(&merge, rhs->map[i].key, &rhs->map[i].value); |
629 | 0 | if(res != UA_STATUSCODE_GOOD) { |
630 | 0 | UA_KeyValueMap_clear(&merge); |
631 | 0 | return res; |
632 | 0 | } |
633 | 0 | } |
634 | | |
635 | 0 | UA_KeyValueMap_clear(lhs); |
636 | 0 | *lhs = merge; |
637 | 0 | return UA_STATUSCODE_GOOD; |
638 | 0 | } |
639 | | |
640 | | /***************************/ |
641 | | /* Random Number Generator */ |
642 | | /***************************/ |
643 | | |
644 | | /* TODO is this safe for multithreading? */ |
645 | | static pcg32_random_t UA_rng = PCG32_INITIALIZER; |
646 | | |
647 | | void |
648 | 473 | UA_random_seed(u64 seed) { |
649 | 473 | pcg32_srandom_r(&UA_rng, seed, (u64)UA_DateTime_now()); |
650 | 473 | } |
651 | | |
652 | | void |
653 | 0 | UA_random_seed_deterministic(UA_UInt64 seed) { |
654 | 0 | pcg32_srandom_r(&UA_rng, seed, 0); |
655 | 0 | } |
656 | | |
657 | | u32 |
658 | 0 | UA_UInt32_random(void) { |
659 | 0 | return (u32)pcg32_random_r(&UA_rng); |
660 | 0 | } |
661 | | |
662 | | UA_Guid |
663 | 473 | UA_Guid_random(void) { |
664 | 473 | UA_Guid result; |
665 | 473 | result.data1 = (u32)pcg32_random_r(&UA_rng); |
666 | 473 | u32 r = (u32)pcg32_random_r(&UA_rng); |
667 | 473 | result.data2 = (u16) r; |
668 | 473 | result.data3 = (u16) (r >> 16); |
669 | 473 | r = (u32)pcg32_random_r(&UA_rng); |
670 | 473 | result.data4[0] = (u8)r; |
671 | 473 | result.data4[1] = (u8)(r >> 4); |
672 | 473 | result.data4[2] = (u8)(r >> 8); |
673 | 473 | result.data4[3] = (u8)(r >> 12); |
674 | 473 | r = (u32)pcg32_random_r(&UA_rng); |
675 | 473 | result.data4[4] = (u8)r; |
676 | 473 | result.data4[5] = (u8)(r >> 4); |
677 | 473 | result.data4[6] = (u8)(r >> 8); |
678 | 473 | result.data4[7] = (u8)(r >> 12); |
679 | 473 | return result; |
680 | 473 | } |
681 | | |
682 | | /********************/ |
683 | | /* Malloc Singleton */ |
684 | | /********************/ |
685 | | |
686 | | #ifdef UA_ENABLE_MALLOC_SINGLETON |
687 | | # include <stdlib.h> |
688 | | UA_EXPORT UA_THREAD_LOCAL void * (*UA_mallocSingleton)(size_t size) = malloc; |
689 | | UA_EXPORT UA_THREAD_LOCAL void (*UA_freeSingleton)(void *ptr) = free; |
690 | | UA_EXPORT UA_THREAD_LOCAL void * (*UA_callocSingleton)(size_t nelem, size_t elsize) = calloc; |
691 | | UA_EXPORT UA_THREAD_LOCAL void * (*UA_reallocSingleton)(void *ptr, size_t size) = realloc; |
692 | | #endif |
693 | | |
694 | | /************************/ |
695 | | /* ReferenceType Lookup */ |
696 | | /************************/ |
697 | | |
698 | | typedef struct { |
699 | | UA_String browseName; |
700 | | UA_UInt32 identifier; |
701 | | } RefTypeName; |
702 | | |
703 | 4.15k | #define KNOWNREFTYPES 17 |
704 | | static const RefTypeName knownRefTypes[KNOWNREFTYPES] = { |
705 | | {UA_STRING_STATIC("References"), UA_NS0ID_REFERENCES}, |
706 | | {UA_STRING_STATIC("HierachicalReferences"), UA_NS0ID_HIERARCHICALREFERENCES}, |
707 | | {UA_STRING_STATIC("NonHierachicalReferences"), UA_NS0ID_NONHIERARCHICALREFERENCES}, |
708 | | {UA_STRING_STATIC("HasChild"), UA_NS0ID_HASCHILD}, |
709 | | {UA_STRING_STATIC("Aggregates"), UA_NS0ID_AGGREGATES}, |
710 | | {UA_STRING_STATIC("HasComponent"), UA_NS0ID_HASCOMPONENT}, |
711 | | {UA_STRING_STATIC("HasProperty"), UA_NS0ID_HASPROPERTY}, |
712 | | {UA_STRING_STATIC("HasOrderedComponent"), UA_NS0ID_HASORDEREDCOMPONENT}, |
713 | | {UA_STRING_STATIC("HasSubtype"), UA_NS0ID_HASSUBTYPE}, |
714 | | {UA_STRING_STATIC("Organizes"), UA_NS0ID_ORGANIZES}, |
715 | | {UA_STRING_STATIC("HasModellingRule"), UA_NS0ID_HASMODELLINGRULE}, |
716 | | {UA_STRING_STATIC("HasTypeDefinition"), UA_NS0ID_HASTYPEDEFINITION}, |
717 | | {UA_STRING_STATIC("HasEncoding"), UA_NS0ID_HASENCODING}, |
718 | | {UA_STRING_STATIC("GeneratesEvent"), UA_NS0ID_GENERATESEVENT}, |
719 | | {UA_STRING_STATIC("AlwaysGeneratesEvent"), UA_NS0ID_ALWAYSGENERATESEVENT}, |
720 | | {UA_STRING_STATIC("HasEventSource"), UA_NS0ID_HASEVENTSOURCE}, |
721 | | {UA_STRING_STATIC("HasNotifier"), UA_NS0ID_HASNOTIFIER} |
722 | | }; |
723 | | |
724 | | UA_StatusCode |
725 | 485 | lookupRefType(UA_Server *server, UA_QualifiedName *qn, UA_NodeId *outRefTypeId) { |
726 | | /* Check well-known ReferenceTypes first */ |
727 | 485 | if(qn->namespaceIndex == 0) { |
728 | 2.89k | for(size_t i = 0; i < KNOWNREFTYPES; i++) { |
729 | 2.80k | if(UA_String_equal(&qn->name, &knownRefTypes[i].browseName)) { |
730 | 396 | *outRefTypeId = UA_NODEID_NUMERIC(0, knownRefTypes[i].identifier); |
731 | 396 | return UA_STATUSCODE_GOOD; |
732 | 396 | } |
733 | 2.80k | } |
734 | 485 | } |
735 | | |
736 | | /* Browse the information model. Return the first results if the browse name |
737 | | * in the hierarchy is ambiguous. */ |
738 | 89 | if(server) { |
739 | 0 | UA_BrowseDescription bd; |
740 | 0 | UA_BrowseDescription_init(&bd); |
741 | 0 | bd.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_REFERENCES); |
742 | 0 | bd.browseDirection = UA_BROWSEDIRECTION_FORWARD; |
743 | 0 | bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE); |
744 | 0 | bd.nodeClassMask = UA_NODECLASS_REFERENCETYPE; |
745 | |
|
746 | 0 | size_t resultsSize = 0; |
747 | 0 | UA_ExpandedNodeId *results = NULL; |
748 | 0 | UA_StatusCode res = |
749 | 0 | UA_Server_browseRecursive(server, &bd, &resultsSize, &results); |
750 | 0 | if(res != UA_STATUSCODE_GOOD) |
751 | 0 | return res; |
752 | 0 | for(size_t i = 0; i < resultsSize; i++) { |
753 | 0 | UA_QualifiedName bn; |
754 | 0 | UA_Server_readBrowseName(server, results[i].nodeId, &bn); |
755 | 0 | if(UA_QualifiedName_equal(qn, &bn)) { |
756 | 0 | UA_QualifiedName_clear(&bn); |
757 | 0 | *outRefTypeId = results[i].nodeId; |
758 | 0 | UA_NodeId_clear(&results[i].nodeId); |
759 | 0 | UA_Array_delete(results, resultsSize, &UA_TYPES[UA_TYPES_NODEID]); |
760 | 0 | return UA_STATUSCODE_GOOD; |
761 | 0 | } |
762 | 0 | UA_QualifiedName_clear(&bn); |
763 | 0 | } |
764 | | |
765 | 0 | UA_Array_delete(results, resultsSize, &UA_TYPES[UA_TYPES_NODEID]); |
766 | 0 | } |
767 | | |
768 | 89 | return UA_STATUSCODE_BADNOTFOUND; |
769 | 89 | } |
770 | | |
771 | | UA_StatusCode |
772 | 20.9k | getRefTypeBrowseName(const UA_NodeId *refTypeId, UA_String *outBN) { |
773 | | /* Canonical name known? */ |
774 | 20.9k | if(refTypeId->namespaceIndex == 0 && |
775 | 20.9k | refTypeId->identifierType == UA_NODEIDTYPE_NUMERIC) { |
776 | 1.26k | for(size_t i = 0; i < KNOWNREFTYPES; i++) { |
777 | 1.26k | if(refTypeId->identifier.numeric != knownRefTypes[i].identifier) |
778 | 882 | continue; |
779 | 382 | memcpy(outBN->data, knownRefTypes[i].browseName.data, knownRefTypes[i].browseName.length); |
780 | 382 | outBN->length = knownRefTypes[i].browseName.length; |
781 | 382 | return UA_STATUSCODE_GOOD; |
782 | 1.26k | } |
783 | 382 | } |
784 | | |
785 | | /* Print the NodeId */ |
786 | 20.5k | return UA_NodeId_print(refTypeId, outBN); |
787 | 20.9k | } |
788 | | |
789 | | /************************/ |
790 | | /* Printing and Parsing */ |
791 | | /************************/ |
792 | | |
793 | | static UA_INLINE UA_Boolean |
794 | 13.1M | isHex(u8 c) { |
795 | 13.1M | return ((c >= 'a' && c <= 'f') || |
796 | 12.9M | (c >= 'A' && c <= 'F') || |
797 | 12.9M | (c >= '0' && c <= '9')); |
798 | 13.1M | } |
799 | | |
800 | | UA_StatusCode |
801 | 411k | UA_String_unescape(UA_String *str, UA_Boolean copyEscape, UA_Escaping esc) { |
802 | 411k | if(esc == UA_ESCAPING_NONE) |
803 | 322k | return UA_STATUSCODE_GOOD; |
804 | | |
805 | | /* Does the string need escaping? */ |
806 | 88.9k | UA_String tmp; |
807 | 88.9k | status res = UA_STATUSCODE_GOOD; |
808 | 88.9k | u8 *pos = str->data; |
809 | 88.9k | u8 *end = str->data + str->length; |
810 | 88.9k | u8 escape_char = (esc == UA_ESCAPING_PERCENT || |
811 | 88.9k | esc == UA_ESCAPING_PERCENT_EXTENDED) ? '%' : '&'; |
812 | 10.8M | for(; pos < end; pos++) { |
813 | 10.7M | if(*pos == escape_char) |
814 | 2.74k | goto escape; |
815 | 10.7M | } |
816 | | |
817 | 86.2k | return UA_STATUSCODE_GOOD; |
818 | | |
819 | 2.74k | escape: |
820 | 2.74k | if(copyEscape) { |
821 | 0 | res = UA_String_copy(str, &tmp); |
822 | 0 | if(res != UA_STATUSCODE_GOOD) |
823 | 0 | return res; |
824 | 0 | pos = tmp.data; |
825 | 0 | end = tmp.data + tmp.length; |
826 | 0 | } |
827 | | |
828 | 2.74k | u8 byte = 0; |
829 | 2.74k | u8 *writepos = pos; |
830 | | |
831 | 2.74k | res = UA_STATUSCODE_BADDECODINGERROR; |
832 | 2.74k | if(esc == UA_ESCAPING_PERCENT || |
833 | 2.74k | esc == UA_ESCAPING_PERCENT_EXTENDED) { |
834 | | /* Percent-Escaping */ |
835 | 7.55M | for(; pos < end; pos++) { |
836 | 7.55M | if(*pos == '%') { |
837 | 6.58M | if(pos + 2 >= end || !isHex(pos[1]) || !isHex(pos[2])) |
838 | 47 | goto out; |
839 | 6.58M | if(pos[1] >= 'a') |
840 | 546 | byte = pos[1] - ('a' - 10); |
841 | 6.58M | else if(pos[1] >= 'A') |
842 | 32 | byte = pos[1] - ('A' - 10); |
843 | 6.58M | else |
844 | 6.58M | byte = pos[1] - '0'; |
845 | 6.58M | byte <<= 4; |
846 | | |
847 | 6.58M | if(pos[2] >= 'a') |
848 | 213k | byte += (u8)(pos[2] - ('a' - 10)); |
849 | 6.37M | else if(pos[2] >= 'A') |
850 | 122 | byte += (u8)(pos[2] - ('A' - 10)); |
851 | 6.37M | else |
852 | 6.37M | byte += (u8)(pos[2] - '0'); |
853 | | |
854 | 6.58M | pos += 2; |
855 | 6.58M | *writepos++ = byte; |
856 | 6.58M | continue; |
857 | 6.58M | } |
858 | 966k | *writepos++ = *pos; |
859 | 966k | } |
860 | 2.74k | } else { |
861 | | /* And-Escaping */ |
862 | 0 | for(; pos < end; pos++) { |
863 | 0 | if(*pos == '&') { |
864 | 0 | pos++; |
865 | 0 | if(pos == end) |
866 | 0 | goto out; |
867 | 0 | } |
868 | 0 | *writepos++ = *pos; |
869 | 0 | } |
870 | 0 | } |
871 | 2.69k | res = UA_STATUSCODE_GOOD; |
872 | | |
873 | 2.74k | out: |
874 | 2.74k | if(copyEscape) { |
875 | 0 | tmp.length = (size_t)(writepos - tmp.data); |
876 | 0 | if(tmp.length == 0) |
877 | 0 | UA_String_clear(&tmp); |
878 | 0 | if(res == UA_STATUSCODE_GOOD) |
879 | 0 | *str = tmp; |
880 | 0 | else |
881 | 0 | UA_String_clear(&tmp); |
882 | 2.74k | } else if(res == UA_STATUSCODE_GOOD) { |
883 | 2.69k | str->length = (size_t)(writepos - str->data); |
884 | 2.69k | } |
885 | 2.74k | return res; |
886 | 2.69k | } |
887 | | |
888 | | static const u8 hexchars[16] = |
889 | | {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; |
890 | | |
891 | | size_t |
892 | 117k | UA_String_escapedSize(const UA_String s, UA_Escaping esc) { |
893 | | /* Find out the overhead from escaping */ |
894 | 117k | size_t overhead = 0; |
895 | 70.7M | for(size_t j = 0; j < s.length; j++) { |
896 | 70.6M | if(esc == UA_ESCAPING_AND_EXTENDED) |
897 | 0 | overhead += isReservedExtended(s.data[j]); |
898 | 70.6M | else if(esc == UA_ESCAPING_AND) |
899 | 0 | overhead += isReservedAnd(s.data[j]); |
900 | 70.6M | else if(esc == UA_ESCAPING_PERCENT) |
901 | 10.8M | overhead += (isReservedPercent(s.data[j]) ? 2 : 0); |
902 | 59.7M | else /* if(esc == UA_ESCAPING_PERCENT_EXTENDED) */ |
903 | 59.7M | overhead += (isReservedPercentExtended(s.data[j]) ? 2 : 0); |
904 | 70.6M | } |
905 | | |
906 | 117k | return s.length + overhead; |
907 | 117k | } |
908 | | |
909 | | size_t |
910 | 114k | UA_String_escapeInsert(u8 *pos, const UA_String s2, UA_Escaping esc) { |
911 | 114k | u8 *begin = pos; |
912 | | |
913 | 114k | if(esc == UA_ESCAPING_NONE) { |
914 | 32.3M | for(size_t j = 0; j < s2.length; j++) |
915 | 32.2M | *pos++ = s2.data[j]; |
916 | 89.4k | } else if(esc == UA_ESCAPING_PERCENT || esc == UA_ESCAPING_PERCENT_EXTENDED) { |
917 | 24.9M | for(size_t j = 0; j < s2.length; j++) { |
918 | 24.9M | UA_Boolean reserved = (esc == UA_ESCAPING_PERCENT_EXTENDED) ? |
919 | 14.0M | isReservedPercentExtended(s2.data[j]) : isReservedPercent(s2.data[j]); |
920 | 24.9M | if(UA_LIKELY(!reserved)) { |
921 | 11.7M | *pos++ = s2.data[j]; |
922 | 13.1M | } else { |
923 | 13.1M | *pos++ = '%'; |
924 | 13.1M | *pos++ = hexchars[s2.data[j] >> 4]; |
925 | 13.1M | *pos++ = hexchars[s2.data[j] & 0x0f]; |
926 | 13.1M | } |
927 | 24.9M | } |
928 | 89.4k | } else { |
929 | 0 | for(size_t j = 0; j < s2.length; j++) { |
930 | 0 | UA_Boolean reserved = (esc == UA_ESCAPING_AND_EXTENDED) ? |
931 | 0 | isReservedExtended(s2.data[j]) : isReservedAnd(s2.data[j]); |
932 | 0 | if(reserved) |
933 | 0 | *pos++ = '&'; |
934 | 0 | *pos++ = s2.data[j]; |
935 | 0 | } |
936 | 0 | } |
937 | | |
938 | 114k | return (size_t)(pos - begin); |
939 | 114k | } |
940 | | |
941 | | UA_StatusCode |
942 | 89.2k | UA_String_escapeAppend(UA_String *s, const UA_String s2, UA_Escaping esc) { |
943 | 89.2k | if(esc == UA_ESCAPING_NONE) |
944 | 0 | return UA_String_append(s, s2); |
945 | | |
946 | | /* Allocate memory for the additional escaped string */ |
947 | 89.2k | size_t escapedLength = UA_String_escapedSize(s2, esc); |
948 | 89.2k | UA_Byte *buf = (UA_Byte*) |
949 | 89.2k | UA_realloc(s->data, s->length + s2.length + escapedLength); |
950 | 89.2k | if(!buf) |
951 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
952 | | |
953 | | /* Escape and insert at the end */ |
954 | 89.2k | s->data = buf; |
955 | 89.2k | UA_String_escapeInsert(s->data + s->length, s2, esc); |
956 | 89.2k | s->length += escapedLength; |
957 | 89.2k | return UA_STATUSCODE_GOOD; |
958 | 89.2k | } |
959 | | |
960 | | static UA_StatusCode |
961 | 528 | moveTmpToOut(UA_String *tmp, UA_String *out) { |
962 | | /* Output has zero length */ |
963 | 528 | if(tmp->length == 0) { |
964 | 14 | UA_assert(tmp->data == NULL); |
965 | 14 | if(out->data == NULL) |
966 | 14 | out->data = (UA_Byte*)UA_EMPTY_ARRAY_SENTINEL; |
967 | 14 | out->length = 0; |
968 | 14 | return UA_STATUSCODE_GOOD; |
969 | 14 | } |
970 | | |
971 | | /* No output buffer provided, return the tmp buffer */ |
972 | 514 | if(out->length == 0) { |
973 | 514 | *out = *tmp; |
974 | 514 | return UA_STATUSCODE_GOOD; |
975 | 514 | } |
976 | | |
977 | | /* The provided buffer is too short */ |
978 | 0 | if(out->length < tmp->length) { |
979 | 0 | UA_String_clear(tmp); |
980 | 0 | return UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED; |
981 | 0 | } |
982 | | |
983 | | /* Copy output to the provided buffer */ |
984 | 0 | memcpy(out->data, tmp->data, tmp->length); |
985 | 0 | out->length = tmp->length; |
986 | 0 | UA_String_clear(tmp); |
987 | 0 | return UA_STATUSCODE_GOOD; |
988 | 0 | } |
989 | | |
990 | | static const UA_NodeId hierarchicalRefs = |
991 | | {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HIERARCHICALREFERENCES}}; |
992 | | static const UA_NodeId aggregatesRefs = |
993 | | {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_AGGREGATES}}; |
994 | | static const UA_NodeId objectsFolder = |
995 | | {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_OBJECTSFOLDER}}; |
996 | | |
997 | | static UA_StatusCode |
998 | 264 | printRelativePath(const UA_RelativePath *rp, UA_String *out, UA_Escaping esc) { |
999 | 264 | UA_String tmp = UA_STRING_NULL; |
1000 | 264 | UA_StatusCode res = UA_STATUSCODE_GOOD; |
1001 | 71.3k | for(size_t i = 0; i < rp->elementsSize && res == UA_STATUSCODE_GOOD; i++) { |
1002 | | /* Print the reference type */ |
1003 | 71.1k | UA_RelativePathElement *elm = &rp->elements[i]; |
1004 | 71.1k | if(UA_NodeId_equal(&hierarchicalRefs, &elm->referenceTypeId) && |
1005 | 53.1k | !elm->isInverse && elm->includeSubtypes) { |
1006 | 52.9k | res |= UA_String_append(&tmp, UA_STRING("/")); |
1007 | 52.9k | } else if(esc == UA_ESCAPING_AND && |
1008 | 0 | UA_NodeId_equal(&aggregatesRefs, &elm->referenceTypeId) && |
1009 | 0 | !elm->isInverse && elm->includeSubtypes) { |
1010 | 0 | res |= UA_String_append(&tmp, UA_STRING(".")); |
1011 | 18.1k | } else { |
1012 | 18.1k | res |= UA_String_append(&tmp, UA_STRING("<")); |
1013 | 18.1k | if(!elm->includeSubtypes) |
1014 | 132 | res |= UA_String_append(&tmp, UA_STRING("#")); |
1015 | 18.1k | if(elm->isInverse) |
1016 | 2 | res |= UA_String_append(&tmp, UA_STRING("!")); |
1017 | 18.1k | if(res != UA_STATUSCODE_GOOD) |
1018 | 0 | break; |
1019 | | |
1020 | 18.1k | UA_Byte bnBuf[512]; |
1021 | 18.1k | UA_String bnBufStr = {512, bnBuf}; |
1022 | 18.1k | res = getRefTypeBrowseName(&elm->referenceTypeId, &bnBufStr); |
1023 | 18.1k | if(res != UA_STATUSCODE_GOOD) { |
1024 | 2.83k | UA_String_init(&bnBufStr); |
1025 | 2.83k | res = getRefTypeBrowseName(&elm->referenceTypeId, &bnBufStr); |
1026 | 2.83k | if(res != UA_STATUSCODE_GOOD) |
1027 | 0 | break; |
1028 | 2.83k | } |
1029 | 18.1k | res |= UA_String_escapeAppend(&tmp, bnBufStr, esc); |
1030 | 18.1k | res |= UA_String_append(&tmp, UA_STRING(">")); |
1031 | 18.1k | if(bnBufStr.data != bnBuf) |
1032 | 2.83k | UA_String_clear(&bnBufStr); |
1033 | 18.1k | } |
1034 | | |
1035 | | /* Print the qualified name */ |
1036 | 71.1k | UA_QualifiedName *qn = &elm->targetName; |
1037 | 71.1k | if(qn->namespaceIndex > 0) { |
1038 | 206 | char nsStr[8]; /* Enough for a uint16 */ |
1039 | 206 | itoaUnsigned(qn->namespaceIndex, nsStr, 10); |
1040 | 206 | res |= UA_String_append(&tmp, UA_STRING(nsStr)); |
1041 | 206 | res |= UA_String_append(&tmp, UA_STRING(":")); |
1042 | 206 | } |
1043 | 71.1k | res |= UA_String_escapeAppend(&tmp, qn->name, esc); |
1044 | 71.1k | } |
1045 | | |
1046 | | /* Encoding failed, clean up */ |
1047 | 264 | if(res != UA_STATUSCODE_GOOD) { |
1048 | 0 | UA_String_clear(&tmp); |
1049 | 0 | return res; |
1050 | 0 | } |
1051 | | |
1052 | 264 | return moveTmpToOut(&tmp, out); |
1053 | 264 | } |
1054 | | |
1055 | | UA_StatusCode |
1056 | 0 | UA_RelativePath_print(const UA_RelativePath *rp, UA_String *out) { |
1057 | 0 | return printRelativePath(rp, out, UA_ESCAPING_AND); |
1058 | 0 | } |
1059 | | |
1060 | | static UA_NodeId baseEventTypeId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_BASEEVENTTYPE}}; |
1061 | | |
1062 | | UA_StatusCode |
1063 | 0 | UA_SimpleAttributeOperand_print(const UA_SimpleAttributeOperand *sao, UA_String *out) { |
1064 | 0 | UA_RelativePathElement rpe; |
1065 | 0 | UA_String tmp = UA_STRING_NULL; |
1066 | 0 | UA_StatusCode res = UA_STATUSCODE_GOOD; |
1067 | | |
1068 | | /* Print the TypeDefinitionId */ |
1069 | 0 | if(!UA_NodeId_equal(&baseEventTypeId, &sao->typeDefinitionId)) { |
1070 | 0 | UA_Byte nodeIdBuf[512]; |
1071 | 0 | UA_String nodeIdBufStr = {512, nodeIdBuf}; |
1072 | 0 | res |= nodeId_printEscape(&sao->typeDefinitionId, &nodeIdBufStr, |
1073 | 0 | NULL, UA_ESCAPING_PERCENT); |
1074 | 0 | res |= UA_String_append(&tmp, nodeIdBufStr); |
1075 | 0 | } |
1076 | | |
1077 | | /* Print the BrowsePath */ |
1078 | 0 | UA_RelativePathElement_init(&rpe); |
1079 | 0 | rpe.includeSubtypes = true; |
1080 | 0 | rpe.referenceTypeId = hierarchicalRefs; |
1081 | 0 | UA_RelativePath rp = {1, &rpe}; |
1082 | 0 | for(size_t i = 0; i < sao->browsePathSize; i++) { |
1083 | 0 | UA_String rpstr = UA_STRING_NULL; |
1084 | 0 | UA_assert(rpstr.data == NULL && rpstr.length == 0); /* pacify clang scan-build */ |
1085 | 0 | rpe.targetName = sao->browsePath[i]; |
1086 | 0 | res |= printRelativePath(&rp, &rpstr, UA_ESCAPING_PERCENT); |
1087 | 0 | res |= UA_String_append(&tmp, rpstr); |
1088 | 0 | UA_String_clear(&rpstr); |
1089 | 0 | } |
1090 | | |
1091 | | /* Print the attribute name */ |
1092 | 0 | if(sao->attributeId != UA_ATTRIBUTEID_VALUE) { |
1093 | 0 | const char *attrName = UA_AttributeId_name((UA_AttributeId)sao->attributeId); |
1094 | 0 | res |= UA_String_append(&tmp, UA_STRING("#")); |
1095 | 0 | res |= UA_String_append(&tmp, UA_STRING((char*)(uintptr_t)attrName)); |
1096 | 0 | } |
1097 | | |
1098 | | /* Print the IndexRange */ |
1099 | 0 | if(sao->indexRange.length > 0) { |
1100 | 0 | res |= UA_String_append(&tmp, UA_STRING("[")); |
1101 | 0 | res |= UA_String_append(&tmp, sao->indexRange); |
1102 | 0 | res |= UA_String_append(&tmp, UA_STRING("]")); |
1103 | 0 | } |
1104 | | |
1105 | | /* Encoding failed, clean up */ |
1106 | 0 | if(res != UA_STATUSCODE_GOOD) { |
1107 | 0 | UA_String_clear(&tmp); |
1108 | 0 | return res; |
1109 | 0 | } |
1110 | | |
1111 | 0 | return moveTmpToOut(&tmp, out); |
1112 | 0 | } |
1113 | | |
1114 | | UA_StatusCode |
1115 | | UA_AttributeOperand_print(const UA_AttributeOperand *ao, |
1116 | 264 | UA_String *out) { |
1117 | 264 | UA_String tmp = UA_STRING_NULL; |
1118 | 264 | UA_StatusCode res = UA_STATUSCODE_GOOD; |
1119 | | |
1120 | | /* Print the TypeDefinitionId */ |
1121 | 264 | if(!UA_NodeId_equal(&objectsFolder, &ao->nodeId)) { |
1122 | 86 | UA_Byte nodeIdBuf[512]; |
1123 | 86 | UA_String nodeIdBufStr = {512, nodeIdBuf}; |
1124 | 86 | res |= nodeId_printEscape(&ao->nodeId, &nodeIdBufStr, |
1125 | 86 | NULL, UA_ESCAPING_PERCENT_EXTENDED); |
1126 | 86 | res |= UA_String_append(&tmp, nodeIdBufStr); |
1127 | 86 | } |
1128 | | |
1129 | | /* Print the BrowsePath */ |
1130 | 264 | UA_String rpstr = UA_STRING_NULL; |
1131 | 264 | UA_assert(rpstr.data == NULL && rpstr.length == 0); /* pacify clang scan-build */ |
1132 | 264 | res |= printRelativePath(&ao->browsePath, &rpstr, UA_ESCAPING_PERCENT_EXTENDED); |
1133 | 264 | res |= UA_String_append(&tmp, rpstr); |
1134 | 264 | UA_String_clear(&rpstr); |
1135 | | |
1136 | | /* Print the attribute name */ |
1137 | 264 | if(ao->attributeId != UA_ATTRIBUTEID_VALUE) { |
1138 | 0 | const char *attrName= UA_AttributeId_name((UA_AttributeId)ao->attributeId); |
1139 | 0 | res |= UA_String_append(&tmp, UA_STRING("#")); |
1140 | 0 | res |= UA_String_append(&tmp, UA_STRING((char*)(uintptr_t)attrName)); |
1141 | 0 | } |
1142 | | |
1143 | | /* Print the IndexRange */ |
1144 | 264 | if(ao->indexRange.length > 0) { |
1145 | 20 | res |= UA_String_append(&tmp, UA_STRING("[")); |
1146 | 20 | res |= UA_String_append(&tmp, ao->indexRange); |
1147 | 20 | res |= UA_String_append(&tmp, UA_STRING("]")); |
1148 | 20 | } |
1149 | | |
1150 | | /* Encoding failed, clean up */ |
1151 | 264 | if(res != UA_STATUSCODE_GOOD) { |
1152 | 0 | UA_String_clear(&tmp); |
1153 | 0 | return res; |
1154 | 0 | } |
1155 | | |
1156 | 264 | return moveTmpToOut(&tmp, out); |
1157 | 264 | } |
1158 | | |
1159 | | UA_StatusCode |
1160 | 0 | UA_ReadValueId_print(const UA_ReadValueId *rvi, UA_String *out) { |
1161 | 0 | UA_String tmp = UA_STRING_NULL; |
1162 | 0 | UA_StatusCode res = UA_STATUSCODE_GOOD; |
1163 | | |
1164 | | /* Print the TypeDefinitionId */ |
1165 | 0 | if(!UA_NodeId_equal(&UA_NODEID_NULL, &rvi->nodeId)) { |
1166 | 0 | UA_Byte nodeIdBuf[512]; |
1167 | 0 | UA_String nodeIdBufStr = {512, nodeIdBuf}; |
1168 | 0 | res |= nodeId_printEscape(&rvi->nodeId, &nodeIdBufStr, |
1169 | 0 | NULL, UA_ESCAPING_PERCENT); |
1170 | 0 | res |= UA_String_append(&tmp, nodeIdBufStr); |
1171 | 0 | } |
1172 | | |
1173 | | /* Print the attribute name */ |
1174 | 0 | if(rvi->attributeId != UA_ATTRIBUTEID_VALUE) { |
1175 | 0 | const char *attrName= UA_AttributeId_name((UA_AttributeId)rvi->attributeId); |
1176 | 0 | res |= UA_String_append(&tmp, UA_STRING("#")); |
1177 | 0 | res |= UA_String_append(&tmp, UA_STRING((char*)(uintptr_t)attrName)); |
1178 | 0 | } |
1179 | | |
1180 | | /* Print the IndexRange */ |
1181 | 0 | if(rvi->indexRange.length > 0) { |
1182 | 0 | res |= UA_String_append(&tmp, UA_STRING("[")); |
1183 | 0 | res |= UA_String_append(&tmp, rvi->indexRange); |
1184 | 0 | res |= UA_String_append(&tmp, UA_STRING("]")); |
1185 | 0 | } |
1186 | | |
1187 | | /* Encoding failed, clean up */ |
1188 | 0 | if(res != UA_STATUSCODE_GOOD) { |
1189 | 0 | UA_String_clear(&tmp); |
1190 | 0 | return res; |
1191 | 0 | } |
1192 | | |
1193 | 0 | return moveTmpToOut(&tmp, out); |
1194 | 0 | } |
1195 | | |
1196 | | /************************/ |
1197 | | /* Cryptography Helpers */ |
1198 | | /************************/ |
1199 | | |
1200 | | UA_ByteString |
1201 | 0 | getLeafCertificate(UA_ByteString chain) { |
1202 | | /* Detect DER encoded X.509 v3 certificate. If the DER detection fails, |
1203 | | * return the entire chain. |
1204 | | * |
1205 | | * The OPC UA standard requires this to be DER. But we also allow other |
1206 | | * formats like PEM. Afterwards it depends on the crypto backend to parse |
1207 | | * it. mbedTLS and OpenSSL detect the format automatically. */ |
1208 | 0 | if(chain.length < 4 || chain.data[0] != 0x30 || chain.data[1] != 0x82) |
1209 | 0 | return chain; |
1210 | | |
1211 | | /* The certificate length is encoded in the next 2 bytes. */ |
1212 | 0 | size_t leafLen = 4; /* Magic numbers + length bytes */ |
1213 | 0 | leafLen += (size_t)(((uint16_t)chain.data[2]) << 8); |
1214 | 0 | leafLen += chain.data[3]; |
1215 | | |
1216 | | /* Consistency check */ |
1217 | 0 | if(leafLen > chain.length) |
1218 | 0 | return UA_BYTESTRING_NULL; |
1219 | | |
1220 | | /* Adjust the length and return */ |
1221 | 0 | chain.length = leafLen; |
1222 | 0 | return chain; |
1223 | 0 | } |
1224 | | |
1225 | | UA_Boolean |
1226 | 0 | UA_constantTimeEqual(const void *ptr1, const void *ptr2, size_t length) { |
1227 | 0 | volatile const UA_Byte *a = (volatile const UA_Byte *)ptr1; |
1228 | 0 | volatile const UA_Byte *b = (volatile const UA_Byte *)ptr2; |
1229 | 0 | volatile UA_Byte c = 0; |
1230 | 0 | for(size_t i = 0; i < length; ++i) { |
1231 | 0 | UA_Byte x = a[i], y = b[i]; |
1232 | 0 | c = c | (x ^ y); |
1233 | 0 | } |
1234 | 0 | return !c; |
1235 | 0 | } |
1236 | | |
1237 | | void |
1238 | 0 | UA_ByteString_memZero(UA_ByteString *bs) { |
1239 | | #if defined(__STDC_LIB_EXT1__) |
1240 | | memset_s(bs->data, bs->length, 0, bs->length); |
1241 | | #elif defined(UA_ARCHITECTURE_WIN32) |
1242 | | SecureZeroMemory(bs->data, bs->length); |
1243 | | #else |
1244 | 0 | volatile unsigned char *volatile ptr = |
1245 | 0 | (volatile unsigned char *)bs->data; |
1246 | 0 | size_t i = 0; |
1247 | 0 | size_t maxLen = bs->length; |
1248 | 0 | while(i < maxLen) { |
1249 | 0 | ptr[i++] = 0; |
1250 | 0 | } |
1251 | 0 | #endif |
1252 | 0 | } |
1253 | | |
1254 | | UA_Boolean |
1255 | | UA_TrustListDataType_contains(const UA_TrustListDataType *trustList, |
1256 | | const UA_ByteString *certificate, |
1257 | 0 | UA_TrustListMasks specifiedList) { |
1258 | 0 | if(!trustList || !certificate) |
1259 | 0 | return false; |
1260 | | |
1261 | 0 | if(specifiedList == UA_TRUSTLISTMASKS_TRUSTEDCERTIFICATES) { |
1262 | 0 | for(size_t i = 0; i < trustList->trustedCertificatesSize; i++) { |
1263 | 0 | if(UA_ByteString_equal(certificate, &trustList->trustedCertificates[i])) |
1264 | 0 | return true; |
1265 | 0 | } |
1266 | 0 | } |
1267 | 0 | if(specifiedList == UA_TRUSTLISTMASKS_TRUSTEDCRLS) { |
1268 | 0 | for(size_t i = 0; i < trustList->trustedCrlsSize; i++) { |
1269 | 0 | if(UA_ByteString_equal(certificate, &trustList->trustedCrls[i])) |
1270 | 0 | return true; |
1271 | 0 | } |
1272 | 0 | } |
1273 | 0 | if(specifiedList == UA_TRUSTLISTMASKS_ISSUERCERTIFICATES) { |
1274 | 0 | for(size_t i = 0; i < trustList->issuerCertificatesSize; i++) { |
1275 | 0 | if(UA_ByteString_equal(certificate, &trustList->issuerCertificates[i])) |
1276 | 0 | return true; |
1277 | 0 | } |
1278 | 0 | } |
1279 | 0 | if(specifiedList == UA_TRUSTLISTMASKS_ISSUERCRLS) { |
1280 | 0 | for(size_t i = 0; i < trustList->issuerCrlsSize; i++) { |
1281 | 0 | if(UA_ByteString_equal(certificate, &trustList->issuerCrls[i])) |
1282 | 0 | return true; |
1283 | 0 | } |
1284 | 0 | } |
1285 | | |
1286 | 0 | return false; |
1287 | 0 | } |
1288 | | |
1289 | | UA_StatusCode |
1290 | 0 | UA_TrustListDataType_add(const UA_TrustListDataType *src, UA_TrustListDataType *dst) { |
1291 | 0 | if(!dst) |
1292 | 0 | return UA_STATUSCODE_BADINVALIDARGUMENT; |
1293 | 0 | if(!src) { |
1294 | 0 | return UA_STATUSCODE_GOOD; |
1295 | 0 | } |
1296 | | |
1297 | 0 | UA_StatusCode retval = UA_STATUSCODE_GOOD; |
1298 | |
|
1299 | 0 | if(src->specifiedLists & UA_TRUSTLISTMASKS_TRUSTEDCERTIFICATES) { |
1300 | 0 | if(dst->trustedCertificates == NULL) |
1301 | 0 | dst->trustedCertificates = (UA_ByteString *)UA_Array_new(0, &UA_TYPES[UA_TYPES_BYTESTRING]); |
1302 | 0 | for(size_t i = 0; i < src->trustedCertificatesSize; i++) { |
1303 | 0 | if(UA_TrustListDataType_contains(dst, &src->trustedCertificates[i], |
1304 | 0 | UA_TRUSTLISTMASKS_TRUSTEDCERTIFICATES)) { |
1305 | 0 | continue; |
1306 | 0 | } |
1307 | 0 | retval = UA_Array_appendCopy((void**)&dst->trustedCertificates, &dst->trustedCertificatesSize, |
1308 | 0 | &src->trustedCertificates[i], &UA_TYPES[UA_TYPES_BYTESTRING]); |
1309 | 0 | if(retval != UA_STATUSCODE_GOOD) { |
1310 | 0 | return retval; |
1311 | 0 | } |
1312 | 0 | } |
1313 | 0 | dst->specifiedLists |= UA_TRUSTLISTMASKS_TRUSTEDCERTIFICATES; |
1314 | 0 | } |
1315 | 0 | if(src->specifiedLists & UA_TRUSTLISTMASKS_TRUSTEDCRLS) { |
1316 | 0 | if(dst->trustedCrls == NULL) |
1317 | 0 | dst->trustedCrls = (UA_ByteString *)UA_Array_new(0, &UA_TYPES[UA_TYPES_BYTESTRING]); |
1318 | 0 | for(size_t i = 0; i < src->trustedCrlsSize; i++) { |
1319 | 0 | if(UA_TrustListDataType_contains(dst, &src->trustedCrls[i], |
1320 | 0 | UA_TRUSTLISTMASKS_TRUSTEDCRLS)) { |
1321 | 0 | continue; |
1322 | 0 | } |
1323 | 0 | retval = UA_Array_appendCopy((void**)&dst->trustedCrls, &dst->trustedCrlsSize, |
1324 | 0 | &src->trustedCrls[i], &UA_TYPES[UA_TYPES_BYTESTRING]); |
1325 | 0 | if(retval != UA_STATUSCODE_GOOD) { |
1326 | 0 | return retval; |
1327 | 0 | } |
1328 | 0 | } |
1329 | 0 | dst->specifiedLists |= UA_TRUSTLISTMASKS_TRUSTEDCRLS; |
1330 | 0 | } |
1331 | 0 | if(src->specifiedLists & UA_TRUSTLISTMASKS_ISSUERCERTIFICATES) { |
1332 | 0 | if(dst->issuerCertificates == NULL) |
1333 | 0 | dst->issuerCertificates = (UA_ByteString *)UA_Array_new(0, &UA_TYPES[UA_TYPES_BYTESTRING]); |
1334 | 0 | for(size_t i = 0; i < src->issuerCertificatesSize; i++) { |
1335 | 0 | if(UA_TrustListDataType_contains(dst, &src->issuerCertificates[i], |
1336 | 0 | UA_TRUSTLISTMASKS_ISSUERCERTIFICATES)) { |
1337 | 0 | continue; |
1338 | 0 | } |
1339 | 0 | retval = UA_Array_appendCopy((void**)&dst->issuerCertificates, &dst->issuerCertificatesSize, |
1340 | 0 | &src->issuerCertificates[i], &UA_TYPES[UA_TYPES_BYTESTRING]); |
1341 | 0 | if(retval != UA_STATUSCODE_GOOD) { |
1342 | 0 | return retval; |
1343 | 0 | } |
1344 | 0 | } |
1345 | 0 | dst->specifiedLists |= UA_TRUSTLISTMASKS_ISSUERCERTIFICATES; |
1346 | 0 | } |
1347 | 0 | if(src->specifiedLists & UA_TRUSTLISTMASKS_ISSUERCRLS) { |
1348 | 0 | if(dst->issuerCrls == NULL) |
1349 | 0 | dst->issuerCrls = (UA_ByteString *)UA_Array_new(0, &UA_TYPES[UA_TYPES_BYTESTRING]); |
1350 | 0 | for(size_t i = 0; i < src->issuerCrlsSize; i++) { |
1351 | 0 | if(UA_TrustListDataType_contains(dst, &src->issuerCrls[i], |
1352 | 0 | UA_TRUSTLISTMASKS_ISSUERCRLS)) { |
1353 | 0 | continue; |
1354 | 0 | } |
1355 | 0 | retval = UA_Array_appendCopy((void**)&dst->issuerCrls, &dst->issuerCrlsSize, |
1356 | 0 | &src->issuerCrls[i], &UA_TYPES[UA_TYPES_BYTESTRING]); |
1357 | 0 | if(retval != UA_STATUSCODE_GOOD) { |
1358 | 0 | return retval; |
1359 | 0 | } |
1360 | 0 | } |
1361 | 0 | dst->specifiedLists |= UA_TRUSTLISTMASKS_ISSUERCRLS; |
1362 | 0 | } |
1363 | | |
1364 | 0 | return retval; |
1365 | 0 | } |
1366 | | |
1367 | | UA_StatusCode |
1368 | 0 | UA_TrustListDataType_set(const UA_TrustListDataType *src, UA_TrustListDataType *dst) { |
1369 | 0 | if(src->specifiedLists & UA_TRUSTLISTMASKS_TRUSTEDCERTIFICATES) { |
1370 | 0 | UA_Array_delete(dst->trustedCertificates, dst->trustedCertificatesSize, &UA_TYPES[UA_TYPES_BYTESTRING]); |
1371 | 0 | dst->trustedCertificates = NULL; |
1372 | 0 | dst->trustedCertificatesSize = 0; |
1373 | 0 | } |
1374 | 0 | if(src->specifiedLists & UA_TRUSTLISTMASKS_TRUSTEDCRLS) { |
1375 | 0 | UA_Array_delete(dst->trustedCrls, dst->trustedCrlsSize, &UA_TYPES[UA_TYPES_BYTESTRING]); |
1376 | 0 | dst->trustedCrls = NULL; |
1377 | 0 | dst->trustedCrlsSize = 0; |
1378 | 0 | } |
1379 | 0 | if(src->specifiedLists & UA_TRUSTLISTMASKS_ISSUERCERTIFICATES) { |
1380 | 0 | UA_Array_delete(dst->issuerCertificates, dst->issuerCertificatesSize, &UA_TYPES[UA_TYPES_BYTESTRING]); |
1381 | 0 | dst->issuerCertificates = NULL; |
1382 | 0 | dst->issuerCertificatesSize = 0; |
1383 | 0 | } |
1384 | 0 | if(src->specifiedLists & UA_TRUSTLISTMASKS_ISSUERCRLS) { |
1385 | 0 | UA_Array_delete(dst->issuerCrls, dst->issuerCrlsSize, &UA_TYPES[UA_TYPES_BYTESTRING]); |
1386 | 0 | dst->issuerCrls = NULL; |
1387 | 0 | dst->issuerCrlsSize = 0; |
1388 | 0 | } |
1389 | 0 | return UA_TrustListDataType_add(src, dst); |
1390 | 0 | } |
1391 | | |
1392 | | UA_StatusCode |
1393 | 0 | UA_TrustListDataType_remove(const UA_TrustListDataType *src, UA_TrustListDataType *dst) { |
1394 | 0 | if(!dst) |
1395 | 0 | return UA_STATUSCODE_BADINVALIDARGUMENT; |
1396 | 0 | if(!src) { |
1397 | 0 | return UA_STATUSCODE_GOOD; |
1398 | 0 | } |
1399 | | |
1400 | 0 | UA_StatusCode retval = UA_STATUSCODE_GOOD; |
1401 | | |
1402 | | /* remove trusted certificates */ |
1403 | 0 | if(dst->trustedCertificatesSize > 0 && src->trustedCertificatesSize > 0) { |
1404 | 0 | UA_ByteString *newList = (UA_ByteString*)UA_calloc(dst->trustedCertificatesSize, sizeof(UA_ByteString)); |
1405 | 0 | size_t newListSize = 0; |
1406 | 0 | size_t oldListSize = dst->trustedCertificatesSize; |
1407 | 0 | UA_Boolean isContained = false; |
1408 | 0 | for(size_t i = 0; i < dst->trustedCertificatesSize; i++) { |
1409 | 0 | for(size_t j = 0; j < src->trustedCertificatesSize; j++) { |
1410 | 0 | if(UA_ByteString_equal(&dst->trustedCertificates[i], &src->trustedCertificates[j])) |
1411 | 0 | isContained = true; |
1412 | 0 | } |
1413 | 0 | if(!isContained) { |
1414 | 0 | UA_ByteString_copy(&dst->trustedCertificates[i], &newList[newListSize]); |
1415 | 0 | newListSize += 1; |
1416 | 0 | } |
1417 | 0 | isContained = false; |
1418 | 0 | } |
1419 | 0 | if(newListSize < dst->trustedCertificatesSize) { |
1420 | 0 | if(newListSize == 0) { |
1421 | 0 | UA_free(newList); |
1422 | 0 | newList = NULL; |
1423 | 0 | } else { |
1424 | 0 | retval = UA_Array_resize((void**)&newList, &oldListSize, newListSize, &UA_TYPES[UA_TYPES_BYTESTRING]); |
1425 | 0 | if(retval != UA_STATUSCODE_GOOD) { |
1426 | 0 | UA_free(newList); |
1427 | 0 | return retval; |
1428 | 0 | } |
1429 | 0 | } |
1430 | 0 | } |
1431 | 0 | UA_Array_delete(dst->trustedCertificates, dst->trustedCertificatesSize, &UA_TYPES[UA_TYPES_BYTESTRING]); |
1432 | 0 | dst->trustedCertificatesSize = 0; |
1433 | 0 | dst->trustedCertificates = newList; |
1434 | 0 | dst->trustedCertificatesSize = newListSize; |
1435 | 0 | } |
1436 | | |
1437 | | /* remove issuer certificates */ |
1438 | 0 | if(dst->issuerCertificatesSize > 0 && src->issuerCertificatesSize > 0) { |
1439 | 0 | UA_ByteString *newList = (UA_ByteString*)UA_calloc(dst->issuerCertificatesSize, sizeof(UA_ByteString)); |
1440 | 0 | size_t newListSize = 0; |
1441 | 0 | size_t oldListSize = dst->issuerCertificatesSize; |
1442 | 0 | UA_Boolean isContained = false; |
1443 | 0 | for(size_t i = 0; i < dst->issuerCertificatesSize; i++) { |
1444 | 0 | for(size_t j = 0; j < src->issuerCertificatesSize; j++) { |
1445 | 0 | if(UA_ByteString_equal(&dst->issuerCertificates[i], &src->issuerCertificates[j])) |
1446 | 0 | isContained = true; |
1447 | 0 | } |
1448 | 0 | if(!isContained) { |
1449 | 0 | UA_ByteString_copy(&dst->issuerCertificates[i], &newList[newListSize]); |
1450 | 0 | newListSize += 1; |
1451 | 0 | } |
1452 | 0 | isContained = false; |
1453 | 0 | } |
1454 | 0 | if(newListSize < dst->issuerCertificatesSize) { |
1455 | 0 | if(newListSize == 0) { |
1456 | 0 | UA_free(newList); |
1457 | 0 | newList = NULL; |
1458 | 0 | } else { |
1459 | 0 | retval = UA_Array_resize((void**)&newList, &oldListSize, newListSize, &UA_TYPES[UA_TYPES_BYTESTRING]); |
1460 | 0 | if(retval != UA_STATUSCODE_GOOD) { |
1461 | 0 | UA_free(newList); |
1462 | 0 | return retval; |
1463 | 0 | } |
1464 | 0 | } |
1465 | 0 | } |
1466 | 0 | UA_Array_delete(dst->issuerCertificates, dst->issuerCertificatesSize, &UA_TYPES[UA_TYPES_BYTESTRING]); |
1467 | 0 | dst->issuerCertificatesSize = 0; |
1468 | 0 | dst->issuerCertificates = newList; |
1469 | 0 | dst->issuerCertificatesSize = newListSize; |
1470 | 0 | } |
1471 | | |
1472 | | /* remove trusted crls */ |
1473 | 0 | if(dst->trustedCrlsSize > 0 && src->trustedCrlsSize > 0) { |
1474 | 0 | UA_ByteString *newList = (UA_ByteString*)UA_calloc(dst->trustedCrlsSize, sizeof(UA_ByteString)); |
1475 | 0 | size_t newListSize = 0; |
1476 | 0 | size_t oldListSize = dst->trustedCrlsSize; |
1477 | 0 | UA_Boolean isContained = false; |
1478 | 0 | for(size_t i = 0; i < dst->trustedCrlsSize; i++) { |
1479 | 0 | for(size_t j = 0; j < src->trustedCrlsSize; j++) { |
1480 | 0 | if(UA_ByteString_equal(&dst->trustedCrls[i], &src->trustedCrls[j])) |
1481 | 0 | isContained = true; |
1482 | 0 | } |
1483 | 0 | if(!isContained) { |
1484 | 0 | UA_ByteString_copy(&dst->trustedCrls[i], &newList[newListSize]); |
1485 | 0 | newListSize += 1; |
1486 | 0 | } |
1487 | 0 | isContained = false; |
1488 | 0 | } |
1489 | 0 | if(newListSize < dst->trustedCrlsSize) { |
1490 | 0 | if(newListSize == 0) { |
1491 | 0 | UA_free(newList); |
1492 | 0 | newList = NULL; |
1493 | 0 | } else { |
1494 | 0 | retval = UA_Array_resize((void**)&newList, &oldListSize, newListSize, &UA_TYPES[UA_TYPES_BYTESTRING]); |
1495 | 0 | if(retval != UA_STATUSCODE_GOOD) { |
1496 | 0 | UA_free(newList); |
1497 | 0 | return retval; |
1498 | 0 | } |
1499 | 0 | } |
1500 | 0 | } |
1501 | 0 | UA_Array_delete(dst->trustedCrls, dst->trustedCrlsSize, &UA_TYPES[UA_TYPES_BYTESTRING]); |
1502 | 0 | dst->trustedCrlsSize = 0; |
1503 | 0 | dst->trustedCrls = newList; |
1504 | 0 | dst->trustedCrlsSize = newListSize; |
1505 | 0 | } |
1506 | | |
1507 | | /* remove issuer crls */ |
1508 | 0 | if(dst->issuerCrlsSize > 0 && src->issuerCrlsSize > 0) { |
1509 | 0 | UA_ByteString *newList = (UA_ByteString*)UA_calloc(dst->issuerCrlsSize, sizeof(UA_ByteString)); |
1510 | 0 | size_t newListSize = 0; |
1511 | 0 | size_t oldListSize = dst->issuerCrlsSize; |
1512 | 0 | UA_Boolean isContained = false; |
1513 | 0 | for(size_t i = 0; i < dst->issuerCrlsSize; i++) { |
1514 | 0 | for(size_t j = 0; j < src->issuerCrlsSize; j++) { |
1515 | 0 | if(UA_ByteString_equal(&dst->issuerCrls[i], &src->issuerCrls[j])) |
1516 | 0 | isContained = true; |
1517 | 0 | } |
1518 | 0 | if(!isContained) { |
1519 | 0 | UA_ByteString_copy(&dst->issuerCrls[i], &newList[newListSize]); |
1520 | 0 | newListSize += 1; |
1521 | 0 | } |
1522 | 0 | isContained = false; |
1523 | 0 | } |
1524 | 0 | if(newListSize < dst->issuerCrlsSize) { |
1525 | 0 | if(newListSize == 0) { |
1526 | 0 | UA_free(newList); |
1527 | 0 | newList = NULL; |
1528 | 0 | } else { |
1529 | 0 | retval = UA_Array_resize((void**)&newList, &oldListSize, newListSize, &UA_TYPES[UA_TYPES_BYTESTRING]); |
1530 | 0 | if(retval != UA_STATUSCODE_GOOD) { |
1531 | 0 | UA_free(newList); |
1532 | 0 | return retval; |
1533 | 0 | } |
1534 | 0 | } |
1535 | 0 | } |
1536 | 0 | UA_Array_delete(dst->issuerCrls, dst->issuerCrlsSize, &UA_TYPES[UA_TYPES_BYTESTRING]); |
1537 | 0 | dst->issuerCrlsSize = 0; |
1538 | 0 | dst->issuerCrls = newList; |
1539 | 0 | dst->issuerCrlsSize = newListSize; |
1540 | 0 | } |
1541 | | |
1542 | 0 | return retval; |
1543 | 0 | } |
1544 | | |
1545 | | UA_UInt32 |
1546 | 0 | UA_TrustListDataType_getSize(const UA_TrustListDataType *trustList) { |
1547 | 0 | UA_UInt32 size = 0; |
1548 | 0 | for(size_t i = 0; i < trustList->trustedCertificatesSize; i++) { |
1549 | 0 | size += (UA_UInt32)trustList->trustedCertificates[i].length; |
1550 | 0 | } |
1551 | 0 | for(size_t i = 0; i < trustList->trustedCrlsSize; i++) { |
1552 | 0 | size += (UA_UInt32)trustList->trustedCrls[i].length; |
1553 | 0 | } |
1554 | 0 | for(size_t i = 0; i < trustList->issuerCertificatesSize; i++) { |
1555 | 0 | size += (UA_UInt32)trustList->issuerCertificates[i].length; |
1556 | 0 | } |
1557 | 0 | for(size_t i = 0; i < trustList->issuerCrlsSize; i++) { |
1558 | 0 | size += (UA_UInt32)trustList->issuerCrls[i].length; |
1559 | 0 | } |
1560 | 0 | return size; |
1561 | 0 | } |