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