Coverage Report

Created: 2026-08-14 07:31

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