Coverage Report

Created: 2025-10-10 07:13

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