Coverage Report

Created: 2026-08-30 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open62541_15/plugins/ua_config_json.c
Line
Count
Source
1
/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
2
 * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
3
 *
4
 *    Copyright 2023 (c) Fraunhofer IOSB (Author: Noel Graf)
5
 *    Copyright 2025 (c) o6 Automation GmbH (Author: Julius Pfrommer)
6
 */
7
8
#include <open62541/plugin/log.h>
9
#include <open62541/server_config_file_based.h>
10
#include "cj5.h"
11
#include "open62541/server_config_default.h"
12
#ifdef UA_ENABLE_ENCRYPTION
13
#include "open62541/plugin/certificategroup_default.h"
14
#endif
15
16
218
#define MAX_TOKENS 1024
17
18
typedef struct {
19
    const char *json;
20
    const cj5_token *tokens;
21
    size_t tokensSize;
22
    size_t index;
23
    UA_Byte depth;
24
    cj5_result result;
25
26
    UA_Logger *logging;
27
} ParsingCtx;
28
29
static UA_ByteString
30
41.7k
getJsonPart(cj5_token tok, const char *json) {
31
41.7k
    UA_ByteString bs;
32
41.7k
    UA_ByteString_init(&bs);
33
41.7k
    if(tok.type == CJ5_TOKEN_STRING) {
34
3.88k
        bs.data = (UA_Byte*)(uintptr_t)(json + tok.start - 1);
35
3.88k
        bs.length = (tok.end - tok.start) + 3;
36
3.88k
        return bs;
37
37.8k
    } else {
38
37.8k
        bs.data = (UA_Byte*)(uintptr_t)(json + tok.start);
39
37.8k
        bs.length = (tok.end - tok.start) + 1;
40
37.8k
        return bs;
41
37.8k
    }
42
41.7k
}
43
44
/* Advances ctx->index and returns the next token, or a zeroed size-0
45
 * sentinel once the stream is exhausted. A field's declared child count
46
 * can desync from the actual token count, so this stays in bounds of
47
 * `tokens[MAX_TOKENS]` even then. */
48
static cj5_token
49
857k
nextToken(ParsingCtx *ctx) {
50
857k
    if(!ctx->tokens || ctx->index + 1 >= ctx->tokensSize) {
51
830k
        ctx->index = ctx->tokensSize;
52
830k
        cj5_token empty;
53
830k
        memset(&empty, 0, sizeof(empty));
54
830k
        return empty;
55
830k
    }
56
26.8k
    ctx->index++;
57
26.8k
    return ctx->tokens[ctx->index];
58
857k
}
59
60
/* Forward declarations*/
61
#define PARSE_JSON(TYPE) static UA_StatusCode                   \
62
    TYPE##_parseJson(ParsingCtx *ctx, void *configField, size_t *configFieldSize)
63
64
typedef UA_StatusCode
65
(*parseJsonSignature)(ParsingCtx *ctx, void *configField, size_t *configFieldSize);
66
67
#ifdef UA_ENABLE_ENCRYPTION
68
static UA_ByteString
69
loadCertificateFile(const char *const path);
70
#endif
71
72
/* The DataType "kind" is an internal type classification. It is used to
73
 * dispatch handling to the correct routines. */
74
#define UA_SERVERCONFIGFIELDKINDS 25
75
typedef enum {
76
    /* Basic Types */
77
    UA_SERVERCONFIGFIELD_INT64 = 0,
78
    UA_SERVERCONFIGFIELD_UINT16,
79
    UA_SERVERCONFIGFIELD_UINT32,
80
    UA_SERVERCONFIGFIELD_UINT64,
81
    UA_SERVERCONFIGFIELD_STRING,
82
    UA_SERVERCONFIGFIELD_LOCALIZEDTEXT,
83
    UA_SERVERCONFIGFIELD_DOUBLE,
84
    UA_SERVERCONFIGFIELD_BOOLEAN,
85
    UA_SERVERCONFIGFIELD_DURATION,
86
    UA_SERVERCONFIGFIELD_DURATIONRANGE,
87
    UA_SERVERCONFIGFIELD_UINT32RANGE,
88
89
    /* Advanced Types */
90
    UA_SERVERCONFIGFIELD_BUILDINFO,
91
    UA_SERVERCONFIGFIELD_APPLICATIONDESCRIPTION,
92
    UA_SERVERCONFIGFIELD_STRINGARRAY,
93
    UA_SERVERCONFIGFIELD_UINT32ARRAY,
94
    UA_SERVERCONFIGFIELD_DATETIME,
95
    UA_SERVERCONFIGFIELD_SUBSCRIPTIONCONFIGURATION,
96
    UA_SERVERCONFIGFIELD_TCPCONFIGURATION,
97
    UA_SERVERCONFIGFIELD_PUBSUBCONFIGURATION,
98
    UA_SERVERCONFIGFIELD_HISTORIZINGCONFIGURATION,
99
    UA_SERVERCONFIGFIELD_MDNSCONFIGURATION,
100
    UA_SERVERCONFIGFIELD_SECURITYPOLICIES,
101
    UA_SERVERCONFIGFIELD_SECURITYPKI,
102
103
    /* Enumerations */
104
    UA_SERVERCONFIGFIELD_APPLICATIONTYPE,
105
    UA_SERVERCONFIGFIELD_RULEHANDLING
106
} UA_ServerConfigFieldKind;
107
108
extern const parseJsonSignature parseJsonJumpTable[UA_SERVERCONFIGFIELDKINDS];
109
110
/*----------------------Basic Types------------------------*/
111
0
PARSE_JSON(Int64Field) {
112
0
    cj5_token tok = nextToken(ctx);
113
0
    UA_ByteString buf = getJsonPart(tok, ctx->json);
114
0
    UA_Int64 out;
115
0
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_INT64], NULL);
116
0
    if(retval != UA_STATUSCODE_GOOD)
117
0
        return retval;
118
0
    UA_Int64 *field = (UA_Int64*)configField;
119
0
    *field = out;
120
0
    return retval;
121
0
}
122
168
PARSE_JSON(UInt16Field) {
123
168
    cj5_token tok = nextToken(ctx);
124
168
    UA_ByteString buf = getJsonPart(tok, ctx->json);
125
168
    UA_UInt16 out;
126
168
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT16], NULL);
127
168
    if(retval != UA_STATUSCODE_GOOD)
128
139
        return retval;
129
29
    UA_UInt16 *field = (UA_UInt16*)configField;
130
29
    *field = out;
131
29
    return retval;
132
168
}
133
299
PARSE_JSON(UInt32Field) {
134
299
    cj5_token tok = nextToken(ctx);
135
299
    UA_ByteString buf = getJsonPart(tok, ctx->json);
136
299
    UA_UInt32 out;
137
299
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT32], NULL);
138
299
    if(retval != UA_STATUSCODE_GOOD)
139
109
        return retval;
140
190
    UA_UInt32 *field = (UA_UInt32*)configField;
141
190
    *field = out;
142
190
    return retval;
143
299
}
144
41
PARSE_JSON(UInt64Field) {
145
41
    cj5_token tok = nextToken(ctx);
146
41
    UA_ByteString buf = getJsonPart(tok, ctx->json);
147
41
    UA_UInt64 out;
148
41
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT64], NULL);
149
41
    if(retval != UA_STATUSCODE_GOOD)
150
26
        return retval;
151
15
    UA_UInt64 *field = (UA_UInt64*)configField;
152
15
    *field = out;
153
15
    return retval;
154
41
}
155
37.6k
PARSE_JSON(StringField) {
156
37.6k
    cj5_token tok = nextToken(ctx);
157
37.6k
    UA_ByteString buf = getJsonPart(tok, ctx->json);
158
37.6k
    UA_String out;
159
37.6k
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_STRING], NULL);
160
37.6k
    if(retval != UA_STATUSCODE_GOOD)
161
37.1k
        return retval;
162
458
    UA_String *field = (UA_String*)configField;
163
458
    if(field != NULL) {
164
458
        UA_String_clear(field);
165
458
        *field = out;
166
458
    }
167
458
    return retval;
168
37.6k
}
169
0
PARSE_JSON(LocalizedTextField) {
170
    /*
171
     applicationName: {
172
        locale: "de-DE",
173
        text: "Test text"
174
    }
175
     */
176
0
    cj5_token tok = nextToken(ctx);
177
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
178
0
    UA_String locale = {.length = 0, .data = NULL};
179
0
    UA_String text = {.length = 0, .data = NULL};
180
0
    for(size_t j = tok.size/2; j > 0; j--) {
181
0
        tok = nextToken(ctx);
182
0
        switch (tok.type) {
183
0
        case CJ5_TOKEN_STRING: {
184
0
            char *field = (char*)UA_malloc(tok.size + 1);
185
0
            unsigned int str_len = 0;
186
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field, &str_len);
187
188
0
            tok = nextToken(ctx);
189
0
            UA_ByteString buf = getJsonPart(tok, ctx->json);
190
0
            if(strcmp(field, "locale") == 0)
191
0
                retval |= UA_decodeJson(&buf, &locale, &UA_TYPES[UA_TYPES_STRING], NULL);
192
0
            else if(strcmp(field, "text") == 0)
193
0
                retval |= UA_decodeJson(&buf, &text, &UA_TYPES[UA_TYPES_STRING], NULL);
194
0
            else {
195
0
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
196
0
            }
197
0
            UA_free(field);
198
0
            break;
199
0
        }
200
0
        default:
201
0
            break;
202
0
        }
203
0
    }
204
0
    UA_LocalizedText out;
205
0
    out.locale = locale;
206
0
    out.text = text;
207
0
    if(retval != UA_STATUSCODE_GOOD)
208
0
        return retval;
209
0
    UA_LocalizedText *field = (UA_LocalizedText*)configField;
210
0
    if(field != NULL) {
211
0
        UA_LocalizedText_clear(field);
212
0
        *field = out;
213
0
    }
214
0
    return retval;
215
0
}
216
159
PARSE_JSON(DoubleField) {
217
159
    cj5_token tok = nextToken(ctx);
218
159
    UA_ByteString buf = getJsonPart(tok, ctx->json);
219
159
    UA_Double out;
220
159
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_DOUBLE], NULL);
221
159
    if(retval != UA_STATUSCODE_GOOD)
222
36
        return retval;
223
123
    UA_Double *field = (UA_Double *)configField;
224
123
    *field = out;
225
123
    return retval;
226
159
}
227
17
PARSE_JSON(BooleanField) {
228
17
    cj5_token tok = nextToken(ctx);
229
17
    UA_ByteString buf = getJsonPart(tok, ctx->json);
230
17
    UA_Boolean out;
231
17
    if(tok.type != CJ5_TOKEN_BOOL) {
232
17
        UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Value of type bool expected.");
233
17
        return UA_STATUSCODE_BADTYPEMISMATCH;
234
17
    }
235
0
    UA_String val = UA_STRING("true");
236
0
    if(UA_String_equal(&val, &buf)) {
237
0
        out = true;
238
0
    }else {
239
0
        out = false;
240
0
    }
241
    /* set server config field */
242
0
    UA_Boolean *field = (UA_Boolean *)configField;
243
0
    *field = out;
244
0
    return UA_STATUSCODE_GOOD;
245
17
}
246
7
PARSE_JSON(DurationField) {
247
7
    UA_Double double_value;
248
7
    UA_StatusCode retval = DoubleField_parseJson(ctx, &double_value, NULL);
249
7
    if(retval != UA_STATUSCODE_GOOD)
250
4
        return retval;
251
3
    UA_Duration *field = (UA_Duration*)configField;
252
3
    *field = (UA_Duration)double_value;
253
3
    return retval;
254
7
}
255
PARSE_JSON(DurationRangeField) {
256
    UA_DurationRange *field = (UA_DurationRange*)configField;
257
    cj5_token tok = nextToken(ctx);
258
    for(size_t j = tok.size/2; j > 0; j--) {
259
        tok = nextToken(ctx);
260
        switch (tok.type) {
261
        case CJ5_TOKEN_STRING: {
262
            char *field_str = (char*)UA_malloc(tok.size + 1);
263
            unsigned int str_len = 0;
264
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
265
            if(strcmp(field_str, "min") == 0)
266
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_DURATION](ctx, &field->min, NULL);
267
            else if(strcmp(field_str, "max") == 0)
268
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_DURATION](ctx, &field->max, NULL);
269
            else {
270
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
271
            }
272
            UA_free(field_str);
273
            break;
274
        }
275
        default:
276
            break;
277
        }
278
    }
279
    return UA_STATUSCODE_GOOD;
280
}
281
PARSE_JSON(UInt32RangeField) {
282
    UA_UInt32Range *field = (UA_UInt32Range*)configField;
283
    cj5_token tok = nextToken(ctx);
284
    for(size_t j = tok.size/2; j > 0; j--) {
285
        tok = nextToken(ctx);
286
        switch (tok.type) {
287
        case CJ5_TOKEN_STRING: {
288
            char *field_str = (char*)UA_malloc(tok.size + 1);
289
            unsigned int str_len = 0;
290
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
291
            if(strcmp(field_str, "min") == 0)
292
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &field->min, NULL);
293
            else if(strcmp(field_str, "max") == 0)
294
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &field->max, NULL);
295
            else {
296
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
297
            }
298
            UA_free(field_str);
299
            break;
300
        }
301
        default:
302
            break;
303
        }
304
    }
305
    return UA_STATUSCODE_GOOD;
306
}
307
308
/*----------------------Advanced Types------------------------*/
309
0
PARSE_JSON(BuildInfo) {
310
0
    UA_BuildInfo *field = (UA_BuildInfo*)configField;
311
0
    cj5_token tok = nextToken(ctx);
312
0
    for(size_t j = tok.size/2; j > 0; j--) {
313
0
        tok = nextToken(ctx);
314
0
        switch (tok.type) {
315
0
        case CJ5_TOKEN_STRING: {
316
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
317
0
            unsigned int str_len = 0;
318
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
319
0
            if(strcmp(field_str, "productUri") == 0)
320
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->productUri, NULL);
321
0
            else if(strcmp(field_str, "manufacturerName") == 0)
322
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->manufacturerName, NULL);
323
0
            else if(strcmp(field_str, "productName") == 0)
324
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->productName, NULL);
325
0
            else if(strcmp(field_str, "softwareVersion") == 0)
326
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->softwareVersion, NULL);
327
0
            else if(strcmp(field_str, "buildNumber") == 0)
328
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->buildNumber, NULL);
329
0
            else if(strcmp(field_str, "buildDate") == 0)
330
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_DATETIME](ctx, &field->buildDate, NULL);
331
0
            else {
332
0
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
333
0
            }
334
0
            UA_free(field_str);
335
0
            break;
336
0
        }
337
0
        default:
338
0
            break;
339
0
        }
340
0
    }
341
0
    return UA_STATUSCODE_GOOD;
342
0
}
343
0
PARSE_JSON(ApplicationDescriptionField) {
344
0
    UA_ApplicationDescription *field = (UA_ApplicationDescription*)configField;
345
0
    cj5_token tok = nextToken(ctx);
346
0
    for(size_t j = tok.size/2; j > 0; j--) {
347
0
        tok = nextToken(ctx);
348
0
        switch (tok.type) {
349
0
        case CJ5_TOKEN_STRING: {
350
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
351
0
            unsigned int str_len = 0;
352
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
353
0
            if(strcmp(field_str, "applicationUri") == 0)
354
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->applicationUri, NULL);
355
0
            else if(strcmp(field_str, "productUri") == 0)
356
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->productUri, NULL);
357
0
            else if(strcmp(field_str, "applicationName") == 0)
358
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_LOCALIZEDTEXT](ctx, &field->applicationName, NULL);
359
0
            else if(strcmp(field_str, "applicationType") == 0)
360
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_APPLICATIONTYPE](ctx, &field->applicationType, NULL);
361
0
            else if(strcmp(field_str, "gatewayServerUri") == 0)
362
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->gatewayServerUri, NULL);
363
0
            else if(strcmp(field_str, "discoveryProfileUri") == 0)
364
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->discoveryProfileUri, NULL);
365
0
            else if(strcmp(field_str, "discoveryUrls") == 0)
366
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRINGARRAY](ctx, &field->discoveryUrls, &field->discoveryUrlsSize);
367
0
            else {
368
0
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
369
0
            }
370
0
            UA_free(field_str);
371
0
            break;
372
0
        }
373
0
        default:
374
0
            break;
375
0
        }
376
0
    }
377
0
    return UA_STATUSCODE_GOOD;
378
0
}
379
24
PARSE_JSON(StringArrayField) {
380
24
    if(configFieldSize == NULL) {
381
0
        UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Pointer to the array size is not set.");
382
0
        return UA_STATUSCODE_BADARGUMENTSMISSING;
383
0
    }
384
24
    cj5_token tok = nextToken(ctx);
385
24
    UA_String *stringArray = (UA_String*)UA_malloc(sizeof(UA_String) * tok.size);
386
24
    size_t stringArraySize = 0;
387
37.3k
    for(size_t j = tok.size; j > 0; j--) {
388
37.2k
        UA_String out = {.length = 0, .data = NULL};
389
37.2k
        parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &out, NULL);
390
37.2k
        UA_String_copy(&out, &stringArray[stringArraySize++]);
391
37.2k
        UA_String_clear(&out);
392
37.2k
    }
393
    /* Add to the config */
394
24
    UA_String **field = (UA_String**)configField;
395
24
    if(*configFieldSize > 0) {
396
24
        UA_Array_delete(*field, *configFieldSize,
397
24
                        &UA_TYPES[UA_TYPES_STRING]);
398
24
        *field = NULL;
399
24
        *configFieldSize = 0;
400
24
    }
401
24
    UA_StatusCode retval =
402
24
        UA_Array_copy(stringArray, stringArraySize,
403
24
                      (void**)field, &UA_TYPES[UA_TYPES_STRING]);
404
24
    *configFieldSize = stringArraySize;
405
406
    /* Clean up */
407
24
    UA_Array_delete(stringArray, stringArraySize, &UA_TYPES[UA_TYPES_STRING]);
408
24
    return retval;
409
24
}
410
0
PARSE_JSON(UInt32ArrayField) {
411
0
    if(configFieldSize == NULL) {
412
0
        UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Pointer to the array size is not set.");
413
0
        return UA_STATUSCODE_BADARGUMENTSMISSING;
414
0
    }
415
0
    cj5_token tok = nextToken(ctx);
416
0
    UA_UInt32 *numberArray = (UA_UInt32*)UA_malloc(sizeof(UA_UInt32) * tok.size);
417
0
    size_t numberArraySize = 0;
418
0
    for(size_t j = tok.size; j > 0; j--) {
419
0
        UA_UInt32 value;
420
0
        UA_StatusCode retval = UInt32Field_parseJson(ctx, &value, NULL);
421
0
        if(retval != UA_STATUSCODE_GOOD)
422
0
            continue;
423
0
        numberArray[numberArraySize++] = value;
424
0
    }
425
    /* Add to the config */
426
0
    UA_UInt32 **field = (UA_UInt32**)configField;
427
0
    if(*configFieldSize > 0) {
428
0
        UA_Array_delete(*field, *configFieldSize,
429
0
                        &UA_TYPES[UA_TYPES_UINT32]);
430
0
        *field = NULL;
431
0
        *configFieldSize = 0;
432
0
    }
433
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
434
0
    if(numberArraySize > 0) {
435
0
        retval = UA_Array_copy(numberArray, numberArraySize,
436
0
                          (void **)field, &UA_TYPES[UA_TYPES_UINT32]);
437
0
        *configFieldSize = numberArraySize;
438
0
    }
439
    /* Clean up */
440
0
    UA_Array_delete(numberArray, numberArraySize, &UA_TYPES[UA_TYPES_UINT32]);
441
0
    return retval;
442
0
}
443
1
PARSE_JSON(DateTimeField) {
444
1
    cj5_token tok = nextToken(ctx);
445
1
    UA_ByteString buf = getJsonPart(tok, ctx->json);
446
1
    UA_DateTime out;
447
1
    UA_DateTime_init(&out);
448
1
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_DATETIME], NULL);
449
1
    if(retval != UA_STATUSCODE_GOOD)
450
1
        return retval;
451
0
    UA_DateTime *field = (UA_DateTime*)configField;
452
0
    *field = out;
453
0
    return retval;
454
1
}
455
456
287
PARSE_JSON(MdnsConfigurationField) {
457
287
#ifdef UA_ENABLE_DISCOVERY_MULTICAST
458
287
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
459
287
    cj5_token tok = nextToken(ctx);
460
144k
    for(size_t j = tok.size/2; j > 0; j--) {
461
144k
        tok = nextToken(ctx);
462
144k
        switch (tok.type) {
463
471
        case CJ5_TOKEN_STRING: {
464
471
            char *field_str = (char*)UA_malloc(tok.size + 1);
465
471
            unsigned int str_len = 0;
466
471
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
467
471
            if(strcmp(field_str, "mdnsServerName") == 0)
468
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &config->mdnsConfig.mdnsServerName, NULL);
469
471
            else if(strcmp(field_str, "serverCapabilities") == 0)
470
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRINGARRAY](ctx, &config->mdnsConfig.serverCapabilities, &config->mdnsConfig.serverCapabilitiesSize);
471
471
#ifdef UA_ENABLE_DISCOVERY_MULTICAST_MDNSD
472
471
            else if(strcmp(field_str, "mdnsInterfaceIP") == 0)
473
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &config->mdnsInterfaceIP, NULL);
474
            /* mdnsIpAddressList and mdnsIpAddressListSize are only available if UA_HAS_GETIFADDR is not defined: */
475
# if !defined(UA_HAS_GETIFADDR)
476
            else if(strcmp(field_str, "mdnsIpAddressList") == 0)
477
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32ARRAY](ctx, &config->mdnsIpAddressList, &config->mdnsIpAddressListSize);
478
# endif
479
471
#endif
480
471
            else {
481
471
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
482
471
            }
483
471
            UA_free(field_str);
484
471
            break;
485
0
        }
486
143k
        default:
487
143k
        break;
488
144k
        }
489
144k
    }
490
287
#endif
491
287
    return UA_STATUSCODE_GOOD;
492
287
}
493
494
4
PARSE_JSON(SubscriptionConfigurationField) {
495
4
#ifdef UA_ENABLE_SUBSCRIPTIONS
496
4
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
497
4
    cj5_token tok = nextToken(ctx);
498
90.5k
    for(size_t j = tok.size/2; j > 0; j--) {
499
90.5k
        tok = nextToken(ctx);
500
90.5k
        switch (tok.type) {
501
32
        case CJ5_TOKEN_STRING: {
502
32
            char *field_str = (char*)UA_malloc(tok.size + 1);
503
32
            unsigned int str_len = 0;
504
32
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
505
32
            if(strcmp(field_str, "maxSubscriptions") == 0)
506
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxSubscriptions, NULL);
507
32
            else if(strcmp(field_str, "maxSubscriptionsPerSession") == 0)
508
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxSubscriptionsPerSession, NULL);
509
32
            else if(strcmp(field_str, "publishingIntervalLimits") == 0)
510
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_DURATIONRANGE](ctx, &config->publishingIntervalLimits, NULL);
511
32
            else if(strcmp(field_str, "lifeTimeCountLimits") == 0)
512
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32RANGE](ctx, &config->lifeTimeCountLimits, NULL);
513
32
            else if(strcmp(field_str, "keepAliveCountLimits") == 0)
514
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32RANGE](ctx, &config->keepAliveCountLimits, NULL);
515
32
            else if(strcmp(field_str, "maxNotificationsPerPublish") == 0)
516
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxNotificationsPerPublish, NULL);
517
32
            else if(strcmp(field_str, "enableRetransmissionQueue") == 0)
518
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->enableRetransmissionQueue, NULL);
519
32
            else if(strcmp(field_str, "maxRetransmissionQueueSize") == 0)
520
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxRetransmissionQueueSize, NULL);
521
32
# ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
522
32
            else if(strcmp(field_str, "maxEventsPerNode") == 0)
523
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxEventsPerNode, NULL);
524
32
# endif
525
32
            else if(strcmp(field_str, "maxMonitoredItems") == 0)
526
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxMonitoredItems, NULL);
527
32
            else if(strcmp(field_str, "maxMonitoredItemsPerSubscription") == 0)
528
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxMonitoredItemsPerSubscription, NULL);
529
32
            else if(strcmp(field_str, "samplingIntervalLimits") == 0)
530
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_DURATIONRANGE](ctx, &config->samplingIntervalLimits, NULL);
531
32
            else if(strcmp(field_str, "queueSizeLimits") == 0)
532
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32RANGE](ctx, &config->queueSizeLimits, NULL);
533
32
            else if(strcmp(field_str, "maxPublishReqPerSession") == 0)
534
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxPublishReqPerSession, NULL);
535
32
            else {
536
32
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
537
32
            }
538
32
            UA_free(field_str);
539
32
            break;
540
0
        }
541
90.5k
        default:
542
90.5k
            break;
543
90.5k
        }
544
90.5k
    }
545
4
#endif
546
4
    return UA_STATUSCODE_GOOD;
547
4
}
548
549
3
PARSE_JSON(TcpConfigurationField) {
550
3
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
551
3
    cj5_token tok = nextToken(ctx);
552
26.7k
    for(size_t j = tok.size/2; j > 0; j--) {
553
26.7k
        tok = nextToken(ctx);
554
26.7k
        switch (tok.type) {
555
0
        case CJ5_TOKEN_STRING: {
556
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
557
0
            unsigned int str_len = 0;
558
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
559
0
            if(strcmp(field_str, "tcpBufSize") == 0)
560
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->tcpBufSize, NULL);
561
0
            else if(strcmp(field_str, "tcpMaxMsgSize") == 0)
562
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->tcpMaxMsgSize, NULL);
563
0
            else if(strcmp(field_str, "tcpMaxChunks") == 0)
564
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->tcpMaxChunks, NULL);
565
0
            else {
566
0
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
567
0
            }
568
0
            UA_free(field_str);
569
0
            break;
570
0
        }
571
26.7k
        default:
572
26.7k
            break;
573
26.7k
        }
574
26.7k
    }
575
3
    return UA_STATUSCODE_GOOD;
576
3
}
577
578
4
PARSE_JSON(PubsubConfigurationField) {
579
4
#ifdef UA_ENABLE_PUBSUB
580
4
    UA_PubSubConfiguration *field = (UA_PubSubConfiguration*)configField;
581
4
    cj5_token tok = nextToken(ctx);
582
503k
    for(size_t j = tok.size/2; j > 0; j--) {
583
503k
        tok = nextToken(ctx);
584
503k
        switch (tok.type) {
585
4
        case CJ5_TOKEN_STRING: {
586
4
            char *field_str = (char*)UA_malloc(tok.size + 1);
587
4
            unsigned int str_len = 0;
588
4
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
589
4
            if(strcmp(field_str, "enableDeltaFrames") == 0)
590
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &field->enableDeltaFrames, NULL);
591
4
#ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
592
4
            else if(strcmp(field_str, "enableInformationModelMethods") == 0)
593
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &field->enableInformationModelMethods, NULL);
594
4
#endif
595
4
            else {
596
4
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
597
4
            }
598
4
            UA_free(field_str);
599
4
            break;
600
0
        }
601
503k
        default:
602
503k
            break;
603
503k
        }
604
503k
    }
605
4
#endif
606
4
    return UA_STATUSCODE_GOOD;
607
4
}
608
609
1
PARSE_JSON(HistorizingConfigurationField) {
610
1
#ifdef UA_ENABLE_HISTORIZING
611
1
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
612
1
    cj5_token tok = nextToken(ctx);
613
3.58k
    for(size_t j = tok.size/2; j > 0; j--) {
614
3.58k
        tok = nextToken(ctx);
615
3.58k
        switch (tok.type) {
616
0
        case CJ5_TOKEN_STRING: {
617
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
618
0
            unsigned int str_len = 0;
619
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
620
0
            if(strcmp(field_str, "accessHistoryDataCapability") == 0)
621
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->accessHistoryDataCapability, NULL);
622
0
            else if(strcmp(field_str, "maxReturnDataValues") == 0)
623
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxReturnDataValues, NULL);
624
0
            else if(strcmp(field_str, "accessHistoryEventsCapability") == 0)
625
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->accessHistoryEventsCapability, NULL);
626
0
            else if(strcmp(field_str, "maxReturnEventValues") == 0)
627
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxReturnEventValues, NULL);
628
0
            else if(strcmp(field_str, "insertDataCapability") == 0)
629
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->insertDataCapability, NULL);
630
0
            else if(strcmp(field_str, "insertEventCapability") == 0)
631
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->insertEventCapability, NULL);
632
0
            else if(strcmp(field_str, "insertAnnotationsCapability") == 0)
633
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->insertAnnotationsCapability, NULL);
634
0
            else if(strcmp(field_str, "replaceDataCapability") == 0)
635
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->replaceDataCapability, NULL);
636
0
            else if(strcmp(field_str, "replaceEventCapability") == 0)
637
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->replaceEventCapability, NULL);
638
0
            else if(strcmp(field_str, "updateDataCapability") == 0)
639
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->updateDataCapability, NULL);
640
0
            else if(strcmp(field_str, "updateEventCapability") == 0)
641
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->updateEventCapability, NULL);
642
0
            else if(strcmp(field_str, "deleteRawCapability") == 0)
643
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->deleteRawCapability, NULL);
644
0
            else if(strcmp(field_str, "deleteEventCapability") == 0)
645
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->deleteEventCapability, NULL);
646
0
            else if(strcmp(field_str, "deleteAtTimeDataCapability") == 0)
647
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->deleteAtTimeDataCapability, NULL);
648
0
            else {
649
0
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
650
0
            }
651
0
            UA_free(field_str);
652
0
            break;
653
0
        }
654
3.58k
        default:
655
3.58k
            break;
656
3.58k
        }
657
3.58k
    }
658
1
#endif
659
1
    return UA_STATUSCODE_GOOD;
660
1
}
661
662
3
PARSE_JSON(SecurityPolciesField) {
663
3
#ifdef UA_ENABLE_ENCRYPTION
664
3
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
665
666
3
    UA_String noneuri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None");
667
3
    UA_String basic128Rsa15uri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15");
668
3
    UA_String basic256uri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Basic256");
669
3
    UA_String basic256Sha256uri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256");
670
3
    UA_String aes128sha256rsaoaepuri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Aes128_Sha256_RsaOaep");
671
3
    UA_String aes256sha256rsapssuri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Aes256_Sha256_RsaPss");
672
673
3
    cj5_token tok = nextToken(ctx);
674
3
    for(size_t j = tok.size; j > 0; j--) {
675
676
3
        UA_String policy = {.length = 0, .data = NULL};
677
3
        UA_ByteString certificate = {.length = 0, .data = NULL};
678
3
        UA_ByteString privateKey = {.length = 0, .data = NULL};
679
680
3
        tok = nextToken(ctx);
681
2.43k
        for(size_t i = tok.size / 2; i > 0; i--) {
682
2.42k
            tok = nextToken(ctx);
683
2.42k
            switch(tok.type) {
684
215
            case CJ5_TOKEN_STRING: {
685
215
                char *field_str = (char *)UA_malloc(tok.size + 1);
686
215
                unsigned int str_len = 0;
687
215
                cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
688
215
                if(strcmp(field_str, "certificate") == 0) {
689
0
                    UA_String out = {.length = 0, .data = NULL};
690
0
                    parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &out, NULL);
691
692
0
                    if(out.length > 0) {
693
0
                        char *certfile = (char *)UA_malloc(out.length + 1);
694
0
                        memcpy(certfile, out.data, out.length);
695
0
                        certfile[out.length] = '\0';
696
0
                        certificate = loadCertificateFile(certfile);
697
0
                        UA_String_clear(&out);
698
0
                        UA_free(certfile);
699
0
                    }
700
215
                } else if(strcmp(field_str, "privateKey") == 0) {
701
47
                    UA_String out = {.length = 0, .data = NULL};
702
47
                    parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &out, NULL);
703
704
47
                    if(out.length > 0) {
705
0
                        char *keyfile = (char *)UA_malloc(out.length + 1);
706
0
                        memcpy(keyfile, out.data, out.length);
707
0
                        keyfile[out.length] = '\0';
708
0
                        privateKey = loadCertificateFile(keyfile);
709
0
                        UA_String_clear(&out);
710
0
                        UA_free(keyfile);
711
0
                    }
712
168
                } else if(strcmp(field_str, "policy") == 0) {
713
0
                    parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &policy, NULL);
714
168
                } else {
715
168
                    UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
716
168
                }
717
215
                UA_free(field_str);
718
215
                break;
719
0
            }
720
2.21k
            default:
721
2.21k
                break;
722
2.42k
            }
723
2.42k
        }
724
725
3
        if(certificate.length == 0 || privateKey.length == 0) {
726
3
            UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION,
727
3
                           "Certificate and PrivateKey must be set for every policy.");
728
3
            if(policy.length > 0)
729
0
                UA_String_clear(&policy);
730
3
            if(certificate.length > 0)
731
0
                UA_ByteString_clear(&certificate);
732
3
            if(privateKey.length > 0)
733
0
                UA_ByteString_clear(&privateKey);
734
3
            return UA_STATUSCODE_BADINTERNALERROR;
735
3
        }
736
0
        UA_StatusCode retval = UA_STATUSCODE_GOOD;
737
0
        if(UA_String_equal(&policy, &noneuri)) {
738
            /* Nothing to do! */
739
0
        } else if(UA_String_equal(&policy, &basic128Rsa15uri)) {
740
0
            retval = UA_ServerConfig_addSecurityPolicyBasic128Rsa15(config, &certificate, &privateKey);
741
0
            if(retval != UA_STATUSCODE_GOOD) {
742
0
                UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION,
743
0
                               "Could not add SecurityPolicy#Basic128Rsa15 with error code %s",
744
0
                               UA_StatusCode_name(retval));
745
0
            }
746
0
        } else if(UA_String_equal(&policy, &basic256uri)) {
747
0
            retval = UA_ServerConfig_addSecurityPolicyBasic256(config, &certificate, &privateKey);
748
0
            if(retval != UA_STATUSCODE_GOOD) {
749
0
                UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION,
750
0
                               "Could not add SecurityPolicy#Basic256 with error code %s",
751
0
                               UA_StatusCode_name(retval));
752
0
            }
753
0
        } else if(UA_String_equal(&policy, &basic256Sha256uri)) {
754
0
            retval = UA_ServerConfig_addSecurityPolicyBasic256Sha256(config, &certificate, &privateKey);
755
0
            if(retval != UA_STATUSCODE_GOOD) {
756
0
                UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION,
757
0
                               "Could not add SecurityPolicy#Basic256Sha256 with error code %s",
758
0
                               UA_StatusCode_name(retval));
759
0
            }
760
0
        } else if(UA_String_equal(&policy, &aes128sha256rsaoaepuri)) {
761
0
            retval = UA_ServerConfig_addSecurityPolicyAes128Sha256RsaOaep(config, &certificate, &privateKey);
762
0
            if(retval != UA_STATUSCODE_GOOD) {
763
0
                UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION,
764
0
                               "Could not add SecurityPolicy#Aes128Sha256RsaOaep with error code %s",
765
0
                               UA_StatusCode_name(retval));
766
0
            }
767
0
        } else if(UA_String_equal(&policy, &aes256sha256rsapssuri)) {
768
0
             retval = UA_ServerConfig_addSecurityPolicyAes256Sha256RsaPss(config, &certificate, &privateKey);
769
0
             if(retval != UA_STATUSCODE_GOOD) {
770
0
                 UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION,
771
0
                                "Could not add SecurityPolicy#Aes256Sha256RsaPss with error code %s",
772
0
                               UA_StatusCode_name(retval));
773
0
            }
774
0
        } else {
775
0
            UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown Security Policy.");
776
0
        }
777
778
        /* Add all Endpoints */
779
0
        UA_ServerConfig_addAllEndpoints(config);
780
781
0
        if(policy.length > 0)
782
0
            UA_String_clear(&policy);
783
0
        if(certificate.length > 0)
784
0
            UA_ByteString_clear(&certificate);
785
0
        if(privateKey.length > 0)
786
0
            UA_ByteString_clear(&privateKey);
787
0
    }
788
0
#endif
789
0
    return UA_STATUSCODE_GOOD;
790
3
}
791
792
3.39k
PARSE_JSON(SecurityPkiField) {
793
3.39k
#ifdef UA_ENABLE_ENCRYPTION
794
3.39k
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
795
3.39k
    UA_String pkiFolder = {.length = 0, .data = NULL};
796
797
3.39k
    cj5_token tok = nextToken(ctx);
798
3.39k
    UA_ByteString buf = getJsonPart(tok, ctx->json);
799
3.39k
    UA_StatusCode retval = UA_decodeJson(&buf, &pkiFolder, &UA_TYPES[UA_TYPES_STRING], NULL);
800
3.39k
    if(retval != UA_STATUSCODE_GOOD)
801
20
        return retval;
802
803
3.37k
#if defined(__linux__) || defined(UA_ARCHITECTURE_WIN32) || defined(__APPLE__) || defined(__OpenBSD__)
804
    /* Set up the parameters for the filestore certificate store */
805
3.37k
    UA_KeyValuePair params[2];
806
3.37k
    size_t paramsSize = 2;
807
808
3.37k
    params[0].key = UA_QUALIFIEDNAME(0, "max-trust-listsize");
809
3.37k
    UA_Variant_setScalar(&params[0].value, &config->maxTrustListSize, &UA_TYPES[UA_TYPES_UINT32]);
810
3.37k
    params[1].key = UA_QUALIFIEDNAME(0, "max-rejected-listsize");
811
3.37k
    UA_Variant_setScalar(&params[1].value, &config->maxRejectedListSize, &UA_TYPES[UA_TYPES_UINT32]);
812
813
3.37k
    UA_KeyValueMap paramsMap;
814
3.37k
    paramsMap.map = params;
815
3.37k
    paramsMap.mapSize = paramsSize;
816
817
    /* set server config field */
818
3.37k
    UA_NodeId defaultApplicationGroup =
819
3.37k
           UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP);
820
3.37k
    retval = UA_CertificateGroup_Filestore(&config->secureChannelPKI, &defaultApplicationGroup,
821
3.37k
                                           pkiFolder, config->logging, &paramsMap);
822
3.37k
    if(retval != UA_STATUSCODE_GOOD) {
823
100
        UA_String_clear(&pkiFolder);
824
100
        return retval;
825
100
    }
826
827
3.27k
    UA_NodeId defaultUserTokenGroup =
828
3.27k
            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP);
829
3.27k
    retval = UA_CertificateGroup_Filestore(&config->sessionPKI, &defaultUserTokenGroup,
830
3.27k
                                            pkiFolder, config->logging, &paramsMap);
831
3.27k
    if(retval != UA_STATUSCODE_GOOD) {
832
1
        UA_String_clear(&pkiFolder);
833
1
        return retval;
834
1
    }
835
836
    /* Clean up */
837
3.27k
    UA_String_clear(&pkiFolder);
838
#else
839
    (void)config;
840
    UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION,
841
                   "pkiFolder is not supported on this platform. "
842
                   "Trusted clients will not be verified.");
843
#endif
844
#else
845
    UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION,
846
                   "pkiFolder is set in the config but UA_ENABLE_ENCRYPTION "
847
                   "is not enabled. Trusted clients will not be verified.");
848
#endif
849
3.27k
    return UA_STATUSCODE_GOOD;
850
3.27k
}
851
852
/*----------------------Enumerations------------------------*/
853
0
PARSE_JSON(ApplicationTypeField) {
854
0
    UA_UInt32 enum_value;
855
0
    UA_StatusCode retval = UInt32Field_parseJson(ctx, &enum_value, NULL);
856
0
    if(retval != UA_STATUSCODE_GOOD)
857
0
        return retval;
858
0
    UA_ApplicationType *field = (UA_ApplicationType*)configField;
859
0
    *field = (UA_ApplicationType)enum_value;
860
0
    return retval;
861
0
}
862
86
PARSE_JSON(RuleHandlingField) {
863
86
    UA_UInt32 enum_value;
864
86
    UA_StatusCode retval = UInt32Field_parseJson(ctx, &enum_value, NULL);
865
86
    if(retval != UA_STATUSCODE_GOOD)
866
59
        return retval;
867
27
    UA_RuleHandling *field = (UA_RuleHandling*)configField;
868
27
    *field = (UA_RuleHandling)enum_value;
869
27
    return retval;
870
86
}
871
872
const parseJsonSignature parseJsonJumpTable[UA_SERVERCONFIGFIELDKINDS] = {
873
    /* Basic Types */
874
    (parseJsonSignature)Int64Field_parseJson,
875
    (parseJsonSignature)UInt16Field_parseJson,
876
    (parseJsonSignature)UInt32Field_parseJson,
877
    (parseJsonSignature)UInt64Field_parseJson,
878
    (parseJsonSignature)StringField_parseJson,
879
    (parseJsonSignature)LocalizedTextField_parseJson,
880
    (parseJsonSignature)DoubleField_parseJson,
881
    (parseJsonSignature)BooleanField_parseJson,
882
    (parseJsonSignature)DurationField_parseJson,
883
    (parseJsonSignature)DurationRangeField_parseJson,
884
    (parseJsonSignature)UInt32RangeField_parseJson,
885
886
    /* Advanced Types */
887
    (parseJsonSignature)BuildInfo_parseJson,
888
    (parseJsonSignature)ApplicationDescriptionField_parseJson,
889
    (parseJsonSignature)StringArrayField_parseJson,
890
    (parseJsonSignature)UInt32ArrayField_parseJson,
891
    (parseJsonSignature)DateTimeField_parseJson,
892
    (parseJsonSignature)SubscriptionConfigurationField_parseJson,
893
    (parseJsonSignature)TcpConfigurationField_parseJson,
894
    (parseJsonSignature)PubsubConfigurationField_parseJson,
895
    (parseJsonSignature)HistorizingConfigurationField_parseJson,
896
    (parseJsonSignature)MdnsConfigurationField_parseJson,
897
    (parseJsonSignature)SecurityPolciesField_parseJson,
898
    (parseJsonSignature)SecurityPkiField_parseJson,
899
900
    /* Enumerations */
901
    (parseJsonSignature)ApplicationTypeField_parseJson,
902
    (parseJsonSignature)RuleHandlingField_parseJson,
903
};
904
905
/* Skips unknown item (simple, object or array) in config file. 
906
* Unknown items may happen if we don't support some features. 
907
* E.g. if  UA_ENABLE_ENCRYPTION is not defined and config file 
908
* contains "securityPolicies" entry.
909
*/
910
static void
911
82
skipUnknownItem(ParsingCtx* ctx) {
912
82
    unsigned int end = ctx->tokens[ctx->index].end;
913
110
    do {
914
110
        ctx->index++;
915
110
    } while (ctx->index < ctx->tokensSize &&
916
85
        ctx->tokens[ctx->index].start < end);
917
82
}
918
919
static UA_StatusCode
920
218
parseJSONConfig(UA_ServerConfig *config, UA_ByteString json_config) {
921
    // Parsing json config
922
218
    const char *json = (const char*)json_config.data;
923
218
    cj5_token tokens[MAX_TOKENS];
924
218
    cj5_result r = cj5_parse(json, (unsigned int)json_config.length, tokens, MAX_TOKENS, NULL);
925
926
    /* Validate the parse result: must succeed and produce a root object
927
     * with at least one key-value pair (i.e. >= 2 tokens). */
928
218
    if(r.error != CJ5_ERROR_NONE || r.num_tokens < 2 ||
929
89
       r.tokens[0].type != CJ5_TOKEN_OBJECT)
930
129
        return UA_STATUSCODE_BADDECODINGERROR;
931
932
89
    ParsingCtx ctx;
933
89
    ctx.json = json;
934
89
    ctx.result = r;
935
89
    ctx.tokens = r.tokens;
936
89
    ctx.tokensSize = r.num_tokens;
937
89
    ctx.index = 1; // The first token is ignored because it is known and not needed.
938
939
89
    ctx.logging = config->logging;
940
941
    /* Buffer for the field name */
942
89
    char field[256];
943
944
89
    size_t serverConfigSize = 0;
945
89
    if(ctx.tokens)
946
89
        serverConfigSize = (ctx.tokens[ctx.index-1].size/2);
947
89
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
948
1.53k
    for (size_t j = serverConfigSize; j > 0 && ctx.index < ctx.tokensSize; j--) {
949
1.45k
        cj5_token tok = ctx.tokens[ctx.index];
950
1.45k
        switch (tok.type) {
951
1.21k
            case CJ5_TOKEN_STRING: {
952
1.21k
                if(tok.size >= 255) {
953
44
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
954
44
                                   "Configuration field name too long");
955
44
                    continue;
956
44
                }
957
1.16k
                unsigned int str_len = 0;
958
1.16k
                cj5_error_code res = cj5_get_str(&ctx.result, (unsigned int)ctx.index, field, &str_len);
959
1.16k
                if(res != CJ5_ERROR_NONE) {
960
749
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
961
749
                                   "Configuration field name not a valid string");
962
749
                    continue;
963
749
                }
964
419
                if(strcmp(field, "buildInfo") == 0)
965
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BUILDINFO](&ctx, &config->buildInfo, NULL);
966
419
                else if(strcmp(field, "applicationDescription") == 0)
967
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_APPLICATIONDESCRIPTION](&ctx, &config->applicationDescription, NULL);
968
419
                else if(strcmp(field, "shutdownDelay") == 0)
969
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_DOUBLE](&ctx, &config->shutdownDelay, NULL);
970
419
                else if(strcmp(field, "verifyRequestTimestamp") == 0)
971
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_RULEHANDLING](&ctx, &config->verifyRequestTimestamp, NULL);
972
419
                else if(strcmp(field, "allowEmptyVariables") == 0)
973
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_RULEHANDLING](&ctx, &config->allowEmptyVariables, NULL);
974
419
                else if(strcmp(field, "serverUrls") == 0)
975
24
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRINGARRAY](&ctx, &config->serverUrls, &config->serverUrlsSize);
976
395
                else if(strcmp(field, "tcpEnabled") == 0)
977
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->tcpEnabled, NULL);
978
395
                else if(strcmp(field, "tcp") == 0)
979
3
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_TCPCONFIGURATION](&ctx, config, NULL);
980
392
                else if(strcmp(field, "securityPolicyNoneDiscoveryOnly") == 0)
981
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->securityPolicyNoneDiscoveryOnly, NULL);
982
392
                else if(strcmp(field, "modellingRulesOnInstances") == 0)
983
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->modellingRulesOnInstances, NULL);
984
392
                else if(strcmp(field, "maxSecureChannels") == 0)
985
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT16](&ctx, &config->maxSecureChannels, NULL);
986
392
                else if(strcmp(field, "maxSecurityTokenLifetime") == 0)
987
10
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxSecurityTokenLifetime, NULL);
988
382
                else if(strcmp(field, "maxSessions") == 0)
989
1
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT16](&ctx, &config->maxSessions, NULL);
990
381
                else if(strcmp(field, "maxSessionTimeout") == 0)
991
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_DOUBLE](&ctx, &config->maxSessionTimeout, NULL);
992
381
                else if(strcmp(field, "maxNodesPerRead") == 0)
993
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerRead, NULL);
994
381
                else if(strcmp(field, "maxNodesPerWrite") == 0)
995
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerWrite, NULL);
996
381
                else if(strcmp(field, "maxNodesPerMethodCall") == 0)
997
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerMethodCall, NULL);
998
381
                else if(strcmp(field, "maxNodesPerBrowse") == 0)
999
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerBrowse, NULL);
1000
381
                else if(strcmp(field, "maxNodesPerRegisterNodes") == 0)
1001
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerRegisterNodes, NULL);
1002
381
                else if(strcmp(field, "maxNodesPerTranslateBrowsePathsToNodeIds") == 0)
1003
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerTranslateBrowsePathsToNodeIds, NULL);
1004
381
                else if(strcmp(field, "maxNodesPerNodeManagement") == 0)
1005
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerNodeManagement, NULL);
1006
381
                else if(strcmp(field, "maxMonitoredItemsPerCall") == 0)
1007
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxMonitoredItemsPerCall, NULL);
1008
381
                else if(strcmp(field, "maxReferencesPerNode") == 0)
1009
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxReferencesPerNode, NULL);
1010
381
                else if(strcmp(field, "reverseReconnectInterval") == 0)
1011
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->reverseReconnectInterval, NULL);
1012
1013
381
#if UA_MULTITHREADING >= 100
1014
381
                else if(strcmp(field, "asyncOperationTimeout") == 0)
1015
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_DOUBLE](&ctx, &config->asyncOperationTimeout, NULL);
1016
381
                else if(strcmp(field, "maxAsyncOperationQueueSize") == 0)
1017
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT64](&ctx, &config->maxAsyncOperationQueueSize, NULL);
1018
381
#endif
1019
1020
381
#ifdef UA_ENABLE_DISCOVERY
1021
381
                else if(strcmp(field, "discoveryCleanupTimeout") == 0)
1022
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->discoveryCleanupTimeout, NULL);
1023
381
#ifdef UA_ENABLE_DISCOVERY_MULTICAST
1024
381
                else if(strcmp(field, "mdnsEnabled") == 0)
1025
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->mdnsEnabled, NULL);
1026
381
                else if(strcmp(field, "mdns") == 0)
1027
287
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_MDNSCONFIGURATION](&ctx, config, NULL);
1028
94
#endif
1029
94
#endif
1030
1031
94
#ifdef UA_ENABLE_SUBSCRIPTIONS
1032
94
                else if(strcmp(field, "subscriptionsEnabled") == 0)
1033
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->subscriptionsEnabled, NULL);
1034
94
                else if(strcmp(field, "subscriptions") == 0)
1035
4
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_SUBSCRIPTIONCONFIGURATION](&ctx, config, NULL);
1036
90
# endif
1037
1038
90
#ifdef UA_ENABLE_HISTORIZING
1039
90
                else if(strcmp(field, "historizingEnabled") == 0)
1040
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->historizingEnabled, NULL);
1041
90
                else if(strcmp(field, "historizing") == 0)
1042
1
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_HISTORIZINGCONFIGURATION](&ctx, config, NULL);
1043
89
#endif
1044
1045
89
#ifdef UA_ENABLE_PUBSUB
1046
89
                else if(strcmp(field, "pubsubEnabled") == 0)
1047
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->pubsubEnabled, NULL);
1048
89
                else if(strcmp(field, "pubsub") == 0)
1049
4
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_PUBSUBCONFIGURATION](&ctx, &config->pubSubConfig, NULL);
1050
85
#endif
1051
85
#ifdef UA_ENABLE_ENCRYPTION
1052
85
                else if(strcmp(field, "securityPolicies") == 0)
1053
3
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_SECURITYPOLICIES](&ctx, config, NULL);
1054
82
                else if(strcmp(field, "pkiFolder") == 0)
1055
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_SECURITYPKI](&ctx, config, NULL);
1056
82
#endif
1057
82
                else {
1058
82
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
1059
82
                                   "Field name '%s' unknown or misspelled. Maybe the feature is not enabled.", field);
1060
                    /* skip the name of item */
1061
82
                    ++ctx.index;
1062
                    /* skip value of unknown item */
1063
82
                    skipUnknownItem(&ctx);
1064
                    /* after skipUnknownItem() ctx->index points to the name of the following item.
1065
                       We must decrement index in oder following increment will
1066
                       still set index to the right position (name of the following item) */
1067
82
                    --ctx.index;
1068
82
                }
1069
419
                if(retval != UA_STATUSCODE_GOOD) {
1070
14
                    UA_LOG_ERROR(ctx.logging, UA_LOGCATEGORY_APPLICATION,
1071
14
                                 "An error occurred while parsing the configuration field %s", field);
1072
14
                    return retval;
1073
14
                }
1074
405
                break;
1075
419
            }
1076
405
            default:
1077
246
                break;
1078
1.45k
        }
1079
651
        ctx.index += 1;
1080
651
    }
1081
75
    return retval;
1082
89
}
1083
1084
UA_Server *
1085
7.62k
UA_Server_newFromFile(const UA_ByteString json_config) {
1086
7.62k
    UA_ServerConfig config;
1087
7.62k
    memset(&config, 0, sizeof(UA_ServerConfig));
1088
7.62k
    UA_StatusCode res = UA_ServerConfig_setDefault(&config);
1089
7.62k
    res |= parseJSONConfig(&config, json_config);
1090
7.62k
    if(res != UA_STATUSCODE_GOOD) {
1091
1.35k
        UA_ServerConfig_clear(&config);
1092
1.35k
        return NULL;
1093
1.35k
    }
1094
6.27k
    return UA_Server_newWithConfig(&config);
1095
7.62k
}
1096
1097
UA_StatusCode
1098
0
UA_ServerConfig_updateFromFile(UA_ServerConfig *config, const UA_ByteString json_config) {
1099
0
    UA_StatusCode res = parseJSONConfig(config, json_config);
1100
0
    return res;
1101
0
}
1102
1103
#ifdef UA_ENABLE_ENCRYPTION
1104
static UA_ByteString
1105
0
loadCertificateFile(const char *const path) {
1106
0
    UA_ByteString fileContents = UA_BYTESTRING_NULL;
1107
1108
    /* Open the file */
1109
0
    FILE *fp = fopen(path, "rb");
1110
0
    if(!fp) {
1111
0
        errno = 0; /* We read errno also from the tcp layer... */
1112
0
        return fileContents;
1113
0
    }
1114
1115
    /* Get the file length, allocate the data and read */
1116
0
    if(fseek(fp, 0, SEEK_END) != 0) {
1117
0
        fclose(fp);
1118
0
        errno = 0;
1119
0
        return fileContents;
1120
0
    }
1121
1122
0
    long length = ftell(fp);
1123
0
    if(length < 0) {
1124
0
        fclose(fp);
1125
0
        errno = 0;
1126
0
        return fileContents;
1127
0
    }
1128
1129
0
    fileContents.length = (size_t)length;
1130
0
    fileContents.data = (UA_Byte *)UA_malloc(fileContents.length * sizeof(UA_Byte));
1131
0
    if(fileContents.data) {
1132
0
        if(fseek(fp, 0, SEEK_SET) != 0) {
1133
0
            fclose(fp);
1134
0
            UA_ByteString_clear(&fileContents);
1135
0
            errno = 0;
1136
0
            return fileContents;
1137
0
        }
1138
0
        size_t read = fread(fileContents.data, sizeof(UA_Byte), fileContents.length, fp);
1139
0
        if(read != fileContents.length)
1140
0
            UA_ByteString_clear(&fileContents);
1141
0
    } else {
1142
0
        fileContents.length = 0;
1143
0
    }
1144
0
    fclose(fp);
1145
1146
0
    return fileContents;
1147
0
}
1148
#endif