Coverage Report

Created: 2026-11-05 07:00

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
172
#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
42.6k
getJsonPart(cj5_token tok, const char *json) {
31
42.6k
    UA_ByteString bs;
32
42.6k
    UA_ByteString_init(&bs);
33
42.6k
    if(tok.type == CJ5_TOKEN_STRING) {
34
4.14k
        bs.data = (UA_Byte*)(uintptr_t)(json + tok.start - 1);
35
4.14k
        bs.length = (tok.end - tok.start) + 3;
36
4.14k
        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
42.6k
}
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
246k
nextToken(ParsingCtx *ctx) {
50
246k
    if(!ctx->tokens || ctx->index + 1 >= ctx->tokensSize) {
51
213k
        ctx->index = ctx->tokensSize;
52
213k
        cj5_token empty;
53
213k
        memset(&empty, 0, sizeof(empty));
54
213k
        return empty;
55
213k
    }
56
33.3k
    ctx->index++;
57
33.3k
    return ctx->tokens[ctx->index];
58
246k
}
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
110
PARSE_JSON(UInt16Field) {
123
110
    cj5_token tok = nextToken(ctx);
124
110
    UA_ByteString buf = getJsonPart(tok, ctx->json);
125
110
    UA_UInt16 out;
126
110
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT16], NULL);
127
110
    if(retval != UA_STATUSCODE_GOOD)
128
85
        return retval;
129
25
    UA_UInt16 *field = (UA_UInt16*)configField;
130
25
    *field = out;
131
25
    return retval;
132
110
}
133
471
PARSE_JSON(UInt32Field) {
134
471
    cj5_token tok = nextToken(ctx);
135
471
    UA_ByteString buf = getJsonPart(tok, ctx->json);
136
471
    UA_UInt32 out;
137
471
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT32], NULL);
138
471
    if(retval != UA_STATUSCODE_GOOD)
139
91
        return retval;
140
380
    UA_UInt32 *field = (UA_UInt32*)configField;
141
380
    *field = out;
142
380
    return retval;
143
471
}
144
34
PARSE_JSON(UInt64Field) {
145
34
    cj5_token tok = nextToken(ctx);
146
34
    UA_ByteString buf = getJsonPart(tok, ctx->json);
147
34
    UA_UInt64 out;
148
34
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT64], NULL);
149
34
    if(retval != UA_STATUSCODE_GOOD)
150
24
        return retval;
151
10
    UA_UInt64 *field = (UA_UInt64*)configField;
152
10
    *field = out;
153
10
    return retval;
154
34
}
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
776
    UA_String *field = (UA_String*)configField;
163
776
    if(field != NULL) {
164
776
        UA_String_clear(field);
165
776
        *field = out;
166
776
    }
167
776
    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
582
    for(size_t j = tok.size/2; j > 0; j--) {
181
516
        tok = nextToken(ctx);
182
516
        switch (tok.type) {
183
278
        case CJ5_TOKEN_STRING: {
184
278
            char *field = (char*)UA_malloc(tok.size + 1);
185
278
            if(!field)
186
0
                return UA_STATUSCODE_BADOUTOFMEMORY;
187
278
            unsigned int str_len = 0;
188
278
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field, &str_len);
189
190
278
            tok = nextToken(ctx);
191
278
            UA_ByteString buf = getJsonPart(tok, ctx->json);
192
278
            if(strcmp(field, "locale") == 0)
193
0
                retval |= UA_decodeJson(&buf, &locale, &UA_TYPES[UA_TYPES_STRING], NULL);
194
278
            else if(strcmp(field, "text") == 0)
195
0
                retval |= UA_decodeJson(&buf, &text, &UA_TYPES[UA_TYPES_STRING], NULL);
196
278
            else {
197
278
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
198
278
            }
199
278
            UA_free(field);
200
278
            break;
201
278
        }
202
238
        default:
203
238
            break;
204
516
        }
205
516
    }
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
372
PARSE_JSON(DoubleField) {
219
372
    cj5_token tok = nextToken(ctx);
220
372
    UA_ByteString buf = getJsonPart(tok, ctx->json);
221
372
    UA_Double out;
222
372
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_DOUBLE], NULL);
223
372
    if(retval != UA_STATUSCODE_GOOD)
224
50
        return retval;
225
322
    UA_Double *field = (UA_Double *)configField;
226
322
    *field = out;
227
322
    return retval;
228
372
}
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
164
PARSE_JSON(DurationField) {
249
164
    UA_Double double_value;
250
164
    UA_StatusCode retval = DoubleField_parseJson(ctx, &double_value, NULL);
251
164
    if(retval != UA_STATUSCODE_GOOD)
252
11
        return retval;
253
153
    UA_Duration *field = (UA_Duration*)configField;
254
153
    *field = (UA_Duration)double_value;
255
153
    return retval;
256
164
}
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
131
        case CJ5_TOKEN_STRING: {
322
131
            char *field_str = (char*)UA_malloc(tok.size + 1);
323
131
            if(!field_str)
324
0
                return UA_STATUSCODE_BADOUTOFMEMORY;
325
131
            unsigned int str_len = 0;
326
131
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
327
131
            if(strcmp(field_str, "productUri") == 0)
328
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->productUri, NULL);
329
131
            else if(strcmp(field_str, "manufacturerName") == 0)
330
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->manufacturerName, NULL);
331
131
            else if(strcmp(field_str, "productName") == 0)
332
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->productName, NULL);
333
131
            else if(strcmp(field_str, "softwareVersion") == 0)
334
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->softwareVersion, NULL);
335
131
            else if(strcmp(field_str, "buildNumber") == 0)
336
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->buildNumber, NULL);
337
131
            else if(strcmp(field_str, "buildDate") == 0)
338
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_DATETIME](ctx, &field->buildDate, NULL);
339
131
            else {
340
131
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
341
131
            }
342
131
            UA_free(field_str);
343
131
            break;
344
131
        }
345
130
        default:
346
130
            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
30
PARSE_JSON(StringArrayField) {
390
30
    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
30
    cj5_token tok = nextToken(ctx);
395
30
    UA_String *stringArray = (UA_String*)UA_malloc(sizeof(UA_String) * tok.size);
396
30
    if(!stringArray && tok.size > 0)
397
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
398
30
    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
30
    UA_String **field = (UA_String**)configField;
407
30
    if(*configFieldSize > 0) {
408
30
        UA_Array_delete(*field, *configFieldSize,
409
30
                        &UA_TYPES[UA_TYPES_STRING]);
410
30
        *field = NULL;
411
30
        *configFieldSize = 0;
412
30
    }
413
30
    UA_StatusCode retval =
414
30
        UA_Array_copy(stringArray, stringArraySize,
415
30
                      (void**)field, &UA_TYPES[UA_TYPES_STRING]);
416
30
    *configFieldSize = stringArraySize;
417
418
    /* Clean up */
419
30
    UA_Array_delete(stringArray, stringArraySize, &UA_TYPES[UA_TYPES_STRING]);
420
30
    return retval;
421
30
}
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
387
PARSE_JSON(MdnsConfigurationField) {
471
387
#ifdef UA_ENABLE_DISCOVERY_MULTICAST
472
387
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
473
387
    cj5_token tok = nextToken(ctx);
474
435
    for(size_t j = tok.size/2; j > 0; j--) {
475
48
        tok = nextToken(ctx);
476
48
        switch (tok.type) {
477
31
        case CJ5_TOKEN_STRING: {
478
31
            char *field_str = (char*)UA_malloc(tok.size + 1);
479
31
            if(!field_str)
480
0
                return UA_STATUSCODE_BADOUTOFMEMORY;
481
31
            unsigned int str_len = 0;
482
31
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
483
31
            if(strcmp(field_str, "mdnsServerName") == 0)
484
1
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &config->mdnsConfig.mdnsServerName, NULL);
485
30
            else if(strcmp(field_str, "serverCapabilities") == 0)
486
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRINGARRAY](ctx, &config->mdnsConfig.serverCapabilities, &config->mdnsConfig.serverCapabilitiesSize);
487
30
#ifdef UA_ENABLE_DISCOVERY_MULTICAST_MDNSD
488
30
            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
30
#endif
496
30
            else {
497
30
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
498
30
            }
499
31
            UA_free(field_str);
500
31
            break;
501
31
        }
502
17
        default:
503
17
        break;
504
48
        }
505
48
    }
506
387
#endif
507
387
    return UA_STATUSCODE_GOOD;
508
387
}
509
510
3
PARSE_JSON(SubscriptionConfigurationField) {
511
3
#ifdef UA_ENABLE_SUBSCRIPTIONS
512
3
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
513
3
    cj5_token tok = nextToken(ctx);
514
44.9k
    for(size_t j = tok.size/2; j > 0; j--) {
515
44.9k
        tok = nextToken(ctx);
516
44.9k
        switch (tok.type) {
517
12
        case CJ5_TOKEN_STRING: {
518
12
            char *field_str = (char*)UA_malloc(tok.size + 1);
519
12
            if(!field_str)
520
0
                return UA_STATUSCODE_BADOUTOFMEMORY;
521
12
            unsigned int str_len = 0;
522
12
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
523
12
            if(strcmp(field_str, "maxSubscriptions") == 0)
524
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxSubscriptions, NULL);
525
12
            else if(strcmp(field_str, "maxSubscriptionsPerSession") == 0)
526
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxSubscriptionsPerSession, NULL);
527
12
            else if(strcmp(field_str, "publishingIntervalLimits") == 0)
528
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_DURATIONRANGE](ctx, &config->publishingIntervalLimits, NULL);
529
12
            else if(strcmp(field_str, "lifeTimeCountLimits") == 0)
530
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32RANGE](ctx, &config->lifeTimeCountLimits, NULL);
531
12
            else if(strcmp(field_str, "keepAliveCountLimits") == 0)
532
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32RANGE](ctx, &config->keepAliveCountLimits, NULL);
533
12
            else if(strcmp(field_str, "maxNotificationsPerPublish") == 0)
534
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxNotificationsPerPublish, NULL);
535
12
            else if(strcmp(field_str, "enableRetransmissionQueue") == 0)
536
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->enableRetransmissionQueue, NULL);
537
12
            else if(strcmp(field_str, "maxRetransmissionQueueSize") == 0)
538
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxRetransmissionQueueSize, NULL);
539
12
# ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
540
12
            else if(strcmp(field_str, "maxEventsPerNode") == 0)
541
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxEventsPerNode, NULL);
542
12
# endif
543
12
            else if(strcmp(field_str, "maxMonitoredItems") == 0)
544
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxMonitoredItems, NULL);
545
12
            else if(strcmp(field_str, "maxMonitoredItemsPerSubscription") == 0)
546
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxMonitoredItemsPerSubscription, NULL);
547
12
            else if(strcmp(field_str, "samplingIntervalLimits") == 0)
548
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_DURATIONRANGE](ctx, &config->samplingIntervalLimits, NULL);
549
12
            else if(strcmp(field_str, "queueSizeLimits") == 0)
550
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32RANGE](ctx, &config->queueSizeLimits, NULL);
551
12
            else if(strcmp(field_str, "maxPublishReqPerSession") == 0)
552
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxPublishReqPerSession, NULL);
553
12
            else {
554
12
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
555
12
            }
556
12
            UA_free(field_str);
557
12
            break;
558
12
        }
559
44.9k
        default:
560
44.9k
            break;
561
44.9k
        }
562
44.9k
    }
563
3
#endif
564
3
    return UA_STATUSCODE_GOOD;
565
3
}
566
567
2
PARSE_JSON(TcpConfigurationField) {
568
2
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
569
2
    cj5_token tok = nextToken(ctx);
570
6.25k
    for(size_t j = tok.size/2; j > 0; j--) {
571
6.25k
        tok = nextToken(ctx);
572
6.25k
        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
6.25k
        default:
592
6.25k
            break;
593
6.25k
        }
594
6.25k
    }
595
2
    return UA_STATUSCODE_GOOD;
596
2
}
597
598
40
PARSE_JSON(PubsubConfigurationField) {
599
40
#ifdef UA_ENABLE_PUBSUB
600
40
    UA_PubSubConfiguration *field = (UA_PubSubConfiguration*)configField;
601
40
    cj5_token tok = nextToken(ctx);
602
53.2k
    for(size_t j = tok.size/2; j > 0; j--) {
603
53.2k
        tok = nextToken(ctx);
604
53.2k
        switch (tok.type) {
605
1
        case CJ5_TOKEN_STRING: {
606
1
            char *field_str = (char*)UA_malloc(tok.size + 1);
607
1
            if(!field_str)
608
0
                return UA_STATUSCODE_BADOUTOFMEMORY;
609
1
            unsigned int str_len = 0;
610
1
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
611
1
            if(strcmp(field_str, "enableDeltaFrames") == 0)
612
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &field->enableDeltaFrames, NULL);
613
1
#ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
614
1
            else if(strcmp(field_str, "enableInformationModelMethods") == 0)
615
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &field->enableInformationModelMethods, NULL);
616
1
#endif
617
1
            else {
618
1
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
619
1
            }
620
1
            UA_free(field_str);
621
1
            break;
622
1
        }
623
53.2k
        default:
624
53.2k
            break;
625
53.2k
        }
626
53.2k
    }
627
40
#endif
628
40
    return UA_STATUSCODE_GOOD;
629
40
}
630
631
330
PARSE_JSON(HistorizingConfigurationField) {
632
330
#ifdef UA_ENABLE_HISTORIZING
633
330
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
634
330
    cj5_token tok = nextToken(ctx);
635
996
    for(size_t j = tok.size/2; j > 0; j--) {
636
666
        tok = nextToken(ctx);
637
666
        switch (tok.type) {
638
332
        case CJ5_TOKEN_STRING: {
639
332
            char *field_str = (char*)UA_malloc(tok.size + 1);
640
332
            if(!field_str)
641
0
                return UA_STATUSCODE_BADOUTOFMEMORY;
642
332
            unsigned int str_len = 0;
643
332
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
644
332
            if(strcmp(field_str, "accessHistoryDataCapability") == 0)
645
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->accessHistoryDataCapability, NULL);
646
332
            else if(strcmp(field_str, "maxReturnDataValues") == 0)
647
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxReturnDataValues, NULL);
648
332
            else if(strcmp(field_str, "accessHistoryEventsCapability") == 0)
649
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->accessHistoryEventsCapability, NULL);
650
332
            else if(strcmp(field_str, "maxReturnEventValues") == 0)
651
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxReturnEventValues, NULL);
652
332
            else if(strcmp(field_str, "insertDataCapability") == 0)
653
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->insertDataCapability, NULL);
654
332
            else if(strcmp(field_str, "insertEventCapability") == 0)
655
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->insertEventCapability, NULL);
656
332
            else if(strcmp(field_str, "insertAnnotationsCapability") == 0)
657
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->insertAnnotationsCapability, NULL);
658
332
            else if(strcmp(field_str, "replaceDataCapability") == 0)
659
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->replaceDataCapability, NULL);
660
332
            else if(strcmp(field_str, "replaceEventCapability") == 0)
661
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->replaceEventCapability, NULL);
662
332
            else if(strcmp(field_str, "updateDataCapability") == 0)
663
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->updateDataCapability, NULL);
664
332
            else if(strcmp(field_str, "updateEventCapability") == 0)
665
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->updateEventCapability, NULL);
666
332
            else if(strcmp(field_str, "deleteRawCapability") == 0)
667
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->deleteRawCapability, NULL);
668
332
            else if(strcmp(field_str, "deleteEventCapability") == 0)
669
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->deleteEventCapability, NULL);
670
332
            else if(strcmp(field_str, "deleteAtTimeDataCapability") == 0)
671
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->deleteAtTimeDataCapability, NULL);
672
332
            else {
673
332
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
674
332
            }
675
332
            UA_free(field_str);
676
332
            break;
677
332
        }
678
334
        default:
679
334
            break;
680
666
        }
681
666
    }
682
330
#endif
683
330
    return UA_STATUSCODE_GOOD;
684
330
}
685
686
10
PARSE_JSON(SecurityPolciesField) {
687
10
#ifdef UA_ENABLE_ENCRYPTION
688
10
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
689
690
10
    UA_String noneuri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None");
691
10
    UA_String basic128Rsa15uri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15");
692
10
    UA_String basic256uri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Basic256");
693
10
    UA_String basic256Sha256uri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256");
694
10
    UA_String aes128sha256rsaoaepuri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Aes128_Sha256_RsaOaep");
695
10
    UA_String aes256sha256rsapssuri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Aes256_Sha256_RsaPss");
696
697
10
    cj5_token tok = nextToken(ctx);
698
10
    for(size_t j = tok.size; j > 0; j--) {
699
700
10
        UA_String policy = {.length = 0, .data = NULL};
701
10
        UA_ByteString certificate = {.length = 0, .data = NULL};
702
10
        UA_ByteString privateKey = {.length = 0, .data = NULL};
703
704
10
        tok = nextToken(ctx);
705
1.87k
        for(size_t i = tok.size / 2; i > 0; i--) {
706
1.86k
            tok = nextToken(ctx);
707
1.86k
            switch(tok.type) {
708
842
            case CJ5_TOKEN_STRING: {
709
842
                char *field_str = (char *)UA_malloc(tok.size + 1);
710
842
                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
842
                unsigned int str_len = 0;
717
842
                cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
718
842
                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
842
                } else if(strcmp(field_str, "privateKey") == 0) {
739
101
                    UA_String out = {.length = 0, .data = NULL};
740
101
                    parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &out, NULL);
741
742
101
                    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
741
                } else if(strcmp(field_str, "policy") == 0) {
759
108
                    parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &policy, NULL);
760
633
                } else {
761
633
                    UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
762
633
                }
763
842
                UA_free(field_str);
764
842
                break;
765
842
            }
766
1.02k
            default:
767
1.02k
                break;
768
1.86k
            }
769
1.86k
        }
770
771
10
        if(certificate.length == 0 || privateKey.length == 0) {
772
10
            UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION,
773
10
                           "Certificate and PrivateKey must be set for every policy.");
774
10
            if(policy.length > 0)
775
0
                UA_String_clear(&policy);
776
10
            if(certificate.length > 0)
777
0
                UA_ByteString_clear(&certificate);
778
10
            if(privateKey.length > 0)
779
0
                UA_ByteString_clear(&privateKey);
780
10
            return UA_STATUSCODE_BADINTERNALERROR;
781
10
        }
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
10
}
837
838
3.30k
PARSE_JSON(SecurityPkiField) {
839
3.30k
#ifdef UA_ENABLE_ENCRYPTION
840
3.30k
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
841
3.30k
    UA_String pkiFolder = {.length = 0, .data = NULL};
842
843
3.30k
    cj5_token tok = nextToken(ctx);
844
3.30k
    UA_ByteString buf = getJsonPart(tok, ctx->json);
845
3.30k
    UA_StatusCode retval = UA_decodeJson(&buf, &pkiFolder, &UA_TYPES[UA_TYPES_STRING], NULL);
846
3.30k
    if(retval != UA_STATUSCODE_GOOD)
847
18
        return retval;
848
849
3.28k
#if defined(__linux__) || defined(UA_ARCHITECTURE_WIN32) || defined(__APPLE__) || defined(__OpenBSD__)
850
    /* Set up the parameters for the filestore certificate store */
851
3.28k
    UA_KeyValuePair params[2];
852
3.28k
    size_t paramsSize = 2;
853
854
3.28k
    params[0].key = UA_QUALIFIEDNAME(0, "max-trust-listsize");
855
3.28k
    UA_Variant_setScalar(&params[0].value, &config->maxTrustListSize, &UA_TYPES[UA_TYPES_UINT32]);
856
3.28k
    params[1].key = UA_QUALIFIEDNAME(0, "max-rejected-listsize");
857
3.28k
    UA_Variant_setScalar(&params[1].value, &config->maxRejectedListSize, &UA_TYPES[UA_TYPES_UINT32]);
858
859
3.28k
    UA_KeyValueMap paramsMap;
860
3.28k
    paramsMap.map = params;
861
3.28k
    paramsMap.mapSize = paramsSize;
862
863
    /* set server config field */
864
3.28k
    UA_NodeId defaultApplicationGroup =
865
3.28k
           UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP);
866
3.28k
    retval = UA_CertificateGroup_Filestore(&config->secureChannelPKI, &defaultApplicationGroup,
867
3.28k
                                           pkiFolder, config->logging, &paramsMap);
868
3.28k
    if(retval != UA_STATUSCODE_GOOD) {
869
45
        UA_String_clear(&pkiFolder);
870
45
        return retval;
871
45
    }
872
873
3.24k
    UA_NodeId defaultUserTokenGroup =
874
3.24k
            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP);
875
3.24k
    retval = UA_CertificateGroup_Filestore(&config->sessionPKI, &defaultUserTokenGroup,
876
3.24k
                                            pkiFolder, config->logging, &paramsMap);
877
3.24k
    if(retval != UA_STATUSCODE_GOOD) {
878
5
        UA_String_clear(&pkiFolder);
879
5
        return retval;
880
5
    }
881
882
    /* Clean up */
883
3.23k
    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.23k
    return UA_STATUSCODE_GOOD;
896
3.24k
}
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
55
PARSE_JSON(RuleHandlingField) {
909
55
    UA_UInt32 enum_value;
910
55
    UA_StatusCode retval = UInt32Field_parseJson(ctx, &enum_value, NULL);
911
55
    if(retval != UA_STATUSCODE_GOOD)
912
40
        return retval;
913
15
    UA_RuleHandling *field = (UA_RuleHandling*)configField;
914
15
    *field = (UA_RuleHandling)enum_value;
915
15
    return retval;
916
55
}
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
115
skipUnknownItem(ParsingCtx* ctx) {
958
115
    unsigned int end = ctx->tokens[ctx->index].end;
959
1.09k
    do {
960
1.09k
        ctx->index++;
961
1.09k
    } while (ctx->index < ctx->tokensSize &&
962
1.05k
        ctx->tokens[ctx->index].start < end);
963
115
}
964
965
static UA_StatusCode
966
172
parseJSONConfig(UA_ServerConfig *config, UA_ByteString json_config) {
967
    // Parsing json config
968
172
    const char *json = (const char*)json_config.data;
969
172
    cj5_token tokens[MAX_TOKENS];
970
172
    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
172
    if(r.error != CJ5_ERROR_NONE || r.num_tokens < 2 ||
975
84
       r.tokens[0].type != CJ5_TOKEN_OBJECT)
976
88
        return UA_STATUSCODE_BADDECODINGERROR;
977
978
84
    ParsingCtx ctx;
979
84
    ctx.json = json;
980
84
    ctx.result = r;
981
84
    ctx.tokens = r.tokens;
982
84
    ctx.tokensSize = r.num_tokens;
983
84
    ctx.index = 1; // The first token is ignored because it is known and not needed.
984
985
84
    ctx.logging = config->logging;
986
987
    /* Buffer for the field name */
988
84
    char field[256];
989
990
84
    size_t serverConfigSize = 0;
991
84
    if(ctx.tokens)
992
84
        serverConfigSize = (ctx.tokens[ctx.index-1].size/2);
993
84
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
994
1.46k
    for (size_t j = serverConfigSize; j > 0 && ctx.index < ctx.tokensSize; j--) {
995
1.39k
        cj5_token tok = ctx.tokens[ctx.index];
996
1.39k
        switch (tok.type) {
997
1.36k
            case CJ5_TOKEN_STRING: {
998
1.36k
                if(tok.size >= 255) {
999
43
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
1000
43
                                   "Configuration field name too long");
1001
43
                    continue;
1002
43
                }
1003
1.32k
                unsigned int str_len = 0;
1004
1.32k
                cj5_error_code res = cj5_get_str(&ctx.result, (unsigned int)ctx.index, field, &str_len);
1005
1.32k
                if(res != CJ5_ERROR_NONE) {
1006
339
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
1007
339
                                   "Configuration field name not a valid string");
1008
339
                    continue;
1009
339
                }
1010
981
                if(strcmp(field, "buildInfo") == 0)
1011
64
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BUILDINFO](&ctx, &config->buildInfo, NULL);
1012
917
                else if(strcmp(field, "applicationDescription") == 0)
1013
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_APPLICATIONDESCRIPTION](&ctx, &config->applicationDescription, NULL);
1014
917
                else if(strcmp(field, "shutdownDelay") == 0)
1015
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_DOUBLE](&ctx, &config->shutdownDelay, NULL);
1016
917
                else if(strcmp(field, "verifyRequestTimestamp") == 0)
1017
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_RULEHANDLING](&ctx, &config->verifyRequestTimestamp, NULL);
1018
917
                else if(strcmp(field, "allowEmptyVariables") == 0)
1019
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_RULEHANDLING](&ctx, &config->allowEmptyVariables, NULL);
1020
917
                else if(strcmp(field, "serverUrls") == 0)
1021
30
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRINGARRAY](&ctx, &config->serverUrls, &config->serverUrlsSize);
1022
887
                else if(strcmp(field, "tcpEnabled") == 0)
1023
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->tcpEnabled, NULL);
1024
887
                else if(strcmp(field, "tcp") == 0)
1025
2
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_TCPCONFIGURATION](&ctx, config, NULL);
1026
885
                else if(strcmp(field, "securityPolicyNoneDiscoveryOnly") == 0)
1027
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->securityPolicyNoneDiscoveryOnly, NULL);
1028
885
                else if(strcmp(field, "modellingRulesOnInstances") == 0)
1029
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->modellingRulesOnInstances, NULL);
1030
885
                else if(strcmp(field, "maxSecureChannels") == 0)
1031
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT16](&ctx, &config->maxSecureChannels, NULL);
1032
885
                else if(strcmp(field, "maxSecurityTokenLifetime") == 0)
1033
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxSecurityTokenLifetime, NULL);
1034
885
                else if(strcmp(field, "maxSessions") == 0)
1035
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT16](&ctx, &config->maxSessions, NULL);
1036
885
                else if(strcmp(field, "maxSessionTimeout") == 0)
1037
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_DOUBLE](&ctx, &config->maxSessionTimeout, NULL);
1038
885
                else if(strcmp(field, "maxNodesPerRead") == 0)
1039
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerRead, NULL);
1040
885
                else if(strcmp(field, "maxNodesPerWrite") == 0)
1041
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerWrite, NULL);
1042
885
                else if(strcmp(field, "maxNodesPerMethodCall") == 0)
1043
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerMethodCall, NULL);
1044
885
                else if(strcmp(field, "maxNodesPerBrowse") == 0)
1045
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerBrowse, NULL);
1046
885
                else if(strcmp(field, "maxNodesPerRegisterNodes") == 0)
1047
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerRegisterNodes, NULL);
1048
885
                else if(strcmp(field, "maxNodesPerTranslateBrowsePathsToNodeIds") == 0)
1049
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerTranslateBrowsePathsToNodeIds, NULL);
1050
885
                else if(strcmp(field, "maxNodesPerNodeManagement") == 0)
1051
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerNodeManagement, NULL);
1052
885
                else if(strcmp(field, "maxMonitoredItemsPerCall") == 0)
1053
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxMonitoredItemsPerCall, NULL);
1054
885
                else if(strcmp(field, "maxReferencesPerNode") == 0)
1055
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxReferencesPerNode, NULL);
1056
885
                else if(strcmp(field, "reverseReconnectInterval") == 0)
1057
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->reverseReconnectInterval, NULL);
1058
1059
885
#if UA_MULTITHREADING >= 100
1060
885
                else if(strcmp(field, "asyncOperationTimeout") == 0)
1061
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_DOUBLE](&ctx, &config->asyncOperationTimeout, NULL);
1062
885
                else if(strcmp(field, "maxAsyncOperationQueueSize") == 0)
1063
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT64](&ctx, &config->maxAsyncOperationQueueSize, NULL);
1064
885
#endif
1065
1066
885
#ifdef UA_ENABLE_DISCOVERY
1067
885
                else if(strcmp(field, "discoveryCleanupTimeout") == 0)
1068
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->discoveryCleanupTimeout, NULL);
1069
885
#ifdef UA_ENABLE_DISCOVERY_MULTICAST
1070
885
                else if(strcmp(field, "mdnsEnabled") == 0)
1071
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->mdnsEnabled, NULL);
1072
885
                else if(strcmp(field, "mdns") == 0)
1073
387
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_MDNSCONFIGURATION](&ctx, config, NULL);
1074
498
#endif
1075
498
#endif
1076
1077
498
#ifdef UA_ENABLE_SUBSCRIPTIONS
1078
498
                else if(strcmp(field, "subscriptionsEnabled") == 0)
1079
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->subscriptionsEnabled, NULL);
1080
498
                else if(strcmp(field, "subscriptions") == 0)
1081
3
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_SUBSCRIPTIONCONFIGURATION](&ctx, config, NULL);
1082
495
# endif
1083
1084
495
#ifdef UA_ENABLE_HISTORIZING
1085
495
                else if(strcmp(field, "historizingEnabled") == 0)
1086
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->historizingEnabled, NULL);
1087
495
                else if(strcmp(field, "historizing") == 0)
1088
330
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_HISTORIZINGCONFIGURATION](&ctx, config, NULL);
1089
165
#endif
1090
1091
165
#ifdef UA_ENABLE_PUBSUB
1092
165
                else if(strcmp(field, "pubsubEnabled") == 0)
1093
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->pubsubEnabled, NULL);
1094
165
                else if(strcmp(field, "pubsub") == 0)
1095
40
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_PUBSUBCONFIGURATION](&ctx, &config->pubSubConfig, NULL);
1096
125
#endif
1097
125
#ifdef UA_ENABLE_ENCRYPTION
1098
125
                else if(strcmp(field, "securityPolicies") == 0)
1099
10
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_SECURITYPOLICIES](&ctx, config, NULL);
1100
115
                else if(strcmp(field, "pkiFolder") == 0)
1101
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_SECURITYPKI](&ctx, config, NULL);
1102
115
#endif
1103
115
                else {
1104
115
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
1105
115
                                   "Field name '%s' unknown or misspelled. Maybe the feature is not enabled.", field);
1106
                    /* skip the name of item */
1107
115
                    ++ctx.index;
1108
                    /* skip value of unknown item */
1109
115
                    skipUnknownItem(&ctx);
1110
                    /* after skipUnknownItem() ctx->index points to the name of the following item.
1111
                       We must decrement index in oder following increment will
1112
                       still set index to the right position (name of the following item) */
1113
115
                    --ctx.index;
1114
115
                }
1115
981
                if(retval != UA_STATUSCODE_GOOD) {
1116
10
                    UA_LOG_ERROR(ctx.logging, UA_LOGCATEGORY_APPLICATION,
1117
10
                                 "An error occurred while parsing the configuration field %s", field);
1118
10
                    return retval;
1119
10
                }
1120
971
                break;
1121
981
            }
1122
971
            default:
1123
30
                break;
1124
1.39k
        }
1125
1.00k
        ctx.index += 1;
1126
1.00k
    }
1127
74
    return retval;
1128
84
}
1129
1130
UA_Server *
1131
7.58k
UA_Server_newFromFile(const UA_ByteString json_config) {
1132
7.58k
    UA_ServerConfig config;
1133
7.58k
    memset(&config, 0, sizeof(UA_ServerConfig));
1134
7.58k
    UA_StatusCode res = UA_ServerConfig_setDefault(&config);
1135
7.58k
    res |= parseJSONConfig(&config, json_config);
1136
7.58k
    if(res != UA_STATUSCODE_GOOD) {
1137
1.21k
        UA_ServerConfig_clear(&config);
1138
1.21k
        return NULL;
1139
1.21k
    }
1140
6.37k
    return UA_Server_newWithConfig(&config);
1141
7.58k
}
1142
1143
UA_StatusCode
1144
0
UA_ServerConfig_updateFromFile(UA_ServerConfig *config, const UA_ByteString json_config) {
1145
0
    UA_StatusCode res = parseJSONConfig(config, json_config);
1146
0
    return res;
1147
0
}
1148
1149
#ifdef UA_ENABLE_ENCRYPTION
1150
static UA_ByteString
1151
loadCertificateFile(const char *const path) {
1152
    UA_ByteString fileContents = UA_BYTESTRING_NULL;
1153
1154
    /* Open the file */
1155
    FILE *fp = fopen(path, "rb");
1156
    if(!fp) {
1157
        errno = 0; /* We read errno also from the tcp layer... */
1158
        return fileContents;
1159
    }
1160
1161
    /* Get the file length, allocate the data and read */
1162
    if(fseek(fp, 0, SEEK_END) != 0) {
1163
        fclose(fp);
1164
        errno = 0;
1165
        return fileContents;
1166
    }
1167
1168
    long length = ftell(fp);
1169
    if(length < 0) {
1170
        fclose(fp);
1171
        errno = 0;
1172
        return fileContents;
1173
    }
1174
1175
    fileContents.length = (size_t)length;
1176
    fileContents.data = (UA_Byte *)UA_malloc(fileContents.length * sizeof(UA_Byte));
1177
    if(fileContents.data) {
1178
        if(fseek(fp, 0, SEEK_SET) != 0) {
1179
            fclose(fp);
1180
            UA_ByteString_clear(&fileContents);
1181
            errno = 0;
1182
            return fileContents;
1183
        }
1184
        size_t read = fread(fileContents.data, sizeof(UA_Byte), fileContents.length, fp);
1185
        if(read != fileContents.length)
1186
            UA_ByteString_clear(&fileContents);
1187
    } else {
1188
        fileContents.length = 0;
1189
    }
1190
    fclose(fp);
1191
1192
    return fileContents;
1193
}
1194
#endif