mirror of
https://github.com/open62541/open62541.git
synced 2025-06-03 04:00:21 +00:00
1085 lines
48 KiB
C
1085 lines
48 KiB
C
/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
|
|
* See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
|
|
*
|
|
* Copyright 2023 (c) Fraunhofer IOSB (Author: Noel Graf)
|
|
*/
|
|
|
|
#include <open62541/server_config_file_based.h>
|
|
#include <open62541/plugin/log_stdout.h>
|
|
#include "cj5.h"
|
|
#include "open62541/server_config_default.h"
|
|
#ifdef UA_ENABLE_ENCRYPTION
|
|
#include "open62541/plugin/certificategroup_default.h"
|
|
#endif
|
|
|
|
#define MAX_TOKENS 1024
|
|
|
|
typedef struct {
|
|
const char *json;
|
|
const cj5_token *tokens;
|
|
size_t tokensSize;
|
|
size_t index;
|
|
UA_Byte depth;
|
|
cj5_result result;
|
|
} ParsingCtx;
|
|
|
|
static UA_ByteString
|
|
getJsonPart(cj5_token tok, const char *json) {
|
|
UA_ByteString bs;
|
|
UA_ByteString_init(&bs);
|
|
if(tok.type == CJ5_TOKEN_STRING) {
|
|
bs.data = (UA_Byte*)(uintptr_t)(json + tok.start - 1);
|
|
bs.length = (tok.end - tok.start) + 3;
|
|
return bs;
|
|
} else {
|
|
bs.data = (UA_Byte*)(uintptr_t)(json + tok.start);
|
|
bs.length = (tok.end - tok.start) + 1;
|
|
return bs;
|
|
}
|
|
}
|
|
|
|
/* Forward declarations*/
|
|
#define PARSE_JSON(TYPE) static UA_StatusCode \
|
|
TYPE##_parseJson(ParsingCtx *ctx, void *configField, size_t *configFieldSize)
|
|
|
|
typedef UA_StatusCode
|
|
(*parseJsonSignature)(ParsingCtx *ctx, void *configField, size_t *configFieldSize);
|
|
|
|
#ifdef UA_ENABLE_ENCRYPTION
|
|
static UA_ByteString
|
|
loadCertificateFile(const char *const path);
|
|
#endif
|
|
|
|
/* The DataType "kind" is an internal type classification. It is used to
|
|
* dispatch handling to the correct routines. */
|
|
#define UA_SERVERCONFIGFIELDKINDS 25
|
|
typedef enum {
|
|
/* Basic Types */
|
|
UA_SERVERCONFIGFIELD_INT64 = 0,
|
|
UA_SERVERCONFIGFIELD_UINT16,
|
|
UA_SERVERCONFIGFIELD_UINT32,
|
|
UA_SERVERCONFIGFIELD_UINT64,
|
|
UA_SERVERCONFIGFIELD_STRING,
|
|
UA_SERVERCONFIGFIELD_LOCALIZEDTEXT,
|
|
UA_SERVERCONFIGFIELD_DOUBLE,
|
|
UA_SERVERCONFIGFIELD_BOOLEAN,
|
|
UA_SERVERCONFIGFIELD_DURATION,
|
|
UA_SERVERCONFIGFIELD_DURATIONRANGE,
|
|
UA_SERVERCONFIGFIELD_UINT32RANGE,
|
|
|
|
/* Advanced Types */
|
|
UA_SERVERCONFIGFIELD_BUILDINFO,
|
|
UA_SERVERCONFIGFIELD_APPLICATIONDESCRIPTION,
|
|
UA_SERVERCONFIGFIELD_STRINGARRAY,
|
|
UA_SERVERCONFIGFIELD_UINT32ARRAY,
|
|
UA_SERVERCONFIGFIELD_DATETIME,
|
|
UA_SERVERCONFIGFIELD_SUBSCRIPTIONCONFIGURATION,
|
|
UA_SERVERCONFIGFIELD_TCPCONFIGURATION,
|
|
UA_SERVERCONFIGFIELD_PUBSUBCONFIGURATION,
|
|
UA_SERVERCONFIGFIELD_HISTORIZINGCONFIGURATION,
|
|
UA_SERVERCONFIGFIELD_MDNSCONFIGURATION,
|
|
UA_SERVERCONFIGFIELD_SECURITYPOLICIES,
|
|
UA_SERVERCONFIGFIELD_SECURITYPKI,
|
|
|
|
/* Enumerations */
|
|
UA_SERVERCONFIGFIELD_APPLICATIONTYPE,
|
|
UA_SERVERCONFIGFIELD_RULEHANDLING
|
|
} UA_ServerConfigFieldKind;
|
|
|
|
extern const parseJsonSignature parseJsonJumpTable[UA_SERVERCONFIGFIELDKINDS];
|
|
|
|
/*----------------------Basic Types------------------------*/
|
|
PARSE_JSON(Int64Field) {
|
|
cj5_token tok = ctx->tokens[++ctx->index];
|
|
UA_ByteString buf = getJsonPart(tok, ctx->json);
|
|
UA_Int64 out;
|
|
UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_INT64], NULL);
|
|
if(retval != UA_STATUSCODE_GOOD)
|
|
return retval;
|
|
UA_Int64 *field = (UA_Int64*)configField;
|
|
*field = out;
|
|
return retval;
|
|
}
|
|
PARSE_JSON(UInt16Field) {
|
|
cj5_token tok = ctx->tokens[++ctx->index];
|
|
UA_ByteString buf = getJsonPart(tok, ctx->json);
|
|
UA_UInt16 out;
|
|
UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT16], NULL);
|
|
if(retval != UA_STATUSCODE_GOOD)
|
|
return retval;
|
|
UA_UInt16 *field = (UA_UInt16*)configField;
|
|
*field = out;
|
|
return retval;
|
|
}
|
|
PARSE_JSON(UInt32Field) {
|
|
cj5_token tok = ctx->tokens[++ctx->index];
|
|
UA_ByteString buf = getJsonPart(tok, ctx->json);
|
|
UA_UInt32 out;
|
|
UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT32], NULL);
|
|
if(retval != UA_STATUSCODE_GOOD)
|
|
return retval;
|
|
UA_UInt32 *field = (UA_UInt32*)configField;
|
|
*field = out;
|
|
return retval;
|
|
}
|
|
PARSE_JSON(UInt64Field) {
|
|
cj5_token tok = ctx->tokens[++ctx->index];
|
|
UA_ByteString buf = getJsonPart(tok, ctx->json);
|
|
UA_UInt64 out;
|
|
UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT64], NULL);
|
|
if(retval != UA_STATUSCODE_GOOD)
|
|
return retval;
|
|
UA_UInt64 *field = (UA_UInt64*)configField;
|
|
*field = out;
|
|
return retval;
|
|
}
|
|
PARSE_JSON(StringField) {
|
|
cj5_token tok = ctx->tokens[++ctx->index];
|
|
UA_ByteString buf = getJsonPart(tok, ctx->json);
|
|
UA_String out;
|
|
UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_STRING], NULL);
|
|
if(retval != UA_STATUSCODE_GOOD)
|
|
return retval;
|
|
UA_String *field = (UA_String*)configField;
|
|
if(field != NULL) {
|
|
UA_String_clear(field);
|
|
*field = out;
|
|
}
|
|
return retval;
|
|
}
|
|
PARSE_JSON(LocalizedTextField) {
|
|
/*
|
|
applicationName: {
|
|
locale: "de-DE",
|
|
text: "Test text"
|
|
}
|
|
*/
|
|
cj5_token tok = ctx->tokens[++ctx->index];
|
|
UA_StatusCode retval = UA_STATUSCODE_GOOD;
|
|
UA_String locale;
|
|
UA_String text;
|
|
for(size_t j = tok.size/2; j > 0; j--) {
|
|
tok = ctx->tokens[++ctx->index];
|
|
switch (tok.type) {
|
|
case CJ5_TOKEN_STRING: {
|
|
char *field = (char*)UA_malloc(tok.size + 1);
|
|
unsigned int str_len = 0;
|
|
cj5_get_str(&ctx->result, (unsigned int)ctx->index, field, &str_len);
|
|
|
|
tok = ctx->tokens[++ctx->index];
|
|
UA_ByteString buf = getJsonPart(tok, ctx->json);
|
|
if(strcmp(field, "locale") == 0)
|
|
retval |= UA_decodeJson(&buf, &locale, &UA_TYPES[UA_TYPES_STRING], NULL);
|
|
else if(strcmp(field, "text") == 0)
|
|
retval |= UA_decodeJson(&buf, &text, &UA_TYPES[UA_TYPES_STRING], NULL);
|
|
else {
|
|
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Unknown field name.");
|
|
}
|
|
UA_free(field);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
UA_LocalizedText out;
|
|
out.locale = locale;
|
|
out.text = text;
|
|
if(retval != UA_STATUSCODE_GOOD)
|
|
return retval;
|
|
UA_LocalizedText *field = (UA_LocalizedText*)configField;
|
|
if(field != NULL) {
|
|
UA_LocalizedText_clear(field);
|
|
*field = out;
|
|
}
|
|
return retval;
|
|
}
|
|
PARSE_JSON(DoubleField) {
|
|
cj5_token tok = ctx->tokens[++ctx->index];
|
|
UA_ByteString buf = getJsonPart(tok, ctx->json);
|
|
UA_Double out;
|
|
UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_DOUBLE], NULL);
|
|
if(retval != UA_STATUSCODE_GOOD)
|
|
return retval;
|
|
UA_Double *field = (UA_Double *)configField;
|
|
*field = out;
|
|
return retval;
|
|
}
|
|
PARSE_JSON(BooleanField) {
|
|
cj5_token tok = ctx->tokens[++ctx->index];
|
|
UA_ByteString buf = getJsonPart(tok, ctx->json);
|
|
UA_Boolean out;
|
|
if(tok.type != CJ5_TOKEN_BOOL) {
|
|
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Value of type bool expected.");
|
|
return UA_STATUSCODE_BADTYPEMISMATCH;
|
|
}
|
|
UA_String val = UA_STRING("true");
|
|
if(UA_String_equal(&val, &buf)) {
|
|
out = true;
|
|
}else {
|
|
out = false;
|
|
}
|
|
/* set server config field */
|
|
UA_Boolean *field = (UA_Boolean *)configField;
|
|
*field = out;
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
PARSE_JSON(DurationField) {
|
|
UA_Double double_value;
|
|
UA_StatusCode retval = DoubleField_parseJson(ctx, &double_value, NULL);
|
|
if(retval != UA_STATUSCODE_GOOD)
|
|
return retval;
|
|
UA_Duration *field = (UA_Duration*)configField;
|
|
*field = (UA_Duration)double_value;
|
|
return retval;
|
|
}
|
|
PARSE_JSON(DurationRangeField) {
|
|
UA_DurationRange *field = (UA_DurationRange*)configField;
|
|
cj5_token tok = ctx->tokens[++ctx->index];
|
|
for(size_t j = tok.size/2; j > 0; j--) {
|
|
tok = ctx->tokens[++ctx->index];
|
|
switch (tok.type) {
|
|
case CJ5_TOKEN_STRING: {
|
|
char *field_str = (char*)UA_malloc(tok.size + 1);
|
|
unsigned int str_len = 0;
|
|
cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
|
|
if(strcmp(field_str, "min") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_DURATION](ctx, &field->min, NULL);
|
|
else if(strcmp(field_str, "max") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_DURATION](ctx, &field->max, NULL);
|
|
else {
|
|
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Unknown field name.");
|
|
}
|
|
UA_free(field_str);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
PARSE_JSON(UInt32RangeField) {
|
|
UA_UInt32Range *field = (UA_UInt32Range*)configField;
|
|
cj5_token tok = ctx->tokens[++ctx->index];
|
|
for(size_t j = tok.size/2; j > 0; j--) {
|
|
tok = ctx->tokens[++ctx->index];
|
|
switch (tok.type) {
|
|
case CJ5_TOKEN_STRING: {
|
|
char *field_str = (char*)UA_malloc(tok.size + 1);
|
|
unsigned int str_len = 0;
|
|
cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
|
|
if(strcmp(field_str, "min") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &field->min, NULL);
|
|
else if(strcmp(field_str, "max") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &field->max, NULL);
|
|
else {
|
|
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Unknown field name.");
|
|
}
|
|
UA_free(field_str);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
|
|
/*----------------------Advanced Types------------------------*/
|
|
PARSE_JSON(BuildInfo) {
|
|
UA_BuildInfo *field = (UA_BuildInfo*)configField;
|
|
cj5_token tok = ctx->tokens[++ctx->index];
|
|
for(size_t j = tok.size/2; j > 0; j--) {
|
|
tok = ctx->tokens[++ctx->index];
|
|
switch (tok.type) {
|
|
case CJ5_TOKEN_STRING: {
|
|
char *field_str = (char*)UA_malloc(tok.size + 1);
|
|
unsigned int str_len = 0;
|
|
cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
|
|
if(strcmp(field_str, "productUri") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->productUri, NULL);
|
|
else if(strcmp(field_str, "manufacturerName") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->manufacturerName, NULL);
|
|
else if(strcmp(field_str, "productName") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->productName, NULL);
|
|
else if(strcmp(field_str, "softwareVersion") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->softwareVersion, NULL);
|
|
else if(strcmp(field_str, "buildNumber") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->buildNumber, NULL);
|
|
else if(strcmp(field_str, "buildDate") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_DATETIME](ctx, &field->buildDate, NULL);
|
|
else {
|
|
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Unknown field name.");
|
|
}
|
|
UA_free(field_str);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
PARSE_JSON(ApplicationDescriptionField) {
|
|
UA_ApplicationDescription *field = (UA_ApplicationDescription*)configField;
|
|
cj5_token tok = ctx->tokens[++ctx->index];
|
|
for(size_t j = tok.size/2; j > 0; j--) {
|
|
tok = ctx->tokens[++ctx->index];
|
|
switch (tok.type) {
|
|
case CJ5_TOKEN_STRING: {
|
|
char *field_str = (char*)UA_malloc(tok.size + 1);
|
|
unsigned int str_len = 0;
|
|
cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
|
|
if(strcmp(field_str, "applicationUri") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->applicationUri, NULL);
|
|
else if(strcmp(field_str, "productUri") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->productUri, NULL);
|
|
else if(strcmp(field_str, "applicationName") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_LOCALIZEDTEXT](ctx, &field->applicationName, NULL);
|
|
else if(strcmp(field_str, "applicationType") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_APPLICATIONTYPE](ctx, &field->applicationType, NULL);
|
|
else if(strcmp(field_str, "gatewayServerUri") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->gatewayServerUri, NULL);
|
|
else if(strcmp(field_str, "discoveryProfileUri") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->discoveryProfileUri, NULL);
|
|
else if(strcmp(field_str, "discoveryUrls") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRINGARRAY](ctx, &field->discoveryUrls, &field->discoveryUrlsSize);
|
|
else {
|
|
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Unknown field name.");
|
|
}
|
|
UA_free(field_str);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
PARSE_JSON(StringArrayField) {
|
|
if(configFieldSize == NULL) {
|
|
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Pointer to the array size is not set.");
|
|
return UA_STATUSCODE_BADARGUMENTSMISSING;
|
|
}
|
|
cj5_token tok = ctx->tokens[++ctx->index];
|
|
UA_String *stringArray = (UA_String*)UA_malloc(sizeof(UA_String) * tok.size);
|
|
size_t stringArraySize = 0;
|
|
for(size_t j = tok.size; j > 0; j--) {
|
|
UA_String out = {.length = 0, .data = NULL};;
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &out, NULL);
|
|
UA_String_copy(&out, &stringArray[stringArraySize++]);
|
|
UA_String_clear(&out);
|
|
}
|
|
/* Add to the config */
|
|
UA_String **field = (UA_String**)configField;
|
|
if(*configFieldSize > 0) {
|
|
UA_Array_delete(*field, *configFieldSize,
|
|
&UA_TYPES[UA_TYPES_STRING]);
|
|
*field = NULL;
|
|
*configFieldSize = 0;
|
|
}
|
|
UA_StatusCode retval =
|
|
UA_Array_copy(stringArray, stringArraySize,
|
|
(void**)field, &UA_TYPES[UA_TYPES_STRING]);
|
|
*configFieldSize = stringArraySize;
|
|
|
|
/* Clean up */
|
|
UA_Array_delete(stringArray, stringArraySize, &UA_TYPES[UA_TYPES_STRING]);
|
|
return retval;
|
|
}
|
|
PARSE_JSON(UInt32ArrayField) {
|
|
if(configFieldSize == NULL) {
|
|
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Pointer to the array size is not set.");
|
|
return UA_STATUSCODE_BADARGUMENTSMISSING;
|
|
}
|
|
cj5_token tok = ctx->tokens[++ctx->index];
|
|
UA_UInt32 *numberArray = (UA_UInt32*)UA_malloc(sizeof(UA_UInt32) * tok.size);;
|
|
size_t numberArraySize = 0;
|
|
for(size_t j = tok.size; j > 0; j--) {
|
|
UA_UInt32 value;
|
|
UA_StatusCode retval = UInt32Field_parseJson(ctx, &value, NULL);
|
|
if(retval != UA_STATUSCODE_GOOD)
|
|
continue;
|
|
numberArray[numberArraySize++] = value;
|
|
}
|
|
/* Add to the config */
|
|
UA_UInt32 **field = (UA_UInt32**)configField;
|
|
if(*configFieldSize > 0) {
|
|
UA_Array_delete(*field, *configFieldSize,
|
|
&UA_TYPES[UA_TYPES_UINT32]);
|
|
*field = NULL;
|
|
*configFieldSize = 0;
|
|
}
|
|
UA_StatusCode retval = UA_STATUSCODE_GOOD;
|
|
if(numberArraySize > 0) {
|
|
retval = UA_Array_copy(numberArray, numberArraySize,
|
|
(void **)field, &UA_TYPES[UA_TYPES_UINT32]);
|
|
*configFieldSize = numberArraySize;
|
|
}
|
|
/* Clean up */
|
|
UA_Array_delete(numberArray, numberArraySize, &UA_TYPES[UA_TYPES_UINT32]);
|
|
return retval;
|
|
}
|
|
PARSE_JSON(DateTimeField) {
|
|
cj5_token tok = ctx->tokens[++ctx->index];
|
|
UA_ByteString buf = getJsonPart(tok, ctx->json);
|
|
UA_DateTime out;
|
|
UA_DateTime_init(&out);
|
|
UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_DATETIME], NULL);
|
|
if(retval != UA_STATUSCODE_GOOD)
|
|
return retval;
|
|
UA_DateTime *field = (UA_DateTime*)configField;
|
|
*field = out;
|
|
return retval;
|
|
}
|
|
|
|
PARSE_JSON(MdnsConfigurationField) {
|
|
#ifdef UA_ENABLE_DISCOVERY_MULTICAST
|
|
UA_ServerConfig *config = (UA_ServerConfig*)configField;
|
|
cj5_token tok = ctx->tokens[++ctx->index];
|
|
for(size_t j = tok.size/2; j > 0; j--) {
|
|
tok = ctx->tokens[++ctx->index];
|
|
switch (tok.type) {
|
|
case CJ5_TOKEN_STRING: {
|
|
char *field_str = (char*)UA_malloc(tok.size + 1);
|
|
unsigned int str_len = 0;
|
|
cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
|
|
if(strcmp(field_str, "mdnsServerName") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &config->mdnsConfig.mdnsServerName, NULL);
|
|
else if(strcmp(field_str, "serverCapabilities") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRINGARRAY](ctx, &config->mdnsConfig.serverCapabilities, &config->mdnsConfig.serverCapabilitiesSize);
|
|
#ifdef UA_ENABLE_DISCOVERY_MULTICAST_MDNSD
|
|
else if(strcmp(field_str, "mdnsInterfaceIP") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &config->mdnsInterfaceIP, NULL);
|
|
/* mdnsIpAddressList and mdnsIpAddressListSize are only available if UA_HAS_GETIFADDR is not defined: */
|
|
# if !defined(UA_HAS_GETIFADDR)
|
|
else if(strcmp(field_str, "mdnsIpAddressList") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32ARRAY](ctx, &config->mdnsIpAddressList, &config->mdnsIpAddressListSize);
|
|
# endif
|
|
#endif
|
|
else {
|
|
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Unknown field name.");
|
|
}
|
|
UA_free(field_str);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
|
|
PARSE_JSON(SubscriptionConfigurationField) {
|
|
#ifdef UA_ENABLE_SUBSCRIPTIONS
|
|
UA_ServerConfig *config = (UA_ServerConfig*)configField;
|
|
cj5_token tok = ctx->tokens[++ctx->index];
|
|
for(size_t j = tok.size/2; j > 0; j--) {
|
|
tok = ctx->tokens[++ctx->index];
|
|
switch (tok.type) {
|
|
case CJ5_TOKEN_STRING: {
|
|
char *field_str = (char*)UA_malloc(tok.size + 1);
|
|
unsigned int str_len = 0;
|
|
cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
|
|
if(strcmp(field_str, "maxSubscriptions") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxSubscriptions, NULL);
|
|
else if(strcmp(field_str, "maxSubscriptionsPerSession") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxSubscriptionsPerSession, NULL);
|
|
else if(strcmp(field_str, "publishingIntervalLimits") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_DURATIONRANGE](ctx, &config->publishingIntervalLimits, NULL);
|
|
else if(strcmp(field_str, "lifeTimeCountLimits") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32RANGE](ctx, &config->lifeTimeCountLimits, NULL);
|
|
else if(strcmp(field_str, "keepAliveCountLimits") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32RANGE](ctx, &config->keepAliveCountLimits, NULL);
|
|
else if(strcmp(field_str, "maxNotificationsPerPublish") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxNotificationsPerPublish, NULL);
|
|
else if(strcmp(field_str, "enableRetransmissionQueue") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->enableRetransmissionQueue, NULL);
|
|
else if(strcmp(field_str, "maxRetransmissionQueueSize") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxRetransmissionQueueSize, NULL);
|
|
# ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
|
|
else if(strcmp(field_str, "maxEventsPerNode") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxEventsPerNode, NULL);
|
|
# endif
|
|
else if(strcmp(field_str, "maxMonitoredItems") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxMonitoredItems, NULL);
|
|
else if(strcmp(field_str, "maxMonitoredItemsPerSubscription") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxMonitoredItemsPerSubscription, NULL);
|
|
else if(strcmp(field_str, "samplingIntervalLimits") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_DURATIONRANGE](ctx, &config->samplingIntervalLimits, NULL);
|
|
else if(strcmp(field_str, "queueSizeLimits") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32RANGE](ctx, &config->queueSizeLimits, NULL);
|
|
else if(strcmp(field_str, "maxPublishReqPerSession") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxPublishReqPerSession, NULL);
|
|
else {
|
|
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Unknown field name.");
|
|
}
|
|
UA_free(field_str);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
|
|
PARSE_JSON(TcpConfigurationField) {
|
|
UA_ServerConfig *config = (UA_ServerConfig*)configField;
|
|
cj5_token tok = ctx->tokens[++ctx->index];
|
|
for(size_t j = tok.size/2; j > 0; j--) {
|
|
tok = ctx->tokens[++ctx->index];
|
|
switch (tok.type) {
|
|
case CJ5_TOKEN_STRING: {
|
|
char *field_str = (char*)UA_malloc(tok.size + 1);
|
|
unsigned int str_len = 0;
|
|
cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
|
|
if(strcmp(field_str, "tcpBufSize") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->tcpBufSize, NULL);
|
|
else if(strcmp(field_str, "tcpMaxMsgSize") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->tcpMaxMsgSize, NULL);
|
|
else if(strcmp(field_str, "tcpMaxChunks") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->tcpMaxChunks, NULL);
|
|
else {
|
|
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Unknown field name.");
|
|
}
|
|
UA_free(field_str);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
|
|
PARSE_JSON(PubsubConfigurationField) {
|
|
#ifdef UA_ENABLE_PUBSUB
|
|
UA_PubSubConfiguration *field = (UA_PubSubConfiguration*)configField;
|
|
cj5_token tok = ctx->tokens[++ctx->index];
|
|
for(size_t j = tok.size/2; j > 0; j--) {
|
|
tok = ctx->tokens[++ctx->index];
|
|
switch (tok.type) {
|
|
case CJ5_TOKEN_STRING: {
|
|
char *field_str = (char*)UA_malloc(tok.size + 1);
|
|
unsigned int str_len = 0;
|
|
cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
|
|
if(strcmp(field_str, "enableDeltaFrames") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &field->enableDeltaFrames, NULL);
|
|
#ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
|
|
else if(strcmp(field_str, "enableInformationModelMethods") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &field->enableInformationModelMethods, NULL);
|
|
#endif
|
|
else {
|
|
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Unknown field name.");
|
|
}
|
|
UA_free(field_str);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
|
|
PARSE_JSON(HistorizingConfigurationField) {
|
|
#ifdef UA_ENABLE_HISTORIZING
|
|
UA_ServerConfig *config = (UA_ServerConfig*)configField;
|
|
cj5_token tok = ctx->tokens[++ctx->index];
|
|
for(size_t j = tok.size/2; j > 0; j--) {
|
|
tok = ctx->tokens[++ctx->index];
|
|
switch (tok.type) {
|
|
case CJ5_TOKEN_STRING: {
|
|
char *field_str = (char*)UA_malloc(tok.size + 1);
|
|
unsigned int str_len = 0;
|
|
cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
|
|
if(strcmp(field_str, "accessHistoryDataCapability") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->accessHistoryDataCapability, NULL);
|
|
else if(strcmp(field_str, "maxReturnDataValues") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxReturnDataValues, NULL);
|
|
else if(strcmp(field_str, "accessHistoryEventsCapability") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->accessHistoryEventsCapability, NULL);
|
|
else if(strcmp(field_str, "maxReturnEventValues") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxReturnEventValues, NULL);
|
|
else if(strcmp(field_str, "insertDataCapability") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->insertDataCapability, NULL);
|
|
else if(strcmp(field_str, "insertEventCapability") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->insertEventCapability, NULL);
|
|
else if(strcmp(field_str, "insertAnnotationsCapability") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->insertAnnotationsCapability, NULL);
|
|
else if(strcmp(field_str, "replaceDataCapability") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->replaceDataCapability, NULL);
|
|
else if(strcmp(field_str, "replaceEventCapability") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->replaceEventCapability, NULL);
|
|
else if(strcmp(field_str, "updateDataCapability") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->updateDataCapability, NULL);
|
|
else if(strcmp(field_str, "updateEventCapability") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->updateEventCapability, NULL);
|
|
else if(strcmp(field_str, "deleteRawCapability") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->deleteRawCapability, NULL);
|
|
else if(strcmp(field_str, "deleteEventCapability") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->deleteEventCapability, NULL);
|
|
else if(strcmp(field_str, "deleteAtTimeDataCapability") == 0)
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->deleteAtTimeDataCapability, NULL);
|
|
else {
|
|
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Unknown field name.");
|
|
}
|
|
UA_free(field_str);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
|
|
PARSE_JSON(SecurityPolciesField) {
|
|
#ifdef UA_ENABLE_ENCRYPTION
|
|
UA_ServerConfig *config = (UA_ServerConfig*)configField;
|
|
|
|
UA_String noneuri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None");
|
|
UA_String basic128Rsa15uri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15");
|
|
UA_String basic256uri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Basic256");
|
|
UA_String basic256Sha256uri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256");
|
|
UA_String aes128sha256rsaoaepuri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Aes128_Sha256_RsaOaep");
|
|
UA_String aes256sha256rsapssuri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Aes256_Sha256_RsaPss");
|
|
|
|
cj5_token tok = ctx->tokens[++ctx->index];
|
|
for(size_t j = tok.size; j > 0; j--) {
|
|
|
|
UA_String policy = {.length = 0, .data = NULL};
|
|
UA_ByteString certificate = {.length = 0, .data = NULL};
|
|
UA_ByteString privateKey = {.length = 0, .data = NULL};
|
|
|
|
tok = ctx->tokens[++ctx->index];
|
|
for(size_t i = tok.size / 2; i > 0; i--) {
|
|
tok = ctx->tokens[++ctx->index];
|
|
switch(tok.type) {
|
|
case CJ5_TOKEN_STRING: {
|
|
char *field_str = (char *)UA_malloc(tok.size + 1);
|
|
unsigned int str_len = 0;
|
|
cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
|
|
if(strcmp(field_str, "certificate") == 0) {
|
|
UA_String out = {.length = 0, .data = NULL};
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &out, NULL);
|
|
|
|
if(out.length > 0) {
|
|
char *certfile = (char *)UA_malloc(out.length + 1);
|
|
memcpy(certfile, out.data, out.length);
|
|
certfile[out.length] = '\0';
|
|
certificate = loadCertificateFile(certfile);
|
|
UA_String_clear(&out);
|
|
UA_free(certfile);
|
|
}
|
|
} else if(strcmp(field_str, "privateKey") == 0) {
|
|
UA_String out = {.length = 0, .data = NULL};
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &out, NULL);
|
|
|
|
if(out.length > 0) {
|
|
char *keyfile = (char *)UA_malloc(out.length + 1);
|
|
memcpy(keyfile, out.data, out.length);
|
|
keyfile[out.length] = '\0';
|
|
privateKey = loadCertificateFile(keyfile);
|
|
UA_String_clear(&out);
|
|
UA_free(keyfile);
|
|
}
|
|
} else if(strcmp(field_str, "policy") == 0) {
|
|
parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &policy, NULL);
|
|
} else {
|
|
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Unknown field name.");
|
|
}
|
|
UA_free(field_str);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(certificate.length == 0 || privateKey.length == 0) {
|
|
UA_LOG_WARNING(config->logging, UA_LOGCATEGORY_USERLAND,
|
|
"Certificate and PrivateKey must be set for every policy.");
|
|
if(policy.length > 0)
|
|
UA_String_clear(&policy);
|
|
if(certificate.length > 0)
|
|
UA_ByteString_clear(&certificate);
|
|
if(privateKey.length > 0)
|
|
UA_ByteString_clear(&privateKey);
|
|
return UA_STATUSCODE_BADINTERNALERROR;
|
|
}
|
|
UA_StatusCode retval = UA_STATUSCODE_GOOD;
|
|
if(UA_String_equal(&policy, &noneuri)) {
|
|
/* Nothing to do! */
|
|
} else if(UA_String_equal(&policy, &basic128Rsa15uri)) {
|
|
retval = UA_ServerConfig_addSecurityPolicyBasic128Rsa15(config, &certificate, &privateKey);
|
|
if(retval != UA_STATUSCODE_GOOD) {
|
|
UA_LOG_WARNING(config->logging, UA_LOGCATEGORY_USERLAND,
|
|
"Could not add SecurityPolicy#Basic128Rsa15 with error code %s",
|
|
UA_StatusCode_name(retval));
|
|
}
|
|
} else if(UA_String_equal(&policy, &basic256uri)) {
|
|
retval = UA_ServerConfig_addSecurityPolicyBasic256(config, &certificate, &privateKey);
|
|
if(retval != UA_STATUSCODE_GOOD) {
|
|
UA_LOG_WARNING(config->logging, UA_LOGCATEGORY_USERLAND,
|
|
"Could not add SecurityPolicy#Basic256 with error code %s",
|
|
UA_StatusCode_name(retval));
|
|
}
|
|
} else if(UA_String_equal(&policy, &basic256Sha256uri)) {
|
|
retval = UA_ServerConfig_addSecurityPolicyBasic256Sha256(config, &certificate, &privateKey);
|
|
if(retval != UA_STATUSCODE_GOOD) {
|
|
UA_LOG_WARNING(config->logging, UA_LOGCATEGORY_USERLAND,
|
|
"Could not add SecurityPolicy#Basic256Sha256 with error code %s",
|
|
UA_StatusCode_name(retval));
|
|
}
|
|
} else if(UA_String_equal(&policy, &aes128sha256rsaoaepuri)) {
|
|
retval = UA_ServerConfig_addSecurityPolicyAes128Sha256RsaOaep(config, &certificate, &privateKey);
|
|
if(retval != UA_STATUSCODE_GOOD) {
|
|
UA_LOG_WARNING(config->logging, UA_LOGCATEGORY_USERLAND,
|
|
"Could not add SecurityPolicy#Aes128Sha256RsaOaep with error code %s",
|
|
UA_StatusCode_name(retval));
|
|
}
|
|
} else if(UA_String_equal(&policy, &aes256sha256rsapssuri)) {
|
|
retval = UA_ServerConfig_addSecurityPolicyAes256Sha256RsaPss(config, &certificate, &privateKey);
|
|
if(retval != UA_STATUSCODE_GOOD) {
|
|
UA_LOG_WARNING(config->logging, UA_LOGCATEGORY_USERLAND,
|
|
"Could not add SecurityPolicy#Aes256Sha256RsaPss with error code %s",
|
|
UA_StatusCode_name(retval));
|
|
}
|
|
} else {
|
|
UA_LOG_WARNING(config->logging, UA_LOGCATEGORY_USERLAND, "Unknown Security Policy.");
|
|
}
|
|
|
|
/* Add all Endpoints */
|
|
UA_ServerConfig_addAllEndpoints(config);
|
|
|
|
if(policy.length > 0)
|
|
UA_String_clear(&policy);
|
|
if(certificate.length > 0)
|
|
UA_ByteString_clear(&certificate);
|
|
if(privateKey.length > 0)
|
|
UA_ByteString_clear(&privateKey);
|
|
}
|
|
#endif
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
|
|
PARSE_JSON(SecurityPkiField) {
|
|
#ifdef UA_ENABLE_ENCRYPTION
|
|
UA_ServerConfig *config = (UA_ServerConfig*)configField;
|
|
UA_String pkiFolder = {.length = 0, .data = NULL};
|
|
|
|
cj5_token tok = ctx->tokens[++ctx->index];
|
|
UA_ByteString buf = getJsonPart(tok, ctx->json);
|
|
UA_StatusCode retval = UA_decodeJson(&buf, &pkiFolder, &UA_TYPES[UA_TYPES_STRING], NULL);
|
|
if(retval != UA_STATUSCODE_GOOD)
|
|
return retval;
|
|
|
|
#if defined(__linux__) || defined(UA_ARCHITECTURE_WIN32)
|
|
/* Currently not supported! */
|
|
(void)config;
|
|
return UA_STATUSCODE_GOOD;
|
|
#else
|
|
/* Set up the parameters */
|
|
UA_KeyValuePair params[2];
|
|
size_t paramsSize = 2;
|
|
|
|
params[0].key = UA_QUALIFIEDNAME(0, "max-trust-listsize");
|
|
UA_Variant_setScalar(¶ms[0].value, &config->maxTrustListSize, &UA_TYPES[UA_TYPES_UINT32]);
|
|
params[1].key = UA_QUALIFIEDNAME(0, "max-rejected-listsize");
|
|
UA_Variant_setScalar(¶ms[1].value, &config->maxRejectedListSize, &UA_TYPES[UA_TYPES_UINT32]);
|
|
|
|
UA_KeyValueMap paramsMap;
|
|
paramsMap.map = params;
|
|
paramsMap.mapSize = paramsSize;
|
|
|
|
/* set server config field */
|
|
UA_NodeId defaultApplicationGroup =
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP);
|
|
retval = UA_CertificateGroup_Filestore(&config->secureChannelPKI, &defaultApplicationGroup,
|
|
pkiFolder, config->logging, ¶msMap);
|
|
if(retval != UA_STATUSCODE_GOOD) {
|
|
UA_String_clear(&pkiFolder);
|
|
return retval;
|
|
}
|
|
|
|
UA_NodeId defaultUserTokenGroup =
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP);
|
|
retval = UA_CertificateGroup_Filestore(&config->sessionPKI, &defaultUserTokenGroup,
|
|
pkiFolder, config->logging, ¶msMap);
|
|
if(retval != UA_STATUSCODE_GOOD) {
|
|
UA_String_clear(&pkiFolder);
|
|
return retval;
|
|
}
|
|
|
|
/* Clean up */
|
|
UA_String_clear(&pkiFolder);
|
|
#endif
|
|
#endif
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
|
|
/*----------------------Enumerations------------------------*/
|
|
PARSE_JSON(ApplicationTypeField) {
|
|
UA_UInt32 enum_value;
|
|
UA_StatusCode retval = UInt32Field_parseJson(ctx, &enum_value, NULL);
|
|
if(retval != UA_STATUSCODE_GOOD)
|
|
return retval;
|
|
UA_ApplicationType *field = (UA_ApplicationType*)configField;
|
|
*field = (UA_ApplicationType)enum_value;
|
|
return retval;
|
|
}
|
|
PARSE_JSON(RuleHandlingField) {
|
|
UA_UInt32 enum_value;
|
|
UA_StatusCode retval = UInt32Field_parseJson(ctx, &enum_value, NULL);
|
|
if(retval != UA_STATUSCODE_GOOD)
|
|
return retval;
|
|
UA_RuleHandling *field = (UA_RuleHandling*)configField;
|
|
*field = (UA_RuleHandling)enum_value;
|
|
return retval;
|
|
}
|
|
|
|
const parseJsonSignature parseJsonJumpTable[UA_SERVERCONFIGFIELDKINDS] = {
|
|
/* Basic Types */
|
|
(parseJsonSignature)Int64Field_parseJson,
|
|
(parseJsonSignature)UInt16Field_parseJson,
|
|
(parseJsonSignature)UInt32Field_parseJson,
|
|
(parseJsonSignature)UInt64Field_parseJson,
|
|
(parseJsonSignature)StringField_parseJson,
|
|
(parseJsonSignature)LocalizedTextField_parseJson,
|
|
(parseJsonSignature)DoubleField_parseJson,
|
|
(parseJsonSignature)BooleanField_parseJson,
|
|
(parseJsonSignature)DurationField_parseJson,
|
|
(parseJsonSignature)DurationRangeField_parseJson,
|
|
(parseJsonSignature)UInt32RangeField_parseJson,
|
|
|
|
/* Advanced Types */
|
|
(parseJsonSignature)BuildInfo_parseJson,
|
|
(parseJsonSignature)ApplicationDescriptionField_parseJson,
|
|
(parseJsonSignature)StringArrayField_parseJson,
|
|
(parseJsonSignature)UInt32ArrayField_parseJson,
|
|
(parseJsonSignature)DateTimeField_parseJson,
|
|
(parseJsonSignature)SubscriptionConfigurationField_parseJson,
|
|
(parseJsonSignature)TcpConfigurationField_parseJson,
|
|
(parseJsonSignature)PubsubConfigurationField_parseJson,
|
|
(parseJsonSignature)HistorizingConfigurationField_parseJson,
|
|
(parseJsonSignature)MdnsConfigurationField_parseJson,
|
|
(parseJsonSignature)SecurityPolciesField_parseJson,
|
|
(parseJsonSignature)SecurityPkiField_parseJson,
|
|
|
|
/* Enumerations */
|
|
(parseJsonSignature)ApplicationTypeField_parseJson,
|
|
(parseJsonSignature)RuleHandlingField_parseJson,
|
|
};
|
|
|
|
/* Skips unknown item (simple, object or array) in config file.
|
|
* Unknown items may happen if we don't support some features.
|
|
* E.g. if UA_ENABLE_ENCRYPTION is not defined and config file
|
|
* contains "securityPolicies" entry.
|
|
*/
|
|
static void
|
|
skipUnknownItem(ParsingCtx* ctx) {
|
|
unsigned int end = ctx->tokens[ctx->index].end;
|
|
do {
|
|
ctx->index++;
|
|
} while (ctx->index < ctx->tokensSize &&
|
|
ctx->tokens[ctx->index].start < end);
|
|
}
|
|
|
|
static UA_StatusCode
|
|
parseJSONConfig(UA_ServerConfig *config, UA_ByteString json_config) {
|
|
// Parsing json config
|
|
const char *json = (const char*)json_config.data;
|
|
cj5_token tokens[MAX_TOKENS];
|
|
cj5_result r = cj5_parse(json, (unsigned int)json_config.length, tokens, MAX_TOKENS, NULL);
|
|
|
|
ParsingCtx ctx;
|
|
ctx.json = json;
|
|
ctx.result = r;
|
|
ctx.tokens = r.tokens;
|
|
ctx.tokensSize = r.num_tokens;
|
|
ctx.index = 1; // The first token is ignored because it is known and not needed.
|
|
|
|
size_t serverConfigSize = 0;
|
|
if(ctx.tokens)
|
|
serverConfigSize = (ctx.tokens[ctx.index-1].size/2);
|
|
UA_StatusCode retval = UA_STATUSCODE_GOOD;
|
|
for (size_t j = serverConfigSize; j > 0; j--) {
|
|
cj5_token tok = ctx.tokens[ctx.index];
|
|
switch (tok.type) {
|
|
case CJ5_TOKEN_STRING: {
|
|
char *field = (char*)UA_malloc(tok.size + 1);
|
|
unsigned int str_len = 0;
|
|
cj5_get_str(&ctx.result, (unsigned int)ctx.index, field, &str_len);
|
|
if(strcmp(field, "buildInfo") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BUILDINFO](&ctx, &config->buildInfo, NULL);
|
|
else if(strcmp(field, "applicationDescription") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_APPLICATIONDESCRIPTION](&ctx, &config->applicationDescription, NULL);
|
|
else if(strcmp(field, "shutdownDelay") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_DOUBLE](&ctx, &config->shutdownDelay, NULL);
|
|
else if(strcmp(field, "verifyRequestTimestamp") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_RULEHANDLING](&ctx, &config->verifyRequestTimestamp, NULL);
|
|
else if(strcmp(field, "allowEmptyVariables") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_RULEHANDLING](&ctx, &config->allowEmptyVariables, NULL);
|
|
else if(strcmp(field, "serverUrls") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRINGARRAY](&ctx, &config->serverUrls, &config->serverUrlsSize);
|
|
else if(strcmp(field, "tcpEnabled") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->tcpEnabled, NULL);
|
|
else if(strcmp(field, "tcp") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_TCPCONFIGURATION](&ctx, config, NULL);
|
|
else if(strcmp(field, "securityPolicyNoneDiscoveryOnly") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->securityPolicyNoneDiscoveryOnly, NULL);
|
|
else if(strcmp(field, "modellingRulesOnInstances") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->modellingRulesOnInstances, NULL);
|
|
else if(strcmp(field, "maxSecureChannels") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT16](&ctx, &config->maxSecureChannels, NULL);
|
|
else if(strcmp(field, "maxSecurityTokenLifetime") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxSecurityTokenLifetime, NULL);
|
|
else if(strcmp(field, "maxSessions") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT16](&ctx, &config->maxSessions, NULL);
|
|
else if(strcmp(field, "maxSessionTimeout") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_DOUBLE](&ctx, &config->maxSessionTimeout, NULL);
|
|
else if(strcmp(field, "maxNodesPerRead") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerRead, NULL);
|
|
else if(strcmp(field, "maxNodesPerWrite") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerWrite, NULL);
|
|
else if(strcmp(field, "maxNodesPerMethodCall") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerMethodCall, NULL);
|
|
else if(strcmp(field, "maxNodesPerBrowse") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerBrowse, NULL);
|
|
else if(strcmp(field, "maxNodesPerRegisterNodes") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerRegisterNodes, NULL);
|
|
else if(strcmp(field, "maxNodesPerTranslateBrowsePathsToNodeIds") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerTranslateBrowsePathsToNodeIds, NULL);
|
|
else if(strcmp(field, "maxNodesPerNodeManagement") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerNodeManagement, NULL);
|
|
else if(strcmp(field, "maxMonitoredItemsPerCall") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxMonitoredItemsPerCall, NULL);
|
|
else if(strcmp(field, "maxReferencesPerNode") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxReferencesPerNode, NULL);
|
|
else if(strcmp(field, "reverseReconnectInterval") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->reverseReconnectInterval, NULL);
|
|
|
|
#if UA_MULTITHREADING >= 100
|
|
else if(strcmp(field, "asyncOperationTimeout") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_DOUBLE](&ctx, &config->asyncOperationTimeout, NULL);
|
|
else if(strcmp(field, "maxAsyncOperationQueueSize") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT64](&ctx, &config->maxAsyncOperationQueueSize, NULL);
|
|
#endif
|
|
|
|
#ifdef UA_ENABLE_DISCOVERY
|
|
else if(strcmp(field, "discoveryCleanupTimeout") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->discoveryCleanupTimeout, NULL);
|
|
#ifdef UA_ENABLE_DISCOVERY_MULTICAST
|
|
else if(strcmp(field, "mdnsEnabled") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->mdnsEnabled, NULL);
|
|
else if(strcmp(field, "mdns") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_MDNSCONFIGURATION](&ctx, config, NULL);
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef UA_ENABLE_SUBSCRIPTIONS
|
|
else if(strcmp(field, "subscriptionsEnabled") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->subscriptionsEnabled, NULL);
|
|
else if(strcmp(field, "subscriptions") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_SUBSCRIPTIONCONFIGURATION](&ctx, config, NULL);
|
|
# endif
|
|
|
|
#ifdef UA_ENABLE_HISTORIZING
|
|
else if(strcmp(field, "historizingEnabled") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->historizingEnabled, NULL);
|
|
else if(strcmp(field, "historizing") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_HISTORIZINGCONFIGURATION](&ctx, config, NULL);
|
|
#endif
|
|
|
|
#ifdef UA_ENABLE_PUBSUB
|
|
else if(strcmp(field, "pubsubEnabled") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->pubsubEnabled, NULL);
|
|
else if(strcmp(field, "pubsub") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_PUBSUBCONFIGURATION](&ctx, &config->pubSubConfig, NULL);
|
|
#endif
|
|
#ifdef UA_ENABLE_ENCRYPTION
|
|
else if(strcmp(field, "securityPolicies") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_SECURITYPOLICIES](&ctx, config, NULL);
|
|
else if(strcmp(field, "pkiFolder") == 0)
|
|
retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_SECURITYPKI](&ctx, config, NULL);
|
|
#endif
|
|
else {
|
|
UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Field name '%s' unknown or misspelled. Maybe the feature is not enabled either.", field);
|
|
/* skip the name of item */
|
|
++ctx.index;
|
|
/* skip value of unknown item */
|
|
skipUnknownItem(&ctx);
|
|
/* after skipUnknownItem() ctx->index points to the name of the following item.
|
|
We must decrement index in oder following increment will
|
|
still set index to the right position (name of the following item) */
|
|
--ctx.index;
|
|
}
|
|
UA_free(field);
|
|
if(retval != UA_STATUSCODE_GOOD) {
|
|
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "An error occurred while parsing the configuration file.");
|
|
return retval;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
ctx.index += 1;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
UA_Server *
|
|
UA_Server_newFromFile(const UA_ByteString json_config) {
|
|
UA_ServerConfig config;
|
|
memset(&config, 0, sizeof(UA_ServerConfig));
|
|
UA_StatusCode res = UA_ServerConfig_setDefault(&config);
|
|
res |= parseJSONConfig(&config, json_config);
|
|
if(res != UA_STATUSCODE_GOOD)
|
|
return NULL;
|
|
return UA_Server_newWithConfig(&config);
|
|
}
|
|
|
|
UA_StatusCode
|
|
UA_ServerConfig_updateFromFile(UA_ServerConfig *config, const UA_ByteString json_config) {
|
|
UA_StatusCode res = parseJSONConfig(config, json_config);
|
|
return res;
|
|
}
|
|
|
|
#ifdef UA_ENABLE_ENCRYPTION
|
|
static UA_ByteString
|
|
loadCertificateFile(const char *const path) {
|
|
UA_ByteString fileContents = UA_BYTESTRING_NULL;
|
|
|
|
/* Open the file */
|
|
FILE *fp = fopen(path, "rb");
|
|
if(!fp) {
|
|
errno = 0; /* We read errno also from the tcp layer... */
|
|
return fileContents;
|
|
}
|
|
|
|
/* Get the file length, allocate the data and read */
|
|
fseek(fp, 0, SEEK_END);
|
|
fileContents.length = (size_t)ftell(fp);
|
|
fileContents.data = (UA_Byte *)UA_malloc(fileContents.length * sizeof(UA_Byte));
|
|
if(fileContents.data) {
|
|
fseek(fp, 0, SEEK_SET);
|
|
size_t read = fread(fileContents.data, sizeof(UA_Byte), fileContents.length, fp);
|
|
if(read != fileContents.length)
|
|
UA_ByteString_clear(&fileContents);
|
|
} else {
|
|
fileContents.length = 0;
|
|
}
|
|
fclose(fp);
|
|
|
|
return fileContents;
|
|
}
|
|
#endif
|