Coverage Report

Created: 2026-11-05 07:00

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