Coverage Report

Created: 2026-07-16 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open62541/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
 *    Copyright 2026 (c) o6 Automation GmbH (Author: Moritz Bruder)
7
 */
8
9
#include <open62541/plugin/log.h>
10
#include <open62541/server.h>
11
#include <open62541/client.h>
12
#include "cj5.h"
13
#include "open62541/server_config_default.h"
14
#include "open62541/client_config_default.h"
15
#include "open62541/plugin/securitypolicy_default.h"
16
#ifdef UA_ENABLE_ENCRYPTION
17
#include "open62541/plugin/certificategroup_default.h"
18
#include <stdio.h>
19
#include <errno.h>
20
#endif
21
22
3.07k
#define MAX_TOKENS 1024
23
24
#define LOG_UNKNOWN_FIELD(ctx, field) \
25
816
    UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name '%s'.", field)
26
27
typedef struct {
28
    const char *json;
29
    unsigned int index;
30
    UA_Byte depth;
31
    cj5_result result;
32
33
    UA_Logger *logging;
34
} ParsingCtx;
35
36
static UA_ByteString
37
177
getJsonPart(cj5_token tok, const char *json) {
38
177
    UA_ByteString bs;
39
177
    UA_ByteString_init(&bs);
40
177
    if(tok.type == CJ5_TOKEN_STRING) {
41
0
        bs.data = (UA_Byte*)(uintptr_t)(json + tok.start - 1);
42
0
        bs.length = (tok.end - tok.start) + 3;
43
0
        return bs;
44
177
    } else {
45
177
        bs.data = (UA_Byte*)(uintptr_t)(json + tok.start);
46
177
        bs.length = (tok.end - tok.start) + 1;
47
177
        return bs;
48
177
    }
49
177
}
50
51
/* Forward declarations*/
52
#define PARSE_JSON(TYPE) static UA_StatusCode                   \
53
    TYPE##_parseJson(ParsingCtx *ctx, void *configField, size_t *configFieldSize)
54
55
typedef UA_StatusCode
56
(*parseJsonSignature)(ParsingCtx *ctx, void *configField, size_t *configFieldSize);
57
58
#ifdef UA_ENABLE_ENCRYPTION
59
static UA_ByteString
60
loadCertificateFile(const char *const path);
61
#endif
62
63
/*----------------------Basic Types------------------------*/
64
#if 0
65
PARSE_JSON(Int64Field) {
66
    cj5_token tok = ctx->result.tokens[++ctx->index];
67
    UA_ByteString buf = getJsonPart(tok, ctx->json);
68
    UA_Int64 out;
69
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_INT64], NULL);
70
    if(retval != UA_STATUSCODE_GOOD)
71
        return retval;
72
    UA_Int64 *field = (UA_Int64*)configField;
73
    *field = out;
74
    return retval;
75
}
76
#endif
77
0
PARSE_JSON(ByteField) {
78
0
    cj5_token tok = ctx->result.tokens[++ctx->index];
79
0
    UA_ByteString buf = getJsonPart(tok, ctx->json);
80
0
    UA_Byte out;
81
0
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_BYTE], NULL);
82
0
    if(retval != UA_STATUSCODE_GOOD)
83
0
        return retval;
84
0
    UA_Byte *field = (UA_Byte*)configField;
85
0
    *field = out;
86
0
    return retval;
87
0
}
88
45
PARSE_JSON(UInt16Field) {
89
45
    cj5_token tok = ctx->result.tokens[++ctx->index];
90
45
    UA_ByteString buf = getJsonPart(tok, ctx->json);
91
45
    UA_UInt16 out;
92
45
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT16], NULL);
93
45
    if(retval != UA_STATUSCODE_GOOD)
94
35
        return retval;
95
10
    UA_UInt16 *field = (UA_UInt16*)configField;
96
10
    *field = out;
97
10
    return retval;
98
45
}
99
71
PARSE_JSON(UInt32Field) {
100
71
    cj5_token tok = ctx->result.tokens[++ctx->index];
101
71
    UA_ByteString buf = getJsonPart(tok, ctx->json);
102
71
    UA_UInt32 out;
103
71
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT32], NULL);
104
71
    if(retval != UA_STATUSCODE_GOOD)
105
46
        return retval;
106
25
    UA_UInt32 *field = (UA_UInt32*)configField;
107
25
    *field = out;
108
25
    return retval;
109
71
}
110
52
PARSE_JSON(UInt64Field) {
111
52
    cj5_token tok = ctx->result.tokens[++ctx->index];
112
52
    UA_ByteString buf = getJsonPart(tok, ctx->json);
113
52
    UA_UInt64 out;
114
52
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT64], NULL);
115
52
    if(retval != UA_STATUSCODE_GOOD)
116
25
        return retval;
117
27
    UA_UInt64 *field = (UA_UInt64*)configField;
118
27
    *field = out;
119
27
    return retval;
120
52
}
121
0
PARSE_JSON(Int32Field) {
122
0
    cj5_token tok = ctx->result.tokens[++ctx->index];
123
0
    UA_ByteString buf = getJsonPart(tok, ctx->json);
124
0
    UA_Int32 out;
125
0
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_INT32], NULL);
126
0
    if(retval != UA_STATUSCODE_GOOD)
127
0
        return retval;
128
0
    UA_Int32 *field = (UA_Int32*)configField;
129
0
    *field = out;
130
0
    return retval;
131
0
}
132
0
PARSE_JSON(StringField) {
133
0
    cj5_token tok = ctx->result.tokens[++ctx->index];
134
0
    UA_ByteString buf = getJsonPart(tok, ctx->json);
135
0
    UA_String out;
136
0
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_STRING], NULL);
137
0
    if(retval != UA_STATUSCODE_GOOD)
138
0
        return retval;
139
0
    UA_String *field = (UA_String*)configField;
140
0
    if(field != NULL) {
141
0
        UA_String_clear(field);
142
0
        *field = out;
143
0
    }
144
0
    return retval;
145
0
}
146
0
PARSE_JSON(ByteStringField) {
147
0
    cj5_token tok = ctx->result.tokens[++ctx->index];
148
0
    UA_ByteString buf = getJsonPart(tok, ctx->json);
149
0
    UA_ByteString out;
150
0
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_BYTESTRING], NULL);
151
0
    if(retval != UA_STATUSCODE_GOOD)
152
0
        return retval;
153
0
    UA_ByteString *field = (UA_ByteString*)configField;
154
0
    *field = out;
155
0
    return retval;
156
0
}
157
0
PARSE_JSON(LocalizedTextField) {
158
    /*
159
     applicationName: {
160
        locale: "de-DE",
161
        text: "Test text"
162
    }
163
     */
164
0
    cj5_token tok = ctx->result.tokens[++ctx->index];
165
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
166
0
    UA_String locale = {.length = 0, .data = NULL};
167
0
    UA_String text = {.length = 0, .data = NULL};
168
0
    for(size_t j = tok.size/2; j > 0; j--) {
169
0
        tok = ctx->result.tokens[++ctx->index];
170
0
        switch (tok.type) {
171
0
        case CJ5_TOKEN_STRING: {
172
0
            char *field = (char*)UA_malloc(tok.size + 1);
173
0
            unsigned int str_len = 0;
174
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field, &str_len);
175
176
0
            tok = ctx->result.tokens[++ctx->index];
177
0
            UA_ByteString buf = getJsonPart(tok, ctx->json);
178
0
            if(strcmp(field, "locale") == 0)
179
0
                retval |= UA_decodeJson(&buf, &locale, &UA_TYPES[UA_TYPES_STRING], NULL);
180
0
            else if(strcmp(field, "text") == 0)
181
0
                retval |= UA_decodeJson(&buf, &text, &UA_TYPES[UA_TYPES_STRING], NULL);
182
0
            else {
183
0
                LOG_UNKNOWN_FIELD(ctx, field);
184
0
            }
185
0
            UA_free(field);
186
0
            break;
187
0
        }
188
0
        default:
189
0
            break;
190
0
        }
191
0
    }
192
0
    UA_LocalizedText out;
193
0
    out.locale = locale;
194
0
    out.text = text;
195
0
    if(retval != UA_STATUSCODE_GOOD)
196
0
        return retval;
197
0
    UA_LocalizedText *field = (UA_LocalizedText*)configField;
198
0
    if(field != NULL) {
199
0
        UA_LocalizedText_clear(field);
200
0
        *field = out;
201
0
    }
202
0
    return retval;
203
0
}
204
5
PARSE_JSON(DoubleField) {
205
5
    cj5_token tok = ctx->result.tokens[++ctx->index];
206
5
    UA_ByteString buf = getJsonPart(tok, ctx->json);
207
5
    UA_Double out;
208
5
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_DOUBLE], NULL);
209
5
    if(retval != UA_STATUSCODE_GOOD)
210
3
        return retval;
211
2
    UA_Double *field = (UA_Double *)configField;
212
2
    *field = out;
213
2
    return retval;
214
5
}
215
4
PARSE_JSON(BooleanField) {
216
4
    cj5_token tok = ctx->result.tokens[++ctx->index];
217
4
    UA_ByteString buf = getJsonPart(tok, ctx->json);
218
4
    UA_Boolean out;
219
4
    if(tok.type != CJ5_TOKEN_BOOL) {
220
4
        UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Value of type bool expected.");
221
4
        return UA_STATUSCODE_BADTYPEMISMATCH;
222
4
    }
223
0
    const UA_String val = UA_STRING_STATIC("true");
224
0
    if(UA_String_equal(&val, &buf)) {
225
0
        out = true;
226
0
    }else {
227
0
        out = false;
228
0
    }
229
    /* set server config field */
230
0
    UA_Boolean *field = (UA_Boolean *)configField;
231
0
    *field = out;
232
0
    return UA_STATUSCODE_GOOD;
233
4
}
234
#ifdef UA_ENABLE_SUBSCRIPTIONS
235
0
PARSE_JSON(DurationField) {
236
0
    UA_Double double_value;
237
0
    UA_StatusCode retval = DoubleField_parseJson(ctx, &double_value, NULL);
238
0
    if(retval != UA_STATUSCODE_GOOD)
239
0
        return retval;
240
0
    UA_Duration *field = (UA_Duration*)configField;
241
0
    *field = (UA_Duration)double_value;
242
0
    return retval;
243
0
}
244
0
PARSE_JSON(DurationRangeField) {
245
0
    UA_DurationRange *field = (UA_DurationRange*)configField;
246
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
247
0
    cj5_token tok = ctx->result.tokens[++ctx->index];
248
0
    for(size_t j = tok.size/2; j > 0; j--) {
249
0
        tok = ctx->result.tokens[++ctx->index];
250
0
        switch (tok.type) {
251
0
        case CJ5_TOKEN_STRING: {
252
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
253
0
            unsigned int str_len = 0;
254
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
255
0
            if(strcmp(field_str, "min") == 0)
256
0
                retval = DurationField_parseJson(ctx, &field->min, NULL);
257
0
            else if(strcmp(field_str, "max") == 0)
258
0
                retval = DurationField_parseJson(ctx, &field->max, NULL);
259
0
            else {
260
0
                LOG_UNKNOWN_FIELD(ctx, field_str);
261
0
            }
262
0
            UA_free(field_str);
263
0
            if(retval != UA_STATUSCODE_GOOD) {
264
0
                return retval;
265
0
            }
266
0
            break;
267
0
        }
268
0
        default:
269
0
            break;
270
0
        }
271
0
    }
272
0
    return UA_STATUSCODE_GOOD;
273
0
}
274
0
PARSE_JSON(UInt32RangeField) {
275
0
    UA_UInt32Range *field = (UA_UInt32Range*)configField;
276
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
277
0
    cj5_token tok = ctx->result.tokens[++ctx->index];
278
0
    for(size_t j = tok.size/2; j > 0; j--) {
279
0
        tok = ctx->result.tokens[++ctx->index];
280
0
        switch (tok.type) {
281
0
        case CJ5_TOKEN_STRING: {
282
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
283
0
            unsigned int str_len = 0;
284
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
285
0
            if(strcmp(field_str, "min") == 0)
286
0
                retval = UInt32Field_parseJson(ctx, &field->min, NULL);
287
0
            else if(strcmp(field_str, "max") == 0)
288
0
                retval = UInt32Field_parseJson(ctx, &field->max, NULL);
289
0
            else {
290
0
                LOG_UNKNOWN_FIELD(ctx, field_str);
291
0
            }
292
0
            UA_free(field_str);
293
0
            if(retval != UA_STATUSCODE_GOOD) {
294
0
                return retval;
295
0
            }
296
0
            break;
297
0
        }
298
0
        default:
299
0
            break;
300
0
        }
301
0
    }
302
0
    return UA_STATUSCODE_GOOD;
303
0
}
304
#endif
305
306
/*----------------------Advanced Types------------------------*/
307
0
PARSE_JSON(StringArrayField) {
308
0
    if(configFieldSize == NULL) {
309
0
        UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Pointer to the array size is not set.");
310
0
        return UA_STATUSCODE_BADARGUMENTSMISSING;
311
0
    }
312
0
    cj5_token tok = ctx->result.tokens[++ctx->index];
313
0
    UA_String *stringArray = (UA_String*)UA_malloc(sizeof(UA_String) * tok.size);
314
0
    size_t stringArraySize = 0;
315
0
    for(size_t j = tok.size; j > 0; j--) {
316
0
        UA_String out = {.length = 0, .data = NULL};
317
0
        UA_StatusCode retval = StringField_parseJson(ctx, &out, NULL);
318
0
        if(retval != UA_STATUSCODE_GOOD) {
319
0
            UA_String_clear(&out);
320
0
            UA_Array_delete(stringArray, stringArraySize, &UA_TYPES[UA_TYPES_STRING]);
321
0
            return retval;
322
0
        }
323
0
        UA_String_copy(&out, &stringArray[stringArraySize++]);
324
0
        UA_String_clear(&out);
325
0
    }
326
    /* Add to the config */
327
0
    UA_String **field = (UA_String**)configField;
328
0
    if(*configFieldSize > 0) {
329
0
        UA_Array_delete(*field, *configFieldSize,
330
0
                        &UA_TYPES[UA_TYPES_STRING]);
331
0
        *field = NULL;
332
0
        *configFieldSize = 0;
333
0
    }
334
0
    UA_StatusCode retval =
335
0
        UA_Array_copy(stringArray, stringArraySize,
336
0
                      (void**)field, &UA_TYPES[UA_TYPES_STRING]);
337
0
    *configFieldSize = stringArraySize;
338
339
    /* Clean up */
340
0
    UA_Array_delete(stringArray, stringArraySize, &UA_TYPES[UA_TYPES_STRING]);
341
0
    return retval;
342
0
}
343
0
PARSE_JSON(DateTimeField) {
344
0
    cj5_token tok = ctx->result.tokens[++ctx->index];
345
0
    UA_ByteString buf = getJsonPart(tok, ctx->json);
346
0
    UA_DateTime out;
347
0
    UA_DateTime_init(&out);
348
0
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_DATETIME], NULL);
349
0
    if(retval != UA_STATUSCODE_GOOD)
350
0
        return retval;
351
0
    UA_DateTime *field = (UA_DateTime*)configField;
352
0
    *field = out;
353
0
    return retval;
354
0
}
355
0
PARSE_JSON(BuildInfo) {
356
0
    UA_BuildInfo *field = (UA_BuildInfo*)configField;
357
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
358
0
    cj5_token tok = ctx->result.tokens[++ctx->index];
359
0
    for(size_t j = tok.size/2; j > 0; j--) {
360
0
        tok = ctx->result.tokens[++ctx->index];
361
0
        switch (tok.type) {
362
0
        case CJ5_TOKEN_STRING: {
363
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
364
0
            unsigned int str_len = 0;
365
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
366
0
            if(strcmp(field_str, "productUri") == 0)
367
0
                retval = StringField_parseJson(ctx, &field->productUri, NULL);
368
0
            else if(strcmp(field_str, "manufacturerName") == 0)
369
0
                retval = StringField_parseJson(ctx, &field->manufacturerName, NULL);
370
0
            else if(strcmp(field_str, "productName") == 0)
371
0
                retval = StringField_parseJson(ctx, &field->productName, NULL);
372
0
            else if(strcmp(field_str, "softwareVersion") == 0)
373
0
                retval = StringField_parseJson(ctx, &field->softwareVersion, NULL);
374
0
            else if(strcmp(field_str, "buildNumber") == 0)
375
0
                retval = StringField_parseJson(ctx, &field->buildNumber, NULL);
376
0
            else if(strcmp(field_str, "buildDate") == 0)
377
0
                retval = DateTimeField_parseJson(ctx, &field->buildDate, NULL);
378
0
            else {
379
0
                LOG_UNKNOWN_FIELD(ctx, field_str);
380
0
            }
381
0
            UA_free(field_str);
382
0
            if(retval != UA_STATUSCODE_GOOD) {
383
0
                return retval;
384
0
            }
385
0
            break;
386
0
        }
387
0
        default:
388
0
            break;
389
0
        }
390
0
    }
391
0
    return UA_STATUSCODE_GOOD;
392
0
}
393
394
0
PARSE_JSON(ApplicationTypeField) {
395
0
    cj5_token tok = ctx->result.tokens[++ctx->index];
396
0
    UA_ByteString rawToken = getJsonPart(tok, ctx->json);
397
0
    UA_ApplicationType *field = (UA_ApplicationType*)configField;
398
0
    char *fieldStr = (char*)UA_malloc(tok.size + 1);
399
0
    unsigned int strLen = 0;
400
401
0
    if(cj5_get_str(&ctx->result, (unsigned int)ctx->index, fieldStr, &strLen) == CJ5_ERROR_NONE) {
402
0
        if(strcmp("Client", fieldStr) == 0)
403
0
            *field = UA_APPLICATIONTYPE_CLIENT;
404
0
        else if(strcmp("Server", fieldStr) == 0)
405
0
            *field = UA_APPLICATIONTYPE_SERVER;
406
0
        else if(strcmp("ClientAndServer", fieldStr) == 0)
407
0
            *field = UA_APPLICATIONTYPE_CLIENTANDSERVER;
408
0
        else if(strcmp("DiscoveryServer", fieldStr) == 0)
409
0
            *field = UA_APPLICATIONTYPE_DISCOVERYSERVER;
410
0
        else {
411
0
            UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
412
0
                        "Unknown ApplicationType '%s'", fieldStr);
413
0
            UA_free(fieldStr);
414
0
            return UA_STATUSCODE_BAD;
415
0
        }
416
0
        UA_free(fieldStr);
417
0
        return UA_STATUSCODE_GOOD;
418
0
    }
419
0
    UA_free(fieldStr);
420
421
    /* Try numeric fallback */
422
0
    UA_UInt32 enumValue;
423
0
    UA_StatusCode retval = UA_decodeJson(&rawToken, &enumValue, &UA_TYPES[UA_TYPES_UINT32], NULL);
424
0
    if(retval != UA_STATUSCODE_GOOD) {
425
0
        UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
426
0
                    "Unknown ApplicationType '%S'", rawToken);
427
0
        return retval;
428
0
    }
429
0
    *field = (UA_ApplicationType)enumValue;
430
0
    return UA_STATUSCODE_GOOD;
431
0
}
432
433
typedef enum {
434
    GENERICAPPLICATIONTYPE_CLIENT,
435
    GENERICAPPLICATIONTYPE_SERVER,
436
    GENERICAPPLICATIONTYPE_ANY
437
} GenericApplicationType;
438
439
static UA_StatusCode
440
GenericApplicationDescriptionField_parseJson(ParsingCtx *ctx, void *configField, size_t *configFieldSize, GenericApplicationType type)
441
83
{
442
83
    UA_ApplicationDescription *field = (UA_ApplicationDescription*)configField;
443
83
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
444
83
    cj5_token tok = ctx->result.tokens[++ctx->index];
445
1.71k
    for(size_t j = tok.size/2; j > 0; j--) {
446
1.63k
        tok = ctx->result.tokens[++ctx->index];
447
1.63k
        switch (tok.type) {
448
696
        case CJ5_TOKEN_STRING: {
449
696
            char *field_str = (char*)UA_malloc(tok.size + 1);
450
696
            unsigned int str_len = 0;
451
696
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
452
696
            if(strcmp(field_str, "applicationUri") == 0)
453
0
                retval = StringField_parseJson(ctx, &field->applicationUri, NULL);
454
696
            else if(strcmp(field_str, "productUri") == 0)
455
0
                retval = StringField_parseJson(ctx, &field->productUri, NULL);
456
696
            else if(strcmp(field_str, "applicationName") == 0)
457
0
                retval = LocalizedTextField_parseJson(ctx, &field->applicationName, NULL);
458
696
            else if(strcmp(field_str, "applicationType") == 0 &&
459
0
                    type != GENERICAPPLICATIONTYPE_CLIENT) {
460
0
                retval = ApplicationTypeField_parseJson(ctx, &field->applicationType, NULL);
461
0
            }
462
696
            else if(strcmp(field_str, "gatewayServerUri") == 0 &&
463
0
                    type != GENERICAPPLICATIONTYPE_CLIENT)
464
0
                retval = StringField_parseJson(ctx, &field->gatewayServerUri, NULL);
465
696
            else if(strcmp(field_str, "discoveryProfileUri") == 0 &&
466
0
                    type != GENERICAPPLICATIONTYPE_CLIENT)
467
0
                retval = StringField_parseJson(ctx, &field->discoveryProfileUri, NULL);
468
696
            else if(strcmp(field_str, "discoveryUrls") == 0)
469
0
                retval = StringArrayField_parseJson(ctx, &field->discoveryUrls, &field->discoveryUrlsSize);
470
696
            else {
471
696
                LOG_UNKNOWN_FIELD(ctx, field_str);
472
696
            }
473
696
            UA_free(field_str);
474
696
            if(retval != UA_STATUSCODE_GOOD) {
475
0
                return retval;
476
0
            }
477
696
            break;
478
696
        }
479
934
        default:
480
934
            break;
481
1.63k
        }
482
1.63k
    }
483
83
    if(type == GENERICAPPLICATIONTYPE_CLIENT) {
484
0
        field->applicationType = UA_APPLICATIONTYPE_CLIENT;
485
0
        field->discoveryUrlsSize = 0;
486
0
        field->discoveryUrls = NULL;
487
0
    }
488
83
    return UA_STATUSCODE_GOOD;
489
83
}
490
491
0
PARSE_JSON(MessageSecurityMode) {
492
0
    size_t index = ++ctx->index;
493
0
    cj5_token tok = ctx->result.tokens[index];
494
0
    UA_ByteString rawToken = getJsonPart(tok, ctx->json);
495
0
    UA_MessageSecurityMode *field = (UA_MessageSecurityMode*)configField;
496
0
    char *fieldStr = (char*)UA_malloc(tok.size + 1);
497
0
    unsigned int strLen = 0;
498
499
0
    if(cj5_get_str(&ctx->result, (unsigned int)ctx->index, fieldStr, &strLen) == CJ5_ERROR_NONE) {
500
0
        if(strcmp("Invalid", fieldStr) == 0)
501
0
            *field = UA_MESSAGESECURITYMODE_INVALID;
502
0
        else if(strcmp("None", fieldStr) == 0)
503
0
            *field = UA_MESSAGESECURITYMODE_NONE;
504
0
        else if(strcmp("Sign", fieldStr) == 0)
505
0
            *field = UA_MESSAGESECURITYMODE_SIGN;
506
0
        else if(strcmp("SignAndEncrypt", fieldStr) == 0)
507
0
            *field = UA_MESSAGESECURITYMODE_SIGNANDENCRYPT;
508
0
        else {
509
0
            UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
510
0
                         "Unknown MessageSecurityMode '%.*s'", (int)strLen, fieldStr);
511
0
            UA_free(fieldStr);
512
0
            return UA_STATUSCODE_BAD;
513
0
        }
514
0
        UA_free(fieldStr);
515
0
        return UA_STATUSCODE_GOOD;
516
0
    }
517
0
    UA_free(fieldStr);
518
    /* Try numeric fallback */
519
0
    UA_UInt32 enumValue;
520
0
    UA_StatusCode retval = UA_decodeJson(&rawToken, &enumValue, &UA_TYPES[UA_TYPES_UINT32], NULL);
521
0
    if(retval == UA_STATUSCODE_GOOD) {
522
0
        *field = (UA_MessageSecurityMode)enumValue;
523
0
        return UA_STATUSCODE_GOOD;
524
0
    }
525
0
    UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
526
0
                    "Unknown MessageSecurityMode '%S'", rawToken);
527
0
    return UA_STATUSCODE_BAD;
528
0
}
529
530
#ifdef UA_ENABLE_SUBSCRIPTIONS
531
18
PARSE_JSON(SubscriptionConfigurationField) {
532
18
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
533
18
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
534
18
    cj5_token tok = ctx->result.tokens[++ctx->index];
535
1.86k
    for(size_t j = tok.size/2; j > 0; j--) {
536
1.84k
        tok = ctx->result.tokens[++ctx->index];
537
1.84k
        switch (tok.type) {
538
29
        case CJ5_TOKEN_STRING: {
539
29
            char *field_str = (char*)UA_malloc(tok.size + 1);
540
29
            unsigned int str_len = 0;
541
29
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
542
29
            if(strcmp(field_str, "maxSubscriptions") == 0)
543
0
                retval = UInt32Field_parseJson(ctx, &config->maxSubscriptions, NULL);
544
29
            else if(strcmp(field_str, "maxSubscriptionsPerSession") == 0)
545
0
                retval = UInt32Field_parseJson(ctx, &config->maxSubscriptionsPerSession, NULL);
546
29
            else if(strcmp(field_str, "publishingIntervalLimits") == 0)
547
0
                retval = DurationRangeField_parseJson(ctx, &config->publishingIntervalLimits, NULL);
548
29
            else if(strcmp(field_str, "lifeTimeCountLimits") == 0)
549
0
                retval = UInt32RangeField_parseJson(ctx, &config->lifeTimeCountLimits, NULL);
550
29
            else if(strcmp(field_str, "keepAliveCountLimits") == 0)
551
0
                retval = UInt32RangeField_parseJson(ctx, &config->keepAliveCountLimits, NULL);
552
29
            else if(strcmp(field_str, "maxNotificationsPerPublish") == 0)
553
0
                retval = UInt32Field_parseJson(ctx, &config->maxNotificationsPerPublish, NULL);
554
29
            else if(strcmp(field_str, "enableRetransmissionQueue") == 0)
555
0
                retval = BooleanField_parseJson(ctx, &config->enableRetransmissionQueue, NULL);
556
29
            else if(strcmp(field_str, "maxRetransmissionQueueSize") == 0)
557
0
                retval = UInt32Field_parseJson(ctx, &config->maxRetransmissionQueueSize, NULL);
558
29
# ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
559
29
            else if(strcmp(field_str, "maxEventsPerNode") == 0)
560
0
                retval = UInt32Field_parseJson(ctx, &config->maxEventsPerNode, NULL);
561
29
# endif
562
29
            else if(strcmp(field_str, "maxMonitoredItems") == 0)
563
0
                retval = UInt32Field_parseJson(ctx, &config->maxMonitoredItems, NULL);
564
29
            else if(strcmp(field_str, "maxMonitoredItemsPerSubscription") == 0)
565
0
                retval = UInt32Field_parseJson(ctx, &config->maxMonitoredItemsPerSubscription, NULL);
566
29
            else if(strcmp(field_str, "samplingIntervalLimits") == 0)
567
0
                retval = DurationRangeField_parseJson(ctx, &config->samplingIntervalLimits, NULL);
568
29
            else if(strcmp(field_str, "queueSizeLimits") == 0)
569
0
                retval = UInt32RangeField_parseJson(ctx, &config->queueSizeLimits, NULL);
570
29
            else if(strcmp(field_str, "maxPublishReqPerSession") == 0)
571
0
                retval = UInt32Field_parseJson(ctx, &config->maxPublishReqPerSession, NULL);
572
29
            else {
573
29
                LOG_UNKNOWN_FIELD(ctx, field_str);
574
29
            }
575
29
            UA_free(field_str);
576
29
            if(retval != UA_STATUSCODE_GOOD) {
577
0
                return retval;
578
0
            }
579
29
            break;
580
29
        }
581
1.81k
        default:
582
1.81k
            break;
583
1.84k
        }
584
1.84k
    }
585
18
    return UA_STATUSCODE_GOOD;
586
18
}
587
#endif
588
589
21
PARSE_JSON(TcpConfigurationField) {
590
21
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
591
21
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
592
21
    cj5_token tok = ctx->result.tokens[++ctx->index];
593
1.03k
    for(size_t j = tok.size/2; j > 0; j--) {
594
1.01k
        tok = ctx->result.tokens[++ctx->index];
595
1.01k
        switch (tok.type) {
596
68
        case CJ5_TOKEN_STRING: {
597
68
            char *field_str = (char*)UA_malloc(tok.size + 1);
598
68
            unsigned int str_len = 0;
599
68
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
600
68
            if(strcmp(field_str, "tcpBufSize") == 0)
601
0
                retval = UInt32Field_parseJson(ctx, &config->tcpBufSize, NULL);
602
68
            else if(strcmp(field_str, "tcpMaxMsgSize") == 0)
603
0
                retval = UInt32Field_parseJson(ctx, &config->tcpMaxMsgSize, NULL);
604
68
            else if(strcmp(field_str, "tcpMaxChunks") == 0)
605
0
                retval = UInt32Field_parseJson(ctx, &config->tcpMaxChunks, NULL);
606
68
            else {
607
68
                LOG_UNKNOWN_FIELD(ctx, field_str);
608
68
            }
609
68
            UA_free(field_str);
610
68
            if(retval != UA_STATUSCODE_GOOD) {
611
0
                return retval;
612
0
            }
613
68
            break;
614
68
        }
615
948
        default:
616
948
            break;
617
1.01k
        }
618
1.01k
    }
619
21
    return UA_STATUSCODE_GOOD;
620
21
}
621
622
#ifdef UA_ENABLE_PUBSUB
623
22
PARSE_JSON(PubsubConfigurationField) {
624
22
    UA_PubSubConfiguration *field = (UA_PubSubConfiguration*)configField;
625
22
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
626
22
    cj5_token tok = ctx->result.tokens[++ctx->index];
627
1.43k
    for(size_t j = tok.size/2; j > 0; j--) {
628
1.41k
        tok = ctx->result.tokens[++ctx->index];
629
1.41k
        switch (tok.type) {
630
12
        case CJ5_TOKEN_STRING: {
631
12
            char *field_str = (char*)UA_malloc(tok.size + 1);
632
12
            unsigned int str_len = 0;
633
12
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
634
12
            if(strcmp(field_str, "enableDeltaFrames") == 0)
635
0
                retval = BooleanField_parseJson(ctx, &field->enableDeltaFrames, NULL);
636
12
#ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
637
12
            else if(strcmp(field_str, "enableInformationModelMethods") == 0)
638
0
                retval = BooleanField_parseJson(ctx, &field->enableInformationModelMethods, NULL);
639
12
#endif
640
12
            else {
641
12
                LOG_UNKNOWN_FIELD(ctx, field_str);
642
12
            }
643
12
            UA_free(field_str);
644
12
            if(retval != UA_STATUSCODE_GOOD) {
645
0
                return retval;
646
0
            }
647
12
            break;
648
12
        }
649
1.40k
        default:
650
1.40k
            break;
651
1.41k
        }
652
1.41k
    }
653
22
    return UA_STATUSCODE_GOOD;
654
22
}
655
#endif
656
657
#ifdef UA_ENABLE_HISTORIZING
658
14
PARSE_JSON(HistorizingConfigurationField) {
659
14
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
660
14
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
661
14
    cj5_token tok = ctx->result.tokens[++ctx->index];
662
654
    for(size_t j = tok.size/2; j > 0; j--) {
663
640
        tok = ctx->result.tokens[++ctx->index];
664
640
        switch (tok.type) {
665
11
        case CJ5_TOKEN_STRING: {
666
11
            char *field_str = (char*)UA_malloc(tok.size + 1);
667
11
            unsigned int str_len = 0;
668
11
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
669
11
            if(strcmp(field_str, "accessHistoryDataCapability") == 0)
670
0
                retval = BooleanField_parseJson(ctx, &config->accessHistoryDataCapability, NULL);
671
11
            else if(strcmp(field_str, "maxReturnDataValues") == 0)
672
0
                retval = UInt32Field_parseJson(ctx, &config->maxReturnDataValues, NULL);
673
11
            else if(strcmp(field_str, "accessHistoryEventsCapability") == 0)
674
0
                retval = BooleanField_parseJson(ctx, &config->accessHistoryEventsCapability, NULL);
675
11
            else if(strcmp(field_str, "maxReturnEventValues") == 0)
676
0
                retval = UInt32Field_parseJson(ctx, &config->maxReturnEventValues, NULL);
677
11
            else if(strcmp(field_str, "insertDataCapability") == 0)
678
0
                retval = BooleanField_parseJson(ctx, &config->insertDataCapability, NULL);
679
11
            else if(strcmp(field_str, "insertEventCapability") == 0)
680
0
                retval = BooleanField_parseJson(ctx, &config->insertEventCapability, NULL);
681
11
            else if(strcmp(field_str, "insertAnnotationsCapability") == 0)
682
0
                retval = BooleanField_parseJson(ctx, &config->insertAnnotationsCapability, NULL);
683
11
            else if(strcmp(field_str, "replaceDataCapability") == 0)
684
0
                retval = BooleanField_parseJson(ctx, &config->replaceDataCapability, NULL);
685
11
            else if(strcmp(field_str, "replaceEventCapability") == 0)
686
0
                retval = BooleanField_parseJson(ctx, &config->replaceEventCapability, NULL);
687
11
            else if(strcmp(field_str, "updateDataCapability") == 0)
688
0
                retval = BooleanField_parseJson(ctx, &config->updateDataCapability, NULL);
689
11
            else if(strcmp(field_str, "updateEventCapability") == 0)
690
0
                retval = BooleanField_parseJson(ctx, &config->updateEventCapability, NULL);
691
11
            else if(strcmp(field_str, "deleteRawCapability") == 0)
692
0
                retval = BooleanField_parseJson(ctx, &config->deleteRawCapability, NULL);
693
11
            else if(strcmp(field_str, "deleteEventCapability") == 0)
694
0
                retval = BooleanField_parseJson(ctx, &config->deleteEventCapability, NULL);
695
11
            else if(strcmp(field_str, "deleteAtTimeDataCapability") == 0)
696
0
                retval = BooleanField_parseJson(ctx, &config->deleteAtTimeDataCapability, NULL);
697
11
            else {
698
11
                LOG_UNKNOWN_FIELD(ctx, field_str);
699
11
            }
700
11
            UA_free(field_str);
701
11
            if(retval != UA_STATUSCODE_GOOD) {
702
0
                return retval;
703
0
            }
704
11
            break;
705
11
        }
706
629
        default:
707
629
            break;
708
640
        }
709
640
    }
710
14
    return UA_STATUSCODE_GOOD;
711
14
}
712
#endif
713
714
static
715
UA_StatusCode
716
UA_SecurityPolicy_initByUri(UA_SecurityPolicy *policy,
717
                            UA_ApplicationType applicationType,
718
                            const UA_String *policyUri,
719
                            const UA_ByteString *localCertificate,
720
                            const UA_ByteString *localPrivateKey,
721
8
                            const UA_Logger *logger) {
722
8
    if(!policy || !policyUri)
723
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
724
725
8
    UA_ByteString certificate = UA_BYTESTRING_NULL;
726
8
    if(localCertificate)
727
8
    certificate = *localCertificate;
728
8
#ifdef UA_ENABLE_ENCRYPTION
729
8
    UA_ByteString privateKey = UA_BYTESTRING_NULL;
730
8
    if(localPrivateKey)
731
8
        privateKey = *localPrivateKey;
732
8
#endif
733
734
8
    if(UA_String_equal(policyUri, &UA_SECURITY_POLICY_NONE_URI))
735
0
        return UA_SecurityPolicy_None(policy, certificate, logger);
736
737
8
#ifdef UA_ENABLE_ENCRYPTION
738
8
    static const UA_String basic128Rsa15Uri =
739
8
        UA_STRING_STATIC("http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15");
740
8
    static const UA_String basic256Uri =
741
8
        UA_STRING_STATIC("http://opcfoundation.org/UA/SecurityPolicy#Basic256");
742
8
    static const UA_String basic256Sha256Uri =
743
8
        UA_STRING_STATIC("http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256");
744
8
    static const UA_String aes128Sha256RsaOaepUri =
745
8
        UA_STRING_STATIC("http://opcfoundation.org/UA/SecurityPolicy#Aes128_Sha256_RsaOaep");
746
8
    static const UA_String aes256Sha256RsaPssUri =
747
8
        UA_STRING_STATIC("http://opcfoundation.org/UA/SecurityPolicy#Aes256_Sha256_RsaPss");
748
#if defined(UA_ENABLE_ENCRYPTION_OPENSSL)
749
    static const UA_String eccNistP256Uri =
750
        UA_STRING_STATIC("http://opcfoundation.org/UA/SecurityPolicy#EccNistP256");
751
#endif
752
753
8
    if(UA_String_equal(policyUri, &basic128Rsa15Uri))
754
0
        return UA_SecurityPolicy_Basic128Rsa15(policy, certificate, privateKey, logger);
755
8
    if(UA_String_equal(policyUri, &basic256Uri))
756
0
        return UA_SecurityPolicy_Basic256(policy, certificate, privateKey, logger);
757
8
    if(UA_String_equal(policyUri, &basic256Sha256Uri))
758
0
        return UA_SecurityPolicy_Basic256Sha256(policy, certificate, privateKey, logger);
759
8
    if(UA_String_equal(policyUri, &aes128Sha256RsaOaepUri))
760
0
        return UA_SecurityPolicy_Aes128Sha256RsaOaep(policy, certificate, privateKey, logger);
761
8
    if(UA_String_equal(policyUri, &aes256Sha256RsaPssUri))
762
0
        return UA_SecurityPolicy_Aes256Sha256RsaPss(policy, certificate, privateKey, logger);
763
#if defined(UA_ENABLE_ENCRYPTION_OPENSSL)
764
    if(UA_String_equal(policyUri, &eccNistP256Uri))
765
        return UA_SecurityPolicy_EccNistP256(policy, applicationType, certificate,
766
                                             privateKey, logger);
767
#endif
768
8
#endif
769
770
8
    return UA_STATUSCODE_BADNOTSUPPORTED;
771
8
}
772
773
0
PARSE_JSON(CertificateFileField) {
774
0
    UA_ByteString *certificate = (UA_ByteString*)configField;
775
0
    UA_ByteString_init(certificate);
776
0
    UA_String filename = {.length = 0, .data = NULL};
777
778
0
    UA_StatusCode retval = StringField_parseJson(ctx, &filename, NULL);
779
0
    if(retval == UA_STATUSCODE_GOOD) {
780
0
        if (filename.length > 0) {
781
0
#ifdef UA_ENABLE_ENCRYPTION
782
0
            char *certfile = (char *)UA_malloc(filename.length + 1);
783
0
            memcpy(certfile, filename.data, filename.length);
784
0
            certfile[filename.length] = '\0';
785
0
            *certificate = loadCertificateFile((char const *)certfile);
786
0
            UA_free(certfile);
787
0
#endif
788
0
            UA_ByteString_clear(&filename);
789
0
        } else {
790
0
            UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION,
791
0
                            "Certificate file path is empty.");
792
0
            retval = UA_STATUSCODE_BADINTERNALERROR;
793
0
        }
794
0
    }
795
0
    return retval;
796
0
}
797
798
static UA_StatusCode
799
SecurityPolicyField_parseJson(ParsingCtx *ctx, UA_SecurityPolicy *field,
800
                              UA_ApplicationType applicationType,
801
8
                              const UA_Logger *logger) {
802
8
    UA_String policy = {.length = 0, .data = NULL};
803
8
    UA_ByteString certificate = {.length = 0, .data = NULL};
804
8
    UA_ByteString privateKey = {.length = 0, .data = NULL};
805
8
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
806
807
8
    cj5_token tok = ctx->result.tokens[++ctx->index];
808
245
    for(size_t i = tok.size / 2; i > 0 && retval == UA_STATUSCODE_GOOD; i--) {
809
237
        tok = ctx->result.tokens[++ctx->index];
810
237
        switch(tok.type) {
811
0
        case CJ5_TOKEN_STRING: {
812
0
            char *field_str = (char *)UA_malloc(tok.size + 1);
813
0
            unsigned int str_len = 0;
814
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
815
0
            if(strcmp(field_str, "certificate") == 0) {
816
0
                retval = CertificateFileField_parseJson(ctx, &certificate, NULL);
817
0
            } else if(strcmp(field_str, "privateKey") == 0) {
818
0
                retval = CertificateFileField_parseJson(ctx, &privateKey, NULL);
819
0
            } else if(strcmp(field_str, "policy") == 0) {
820
0
                retval = StringField_parseJson(ctx, &policy, NULL);
821
0
            } else {
822
0
                LOG_UNKNOWN_FIELD(ctx, field_str);
823
0
            }
824
0
            UA_free(field_str);
825
0
            break;
826
0
        }
827
237
        default:
828
237
            break;
829
237
        }
830
237
    }
831
832
8
    if(retval == UA_STATUSCODE_GOOD) {
833
8
        retval = UA_SecurityPolicy_initByUri(field, applicationType, &policy,
834
8
                                             &certificate, &privateKey, logger);
835
8
    }
836
837
8
    if(policy.length > 0)
838
0
        UA_String_clear(&policy);
839
8
    if(certificate.length > 0)
840
0
        UA_ByteString_clear(&certificate);
841
8
    if(privateKey.length > 0)
842
0
        UA_ByteString_clear(&privateKey);
843
844
8
    return retval;
845
8
}
846
847
static UA_StatusCode
848
SecurityPoliciesField_parseJson(ParsingCtx *ctx, void *configField, size_t *configFieldSize, UA_ApplicationType applicationType,
849
8
                                const UA_Logger *logger) {
850
8
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
851
8
    UA_SecurityPolicy **securityPoliciesField = (UA_SecurityPolicy**)configField;
852
853
8
    cj5_token tok = ctx->result.tokens[++ctx->index];
854
8
    for(size_t j = tok.size; j > 0; j--) {
855
8
        UA_SecurityPolicy *tmp = (UA_SecurityPolicy*)
856
8
            UA_realloc(*securityPoliciesField, sizeof(UA_SecurityPolicy) * (*configFieldSize + 1));
857
8
        if(!tmp) {
858
0
            retval = UA_STATUSCODE_BADOUTOFMEMORY;
859
0
            break;
860
0
        }
861
8
        *securityPoliciesField = tmp;
862
8
        retval = SecurityPolicyField_parseJson(ctx, &tmp[*configFieldSize],
863
8
                                               applicationType, logger);
864
8
        if(retval != UA_STATUSCODE_GOOD) {
865
8
            if(*configFieldSize == 0) {
866
0
                UA_free(*securityPoliciesField);
867
0
                *securityPoliciesField = NULL;
868
0
            }
869
8
            break;
870
8
        }
871
0
        (*configFieldSize)++;
872
0
    }
873
8
    return retval;
874
8
}
875
876
#ifdef UA_ENABLE_ENCRYPTION
877
0
PARSE_JSON(SecurityPkiField) {
878
0
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
879
0
    UA_String pkiFolder = {.length = 0, .data = NULL};
880
881
0
    cj5_token tok = ctx->result.tokens[++ctx->index];
882
0
    UA_ByteString buf = getJsonPart(tok, ctx->json);
883
0
    UA_StatusCode retval = UA_decodeJson(&buf, &pkiFolder, &UA_TYPES[UA_TYPES_STRING], NULL);
884
0
    if(retval != UA_STATUSCODE_GOOD)
885
0
        return retval;
886
887
0
#if defined(__linux__) || defined(UA_ARCHITECTURE_WIN32) || defined(__APPLE__) || defined(__OpenBSD__)
888
    /* Set up the parameters for the filestore certificate store */
889
0
    UA_KeyValuePair params[2];
890
0
    size_t paramsSize = 2;
891
892
0
    params[0].key = UA_QUALIFIEDNAME(0, "max-trust-listsize");
893
0
    UA_Variant_setScalar(&params[0].value, &config->maxTrustListSize, &UA_TYPES[UA_TYPES_UINT32]);
894
0
    params[1].key = UA_QUALIFIEDNAME(0, "max-rejected-listsize");
895
0
    UA_Variant_setScalar(&params[1].value, &config->maxRejectedListSize, &UA_TYPES[UA_TYPES_UINT32]);
896
897
0
    UA_KeyValueMap paramsMap;
898
0
    paramsMap.map = params;
899
0
    paramsMap.mapSize = paramsSize;
900
901
    /* set server config field */
902
0
    UA_NodeId defaultApplicationGroup =
903
0
           UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP);
904
0
    retval = UA_CertificateGroup_Filestore(&config->secureChannelPKI, &defaultApplicationGroup,
905
0
                                           pkiFolder, config->logging, &paramsMap);
906
0
    if(retval != UA_STATUSCODE_GOOD) {
907
0
        UA_String_clear(&pkiFolder);
908
0
        return retval;
909
0
    }
910
911
0
    UA_NodeId defaultUserTokenGroup =
912
0
            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP);
913
0
    retval = UA_CertificateGroup_Filestore(&config->sessionPKI, &defaultUserTokenGroup,
914
0
                                            pkiFolder, config->logging, &paramsMap);
915
0
    if(retval != UA_STATUSCODE_GOOD) {
916
0
        UA_String_clear(&pkiFolder);
917
0
        return retval;
918
0
    }
919
920
    /* Clean up */
921
0
    UA_String_clear(&pkiFolder);
922
#else
923
    (void)config;
924
    UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION,
925
                   "pkiFolder is not supported on this platform. "
926
                   "Trusted clients will not be verified.");
927
#endif
928
0
    return UA_STATUSCODE_GOOD;
929
0
}
930
#endif
931
932
56
PARSE_JSON(RuleHandlingField) {
933
56
    UA_UInt32 enum_value;
934
56
    UA_StatusCode retval = UInt32Field_parseJson(ctx, &enum_value, NULL);
935
56
    if(retval != UA_STATUSCODE_GOOD)
936
34
        return retval;
937
22
    UA_RuleHandling *field = (UA_RuleHandling*)configField;
938
22
    *field = (UA_RuleHandling)enum_value;
939
22
    return retval;
940
56
}
941
942
/* Skips unknown item (simple, object or array) in config file.
943
* Unknown items may happen if we don't support some features.
944
* E.g. if  UA_ENABLE_ENCRYPTION is not defined and config file
945
* contains "securityPolicies" entry.
946
*/
947
static void
948
4.50k
skipUnknownItem(ParsingCtx* ctx) {
949
4.50k
    cj5_skip(&ctx->result, &ctx->index);
950
4.50k
}
951
952
static UA_StatusCode
953
3.07k
parseJSONServerConfig(UA_ServerConfig *config, UA_ByteString json_config) {
954
    // Parsing json config
955
3.07k
    const char *json = (const char*)json_config.data;
956
3.07k
    cj5_token tokens[MAX_TOKENS];
957
3.07k
    cj5_result r = cj5_parse(json, (unsigned int)json_config.length, tokens, MAX_TOKENS, NULL);
958
959
    /* Validate the parse result: must succeed and produce a root object
960
     * with at least one key-value pair (i.e. >= 2 tokens). */
961
3.07k
    if(r.error != CJ5_ERROR_NONE || r.num_tokens < 2 ||
962
2.38k
       r.tokens[0].type != CJ5_TOKEN_OBJECT)
963
728
        return UA_STATUSCODE_BADDECODINGERROR;
964
965
2.34k
    ParsingCtx ctx;
966
2.34k
    ctx.json = json;
967
2.34k
    ctx.result = r;
968
2.34k
    ctx.index = 1; // The first token is ignored because it is known and not needed.
969
970
2.34k
    ctx.logging = config->logging;
971
972
    /* Buffer for the field name */
973
2.34k
    char field[256];
974
975
2.34k
    size_t serverConfigSize = 0;
976
2.34k
    if(ctx.result.tokens)
977
2.34k
        serverConfigSize = (ctx.result.tokens[ctx.index-1].size/2);
978
2.34k
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
979
13.5k
    for (size_t j = serverConfigSize; j > 0; j--) {
980
11.2k
        cj5_token tok = ctx.result.tokens[ctx.index];
981
11.2k
        switch (tok.type) {
982
10.7k
            case CJ5_TOKEN_STRING: {
983
10.7k
                if(tok.size >= 255) {
984
410
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
985
410
                                   "Configuration field name too long");
986
410
                    continue;
987
410
                }
988
10.3k
                unsigned int str_len = 0;
989
10.3k
                cj5_error_code res = cj5_get_str(&ctx.result, (unsigned int)ctx.index, field, &str_len);
990
10.3k
                if(res != CJ5_ERROR_NONE) {
991
5.49k
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
992
5.49k
                                   "Configuration field name not a valid string");
993
5.49k
                    continue;
994
5.49k
                }
995
4.85k
                if(strcmp(field, "buildInfo") == 0)
996
0
                    retval = BuildInfo_parseJson(&ctx, &config->buildInfo, NULL);
997
4.85k
                else if(strcmp(field, "applicationDescription") == 0)
998
83
                    retval = GenericApplicationDescriptionField_parseJson(&ctx, &config->applicationDescription, NULL, GENERICAPPLICATIONTYPE_SERVER);
999
4.76k
                else if(strcmp(field, "shutdownDelay") == 0)
1000
0
                    retval = DoubleField_parseJson(&ctx, &config->shutdownDelay, NULL);
1001
4.76k
                else if(strcmp(field, "verifyRequestTimestamp") == 0)
1002
56
                    retval = RuleHandlingField_parseJson(&ctx, &config->verifyRequestTimestamp, NULL);
1003
4.71k
                else if(strcmp(field, "allowEmptyVariables") == 0)
1004
0
                    retval = RuleHandlingField_parseJson(&ctx, &config->allowEmptyVariables, NULL);
1005
4.71k
                else if(strcmp(field, "serverUrls") == 0)
1006
0
                    retval = StringArrayField_parseJson(&ctx, &config->serverUrls, &config->serverUrlsSize);
1007
4.71k
                else if(strcmp(field, "tcpEnabled") == 0)
1008
0
                    retval = BooleanField_parseJson(&ctx, &config->tcpEnabled, NULL);
1009
4.71k
                else if(strcmp(field, "tcp") == 0)
1010
21
                    retval = TcpConfigurationField_parseJson(&ctx, config, NULL);
1011
4.69k
                else if(strcmp(field, "securityPolicyNoneDiscoveryOnly") == 0)
1012
1
                    retval = BooleanField_parseJson(&ctx, &config->securityPolicyNoneDiscoveryOnly, NULL);
1013
4.68k
                else if(strcmp(field, "modellingRulesOnInstances") == 0)
1014
0
                    retval = BooleanField_parseJson(&ctx, &config->modellingRulesOnInstances, NULL);
1015
4.68k
                else if(strcmp(field, "maxSecureChannels") == 0)
1016
2
                    retval = UInt16Field_parseJson(&ctx, &config->maxSecureChannels, NULL);
1017
4.68k
                else if(strcmp(field, "maxSecurityTokenLifetime") == 0)
1018
11
                    retval = UInt32Field_parseJson(&ctx, &config->maxSecurityTokenLifetime, NULL);
1019
4.67k
                else if(strcmp(field, "maxSessions") == 0)
1020
43
                    retval = UInt16Field_parseJson(&ctx, &config->maxSessions, NULL);
1021
4.63k
                else if(strcmp(field, "maxSessionTimeout") == 0)
1022
5
                    retval = DoubleField_parseJson(&ctx, &config->maxSessionTimeout, NULL);
1023
4.62k
                else if(strcmp(field, "maxNodesPerRead") == 0)
1024
1
                    retval = UInt32Field_parseJson(&ctx, &config->maxNodesPerRead, NULL);
1025
4.62k
                else if(strcmp(field, "maxNodesPerWrite") == 0)
1026
1
                    retval = UInt32Field_parseJson(&ctx, &config->maxNodesPerWrite, NULL);
1027
4.62k
                else if(strcmp(field, "maxNodesPerMethodCall") == 0)
1028
0
                    retval = UInt32Field_parseJson(&ctx, &config->maxNodesPerMethodCall, NULL);
1029
4.62k
                else if(strcmp(field, "maxNodesPerBrowse") == 0)
1030
0
                    retval = UInt32Field_parseJson(&ctx, &config->maxNodesPerBrowse, NULL);
1031
4.62k
                else if(strcmp(field, "maxNodesPerRegisterNodes") == 0)
1032
0
                    retval = UInt32Field_parseJson(&ctx, &config->maxNodesPerRegisterNodes, NULL);
1033
4.62k
                else if(strcmp(field, "maxNodesPerTranslateBrowsePathsToNodeIds") == 0)
1034
0
                    retval = UInt32Field_parseJson(&ctx, &config->maxNodesPerTranslateBrowsePathsToNodeIds, NULL);
1035
4.62k
                else if(strcmp(field, "maxNodesPerNodeManagement") == 0)
1036
0
                    retval = UInt32Field_parseJson(&ctx, &config->maxNodesPerNodeManagement, NULL);
1037
4.62k
                else if(strcmp(field, "maxMonitoredItemsPerCall") == 0)
1038
0
                    retval = UInt32Field_parseJson(&ctx, &config->maxMonitoredItemsPerCall, NULL);
1039
4.62k
                else if(strcmp(field, "maxReferencesPerNode") == 0)
1040
1
                    retval = UInt32Field_parseJson(&ctx, &config->maxReferencesPerNode, NULL);
1041
4.62k
                else if(strcmp(field, "reverseReconnectInterval") == 0)
1042
1
                    retval = UInt32Field_parseJson(&ctx, &config->reverseReconnectInterval, NULL);
1043
1044
4.62k
#if UA_MULTITHREADING >= 100
1045
4.62k
                else if(strcmp(field, "asyncOperationTimeout") == 0)
1046
0
                    retval = DoubleField_parseJson(&ctx, &config->asyncOperationTimeout, NULL);
1047
4.62k
                else if(strcmp(field, "maxAsyncOperationQueueSize") == 0)
1048
52
                    retval = UInt64Field_parseJson(&ctx, &config->maxAsyncOperationQueueSize, NULL);
1049
4.57k
#endif
1050
1051
4.57k
#ifdef UA_ENABLE_DISCOVERY
1052
4.57k
                else if(strcmp(field, "registeredServersEnabled") == 0)
1053
0
                    retval = BooleanField_parseJson(&ctx, &config->registeredServersEnabled, NULL);
1054
4.57k
                else if(strcmp(field, "registeredServerCleanupTimeout") == 0)
1055
0
                    retval = UInt32Field_parseJson(&ctx, &config->registeredServerCleanupTimeout, NULL);
1056
4.57k
                else if(strcmp(field, "serversOnNetworkEnabled") == 0)
1057
0
                    retval = BooleanField_parseJson(&ctx, &config->serversOnNetworkEnabled, NULL);
1058
4.57k
#endif
1059
1060
4.57k
#ifdef UA_ENABLE_SUBSCRIPTIONS
1061
4.57k
                else if(strcmp(field, "subscriptionsEnabled") == 0)
1062
1
                    retval = BooleanField_parseJson(&ctx, &config->subscriptionsEnabled, NULL);
1063
4.57k
                else if(strcmp(field, "subscriptions") == 0)
1064
18
                    retval = SubscriptionConfigurationField_parseJson(&ctx, config, NULL);
1065
4.55k
# endif
1066
1067
4.55k
#ifdef UA_ENABLE_HISTORIZING
1068
4.55k
                else if(strcmp(field, "historizingEnabled") == 0)
1069
1
                    retval = BooleanField_parseJson(&ctx, &config->historizingEnabled, NULL);
1070
4.55k
                else if(strcmp(field, "historizing") == 0)
1071
14
                    retval = HistorizingConfigurationField_parseJson(&ctx, config, NULL);
1072
4.53k
#endif
1073
1074
4.53k
#ifdef UA_ENABLE_PUBSUB
1075
4.53k
                else if(strcmp(field, "pubsubEnabled") == 0)
1076
1
                    retval = BooleanField_parseJson(&ctx, &config->pubsubEnabled, NULL);
1077
4.53k
                else if(strcmp(field, "pubsub") == 0)
1078
22
                    retval = PubsubConfigurationField_parseJson(&ctx, &config->pubSubConfig, NULL);
1079
4.51k
#endif
1080
4.51k
#ifdef UA_ENABLE_ENCRYPTION
1081
4.51k
                else if(strcmp(field, "securityPolicies") == 0)
1082
8
                    retval = SecurityPoliciesField_parseJson(&ctx, &config->securityPolicies, &config->securityPoliciesSize, UA_APPLICATIONTYPE_SERVER, config->logging);
1083
4.50k
                else if(strcmp(field, "pkiFolder") == 0)
1084
0
                    retval = SecurityPkiField_parseJson(&ctx, config, NULL);
1085
4.50k
#endif
1086
4.50k
                else {
1087
4.50k
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
1088
4.50k
                                   "Field name '%s' unknown or misspelled. Maybe the feature is not enabled.", field);
1089
                    /* skip the name of item */
1090
4.50k
                    ++ctx.index;
1091
                    /* skip value of unknown item */
1092
4.50k
                    skipUnknownItem(&ctx);
1093
                    /* after skipUnknownItem() ctx->index points to the name of the following item.
1094
                       We must decrement index in oder following increment will
1095
                       still set index to the right position (name of the following item) */
1096
4.50k
                    --ctx.index;
1097
4.50k
                }
1098
4.85k
                if(retval != UA_STATUSCODE_GOOD) {
1099
121
                    UA_LOG_ERROR(ctx.logging, UA_LOGCATEGORY_APPLICATION,
1100
121
                                 "An error occurred while parsing the configuration field %s", field);
1101
121
                    return retval;
1102
121
                }
1103
4.72k
                break;
1104
4.85k
            }
1105
4.72k
            default:
1106
527
                break;
1107
11.2k
        }
1108
5.25k
        ctx.index += 1;
1109
5.25k
    }
1110
2.22k
    return retval;
1111
2.34k
}
1112
1113
UA_Server *
1114
3.19k
UA_Server_newFromFile(const UA_ByteString jsonConfig) {
1115
3.19k
    UA_ServerConfig config;
1116
3.19k
    UA_StatusCode res = UA_ServerConfig_loadFromFile(&config, jsonConfig);
1117
3.19k
    if(res != UA_STATUSCODE_GOOD)
1118
948
        return NULL;
1119
2.24k
    return UA_Server_newWithConfig(&config);
1120
3.19k
}
1121
1122
UA_StatusCode
1123
3.07k
UA_ServerConfig_loadFromFile(UA_ServerConfig *config, const UA_ByteString jsonConfig) {
1124
3.07k
    memset(config, 0, sizeof(UA_ServerConfig));
1125
3.07k
    UA_StatusCode res = UA_ServerConfig_setDefault(config);
1126
3.07k
    if (res == UA_STATUSCODE_GOOD) {
1127
3.07k
        res = parseJSONServerConfig(config, jsonConfig);
1128
3.07k
        if (UA_StatusCode_isBad(res)) {
1129
849
            UA_ServerConfig_clear(config);
1130
849
        }
1131
3.07k
    }
1132
3.07k
    return res;
1133
3.07k
}
1134
1135
0
PARSE_JSON(ConnectionConfig) {
1136
0
    UA_ConnectionConfig *field = (UA_ConnectionConfig*)configField;
1137
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
1138
0
    cj5_token tok = ctx->result.tokens[++ctx->index];
1139
0
    for(size_t j = tok.size/2; j > 0; j--) {
1140
0
        tok = ctx->result.tokens[++ctx->index];
1141
0
        switch (tok.type) {
1142
0
        case CJ5_TOKEN_STRING: {
1143
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
1144
0
            unsigned int str_len = 0;
1145
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
1146
0
            if(strcmp(field_str, "protocolVersion") == 0)
1147
0
                retval = UInt32Field_parseJson(ctx, &field->protocolVersion, NULL);
1148
0
            else if(strcmp(field_str, "recvBufferSize") == 0)
1149
0
                retval = UInt32Field_parseJson(ctx, &field->recvBufferSize, NULL);
1150
0
            else if(strcmp(field_str, "sendBufferSize") == 0)
1151
0
                retval = UInt32Field_parseJson(ctx, &field->sendBufferSize, NULL);
1152
0
            else if(strcmp(field_str, "localMaxMessageSize") == 0)
1153
0
                retval = UInt32Field_parseJson(ctx, &field->localMaxMessageSize, NULL);
1154
0
            else if(strcmp(field_str, "remoteMaxMessageSize") == 0)
1155
0
                retval = UInt32Field_parseJson(ctx, &field->remoteMaxMessageSize, NULL);
1156
0
            else if(strcmp(field_str, "localMaxChunkCount") == 0)
1157
0
                retval = UInt32Field_parseJson(ctx, &field->localMaxChunkCount, NULL);
1158
0
            else if(strcmp(field_str, "remoteMaxChunkCount") == 0)
1159
0
                retval = UInt32Field_parseJson(ctx, &field->remoteMaxChunkCount, NULL);
1160
0
            else {
1161
0
                LOG_UNKNOWN_FIELD(ctx, field_str);
1162
0
            }
1163
0
            UA_free(field_str);
1164
0
            if(retval != UA_STATUSCODE_GOOD) {
1165
0
                return retval;
1166
0
            }
1167
0
            break;
1168
0
        }
1169
0
        default:
1170
0
            break;
1171
0
        }
1172
0
    }
1173
0
    return UA_STATUSCODE_GOOD;
1174
0
}
1175
1176
0
PARSE_JSON(UserTokenType) {
1177
0
    cj5_token tok = ctx->result.tokens[++ctx->index];
1178
0
    UA_ByteString rawToken = getJsonPart(tok, ctx->json);
1179
0
    UA_UserTokenType *field = (UA_UserTokenType*)configField;
1180
0
    char *fieldStr = (char*)UA_malloc(tok.size + 1);
1181
0
    unsigned int strLen = 0;
1182
1183
0
    if(cj5_get_str(&ctx->result, (unsigned int)ctx->index, fieldStr, &strLen) == CJ5_ERROR_NONE) {
1184
0
        if(strcmp("Anonymous", fieldStr) == 0)
1185
0
            *field = UA_USERTOKENTYPE_ANONYMOUS;
1186
0
        else if(strcmp("UserName", fieldStr) == 0)
1187
0
            *field = UA_USERTOKENTYPE_USERNAME;
1188
0
        else if(strcmp("Certificate", fieldStr) == 0)
1189
0
            *field = UA_USERTOKENTYPE_CERTIFICATE;
1190
0
        else if(strcmp("IssuedToken", fieldStr) == 0)
1191
0
            *field = UA_USERTOKENTYPE_ISSUEDTOKEN;
1192
0
        else {
1193
0
            UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
1194
0
                        "Unknown UserTokenType '%s'", fieldStr);
1195
0
            UA_free(fieldStr);
1196
0
            return UA_STATUSCODE_BAD;
1197
0
        }
1198
0
        UA_free(fieldStr);
1199
0
        return UA_STATUSCODE_GOOD;
1200
0
    }
1201
0
    UA_free(fieldStr);
1202
1203
    /* Try numeric fallback */
1204
0
    UA_UInt32 enumValue;
1205
0
    UA_StatusCode retval = UA_decodeJson(&rawToken, &enumValue, &UA_TYPES[UA_TYPES_UINT32], NULL);
1206
0
    if(retval != UA_STATUSCODE_GOOD) {
1207
0
        UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
1208
0
                    "Unknown UserTokenType '%S'", rawToken);
1209
0
        return retval;
1210
0
    }
1211
0
    *field = (UA_UserTokenType)enumValue;
1212
0
    return UA_STATUSCODE_GOOD;
1213
0
}
1214
1215
/* Parse the userIdentityToken field. This creates an ExtensionObject containing
1216
 * one of three token types: Anonymous, Username, or Certificate.
1217
 * The type can be explicitly specified via the "type" field, or inferred by
1218
 * the presence of other fields. */
1219
0
PARSE_JSON(UserIdentityToken) {
1220
0
    UA_ExtensionObject *field = (UA_ExtensionObject*)configField;
1221
0
    cj5_token tok = ctx->result.tokens[++ctx->index];
1222
1223
    /* Variables to track which fields are encountered */
1224
0
    UA_Int32 detectedType = -1; /* -1 indicates no type detected yet */
1225
0
    UA_Boolean typeExplicitlySet = false;
1226
1227
    /* Temporary storage for token fields */
1228
0
    UA_AnonymousIdentityToken anonToken;
1229
0
    UA_UserNameIdentityToken userNameToken;
1230
0
    UA_X509IdentityToken certToken;
1231
1232
0
    UA_AnonymousIdentityToken_init(&anonToken);
1233
0
    UA_UserNameIdentityToken_init(&userNameToken);
1234
0
    UA_X509IdentityToken_init(&certToken);
1235
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
1236
1237
0
    for(size_t j = tok.size/2; j > 0 && retval == UA_STATUSCODE_GOOD; j--) {
1238
0
        tok = ctx->result.tokens[++ctx->index];
1239
0
        switch (tok.type) {
1240
0
        case CJ5_TOKEN_STRING: {
1241
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
1242
0
            unsigned int str_len = 0;
1243
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
1244
1245
0
            if(strcmp(field_str, "type") == 0) {
1246
1247
0
                if (detectedType != -1) {
1248
0
                    UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
1249
0
                                 "Inconsistent type information in userIdentityToken. Type was "
1250
0
                                 "already detected as %d, but 'type' field is set to a different "
1251
0
                                 "value.",
1252
0
                                 detectedType);
1253
0
                    retval = UA_STATUSCODE_BAD;
1254
0
                }
1255
1256
0
                if (retval == UA_STATUSCODE_GOOD) {
1257
                    /* Parse the type field */
1258
0
                    cj5_token typeToken = ctx->result.tokens[++ctx->index];
1259
0
                    char *typeStr = (char*)UA_malloc(typeToken.size + 1);
1260
0
                    unsigned int typeLen = 0;
1261
1262
0
                    if(cj5_get_str(&ctx->result, (unsigned int)ctx->index, typeStr, &typeLen) == CJ5_ERROR_NONE) {
1263
0
                        if(strcmp("Anonymous", typeStr) == 0) {
1264
0
                            detectedType = UA_USERTOKENTYPE_ANONYMOUS;
1265
0
                            typeExplicitlySet = true;
1266
0
                        } else if(strcmp("UserName", typeStr) == 0) {
1267
0
                            detectedType = UA_USERTOKENTYPE_USERNAME;
1268
0
                            typeExplicitlySet = true;
1269
0
                        } else if(strcmp("Certificate", typeStr) == 0) {
1270
0
                            detectedType = UA_USERTOKENTYPE_CERTIFICATE;
1271
0
                            typeExplicitlySet = true;
1272
0
                        } else {
1273
0
                            UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
1274
0
                                        "Unknown userIdentityToken type '%s'", typeStr);
1275
0
                            retval = UA_STATUSCODE_BAD;
1276
0
                        }
1277
0
                    } else {
1278
0
                        UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
1279
0
                                    "Failed to parse userIdentityToken type field");
1280
0
                        retval = UA_STATUSCODE_BAD;
1281
0
                    }
1282
0
                    UA_free(typeStr);
1283
0
                }
1284
0
            }
1285
0
            else if(strcmp(field_str, "userName") == 0) {
1286
                /* This field locks in UserName token type */
1287
0
                if(!typeExplicitlySet && detectedType == -1) {
1288
0
                    detectedType = UA_USERTOKENTYPE_USERNAME;
1289
0
                }
1290
0
                if(detectedType != UA_USERTOKENTYPE_USERNAME) {
1291
0
                    UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
1292
0
                                "Field 'userName' can only be used with UserName token type");
1293
0
                    retval = UA_STATUSCODE_BAD;
1294
0
                }
1295
0
                else {
1296
0
                    retval = StringField_parseJson(ctx, &userNameToken.userName, NULL);
1297
0
                }
1298
0
            }
1299
0
            else if(strcmp(field_str, "password") == 0) {
1300
                /* This field locks in UserName token type */
1301
0
                if(!typeExplicitlySet && detectedType == -1) {
1302
0
                    detectedType = UA_USERTOKENTYPE_USERNAME;
1303
0
                }
1304
0
                if(detectedType != UA_USERTOKENTYPE_USERNAME) {
1305
0
                    UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
1306
0
                                "Field 'password' can only be used with UserName token type");
1307
0
                    retval = UA_STATUSCODE_BAD;
1308
0
                } else {
1309
0
                    retval = ByteStringField_parseJson(ctx, &userNameToken.password, NULL);
1310
0
                }
1311
0
            }
1312
0
            else if(strcmp(field_str, "encryptionAlgorithm") == 0) {
1313
                /* This field can be used with UserName token type */
1314
0
                if(!typeExplicitlySet && detectedType == -1) {
1315
0
                    detectedType = UA_USERTOKENTYPE_USERNAME;
1316
0
                }
1317
0
                if(detectedType != UA_USERTOKENTYPE_USERNAME) {
1318
0
                    UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
1319
0
                                "Field 'encryptionAlgorithm' can only be used with UserName token type");
1320
0
                    retval = UA_STATUSCODE_BAD;
1321
0
                }
1322
0
                else {
1323
0
                    retval = StringField_parseJson(ctx, &userNameToken.encryptionAlgorithm, NULL);
1324
0
                }
1325
0
            }
1326
0
            else if(strcmp(field_str, "certificateData") == 0) {
1327
                /* This field locks in Certificate token type */
1328
0
                if(!typeExplicitlySet && detectedType == -1) {
1329
0
                    detectedType = UA_USERTOKENTYPE_CERTIFICATE;
1330
0
                }
1331
0
                if(detectedType != UA_USERTOKENTYPE_CERTIFICATE) {
1332
0
                    UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
1333
0
                                "Field 'certificateData' can only be used with Certificate token type");
1334
0
                    retval = UA_STATUSCODE_BAD;
1335
0
                }
1336
0
                else {
1337
0
                    retval = CertificateFileField_parseJson(ctx, &certToken.certificateData, NULL);
1338
0
                }
1339
0
            }
1340
0
            else {
1341
0
                LOG_UNKNOWN_FIELD(ctx, field_str);
1342
0
            }
1343
0
            UA_free(field_str);
1344
0
            break;
1345
0
        }
1346
0
        default:
1347
0
            break;
1348
0
        }
1349
0
    }
1350
1351
0
    if(retval == UA_STATUSCODE_GOOD) {
1352
        /* If no type was detected, default to Anonymous */
1353
0
        if(detectedType == -1) {
1354
0
            detectedType = UA_USERTOKENTYPE_ANONYMOUS;
1355
0
        }
1356
1357
        /* Create the ExtensionObject with the appropriate token type */
1358
0
        if(detectedType == UA_USERTOKENTYPE_ANONYMOUS) {
1359
0
            retval = UA_ExtensionObject_setValueCopy(field, &anonToken,
1360
0
                                                    &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN]);
1361
0
        } else if(detectedType == UA_USERTOKENTYPE_USERNAME) {
1362
0
            retval = UA_ExtensionObject_setValueCopy(field, &userNameToken,
1363
0
                                                    &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]);
1364
0
        } else if(detectedType == UA_USERTOKENTYPE_CERTIFICATE) {
1365
0
            retval = UA_ExtensionObject_setValueCopy(field, &certToken,
1366
0
                                                    &UA_TYPES[UA_TYPES_X509IDENTITYTOKEN]);
1367
0
        } else {
1368
0
            UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
1369
0
                        "Invalid userIdentityToken type");
1370
0
            retval = UA_STATUSCODE_BAD;
1371
0
        }
1372
0
    }
1373
1374
0
    UA_AnonymousIdentityToken_clear(&anonToken);
1375
0
    UA_UserNameIdentityToken_clear(&userNameToken);
1376
0
    UA_X509IdentityToken_clear(&certToken);
1377
0
    return retval;
1378
0
}
1379
1380
0
PARSE_JSON(UserTokenPolicy) {
1381
0
    UA_Boolean issuedTokenTypeFieldsUsed = false;
1382
1383
0
    UA_UserTokenPolicy *field = (UA_UserTokenPolicy*)configField;
1384
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
1385
0
    cj5_token tok = ctx->result.tokens[++ctx->index];
1386
0
    for(size_t j = tok.size/2; j > 0; j--) {
1387
0
        tok = ctx->result.tokens[++ctx->index];
1388
0
        switch (tok.type) {
1389
0
        case CJ5_TOKEN_STRING: {
1390
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
1391
0
            unsigned int str_len = 0;
1392
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
1393
0
            if(strcmp(field_str, "policyId") == 0)
1394
0
                retval = StringField_parseJson(ctx, &field->policyId, NULL);
1395
0
            else if(strcmp(field_str, "tokenType") == 0)
1396
0
                retval = UserTokenType_parseJson(ctx, &field->tokenType, NULL);
1397
0
            else if(strcmp(field_str, "issuedTokenType") == 0) {
1398
0
                issuedTokenTypeFieldsUsed = true;
1399
0
                retval = StringField_parseJson(ctx, &field->issuedTokenType, NULL);
1400
0
            }
1401
0
            else if(strcmp(field_str, "issuerEndpointUrl") == 0) {
1402
0
                issuedTokenTypeFieldsUsed = true;
1403
0
                retval = StringField_parseJson(ctx, &field->issuerEndpointUrl, NULL);
1404
0
            }
1405
0
            else if(strcmp(field_str, "securityPolicyUri") == 0)
1406
0
                retval = StringField_parseJson(ctx, &field->securityPolicyUri, NULL);
1407
0
            else {
1408
0
                LOG_UNKNOWN_FIELD(ctx, field_str);
1409
0
            }
1410
0
            UA_free(field_str);
1411
0
            if(retval != UA_STATUSCODE_GOOD) {
1412
0
                return retval;
1413
0
            }
1414
0
            break;
1415
0
        }
1416
0
        default:
1417
0
            break;
1418
0
        }
1419
0
    }
1420
0
    if(issuedTokenTypeFieldsUsed && field->tokenType != UA_USERTOKENTYPE_ISSUEDTOKEN) {
1421
0
        UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
1422
0
                    "Fields 'issuedTokenType' and 'issuerEndpointUrl' can only be used if tokenType is 'IssuedToken'.");
1423
0
        return UA_STATUSCODE_BAD;
1424
0
    }
1425
1426
0
    return UA_STATUSCODE_GOOD;
1427
0
}
1428
1429
0
PARSE_JSON(UserTokenPolicyArrayField) {
1430
0
    if(configFieldSize == NULL) {
1431
0
        UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Pointer to the array size is not set.");
1432
0
        return UA_STATUSCODE_BADARGUMENTSMISSING;
1433
0
    }
1434
0
    cj5_token tok = ctx->result.tokens[++ctx->index];
1435
0
    UA_UserTokenPolicy *policyArray = (UA_UserTokenPolicy*)UA_malloc(sizeof(UA_UserTokenPolicy) * tok.size);
1436
0
    size_t policyArraySize = 0;
1437
0
    for(size_t j = tok.size; j > 0; j--) {
1438
        /* initialize element to zeros so clear functions are safe */
1439
0
        memset(&policyArray[policyArraySize], 0, sizeof(UA_UserTokenPolicy));
1440
0
        UA_StatusCode retval = UserTokenPolicy_parseJson(ctx, &policyArray[policyArraySize], NULL);
1441
0
        if(retval != UA_STATUSCODE_GOOD) {
1442
0
            UA_Array_delete(policyArray, policyArraySize, &UA_TYPES[UA_TYPES_USERTOKENPOLICY]);
1443
0
            return retval;
1444
0
        }
1445
0
        policyArraySize++;
1446
0
    }
1447
    /* Add to the config */
1448
0
    UA_UserTokenPolicy **field = (UA_UserTokenPolicy**)configField;
1449
0
    if(*configFieldSize > 0) {
1450
0
        UA_Array_delete(*field, *configFieldSize,
1451
0
                        &UA_TYPES[UA_TYPES_USERTOKENPOLICY]);
1452
0
        *field = NULL;
1453
0
        *configFieldSize = 0;
1454
0
    }
1455
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
1456
0
    if(policyArraySize > 0) {
1457
0
        retval = UA_Array_copy(policyArray, policyArraySize,
1458
0
                               (void **)field, &UA_TYPES[UA_TYPES_USERTOKENPOLICY]);
1459
0
        *configFieldSize = policyArraySize;
1460
0
    }
1461
1462
    /* Clean up */
1463
0
    UA_Array_delete(policyArray, policyArraySize, &UA_TYPES[UA_TYPES_USERTOKENPOLICY]);
1464
0
    return retval;
1465
0
}
1466
1467
1468
0
PARSE_JSON(EndpointDescription) {
1469
0
    UA_EndpointDescription *field = (UA_EndpointDescription*)configField;
1470
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
1471
0
    cj5_token tok = ctx->result.tokens[++ctx->index];
1472
0
    for(size_t j = tok.size/2; j > 0; j--) {
1473
0
        tok = ctx->result.tokens[++ctx->index];
1474
0
        switch (tok.type) {
1475
0
        case CJ5_TOKEN_STRING: {
1476
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
1477
0
            unsigned int str_len = 0;
1478
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
1479
0
            if(strcmp(field_str, "endpointUrl") == 0)
1480
0
                retval = StringField_parseJson(ctx, &field->endpointUrl, NULL);
1481
0
            else if(strcmp(field_str, "server") == 0)
1482
0
                retval = GenericApplicationDescriptionField_parseJson(ctx, &field->server, NULL, GENERICAPPLICATIONTYPE_ANY);
1483
0
            else if(strcmp(field_str, "serverCertificate") == 0)
1484
0
                retval = CertificateFileField_parseJson(ctx, &field->serverCertificate, NULL);
1485
0
            else if(strcmp(field_str, "securityMode") == 0)
1486
0
                retval = MessageSecurityMode_parseJson(ctx, &field->securityMode, NULL);
1487
0
            else if(strcmp(field_str, "securityPolicyUri") == 0)
1488
0
                retval = StringField_parseJson(ctx, &field->securityPolicyUri, NULL);
1489
0
            else if(strcmp(field_str, "userIdentityTokens") == 0)
1490
0
                retval = UserTokenPolicyArrayField_parseJson(ctx, &field->userIdentityTokens, &field->userIdentityTokensSize);
1491
0
            else if(strcmp(field_str, "transportProfileUri") == 0)
1492
0
                retval = StringField_parseJson(ctx, &field->transportProfileUri, NULL);
1493
0
            else if(strcmp(field_str, "securityLevel") == 0)
1494
0
                retval = ByteField_parseJson(ctx, &field->securityLevel, NULL);
1495
0
            else {
1496
0
                LOG_UNKNOWN_FIELD(ctx, field_str);
1497
0
            }
1498
0
            UA_free(field_str);
1499
0
            if(retval != UA_STATUSCODE_GOOD) {
1500
0
                return retval;
1501
0
            }
1502
0
            break;
1503
0
        }
1504
0
        default:
1505
0
            break;
1506
0
        }
1507
0
    }
1508
0
    return UA_STATUSCODE_GOOD;
1509
0
}
1510
1511
static UA_StatusCode
1512
0
parseJSONClientConfig(UA_ClientConfig *config, UA_ByteString json_config) {
1513
    // Parsing json config
1514
0
    const char *json = (const char*)json_config.data;
1515
0
    cj5_token tokens[MAX_TOKENS];
1516
0
    cj5_result r = cj5_parse(json, (unsigned int)json_config.length, tokens, MAX_TOKENS, NULL);
1517
1518
0
    ParsingCtx ctx;
1519
0
    ctx.json = json;
1520
0
    ctx.result = r;
1521
0
    ctx.index = 1; // The first token is ignored because it is known and not needed.
1522
1523
0
    ctx.logging = config->logging;
1524
1525
0
    size_t clientConfigSize = 0;
1526
0
    if(ctx.result.tokens)
1527
0
        clientConfigSize = (ctx.result.tokens[ctx.index-1].size/2);
1528
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
1529
0
    for (size_t j = clientConfigSize; j > 0; j--) {
1530
0
        cj5_token tok = ctx.result.tokens[ctx.index];
1531
0
        switch (tok.type) {
1532
0
            case CJ5_TOKEN_STRING: {
1533
0
                char *field = (char*)UA_malloc(tok.size + 1);
1534
0
                unsigned int str_len = 0;
1535
0
                cj5_get_str(&ctx.result, (unsigned int)ctx.index, field, &str_len);
1536
0
                if(strcmp(field, "timeout") == 0)
1537
0
                    retval = Int32Field_parseJson(&ctx, &config->timeout, NULL);
1538
0
                else if(strcmp(field, "applicationDescription") == 0)
1539
0
                    retval = GenericApplicationDescriptionField_parseJson(&ctx, &config->clientDescription, NULL, GENERICAPPLICATIONTYPE_CLIENT);
1540
0
                else if(strcmp(field, "endpointUrl") == 0)
1541
0
                    retval = StringField_parseJson(&ctx, &config->endpointUrl, NULL);
1542
0
                else if (strcmp(field, "userIdentityToken") == 0)
1543
0
                    retval = UserIdentityToken_parseJson(&ctx, &config->userIdentityToken, NULL);
1544
0
                else if(strcmp(field, "sessionName") == 0)
1545
0
                    retval = StringField_parseJson(&ctx, &config->sessionName, NULL);
1546
0
                else if(strcmp(field, "sessionLocaleIds") == 0)
1547
                    /* UA_LocaleId is an alias of UA_String */
1548
0
                    retval = StringArrayField_parseJson(&ctx, &config->sessionLocaleIds, &config->sessionLocaleIdsSize);
1549
0
                else if(strcmp(field, "noSession") == 0)
1550
0
                    retval = BooleanField_parseJson(&ctx, &config->noSession, NULL);
1551
0
                else if(strcmp(field, "noReconnect") == 0)
1552
0
                    retval = BooleanField_parseJson(&ctx, &config->noReconnect, NULL);
1553
0
                else if(strcmp(field, "noNewSession") == 0)
1554
0
                    retval = BooleanField_parseJson(&ctx, &config->noNewSession, NULL);
1555
0
                else if(strcmp(field, "secureChannelLifeTime") == 0)
1556
0
                    retval = UInt32Field_parseJson(&ctx, &config->secureChannelLifeTime, NULL);
1557
0
                else if(strcmp(field, "requestedSessionTimeout") == 0)
1558
0
                    retval = UInt32Field_parseJson(&ctx, &config->requestedSessionTimeout, NULL);
1559
0
                else if(strcmp(field, "localConnectionConfig") == 0)
1560
0
                    retval = ConnectionConfig_parseJson(&ctx, &config->localConnectionConfig, NULL);
1561
0
                else if(strcmp(field, "connectivityCheckInterval") == 0)
1562
0
                    retval = UInt32Field_parseJson(&ctx, &config->connectivityCheckInterval, NULL);
1563
0
                else if(strcmp(field, "tcpReuseAddr") == 0)
1564
0
                    retval = BooleanField_parseJson(&ctx, &config->tcpReuseAddr, NULL);
1565
0
                else if(strcmp(field, "endpoint") == 0)
1566
0
                    retval = EndpointDescription_parseJson(&ctx, &config->endpoint, NULL);
1567
0
                else if(strcmp(field, "userTokenPolicy") == 0)
1568
0
                    retval = UserTokenPolicy_parseJson(&ctx, &config->userTokenPolicy, NULL);
1569
0
                else if(strcmp(field, "applicationUri") == 0)
1570
0
                    retval = StringField_parseJson(&ctx, &config->applicationUri, NULL);
1571
0
                else if(strcmp(field, "securityMode") == 0)
1572
0
                    retval = MessageSecurityMode_parseJson(&ctx, &config->securityMode, NULL);
1573
0
                else if(strcmp(field, "securityPolicyUri") == 0)
1574
0
                    retval = StringField_parseJson(&ctx, &config->securityPolicyUri, NULL);
1575
0
                else if(strcmp(field, "authSecurityPolicyUri") == 0)
1576
0
                    retval = StringField_parseJson(&ctx, &config->authSecurityPolicyUri, NULL);
1577
0
                else if(strcmp(field, "securityPolicies") == 0)
1578
0
                    retval = SecurityPoliciesField_parseJson(&ctx, &config->securityPolicies, &config->securityPoliciesSize, UA_APPLICATIONTYPE_CLIENT, config->logging);
1579
0
                else if(strcmp(field, "authSecurityPolicies") == 0)
1580
0
                    retval = SecurityPoliciesField_parseJson(&ctx, &config->authSecurityPolicies, &config->authSecurityPoliciesSize, UA_APPLICATIONTYPE_CLIENT, config->logging);
1581
0
                else if(strcmp(field, "allowNonePolicyPassword") == 0)
1582
0
                    retval = BooleanField_parseJson(&ctx, &config->allowNonePolicyPassword, NULL);
1583
0
#ifdef UA_ENABLE_ENCRYPTION
1584
0
                else if(strcmp(field, "maxTrustListSize") == 0)
1585
0
                    retval = UInt32Field_parseJson(&ctx, &config->maxTrustListSize, NULL);
1586
0
                else if(strcmp(field, "maxRejectedListSize") == 0)
1587
0
                    retval = UInt32Field_parseJson(&ctx, &config->maxRejectedListSize, NULL);
1588
0
#endif
1589
0
                else if(strcmp(field, "namespaces") == 0)
1590
0
                    retval = StringArrayField_parseJson(&ctx, &config->namespaces, &config->namespacesSize);
1591
0
                else if(strcmp(field, "outStandingPublishRequests") == 0)
1592
0
                    retval = UInt16Field_parseJson(&ctx, &config->outStandingPublishRequests, NULL);
1593
0
                else {
1594
0
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
1595
0
                                   "Field name '%s' unknown or misspelled. Maybe the feature is not enabled either.", field);
1596
                    /* skip the name of item */
1597
0
                    ++ctx.index;
1598
                    /* skip value of unknown item */
1599
0
                    skipUnknownItem(&ctx);
1600
                    /* after skipUnknownItem() ctx->index points to the name of the following item.
1601
                       We must decrement index in oder following increment will
1602
                       still set index to the right position (name of the following item) */
1603
0
                    --ctx.index;
1604
0
                }
1605
0
                UA_free(field);
1606
0
                if(retval != UA_STATUSCODE_GOOD) {
1607
0
                    UA_LOG_ERROR(ctx.logging, UA_LOGCATEGORY_APPLICATION, "An error occurred while parsing the configuration file.");
1608
0
                    return retval;
1609
0
                }
1610
0
                break;
1611
0
            }
1612
0
            default:
1613
0
                break;
1614
0
        }
1615
0
        ctx.index += 1;
1616
0
    }
1617
0
    return retval;
1618
0
}
1619
1620
UA_Client *
1621
UA_Client_newFromFile(const UA_ByteString jsonConfig)
1622
0
{
1623
0
    UA_ClientConfig config;
1624
0
    UA_StatusCode res = UA_ClientConfig_loadFromFile(&config, jsonConfig);
1625
0
    if(res != UA_STATUSCODE_GOOD)
1626
0
        return NULL;
1627
0
    return UA_Client_newWithConfig(&config);
1628
0
}
1629
1630
UA_StatusCode
1631
UA_ClientConfig_loadFromFile(UA_ClientConfig *config, const UA_ByteString jsonConfig)
1632
0
{
1633
0
    memset(config, 0, sizeof(UA_ClientConfig));
1634
0
    UA_StatusCode res = UA_ClientConfig_setDefault(config);
1635
0
    if (res == UA_STATUSCODE_GOOD) {
1636
0
        res = parseJSONClientConfig(config, jsonConfig);
1637
0
        if (UA_StatusCode_isBad(res)) {
1638
0
            UA_ClientConfig_clear(config);
1639
0
        }
1640
0
    }
1641
0
    return res;
1642
0
}
1643
1644
#ifdef UA_ENABLE_ENCRYPTION
1645
static UA_ByteString
1646
0
loadCertificateFile(const char *const path) {
1647
0
    UA_ByteString fileContents = UA_BYTESTRING_NULL;
1648
1649
    /* Open the file */
1650
0
    FILE *fp = fopen(path, "rb");
1651
0
    if(!fp) {
1652
0
        errno = 0; /* We read errno also from the tcp layer... */
1653
0
        return fileContents;
1654
0
    }
1655
1656
    /* Get the file length, allocate the data and read */
1657
0
    if(fseek(fp, 0, SEEK_END) != 0) {
1658
0
        fclose(fp);
1659
0
        errno = 0;
1660
0
        return fileContents;
1661
0
    }
1662
1663
0
    long length = ftell(fp);
1664
0
    if(length < 0) {
1665
0
        fclose(fp);
1666
0
        errno = 0;
1667
0
        return fileContents;
1668
0
    }
1669
1670
0
    fileContents.length = (size_t)length;
1671
0
    fileContents.data = (UA_Byte *)UA_malloc(fileContents.length * sizeof(UA_Byte));
1672
0
    if(fileContents.data) {
1673
0
        if(fseek(fp, 0, SEEK_SET) != 0) {
1674
0
            fclose(fp);
1675
0
            UA_ByteString_clear(&fileContents);
1676
0
            errno = 0;
1677
0
            return fileContents;
1678
0
        }
1679
0
        size_t read = fread(fileContents.data, sizeof(UA_Byte), fileContents.length, fp);
1680
0
        if(read != fileContents.length)
1681
0
            UA_ByteString_clear(&fileContents);
1682
0
    } else {
1683
0
        fileContents.length = 0;
1684
0
    }
1685
0
    fclose(fp);
1686
1687
0
    return fileContents;
1688
0
}
1689
#endif