UA_Boolean compatibleValue(UA_Server *server, const UA_NodeId *targetDataTypeId, UA_Int32 targetValueRank, size_t targetArrayDimensionsSize, const UA_UInt32 *targetArrayDimensions, const UA_Variant *value, const UA_NumericRange *range) { /* Empty variant is only allowed for BaseDataType */ if(!value->type) { if(UA_NodeId_equal(targetDataTypeId, &UA_TYPES[UA_TYPES_VARIANT].typeId) || UA_NodeId_equal(targetDataTypeId, &UA_NODEID_NULL)) return true; UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER, "Only Variables with data type BaseDataType may contain " "a null (empty) value"); return false; } /* Has the value a subtype of the required type? BaseDataType (Variant) can * be anything... */ if(!compatibleDataType(server, &value->type->typeId, targetDataTypeId)) return false; /* Array dimensions are checked later when writing the range */ if(range) return true; /* See if the array dimensions match. */ if(!compatibleValueArrayDimensions(value, targetArrayDimensionsSize, targetArrayDimensions)) return false; /* Check if the valuerank allows for the value dimension */ return compatibleValueRankValue(targetValueRank, value); }
static UA_StatusCode deleteOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node, const UA_DeleteReferencesItem *item) { UA_Boolean edited = UA_FALSE; for(UA_Int32 i = node->referencesSize - 1; i >= 0; i--) { if(!UA_NodeId_equal(&item->targetNodeId.nodeId, &node->references[i].targetId.nodeId)) continue; if(!UA_NodeId_equal(&item->referenceTypeId, &node->references[i].referenceTypeId)) continue; if(item->isForward == node->references[i].isInverse) continue; /* move the last entry to override the current position */ UA_ReferenceNode_deleteMembers(&node->references[i]); node->references[i] = node->references[node->referencesSize-1]; node->referencesSize--; edited = UA_TRUE; break; } if(!edited) return UA_STATUSCODE_UNCERTAINREFERENCENOTDELETED; /* we removed the last reference */ if(node->referencesSize == 0 && node->references) UA_free(node->references); return UA_STATUSCODE_GOOD;; }
/** * Gets a list of nodeIds which are referenced by this node. * Hereby the list will be filtered to only include references * which are of the given `references` types. */ static UA_StatusCode getReferencedNodesWithReferenceType(UA_NodeId **results_ptr, size_t *results_count, size_t *results_size, const UA_Node *node, const UA_NodeReferenceKind *references, size_t referencesSize) { UA_NodeId *results = *results_ptr; for(size_t i = 0; i < node->referencesSize; ++i) { /* Is the reference kind relevant? */ UA_NodeReferenceKind *refs = &node->references[i]; bool referenceInList = false; for (size_t j=0; j<referencesSize && !referenceInList; j++) { referenceInList = refs->isInverse == references[j].isInverse && UA_NodeId_equal(&references[j].referenceTypeId, &refs->referenceTypeId); } if (!referenceInList) continue; /* Append all targets of the reference kind .. if not a duplicate */ for(size_t j = 0; j < refs->targetIdsSize; ++j) { /* Is the target a duplicate? (multi-inheritance) */ UA_NodeId *targetId = &refs->targetIds[j].nodeId; UA_Boolean duplicate = false; for(size_t k = 0; k < *results_count; ++k) { if(UA_NodeId_equal(targetId, &results[k])) { duplicate = true; break; } } if(duplicate) continue; /* Increase array length if necessary */ if(*results_count >= *results_size) { size_t new_size = sizeof(UA_NodeId) * (*results_size) * 2; UA_NodeId *new_results = (UA_NodeId*)UA_realloc(results, new_size); if(!new_results) { UA_Array_delete(results, *results_count, &UA_TYPES[UA_TYPES_NODEID]); return UA_STATUSCODE_BADOUTOFMEMORY; } results = new_results; *results_ptr = results; *results_size *= 2; } /* Copy new nodeid to the end of the list */ UA_StatusCode retval = UA_NodeId_copy(targetId, &results[*results_count]); if(retval != UA_STATUSCODE_GOOD) { UA_Array_delete(results, *results_count, &UA_TYPES[UA_TYPES_NODEID]); return retval; } *results_count += 1; } } return UA_STATUSCODE_GOOD; }
UA_Boolean UA_Node_hasSubTypeOrInstances(const UA_Node *node) { const UA_NodeId hasSubType = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE); const UA_NodeId hasTypeDefinition = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION); for(size_t i = 0; i < node->referencesSize; ++i) { if(node->references[i].isInverse == false && UA_NodeId_equal(&node->references[i].referenceTypeId, &hasSubType)) return true; if(node->references[i].isInverse == true && UA_NodeId_equal(&node->references[i].referenceTypeId, &hasTypeDefinition)) return true; } return false; }
static UA_StatusCode argConformsToDefinition(UA_CallMethodRequest *rs, const UA_VariableNode *argDefinition) { if(argDefinition->value.variant.type != &UA_TYPES[UA_TYPES_ARGUMENT] && argDefinition->value.variant.type != &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]) return UA_STATUSCODE_BADINTERNALERROR; if(rs->inputArgumentsSize < argDefinition->value.variant.arrayLength) return UA_STATUSCODE_BADARGUMENTSMISSING; if(rs->inputArgumentsSize > argDefinition->value.variant.arrayLength) return UA_STATUSCODE_BADINVALIDARGUMENT; const UA_ExtensionObject *thisArgDefExtObj; UA_Variant *var; UA_Argument arg; size_t decodingOffset = 0; UA_StatusCode retval = UA_STATUSCODE_GOOD; UA_NodeId ArgumentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ARGUMENT + UA_ENCODINGOFFSET_BINARY); for(int i = 0; i<rs->inputArgumentsSize; i++) { var = &rs->inputArguments[i]; if(argDefinition->value.variant.type == &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]) { thisArgDefExtObj = &((const UA_ExtensionObject *) (argDefinition->value.variant.data))[i]; decodingOffset = 0; if(!UA_NodeId_equal(&ArgumentNodeId, &thisArgDefExtObj->typeId)) return UA_STATUSCODE_BADINTERNALERROR; UA_decodeBinary(&thisArgDefExtObj->body, &decodingOffset, &arg, &UA_TYPES[UA_TYPES_ARGUMENT]); } else if(argDefinition->value.variant.type == &UA_TYPES[UA_TYPES_ARGUMENT]) arg = ((UA_Argument *) argDefinition->value.variant.data)[i]; retval |= statisfySignature(var, arg); } return retval; }
END_TEST static void handler_events_propagate(UA_Client *lclient, UA_UInt32 subId, void *subContext, UA_UInt32 monId, void *monContext, size_t nEventFields, UA_Variant *eventFields) { UA_Boolean foundSeverity = UA_FALSE; UA_Boolean foundMessage = UA_FALSE; UA_Boolean foundType = UA_FALSE; UA_Boolean foundSource = UA_FALSE; ck_assert_uint_eq(*(UA_UInt32 *) monContext, monitoredItemId); ck_assert_uint_eq(nEventFields, nSelectClauses); // check all event fields for (unsigned int i = 0; i < nEventFields; i++) { // find out which attribute of the event is being looked at if (UA_Variant_hasScalarType(&eventFields[i], &UA_TYPES[UA_TYPES_UINT16])) { // Severity ck_assert_uint_eq(*((UA_UInt16 *) (eventFields[i].data)), 1000); foundSeverity = UA_TRUE; } else if (UA_Variant_hasScalarType(&eventFields[i], &UA_TYPES[UA_TYPES_LOCALIZEDTEXT])) { // Message UA_LocalizedText comp = UA_LOCALIZEDTEXT("en-US", "Generated Event"); ck_assert(UA_String_equal(&((UA_LocalizedText *) eventFields[i].data)->locale, &comp.locale)); ck_assert(UA_String_equal(&((UA_LocalizedText *) eventFields[i].data)->text, &comp.text)); foundMessage = UA_TRUE; } else if (UA_Variant_hasScalarType(&eventFields[i], &UA_TYPES[UA_TYPES_NODEID])) { // either SourceNode or EventType UA_NodeId serverNameSpaceId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACES); if (UA_NodeId_equal((UA_NodeId *)eventFields[i].data, &eventType)) { // EventType foundType = UA_TRUE; } else if (UA_NodeId_equal((UA_NodeId *)eventFields[i].data, &serverNameSpaceId)) { // SourceNode foundSource = UA_TRUE; } else { ck_assert_msg(UA_FALSE, "NodeId doesn't match"); } } else { ck_assert_msg(UA_FALSE, "Field doesn't match"); } } ck_assert_uint_eq(foundMessage, UA_TRUE); ck_assert_uint_eq(foundSeverity, UA_TRUE); ck_assert_uint_eq(foundType, UA_TRUE); ck_assert_uint_eq(foundSource, UA_TRUE); notificationReceived = true; }
static UA_StatusCode CopyValueIntoNode(UA_VariableNode *node, const UA_WriteValue *wvalue) { UA_assert(wvalue->attributeId == UA_ATTRIBUTEID_VALUE); UA_assert(node->nodeClass == UA_NODECLASS_VARIABLE || node->nodeClass == UA_NODECLASS_VARIABLETYPE); UA_assert(node->valueSource == UA_VALUESOURCE_VARIANT); /* Parse the range */ UA_NumericRange range; UA_NumericRange *rangeptr = NULL; UA_StatusCode retval = UA_STATUSCODE_GOOD; if(wvalue->indexRange.length > 0) { retval = parse_numericrange(&wvalue->indexRange, &range); if(retval != UA_STATUSCODE_GOOD) return retval; rangeptr = ⦥ } /* The nodeid on the wire may be != the nodeid in the node: opaque types, enums and bytestrings. nodeV contains the correct type definition. */ const UA_Variant *newV = &wvalue->value.value; UA_Variant *oldV = &node->value.variant.value; UA_Variant cast_v; if (oldV->type != NULL) { // Don't run NodeId_equal on a NULL pointer (happens if the variable never held a variant) if(!UA_NodeId_equal(&oldV->type->typeId, &newV->type->typeId)) { cast_v = wvalue->value.value; newV = &cast_v; enum type_equivalence te1 = typeEquivalence(oldV->type); enum type_equivalence te2 = typeEquivalence(newV->type); if(te1 != TYPE_EQUIVALENCE_NONE && te1 == te2) { /* An enum was sent as an int32, or an opaque type as a bytestring. This is detected with the typeIndex indicated the "true" datatype. */ cast_v.type = oldV->type; } else if(oldV->type == &UA_TYPES[UA_TYPES_BYTE] && !UA_Variant_isScalar(oldV) && newV->type == &UA_TYPES[UA_TYPES_BYTESTRING] && UA_Variant_isScalar(newV)) { /* a string is written to a byte array */ UA_ByteString *str = (UA_ByteString*) newV->data; cast_v.arrayLength = str->length; cast_v.data = str->data; cast_v.type = &UA_TYPES[UA_TYPES_BYTE]; } else { if(rangeptr) UA_free(range.dimensions); return UA_STATUSCODE_BADTYPEMISMATCH; } } } if(!rangeptr) { UA_Variant_deleteMembers(&node->value.variant.value); UA_Variant_copy(newV, &node->value.variant.value); } else retval = UA_Variant_setRangeCopy(&node->value.variant.value, newV->data, newV->arrayLength, range); if(node->value.variant.callback.onWrite) node->value.variant.callback.onWrite(node->value.variant.callback.handle, node->nodeId, &node->value.variant.value, rangeptr); if(rangeptr) UA_free(range.dimensions); return retval; }
/* Custom AccessControl policy that disallows access to one specific node */ static UA_Byte getUserAccessLevel_disallowSpecific(UA_Server *server, UA_AccessControl *ac, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, void *nodeContext) { if(UA_NodeId_equal(nodeId, &accessDenied)) return 0x00; return 0xFF; }
/* copy an existing variable under the given parent. then instantiate the variable for all hastypedefinitions of the original version. */ static UA_StatusCode copyExistingVariable(UA_Server *server, UA_Session *session, const UA_NodeId *variable, const UA_NodeId *referenceType, const UA_NodeId *parent) { const UA_VariableNode *node = (const UA_VariableNode*)UA_NodeStore_get(server->nodestore, variable); if(!node) return UA_STATUSCODE_BADNODEIDINVALID; if(node->nodeClass != UA_NODECLASS_VARIABLE) return UA_STATUSCODE_BADNODECLASSINVALID; // copy the variable attributes UA_VariableAttributes attr; UA_VariableAttributes_init(&attr); UA_LocalizedText_copy(&node->displayName, &attr.displayName); UA_LocalizedText_copy(&node->description, &attr.description); attr.writeMask = node->writeMask; attr.userWriteMask = node->userWriteMask; // todo: handle data sources!!!! UA_Variant_copy(&node->value.variant.value, &attr.value); // datatype is taken from the value // valuerank is taken from the value // array dimensions are taken from the value attr.accessLevel = node->accessLevel; attr.userAccessLevel = node->userAccessLevel; attr.minimumSamplingInterval = node->minimumSamplingInterval; attr.historizing = node->historizing; UA_AddNodesItem item; UA_AddNodesItem_init(&item); UA_NodeId_copy(parent, &item.parentNodeId.nodeId); UA_NodeId_copy(referenceType, &item.referenceTypeId); UA_QualifiedName_copy(&node->browseName, &item.browseName); item.nodeClass = UA_NODECLASS_VARIABLE; item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE; item.nodeAttributes.content.decoded.type = &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES]; item.nodeAttributes.content.decoded.data = &attr; // don't add a typedefinition here. // add the new variable UA_AddNodesResult res; UA_AddNodesResult_init(&res); Service_AddNodes_single(server, session, &item, &res); UA_VariableAttributes_deleteMembers(&attr); UA_AddNodesItem_deleteMembers(&item); // now instantiate the variable for all hastypedefinition references for(size_t i = 0; i < node->referencesSize; i++) { UA_ReferenceNode *rn = &node->references[i]; if(rn->isInverse) continue; const UA_NodeId hasTypeDef = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION); if(!UA_NodeId_equal(&rn->referenceTypeId, &hasTypeDef)) continue; instantiateVariableNode(server, session, &res.addedNodeId, &rn->targetId.nodeId); } UA_AddNodesResult_deleteMembers(&res); return UA_STATUSCODE_GOOD; }
static UA_StatusCode satisfySignature(const UA_Variant *var, const UA_Argument *arg) { if(!UA_NodeId_equal(&var->type->typeId, &arg->dataType) ) return UA_STATUSCODE_BADINVALIDARGUMENT; // Note: The namespace compiler will compile nodes with their actual array dimensions // Todo: Check if this is standard conform for scalars if(arg->arrayDimensionsSize > 0 && var->arrayDimensionsSize > 0) if(var->arrayDimensionsSize != arg->arrayDimensionsSize) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_UInt32 *varDims = var->arrayDimensions; size_t varDimsSize = var->arrayDimensionsSize; UA_Boolean scalar = UA_Variant_isScalar(var); /* The dimension 1 is implicit in the array length */ UA_UInt32 fakeDims; if(!scalar && !varDims) { fakeDims = (UA_UInt32)var->arrayLength; varDims = &fakeDims; varDimsSize = 1; } /* ValueRank Semantics * n >= 1: the value is an array with the specified number of dimens*ions. * n = 0: the value is an array with one or more dimensions. * n = -1: the value is a scalar. * n = -2: the value can be a scalar or an array with any number of dimensions. * n = -3: the value can be a scalar or a one dimensional array. */ switch(arg->valueRank) { case -3: if(varDimsSize > 1) return UA_STATUSCODE_BADINVALIDARGUMENT; break; case -2: break; case -1: if(!scalar) return UA_STATUSCODE_BADINVALIDARGUMENT; break; case 0: if(scalar || !varDims) return UA_STATUSCODE_BADINVALIDARGUMENT; break; default: break; } /* do the array dimensions match? */ if(arg->arrayDimensionsSize != varDimsSize) return UA_STATUSCODE_BADINVALIDARGUMENT; for(size_t i = 0; i < varDimsSize; i++) { if(arg->arrayDimensions[i] != varDims[i]) return UA_STATUSCODE_BADINVALIDARGUMENT; } return UA_STATUSCODE_GOOD; }
/* copy an existing object under the given parent. then instantiate the variable for all hastypedefinitions of the original version. */ static UA_StatusCode copyExistingObject(UA_Server *server, UA_Session *session, const UA_NodeId *variable, const UA_NodeId *referenceType, const UA_NodeId *parent, UA_InstantiationCallback *instantiationCallback) { const UA_ObjectNode *node = (const UA_ObjectNode*)UA_NodeStore_get(server->nodestore, variable); if(!node) return UA_STATUSCODE_BADNODEIDINVALID; if(node->nodeClass != UA_NODECLASS_OBJECT) return UA_STATUSCODE_BADNODECLASSINVALID; // copy the variable attributes UA_ObjectAttributes attr; UA_ObjectAttributes_init(&attr); UA_LocalizedText_copy(&node->displayName, &attr.displayName); UA_LocalizedText_copy(&node->description, &attr.description); attr.writeMask = node->writeMask; attr.userWriteMask = node->userWriteMask; attr.eventNotifier = node->eventNotifier; UA_AddNodesItem item; UA_AddNodesItem_init(&item); UA_NodeId_copy(parent, &item.parentNodeId.nodeId); UA_NodeId_copy(referenceType, &item.referenceTypeId); UA_QualifiedName_copy(&node->browseName, &item.browseName); item.nodeClass = UA_NODECLASS_OBJECT; item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE; item.nodeAttributes.content.decoded.type = &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES]; item.nodeAttributes.content.decoded.data = &attr; // don't add a typedefinition here. // add the new object UA_AddNodesResult res; UA_AddNodesResult_init(&res); Service_AddNodes_single(server, session, &item, &res, instantiationCallback); UA_ObjectAttributes_deleteMembers(&attr); UA_AddNodesItem_deleteMembers(&item); // now instantiate the object for all hastypedefinition references for(size_t i = 0; i < node->referencesSize; i++) { UA_ReferenceNode *rn = &node->references[i]; if(rn->isInverse) continue; const UA_NodeId hasTypeDef = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION); if(!UA_NodeId_equal(&rn->referenceTypeId, &hasTypeDef)) continue; instantiateObjectNode(server, session, &res.addedNodeId, &rn->targetId.nodeId, instantiationCallback); } if (instantiationCallback != NULL) instantiationCallback->method(res.addedNodeId, node->nodeId, instantiationCallback->handle); UA_AddNodesResult_deleteMembers(&res); return UA_STATUSCODE_GOOD; }
UA_Boolean compatibleDataType(UA_Server *server, const UA_NodeId *dataType, const UA_NodeId *constraintDataType) { /* Do not allow empty datatypes */ if(UA_NodeId_isNull(dataType)) return false; /* No constraint (TODO: use variant instead) */ if(UA_NodeId_isNull(constraintDataType)) return true; /* Variant allows any subtype */ if(UA_NodeId_equal(constraintDataType, &UA_TYPES[UA_TYPES_VARIANT].typeId)) return true; /* Is the value-type a subtype of the required type? */ if(isNodeInTree(&server->config.nodestore, dataType, constraintDataType, &subtypeId, 1)) return true; /* If value is a built-in type: The target data type may be a sub type of * the built-in type. (e.g. UtcTime is sub-type of DateTime and has a * DateTime value). A type is builtin if its NodeId is in Namespace 0 and * has a numeric identifier <= 25 (DiagnosticInfo) */ if(dataType->namespaceIndex == 0 && dataType->identifierType == UA_NODEIDTYPE_NUMERIC && dataType->identifier.numeric <= 25 && isNodeInTree(&server->config.nodestore, constraintDataType, dataType, &subtypeId, 1)) return true; /* Enum allows Int32 (only) */ if(UA_NodeId_equal(dataType, &UA_TYPES[UA_TYPES_INT32].typeId) && isNodeInTree(&server->config.nodestore, constraintDataType, &enumNodeId, &subtypeId, 1)) return true; return false; }
/* Returns UA_TRUE if an entry was found under the nodeid. Otherwise, returns false and sets slot to a pointer to the next free slot. */ static UA_Boolean containsNodeId(const UA_NodeStore *ns, const UA_NodeId *nodeid, UA_NodeStoreEntry **entry) { hash_t h = hash(nodeid); UA_UInt32 size = ns->size; hash_t index = mod(h, size); UA_NodeStoreEntry *e = &ns->entries[index]; if(!e->taken) { *entry = e; return UA_FALSE; } if(UA_NodeId_equal(&e->node.node.nodeId, nodeid)) { *entry = e; return UA_TRUE; } hash_t hash2 = mod2(h, size); for(;;) { index += hash2; if(index >= size) index -= size; e = &ns->entries[index]; if(!e->taken) { *entry = e; return UA_FALSE; } if(UA_NodeId_equal(&e->node.node.nodeId, nodeid)) { *entry = e; return UA_TRUE; } } /* NOTREACHED */ return UA_TRUE; }
/* Returns true if an entry was found under the nodeid. Otherwise, returns false and sets slot to a pointer to the next free slot. */ static UA_Boolean containsNodeId(const UA_NodeStore *ns, const UA_NodeId *nodeid, UA_NodeStoreEntry ***entry) { hash_t h = hash(nodeid); UA_UInt32 size = ns->size; hash_t idx = mod(h, size); UA_NodeStoreEntry *e = ns->entries[idx]; if(!e) { *entry = &ns->entries[idx]; return false; } if(UA_NodeId_equal(&e->node.nodeId, nodeid)) { *entry = &ns->entries[idx]; return true; } hash_t hash2 = mod2(h, size); for(;;) { idx += hash2; if(idx >= size) idx -= size; e = ns->entries[idx]; if(!e) { *entry = &ns->entries[idx]; return false; } if(UA_NodeId_equal(&e->node.nodeId, nodeid)) { *entry = &ns->entries[idx]; return true; } } /* NOTREACHED */ return true; }
const UA_Node * getNodeType(UA_Server *server, const UA_Node *node) { /* The reference to the parent is different for variable and variabletype */ UA_NodeId parentRef; UA_Boolean inverse; UA_NodeClass typeNodeClass; switch(node->nodeClass) { case UA_NODECLASS_OBJECT: parentRef = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION); inverse = false; typeNodeClass = UA_NODECLASS_OBJECTTYPE; break; case UA_NODECLASS_VARIABLE: parentRef = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION); inverse = false; typeNodeClass = UA_NODECLASS_VARIABLETYPE; break; case UA_NODECLASS_OBJECTTYPE: case UA_NODECLASS_VARIABLETYPE: case UA_NODECLASS_REFERENCETYPE: case UA_NODECLASS_DATATYPE: parentRef = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE); inverse = true; typeNodeClass = node->nodeClass; break; default: return NULL; } /* Return the first matching candidate */ for(size_t i = 0; i < node->referencesSize; ++i) { if(node->references[i].isInverse != inverse) continue; if(!UA_NodeId_equal(&node->references[i].referenceTypeId, &parentRef)) continue; UA_assert(node->references[i].targetIdsSize > 0); const UA_NodeId *targetId = &node->references[i].targetIds[0].nodeId; const UA_Node *type = UA_Nodestore_getNode(server->nsCtx, targetId); if(!type) continue; if(type->nodeClass == typeNodeClass) return type; UA_Nodestore_releaseNode(server->nsCtx, type); } return NULL; }
static UA_StatusCode statisfySignature(UA_Variant *var, UA_Argument arg) { if(!UA_NodeId_equal(&var->type->typeId, &arg.dataType) ) return UA_STATUSCODE_BADINVALIDARGUMENT; // Note: The namespace compiler will compile nodes with their actual array dimensions, never -1 if(arg.arrayDimensionsSize > 0 && var->arrayDimensionsSize > 0) if(var->arrayDimensionsSize != arg.arrayDimensionsSize) return UA_STATUSCODE_BADINVALIDARGUMENT; // Continue with jpfr's statisfySignature from here on /* ValueRank Semantics * n >= 1: the value is an array with the specified number of dimens*ions. * n = 0: the value is an array with one or more dimensions. * n = -1: the value is a scalar. * n = -2: the value can be a scalar or an array with any number of dimensions. * n = -3: the value can be a scalar or a one dimensional array. */ UA_Boolean scalar = UA_Variant_isScalar(var); if(arg.valueRank == 0 && scalar) return UA_STATUSCODE_BADINVALIDARGUMENT; if(arg.valueRank == -1 && !scalar) return UA_STATUSCODE_BADINVALIDARGUMENT; if(arg.valueRank == -3 && var->arrayDimensionsSize > 1) return UA_STATUSCODE_BADINVALIDARGUMENT; if(arg.valueRank > 1 && var->arrayDimensionsSize != arg.arrayDimensionsSize) return UA_STATUSCODE_BADINVALIDARGUMENT; //variants do not always encode the dimension flag (e.g. 1d array) if(var->arrayDimensionsSize==-1 && arg.arrayDimensionsSize == 1 && var->arrayLength > 0 && arg.arrayDimensions[0] == (UA_UInt32)var->arrayLength ){ return UA_STATUSCODE_GOOD; }else{ if(arg.valueRank >= 1 && var->arrayDimensionsSize != arg.arrayDimensionsSize) return UA_STATUSCODE_BADINVALIDARGUMENT; if(arg.arrayDimensionsSize >= 1) { if(arg.arrayDimensionsSize != var->arrayDimensionsSize) return UA_STATUSCODE_BADINVALIDARGUMENT; for(UA_Int32 i = 0; i < arg.arrayDimensionsSize; i++) { if(arg.arrayDimensions[i] != (UA_UInt32) var->arrayDimensions[i]) return UA_STATUSCODE_BADINVALIDARGUMENT; } } } return UA_STATUSCODE_GOOD; }
static void variables_nodeids(void) { UA_NodeId id1 = UA_NODEID_NUMERIC(1, 1234); id1.namespaceIndex = 3; UA_NodeId id2 = UA_NODEID_STRING(1, "testid"); /* the string is static */ UA_Boolean eq = UA_NodeId_equal(&id1, &id2); if(eq) return; UA_NodeId id3; UA_NodeId_copy(&id2, &id3); UA_NodeId_deleteMembers(&id3); UA_NodeId id4 = UA_NODEID_STRING_ALLOC(1, "testid"); /* the string is copied to the heap */ UA_NodeId_deleteMembers(&id4); }
static const UA_VariableNode * getArgumentsVariableNode(UA_Server *server, const UA_MethodNode *ofMethod, UA_String withBrowseName) { UA_NodeId hasProperty = UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY); for(size_t i = 0; i < ofMethod->referencesSize; i++) { if(ofMethod->references[i].isInverse == false && UA_NodeId_equal(&hasProperty, &ofMethod->references[i].referenceTypeId)) { const UA_Node *refTarget = UA_NodeStore_get(server->nodestore, &ofMethod->references[i].targetId.nodeId); if(!refTarget) continue; if(refTarget->nodeClass == UA_NODECLASS_VARIABLE && refTarget->browseName.namespaceIndex == 0 && UA_String_equal(&withBrowseName, &refTarget->browseName.name)) { return (const UA_VariableNode*) refTarget; } } } return NULL; }
static void processDecodedOPNResponseAsync(void *application, UA_SecureChannel *channel, UA_MessageType messageType, UA_UInt32 requestId, const UA_ByteString *message) { /* Does the request id match? */ UA_Client *client = (UA_Client*)application; if(requestId != client->requestId) { UA_Client_disconnect(client); return; } /* Is the content of the expected type? */ size_t offset = 0; UA_NodeId responseId; UA_NodeId expectedId = UA_NODEID_NUMERIC( 0, UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE].binaryEncodingId); UA_StatusCode retval = UA_NodeId_decodeBinary(message, &offset, &responseId); if(retval != UA_STATUSCODE_GOOD) { UA_Client_disconnect(client); return; } if(!UA_NodeId_equal(&responseId, &expectedId)) { UA_NodeId_deleteMembers(&responseId); UA_Client_disconnect(client); return; } UA_NodeId_deleteMembers (&responseId); /* Decode the response */ UA_OpenSecureChannelResponse response; retval = UA_OpenSecureChannelResponse_decodeBinary(message, &offset, &response); if(retval != UA_STATUSCODE_GOOD) { UA_Client_disconnect(client); return; } /* Response.securityToken.revisedLifetime is UInt32 we need to cast it to * DateTime=Int64 we take 75% of lifetime to start renewing as described in * standard */ client->nextChannelRenewal = UA_DateTime_nowMonotonic() + (UA_DateTime) (response.securityToken.revisedLifetime * (UA_Double) UA_DATETIME_MSEC * 0.75); /* Replace the token and nonce */ UA_ChannelSecurityToken_deleteMembers(&client->channel.securityToken); UA_ByteString_deleteMembers(&client->channel.remoteNonce); client->channel.securityToken = response.securityToken; client->channel.remoteNonce = response.serverNonce; UA_ResponseHeader_deleteMembers(&response.responseHeader); /* the other members were moved */ if(client->channel.state == UA_SECURECHANNELSTATE_OPEN) UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "SecureChannel renewed"); else UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "SecureChannel opened"); client->channel.state = UA_SECURECHANNELSTATE_OPEN; if(client->state < UA_CLIENTSTATE_SECURECHANNEL) setClientState(client, UA_CLIENTSTATE_SECURECHANNEL); }
static UA_StatusCode satisfySignature(UA_Server *server, const UA_Variant *var, const UA_Argument *arg) { if(!UA_NodeId_equal(&var->type->typeId, &arg->dataType)){ if(!UA_NodeId_equal(&var->type->typeId, &UA_TYPES[UA_TYPES_INT32].typeId)) return UA_STATUSCODE_BADINVALIDARGUMENT; /* enumerations are encoded as int32 -> if provided var is integer, check if arg is an enumeration type */ UA_NodeId enumerationNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ENUMERATION); UA_NodeId hasSubTypeNodeId = UA_NODEID_NUMERIC(0,UA_NS0ID_HASSUBTYPE); UA_Boolean found = false; UA_StatusCode retval = isNodeInTree(server->nodestore, &arg->dataType, &enumerationNodeId, &hasSubTypeNodeId, 1, 1, &found); if(retval != UA_STATUSCODE_GOOD) return UA_STATUSCODE_BADINTERNALERROR; if(!found) return UA_STATUSCODE_BADINVALIDARGUMENT; } // Note: The namespace compiler will compile nodes with their actual array dimensions // Todo: Check if this is standard conform for scalars if(arg->arrayDimensionsSize > 0 && var->arrayDimensionsSize > 0) if(var->arrayDimensionsSize != arg->arrayDimensionsSize) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_Int32 *varDims = var->arrayDimensions; size_t varDimsSize = var->arrayDimensionsSize; UA_Boolean scalar = UA_Variant_isScalar(var); /* The dimension 1 is implicit in the array length */ UA_Int32 fakeDims; if(!scalar && !varDims) { fakeDims = (UA_Int32)var->arrayLength; varDims = &fakeDims; varDimsSize = 1; } /* ValueRank Semantics * n >= 1: the value is an array with the specified number of dimens*ions. * n = 0: the value is an array with one or more dimensions. * n = -1: the value is a scalar. * n = -2: the value can be a scalar or an array with any number of dimensions. * n = -3: the value can be a scalar or a one dimensional array. */ switch(arg->valueRank) { case -3: if(varDimsSize > 1) return UA_STATUSCODE_BADINVALIDARGUMENT; break; case -2: break; case -1: if(!scalar) return UA_STATUSCODE_BADINVALIDARGUMENT; break; case 0: if(scalar || !varDims) return UA_STATUSCODE_BADINVALIDARGUMENT; break; default: break; } /* do the array dimensions match? */ if(arg->arrayDimensionsSize != varDimsSize) return UA_STATUSCODE_BADINVALIDARGUMENT; for(size_t i = 0; i < varDimsSize; i++) { if((UA_Int32)arg->arrayDimensions[i] != varDims[i]) return UA_STATUSCODE_BADINVALIDARGUMENT; } return UA_STATUSCODE_GOOD; }
static UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) { UA_StatusCode retval = UA_STATUSCODE_GOOD; /* is there a value at all */ if(!wvalue->value.hasValue) return UA_STATUSCODE_BADTYPEMISMATCH; // we might repeat writing, e.g. when the node got replaced mid-work UA_Boolean done = UA_FALSE; while(!done) { const UA_Node *node = UA_NodeStore_get(server->nodestore, &wvalue->nodeId); if(!node) return UA_STATUSCODE_BADNODEIDUNKNOWN; switch(wvalue->attributeId) { case UA_ATTRIBUTEID_NODEID: case UA_ATTRIBUTEID_NODECLASS: case UA_ATTRIBUTEID_BROWSENAME: case UA_ATTRIBUTEID_DISPLAYNAME: case UA_ATTRIBUTEID_DESCRIPTION: case UA_ATTRIBUTEID_WRITEMASK: case UA_ATTRIBUTEID_USERWRITEMASK: case UA_ATTRIBUTEID_ISABSTRACT: case UA_ATTRIBUTEID_SYMMETRIC: case UA_ATTRIBUTEID_INVERSENAME: case UA_ATTRIBUTEID_CONTAINSNOLOOPS: case UA_ATTRIBUTEID_EVENTNOTIFIER: retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_VALUE: { if(node->nodeClass != UA_NODECLASS_VARIABLE && node->nodeClass != UA_NODECLASS_VARIABLETYPE) { retval = UA_STATUSCODE_BADTYPEMISMATCH; break; } /* parse the range */ UA_Boolean hasRange = UA_FALSE; UA_NumericRange range; if(wvalue->indexRange.length > 0) { retval = parse_numericrange(wvalue->indexRange, &range); if(retval != UA_STATUSCODE_GOOD) break; hasRange = UA_TRUE; } /* the relevant members are similar for variables and variabletypes */ const UA_VariableNode *vn = (const UA_VariableNode*)node; if(vn->valueSource == UA_VALUESOURCE_DATASOURCE) { if(!vn->value.dataSource.write) { retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; } // todo: writing ranges retval = vn->value.dataSource.write(vn->value.dataSource.handle, &wvalue->value.value); done = UA_TRUE; break; } const UA_Variant *oldV = &vn->value.variant; /* the nodeid on the wire may be != the nodeid in the node: opaque types, enums and bytestrings */ if(!UA_NodeId_equal(&oldV->type->typeId, &wvalue->value.value.type->typeId)) { if(oldV->type->namespaceZero && wvalue->value.value.type->namespaceZero && oldV->type->typeIndex == wvalue->value.value.type->typeIndex) /* An enum was sent as an int32, or an opaque type as a bytestring. This is detected with the typeIndex indicated the "true" datatype. */ wvalue->value.value.type = oldV->type; else if(oldV->type == &UA_TYPES[UA_TYPES_BYTE] && (!oldV->data || vn->value.variant.arrayLength > -1) /* isArray */ && wvalue->value.value.type == &UA_TYPES[UA_TYPES_BYTESTRING] && wvalue->value.value.data && wvalue->value.value.arrayLength == -1 /* isScalar */) { /* a string is written to a byte array */ UA_ByteString *str = (UA_ByteString*) wvalue->value.value.data; wvalue->value.value.arrayLength = str->length; wvalue->value.value.data = str->data; wvalue->value.value.type = &UA_TYPES[UA_TYPES_BYTE]; UA_free(str); } else { retval = UA_STATUSCODE_BADTYPEMISMATCH; break; } } /* copy the node */ UA_VariableNode *newVn = (node->nodeClass == UA_NODECLASS_VARIABLE) ? UA_VariableNode_new() : (UA_VariableNode*)UA_VariableTypeNode_new(); if(!newVn) { retval = UA_STATUSCODE_BADOUTOFMEMORY; break; } retval = (node->nodeClass == UA_NODECLASS_VARIABLE) ? UA_VariableNode_copy(vn, newVn) : UA_VariableTypeNode_copy((const UA_VariableTypeNode*)vn, (UA_VariableTypeNode*)newVn); if(retval != UA_STATUSCODE_GOOD) goto clean_up; /* insert the new value*/ if(hasRange) retval = UA_Variant_setRange(&newVn->value.variant, wvalue->value.value.data, range); else { UA_Variant_deleteMembers(&newVn->value.variant); retval = UA_Variant_copy(&wvalue->value.value, &newVn->value.variant); } if(retval != UA_STATUSCODE_GOOD || UA_NodeStore_replace(server->nodestore, node, (UA_Node*)newVn, UA_NULL) != UA_STATUSCODE_GOOD) goto clean_up; if(hasRange) UA_free(range.dimensions); done = UA_TRUE; break; clean_up: if(node->nodeClass == UA_NODECLASS_VARIABLE) UA_VariableNode_delete(newVn); else UA_VariableTypeNode_delete((UA_VariableTypeNode*)newVn); if(hasRange) UA_free(range.dimensions); } break; case UA_ATTRIBUTEID_DATATYPE: case UA_ATTRIBUTEID_VALUERANK: case UA_ATTRIBUTEID_ARRAYDIMENSIONS: case UA_ATTRIBUTEID_ACCESSLEVEL: case UA_ATTRIBUTEID_USERACCESSLEVEL: case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL: case UA_ATTRIBUTEID_HISTORIZING: case UA_ATTRIBUTEID_EXECUTABLE: case UA_ATTRIBUTEID_USEREXECUTABLE: retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; default: retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID; break; } UA_NodeStore_release(node); if(retval != UA_STATUSCODE_GOOD) break; } return retval; }
static void callMethod(UA_Server *server, UA_Session *session, UA_CallMethodRequest *request, UA_CallMethodResult *result) { const UA_MethodNode *methodCalled = (const UA_MethodNode*)UA_NodeStore_get(server->nodestore, &request->methodId); if(!methodCalled) { result->statusCode = UA_STATUSCODE_BADMETHODINVALID; return; } const UA_ObjectNode *withObject = (const UA_ObjectNode*)UA_NodeStore_get(server->nodestore, &request->objectId); if(!withObject) { result->statusCode = UA_STATUSCODE_BADNODEIDINVALID; goto releaseMethodReturn; } if(methodCalled->nodeClass != UA_NODECLASS_METHOD) { result->statusCode = UA_STATUSCODE_BADNODECLASSINVALID; goto releaseBothReturn; } if(withObject->nodeClass != UA_NODECLASS_OBJECT && withObject->nodeClass != UA_NODECLASS_OBJECTTYPE) { result->statusCode = UA_STATUSCODE_BADNODECLASSINVALID; goto releaseBothReturn; } /* Verify method/object relations */ // Object must have a hasComponent reference (or any inherited referenceType from sayd reference) // to be valid for a methodCall... result->statusCode = UA_STATUSCODE_BADMETHODINVALID; for(UA_Int32 i = 0; i < withObject->referencesSize; i++) { if(withObject->references[i].referenceTypeId.identifier.numeric == UA_NS0ID_HASCOMPONENT) { // FIXME: Not checking any subtypes of HasComponent at the moment if(UA_NodeId_equal(&withObject->references[i].targetId.nodeId, &methodCalled->nodeId)) { result->statusCode = UA_STATUSCODE_GOOD; break; } } } if(result->statusCode != UA_STATUSCODE_GOOD) goto releaseBothReturn; /* Verify method executable */ if(methodCalled->executable == UA_FALSE || methodCalled->userExecutable == UA_FALSE) { result->statusCode = UA_STATUSCODE_BADNOTWRITABLE; // There is no NOTEXECUTABLE? goto releaseBothReturn; } /* Verify Input Argument count, types and sizes */ const UA_VariableNode *inputArguments = getArgumentsVariableNode(server, methodCalled, UA_STRING("InputArguments")); if(inputArguments) { // Expects arguments result->statusCode = argConformsToDefinition(request, inputArguments); UA_NodeStore_release((const UA_Node*)inputArguments); if(result->statusCode != UA_STATUSCODE_GOOD) goto releaseBothReturn; } else if(request->inputArgumentsSize > 0) { // Expects no arguments, but got some result->statusCode = UA_STATUSCODE_BADINVALIDARGUMENT; goto releaseBothReturn; } const UA_VariableNode *outputArguments = getArgumentsVariableNode(server, methodCalled, UA_STRING("OutputArguments")); if(!outputArguments) { // A MethodNode must have an OutputArguments variable (which may be empty) result->statusCode = UA_STATUSCODE_BADINTERNALERROR; goto releaseBothReturn; } // Call method if available if(methodCalled->attachedMethod) { result->outputArguments = UA_Array_new(&UA_TYPES[UA_TYPES_VARIANT], outputArguments->value.variant.value.arrayLength); result->outputArgumentsSize = outputArguments->value.variant.value.arrayLength; result->statusCode = methodCalled->attachedMethod(withObject->nodeId, request->inputArguments, result->outputArguments, methodCalled->methodHandle); } else result->statusCode = UA_STATUSCODE_BADNOTWRITABLE; // There is no NOTEXECUTABLE? /* FIXME: Verify Output Argument count, types and sizes */ if(outputArguments) UA_NodeStore_release((const UA_Node*)outputArguments); releaseBothReturn: UA_NodeStore_release((const UA_Node*)withObject); releaseMethodReturn: UA_NodeStore_release((const UA_Node*)methodCalled); }
OV_DLLFNCEXPORT UA_StatusCode propertyValueStatementOPCUAInterface_interface_ovExpressionSemanticNodeToOPCUA( void *handle, const UA_NodeId *nodeId, UA_Node** opcuaNode) { UA_Node *newNode = NULL; UA_StatusCode result = UA_STATUSCODE_GOOD; OV_PATH path; OV_INSTPTR_ov_object pobj = NULL; OV_TICKET *pTicket = NULL; OV_VTBLPTR_ov_object pVtblObj = NULL; OV_ACCESS access; UA_NodeClass nodeClass; OV_ELEMENT element; ov_memstack_lock(); result = opcua_nodeStoreFunctions_resolveNodeIdToPath(*nodeId, &path); if(result != UA_STATUSCODE_GOOD){ ov_memstack_unlock(); return result; } element = path.elements[path.size-1]; ov_memstack_unlock(); result = opcua_nodeStoreFunctions_getVtblPointerAndCheckAccess(&(element), pTicket, &pobj, &pVtblObj, &access); if(result != UA_STATUSCODE_GOOD){ return result; } nodeClass = UA_NODECLASS_VARIABLE; newNode = (UA_Node*)UA_calloc(1, sizeof(UA_VariableNode)); // Basic Attribute // BrowseName UA_QualifiedName qName; qName.name = UA_String_fromChars(pobj->v_identifier); qName.namespaceIndex = nodeId->namespaceIndex; newNode->browseName = qName; // Description // Description OV_STRING tempString = pVtblObj->m_getcomment(pobj, &element); UA_LocalizedText lText; UA_LocalizedText_init(&lText); lText.locale = UA_String_fromChars("en"); if(tempString){ lText.text = UA_String_fromChars(tempString); } else { lText.text = UA_String_fromChars(""); } UA_LocalizedText_copy(&lText,&newNode->description); UA_LocalizedText_deleteMembers(&lText); // DisplayName UA_LocalizedText displayName; UA_LocalizedText_init(&displayName); displayName.locale = UA_String_fromChars("en"); displayName.text = UA_String_fromChars(pobj->v_identifier); UA_LocalizedText_copy(&displayName, &newNode->displayName); UA_LocalizedText_deleteMembers(&displayName); // NodeId UA_NodeId_copy(nodeId, &newNode->nodeId); // NodeClass newNode->nodeClass = nodeClass; // WriteMask UA_UInt32 writeMask = 0; if(element.elemtype != OV_ET_VARIABLE){ if(access & OV_AC_WRITE){ writeMask |= (1<<2); // BrowseName writeMask |= (1<<6); // DisplayName } if(access & OV_AC_RENAMEABLE){ writeMask |= (1<<14); // NodeId } } newNode->writeMask = writeMask; // Variable specific attributes // arrayDemensions ((UA_VariableNode*)newNode)->arrayDimensionsSize = 0; ((UA_VariableNode*)newNode)->arrayDimensions = NULL; // UA_Array_new(((UA_VariableNode*)newNode)->arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]); /* scalar or one dimension */ // valuerank ((UA_VariableNode*)newNode)->valueRank = 1; /* one dimension */ // value OV_ELEMENT tmpPart; tmpPart.elemtype = OV_ET_NONE; OV_ELEMENT tmpParrent; tmpParrent.pobj = pobj; tmpParrent.elemtype = OV_ET_OBJECT; UA_ExpressionSemanticEnum tmpExpressionSemantic = 0; do { ov_element_getnextpart(&tmpParrent, &tmpPart, OV_ET_VARIABLE); if (tmpPart.elemtype == OV_ET_NONE) break; if (ov_string_compare(tmpPart.elemunion.pvar->v_identifier, "ExpressionSemanticEnum") == OV_STRCMP_EQUAL){ tmpExpressionSemantic = *(UA_UInt32*)tmpPart.pvalue; break; } } while(TRUE); ((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->type = &UA_PROPERTYVALUESTATEMENT[UA_PROPERTYVALUESTATEMENT_EXPRESSIONSEMANTICENUM]; ((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data = UA_ExpressionSemanticEnum_new(); if (!((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data){ result = UA_STATUSCODE_BADOUTOFMEMORY; return result; } ((UA_VariableNode*)newNode)->value.data.value.hasValue = TRUE; ((UA_VariableNode*)newNode)->valueSource = UA_VALUESOURCE_DATA; UA_ExpressionSemanticEnum_copy(&tmpExpressionSemantic, ((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data); UA_ExpressionSemanticEnum_deleteMembers(&tmpExpressionSemantic); // accessLevel UA_Byte accessLevel = 0; if(access & OV_AC_READ){ accessLevel |= (1<<0); } if(access & OV_AC_WRITE){ accessLevel |= (1<<1); } ((UA_VariableNode*)newNode)->accessLevel = accessLevel; // minimumSamplingInterval ((UA_VariableNode*)newNode)->minimumSamplingInterval = -1; // historizing ((UA_VariableNode*)newNode)->historizing = UA_FALSE; // dataType ((UA_VariableNode*)newNode)->dataType = UA_NODEID_NUMERIC(pinterface->v_modelnamespace.index, UA_NSPROPERTYVALUESTATEMENTID_EXPRESSIONSEMANTICENUM); // References addReference(newNode); OV_UINT len = 0; OV_STRING *plist = NULL; OV_STRING tmpString = NULL; copyOPCUAStringToOV(nodeId->identifier.string, &tmpString); plist = ov_string_split(tmpString, "/", &len); ov_string_setvalue(&tmpString, plist[0]); for (OV_UINT i = 1; i < len-1; i++){ ov_string_append(&tmpString, "/"); ov_string_append(&tmpString, plist[i]); } ov_string_freelist(plist); UA_NodeId tmpNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION); for (size_t i = 0; i < newNode->referencesSize; i++){ if (UA_NodeId_equal(&newNode->references[i].referenceTypeId, &tmpNodeId)){ newNode->references[i].targetId = UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE); continue; } OV_STRING tmpString2 = NULL; copyOPCUAStringToOV(newNode->references[i].targetId.nodeId.identifier.string, &tmpString2); if (ov_string_compare(tmpString, tmpString2) == OV_STRCMP_EQUAL && newNode->references[i].isInverse == UA_TRUE){ newNode->references[i].referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY); } ov_string_setvalue(&tmpString2, NULL); } ov_string_setvalue(&tmpString, NULL); UA_NodeId_deleteMembers(&tmpNodeId); *opcuaNode = newNode; return UA_STATUSCODE_GOOD; }
/* We are in a rcu_read lock. So the node will not be freed under our feet. */ static int compare(struct cds_lfht_node *htn, const void *orig) { const UA_NodeId *origid = (const UA_NodeId *)orig; const UA_NodeId *newid = &((struct nodeEntry *)htn)->node.nodeId; /* The htn is first in the entry structure. */ return UA_NodeId_equal(newid, origid); }
bool UaNodeId::operator==(const UaNodeId& other) const { return UA_NodeId_equal( &m_impl, &other.m_impl ); }
void Service_Call(UA_Server *server, UA_Session *session, const UA_CallRequest *request, UA_CallResponse *response) { if(request->methodsToCallSize <= 0) { response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO; return; } response->results = UA_Array_new(&UA_TYPES[UA_TYPES_CALLMETHODRESULT], request->methodsToCallSize); if(!response->results) { response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY; return; } response->resultsSize = request->methodsToCallSize; for(UA_Int32 i = 0; i < request->methodsToCallSize;i++) { UA_CallMethodRequest *rq = &request->methodsToCall[i]; UA_CallMethodResult *rs = &response->results[i]; /* Get/Check Nodes */ const UA_MethodNode *methodCalled = (const UA_MethodNode*) UA_NodeStore_get(server->nodestore, &rq->methodId); if(methodCalled == UA_NULL) { rs->statusCode = UA_STATUSCODE_BADMETHODINVALID; continue; } const UA_ObjectNode *withObject = (const UA_ObjectNode *) UA_NodeStore_get(server->nodestore, &rq->objectId); if(withObject == UA_NULL) { rs->statusCode = UA_STATUSCODE_BADNODEIDINVALID; printf("Obj not found\n"); continue; } if(methodCalled->nodeClass != UA_NODECLASS_METHOD) { rs->statusCode = UA_STATUSCODE_BADNODECLASSINVALID; continue; } if(withObject->nodeClass != UA_NODECLASS_OBJECT && withObject->nodeClass != UA_NODECLASS_OBJECTTYPE) { rs->statusCode = UA_STATUSCODE_BADNODECLASSINVALID; printf("Obj not found 1\n"); continue; } /* Verify method/object relations */ // Object must have a hasComponent reference (or any inherited referenceType from sayd reference) // to be valid for a methodCall... for(UA_Int32 i = 0; i < withObject->referencesSize; i++) { if(withObject->references[i].referenceTypeId.identifier.numeric == UA_NS0ID_HASCOMPONENT) { // FIXME: Not checking any subtypes of HasComponent at the moment if(UA_NodeId_equal(&withObject->references[i].targetId.nodeId, &methodCalled->nodeId)) { rs->statusCode = UA_STATUSCODE_GOOD; break; } } } if(rs->statusCode != UA_STATUSCODE_GOOD) continue; /* Verify method executable */ if(((const UA_MethodNode *) methodCalled)->executable == UA_FALSE || ((const UA_MethodNode *) methodCalled)->userExecutable == UA_FALSE ) { rs->statusCode = UA_STATUSCODE_BADNOTWRITABLE; // There is no NOTEXECUTABLE? continue; } /* Verify Input Argument count, types and sizes */ const UA_VariableNode *inputArguments = getArgumentsVariableNode(server, methodCalled, UA_STRING("InputArguments")); if(inputArguments) { // Expects arguments rs->statusCode = argConformsToDefinition(rq, inputArguments); UA_NodeStore_release((const UA_Node*)inputArguments); if(rs->statusCode != UA_STATUSCODE_GOOD) continue; } else if(rq->inputArgumentsSize > 0) { // Expects no arguments, but got some rs->statusCode = UA_STATUSCODE_BADINVALIDARGUMENT; UA_NodeStore_release((const UA_Node*)inputArguments); continue; } const UA_VariableNode *outputArguments = getArgumentsVariableNode(server, methodCalled, UA_STRING("OutputArguments")); if(!outputArguments) { // A MethodNode must have an OutputArguments variable (which may be empty) rs->statusCode = UA_STATUSCODE_BADINTERNALERROR; continue; } // Call method if available if(methodCalled->attachedMethod) { rs->outputArguments = UA_Array_new(&UA_TYPES[UA_TYPES_VARIANT], outputArguments->value.variant.arrayLength); rs->outputArgumentsSize = outputArguments->value.variant.arrayLength; rs->statusCode = methodCalled->attachedMethod(withObject->nodeId, rq->inputArguments, rs->outputArguments); } else rs->statusCode = UA_STATUSCODE_BADNOTWRITABLE; // There is no NOTEXECUTABLE? /* FIXME: Verify Output Argument count, types and sizes */ if(outputArguments) { UA_NodeStore_release((const UA_Node*)outputArguments); } UA_NodeStore_release((const UA_Node *)withObject); UA_NodeStore_release((const UA_Node *)methodCalled); } }
static UA_Boolean isNodeInTreeNoCircular(void *nsCtx, const UA_NodeId *leafNode, const UA_NodeId *nodeToFind, struct ref_history *visitedRefs, const UA_NodeId *referenceTypeIds, size_t referenceTypeIdsSize) { if(UA_NodeId_equal(nodeToFind, leafNode)) return true; if(visitedRefs->depth >= UA_MAX_TREE_RECURSE) return false; const UA_Node *node = UA_Nodestore_getNode(nsCtx, leafNode); if(!node) return false; for(size_t i = 0; i < node->referencesSize; ++i) { UA_NodeReferenceKind *refs = &node->references[i]; /* Search upwards in the tree */ if(!refs->isInverse) continue; /* Consider only the indicated reference types */ UA_Boolean match = false; for(size_t j = 0; j < referenceTypeIdsSize; ++j) { if(UA_NodeId_equal(&refs->referenceTypeId, &referenceTypeIds[j])) { match = true; break; } } if(!match) continue; /* Match the targets or recurse */ for(size_t j = 0; j < refs->targetIdsSize; ++j) { /* Check if we already have seen the referenced node and skip to * avoid endless recursion. Do this only at every 5th depth to save * effort. Circular dependencies are rare and forbidden for most * reference types. */ if(visitedRefs->depth % 5 == 4) { struct ref_history *last = visitedRefs; UA_Boolean skip = false; while(!skip && last) { if(UA_NodeId_equal(last->id, &refs->targetIds[j].nodeId)) skip = true; last = last->parent; } if(skip) continue; } /* Stack-allocate the visitedRefs structure for the next depth */ struct ref_history nextVisitedRefs = {visitedRefs, &refs->targetIds[j].nodeId, (UA_UInt16)(visitedRefs->depth+1)}; /* Recurse */ UA_Boolean foundRecursive = isNodeInTreeNoCircular(nsCtx, &refs->targetIds[j].nodeId, nodeToFind, &nextVisitedRefs, referenceTypeIds, referenceTypeIdsSize); if(foundRecursive) { UA_Nodestore_releaseNode(nsCtx, node); return true; } } } UA_Nodestore_releaseNode(nsCtx, node); return false; }
static UA_StatusCode SecureChannelHandshake(UA_Client *client, UA_Boolean renew) { /* Check if sc is still valid */ if(renew && client->scExpiresAt - UA_DateTime_now() > client->config.timeToRenewSecureChannel * 10000) return UA_STATUSCODE_GOOD; UA_SecureConversationMessageHeader messageHeader; messageHeader.messageHeader.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_OPNF; messageHeader.secureChannelId = 0; UA_SequenceHeader seqHeader; seqHeader.sequenceNumber = ++client->channel.sequenceNumber; seqHeader.requestId = ++client->requestId; UA_AsymmetricAlgorithmSecurityHeader asymHeader; UA_AsymmetricAlgorithmSecurityHeader_init(&asymHeader); asymHeader.securityPolicyUri = UA_STRING_ALLOC("http://opcfoundation.org/UA/SecurityPolicy#None"); /* id of opensecurechannelrequest */ UA_NodeId requestType = UA_NODEID_NUMERIC(0, UA_NS0ID_OPENSECURECHANNELREQUEST + UA_ENCODINGOFFSET_BINARY); UA_OpenSecureChannelRequest opnSecRq; UA_OpenSecureChannelRequest_init(&opnSecRq); opnSecRq.requestHeader.timestamp = UA_DateTime_now(); opnSecRq.requestHeader.authenticationToken = client->authenticationToken; opnSecRq.requestedLifetime = client->config.secureChannelLifeTime; if(renew) { opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_RENEW; UA_LOG_DEBUG(client->logger, UA_LOGCATEGORY_SECURECHANNEL, "Requesting to renew the SecureChannel"); } else { opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_ISSUE; UA_ByteString_init(&client->channel.clientNonce); UA_ByteString_copy(&client->channel.clientNonce, &opnSecRq.clientNonce); opnSecRq.securityMode = UA_MESSAGESECURITYMODE_NONE; UA_LOG_DEBUG(client->logger, UA_LOGCATEGORY_SECURECHANNEL, "Requesting to open a SecureChannel"); } UA_ByteString message; UA_Connection *c = &client->connection; UA_StatusCode retval = c->getSendBuffer(c, c->remoteConf.recvBufferSize, &message); if(retval != UA_STATUSCODE_GOOD) { UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader); UA_OpenSecureChannelRequest_deleteMembers(&opnSecRq); return retval; } size_t offset = 12; retval = UA_AsymmetricAlgorithmSecurityHeader_encodeBinary(&asymHeader, &message, &offset); retval |= UA_SequenceHeader_encodeBinary(&seqHeader, &message, &offset); retval |= UA_NodeId_encodeBinary(&requestType, &message, &offset); retval |= UA_OpenSecureChannelRequest_encodeBinary(&opnSecRq, &message, &offset); messageHeader.messageHeader.messageSize = offset; offset = 0; retval |= UA_SecureConversationMessageHeader_encodeBinary(&messageHeader, &message, &offset); UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader); UA_OpenSecureChannelRequest_deleteMembers(&opnSecRq); if(retval != UA_STATUSCODE_GOOD) { client->connection.releaseSendBuffer(&client->connection, &message); return retval; } message.length = messageHeader.messageHeader.messageSize; retval = client->connection.send(&client->connection, &message); if(retval != UA_STATUSCODE_GOOD) return retval; UA_ByteString reply; UA_ByteString_init(&reply); do { retval = client->connection.recv(&client->connection, &reply, client->config.timeout); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_DEBUG(client->logger, UA_LOGCATEGORY_SECURECHANNEL, "Receiving OpenSecureChannelResponse failed"); return retval; } } while(!reply.data); offset = 0; UA_SecureConversationMessageHeader_decodeBinary(&reply, &offset, &messageHeader); UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(&reply, &offset, &asymHeader); UA_SequenceHeader_decodeBinary(&reply, &offset, &seqHeader); UA_NodeId_decodeBinary(&reply, &offset, &requestType); UA_NodeId expectedRequest = UA_NODEID_NUMERIC(0, UA_NS0ID_OPENSECURECHANNELRESPONSE + UA_ENCODINGOFFSET_BINARY); if(!UA_NodeId_equal(&requestType, &expectedRequest)) { UA_ByteString_deleteMembers(&reply); UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader); UA_NodeId_deleteMembers(&requestType); UA_LOG_DEBUG(client->logger, UA_LOGCATEGORY_CLIENT, "Reply answers the wrong request. Expected OpenSecureChannelResponse."); return UA_STATUSCODE_BADINTERNALERROR; } UA_OpenSecureChannelResponse response; UA_OpenSecureChannelResponse_init(&response); retval = UA_OpenSecureChannelResponse_decodeBinary(&reply, &offset, &response); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_DEBUG(client->logger, UA_LOGCATEGORY_SECURECHANNEL, "Decoding OpenSecureChannelResponse failed"); UA_ByteString_deleteMembers(&reply); UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader); UA_OpenSecureChannelResponse_init(&response); response.responseHeader.serviceResult = retval; return retval; } client->scExpiresAt = UA_DateTime_now() + response.securityToken.revisedLifetime * 10000; UA_ByteString_deleteMembers(&reply); retval = response.responseHeader.serviceResult; if(retval != UA_STATUSCODE_GOOD) UA_LOG_DEBUG(client->logger, UA_LOGCATEGORY_SECURECHANNEL, "SecureChannel could not be opened / renewed"); else if(!renew) { UA_ChannelSecurityToken_copy(&response.securityToken, &client->channel.securityToken); /* if the handshake is repeated, replace the old nonce */ UA_ByteString_deleteMembers(&client->channel.serverNonce); UA_ByteString_copy(&response.serverNonce, &client->channel.serverNonce); UA_LOG_DEBUG(client->logger, UA_LOGCATEGORY_SECURECHANNEL, "SecureChannel opened"); } else UA_LOG_DEBUG(client->logger, UA_LOGCATEGORY_SECURECHANNEL, "SecureChannel renewed"); UA_OpenSecureChannelResponse_deleteMembers(&response); UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader); return retval; }
void __UA_Client_Service(UA_Client *client, const void *r, const UA_DataType *requestType, void *response, const UA_DataType *responseType) { /* Requests always begin witih a RequestHeader, therefore we can cast. */ UA_RequestHeader *request = (void*)(uintptr_t)r; UA_StatusCode retval = UA_STATUSCODE_GOOD; UA_init(response, responseType); UA_ResponseHeader *respHeader = (UA_ResponseHeader*)response; /* make sure we have a valid session */ retval = UA_Client_manuallyRenewSecureChannel(client); if(retval != UA_STATUSCODE_GOOD) { respHeader->serviceResult = retval; client->state = UA_CLIENTSTATE_ERRORED; return; } /* handling request parameters */ UA_NodeId_copy(&client->authenticationToken, &request->authenticationToken); request->timestamp = UA_DateTime_now(); request->requestHandle = ++client->requestHandle; /* Send the request */ UA_UInt32 requestId = ++client->requestId; UA_LOG_DEBUG(client->logger, UA_LOGCATEGORY_CLIENT, "Sending a request of type %i", requestType->typeId.identifier.numeric); retval = UA_SecureChannel_sendBinaryMessage(&client->channel, requestId, request, requestType); if(retval) { if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED) respHeader->serviceResult = UA_STATUSCODE_BADREQUESTTOOLARGE; else respHeader->serviceResult = retval; client->state = UA_CLIENTSTATE_ERRORED; return; } /* Retrieve the response */ // Todo: push this into the generic securechannel implementation for client and server UA_ByteString reply; UA_ByteString_init(&reply); do { retval = client->connection.recv(&client->connection, &reply, client->config.timeout); if(retval != UA_STATUSCODE_GOOD) { respHeader->serviceResult = retval; client->state = UA_CLIENTSTATE_ERRORED; return; } } while(!reply.data); size_t offset = 0; UA_SecureConversationMessageHeader msgHeader; retval |= UA_SecureConversationMessageHeader_decodeBinary(&reply, &offset, &msgHeader); UA_SymmetricAlgorithmSecurityHeader symHeader; retval |= UA_SymmetricAlgorithmSecurityHeader_decodeBinary(&reply, &offset, &symHeader); UA_SequenceHeader seqHeader; retval |= UA_SequenceHeader_decodeBinary(&reply, &offset, &seqHeader); UA_NodeId responseId; retval |= UA_NodeId_decodeBinary(&reply, &offset, &responseId); UA_NodeId expectedNodeId = UA_NODEID_NUMERIC(0, responseType->typeId.identifier.numeric + UA_ENCODINGOFFSET_BINARY); if(retval != UA_STATUSCODE_GOOD) { goto finish; } /* Todo: we need to demux responses since a publish responses may come at any time */ if(!UA_NodeId_equal(&responseId, &expectedNodeId) || seqHeader.requestId != requestId) { if(responseId.identifier.numeric != UA_NS0ID_SERVICEFAULT + UA_ENCODINGOFFSET_BINARY) { UA_LOG_ERROR(client->logger, UA_LOGCATEGORY_CLIENT, "Reply answers the wrong request. Expected ns=%i,i=%i. But retrieved ns=%i,i=%i", expectedNodeId.namespaceIndex, expectedNodeId.identifier.numeric, responseId.namespaceIndex, responseId.identifier.numeric); respHeader->serviceResult = UA_STATUSCODE_BADINTERNALERROR; } else retval = UA_decodeBinary(&reply, &offset, respHeader, &UA_TYPES[UA_TYPES_SERVICEFAULT]); goto finish; } retval = UA_decodeBinary(&reply, &offset, response, responseType); if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED) retval = UA_STATUSCODE_BADRESPONSETOOLARGE; finish: UA_SymmetricAlgorithmSecurityHeader_deleteMembers(&symHeader); UA_ByteString_deleteMembers(&reply); if(retval != UA_STATUSCODE_GOOD){ UA_LOG_INFO(client->logger, UA_LOGCATEGORY_CLIENT, "Error receiving the response"); client->state = UA_CLIENTSTATE_ERRORED; respHeader->serviceResult = retval; } UA_LOG_DEBUG(client->logger, UA_LOGCATEGORY_CLIENT, "Received a response of type %i", responseId.identifier.numeric); }
} END_TEST START_TEST(DeleteObjectAndReferences) { /* Add an object of the type */ UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("en-US","my object"); UA_NodeId objectid = UA_NODEID_NUMERIC(0, 23372337); UA_StatusCode res; res = UA_Server_addObjectNode(server, objectid, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(0, ""), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE), attr, NULL, NULL); ck_assert_int_eq(res, UA_STATUSCODE_GOOD); /* Verify that we have a reference to the node from the objects folder */ UA_BrowseDescription bd; UA_BrowseDescription_init(&bd); bd.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER); bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT); bd.browseDirection = UA_BROWSEDIRECTION_FORWARD; UA_BrowseResult br = UA_Server_browse(server, 0, &bd); ck_assert_int_eq(br.statusCode, UA_STATUSCODE_GOOD); size_t refCount = 0; for(size_t i = 0; i < br.referencesSize; ++i) { if(UA_NodeId_equal(&br.references[i].nodeId.nodeId, &objectid)) refCount++; } ck_assert_int_eq(refCount, 1); UA_BrowseResult_deleteMembers(&br); /* Delete the object */ UA_Server_deleteNode(server, objectid, true); /* Browse again, this time we expect that no reference is found */ br = UA_Server_browse(server, 0, &bd); ck_assert_int_eq(br.statusCode, UA_STATUSCODE_GOOD); refCount = 0; for(size_t i = 0; i < br.referencesSize; ++i) { if(UA_NodeId_equal(&br.references[i].nodeId.nodeId, &objectid)) refCount++; } ck_assert_int_eq(refCount, 0); UA_BrowseResult_deleteMembers(&br); /* Add an object the second time */ attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("en-US","my object"); objectid = UA_NODEID_NUMERIC(0, 23372337); res = UA_Server_addObjectNode(server, objectid, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(0, ""), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE), attr, NULL, NULL); ck_assert_int_eq(res, UA_STATUSCODE_GOOD); /* Browse again, this time we expect that a single reference to the node is found */ refCount = 0; br = UA_Server_browse(server, 0, &bd); ck_assert_int_eq(br.statusCode, UA_STATUSCODE_GOOD); for(size_t i = 0; i < br.referencesSize; ++i) { if(UA_NodeId_equal(&br.references[i].nodeId.nodeId, &objectid)) refCount++; } ck_assert_int_eq(refCount, 1); UA_BrowseResult_deleteMembers(&br); } END_TEST