Coverage Report

Created: 2026-11-12 06:54

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