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