#include "ua_server_internal.h"
const UA_DataTypeArray *
serverCustomTypes(UA_Server *server) {
if(server->customTypes_internalSize == 0)
return server->config.customDataTypes;
server->customTypes_internal[server->customTypes_internalSize-1].next = server->config.customDataTypes;
return server->customTypes_internal;
}
const UA_DataTypeArray *
UA_Server_getDataTypes(UA_Server *server) {
lockServer(server);
const UA_DataTypeArray * out = serverCustomTypes(server);
unlockServer(server);
return out;
}
const UA_DataType *
UA_Server_findDataType(UA_Server *server, const UA_NodeId *typeId) {
return UA_findDataTypeWithCustom(typeId, serverCustomTypes(server));
}
static UA_StatusCode
addDataType(UA_Server *server, UA_DataType *dt) {
#define TYPES_LIST_SIZE 64
UA_DataTypeArray *current = NULL;
if(server->customTypes_internalSize > 0)
current = &server->customTypes_internal[server->customTypes_internalSize-1];
if(!current || current->typesSize == TYPES_LIST_SIZE) {
UA_DataTypeArray *lol = (UA_DataTypeArray*)
UA_realloc(server->customTypes_internal,
sizeof(UA_DataTypeArray) * (server->customTypes_internalSize+1));
if(!lol)
return UA_STATUSCODE_BADOUTOFMEMORY;
memset(&lol[server->customTypes_internalSize], 0, sizeof(UA_DataTypeArray));
server->customTypes_internal = lol;
server->customTypes_internalSize++;
for(size_t i = 0; i < server->customTypes_internalSize-1; i++)
lol[i].next = &lol[i+1];
current = &server->customTypes_internal[server->customTypes_internalSize-1];
current->types = (UA_DataType*)UA_calloc(TYPES_LIST_SIZE, sizeof(UA_DataType));
current->typesSize = 0;
if(!current->types) {
server->customTypes_internalSize--;
return UA_STATUSCODE_BADOUTOFMEMORY;
}
}
current->types[current->typesSize] = *dt;
current->typesSize++;
return UA_STATUSCODE_GOOD;
}
UA_StatusCode
UA_Server_addDataType(UA_Server *server, const UA_NodeId parentNodeId,
const UA_DataType *type) {
if(UA_Server_findDataType(server, &type->typeId))
return UA_STATUSCODE_BADNODEIDEXISTS;
UA_DataType dt2;
UA_StatusCode res = UA_DataType_copy(type, &dt2);
if(res != UA_STATUSCODE_GOOD)
return res;
res = addDataType(server, &dt2);
if(res != UA_STATUSCODE_GOOD)
UA_DataType_clear(&dt2);
return res;
}
UA_StatusCode
UA_Server_addDataTypeFromDescription(UA_Server *server,
const UA_ExtensionObject *description) {
UA_DataType dt;
UA_StatusCode res =
UA_DataType_fromDescription(&dt, description, serverCustomTypes(server));
if(res != UA_STATUSCODE_GOOD)
return res;
if(UA_Server_findDataType(server, &dt.typeId)) {
UA_DataType_clear(&dt);
return UA_STATUSCODE_BADNODEIDEXISTS;
}
res = addDataType(server, &dt);
if(res != UA_STATUSCODE_GOOD)
UA_DataType_clear(&dt);
return res;
}
struct ReturnTypeContext {
UA_Server *server;
UA_UInt32 attributeMask;
UA_ReferenceTypeSet references;
UA_BrowseDirection referenceDirections;
};
static void *
returnFirstType(void *context, UA_ReferenceTarget *t) {
struct ReturnTypeContext *ctx = (struct ReturnTypeContext*)context;
return (void *)(uintptr_t)UA_NODESTORE_GETFROMREF_SELECTIVE(
ctx->server, t->targetId, ctx->attributeMask, ctx->references,
ctx->referenceDirections);
}
const UA_Node *
getNodeType(UA_Server *server, const UA_NodeHead *head,
UA_UInt32 attributeMask, UA_ReferenceTypeSet references,
UA_BrowseDirection referenceDirections) {
UA_Byte parentRefIndex;
UA_Boolean inverse;
switch(head->nodeClass) {
case UA_NODECLASS_OBJECT:
case UA_NODECLASS_VARIABLE:
parentRefIndex = UA_REFERENCETYPEINDEX_HASTYPEDEFINITION;
inverse = false;
break;
case UA_NODECLASS_OBJECTTYPE:
case UA_NODECLASS_VARIABLETYPE:
case UA_NODECLASS_REFERENCETYPE:
case UA_NODECLASS_DATATYPE:
parentRefIndex = UA_REFERENCETYPEINDEX_HASSUBTYPE;
inverse = true;
break;
default:
return NULL;
}
struct ReturnTypeContext ctx;
ctx.server = server;
ctx.attributeMask = attributeMask;
ctx.references = references;
ctx.referenceDirections = referenceDirections;
for(size_t i = 0; i < head->referencesSize; ++i) {
UA_NodeReferenceKind *rk = &head->references[i];
if(rk->isInverse != inverse)
continue;
if(rk->referenceTypeIndex != parentRefIndex)
continue;
const UA_Node *type = (const UA_Node*)
UA_NodeReferenceKind_iterate(rk, returnFirstType, &ctx);
if(type)
return type;
}
return NULL;
}
UA_Boolean
UA_Node_hasSubTypeOrInstances(const UA_NodeHead *head) {
for(size_t i = 0; i < head->referencesSize; ++i) {
if(head->references[i].isInverse == false &&
head->references[i].referenceTypeIndex == UA_REFERENCETYPEINDEX_HASSUBTYPE)
return true;
if(head->references[i].isInverse == true &&
head->references[i].referenceTypeIndex == UA_REFERENCETYPEINDEX_HASTYPEDEFINITION)
return true;
}
return false;
}
UA_StatusCode
getTypeAndInterfaceHierarchy(UA_Server *server, const UA_NodeId *leafNode,
UA_Boolean includeLeaf, UA_NodeId **typeHierarchy,
size_t *typeHierarchySize) {
UA_ReferenceTypeSet hastype = UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASTYPEDEFINITION);
UA_ReferenceTypeSet hassubtype = UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASSUBTYPE);
UA_ReferenceTypeSet hasinterface = UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASINTERFACE);
RefTree rt;
UA_StatusCode res = RefTree_init(&rt);
if(res != UA_STATUSCODE_GOOD)
return res;
res = RefTree_addNodeId(&rt, leafNode, NULL);
if(res != UA_STATUSCODE_GOOD)
goto errout;
res = browseRecursiveRefTree(server, &rt, UA_BROWSEDIRECTION_FORWARD, &hastype,
UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_VARIABLETYPE);
if(res != UA_STATUSCODE_GOOD)
goto errout;
res = browseRecursiveRefTree(server, &rt, UA_BROWSEDIRECTION_INVERSE, &hassubtype,
UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_VARIABLETYPE);
if(res != UA_STATUSCODE_GOOD)
goto errout;
res = browseRecursiveRefTree(server, &rt, UA_BROWSEDIRECTION_FORWARD, &hasinterface,
UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_VARIABLETYPE);
errout:
if(res != UA_STATUSCODE_GOOD || rt.size == 0) {
RefTree_clear(&rt);
return res;
}
UA_NodeId *outArray = (UA_NodeId*)rt.targets;
size_t pos = 0;
for(size_t i = 0; i < rt.size; i++) {
UA_NodeId *n = &outArray[pos];
UA_ExpandedNodeId *e = &rt.targets[i];
if(!UA_ExpandedNodeId_isLocal(e)) {
UA_ExpandedNodeId_clear(e);
continue;
}
*n = e->nodeId;
UA_String_clear(&e->namespaceUri);
pos++;
}
*typeHierarchySize = pos;
*typeHierarchy = outArray;
return UA_STATUSCODE_GOOD;
}
UA_StatusCode
getAllInterfaces(UA_Server *server, const UA_NodeId *objectNode,
UA_NodeId **interfaceNodes, size_t *interfaceNodesSize) {
UA_ReferenceTypeSet hastype = UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASTYPEDEFINITION);
UA_ReferenceTypeSet hassubtype = UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASSUBTYPE);
UA_ReferenceTypeSet hasinterface = UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASINTERFACE);
size_t beforeInterfaces = 0;
RefTree rt;
UA_StatusCode res = RefTree_init(&rt);
if(res != UA_STATUSCODE_GOOD)
return res;
res = RefTree_addNodeId(&rt, objectNode, NULL);
if(res != UA_STATUSCODE_GOOD)
goto errout;
res = browseRecursiveRefTree(server, &rt, UA_BROWSEDIRECTION_FORWARD, &hastype,
UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_VARIABLETYPE);
if(res != UA_STATUSCODE_GOOD)
goto errout;
res = browseRecursiveRefTree(server, &rt, UA_BROWSEDIRECTION_INVERSE, &hassubtype,
UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_VARIABLETYPE);
if(res != UA_STATUSCODE_GOOD)
goto errout;
beforeInterfaces = rt.size;
res = browseRecursiveRefTree(server, &rt, UA_BROWSEDIRECTION_FORWARD, &hasinterface,
UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_VARIABLETYPE);
errout:
if(res != UA_STATUSCODE_GOOD || rt.size == 0) {
RefTree_clear(&rt);
return res;
}
UA_NodeId *outArray = (UA_NodeId*)rt.targets;
size_t pos = 0;
for(size_t i = 0; i < rt.size; i++) {
UA_NodeId *n = &outArray[pos];
UA_ExpandedNodeId *e = &rt.targets[i];
if(i < beforeInterfaces || !UA_ExpandedNodeId_isLocal(e)) {
UA_ExpandedNodeId_clear(e);
continue;
}
*n = e->nodeId;
UA_String_clear(&e->namespaceUri);
pos++;
}
if(pos == 0) {
RefTree_clear(&rt);
outArray = NULL;
}
*interfaceNodesSize = pos;
*interfaceNodes = outArray;
return UA_STATUSCODE_GOOD;
}
UA_StatusCode
editNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
UA_UInt32 attributeMask, UA_ReferenceTypeSet references,
UA_BrowseDirection referenceDirections,
UA_EditNodeCallback callback, void *data) {
UA_Node *node =
UA_NODESTORE_GET_EDIT_SELECTIVE(server, nodeId, attributeMask,
references, referenceDirections);
if(!node)
return UA_STATUSCODE_BADNODEIDUNKNOWN;
UA_StatusCode retval = callback(server, session, node, data);
UA_NODESTORE_RELEASE(server, node);
return retval;
}
UA_StatusCode
validateCertificate(UA_Server *server, UA_CertificateGroup *cg,
UA_SecureChannel *channel, UA_Session *session,
const char *logPrefix,
const UA_ApplicationDescription *ad,
const UA_ByteString certificate) {
UA_StatusCode res = UA_STATUSCODE_GOOD;
if(ad) {
res = UA_CertificateUtils_verifyApplicationUri(&certificate,
&ad->applicationUri);
if(res != UA_STATUSCODE_GOOD) {
if(server->config.allowAllCertificateUris <= UA_RULEHANDLING_WARN) {
if(session) {
UA_LOG_ERROR_SESSION(server->config.logging, session,
"%s: The client's ApplicationUri "
"could not be verified against the "
"ApplicationUri %S from the client's "
"ApplicationDescription", logPrefix,
ad->applicationUri);
} else if(channel) {
UA_LOG_ERROR_CHANNEL(server->config.logging, channel,
"%s: The client certificate's ApplicationUri "
"could not be verified against the "
"ApplicationUri %S from the client's "
"ApplicationDescription", logPrefix,
ad->applicationUri);
} else {
UA_LOG_ERROR(server->config.logging, UA_LOGCATEGORY_SERVER,
"%s: The server certificate's ApplicationUri "
"could not be verified against the "
"ApplicationUri %S from its "
"ApplicationDescription", logPrefix,
ad->applicationUri);
}
}
if(server->config.allowAllCertificateUris <= UA_RULEHANDLING_ABORT)
return UA_STATUSCODE_BADCERTIFICATEINVALID;
}
}
if(!cg->verifyCertificate) {
UA_LOG_ERROR(server->config.logging, UA_LOGCATEGORY_SERVER,
"%s: Could not validate the certificate "
"as the CertificateGroup is not configured", logPrefix);
return UA_STATUSCODE_BADINTERNALERROR;
}
res = cg->verifyCertificate(cg, &certificate);
if(res != UA_STATUSCODE_GOOD) {
const char *descr = UA_StatusCode_name(res);
if(session) {
UA_LOG_ERROR_SESSION(server->config.logging, session,
"%s: The client certificate failed the verification "
"in the CertificateGroup with StatusCode %s",
logPrefix, descr);
} else if(channel) {
UA_LOG_ERROR_CHANNEL(server->config.logging, channel,
"%s: The client certificate failed the verification "
"in the CertificateGroup with StatusCode %s",
logPrefix, descr);
} else {
UA_LOG_ERROR(server->config.logging, UA_LOGCATEGORY_SERVER,
"%s: The client certificate failed the verification "
"in the CertificateGroup with StatusCode %s",
logPrefix, descr);
}
}
return res;
}
const UA_ObjectAttributes UA_ObjectAttributes_default = {
0,
{{0, NULL}, {0, NULL}},
{{0, NULL}, {0, NULL}},
0, 0,
0
};
const UA_VariableAttributes UA_VariableAttributes_default = {
0,
{{0, NULL}, {0, NULL}},
{{0, NULL}, {0, NULL}},
0, 0,
{NULL, UA_VARIANT_DATA,
0, NULL, 0, NULL},
{0, UA_NODEIDTYPE_NUMERIC,
{UA_NS0ID_BASEDATATYPE}},
UA_VALUERANK_ANY,
0, NULL,
UA_ACCESSLEVELMASK_READ |
UA_ACCESSLEVELMASK_STATUSWRITE |
UA_ACCESSLEVELMASK_TIMESTAMPWRITE,
0,
0.0,
false
};
const UA_MethodAttributes UA_MethodAttributes_default = {
0,
{{0, NULL}, {0, NULL}},
{{0, NULL}, {0, NULL}},
0, 0,
true, true
};
const UA_ObjectTypeAttributes UA_ObjectTypeAttributes_default = {
0,
{{0, NULL}, {0, NULL}},
{{0, NULL}, {0, NULL}},
0, 0,
false
};
const UA_VariableTypeAttributes UA_VariableTypeAttributes_default = {
0,
{{0, NULL}, {0, NULL}},
{{0, NULL}, {0, NULL}},
0, 0,
{NULL, UA_VARIANT_DATA,
0, NULL, 0, NULL},
{0, UA_NODEIDTYPE_NUMERIC,
{UA_NS0ID_BASEDATATYPE}},
UA_VALUERANK_ANY,
0, NULL,
false
};
const UA_ReferenceTypeAttributes UA_ReferenceTypeAttributes_default = {
0,
{{0, NULL}, {0, NULL}},
{{0, NULL}, {0, NULL}},
0, 0,
false,
false,
{{0, NULL}, {0, NULL}}
};
const UA_DataTypeAttributes UA_DataTypeAttributes_default = {
0,
{{0, NULL}, {0, NULL}},
{{0, NULL}, {0, NULL}},
0, 0,
false
};
const UA_ViewAttributes UA_ViewAttributes_default = {
0,
{{0, NULL}, {0, NULL}},
{{0, NULL}, {0, NULL}},
0, 0,
false,
0
};