/* THIS IS A SINGLE-FILE DISTRIBUTION CONCATENATED FROM THE OPEN62541 SOURCES * visit http://open62541.org/ for information about this software * Git-Revision: v1.0 */ /* * Copyright (C) 2014-2018 the contributors as stated in the AUTHORS file * * This file is part of open62541. open62541 is free software: you can * redistribute it and/or modify it under the terms of the Mozilla Public * License v2.0 as stated in the LICENSE file provided with open62541. * * open62541 is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. */ #ifndef UA_DYNAMIC_LINKING_EXPORT # define UA_DYNAMIC_LINKING_EXPORT # define MDNSD_DYNAMIC_LINKING #endif /* Disable security warnings for BSD sockets on MSVC */ #ifdef _MSC_VER # define _CRT_SECURE_NO_WARNINGS #endif #include "open62541.h" /*********************************** amalgamated original file "/home/jvoe/open62541/deps/open62541_queue.h" ***********************************/ /* $OpenBSD: queue.h,v 1.38 2013/07/03 15:05:21 fgsch Exp $ */ /* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ /* * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)queue.h 8.5 (Berkeley) 8/20/94 */ /* * This file defines five types of data structures: singly-linked lists, * lists, simple queues, tail queues, and circular queues. * * * A singly-linked list is headed by a single forward pointer. The elements * are singly linked for minimum space and pointer manipulation overhead at * the expense of O(n) removal for arbitrary elements. New elements can be * added to the list after an existing element or at the head of the list. * Elements being removed from the head of the list should use the explicit * macro for this purpose for optimum efficiency. A singly-linked list may * only be traversed in the forward direction. Singly-linked lists are ideal * for applications with large datasets and few or no removals or for * implementing a LIFO queue. * * A list is headed by a single forward pointer (or an array of forward * pointers for a hash table header). The elements are doubly linked * so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before * or after an existing element or at the head of the list. A list * may only be traversed in the forward direction. * * A simple queue is headed by a pair of pointers, one the head of the * list and the other to the tail of the list. The elements are singly * linked to save space, so elements can only be removed from the * head of the list. New elements can be added to the list before or after * an existing element, at the head of the list, or at the end of the * list. A simple queue may only be traversed in the forward direction. * * A tail queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or * after an existing element, at the head of the list, or at the end of * the list. A tail queue may be traversed in either direction. * * A circle queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or after * an existing element, at the head of the list, or at the end of the list. * A circle queue may be traversed in either direction, but has a more * complex end of list detection. * * For details on the use of these macros, see the queue(3) manual page. */ #if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) #define _Q_INVALIDATE(a) (a) = ((void *)-1) #else #define _Q_INVALIDATE(a) #endif /* * Singly-linked List definitions. */ #define SLIST_HEAD(name, type) \ struct name { \ struct type *slh_first; /* first element */ \ } #define SLIST_HEAD_INITIALIZER(head) \ { NULL } #define SLIST_ENTRY(type) \ struct { \ struct type *sle_next; /* next element */ \ } /* * Singly-linked List access methods. */ #define SLIST_FIRST(head) ((head)->slh_first) #define SLIST_END(head) NULL #define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) #define SLIST_FOREACH(var, head, field) \ for((var) = SLIST_FIRST(head); \ (var) != SLIST_END(head); \ (var) = SLIST_NEXT(var, field)) #define SLIST_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = SLIST_FIRST(head); \ (var) && ((tvar) = SLIST_NEXT(var, field), 1); \ (var) = (tvar)) /* * Singly-linked List functions. */ #define SLIST_INIT(head) { \ SLIST_FIRST(head) = SLIST_END(head); \ } #define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ (elm)->field.sle_next = (slistelm)->field.sle_next; \ (slistelm)->field.sle_next = (elm); \ } while (0) #define SLIST_INSERT_HEAD(head, elm, field) do { \ (elm)->field.sle_next = (head)->slh_first; \ (head)->slh_first = (elm); \ } while (0) #define SLIST_REMOVE_AFTER(elm, field) do { \ (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ } while (0) #define SLIST_REMOVE_HEAD(head, field) do { \ (head)->slh_first = (head)->slh_first->field.sle_next; \ } while (0) #define SLIST_REMOVE(head, elm, type, field) do { \ if ((head)->slh_first == (elm)) { \ SLIST_REMOVE_HEAD((head), field); \ } else { \ struct type *curelm = (head)->slh_first; \ \ while (curelm->field.sle_next != (elm)) \ curelm = curelm->field.sle_next; \ curelm->field.sle_next = \ curelm->field.sle_next->field.sle_next; \ _Q_INVALIDATE((elm)->field.sle_next); \ } \ } while (0) /* * List definitions. */ #define LIST_HEAD(name, type) \ struct name { \ struct type *lh_first; /* first element */ \ } #define LIST_HEAD_INITIALIZER(head) \ { NULL } #define LIST_ENTRY(type) \ struct { \ struct type *le_next; /* next element */ \ struct type **le_prev; /* address of previous next element */ \ } /* * List access methods */ #define LIST_FIRST(head) ((head)->lh_first) #define LIST_END(head) NULL #define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) #define LIST_NEXT(elm, field) ((elm)->field.le_next) #define LIST_FOREACH(var, head, field) \ for((var) = LIST_FIRST(head); \ (var)!= LIST_END(head); \ (var) = LIST_NEXT(var, field)) #define LIST_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = LIST_FIRST(head); \ (var) && ((tvar) = LIST_NEXT(var, field), 1); \ (var) = (tvar)) /* * List functions. */ #define LIST_INIT(head) do { \ LIST_FIRST(head) = LIST_END(head); \ } while (0) #define LIST_INSERT_AFTER(listelm, elm, field) do { \ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ (listelm)->field.le_next->field.le_prev = \ &(elm)->field.le_next; \ (listelm)->field.le_next = (elm); \ (elm)->field.le_prev = &(listelm)->field.le_next; \ } while (0) #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.le_prev = (listelm)->field.le_prev; \ (elm)->field.le_next = (listelm); \ *(listelm)->field.le_prev = (elm); \ (listelm)->field.le_prev = &(elm)->field.le_next; \ } while (0) #define LIST_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.le_next = (head)->lh_first) != NULL) \ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ (head)->lh_first = (elm); \ (elm)->field.le_prev = &(head)->lh_first; \ } while (0) #define LIST_REMOVE(elm, field) do { \ if ((elm)->field.le_next != NULL) \ (elm)->field.le_next->field.le_prev = \ (elm)->field.le_prev; \ *(elm)->field.le_prev = (elm)->field.le_next; \ _Q_INVALIDATE((elm)->field.le_prev); \ _Q_INVALIDATE((elm)->field.le_next); \ } while (0) #define LIST_REPLACE(elm, elm2, field) do { \ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ (elm2)->field.le_next->field.le_prev = \ &(elm2)->field.le_next; \ (elm2)->field.le_prev = (elm)->field.le_prev; \ *(elm2)->field.le_prev = (elm2); \ _Q_INVALIDATE((elm)->field.le_prev); \ _Q_INVALIDATE((elm)->field.le_next); \ } while (0) /* * Simple queue definitions. */ #define SIMPLEQ_HEAD(name, type) \ struct name { \ struct type *sqh_first; /* first element */ \ struct type **sqh_last; /* addr of last next element */ \ } #define SIMPLEQ_HEAD_INITIALIZER(head) \ { NULL, &(head).sqh_first } #define SIMPLEQ_ENTRY(type) \ struct { \ struct type *sqe_next; /* next element */ \ } /* * Simple queue access methods. */ #define SIMPLEQ_FIRST(head) ((head)->sqh_first) #define SIMPLEQ_END(head) NULL #define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) #define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) #define SIMPLEQ_FOREACH(var, head, field) \ for((var) = SIMPLEQ_FIRST(head); \ (var) != SIMPLEQ_END(head); \ (var) = SIMPLEQ_NEXT(var, field)) #define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = SIMPLEQ_FIRST(head); \ (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \ (var) = (tvar)) /* * Simple queue functions. */ #define SIMPLEQ_INIT(head) do { \ (head)->sqh_first = NULL; \ (head)->sqh_last = &(head)->sqh_first; \ } while (0) #define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ (head)->sqh_last = &(elm)->field.sqe_next; \ (head)->sqh_first = (elm); \ } while (0) #define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.sqe_next = NULL; \ *(head)->sqh_last = (elm); \ (head)->sqh_last = &(elm)->field.sqe_next; \ } while (0) #define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ (head)->sqh_last = &(elm)->field.sqe_next; \ (listelm)->field.sqe_next = (elm); \ } while (0) #define SIMPLEQ_REMOVE_HEAD(head, field) do { \ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ (head)->sqh_last = &(head)->sqh_first; \ } while (0) #define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ == NULL) \ (head)->sqh_last = &(elm)->field.sqe_next; \ } while (0) /* * XOR Simple queue definitions. */ #define XSIMPLEQ_HEAD(name, type) \ struct name { \ struct type *sqx_first; /* first element */ \ struct type **sqx_last; /* addr of last next element */ \ unsigned long sqx_cookie; \ } #define XSIMPLEQ_ENTRY(type) \ struct { \ struct type *sqx_next; /* next element */ \ } /* * XOR Simple queue access methods. */ #define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \ (unsigned long)(ptr))) #define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first)) #define XSIMPLEQ_END(head) NULL #define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head)) #define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next)) #define XSIMPLEQ_FOREACH(var, head, field) \ for ((var) = XSIMPLEQ_FIRST(head); \ (var) != XSIMPLEQ_END(head); \ (var) = XSIMPLEQ_NEXT(head, var, field)) #define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = XSIMPLEQ_FIRST(head); \ (var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \ (var) = (tvar)) /* * XOR Simple queue functions. */ #define XSIMPLEQ_INIT(head) do { \ arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \ (head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ } while (0) #define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.sqx_next = (head)->sqx_first) == \ XSIMPLEQ_XOR(head, NULL)) \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ (head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \ } while (0) #define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \ *(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ } while (0) #define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \ XSIMPLEQ_XOR(head, NULL)) \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ (listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \ } while (0) #define XSIMPLEQ_REMOVE_HEAD(head, field) do { \ if (((head)->sqx_first = XSIMPLEQ_XOR(head, \ (head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ } while (0) #define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \ (elm)->field.sqx_next)->field.sqx_next) \ == XSIMPLEQ_XOR(head, NULL)) \ (head)->sqx_last = \ XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ } while (0) /* * Tail queue definitions. */ #define TAILQ_HEAD(name, type) \ struct name { \ struct type *tqh_first; /* first element */ \ struct type **tqh_last; /* addr of last next element */ \ } #define TAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).tqh_first } #define TAILQ_ENTRY(type) \ struct { \ struct type *tqe_next; /* next element */ \ struct type **tqe_prev; /* address of previous next element */ \ } /* * tail queue access methods */ #define TAILQ_FIRST(head) ((head)->tqh_first) #define TAILQ_END(head) NULL #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) #define TAILQ_LAST(head, headname) \ (*(((struct headname *)((head)->tqh_last))->tqh_last)) /* XXX */ #define TAILQ_PREV(elm, headname, field) \ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) #define TAILQ_EMPTY(head) \ (TAILQ_FIRST(head) == TAILQ_END(head)) #define TAILQ_FOREACH(var, head, field) \ for((var) = TAILQ_FIRST(head); \ (var) != TAILQ_END(head); \ (var) = TAILQ_NEXT(var, field)) #define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = TAILQ_FIRST(head); \ (var) != TAILQ_END(head) && \ ((tvar) = TAILQ_NEXT(var, field), 1); \ (var) = (tvar)) #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ for((var) = TAILQ_LAST(head, headname); \ (var) != TAILQ_END(head); \ (var) = TAILQ_PREV(var, headname, field)) #define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ for ((var) = TAILQ_LAST(head, headname); \ (var) != TAILQ_END(head) && \ ((tvar) = TAILQ_PREV(var, headname, field), 1); \ (var) = (tvar)) /* * Tail queue functions. */ #define TAILQ_INIT(head) do { \ (head)->tqh_first = NULL; \ (head)->tqh_last = &(head)->tqh_first; \ } while (0) #define TAILQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ (head)->tqh_first->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (head)->tqh_first = (elm); \ (elm)->field.tqe_prev = &(head)->tqh_first; \ } while (0) #define TAILQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.tqe_next = NULL; \ (elm)->field.tqe_prev = (head)->tqh_last; \ *(head)->tqh_last = (elm); \ (head)->tqh_last = &(elm)->field.tqe_next; \ } while (0) #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ (elm)->field.tqe_next->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (listelm)->field.tqe_next = (elm); \ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ } while (0) #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ (elm)->field.tqe_next = (listelm); \ *(listelm)->field.tqe_prev = (elm); \ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ } while (0) #define TAILQ_REMOVE(head, elm, field) do { \ if (((elm)->field.tqe_next) != NULL) \ (elm)->field.tqe_next->field.tqe_prev = \ (elm)->field.tqe_prev; \ else \ (head)->tqh_last = (elm)->field.tqe_prev; \ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ _Q_INVALIDATE((elm)->field.tqe_prev); \ _Q_INVALIDATE((elm)->field.tqe_next); \ } while (0) #define TAILQ_REPLACE(head, elm, elm2, field) do { \ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ (elm2)->field.tqe_next->field.tqe_prev = \ &(elm2)->field.tqe_next; \ else \ (head)->tqh_last = &(elm2)->field.tqe_next; \ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ *(elm2)->field.tqe_prev = (elm2); \ _Q_INVALIDATE((elm)->field.tqe_prev); \ _Q_INVALIDATE((elm)->field.tqe_next); \ } while (0) /* * Circular queue definitions. */ #define CIRCLEQ_HEAD(name, type) \ struct name { \ struct type *cqh_first; /* first element */ \ struct type *cqh_last; /* last element */ \ } #define CIRCLEQ_HEAD_INITIALIZER(head) \ { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } #define CIRCLEQ_ENTRY(type) \ struct { \ struct type *cqe_next; /* next element */ \ struct type *cqe_prev; /* previous element */ \ } /* * Circular queue access methods */ #define CIRCLEQ_FIRST(head) ((head)->cqh_first) #define CIRCLEQ_LAST(head) ((head)->cqh_last) #define CIRCLEQ_END(head) ((void *)(head)) #define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) #define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) #define CIRCLEQ_EMPTY(head) \ (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) #define CIRCLEQ_FOREACH(var, head, field) \ for((var) = CIRCLEQ_FIRST(head); \ (var) != CIRCLEQ_END(head); \ (var) = CIRCLEQ_NEXT(var, field)) #define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = CIRCLEQ_FIRST(head); \ (var) != CIRCLEQ_END(head) && \ ((tvar) = CIRCLEQ_NEXT(var, field), 1); \ (var) = (tvar)) #define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ for((var) = CIRCLEQ_LAST(head); \ (var) != CIRCLEQ_END(head); \ (var) = CIRCLEQ_PREV(var, field)) #define CIRCLEQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ for ((var) = CIRCLEQ_LAST(head, headname); \ (var) != CIRCLEQ_END(head) && \ ((tvar) = CIRCLEQ_PREV(var, headname, field), 1); \ (var) = (tvar)) /* * Circular queue functions. */ #define CIRCLEQ_INIT(head) do { \ (head)->cqh_first = CIRCLEQ_END(head); \ (head)->cqh_last = CIRCLEQ_END(head); \ } while (0) #define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ (elm)->field.cqe_next = (listelm)->field.cqe_next; \ (elm)->field.cqe_prev = (listelm); \ if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ (head)->cqh_last = (elm); \ else \ (listelm)->field.cqe_next->field.cqe_prev = (elm); \ (listelm)->field.cqe_next = (elm); \ } while (0) #define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ (elm)->field.cqe_next = (listelm); \ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ (head)->cqh_first = (elm); \ else \ (listelm)->field.cqe_prev->field.cqe_next = (elm); \ (listelm)->field.cqe_prev = (elm); \ } while (0) #define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ (elm)->field.cqe_next = (head)->cqh_first; \ (elm)->field.cqe_prev = CIRCLEQ_END(head); \ if ((head)->cqh_last == CIRCLEQ_END(head)) \ (head)->cqh_last = (elm); \ else \ (head)->cqh_first->field.cqe_prev = (elm); \ (head)->cqh_first = (elm); \ } while (0) #define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.cqe_next = CIRCLEQ_END(head); \ (elm)->field.cqe_prev = (head)->cqh_last; \ if ((head)->cqh_first == CIRCLEQ_END(head)) \ (head)->cqh_first = (elm); \ else \ (head)->cqh_last->field.cqe_next = (elm); \ (head)->cqh_last = (elm); \ } while (0) #define CIRCLEQ_REMOVE(head, elm, field) do { \ if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ (head)->cqh_last = (elm)->field.cqe_prev; \ else \ (elm)->field.cqe_next->field.cqe_prev = \ (elm)->field.cqe_prev; \ if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ (head)->cqh_first = (elm)->field.cqe_next; \ else \ (elm)->field.cqe_prev->field.cqe_next = \ (elm)->field.cqe_next; \ _Q_INVALIDATE((elm)->field.cqe_prev); \ _Q_INVALIDATE((elm)->field.cqe_next); \ } while (0) #define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ CIRCLEQ_END(head)) \ (head)->cqh_last = (elm2); \ else \ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ CIRCLEQ_END(head)) \ (head)->cqh_first = (elm2); \ else \ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ _Q_INVALIDATE((elm)->field.cqe_prev); \ _Q_INVALIDATE((elm)->field.cqe_next); \ } while (0) /*********************************** amalgamated original file "/home/jvoe/open62541/deps/pcg_basic.h" ***********************************/ /* * PCG Random Number Generation for C. * * Copyright 2014 Melissa O'Neill * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * For additional information about the PCG random number generation scheme, * including its license and other licensing options, visit * * http://www.pcg-random.org */ #ifdef __cplusplus extern "C" { #endif typedef struct pcg_state_setseq_64 { uint64_t state; /* RNG state. All values are possible. */ uint64_t inc; /* Controls which RNG sequence (stream) is selected. Must * *always* be odd. */ } pcg32_random_t; #define PCG32_INITIALIZER { 0x853c49e6748fea9bULL, 0xda3e39cb94b95bdbULL } void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initial_state, uint64_t initseq); uint32_t pcg32_random_r(pcg32_random_t* rng); #ifdef __cplusplus } #endif /*********************************** amalgamated original file "/home/jvoe/open62541/deps/libc_time.h" ***********************************/ struct mytm { int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mon; int tm_year; }; int __secs_to_tm(long long t, struct mytm *tm); long long __tm_to_secs(const struct mytm *tm); /*********************************** amalgamated original file "/home/jvoe/open62541/deps/base64.h" ***********************************/ #ifndef UA_BASE64_H_ #define UA_BASE64_H_ _UA_BEGIN_DECLS #include /** * base64_encode - Base64 encode * @src: Data to be encoded * @len: Length of the data to be encoded * @out_len: Pointer to output length variable * Returns: Allocated buffer of out_len bytes of encoded data, * or %NULL on failure. The output is NOT Null-terminated. */ unsigned char * UA_base64(const unsigned char *src, size_t len, size_t *out_len); /** * base64_decode - Base64 decode * @src: Data to be decoded * @len: Length of the data to be decoded * @out_len: Pointer to output length variable * Returns: Allocated buffer of out_len bytes of decoded data, * or %NULL on failure. */ unsigned char * UA_unbase64(const unsigned char *src, size_t len, size_t *out_len); _UA_END_DECLS #endif /* UA_BASE64_H_ */ /*********************************** amalgamated original file "/home/jvoe/open62541/src/ua_util_internal.h" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2014, 2017 (c) Florian Palm * Copyright 2015 (c) LEvertz * Copyright 2015-2016 (c) Sten Grüner * Copyright 2015 (c) Chris Iatrou * Copyright 2015-2016 (c) Oleksiy Vasylyev * Copyright 2017 (c) Stefan Profanter, fortiss GmbH */ #define UA_INTERNAL _UA_BEGIN_DECLS /* Macro-Expand for MSVC workarounds */ #define UA_MACRO_EXPAND(x) x /* Integer Shortnames * ------------------ * These are not exposed on the public API, since many user-applications make * the same definitions in their headers. */ typedef UA_Byte u8; typedef UA_SByte i8; typedef UA_UInt16 u16; typedef UA_Int16 i16; typedef UA_UInt32 u32; typedef UA_Int32 i32; typedef UA_UInt64 u64; typedef UA_Int64 i64; typedef UA_StatusCode status; /* Utility Functions * ----------------- */ #ifdef UA_DEBUG_DUMP_PKGS void UA_EXPORT UA_dump_hex_pkg(UA_Byte* buffer, size_t bufferLen); #endif _UA_END_DECLS /*********************************** amalgamated original file "/home/jvoe/open62541/src/ua_types_encoding_binary.h" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2015 (c) Sten Grüner * Copyright 2014, 2017 (c) Florian Palm * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB */ _UA_BEGIN_DECLS typedef UA_StatusCode (*UA_exchangeEncodeBuffer)(void *handle, UA_Byte **bufPos, const UA_Byte **bufEnd); /* Encodes the scalar value described by type in the binary encoding. Encoding * is thread-safe if thread-local variables are enabled. Encoding is also * reentrant and can be safely called from signal handlers or interrupts. * * @param src The value. Must not be NULL. * @param type The value type. Must not be NULL. * @param bufPos Points to a pointer to the current position in the encoding * buffer. Must not be NULL. The pointer is advanced by the number of * encoded bytes, or, if the buffer is exchanged, to the position in the * new buffer. * @param bufEnd Points to a pointer to the end of the encoding buffer (encoding * always stops before *buf_end). Must not be NULL. The pointer is * changed when the buffer is exchanged. * @param exchangeCallback Called when the end of the buffer is reached. This is used to send out a message chunk before continuing with the encoding. Is ignored if NULL. * @param exchangeHandle Custom data passed into the exchangeCallback. * @return Returns a statuscode whether encoding succeeded. */ UA_StatusCode UA_encodeBinary(const void *src, const UA_DataType *type, UA_Byte **bufPos, const UA_Byte **bufEnd, UA_exchangeEncodeBuffer exchangeCallback, void *exchangeHandle) UA_FUNC_ATTR_WARN_UNUSED_RESULT; /* Decodes a scalar value described by type from binary encoding. Decoding * is thread-safe if thread-local variables are enabled. Decoding is also * reentrant and can be safely called from signal handlers or interrupts. * * @param src The buffer with the binary encoded value. Must not be NULL. * @param offset The current position in the buffer. Must not be NULL. The value * is advanced as decoding progresses. * @param dst The target value. Must not be NULL. The target is assumed to have * size type->memSize. The value is reset to zero before decoding. If * decoding fails, members are deleted and the value is reset (zeroed) * again. * @param type The value type. Must not be NULL. * @param customTypesSize The number of non-standard datatypes contained in the * customTypes array. * @param customTypes An array of non-standard datatypes (not included in * UA_TYPES). Can be NULL if customTypesSize is zero. * @return Returns a statuscode whether decoding succeeded. */ UA_StatusCode UA_decodeBinary(const UA_ByteString *src, size_t *offset, void *dst, const UA_DataType *type, const UA_DataTypeArray *customTypes) UA_FUNC_ATTR_WARN_UNUSED_RESULT; /* Returns the number of bytes the value p takes in binary encoding. Returns * zero if an error occurs. UA_calcSizeBinary is thread-safe and reentrant since * it does not access global (thread-local) variables. */ size_t UA_calcSizeBinary(const void *p, const UA_DataType *type); const UA_DataType * UA_findDataTypeByBinary(const UA_NodeId *typeId); _UA_END_DECLS /*********************************** amalgamated original file "/home/jvoe/open62541/build/src_generated/open62541/types_generated_encoding_binary.h" ***********************************/ /* Generated from Opc.Ua.Types.bsd with script /home/jvoe/open62541/tools/generate_datatypes.py * on host rigel by user jvoe at 2019-09-27 03:59:36 */ #ifdef UA_ENABLE_AMALGAMATION #else #endif /* Boolean */ static UA_INLINE size_t UA_Boolean_calcSizeBinary(const UA_Boolean *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_BOOLEAN]); } static UA_INLINE UA_StatusCode UA_Boolean_encodeBinary(const UA_Boolean *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_BOOLEAN], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_Boolean_decodeBinary(const UA_ByteString *src, size_t *offset, UA_Boolean *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_BOOLEAN], NULL); } /* SByte */ static UA_INLINE size_t UA_SByte_calcSizeBinary(const UA_SByte *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_SBYTE]); } static UA_INLINE UA_StatusCode UA_SByte_encodeBinary(const UA_SByte *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_SBYTE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_SByte_decodeBinary(const UA_ByteString *src, size_t *offset, UA_SByte *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_SBYTE], NULL); } /* Byte */ static UA_INLINE size_t UA_Byte_calcSizeBinary(const UA_Byte *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_BYTE]); } static UA_INLINE UA_StatusCode UA_Byte_encodeBinary(const UA_Byte *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_BYTE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_Byte_decodeBinary(const UA_ByteString *src, size_t *offset, UA_Byte *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_BYTE], NULL); } /* Int16 */ static UA_INLINE size_t UA_Int16_calcSizeBinary(const UA_Int16 *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_INT16]); } static UA_INLINE UA_StatusCode UA_Int16_encodeBinary(const UA_Int16 *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_INT16], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_Int16_decodeBinary(const UA_ByteString *src, size_t *offset, UA_Int16 *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_INT16], NULL); } /* UInt16 */ static UA_INLINE size_t UA_UInt16_calcSizeBinary(const UA_UInt16 *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_UINT16]); } static UA_INLINE UA_StatusCode UA_UInt16_encodeBinary(const UA_UInt16 *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_UINT16], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_UInt16_decodeBinary(const UA_ByteString *src, size_t *offset, UA_UInt16 *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_UINT16], NULL); } /* Int32 */ static UA_INLINE size_t UA_Int32_calcSizeBinary(const UA_Int32 *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_INT32]); } static UA_INLINE UA_StatusCode UA_Int32_encodeBinary(const UA_Int32 *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_INT32], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_Int32_decodeBinary(const UA_ByteString *src, size_t *offset, UA_Int32 *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_INT32], NULL); } /* UInt32 */ static UA_INLINE size_t UA_UInt32_calcSizeBinary(const UA_UInt32 *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_UINT32]); } static UA_INLINE UA_StatusCode UA_UInt32_encodeBinary(const UA_UInt32 *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_UINT32], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_UInt32_decodeBinary(const UA_ByteString *src, size_t *offset, UA_UInt32 *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_UINT32], NULL); } /* Int64 */ static UA_INLINE size_t UA_Int64_calcSizeBinary(const UA_Int64 *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_INT64]); } static UA_INLINE UA_StatusCode UA_Int64_encodeBinary(const UA_Int64 *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_INT64], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_Int64_decodeBinary(const UA_ByteString *src, size_t *offset, UA_Int64 *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_INT64], NULL); } /* UInt64 */ static UA_INLINE size_t UA_UInt64_calcSizeBinary(const UA_UInt64 *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_UINT64]); } static UA_INLINE UA_StatusCode UA_UInt64_encodeBinary(const UA_UInt64 *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_UINT64], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_UInt64_decodeBinary(const UA_ByteString *src, size_t *offset, UA_UInt64 *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_UINT64], NULL); } /* Float */ static UA_INLINE size_t UA_Float_calcSizeBinary(const UA_Float *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_FLOAT]); } static UA_INLINE UA_StatusCode UA_Float_encodeBinary(const UA_Float *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_FLOAT], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_Float_decodeBinary(const UA_ByteString *src, size_t *offset, UA_Float *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_FLOAT], NULL); } /* Double */ static UA_INLINE size_t UA_Double_calcSizeBinary(const UA_Double *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_DOUBLE]); } static UA_INLINE UA_StatusCode UA_Double_encodeBinary(const UA_Double *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_DOUBLE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_Double_decodeBinary(const UA_ByteString *src, size_t *offset, UA_Double *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_DOUBLE], NULL); } /* String */ static UA_INLINE size_t UA_String_calcSizeBinary(const UA_String *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_STRING]); } static UA_INLINE UA_StatusCode UA_String_encodeBinary(const UA_String *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_STRING], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_String_decodeBinary(const UA_ByteString *src, size_t *offset, UA_String *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_STRING], NULL); } /* DateTime */ static UA_INLINE size_t UA_DateTime_calcSizeBinary(const UA_DateTime *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_DATETIME]); } static UA_INLINE UA_StatusCode UA_DateTime_encodeBinary(const UA_DateTime *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_DATETIME], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_DateTime_decodeBinary(const UA_ByteString *src, size_t *offset, UA_DateTime *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_DATETIME], NULL); } /* Guid */ static UA_INLINE size_t UA_Guid_calcSizeBinary(const UA_Guid *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_GUID]); } static UA_INLINE UA_StatusCode UA_Guid_encodeBinary(const UA_Guid *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_GUID], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_Guid_decodeBinary(const UA_ByteString *src, size_t *offset, UA_Guid *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_GUID], NULL); } /* ByteString */ static UA_INLINE size_t UA_ByteString_calcSizeBinary(const UA_ByteString *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_BYTESTRING]); } static UA_INLINE UA_StatusCode UA_ByteString_encodeBinary(const UA_ByteString *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_BYTESTRING], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ByteString_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ByteString *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_BYTESTRING], NULL); } /* XmlElement */ static UA_INLINE size_t UA_XmlElement_calcSizeBinary(const UA_XmlElement *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_XMLELEMENT]); } static UA_INLINE UA_StatusCode UA_XmlElement_encodeBinary(const UA_XmlElement *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_XMLELEMENT], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_XmlElement_decodeBinary(const UA_ByteString *src, size_t *offset, UA_XmlElement *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_XMLELEMENT], NULL); } /* NodeId */ static UA_INLINE size_t UA_NodeId_calcSizeBinary(const UA_NodeId *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_NODEID]); } static UA_INLINE UA_StatusCode UA_NodeId_encodeBinary(const UA_NodeId *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_NODEID], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_NodeId_decodeBinary(const UA_ByteString *src, size_t *offset, UA_NodeId *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_NODEID], NULL); } /* ExpandedNodeId */ static UA_INLINE size_t UA_ExpandedNodeId_calcSizeBinary(const UA_ExpandedNodeId *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); } static UA_INLINE UA_StatusCode UA_ExpandedNodeId_encodeBinary(const UA_ExpandedNodeId *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_EXPANDEDNODEID], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ExpandedNodeId_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ExpandedNodeId *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_EXPANDEDNODEID], NULL); } /* StatusCode */ static UA_INLINE size_t UA_StatusCode_calcSizeBinary(const UA_StatusCode *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_STATUSCODE]); } static UA_INLINE UA_StatusCode UA_StatusCode_encodeBinary(const UA_StatusCode *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_STATUSCODE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_StatusCode_decodeBinary(const UA_ByteString *src, size_t *offset, UA_StatusCode *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_STATUSCODE], NULL); } /* QualifiedName */ static UA_INLINE size_t UA_QualifiedName_calcSizeBinary(const UA_QualifiedName *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]); } static UA_INLINE UA_StatusCode UA_QualifiedName_encodeBinary(const UA_QualifiedName *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_QUALIFIEDNAME], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_QualifiedName_decodeBinary(const UA_ByteString *src, size_t *offset, UA_QualifiedName *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_QUALIFIEDNAME], NULL); } /* LocalizedText */ static UA_INLINE size_t UA_LocalizedText_calcSizeBinary(const UA_LocalizedText *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]); } static UA_INLINE UA_StatusCode UA_LocalizedText_encodeBinary(const UA_LocalizedText *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_LocalizedText_decodeBinary(const UA_ByteString *src, size_t *offset, UA_LocalizedText *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], NULL); } /* ExtensionObject */ static UA_INLINE size_t UA_ExtensionObject_calcSizeBinary(const UA_ExtensionObject *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]); } static UA_INLINE UA_StatusCode UA_ExtensionObject_encodeBinary(const UA_ExtensionObject *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ExtensionObject_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ExtensionObject *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT], NULL); } /* DataValue */ static UA_INLINE size_t UA_DataValue_calcSizeBinary(const UA_DataValue *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_DATAVALUE]); } static UA_INLINE UA_StatusCode UA_DataValue_encodeBinary(const UA_DataValue *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_DATAVALUE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_DataValue_decodeBinary(const UA_ByteString *src, size_t *offset, UA_DataValue *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_DATAVALUE], NULL); } /* Variant */ static UA_INLINE size_t UA_Variant_calcSizeBinary(const UA_Variant *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_VARIANT]); } static UA_INLINE UA_StatusCode UA_Variant_encodeBinary(const UA_Variant *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_VARIANT], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_Variant_decodeBinary(const UA_ByteString *src, size_t *offset, UA_Variant *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_VARIANT], NULL); } /* DiagnosticInfo */ static UA_INLINE size_t UA_DiagnosticInfo_calcSizeBinary(const UA_DiagnosticInfo *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_DIAGNOSTICINFO]); } static UA_INLINE UA_StatusCode UA_DiagnosticInfo_encodeBinary(const UA_DiagnosticInfo *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_DiagnosticInfo_decodeBinary(const UA_ByteString *src, size_t *offset, UA_DiagnosticInfo *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], NULL); } /* ViewAttributes */ static UA_INLINE size_t UA_ViewAttributes_calcSizeBinary(const UA_ViewAttributes *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_VIEWATTRIBUTES]); } static UA_INLINE UA_StatusCode UA_ViewAttributes_encodeBinary(const UA_ViewAttributes *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_VIEWATTRIBUTES], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ViewAttributes_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ViewAttributes *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_VIEWATTRIBUTES], NULL); } /* ElementOperand */ static UA_INLINE size_t UA_ElementOperand_calcSizeBinary(const UA_ElementOperand *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_ELEMENTOPERAND]); } static UA_INLINE UA_StatusCode UA_ElementOperand_encodeBinary(const UA_ElementOperand *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_ELEMENTOPERAND], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ElementOperand_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ElementOperand *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_ELEMENTOPERAND], NULL); } /* VariableAttributes */ static UA_INLINE size_t UA_VariableAttributes_calcSizeBinary(const UA_VariableAttributes *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES]); } static UA_INLINE UA_StatusCode UA_VariableAttributes_encodeBinary(const UA_VariableAttributes *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_VariableAttributes_decodeBinary(const UA_ByteString *src, size_t *offset, UA_VariableAttributes *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES], NULL); } /* EnumValueType */ static UA_INLINE size_t UA_EnumValueType_calcSizeBinary(const UA_EnumValueType *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_ENUMVALUETYPE]); } static UA_INLINE UA_StatusCode UA_EnumValueType_encodeBinary(const UA_EnumValueType *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_ENUMVALUETYPE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_EnumValueType_decodeBinary(const UA_ByteString *src, size_t *offset, UA_EnumValueType *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_ENUMVALUETYPE], NULL); } /* EventFieldList */ static UA_INLINE size_t UA_EventFieldList_calcSizeBinary(const UA_EventFieldList *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_EVENTFIELDLIST]); } static UA_INLINE UA_StatusCode UA_EventFieldList_encodeBinary(const UA_EventFieldList *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_EVENTFIELDLIST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_EventFieldList_decodeBinary(const UA_ByteString *src, size_t *offset, UA_EventFieldList *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_EVENTFIELDLIST], NULL); } /* MonitoredItemCreateResult */ static UA_INLINE size_t UA_MonitoredItemCreateResult_calcSizeBinary(const UA_MonitoredItemCreateResult *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_MONITOREDITEMCREATERESULT]); } static UA_INLINE UA_StatusCode UA_MonitoredItemCreateResult_encodeBinary(const UA_MonitoredItemCreateResult *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_MONITOREDITEMCREATERESULT], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_MonitoredItemCreateResult_decodeBinary(const UA_ByteString *src, size_t *offset, UA_MonitoredItemCreateResult *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_MONITOREDITEMCREATERESULT], NULL); } /* ServerDiagnosticsSummaryDataType */ static UA_INLINE size_t UA_ServerDiagnosticsSummaryDataType_calcSizeBinary(const UA_ServerDiagnosticsSummaryDataType *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_SERVERDIAGNOSTICSSUMMARYDATATYPE]); } static UA_INLINE UA_StatusCode UA_ServerDiagnosticsSummaryDataType_encodeBinary(const UA_ServerDiagnosticsSummaryDataType *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_SERVERDIAGNOSTICSSUMMARYDATATYPE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ServerDiagnosticsSummaryDataType_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ServerDiagnosticsSummaryDataType *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_SERVERDIAGNOSTICSSUMMARYDATATYPE], NULL); } /* ContentFilterElementResult */ static UA_INLINE size_t UA_ContentFilterElementResult_calcSizeBinary(const UA_ContentFilterElementResult *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_CONTENTFILTERELEMENTRESULT]); } static UA_INLINE UA_StatusCode UA_ContentFilterElementResult_encodeBinary(const UA_ContentFilterElementResult *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_CONTENTFILTERELEMENTRESULT], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ContentFilterElementResult_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ContentFilterElementResult *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_CONTENTFILTERELEMENTRESULT], NULL); } /* LiteralOperand */ static UA_INLINE size_t UA_LiteralOperand_calcSizeBinary(const UA_LiteralOperand *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_LITERALOPERAND]); } static UA_INLINE UA_StatusCode UA_LiteralOperand_encodeBinary(const UA_LiteralOperand *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_LITERALOPERAND], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_LiteralOperand_decodeBinary(const UA_ByteString *src, size_t *offset, UA_LiteralOperand *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_LITERALOPERAND], NULL); } /* MessageSecurityMode */ static UA_INLINE size_t UA_MessageSecurityMode_calcSizeBinary(const UA_MessageSecurityMode *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_MESSAGESECURITYMODE]); } static UA_INLINE UA_StatusCode UA_MessageSecurityMode_encodeBinary(const UA_MessageSecurityMode *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_MESSAGESECURITYMODE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_MessageSecurityMode_decodeBinary(const UA_ByteString *src, size_t *offset, UA_MessageSecurityMode *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_MESSAGESECURITYMODE], NULL); } /* UtcTime */ static UA_INLINE size_t UA_UtcTime_calcSizeBinary(const UA_UtcTime *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_UTCTIME]); } static UA_INLINE UA_StatusCode UA_UtcTime_encodeBinary(const UA_UtcTime *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_UTCTIME], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_UtcTime_decodeBinary(const UA_ByteString *src, size_t *offset, UA_UtcTime *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_UTCTIME], NULL); } /* UserIdentityToken */ static UA_INLINE size_t UA_UserIdentityToken_calcSizeBinary(const UA_UserIdentityToken *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_USERIDENTITYTOKEN]); } static UA_INLINE UA_StatusCode UA_UserIdentityToken_encodeBinary(const UA_UserIdentityToken *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_USERIDENTITYTOKEN], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_UserIdentityToken_decodeBinary(const UA_ByteString *src, size_t *offset, UA_UserIdentityToken *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_USERIDENTITYTOKEN], NULL); } /* X509IdentityToken */ static UA_INLINE size_t UA_X509IdentityToken_calcSizeBinary(const UA_X509IdentityToken *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_X509IDENTITYTOKEN]); } static UA_INLINE UA_StatusCode UA_X509IdentityToken_encodeBinary(const UA_X509IdentityToken *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_X509IDENTITYTOKEN], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_X509IdentityToken_decodeBinary(const UA_ByteString *src, size_t *offset, UA_X509IdentityToken *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_X509IDENTITYTOKEN], NULL); } /* MonitoredItemNotification */ static UA_INLINE size_t UA_MonitoredItemNotification_calcSizeBinary(const UA_MonitoredItemNotification *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_MONITOREDITEMNOTIFICATION]); } static UA_INLINE UA_StatusCode UA_MonitoredItemNotification_encodeBinary(const UA_MonitoredItemNotification *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_MONITOREDITEMNOTIFICATION], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_MonitoredItemNotification_decodeBinary(const UA_ByteString *src, size_t *offset, UA_MonitoredItemNotification *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_MONITOREDITEMNOTIFICATION], NULL); } /* ResponseHeader */ static UA_INLINE size_t UA_ResponseHeader_calcSizeBinary(const UA_ResponseHeader *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_RESPONSEHEADER]); } static UA_INLINE UA_StatusCode UA_ResponseHeader_encodeBinary(const UA_ResponseHeader *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_RESPONSEHEADER], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ResponseHeader_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ResponseHeader *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_RESPONSEHEADER], NULL); } /* SignatureData */ static UA_INLINE size_t UA_SignatureData_calcSizeBinary(const UA_SignatureData *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_SIGNATUREDATA]); } static UA_INLINE UA_StatusCode UA_SignatureData_encodeBinary(const UA_SignatureData *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_SIGNATUREDATA], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_SignatureData_decodeBinary(const UA_ByteString *src, size_t *offset, UA_SignatureData *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_SIGNATUREDATA], NULL); } /* ModifySubscriptionResponse */ static UA_INLINE size_t UA_ModifySubscriptionResponse_calcSizeBinary(const UA_ModifySubscriptionResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONRESPONSE]); } static UA_INLINE UA_StatusCode UA_ModifySubscriptionResponse_encodeBinary(const UA_ModifySubscriptionResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ModifySubscriptionResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ModifySubscriptionResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONRESPONSE], NULL); } /* NodeAttributes */ static UA_INLINE size_t UA_NodeAttributes_calcSizeBinary(const UA_NodeAttributes *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_NODEATTRIBUTES]); } static UA_INLINE UA_StatusCode UA_NodeAttributes_encodeBinary(const UA_NodeAttributes *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_NODEATTRIBUTES], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_NodeAttributes_decodeBinary(const UA_ByteString *src, size_t *offset, UA_NodeAttributes *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_NODEATTRIBUTES], NULL); } /* ActivateSessionResponse */ static UA_INLINE size_t UA_ActivateSessionResponse_calcSizeBinary(const UA_ActivateSessionResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE]); } static UA_INLINE UA_StatusCode UA_ActivateSessionResponse_encodeBinary(const UA_ActivateSessionResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ActivateSessionResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ActivateSessionResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE], NULL); } /* VariableTypeAttributes */ static UA_INLINE size_t UA_VariableTypeAttributes_calcSizeBinary(const UA_VariableTypeAttributes *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES]); } static UA_INLINE UA_StatusCode UA_VariableTypeAttributes_encodeBinary(const UA_VariableTypeAttributes *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_VariableTypeAttributes_decodeBinary(const UA_ByteString *src, size_t *offset, UA_VariableTypeAttributes *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES], NULL); } /* CallMethodResult */ static UA_INLINE size_t UA_CallMethodResult_calcSizeBinary(const UA_CallMethodResult *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_CALLMETHODRESULT]); } static UA_INLINE UA_StatusCode UA_CallMethodResult_encodeBinary(const UA_CallMethodResult *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_CALLMETHODRESULT], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_CallMethodResult_decodeBinary(const UA_ByteString *src, size_t *offset, UA_CallMethodResult *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_CALLMETHODRESULT], NULL); } /* MonitoringMode */ static UA_INLINE size_t UA_MonitoringMode_calcSizeBinary(const UA_MonitoringMode *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_MONITORINGMODE]); } static UA_INLINE UA_StatusCode UA_MonitoringMode_encodeBinary(const UA_MonitoringMode *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_MONITORINGMODE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_MonitoringMode_decodeBinary(const UA_ByteString *src, size_t *offset, UA_MonitoringMode *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_MONITORINGMODE], NULL); } /* SetMonitoringModeResponse */ static UA_INLINE size_t UA_SetMonitoringModeResponse_calcSizeBinary(const UA_SetMonitoringModeResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_SETMONITORINGMODERESPONSE]); } static UA_INLINE UA_StatusCode UA_SetMonitoringModeResponse_encodeBinary(const UA_SetMonitoringModeResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_SETMONITORINGMODERESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_SetMonitoringModeResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_SetMonitoringModeResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_SETMONITORINGMODERESPONSE], NULL); } /* BrowseResultMask */ static UA_INLINE size_t UA_BrowseResultMask_calcSizeBinary(const UA_BrowseResultMask *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_BROWSERESULTMASK]); } static UA_INLINE UA_StatusCode UA_BrowseResultMask_encodeBinary(const UA_BrowseResultMask *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_BROWSERESULTMASK], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_BrowseResultMask_decodeBinary(const UA_ByteString *src, size_t *offset, UA_BrowseResultMask *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_BROWSERESULTMASK], NULL); } /* RequestHeader */ static UA_INLINE size_t UA_RequestHeader_calcSizeBinary(const UA_RequestHeader *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_REQUESTHEADER]); } static UA_INLINE UA_StatusCode UA_RequestHeader_encodeBinary(const UA_RequestHeader *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_REQUESTHEADER], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_RequestHeader_decodeBinary(const UA_ByteString *src, size_t *offset, UA_RequestHeader *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_REQUESTHEADER], NULL); } /* MonitoredItemModifyResult */ static UA_INLINE size_t UA_MonitoredItemModifyResult_calcSizeBinary(const UA_MonitoredItemModifyResult *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_MONITOREDITEMMODIFYRESULT]); } static UA_INLINE UA_StatusCode UA_MonitoredItemModifyResult_encodeBinary(const UA_MonitoredItemModifyResult *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_MONITOREDITEMMODIFYRESULT], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_MonitoredItemModifyResult_decodeBinary(const UA_ByteString *src, size_t *offset, UA_MonitoredItemModifyResult *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_MONITOREDITEMMODIFYRESULT], NULL); } /* CloseSecureChannelRequest */ static UA_INLINE size_t UA_CloseSecureChannelRequest_calcSizeBinary(const UA_CloseSecureChannelRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_CLOSESECURECHANNELREQUEST]); } static UA_INLINE UA_StatusCode UA_CloseSecureChannelRequest_encodeBinary(const UA_CloseSecureChannelRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_CLOSESECURECHANNELREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_CloseSecureChannelRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_CloseSecureChannelRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_CLOSESECURECHANNELREQUEST], NULL); } /* NotificationMessage */ static UA_INLINE size_t UA_NotificationMessage_calcSizeBinary(const UA_NotificationMessage *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_NOTIFICATIONMESSAGE]); } static UA_INLINE UA_StatusCode UA_NotificationMessage_encodeBinary(const UA_NotificationMessage *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_NOTIFICATIONMESSAGE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_NotificationMessage_decodeBinary(const UA_ByteString *src, size_t *offset, UA_NotificationMessage *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_NOTIFICATIONMESSAGE], NULL); } /* CreateSubscriptionResponse */ static UA_INLINE size_t UA_CreateSubscriptionResponse_calcSizeBinary(const UA_CreateSubscriptionResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONRESPONSE]); } static UA_INLINE UA_StatusCode UA_CreateSubscriptionResponse_encodeBinary(const UA_CreateSubscriptionResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_CreateSubscriptionResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_CreateSubscriptionResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONRESPONSE], NULL); } /* MdnsDiscoveryConfiguration */ static UA_INLINE size_t UA_MdnsDiscoveryConfiguration_calcSizeBinary(const UA_MdnsDiscoveryConfiguration *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_MDNSDISCOVERYCONFIGURATION]); } static UA_INLINE UA_StatusCode UA_MdnsDiscoveryConfiguration_encodeBinary(const UA_MdnsDiscoveryConfiguration *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_MDNSDISCOVERYCONFIGURATION], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_MdnsDiscoveryConfiguration_decodeBinary(const UA_ByteString *src, size_t *offset, UA_MdnsDiscoveryConfiguration *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_MDNSDISCOVERYCONFIGURATION], NULL); } /* BrowseDirection */ static UA_INLINE size_t UA_BrowseDirection_calcSizeBinary(const UA_BrowseDirection *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_BROWSEDIRECTION]); } static UA_INLINE UA_StatusCode UA_BrowseDirection_encodeBinary(const UA_BrowseDirection *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_BROWSEDIRECTION], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_BrowseDirection_decodeBinary(const UA_ByteString *src, size_t *offset, UA_BrowseDirection *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_BROWSEDIRECTION], NULL); } /* CallMethodRequest */ static UA_INLINE size_t UA_CallMethodRequest_calcSizeBinary(const UA_CallMethodRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_CALLMETHODREQUEST]); } static UA_INLINE UA_StatusCode UA_CallMethodRequest_encodeBinary(const UA_CallMethodRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_CALLMETHODREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_CallMethodRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_CallMethodRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_CALLMETHODREQUEST], NULL); } /* ReadResponse */ static UA_INLINE size_t UA_ReadResponse_calcSizeBinary(const UA_ReadResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_READRESPONSE]); } static UA_INLINE UA_StatusCode UA_ReadResponse_encodeBinary(const UA_ReadResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_READRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ReadResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ReadResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_READRESPONSE], NULL); } /* TimestampsToReturn */ static UA_INLINE size_t UA_TimestampsToReturn_calcSizeBinary(const UA_TimestampsToReturn *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_TIMESTAMPSTORETURN]); } static UA_INLINE UA_StatusCode UA_TimestampsToReturn_encodeBinary(const UA_TimestampsToReturn *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_TIMESTAMPSTORETURN], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_TimestampsToReturn_decodeBinary(const UA_ByteString *src, size_t *offset, UA_TimestampsToReturn *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_TIMESTAMPSTORETURN], NULL); } /* NodeClass */ static UA_INLINE size_t UA_NodeClass_calcSizeBinary(const UA_NodeClass *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_NODECLASS]); } static UA_INLINE UA_StatusCode UA_NodeClass_encodeBinary(const UA_NodeClass *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_NODECLASS], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_NodeClass_decodeBinary(const UA_ByteString *src, size_t *offset, UA_NodeClass *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_NODECLASS], NULL); } /* ObjectTypeAttributes */ static UA_INLINE size_t UA_ObjectTypeAttributes_calcSizeBinary(const UA_ObjectTypeAttributes *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES]); } static UA_INLINE UA_StatusCode UA_ObjectTypeAttributes_encodeBinary(const UA_ObjectTypeAttributes *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ObjectTypeAttributes_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ObjectTypeAttributes *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES], NULL); } /* SecurityTokenRequestType */ static UA_INLINE size_t UA_SecurityTokenRequestType_calcSizeBinary(const UA_SecurityTokenRequestType *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_SECURITYTOKENREQUESTTYPE]); } static UA_INLINE UA_StatusCode UA_SecurityTokenRequestType_encodeBinary(const UA_SecurityTokenRequestType *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_SECURITYTOKENREQUESTTYPE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_SecurityTokenRequestType_decodeBinary(const UA_ByteString *src, size_t *offset, UA_SecurityTokenRequestType *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_SECURITYTOKENREQUESTTYPE], NULL); } /* CloseSessionResponse */ static UA_INLINE size_t UA_CloseSessionResponse_calcSizeBinary(const UA_CloseSessionResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_CLOSESESSIONRESPONSE]); } static UA_INLINE UA_StatusCode UA_CloseSessionResponse_encodeBinary(const UA_CloseSessionResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_CLOSESESSIONRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_CloseSessionResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_CloseSessionResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_CLOSESESSIONRESPONSE], NULL); } /* SetPublishingModeRequest */ static UA_INLINE size_t UA_SetPublishingModeRequest_calcSizeBinary(const UA_SetPublishingModeRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_SETPUBLISHINGMODEREQUEST]); } static UA_INLINE UA_StatusCode UA_SetPublishingModeRequest_encodeBinary(const UA_SetPublishingModeRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_SETPUBLISHINGMODEREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_SetPublishingModeRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_SetPublishingModeRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_SETPUBLISHINGMODEREQUEST], NULL); } /* IssuedIdentityToken */ static UA_INLINE size_t UA_IssuedIdentityToken_calcSizeBinary(const UA_IssuedIdentityToken *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_ISSUEDIDENTITYTOKEN]); } static UA_INLINE UA_StatusCode UA_IssuedIdentityToken_encodeBinary(const UA_IssuedIdentityToken *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_ISSUEDIDENTITYTOKEN], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_IssuedIdentityToken_decodeBinary(const UA_ByteString *src, size_t *offset, UA_IssuedIdentityToken *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_ISSUEDIDENTITYTOKEN], NULL); } /* ServerOnNetwork */ static UA_INLINE size_t UA_ServerOnNetwork_calcSizeBinary(const UA_ServerOnNetwork *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_SERVERONNETWORK]); } static UA_INLINE UA_StatusCode UA_ServerOnNetwork_encodeBinary(const UA_ServerOnNetwork *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_SERVERONNETWORK], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ServerOnNetwork_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ServerOnNetwork *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_SERVERONNETWORK], NULL); } /* DeleteMonitoredItemsResponse */ static UA_INLINE size_t UA_DeleteMonitoredItemsResponse_calcSizeBinary(const UA_DeleteMonitoredItemsResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSRESPONSE]); } static UA_INLINE UA_StatusCode UA_DeleteMonitoredItemsResponse_encodeBinary(const UA_DeleteMonitoredItemsResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_DeleteMonitoredItemsResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_DeleteMonitoredItemsResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSRESPONSE], NULL); } /* ApplicationType */ static UA_INLINE size_t UA_ApplicationType_calcSizeBinary(const UA_ApplicationType *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_APPLICATIONTYPE]); } static UA_INLINE UA_StatusCode UA_ApplicationType_encodeBinary(const UA_ApplicationType *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_APPLICATIONTYPE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ApplicationType_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ApplicationType *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_APPLICATIONTYPE], NULL); } /* DiscoveryConfiguration */ static UA_INLINE size_t UA_DiscoveryConfiguration_calcSizeBinary(const UA_DiscoveryConfiguration *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_DISCOVERYCONFIGURATION]); } static UA_INLINE UA_StatusCode UA_DiscoveryConfiguration_encodeBinary(const UA_DiscoveryConfiguration *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_DISCOVERYCONFIGURATION], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_DiscoveryConfiguration_decodeBinary(const UA_ByteString *src, size_t *offset, UA_DiscoveryConfiguration *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_DISCOVERYCONFIGURATION], NULL); } /* BrowseNextRequest */ static UA_INLINE size_t UA_BrowseNextRequest_calcSizeBinary(const UA_BrowseNextRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_BROWSENEXTREQUEST]); } static UA_INLINE UA_StatusCode UA_BrowseNextRequest_encodeBinary(const UA_BrowseNextRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_BROWSENEXTREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_BrowseNextRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_BrowseNextRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_BROWSENEXTREQUEST], NULL); } /* ModifySubscriptionRequest */ static UA_INLINE size_t UA_ModifySubscriptionRequest_calcSizeBinary(const UA_ModifySubscriptionRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONREQUEST]); } static UA_INLINE UA_StatusCode UA_ModifySubscriptionRequest_encodeBinary(const UA_ModifySubscriptionRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ModifySubscriptionRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ModifySubscriptionRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONREQUEST], NULL); } /* BrowseDescription */ static UA_INLINE size_t UA_BrowseDescription_calcSizeBinary(const UA_BrowseDescription *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_BROWSEDESCRIPTION]); } static UA_INLINE UA_StatusCode UA_BrowseDescription_encodeBinary(const UA_BrowseDescription *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_BROWSEDESCRIPTION], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_BrowseDescription_decodeBinary(const UA_ByteString *src, size_t *offset, UA_BrowseDescription *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_BROWSEDESCRIPTION], NULL); } /* SignedSoftwareCertificate */ static UA_INLINE size_t UA_SignedSoftwareCertificate_calcSizeBinary(const UA_SignedSoftwareCertificate *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_SIGNEDSOFTWARECERTIFICATE]); } static UA_INLINE UA_StatusCode UA_SignedSoftwareCertificate_encodeBinary(const UA_SignedSoftwareCertificate *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_SIGNEDSOFTWARECERTIFICATE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_SignedSoftwareCertificate_decodeBinary(const UA_ByteString *src, size_t *offset, UA_SignedSoftwareCertificate *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_SIGNEDSOFTWARECERTIFICATE], NULL); } /* BrowsePathTarget */ static UA_INLINE size_t UA_BrowsePathTarget_calcSizeBinary(const UA_BrowsePathTarget *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_BROWSEPATHTARGET]); } static UA_INLINE UA_StatusCode UA_BrowsePathTarget_encodeBinary(const UA_BrowsePathTarget *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_BROWSEPATHTARGET], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_BrowsePathTarget_decodeBinary(const UA_ByteString *src, size_t *offset, UA_BrowsePathTarget *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_BROWSEPATHTARGET], NULL); } /* WriteResponse */ static UA_INLINE size_t UA_WriteResponse_calcSizeBinary(const UA_WriteResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_WRITERESPONSE]); } static UA_INLINE UA_StatusCode UA_WriteResponse_encodeBinary(const UA_WriteResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_WRITERESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_WriteResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_WriteResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_WRITERESPONSE], NULL); } /* AddNodesResult */ static UA_INLINE size_t UA_AddNodesResult_calcSizeBinary(const UA_AddNodesResult *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_ADDNODESRESULT]); } static UA_INLINE UA_StatusCode UA_AddNodesResult_encodeBinary(const UA_AddNodesResult *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_ADDNODESRESULT], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_AddNodesResult_decodeBinary(const UA_ByteString *src, size_t *offset, UA_AddNodesResult *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_ADDNODESRESULT], NULL); } /* RegisterServerResponse */ static UA_INLINE size_t UA_RegisterServerResponse_calcSizeBinary(const UA_RegisterServerResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_REGISTERSERVERRESPONSE]); } static UA_INLINE UA_StatusCode UA_RegisterServerResponse_encodeBinary(const UA_RegisterServerResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_REGISTERSERVERRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_RegisterServerResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_RegisterServerResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_REGISTERSERVERRESPONSE], NULL); } /* AddReferencesItem */ static UA_INLINE size_t UA_AddReferencesItem_calcSizeBinary(const UA_AddReferencesItem *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_ADDREFERENCESITEM]); } static UA_INLINE UA_StatusCode UA_AddReferencesItem_encodeBinary(const UA_AddReferencesItem *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_ADDREFERENCESITEM], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_AddReferencesItem_decodeBinary(const UA_ByteString *src, size_t *offset, UA_AddReferencesItem *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_ADDREFERENCESITEM], NULL); } /* RegisterServer2Response */ static UA_INLINE size_t UA_RegisterServer2Response_calcSizeBinary(const UA_RegisterServer2Response *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_REGISTERSERVER2RESPONSE]); } static UA_INLINE UA_StatusCode UA_RegisterServer2Response_encodeBinary(const UA_RegisterServer2Response *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_REGISTERSERVER2RESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_RegisterServer2Response_decodeBinary(const UA_ByteString *src, size_t *offset, UA_RegisterServer2Response *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_REGISTERSERVER2RESPONSE], NULL); } /* DeleteReferencesResponse */ static UA_INLINE size_t UA_DeleteReferencesResponse_calcSizeBinary(const UA_DeleteReferencesResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_DELETEREFERENCESRESPONSE]); } static UA_INLINE UA_StatusCode UA_DeleteReferencesResponse_encodeBinary(const UA_DeleteReferencesResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_DELETEREFERENCESRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_DeleteReferencesResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_DeleteReferencesResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_DELETEREFERENCESRESPONSE], NULL); } /* RelativePathElement */ static UA_INLINE size_t UA_RelativePathElement_calcSizeBinary(const UA_RelativePathElement *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_RELATIVEPATHELEMENT]); } static UA_INLINE UA_StatusCode UA_RelativePathElement_encodeBinary(const UA_RelativePathElement *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_RELATIVEPATHELEMENT], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_RelativePathElement_decodeBinary(const UA_ByteString *src, size_t *offset, UA_RelativePathElement *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_RELATIVEPATHELEMENT], NULL); } /* SubscriptionAcknowledgement */ static UA_INLINE size_t UA_SubscriptionAcknowledgement_calcSizeBinary(const UA_SubscriptionAcknowledgement *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_SUBSCRIPTIONACKNOWLEDGEMENT]); } static UA_INLINE UA_StatusCode UA_SubscriptionAcknowledgement_encodeBinary(const UA_SubscriptionAcknowledgement *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_SUBSCRIPTIONACKNOWLEDGEMENT], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_SubscriptionAcknowledgement_decodeBinary(const UA_ByteString *src, size_t *offset, UA_SubscriptionAcknowledgement *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_SUBSCRIPTIONACKNOWLEDGEMENT], NULL); } /* CreateMonitoredItemsResponse */ static UA_INLINE size_t UA_CreateMonitoredItemsResponse_calcSizeBinary(const UA_CreateMonitoredItemsResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSRESPONSE]); } static UA_INLINE UA_StatusCode UA_CreateMonitoredItemsResponse_encodeBinary(const UA_CreateMonitoredItemsResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_CreateMonitoredItemsResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_CreateMonitoredItemsResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSRESPONSE], NULL); } /* DeleteReferencesItem */ static UA_INLINE size_t UA_DeleteReferencesItem_calcSizeBinary(const UA_DeleteReferencesItem *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_DELETEREFERENCESITEM]); } static UA_INLINE UA_StatusCode UA_DeleteReferencesItem_encodeBinary(const UA_DeleteReferencesItem *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_DELETEREFERENCESITEM], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_DeleteReferencesItem_decodeBinary(const UA_ByteString *src, size_t *offset, UA_DeleteReferencesItem *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_DELETEREFERENCESITEM], NULL); } /* WriteValue */ static UA_INLINE size_t UA_WriteValue_calcSizeBinary(const UA_WriteValue *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_WRITEVALUE]); } static UA_INLINE UA_StatusCode UA_WriteValue_encodeBinary(const UA_WriteValue *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_WRITEVALUE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_WriteValue_decodeBinary(const UA_ByteString *src, size_t *offset, UA_WriteValue *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_WRITEVALUE], NULL); } /* DataTypeAttributes */ static UA_INLINE size_t UA_DataTypeAttributes_calcSizeBinary(const UA_DataTypeAttributes *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES]); } static UA_INLINE UA_StatusCode UA_DataTypeAttributes_encodeBinary(const UA_DataTypeAttributes *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_DataTypeAttributes_decodeBinary(const UA_ByteString *src, size_t *offset, UA_DataTypeAttributes *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES], NULL); } /* AddReferencesResponse */ static UA_INLINE size_t UA_AddReferencesResponse_calcSizeBinary(const UA_AddReferencesResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_ADDREFERENCESRESPONSE]); } static UA_INLINE UA_StatusCode UA_AddReferencesResponse_encodeBinary(const UA_AddReferencesResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_ADDREFERENCESRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_AddReferencesResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_AddReferencesResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_ADDREFERENCESRESPONSE], NULL); } /* DeadbandType */ static UA_INLINE size_t UA_DeadbandType_calcSizeBinary(const UA_DeadbandType *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_DEADBANDTYPE]); } static UA_INLINE UA_StatusCode UA_DeadbandType_encodeBinary(const UA_DeadbandType *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_DEADBANDTYPE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_DeadbandType_decodeBinary(const UA_ByteString *src, size_t *offset, UA_DeadbandType *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_DEADBANDTYPE], NULL); } /* DataChangeTrigger */ static UA_INLINE size_t UA_DataChangeTrigger_calcSizeBinary(const UA_DataChangeTrigger *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_DATACHANGETRIGGER]); } static UA_INLINE UA_StatusCode UA_DataChangeTrigger_encodeBinary(const UA_DataChangeTrigger *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_DATACHANGETRIGGER], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_DataChangeTrigger_decodeBinary(const UA_ByteString *src, size_t *offset, UA_DataChangeTrigger *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_DATACHANGETRIGGER], NULL); } /* BuildInfo */ static UA_INLINE size_t UA_BuildInfo_calcSizeBinary(const UA_BuildInfo *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_BUILDINFO]); } static UA_INLINE UA_StatusCode UA_BuildInfo_encodeBinary(const UA_BuildInfo *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_BUILDINFO], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_BuildInfo_decodeBinary(const UA_ByteString *src, size_t *offset, UA_BuildInfo *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_BUILDINFO], NULL); } /* FilterOperand */ static UA_INLINE size_t UA_FilterOperand_calcSizeBinary(const UA_FilterOperand *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_FILTEROPERAND]); } static UA_INLINE UA_StatusCode UA_FilterOperand_encodeBinary(const UA_FilterOperand *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_FILTEROPERAND], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_FilterOperand_decodeBinary(const UA_ByteString *src, size_t *offset, UA_FilterOperand *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_FILTEROPERAND], NULL); } /* MonitoringParameters */ static UA_INLINE size_t UA_MonitoringParameters_calcSizeBinary(const UA_MonitoringParameters *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_MONITORINGPARAMETERS]); } static UA_INLINE UA_StatusCode UA_MonitoringParameters_encodeBinary(const UA_MonitoringParameters *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_MONITORINGPARAMETERS], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_MonitoringParameters_decodeBinary(const UA_ByteString *src, size_t *offset, UA_MonitoringParameters *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_MONITORINGPARAMETERS], NULL); } /* DeleteNodesItem */ static UA_INLINE size_t UA_DeleteNodesItem_calcSizeBinary(const UA_DeleteNodesItem *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_DELETENODESITEM]); } static UA_INLINE UA_StatusCode UA_DeleteNodesItem_encodeBinary(const UA_DeleteNodesItem *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_DELETENODESITEM], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_DeleteNodesItem_decodeBinary(const UA_ByteString *src, size_t *offset, UA_DeleteNodesItem *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_DELETENODESITEM], NULL); } /* ReadValueId */ static UA_INLINE size_t UA_ReadValueId_calcSizeBinary(const UA_ReadValueId *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_READVALUEID]); } static UA_INLINE UA_StatusCode UA_ReadValueId_encodeBinary(const UA_ReadValueId *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_READVALUEID], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ReadValueId_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ReadValueId *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_READVALUEID], NULL); } /* CallRequest */ static UA_INLINE size_t UA_CallRequest_calcSizeBinary(const UA_CallRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_CALLREQUEST]); } static UA_INLINE UA_StatusCode UA_CallRequest_encodeBinary(const UA_CallRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_CALLREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_CallRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_CallRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_CALLREQUEST], NULL); } /* RelativePath */ static UA_INLINE size_t UA_RelativePath_calcSizeBinary(const UA_RelativePath *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_RELATIVEPATH]); } static UA_INLINE UA_StatusCode UA_RelativePath_encodeBinary(const UA_RelativePath *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_RELATIVEPATH], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_RelativePath_decodeBinary(const UA_ByteString *src, size_t *offset, UA_RelativePath *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_RELATIVEPATH], NULL); } /* DeleteNodesRequest */ static UA_INLINE size_t UA_DeleteNodesRequest_calcSizeBinary(const UA_DeleteNodesRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_DELETENODESREQUEST]); } static UA_INLINE UA_StatusCode UA_DeleteNodesRequest_encodeBinary(const UA_DeleteNodesRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_DELETENODESREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_DeleteNodesRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_DeleteNodesRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_DELETENODESREQUEST], NULL); } /* MonitoredItemModifyRequest */ static UA_INLINE size_t UA_MonitoredItemModifyRequest_calcSizeBinary(const UA_MonitoredItemModifyRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_MONITOREDITEMMODIFYREQUEST]); } static UA_INLINE UA_StatusCode UA_MonitoredItemModifyRequest_encodeBinary(const UA_MonitoredItemModifyRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_MONITOREDITEMMODIFYREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_MonitoredItemModifyRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_MonitoredItemModifyRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_MONITOREDITEMMODIFYREQUEST], NULL); } /* UserTokenType */ static UA_INLINE size_t UA_UserTokenType_calcSizeBinary(const UA_UserTokenType *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_USERTOKENTYPE]); } static UA_INLINE UA_StatusCode UA_UserTokenType_encodeBinary(const UA_UserTokenType *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_USERTOKENTYPE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_UserTokenType_decodeBinary(const UA_ByteString *src, size_t *offset, UA_UserTokenType *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_USERTOKENTYPE], NULL); } /* AggregateConfiguration */ static UA_INLINE size_t UA_AggregateConfiguration_calcSizeBinary(const UA_AggregateConfiguration *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_AGGREGATECONFIGURATION]); } static UA_INLINE UA_StatusCode UA_AggregateConfiguration_encodeBinary(const UA_AggregateConfiguration *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_AGGREGATECONFIGURATION], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_AggregateConfiguration_decodeBinary(const UA_ByteString *src, size_t *offset, UA_AggregateConfiguration *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_AGGREGATECONFIGURATION], NULL); } /* LocaleId */ static UA_INLINE size_t UA_LocaleId_calcSizeBinary(const UA_LocaleId *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_LOCALEID]); } static UA_INLINE UA_StatusCode UA_LocaleId_encodeBinary(const UA_LocaleId *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_LOCALEID], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_LocaleId_decodeBinary(const UA_ByteString *src, size_t *offset, UA_LocaleId *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_LOCALEID], NULL); } /* UnregisterNodesResponse */ static UA_INLINE size_t UA_UnregisterNodesResponse_calcSizeBinary(const UA_UnregisterNodesResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_UNREGISTERNODESRESPONSE]); } static UA_INLINE UA_StatusCode UA_UnregisterNodesResponse_encodeBinary(const UA_UnregisterNodesResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_UNREGISTERNODESRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_UnregisterNodesResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_UnregisterNodesResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_UNREGISTERNODESRESPONSE], NULL); } /* ContentFilterResult */ static UA_INLINE size_t UA_ContentFilterResult_calcSizeBinary(const UA_ContentFilterResult *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_CONTENTFILTERRESULT]); } static UA_INLINE UA_StatusCode UA_ContentFilterResult_encodeBinary(const UA_ContentFilterResult *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_CONTENTFILTERRESULT], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ContentFilterResult_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ContentFilterResult *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_CONTENTFILTERRESULT], NULL); } /* UserTokenPolicy */ static UA_INLINE size_t UA_UserTokenPolicy_calcSizeBinary(const UA_UserTokenPolicy *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_USERTOKENPOLICY]); } static UA_INLINE UA_StatusCode UA_UserTokenPolicy_encodeBinary(const UA_UserTokenPolicy *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_USERTOKENPOLICY], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_UserTokenPolicy_decodeBinary(const UA_ByteString *src, size_t *offset, UA_UserTokenPolicy *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_USERTOKENPOLICY], NULL); } /* DeleteMonitoredItemsRequest */ static UA_INLINE size_t UA_DeleteMonitoredItemsRequest_calcSizeBinary(const UA_DeleteMonitoredItemsRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSREQUEST]); } static UA_INLINE UA_StatusCode UA_DeleteMonitoredItemsRequest_encodeBinary(const UA_DeleteMonitoredItemsRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_DeleteMonitoredItemsRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_DeleteMonitoredItemsRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSREQUEST], NULL); } /* SetMonitoringModeRequest */ static UA_INLINE size_t UA_SetMonitoringModeRequest_calcSizeBinary(const UA_SetMonitoringModeRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_SETMONITORINGMODEREQUEST]); } static UA_INLINE UA_StatusCode UA_SetMonitoringModeRequest_encodeBinary(const UA_SetMonitoringModeRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_SETMONITORINGMODEREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_SetMonitoringModeRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_SetMonitoringModeRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_SETMONITORINGMODEREQUEST], NULL); } /* Duration */ static UA_INLINE size_t UA_Duration_calcSizeBinary(const UA_Duration *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_DURATION]); } static UA_INLINE UA_StatusCode UA_Duration_encodeBinary(const UA_Duration *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_DURATION], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_Duration_decodeBinary(const UA_ByteString *src, size_t *offset, UA_Duration *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_DURATION], NULL); } /* ReferenceTypeAttributes */ static UA_INLINE size_t UA_ReferenceTypeAttributes_calcSizeBinary(const UA_ReferenceTypeAttributes *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES]); } static UA_INLINE UA_StatusCode UA_ReferenceTypeAttributes_encodeBinary(const UA_ReferenceTypeAttributes *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ReferenceTypeAttributes_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ReferenceTypeAttributes *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES], NULL); } /* GetEndpointsRequest */ static UA_INLINE size_t UA_GetEndpointsRequest_calcSizeBinary(const UA_GetEndpointsRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST]); } static UA_INLINE UA_StatusCode UA_GetEndpointsRequest_encodeBinary(const UA_GetEndpointsRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_GetEndpointsRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_GetEndpointsRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST], NULL); } /* CloseSecureChannelResponse */ static UA_INLINE size_t UA_CloseSecureChannelResponse_calcSizeBinary(const UA_CloseSecureChannelResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_CLOSESECURECHANNELRESPONSE]); } static UA_INLINE UA_StatusCode UA_CloseSecureChannelResponse_encodeBinary(const UA_CloseSecureChannelResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_CLOSESECURECHANNELRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_CloseSecureChannelResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_CloseSecureChannelResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_CLOSESECURECHANNELRESPONSE], NULL); } /* ViewDescription */ static UA_INLINE size_t UA_ViewDescription_calcSizeBinary(const UA_ViewDescription *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_VIEWDESCRIPTION]); } static UA_INLINE UA_StatusCode UA_ViewDescription_encodeBinary(const UA_ViewDescription *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_VIEWDESCRIPTION], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ViewDescription_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ViewDescription *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_VIEWDESCRIPTION], NULL); } /* SetPublishingModeResponse */ static UA_INLINE size_t UA_SetPublishingModeResponse_calcSizeBinary(const UA_SetPublishingModeResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_SETPUBLISHINGMODERESPONSE]); } static UA_INLINE UA_StatusCode UA_SetPublishingModeResponse_encodeBinary(const UA_SetPublishingModeResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_SETPUBLISHINGMODERESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_SetPublishingModeResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_SetPublishingModeResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_SETPUBLISHINGMODERESPONSE], NULL); } /* StatusChangeNotification */ static UA_INLINE size_t UA_StatusChangeNotification_calcSizeBinary(const UA_StatusChangeNotification *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_STATUSCHANGENOTIFICATION]); } static UA_INLINE UA_StatusCode UA_StatusChangeNotification_encodeBinary(const UA_StatusChangeNotification *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_STATUSCHANGENOTIFICATION], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_StatusChangeNotification_decodeBinary(const UA_ByteString *src, size_t *offset, UA_StatusChangeNotification *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_STATUSCHANGENOTIFICATION], NULL); } /* NodeAttributesMask */ static UA_INLINE size_t UA_NodeAttributesMask_calcSizeBinary(const UA_NodeAttributesMask *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_NODEATTRIBUTESMASK]); } static UA_INLINE UA_StatusCode UA_NodeAttributesMask_encodeBinary(const UA_NodeAttributesMask *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_NODEATTRIBUTESMASK], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_NodeAttributesMask_decodeBinary(const UA_ByteString *src, size_t *offset, UA_NodeAttributesMask *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_NODEATTRIBUTESMASK], NULL); } /* EventFilterResult */ static UA_INLINE size_t UA_EventFilterResult_calcSizeBinary(const UA_EventFilterResult *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_EVENTFILTERRESULT]); } static UA_INLINE UA_StatusCode UA_EventFilterResult_encodeBinary(const UA_EventFilterResult *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_EVENTFILTERRESULT], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_EventFilterResult_decodeBinary(const UA_ByteString *src, size_t *offset, UA_EventFilterResult *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_EVENTFILTERRESULT], NULL); } /* MonitoredItemCreateRequest */ static UA_INLINE size_t UA_MonitoredItemCreateRequest_calcSizeBinary(const UA_MonitoredItemCreateRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_MONITOREDITEMCREATEREQUEST]); } static UA_INLINE UA_StatusCode UA_MonitoredItemCreateRequest_encodeBinary(const UA_MonitoredItemCreateRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_MONITOREDITEMCREATEREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_MonitoredItemCreateRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_MonitoredItemCreateRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_MONITOREDITEMCREATEREQUEST], NULL); } /* Range */ static UA_INLINE size_t UA_Range_calcSizeBinary(const UA_Range *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_RANGE]); } static UA_INLINE UA_StatusCode UA_Range_encodeBinary(const UA_Range *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_RANGE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_Range_decodeBinary(const UA_ByteString *src, size_t *offset, UA_Range *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_RANGE], NULL); } /* DataChangeNotification */ static UA_INLINE size_t UA_DataChangeNotification_calcSizeBinary(const UA_DataChangeNotification *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_DATACHANGENOTIFICATION]); } static UA_INLINE UA_StatusCode UA_DataChangeNotification_encodeBinary(const UA_DataChangeNotification *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_DATACHANGENOTIFICATION], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_DataChangeNotification_decodeBinary(const UA_ByteString *src, size_t *offset, UA_DataChangeNotification *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_DATACHANGENOTIFICATION], NULL); } /* Argument */ static UA_INLINE size_t UA_Argument_calcSizeBinary(const UA_Argument *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_ARGUMENT]); } static UA_INLINE UA_StatusCode UA_Argument_encodeBinary(const UA_Argument *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_ARGUMENT], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_Argument_decodeBinary(const UA_ByteString *src, size_t *offset, UA_Argument *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_ARGUMENT], NULL); } /* ChannelSecurityToken */ static UA_INLINE size_t UA_ChannelSecurityToken_calcSizeBinary(const UA_ChannelSecurityToken *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_CHANNELSECURITYTOKEN]); } static UA_INLINE UA_StatusCode UA_ChannelSecurityToken_encodeBinary(const UA_ChannelSecurityToken *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_CHANNELSECURITYTOKEN], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ChannelSecurityToken_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ChannelSecurityToken *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_CHANNELSECURITYTOKEN], NULL); } /* ServerState */ static UA_INLINE size_t UA_ServerState_calcSizeBinary(const UA_ServerState *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_SERVERSTATE]); } static UA_INLINE UA_StatusCode UA_ServerState_encodeBinary(const UA_ServerState *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_SERVERSTATE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ServerState_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ServerState *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_SERVERSTATE], NULL); } /* EventNotificationList */ static UA_INLINE size_t UA_EventNotificationList_calcSizeBinary(const UA_EventNotificationList *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_EVENTNOTIFICATIONLIST]); } static UA_INLINE UA_StatusCode UA_EventNotificationList_encodeBinary(const UA_EventNotificationList *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_EVENTNOTIFICATIONLIST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_EventNotificationList_decodeBinary(const UA_ByteString *src, size_t *offset, UA_EventNotificationList *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_EVENTNOTIFICATIONLIST], NULL); } /* AnonymousIdentityToken */ static UA_INLINE size_t UA_AnonymousIdentityToken_calcSizeBinary(const UA_AnonymousIdentityToken *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN]); } static UA_INLINE UA_StatusCode UA_AnonymousIdentityToken_encodeBinary(const UA_AnonymousIdentityToken *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_AnonymousIdentityToken_decodeBinary(const UA_ByteString *src, size_t *offset, UA_AnonymousIdentityToken *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN], NULL); } /* FilterOperator */ static UA_INLINE size_t UA_FilterOperator_calcSizeBinary(const UA_FilterOperator *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_FILTEROPERATOR]); } static UA_INLINE UA_StatusCode UA_FilterOperator_encodeBinary(const UA_FilterOperator *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_FILTEROPERATOR], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_FilterOperator_decodeBinary(const UA_ByteString *src, size_t *offset, UA_FilterOperator *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_FILTEROPERATOR], NULL); } /* AggregateFilter */ static UA_INLINE size_t UA_AggregateFilter_calcSizeBinary(const UA_AggregateFilter *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_AGGREGATEFILTER]); } static UA_INLINE UA_StatusCode UA_AggregateFilter_encodeBinary(const UA_AggregateFilter *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_AGGREGATEFILTER], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_AggregateFilter_decodeBinary(const UA_ByteString *src, size_t *offset, UA_AggregateFilter *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_AGGREGATEFILTER], NULL); } /* RepublishResponse */ static UA_INLINE size_t UA_RepublishResponse_calcSizeBinary(const UA_RepublishResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_REPUBLISHRESPONSE]); } static UA_INLINE UA_StatusCode UA_RepublishResponse_encodeBinary(const UA_RepublishResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_REPUBLISHRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_RepublishResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_RepublishResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_REPUBLISHRESPONSE], NULL); } /* DeleteSubscriptionsResponse */ static UA_INLINE size_t UA_DeleteSubscriptionsResponse_calcSizeBinary(const UA_DeleteSubscriptionsResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSRESPONSE]); } static UA_INLINE UA_StatusCode UA_DeleteSubscriptionsResponse_encodeBinary(const UA_DeleteSubscriptionsResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_DeleteSubscriptionsResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_DeleteSubscriptionsResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSRESPONSE], NULL); } /* RegisterNodesRequest */ static UA_INLINE size_t UA_RegisterNodesRequest_calcSizeBinary(const UA_RegisterNodesRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_REGISTERNODESREQUEST]); } static UA_INLINE UA_StatusCode UA_RegisterNodesRequest_encodeBinary(const UA_RegisterNodesRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_REGISTERNODESREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_RegisterNodesRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_RegisterNodesRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_REGISTERNODESREQUEST], NULL); } /* MethodAttributes */ static UA_INLINE size_t UA_MethodAttributes_calcSizeBinary(const UA_MethodAttributes *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_METHODATTRIBUTES]); } static UA_INLINE UA_StatusCode UA_MethodAttributes_encodeBinary(const UA_MethodAttributes *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_METHODATTRIBUTES], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_MethodAttributes_decodeBinary(const UA_ByteString *src, size_t *offset, UA_MethodAttributes *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_METHODATTRIBUTES], NULL); } /* UserNameIdentityToken */ static UA_INLINE size_t UA_UserNameIdentityToken_calcSizeBinary(const UA_UserNameIdentityToken *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]); } static UA_INLINE UA_StatusCode UA_UserNameIdentityToken_encodeBinary(const UA_UserNameIdentityToken *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_UserNameIdentityToken_decodeBinary(const UA_ByteString *src, size_t *offset, UA_UserNameIdentityToken *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN], NULL); } /* UnregisterNodesRequest */ static UA_INLINE size_t UA_UnregisterNodesRequest_calcSizeBinary(const UA_UnregisterNodesRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_UNREGISTERNODESREQUEST]); } static UA_INLINE UA_StatusCode UA_UnregisterNodesRequest_encodeBinary(const UA_UnregisterNodesRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_UNREGISTERNODESREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_UnregisterNodesRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_UnregisterNodesRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_UNREGISTERNODESREQUEST], NULL); } /* OpenSecureChannelResponse */ static UA_INLINE size_t UA_OpenSecureChannelResponse_calcSizeBinary(const UA_OpenSecureChannelResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE]); } static UA_INLINE UA_StatusCode UA_OpenSecureChannelResponse_encodeBinary(const UA_OpenSecureChannelResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_OpenSecureChannelResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_OpenSecureChannelResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE], NULL); } /* SetTriggeringResponse */ static UA_INLINE size_t UA_SetTriggeringResponse_calcSizeBinary(const UA_SetTriggeringResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_SETTRIGGERINGRESPONSE]); } static UA_INLINE UA_StatusCode UA_SetTriggeringResponse_encodeBinary(const UA_SetTriggeringResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_SETTRIGGERINGRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_SetTriggeringResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_SetTriggeringResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_SETTRIGGERINGRESPONSE], NULL); } /* SimpleAttributeOperand */ static UA_INLINE size_t UA_SimpleAttributeOperand_calcSizeBinary(const UA_SimpleAttributeOperand *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND]); } static UA_INLINE UA_StatusCode UA_SimpleAttributeOperand_encodeBinary(const UA_SimpleAttributeOperand *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_SimpleAttributeOperand_decodeBinary(const UA_ByteString *src, size_t *offset, UA_SimpleAttributeOperand *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND], NULL); } /* RepublishRequest */ static UA_INLINE size_t UA_RepublishRequest_calcSizeBinary(const UA_RepublishRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_REPUBLISHREQUEST]); } static UA_INLINE UA_StatusCode UA_RepublishRequest_encodeBinary(const UA_RepublishRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_REPUBLISHREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_RepublishRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_RepublishRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_REPUBLISHREQUEST], NULL); } /* RegisterNodesResponse */ static UA_INLINE size_t UA_RegisterNodesResponse_calcSizeBinary(const UA_RegisterNodesResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_REGISTERNODESRESPONSE]); } static UA_INLINE UA_StatusCode UA_RegisterNodesResponse_encodeBinary(const UA_RegisterNodesResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_REGISTERNODESRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_RegisterNodesResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_RegisterNodesResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_REGISTERNODESRESPONSE], NULL); } /* ModifyMonitoredItemsResponse */ static UA_INLINE size_t UA_ModifyMonitoredItemsResponse_calcSizeBinary(const UA_ModifyMonitoredItemsResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSRESPONSE]); } static UA_INLINE UA_StatusCode UA_ModifyMonitoredItemsResponse_encodeBinary(const UA_ModifyMonitoredItemsResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ModifyMonitoredItemsResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ModifyMonitoredItemsResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSRESPONSE], NULL); } /* DeleteSubscriptionsRequest */ static UA_INLINE size_t UA_DeleteSubscriptionsRequest_calcSizeBinary(const UA_DeleteSubscriptionsRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSREQUEST]); } static UA_INLINE UA_StatusCode UA_DeleteSubscriptionsRequest_encodeBinary(const UA_DeleteSubscriptionsRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_DeleteSubscriptionsRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_DeleteSubscriptionsRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSREQUEST], NULL); } /* RedundancySupport */ static UA_INLINE size_t UA_RedundancySupport_calcSizeBinary(const UA_RedundancySupport *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_REDUNDANCYSUPPORT]); } static UA_INLINE UA_StatusCode UA_RedundancySupport_encodeBinary(const UA_RedundancySupport *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_REDUNDANCYSUPPORT], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_RedundancySupport_decodeBinary(const UA_ByteString *src, size_t *offset, UA_RedundancySupport *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_REDUNDANCYSUPPORT], NULL); } /* BrowsePath */ static UA_INLINE size_t UA_BrowsePath_calcSizeBinary(const UA_BrowsePath *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_BROWSEPATH]); } static UA_INLINE UA_StatusCode UA_BrowsePath_encodeBinary(const UA_BrowsePath *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_BROWSEPATH], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_BrowsePath_decodeBinary(const UA_ByteString *src, size_t *offset, UA_BrowsePath *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_BROWSEPATH], NULL); } /* ObjectAttributes */ static UA_INLINE size_t UA_ObjectAttributes_calcSizeBinary(const UA_ObjectAttributes *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES]); } static UA_INLINE UA_StatusCode UA_ObjectAttributes_encodeBinary(const UA_ObjectAttributes *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ObjectAttributes_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ObjectAttributes *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES], NULL); } /* PublishRequest */ static UA_INLINE size_t UA_PublishRequest_calcSizeBinary(const UA_PublishRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_PUBLISHREQUEST]); } static UA_INLINE UA_StatusCode UA_PublishRequest_encodeBinary(const UA_PublishRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_PUBLISHREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_PublishRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_PublishRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_PUBLISHREQUEST], NULL); } /* FindServersRequest */ static UA_INLINE size_t UA_FindServersRequest_calcSizeBinary(const UA_FindServersRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_FINDSERVERSREQUEST]); } static UA_INLINE UA_StatusCode UA_FindServersRequest_encodeBinary(const UA_FindServersRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_FINDSERVERSREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_FindServersRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_FindServersRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_FINDSERVERSREQUEST], NULL); } /* FindServersOnNetworkResponse */ static UA_INLINE size_t UA_FindServersOnNetworkResponse_calcSizeBinary(const UA_FindServersOnNetworkResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKRESPONSE]); } static UA_INLINE UA_StatusCode UA_FindServersOnNetworkResponse_encodeBinary(const UA_FindServersOnNetworkResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_FindServersOnNetworkResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_FindServersOnNetworkResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKRESPONSE], NULL); } /* ReferenceDescription */ static UA_INLINE size_t UA_ReferenceDescription_calcSizeBinary(const UA_ReferenceDescription *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_REFERENCEDESCRIPTION]); } static UA_INLINE UA_StatusCode UA_ReferenceDescription_encodeBinary(const UA_ReferenceDescription *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_REFERENCEDESCRIPTION], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ReferenceDescription_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ReferenceDescription *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_REFERENCEDESCRIPTION], NULL); } /* CreateSubscriptionRequest */ static UA_INLINE size_t UA_CreateSubscriptionRequest_calcSizeBinary(const UA_CreateSubscriptionRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONREQUEST]); } static UA_INLINE UA_StatusCode UA_CreateSubscriptionRequest_encodeBinary(const UA_CreateSubscriptionRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_CreateSubscriptionRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_CreateSubscriptionRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONREQUEST], NULL); } /* FindServersOnNetworkRequest */ static UA_INLINE size_t UA_FindServersOnNetworkRequest_calcSizeBinary(const UA_FindServersOnNetworkRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKREQUEST]); } static UA_INLINE UA_StatusCode UA_FindServersOnNetworkRequest_encodeBinary(const UA_FindServersOnNetworkRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_FindServersOnNetworkRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_FindServersOnNetworkRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKREQUEST], NULL); } /* CallResponse */ static UA_INLINE size_t UA_CallResponse_calcSizeBinary(const UA_CallResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_CALLRESPONSE]); } static UA_INLINE UA_StatusCode UA_CallResponse_encodeBinary(const UA_CallResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_CALLRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_CallResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_CallResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_CALLRESPONSE], NULL); } /* DeleteNodesResponse */ static UA_INLINE size_t UA_DeleteNodesResponse_calcSizeBinary(const UA_DeleteNodesResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_DELETENODESRESPONSE]); } static UA_INLINE UA_StatusCode UA_DeleteNodesResponse_encodeBinary(const UA_DeleteNodesResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_DELETENODESRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_DeleteNodesResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_DeleteNodesResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_DELETENODESRESPONSE], NULL); } /* ModifyMonitoredItemsRequest */ static UA_INLINE size_t UA_ModifyMonitoredItemsRequest_calcSizeBinary(const UA_ModifyMonitoredItemsRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSREQUEST]); } static UA_INLINE UA_StatusCode UA_ModifyMonitoredItemsRequest_encodeBinary(const UA_ModifyMonitoredItemsRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ModifyMonitoredItemsRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ModifyMonitoredItemsRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSREQUEST], NULL); } /* ServiceFault */ static UA_INLINE size_t UA_ServiceFault_calcSizeBinary(const UA_ServiceFault *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_SERVICEFAULT]); } static UA_INLINE UA_StatusCode UA_ServiceFault_encodeBinary(const UA_ServiceFault *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_SERVICEFAULT], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ServiceFault_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ServiceFault *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_SERVICEFAULT], NULL); } /* PublishResponse */ static UA_INLINE size_t UA_PublishResponse_calcSizeBinary(const UA_PublishResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]); } static UA_INLINE UA_StatusCode UA_PublishResponse_encodeBinary(const UA_PublishResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_PUBLISHRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_PublishResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_PublishResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_PUBLISHRESPONSE], NULL); } /* CreateMonitoredItemsRequest */ static UA_INLINE size_t UA_CreateMonitoredItemsRequest_calcSizeBinary(const UA_CreateMonitoredItemsRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSREQUEST]); } static UA_INLINE UA_StatusCode UA_CreateMonitoredItemsRequest_encodeBinary(const UA_CreateMonitoredItemsRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_CreateMonitoredItemsRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_CreateMonitoredItemsRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSREQUEST], NULL); } /* OpenSecureChannelRequest */ static UA_INLINE size_t UA_OpenSecureChannelRequest_calcSizeBinary(const UA_OpenSecureChannelRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_OPENSECURECHANNELREQUEST]); } static UA_INLINE UA_StatusCode UA_OpenSecureChannelRequest_encodeBinary(const UA_OpenSecureChannelRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_OPENSECURECHANNELREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_OpenSecureChannelRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_OpenSecureChannelRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_OPENSECURECHANNELREQUEST], NULL); } /* CloseSessionRequest */ static UA_INLINE size_t UA_CloseSessionRequest_calcSizeBinary(const UA_CloseSessionRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST]); } static UA_INLINE UA_StatusCode UA_CloseSessionRequest_encodeBinary(const UA_CloseSessionRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_CloseSessionRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_CloseSessionRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST], NULL); } /* SetTriggeringRequest */ static UA_INLINE size_t UA_SetTriggeringRequest_calcSizeBinary(const UA_SetTriggeringRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_SETTRIGGERINGREQUEST]); } static UA_INLINE UA_StatusCode UA_SetTriggeringRequest_encodeBinary(const UA_SetTriggeringRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_SETTRIGGERINGREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_SetTriggeringRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_SetTriggeringRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_SETTRIGGERINGREQUEST], NULL); } /* BrowseResult */ static UA_INLINE size_t UA_BrowseResult_calcSizeBinary(const UA_BrowseResult *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_BROWSERESULT]); } static UA_INLINE UA_StatusCode UA_BrowseResult_encodeBinary(const UA_BrowseResult *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_BROWSERESULT], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_BrowseResult_decodeBinary(const UA_ByteString *src, size_t *offset, UA_BrowseResult *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_BROWSERESULT], NULL); } /* AddReferencesRequest */ static UA_INLINE size_t UA_AddReferencesRequest_calcSizeBinary(const UA_AddReferencesRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_ADDREFERENCESREQUEST]); } static UA_INLINE UA_StatusCode UA_AddReferencesRequest_encodeBinary(const UA_AddReferencesRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_ADDREFERENCESREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_AddReferencesRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_AddReferencesRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_ADDREFERENCESREQUEST], NULL); } /* AddNodesItem */ static UA_INLINE size_t UA_AddNodesItem_calcSizeBinary(const UA_AddNodesItem *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_ADDNODESITEM]); } static UA_INLINE UA_StatusCode UA_AddNodesItem_encodeBinary(const UA_AddNodesItem *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_ADDNODESITEM], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_AddNodesItem_decodeBinary(const UA_ByteString *src, size_t *offset, UA_AddNodesItem *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_ADDNODESITEM], NULL); } /* ServerStatusDataType */ static UA_INLINE size_t UA_ServerStatusDataType_calcSizeBinary(const UA_ServerStatusDataType *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_SERVERSTATUSDATATYPE]); } static UA_INLINE UA_StatusCode UA_ServerStatusDataType_encodeBinary(const UA_ServerStatusDataType *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_SERVERSTATUSDATATYPE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ServerStatusDataType_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ServerStatusDataType *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_SERVERSTATUSDATATYPE], NULL); } /* BrowseNextResponse */ static UA_INLINE size_t UA_BrowseNextResponse_calcSizeBinary(const UA_BrowseNextResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_BROWSENEXTRESPONSE]); } static UA_INLINE UA_StatusCode UA_BrowseNextResponse_encodeBinary(const UA_BrowseNextResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_BROWSENEXTRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_BrowseNextResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_BrowseNextResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_BROWSENEXTRESPONSE], NULL); } /* RegisteredServer */ static UA_INLINE size_t UA_RegisteredServer_calcSizeBinary(const UA_RegisteredServer *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_REGISTEREDSERVER]); } static UA_INLINE UA_StatusCode UA_RegisteredServer_encodeBinary(const UA_RegisteredServer *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_REGISTEREDSERVER], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_RegisteredServer_decodeBinary(const UA_ByteString *src, size_t *offset, UA_RegisteredServer *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_REGISTEREDSERVER], NULL); } /* ApplicationDescription */ static UA_INLINE size_t UA_ApplicationDescription_calcSizeBinary(const UA_ApplicationDescription *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]); } static UA_INLINE UA_StatusCode UA_ApplicationDescription_encodeBinary(const UA_ApplicationDescription *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ApplicationDescription_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ApplicationDescription *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION], NULL); } /* ReadRequest */ static UA_INLINE size_t UA_ReadRequest_calcSizeBinary(const UA_ReadRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_READREQUEST]); } static UA_INLINE UA_StatusCode UA_ReadRequest_encodeBinary(const UA_ReadRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_READREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ReadRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ReadRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_READREQUEST], NULL); } /* ActivateSessionRequest */ static UA_INLINE size_t UA_ActivateSessionRequest_calcSizeBinary(const UA_ActivateSessionRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST]); } static UA_INLINE UA_StatusCode UA_ActivateSessionRequest_encodeBinary(const UA_ActivateSessionRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ActivateSessionRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ActivateSessionRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST], NULL); } /* BrowsePathResult */ static UA_INLINE size_t UA_BrowsePathResult_calcSizeBinary(const UA_BrowsePathResult *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_BROWSEPATHRESULT]); } static UA_INLINE UA_StatusCode UA_BrowsePathResult_encodeBinary(const UA_BrowsePathResult *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_BROWSEPATHRESULT], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_BrowsePathResult_decodeBinary(const UA_ByteString *src, size_t *offset, UA_BrowsePathResult *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_BROWSEPATHRESULT], NULL); } /* AddNodesRequest */ static UA_INLINE size_t UA_AddNodesRequest_calcSizeBinary(const UA_AddNodesRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_ADDNODESREQUEST]); } static UA_INLINE UA_StatusCode UA_AddNodesRequest_encodeBinary(const UA_AddNodesRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_ADDNODESREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_AddNodesRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_AddNodesRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_ADDNODESREQUEST], NULL); } /* BrowseRequest */ static UA_INLINE size_t UA_BrowseRequest_calcSizeBinary(const UA_BrowseRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_BROWSEREQUEST]); } static UA_INLINE UA_StatusCode UA_BrowseRequest_encodeBinary(const UA_BrowseRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_BROWSEREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_BrowseRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_BrowseRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_BROWSEREQUEST], NULL); } /* WriteRequest */ static UA_INLINE size_t UA_WriteRequest_calcSizeBinary(const UA_WriteRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_WRITEREQUEST]); } static UA_INLINE UA_StatusCode UA_WriteRequest_encodeBinary(const UA_WriteRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_WRITEREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_WriteRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_WriteRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_WRITEREQUEST], NULL); } /* AddNodesResponse */ static UA_INLINE size_t UA_AddNodesResponse_calcSizeBinary(const UA_AddNodesResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_ADDNODESRESPONSE]); } static UA_INLINE UA_StatusCode UA_AddNodesResponse_encodeBinary(const UA_AddNodesResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_ADDNODESRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_AddNodesResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_AddNodesResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_ADDNODESRESPONSE], NULL); } /* RegisterServer2Request */ static UA_INLINE size_t UA_RegisterServer2Request_calcSizeBinary(const UA_RegisterServer2Request *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_REGISTERSERVER2REQUEST]); } static UA_INLINE UA_StatusCode UA_RegisterServer2Request_encodeBinary(const UA_RegisterServer2Request *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_REGISTERSERVER2REQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_RegisterServer2Request_decodeBinary(const UA_ByteString *src, size_t *offset, UA_RegisterServer2Request *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_REGISTERSERVER2REQUEST], NULL); } /* AttributeOperand */ static UA_INLINE size_t UA_AttributeOperand_calcSizeBinary(const UA_AttributeOperand *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_ATTRIBUTEOPERAND]); } static UA_INLINE UA_StatusCode UA_AttributeOperand_encodeBinary(const UA_AttributeOperand *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_ATTRIBUTEOPERAND], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_AttributeOperand_decodeBinary(const UA_ByteString *src, size_t *offset, UA_AttributeOperand *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_ATTRIBUTEOPERAND], NULL); } /* DataChangeFilter */ static UA_INLINE size_t UA_DataChangeFilter_calcSizeBinary(const UA_DataChangeFilter *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_DATACHANGEFILTER]); } static UA_INLINE UA_StatusCode UA_DataChangeFilter_encodeBinary(const UA_DataChangeFilter *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_DATACHANGEFILTER], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_DataChangeFilter_decodeBinary(const UA_ByteString *src, size_t *offset, UA_DataChangeFilter *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_DATACHANGEFILTER], NULL); } /* EndpointDescription */ static UA_INLINE size_t UA_EndpointDescription_calcSizeBinary(const UA_EndpointDescription *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]); } static UA_INLINE UA_StatusCode UA_EndpointDescription_encodeBinary(const UA_EndpointDescription *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_EndpointDescription_decodeBinary(const UA_ByteString *src, size_t *offset, UA_EndpointDescription *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION], NULL); } /* DeleteReferencesRequest */ static UA_INLINE size_t UA_DeleteReferencesRequest_calcSizeBinary(const UA_DeleteReferencesRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_DELETEREFERENCESREQUEST]); } static UA_INLINE UA_StatusCode UA_DeleteReferencesRequest_encodeBinary(const UA_DeleteReferencesRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_DELETEREFERENCESREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_DeleteReferencesRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_DeleteReferencesRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_DELETEREFERENCESREQUEST], NULL); } /* TranslateBrowsePathsToNodeIdsRequest */ static UA_INLINE size_t UA_TranslateBrowsePathsToNodeIdsRequest_calcSizeBinary(const UA_TranslateBrowsePathsToNodeIdsRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSREQUEST]); } static UA_INLINE UA_StatusCode UA_TranslateBrowsePathsToNodeIdsRequest_encodeBinary(const UA_TranslateBrowsePathsToNodeIdsRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_TranslateBrowsePathsToNodeIdsRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_TranslateBrowsePathsToNodeIdsRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSREQUEST], NULL); } /* FindServersResponse */ static UA_INLINE size_t UA_FindServersResponse_calcSizeBinary(const UA_FindServersResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_FINDSERVERSRESPONSE]); } static UA_INLINE UA_StatusCode UA_FindServersResponse_encodeBinary(const UA_FindServersResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_FINDSERVERSRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_FindServersResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_FindServersResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_FINDSERVERSRESPONSE], NULL); } /* CreateSessionRequest */ static UA_INLINE size_t UA_CreateSessionRequest_calcSizeBinary(const UA_CreateSessionRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST]); } static UA_INLINE UA_StatusCode UA_CreateSessionRequest_encodeBinary(const UA_CreateSessionRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_CreateSessionRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_CreateSessionRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST], NULL); } /* ContentFilterElement */ static UA_INLINE size_t UA_ContentFilterElement_calcSizeBinary(const UA_ContentFilterElement *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_CONTENTFILTERELEMENT]); } static UA_INLINE UA_StatusCode UA_ContentFilterElement_encodeBinary(const UA_ContentFilterElement *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_CONTENTFILTERELEMENT], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ContentFilterElement_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ContentFilterElement *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_CONTENTFILTERELEMENT], NULL); } /* RegisterServerRequest */ static UA_INLINE size_t UA_RegisterServerRequest_calcSizeBinary(const UA_RegisterServerRequest *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_REGISTERSERVERREQUEST]); } static UA_INLINE UA_StatusCode UA_RegisterServerRequest_encodeBinary(const UA_RegisterServerRequest *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_REGISTERSERVERREQUEST], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_RegisterServerRequest_decodeBinary(const UA_ByteString *src, size_t *offset, UA_RegisterServerRequest *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_REGISTERSERVERREQUEST], NULL); } /* TranslateBrowsePathsToNodeIdsResponse */ static UA_INLINE size_t UA_TranslateBrowsePathsToNodeIdsResponse_calcSizeBinary(const UA_TranslateBrowsePathsToNodeIdsResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE]); } static UA_INLINE UA_StatusCode UA_TranslateBrowsePathsToNodeIdsResponse_encodeBinary(const UA_TranslateBrowsePathsToNodeIdsResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_TranslateBrowsePathsToNodeIdsResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_TranslateBrowsePathsToNodeIdsResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE], NULL); } /* BrowseResponse */ static UA_INLINE size_t UA_BrowseResponse_calcSizeBinary(const UA_BrowseResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_BROWSERESPONSE]); } static UA_INLINE UA_StatusCode UA_BrowseResponse_encodeBinary(const UA_BrowseResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_BROWSERESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_BrowseResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_BrowseResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_BROWSERESPONSE], NULL); } /* CreateSessionResponse */ static UA_INLINE size_t UA_CreateSessionResponse_calcSizeBinary(const UA_CreateSessionResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE]); } static UA_INLINE UA_StatusCode UA_CreateSessionResponse_encodeBinary(const UA_CreateSessionResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_CreateSessionResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_CreateSessionResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE], NULL); } /* ContentFilter */ static UA_INLINE size_t UA_ContentFilter_calcSizeBinary(const UA_ContentFilter *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_CONTENTFILTER]); } static UA_INLINE UA_StatusCode UA_ContentFilter_encodeBinary(const UA_ContentFilter *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_CONTENTFILTER], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ContentFilter_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ContentFilter *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_CONTENTFILTER], NULL); } /* GetEndpointsResponse */ static UA_INLINE size_t UA_GetEndpointsResponse_calcSizeBinary(const UA_GetEndpointsResponse *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE]); } static UA_INLINE UA_StatusCode UA_GetEndpointsResponse_encodeBinary(const UA_GetEndpointsResponse *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_GetEndpointsResponse_decodeBinary(const UA_ByteString *src, size_t *offset, UA_GetEndpointsResponse *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE], NULL); } /* EventFilter */ static UA_INLINE size_t UA_EventFilter_calcSizeBinary(const UA_EventFilter *src) { return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_EVENTFILTER]); } static UA_INLINE UA_StatusCode UA_EventFilter_encodeBinary(const UA_EventFilter *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TYPES[UA_TYPES_EVENTFILTER], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_EventFilter_decodeBinary(const UA_ByteString *src, size_t *offset, UA_EventFilter *dst) { return UA_decodeBinary(src, offset, dst, &UA_TYPES[UA_TYPES_EVENTFILTER], NULL); } /*********************************** amalgamated original file "/home/jvoe/open62541/build/src_generated/open62541/transport_generated.h" ***********************************/ /* Generated from Opc.Ua.Types.bsd, Custom.Opc.Ua.Transport.bsd with script /home/jvoe/open62541/tools/generate_datatypes.py * on host rigel by user jvoe at 2019-09-27 03:59:36 */ #ifdef UA_ENABLE_AMALGAMATION #else #endif _UA_BEGIN_DECLS /** * Every type is assigned an index in an array containing the type descriptions. * These descriptions are used during type handling (copying, deletion, * binary encoding, ...). */ #define UA_TRANSPORT_COUNT 12 extern UA_EXPORT const UA_DataType UA_TRANSPORT[UA_TRANSPORT_COUNT]; /** * SecureConversationMessageAbortBody * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Secure Conversation Message Abort Body */ typedef struct { UA_UInt32 error; UA_String reason; } UA_SecureConversationMessageAbortBody; #define UA_TRANSPORT_SECURECONVERSATIONMESSAGEABORTBODY 0 /** * SecureConversationMessageFooter * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Secure Conversation Message Footer */ typedef struct { size_t paddingSize; UA_Byte *padding; UA_Byte signature; } UA_SecureConversationMessageFooter; #define UA_TRANSPORT_SECURECONVERSATIONMESSAGEFOOTER 1 /** * TcpHelloMessage * ^^^^^^^^^^^^^^^ * Hello Message */ typedef struct { UA_UInt32 protocolVersion; UA_UInt32 receiveBufferSize; UA_UInt32 sendBufferSize; UA_UInt32 maxMessageSize; UA_UInt32 maxChunkCount; UA_String endpointUrl; } UA_TcpHelloMessage; #define UA_TRANSPORT_TCPHELLOMESSAGE 2 /** * TcpErrorMessage * ^^^^^^^^^^^^^^^ * Error Message */ typedef struct { UA_UInt32 error; UA_String reason; } UA_TcpErrorMessage; #define UA_TRANSPORT_TCPERRORMESSAGE 3 /** * MessageType * ^^^^^^^^^^^ * Message Type and whether the message contains an intermediate chunk */ typedef enum { UA_MESSAGETYPE_ACK = 0x4B4341, UA_MESSAGETYPE_HEL = 0x4C4548, UA_MESSAGETYPE_MSG = 0x47534D, UA_MESSAGETYPE_OPN = 0x4E504F, UA_MESSAGETYPE_CLO = 0x4F4C43, UA_MESSAGETYPE_ERR = 0x525245, __UA_MESSAGETYPE_FORCE32BIT = 0x7fffffff } UA_MessageType; UA_STATIC_ASSERT(sizeof(UA_MessageType) == sizeof(UA_Int32), enum_must_be_32bit); #define UA_TRANSPORT_MESSAGETYPE 4 /** * AsymmetricAlgorithmSecurityHeader * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Security Header */ typedef struct { UA_ByteString securityPolicyUri; UA_ByteString senderCertificate; UA_ByteString receiverCertificateThumbprint; } UA_AsymmetricAlgorithmSecurityHeader; #define UA_TRANSPORT_ASYMMETRICALGORITHMSECURITYHEADER 5 /** * TcpAcknowledgeMessage * ^^^^^^^^^^^^^^^^^^^^^ * Acknowledge Message */ typedef struct { UA_UInt32 protocolVersion; UA_UInt32 receiveBufferSize; UA_UInt32 sendBufferSize; UA_UInt32 maxMessageSize; UA_UInt32 maxChunkCount; } UA_TcpAcknowledgeMessage; #define UA_TRANSPORT_TCPACKNOWLEDGEMESSAGE 6 /** * SequenceHeader * ^^^^^^^^^^^^^^ * Secure Layer Sequence Header */ typedef struct { UA_UInt32 sequenceNumber; UA_UInt32 requestId; } UA_SequenceHeader; #define UA_TRANSPORT_SEQUENCEHEADER 7 /** * TcpMessageHeader * ^^^^^^^^^^^^^^^^ * TCP Header */ typedef struct { UA_UInt32 messageTypeAndChunkType; UA_UInt32 messageSize; } UA_TcpMessageHeader; #define UA_TRANSPORT_TCPMESSAGEHEADER 8 /** * ChunkType * ^^^^^^^^^ * Type of the chunk */ typedef enum { UA_CHUNKTYPE_FINAL = 0x46000000, UA_CHUNKTYPE_INTERMEDIATE = 0x43000000, UA_CHUNKTYPE_ABORT = 0x41000000, __UA_CHUNKTYPE_FORCE32BIT = 0x7fffffff } UA_ChunkType; UA_STATIC_ASSERT(sizeof(UA_ChunkType) == sizeof(UA_Int32), enum_must_be_32bit); #define UA_TRANSPORT_CHUNKTYPE 9 /** * SymmetricAlgorithmSecurityHeader * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Secure Layer Symmetric Algorithm Header */ typedef struct { UA_UInt32 tokenId; } UA_SymmetricAlgorithmSecurityHeader; #define UA_TRANSPORT_SYMMETRICALGORITHMSECURITYHEADER 10 /** * SecureConversationMessageHeader * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Secure Layer Sequence Header */ typedef struct { UA_TcpMessageHeader messageHeader; UA_UInt32 secureChannelId; } UA_SecureConversationMessageHeader; #define UA_TRANSPORT_SECURECONVERSATIONMESSAGEHEADER 11 _UA_END_DECLS /*********************************** amalgamated original file "/home/jvoe/open62541/build/src_generated/open62541/transport_generated_handling.h" ***********************************/ /* Generated from Opc.Ua.Types.bsd, Custom.Opc.Ua.Transport.bsd with script /home/jvoe/open62541/tools/generate_datatypes.py * on host rigel by user jvoe at 2019-09-27 03:59:36 */ _UA_BEGIN_DECLS #if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6 # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wmissing-field-initializers" # pragma GCC diagnostic ignored "-Wmissing-braces" #endif /* SecureConversationMessageAbortBody */ static UA_INLINE void UA_SecureConversationMessageAbortBody_init(UA_SecureConversationMessageAbortBody *p) { memset(p, 0, sizeof(UA_SecureConversationMessageAbortBody)); } static UA_INLINE UA_SecureConversationMessageAbortBody * UA_SecureConversationMessageAbortBody_new(void) { return (UA_SecureConversationMessageAbortBody*)UA_new(&UA_TRANSPORT[UA_TRANSPORT_SECURECONVERSATIONMESSAGEABORTBODY]); } static UA_INLINE UA_StatusCode UA_SecureConversationMessageAbortBody_copy(const UA_SecureConversationMessageAbortBody *src, UA_SecureConversationMessageAbortBody *dst) { return UA_copy(src, dst, &UA_TRANSPORT[UA_TRANSPORT_SECURECONVERSATIONMESSAGEABORTBODY]); } static UA_INLINE void UA_SecureConversationMessageAbortBody_deleteMembers(UA_SecureConversationMessageAbortBody *p) { UA_clear(p, &UA_TRANSPORT[UA_TRANSPORT_SECURECONVERSATIONMESSAGEABORTBODY]); } static UA_INLINE void UA_SecureConversationMessageAbortBody_clear(UA_SecureConversationMessageAbortBody *p) { UA_clear(p, &UA_TRANSPORT[UA_TRANSPORT_SECURECONVERSATIONMESSAGEABORTBODY]); } static UA_INLINE void UA_SecureConversationMessageAbortBody_delete(UA_SecureConversationMessageAbortBody *p) { UA_delete(p, &UA_TRANSPORT[UA_TRANSPORT_SECURECONVERSATIONMESSAGEABORTBODY]); } /* SecureConversationMessageFooter */ static UA_INLINE void UA_SecureConversationMessageFooter_init(UA_SecureConversationMessageFooter *p) { memset(p, 0, sizeof(UA_SecureConversationMessageFooter)); } static UA_INLINE UA_SecureConversationMessageFooter * UA_SecureConversationMessageFooter_new(void) { return (UA_SecureConversationMessageFooter*)UA_new(&UA_TRANSPORT[UA_TRANSPORT_SECURECONVERSATIONMESSAGEFOOTER]); } static UA_INLINE UA_StatusCode UA_SecureConversationMessageFooter_copy(const UA_SecureConversationMessageFooter *src, UA_SecureConversationMessageFooter *dst) { return UA_copy(src, dst, &UA_TRANSPORT[UA_TRANSPORT_SECURECONVERSATIONMESSAGEFOOTER]); } static UA_INLINE void UA_SecureConversationMessageFooter_deleteMembers(UA_SecureConversationMessageFooter *p) { UA_clear(p, &UA_TRANSPORT[UA_TRANSPORT_SECURECONVERSATIONMESSAGEFOOTER]); } static UA_INLINE void UA_SecureConversationMessageFooter_clear(UA_SecureConversationMessageFooter *p) { UA_clear(p, &UA_TRANSPORT[UA_TRANSPORT_SECURECONVERSATIONMESSAGEFOOTER]); } static UA_INLINE void UA_SecureConversationMessageFooter_delete(UA_SecureConversationMessageFooter *p) { UA_delete(p, &UA_TRANSPORT[UA_TRANSPORT_SECURECONVERSATIONMESSAGEFOOTER]); } /* TcpHelloMessage */ static UA_INLINE void UA_TcpHelloMessage_init(UA_TcpHelloMessage *p) { memset(p, 0, sizeof(UA_TcpHelloMessage)); } static UA_INLINE UA_TcpHelloMessage * UA_TcpHelloMessage_new(void) { return (UA_TcpHelloMessage*)UA_new(&UA_TRANSPORT[UA_TRANSPORT_TCPHELLOMESSAGE]); } static UA_INLINE UA_StatusCode UA_TcpHelloMessage_copy(const UA_TcpHelloMessage *src, UA_TcpHelloMessage *dst) { return UA_copy(src, dst, &UA_TRANSPORT[UA_TRANSPORT_TCPHELLOMESSAGE]); } static UA_INLINE void UA_TcpHelloMessage_deleteMembers(UA_TcpHelloMessage *p) { UA_clear(p, &UA_TRANSPORT[UA_TRANSPORT_TCPHELLOMESSAGE]); } static UA_INLINE void UA_TcpHelloMessage_clear(UA_TcpHelloMessage *p) { UA_clear(p, &UA_TRANSPORT[UA_TRANSPORT_TCPHELLOMESSAGE]); } static UA_INLINE void UA_TcpHelloMessage_delete(UA_TcpHelloMessage *p) { UA_delete(p, &UA_TRANSPORT[UA_TRANSPORT_TCPHELLOMESSAGE]); } /* TcpErrorMessage */ static UA_INLINE void UA_TcpErrorMessage_init(UA_TcpErrorMessage *p) { memset(p, 0, sizeof(UA_TcpErrorMessage)); } static UA_INLINE UA_TcpErrorMessage * UA_TcpErrorMessage_new(void) { return (UA_TcpErrorMessage*)UA_new(&UA_TRANSPORT[UA_TRANSPORT_TCPERRORMESSAGE]); } static UA_INLINE UA_StatusCode UA_TcpErrorMessage_copy(const UA_TcpErrorMessage *src, UA_TcpErrorMessage *dst) { return UA_copy(src, dst, &UA_TRANSPORT[UA_TRANSPORT_TCPERRORMESSAGE]); } static UA_INLINE void UA_TcpErrorMessage_deleteMembers(UA_TcpErrorMessage *p) { UA_clear(p, &UA_TRANSPORT[UA_TRANSPORT_TCPERRORMESSAGE]); } static UA_INLINE void UA_TcpErrorMessage_clear(UA_TcpErrorMessage *p) { UA_clear(p, &UA_TRANSPORT[UA_TRANSPORT_TCPERRORMESSAGE]); } static UA_INLINE void UA_TcpErrorMessage_delete(UA_TcpErrorMessage *p) { UA_delete(p, &UA_TRANSPORT[UA_TRANSPORT_TCPERRORMESSAGE]); } /* MessageType */ static UA_INLINE void UA_MessageType_init(UA_MessageType *p) { memset(p, 0, sizeof(UA_MessageType)); } static UA_INLINE UA_MessageType * UA_MessageType_new(void) { return (UA_MessageType*)UA_new(&UA_TRANSPORT[UA_TRANSPORT_MESSAGETYPE]); } static UA_INLINE UA_StatusCode UA_MessageType_copy(const UA_MessageType *src, UA_MessageType *dst) { *dst = *src; return UA_STATUSCODE_GOOD; } static UA_INLINE void UA_MessageType_deleteMembers(UA_MessageType *p) { memset(p, 0, sizeof(UA_MessageType)); } static UA_INLINE void UA_MessageType_clear(UA_MessageType *p) { memset(p, 0, sizeof(UA_MessageType)); } static UA_INLINE void UA_MessageType_delete(UA_MessageType *p) { UA_delete(p, &UA_TRANSPORT[UA_TRANSPORT_MESSAGETYPE]); } /* AsymmetricAlgorithmSecurityHeader */ static UA_INLINE void UA_AsymmetricAlgorithmSecurityHeader_init(UA_AsymmetricAlgorithmSecurityHeader *p) { memset(p, 0, sizeof(UA_AsymmetricAlgorithmSecurityHeader)); } static UA_INLINE UA_AsymmetricAlgorithmSecurityHeader * UA_AsymmetricAlgorithmSecurityHeader_new(void) { return (UA_AsymmetricAlgorithmSecurityHeader*)UA_new(&UA_TRANSPORT[UA_TRANSPORT_ASYMMETRICALGORITHMSECURITYHEADER]); } static UA_INLINE UA_StatusCode UA_AsymmetricAlgorithmSecurityHeader_copy(const UA_AsymmetricAlgorithmSecurityHeader *src, UA_AsymmetricAlgorithmSecurityHeader *dst) { return UA_copy(src, dst, &UA_TRANSPORT[UA_TRANSPORT_ASYMMETRICALGORITHMSECURITYHEADER]); } static UA_INLINE void UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(UA_AsymmetricAlgorithmSecurityHeader *p) { UA_clear(p, &UA_TRANSPORT[UA_TRANSPORT_ASYMMETRICALGORITHMSECURITYHEADER]); } static UA_INLINE void UA_AsymmetricAlgorithmSecurityHeader_clear(UA_AsymmetricAlgorithmSecurityHeader *p) { UA_clear(p, &UA_TRANSPORT[UA_TRANSPORT_ASYMMETRICALGORITHMSECURITYHEADER]); } static UA_INLINE void UA_AsymmetricAlgorithmSecurityHeader_delete(UA_AsymmetricAlgorithmSecurityHeader *p) { UA_delete(p, &UA_TRANSPORT[UA_TRANSPORT_ASYMMETRICALGORITHMSECURITYHEADER]); } /* TcpAcknowledgeMessage */ static UA_INLINE void UA_TcpAcknowledgeMessage_init(UA_TcpAcknowledgeMessage *p) { memset(p, 0, sizeof(UA_TcpAcknowledgeMessage)); } static UA_INLINE UA_TcpAcknowledgeMessage * UA_TcpAcknowledgeMessage_new(void) { return (UA_TcpAcknowledgeMessage*)UA_new(&UA_TRANSPORT[UA_TRANSPORT_TCPACKNOWLEDGEMESSAGE]); } static UA_INLINE UA_StatusCode UA_TcpAcknowledgeMessage_copy(const UA_TcpAcknowledgeMessage *src, UA_TcpAcknowledgeMessage *dst) { *dst = *src; return UA_STATUSCODE_GOOD; } static UA_INLINE void UA_TcpAcknowledgeMessage_deleteMembers(UA_TcpAcknowledgeMessage *p) { memset(p, 0, sizeof(UA_TcpAcknowledgeMessage)); } static UA_INLINE void UA_TcpAcknowledgeMessage_clear(UA_TcpAcknowledgeMessage *p) { memset(p, 0, sizeof(UA_TcpAcknowledgeMessage)); } static UA_INLINE void UA_TcpAcknowledgeMessage_delete(UA_TcpAcknowledgeMessage *p) { UA_delete(p, &UA_TRANSPORT[UA_TRANSPORT_TCPACKNOWLEDGEMESSAGE]); } /* SequenceHeader */ static UA_INLINE void UA_SequenceHeader_init(UA_SequenceHeader *p) { memset(p, 0, sizeof(UA_SequenceHeader)); } static UA_INLINE UA_SequenceHeader * UA_SequenceHeader_new(void) { return (UA_SequenceHeader*)UA_new(&UA_TRANSPORT[UA_TRANSPORT_SEQUENCEHEADER]); } static UA_INLINE UA_StatusCode UA_SequenceHeader_copy(const UA_SequenceHeader *src, UA_SequenceHeader *dst) { *dst = *src; return UA_STATUSCODE_GOOD; } static UA_INLINE void UA_SequenceHeader_deleteMembers(UA_SequenceHeader *p) { memset(p, 0, sizeof(UA_SequenceHeader)); } static UA_INLINE void UA_SequenceHeader_clear(UA_SequenceHeader *p) { memset(p, 0, sizeof(UA_SequenceHeader)); } static UA_INLINE void UA_SequenceHeader_delete(UA_SequenceHeader *p) { UA_delete(p, &UA_TRANSPORT[UA_TRANSPORT_SEQUENCEHEADER]); } /* TcpMessageHeader */ static UA_INLINE void UA_TcpMessageHeader_init(UA_TcpMessageHeader *p) { memset(p, 0, sizeof(UA_TcpMessageHeader)); } static UA_INLINE UA_TcpMessageHeader * UA_TcpMessageHeader_new(void) { return (UA_TcpMessageHeader*)UA_new(&UA_TRANSPORT[UA_TRANSPORT_TCPMESSAGEHEADER]); } static UA_INLINE UA_StatusCode UA_TcpMessageHeader_copy(const UA_TcpMessageHeader *src, UA_TcpMessageHeader *dst) { *dst = *src; return UA_STATUSCODE_GOOD; } static UA_INLINE void UA_TcpMessageHeader_deleteMembers(UA_TcpMessageHeader *p) { memset(p, 0, sizeof(UA_TcpMessageHeader)); } static UA_INLINE void UA_TcpMessageHeader_clear(UA_TcpMessageHeader *p) { memset(p, 0, sizeof(UA_TcpMessageHeader)); } static UA_INLINE void UA_TcpMessageHeader_delete(UA_TcpMessageHeader *p) { UA_delete(p, &UA_TRANSPORT[UA_TRANSPORT_TCPMESSAGEHEADER]); } /* ChunkType */ static UA_INLINE void UA_ChunkType_init(UA_ChunkType *p) { memset(p, 0, sizeof(UA_ChunkType)); } static UA_INLINE UA_ChunkType * UA_ChunkType_new(void) { return (UA_ChunkType*)UA_new(&UA_TRANSPORT[UA_TRANSPORT_CHUNKTYPE]); } static UA_INLINE UA_StatusCode UA_ChunkType_copy(const UA_ChunkType *src, UA_ChunkType *dst) { *dst = *src; return UA_STATUSCODE_GOOD; } static UA_INLINE void UA_ChunkType_deleteMembers(UA_ChunkType *p) { memset(p, 0, sizeof(UA_ChunkType)); } static UA_INLINE void UA_ChunkType_clear(UA_ChunkType *p) { memset(p, 0, sizeof(UA_ChunkType)); } static UA_INLINE void UA_ChunkType_delete(UA_ChunkType *p) { UA_delete(p, &UA_TRANSPORT[UA_TRANSPORT_CHUNKTYPE]); } /* SymmetricAlgorithmSecurityHeader */ static UA_INLINE void UA_SymmetricAlgorithmSecurityHeader_init(UA_SymmetricAlgorithmSecurityHeader *p) { memset(p, 0, sizeof(UA_SymmetricAlgorithmSecurityHeader)); } static UA_INLINE UA_SymmetricAlgorithmSecurityHeader * UA_SymmetricAlgorithmSecurityHeader_new(void) { return (UA_SymmetricAlgorithmSecurityHeader*)UA_new(&UA_TRANSPORT[UA_TRANSPORT_SYMMETRICALGORITHMSECURITYHEADER]); } static UA_INLINE UA_StatusCode UA_SymmetricAlgorithmSecurityHeader_copy(const UA_SymmetricAlgorithmSecurityHeader *src, UA_SymmetricAlgorithmSecurityHeader *dst) { *dst = *src; return UA_STATUSCODE_GOOD; } static UA_INLINE void UA_SymmetricAlgorithmSecurityHeader_deleteMembers(UA_SymmetricAlgorithmSecurityHeader *p) { memset(p, 0, sizeof(UA_SymmetricAlgorithmSecurityHeader)); } static UA_INLINE void UA_SymmetricAlgorithmSecurityHeader_clear(UA_SymmetricAlgorithmSecurityHeader *p) { memset(p, 0, sizeof(UA_SymmetricAlgorithmSecurityHeader)); } static UA_INLINE void UA_SymmetricAlgorithmSecurityHeader_delete(UA_SymmetricAlgorithmSecurityHeader *p) { UA_delete(p, &UA_TRANSPORT[UA_TRANSPORT_SYMMETRICALGORITHMSECURITYHEADER]); } /* SecureConversationMessageHeader */ static UA_INLINE void UA_SecureConversationMessageHeader_init(UA_SecureConversationMessageHeader *p) { memset(p, 0, sizeof(UA_SecureConversationMessageHeader)); } static UA_INLINE UA_SecureConversationMessageHeader * UA_SecureConversationMessageHeader_new(void) { return (UA_SecureConversationMessageHeader*)UA_new(&UA_TRANSPORT[UA_TRANSPORT_SECURECONVERSATIONMESSAGEHEADER]); } static UA_INLINE UA_StatusCode UA_SecureConversationMessageHeader_copy(const UA_SecureConversationMessageHeader *src, UA_SecureConversationMessageHeader *dst) { *dst = *src; return UA_STATUSCODE_GOOD; } static UA_INLINE void UA_SecureConversationMessageHeader_deleteMembers(UA_SecureConversationMessageHeader *p) { memset(p, 0, sizeof(UA_SecureConversationMessageHeader)); } static UA_INLINE void UA_SecureConversationMessageHeader_clear(UA_SecureConversationMessageHeader *p) { memset(p, 0, sizeof(UA_SecureConversationMessageHeader)); } static UA_INLINE void UA_SecureConversationMessageHeader_delete(UA_SecureConversationMessageHeader *p) { UA_delete(p, &UA_TRANSPORT[UA_TRANSPORT_SECURECONVERSATIONMESSAGEHEADER]); } #if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6 # pragma GCC diagnostic pop #endif _UA_END_DECLS /*********************************** amalgamated original file "/home/jvoe/open62541/build/src_generated/open62541/transport_generated_encoding_binary.h" ***********************************/ /* Generated from Opc.Ua.Types.bsd, Custom.Opc.Ua.Transport.bsd with script /home/jvoe/open62541/tools/generate_datatypes.py * on host rigel by user jvoe at 2019-09-27 03:59:36 */ #ifdef UA_ENABLE_AMALGAMATION #else #endif /* SecureConversationMessageAbortBody */ static UA_INLINE size_t UA_SecureConversationMessageAbortBody_calcSizeBinary(const UA_SecureConversationMessageAbortBody *src) { return UA_calcSizeBinary(src, &UA_TRANSPORT[UA_TRANSPORT_SECURECONVERSATIONMESSAGEABORTBODY]); } static UA_INLINE UA_StatusCode UA_SecureConversationMessageAbortBody_encodeBinary(const UA_SecureConversationMessageAbortBody *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TRANSPORT[UA_TRANSPORT_SECURECONVERSATIONMESSAGEABORTBODY], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_SecureConversationMessageAbortBody_decodeBinary(const UA_ByteString *src, size_t *offset, UA_SecureConversationMessageAbortBody *dst) { return UA_decodeBinary(src, offset, dst, &UA_TRANSPORT[UA_TRANSPORT_SECURECONVERSATIONMESSAGEABORTBODY], NULL); } /* SecureConversationMessageFooter */ static UA_INLINE size_t UA_SecureConversationMessageFooter_calcSizeBinary(const UA_SecureConversationMessageFooter *src) { return UA_calcSizeBinary(src, &UA_TRANSPORT[UA_TRANSPORT_SECURECONVERSATIONMESSAGEFOOTER]); } static UA_INLINE UA_StatusCode UA_SecureConversationMessageFooter_encodeBinary(const UA_SecureConversationMessageFooter *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TRANSPORT[UA_TRANSPORT_SECURECONVERSATIONMESSAGEFOOTER], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_SecureConversationMessageFooter_decodeBinary(const UA_ByteString *src, size_t *offset, UA_SecureConversationMessageFooter *dst) { return UA_decodeBinary(src, offset, dst, &UA_TRANSPORT[UA_TRANSPORT_SECURECONVERSATIONMESSAGEFOOTER], NULL); } /* TcpHelloMessage */ static UA_INLINE size_t UA_TcpHelloMessage_calcSizeBinary(const UA_TcpHelloMessage *src) { return UA_calcSizeBinary(src, &UA_TRANSPORT[UA_TRANSPORT_TCPHELLOMESSAGE]); } static UA_INLINE UA_StatusCode UA_TcpHelloMessage_encodeBinary(const UA_TcpHelloMessage *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TRANSPORT[UA_TRANSPORT_TCPHELLOMESSAGE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_TcpHelloMessage_decodeBinary(const UA_ByteString *src, size_t *offset, UA_TcpHelloMessage *dst) { return UA_decodeBinary(src, offset, dst, &UA_TRANSPORT[UA_TRANSPORT_TCPHELLOMESSAGE], NULL); } /* TcpErrorMessage */ static UA_INLINE size_t UA_TcpErrorMessage_calcSizeBinary(const UA_TcpErrorMessage *src) { return UA_calcSizeBinary(src, &UA_TRANSPORT[UA_TRANSPORT_TCPERRORMESSAGE]); } static UA_INLINE UA_StatusCode UA_TcpErrorMessage_encodeBinary(const UA_TcpErrorMessage *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TRANSPORT[UA_TRANSPORT_TCPERRORMESSAGE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_TcpErrorMessage_decodeBinary(const UA_ByteString *src, size_t *offset, UA_TcpErrorMessage *dst) { return UA_decodeBinary(src, offset, dst, &UA_TRANSPORT[UA_TRANSPORT_TCPERRORMESSAGE], NULL); } /* MessageType */ static UA_INLINE size_t UA_MessageType_calcSizeBinary(const UA_MessageType *src) { return UA_calcSizeBinary(src, &UA_TRANSPORT[UA_TRANSPORT_MESSAGETYPE]); } static UA_INLINE UA_StatusCode UA_MessageType_encodeBinary(const UA_MessageType *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TRANSPORT[UA_TRANSPORT_MESSAGETYPE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_MessageType_decodeBinary(const UA_ByteString *src, size_t *offset, UA_MessageType *dst) { return UA_decodeBinary(src, offset, dst, &UA_TRANSPORT[UA_TRANSPORT_MESSAGETYPE], NULL); } /* AsymmetricAlgorithmSecurityHeader */ static UA_INLINE size_t UA_AsymmetricAlgorithmSecurityHeader_calcSizeBinary(const UA_AsymmetricAlgorithmSecurityHeader *src) { return UA_calcSizeBinary(src, &UA_TRANSPORT[UA_TRANSPORT_ASYMMETRICALGORITHMSECURITYHEADER]); } static UA_INLINE UA_StatusCode UA_AsymmetricAlgorithmSecurityHeader_encodeBinary(const UA_AsymmetricAlgorithmSecurityHeader *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TRANSPORT[UA_TRANSPORT_ASYMMETRICALGORITHMSECURITYHEADER], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(const UA_ByteString *src, size_t *offset, UA_AsymmetricAlgorithmSecurityHeader *dst) { return UA_decodeBinary(src, offset, dst, &UA_TRANSPORT[UA_TRANSPORT_ASYMMETRICALGORITHMSECURITYHEADER], NULL); } /* TcpAcknowledgeMessage */ static UA_INLINE size_t UA_TcpAcknowledgeMessage_calcSizeBinary(const UA_TcpAcknowledgeMessage *src) { return UA_calcSizeBinary(src, &UA_TRANSPORT[UA_TRANSPORT_TCPACKNOWLEDGEMESSAGE]); } static UA_INLINE UA_StatusCode UA_TcpAcknowledgeMessage_encodeBinary(const UA_TcpAcknowledgeMessage *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TRANSPORT[UA_TRANSPORT_TCPACKNOWLEDGEMESSAGE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_TcpAcknowledgeMessage_decodeBinary(const UA_ByteString *src, size_t *offset, UA_TcpAcknowledgeMessage *dst) { return UA_decodeBinary(src, offset, dst, &UA_TRANSPORT[UA_TRANSPORT_TCPACKNOWLEDGEMESSAGE], NULL); } /* SequenceHeader */ static UA_INLINE size_t UA_SequenceHeader_calcSizeBinary(const UA_SequenceHeader *src) { return UA_calcSizeBinary(src, &UA_TRANSPORT[UA_TRANSPORT_SEQUENCEHEADER]); } static UA_INLINE UA_StatusCode UA_SequenceHeader_encodeBinary(const UA_SequenceHeader *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TRANSPORT[UA_TRANSPORT_SEQUENCEHEADER], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_SequenceHeader_decodeBinary(const UA_ByteString *src, size_t *offset, UA_SequenceHeader *dst) { return UA_decodeBinary(src, offset, dst, &UA_TRANSPORT[UA_TRANSPORT_SEQUENCEHEADER], NULL); } /* TcpMessageHeader */ static UA_INLINE size_t UA_TcpMessageHeader_calcSizeBinary(const UA_TcpMessageHeader *src) { return UA_calcSizeBinary(src, &UA_TRANSPORT[UA_TRANSPORT_TCPMESSAGEHEADER]); } static UA_INLINE UA_StatusCode UA_TcpMessageHeader_encodeBinary(const UA_TcpMessageHeader *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TRANSPORT[UA_TRANSPORT_TCPMESSAGEHEADER], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_TcpMessageHeader_decodeBinary(const UA_ByteString *src, size_t *offset, UA_TcpMessageHeader *dst) { return UA_decodeBinary(src, offset, dst, &UA_TRANSPORT[UA_TRANSPORT_TCPMESSAGEHEADER], NULL); } /* ChunkType */ static UA_INLINE size_t UA_ChunkType_calcSizeBinary(const UA_ChunkType *src) { return UA_calcSizeBinary(src, &UA_TRANSPORT[UA_TRANSPORT_CHUNKTYPE]); } static UA_INLINE UA_StatusCode UA_ChunkType_encodeBinary(const UA_ChunkType *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TRANSPORT[UA_TRANSPORT_CHUNKTYPE], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_ChunkType_decodeBinary(const UA_ByteString *src, size_t *offset, UA_ChunkType *dst) { return UA_decodeBinary(src, offset, dst, &UA_TRANSPORT[UA_TRANSPORT_CHUNKTYPE], NULL); } /* SymmetricAlgorithmSecurityHeader */ static UA_INLINE size_t UA_SymmetricAlgorithmSecurityHeader_calcSizeBinary(const UA_SymmetricAlgorithmSecurityHeader *src) { return UA_calcSizeBinary(src, &UA_TRANSPORT[UA_TRANSPORT_SYMMETRICALGORITHMSECURITYHEADER]); } static UA_INLINE UA_StatusCode UA_SymmetricAlgorithmSecurityHeader_encodeBinary(const UA_SymmetricAlgorithmSecurityHeader *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TRANSPORT[UA_TRANSPORT_SYMMETRICALGORITHMSECURITYHEADER], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_SymmetricAlgorithmSecurityHeader_decodeBinary(const UA_ByteString *src, size_t *offset, UA_SymmetricAlgorithmSecurityHeader *dst) { return UA_decodeBinary(src, offset, dst, &UA_TRANSPORT[UA_TRANSPORT_SYMMETRICALGORITHMSECURITYHEADER], NULL); } /* SecureConversationMessageHeader */ static UA_INLINE size_t UA_SecureConversationMessageHeader_calcSizeBinary(const UA_SecureConversationMessageHeader *src) { return UA_calcSizeBinary(src, &UA_TRANSPORT[UA_TRANSPORT_SECURECONVERSATIONMESSAGEHEADER]); } static UA_INLINE UA_StatusCode UA_SecureConversationMessageHeader_encodeBinary(const UA_SecureConversationMessageHeader *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { return UA_encodeBinary(src, &UA_TRANSPORT[UA_TRANSPORT_SECURECONVERSATIONMESSAGEHEADER], bufPos, &bufEnd, NULL, NULL); } static UA_INLINE UA_StatusCode UA_SecureConversationMessageHeader_decodeBinary(const UA_ByteString *src, size_t *offset, UA_SecureConversationMessageHeader *dst) { return UA_decodeBinary(src, offset, dst, &UA_TRANSPORT[UA_TRANSPORT_SECURECONVERSATIONMESSAGEHEADER], NULL); } /*********************************** amalgamated original file "/home/jvoe/open62541/src/ua_connection_internal.h" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2016-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2017 (c) Florian Palm * Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB */ _UA_BEGIN_DECLS /* Process the remote configuration in the HEL/ACK handshake. The connection * config is initialized with the local settings. */ UA_StatusCode UA_Connection_processHELACK(UA_Connection *connection, const UA_ConnectionConfig *localConfig, const UA_ConnectionConfig *remoteConfig); /* The application can be the client or the server */ typedef UA_StatusCode (*UA_Connection_processChunk)(void *application, UA_Connection *connection, UA_ByteString *chunk); /* The network layer may receive several chunks in one packet since TCP is a * streaming protocol. The last chunk in the packet may be only partial. This * method calls the processChunk callback on all full chunks that were received. * The last incomplete chunk is buffered in the connection for the next * iteration. * * The packet itself is not edited in this method. But possibly in the callback * that is executed on complete chunks. * * @param connection The connection * @param application The client or server application * @param processCallback The function pointer for processing each chunk * @param packet The received packet. * @return Returns UA_STATUSCODE_GOOD or an error code. When an error occurs, * the current buffer in the connection are * freed. */ UA_StatusCode UA_Connection_processChunks(UA_Connection *connection, void *application, UA_Connection_processChunk processCallback, const UA_ByteString *packet); /* Try to receive at least one complete chunk on the connection. This blocks the * current thread up to the given timeout. * * @param connection The connection * @param application The client or server application * @param processCallback The function pointer for processing each chunk * @param timeout The timeout (in milliseconds) the method will block at most. * @return Returns UA_STATUSCODE_GOOD or an error code. When an timeout occurs, * UA_STATUSCODE_GOODNONCRITICALTIMEOUT is returned. */ UA_StatusCode UA_Connection_receiveChunksBlocking(UA_Connection *connection, void *application, UA_Connection_processChunk processCallback, UA_UInt32 timeout); UA_StatusCode UA_Connection_receiveChunksNonBlocking(UA_Connection *connection, void *application, UA_Connection_processChunk processCallback); /* When a fatal error occurs the Server shall send an Error Message to the * Client and close the socket. When a Client encounters one of these errors, it * shall also close the socket but does not send an Error Message. After the * socket is closed a Client shall try to reconnect automatically using the * mechanisms described in [...]. */ void UA_Connection_sendError(UA_Connection *connection, UA_TcpErrorMessage *error); void UA_Connection_detachSecureChannel(UA_Connection *connection); void UA_Connection_attachSecureChannel(UA_Connection *connection, UA_SecureChannel *channel); _UA_END_DECLS /*********************************** amalgamated original file "/home/jvoe/open62541/src/ua_securechannel.h" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2017 (c) Florian Palm * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB */ _UA_BEGIN_DECLS #define UA_SECURE_CONVERSATION_MESSAGE_HEADER_LENGTH 12 #define UA_SECURE_MESSAGE_HEADER_LENGTH 24 /* Thread-local variables to force failure modes during testing */ #ifdef UA_ENABLE_UNIT_TEST_FAILURE_HOOKS extern UA_StatusCode decrypt_verifySignatureFailure; extern UA_StatusCode sendAsym_sendFailure; extern UA_StatusCode processSym_seqNumberFailure; #endif /* The Session implementation differs between client and server. Still, it is * expected that the Session structure begins with the SessionHeader. This is * the interface that will be used by the SecureChannel. The lifecycle of * Sessions is independent of the underlying SecureChannel. But every Session * can be attached to only one SecureChannel. */ typedef struct UA_SessionHeader { LIST_ENTRY(UA_SessionHeader) pointers; UA_NodeId authenticationToken; UA_SecureChannel *channel; /* The pointer back to the SecureChannel in the session. */ } UA_SessionHeader; /* For chunked requests */ typedef struct UA_ChunkPayload { SIMPLEQ_ENTRY(UA_ChunkPayload) pointers; UA_ByteString bytes; UA_Boolean copied; /* Do the bytes point to a buffer from the network or was memory allocated for the chunk separately */ } UA_ChunkPayload; /* Receieved messages. Process them only in order. The Chunk payload has all * headers and the padding stripped out. The payload begins at the * ExtensionObject prefix.*/ typedef struct UA_Message { TAILQ_ENTRY(UA_Message) pointers; UA_UInt32 requestId; UA_MessageType messageType; SIMPLEQ_HEAD(pp, UA_ChunkPayload) chunkPayloads; size_t chunkPayloadsSize; /* No of chunks received so far */ size_t messageSize; /* Total length of the chunks received so far */ UA_Boolean final; /* All chunks for the message have been received */ } UA_Message; typedef enum { UA_SECURECHANNELSTATE_FRESH, UA_SECURECHANNELSTATE_OPEN, UA_SECURECHANNELSTATE_CLOSED } UA_SecureChannelState; typedef TAILQ_HEAD(UA_MessageQueue, UA_Message) UA_MessageQueue; struct UA_SecureChannel { UA_SecureChannelState state; UA_MessageSecurityMode securityMode; /* We use three tokens because when switching tokens the client is allowed to accept * messages with the old token for up to 25% of the lifetime after the token would have timed out. * For messages that are sent, the new token is already used, which is contained in the securityToken * variable. The nextSecurityToken variable holds a newly issued token, that will be automatically * revolved into the securityToken variable. This could be done with two variables, but would require * greater changes to the current code. This could be done in the future after the client and networking * structure has been reworked, which would make this easier to implement. */ UA_ChannelSecurityToken securityToken; /* the channelId is contained in the securityToken */ UA_ChannelSecurityToken nextSecurityToken; UA_ChannelSecurityToken previousSecurityToken; /* The endpoint and context of the channel */ const UA_SecurityPolicy *securityPolicy; void *channelContext; /* For interaction with the security policy */ UA_Connection *connection; /* Asymmetric encryption info */ UA_ByteString remoteCertificate; UA_Byte remoteCertificateThumbprint[20]; /* The thumbprint of the remote certificate */ /* Symmetric encryption info */ UA_ByteString remoteNonce; UA_ByteString localNonce; UA_UInt32 receiveSequenceNumber; UA_UInt32 sendSequenceNumber; LIST_HEAD(, UA_SessionHeader) sessions; UA_MessageQueue messages; }; void UA_SecureChannel_init(UA_SecureChannel *channel); void UA_SecureChannel_close(UA_SecureChannel *channel); UA_StatusCode UA_SecureChannel_setSecurityPolicy(UA_SecureChannel *channel, const UA_SecurityPolicy *securityPolicy, const UA_ByteString *remoteCertificate); /* Remove (partially) received unprocessed messages */ void UA_SecureChannel_deleteMessages(UA_SecureChannel *channel); void UA_SecureChannel_deleteMembers(UA_SecureChannel *channel); /* Generates new keys and sets them in the channel context */ UA_StatusCode UA_SecureChannel_generateNewKeys(UA_SecureChannel* channel); /* Wrapper function for generating a local nonce for the supplied channel. Uses * the random generator of the channels security policy to allocate and generate * a nonce with the specified length. */ UA_StatusCode UA_SecureChannel_generateLocalNonce(UA_SecureChannel *channel); UA_SessionHeader * UA_SecureChannel_getSession(UA_SecureChannel *channel, const UA_NodeId *authenticationToken); UA_StatusCode UA_SecureChannel_revolveTokens(UA_SecureChannel *channel); /** * Sending Messages * ---------------- */ UA_StatusCode UA_SecureChannel_sendAsymmetricOPNMessage(UA_SecureChannel *channel, UA_UInt32 requestId, const void *content, const UA_DataType *contentType); UA_StatusCode UA_SecureChannel_sendSymmetricMessage(UA_SecureChannel *channel, UA_UInt32 requestId, UA_MessageType messageType, void *payload, const UA_DataType *payloadType); /* The MessageContext is forwarded into the encoding layer so that we can send * chunks before continuing to encode. This lets us reuse a fixed chunk-sized * messages buffer. */ typedef struct { UA_SecureChannel *channel; UA_UInt32 requestId; UA_UInt32 messageType; UA_UInt16 chunksSoFar; size_t messageSizeSoFar; UA_ByteString messageBuffer; UA_Byte *buf_pos; const UA_Byte *buf_end; UA_Boolean final; } UA_MessageContext; /* Start the context of a new symmetric message. */ UA_StatusCode UA_MessageContext_begin(UA_MessageContext *mc, UA_SecureChannel *channel, UA_UInt32 requestId, UA_MessageType messageType); /* Encode the content and send out full chunks. If the return code is good, then * the ChunkInfo contains encoded content that has not been sent. If the return * code is bad, then the ChunkInfo has been cleaned up internally. */ UA_StatusCode UA_MessageContext_encode(UA_MessageContext *mc, const void *content, const UA_DataType *contentType); /* Sends a symmetric message already encoded in the context. The context is * cleaned up, also in case of errors. */ UA_StatusCode UA_MessageContext_finish(UA_MessageContext *mc); /* To be used when a failure occures when a MessageContext is open. Note that * the _encode and _finish methods will clean up internally. _abort can be run * on a MessageContext that has already been cleaned up before. */ void UA_MessageContext_abort(UA_MessageContext *mc); /** * Receive Message * --------------- */ /* Decrypt a chunk and add it to the message. Create a new message if necessary. */ UA_StatusCode UA_SecureChannel_decryptAddChunk(UA_SecureChannel *channel, const UA_ByteString *chunk, UA_Boolean allowPreviousToken); /* The network buffer is about to be cleared. Copy all chunks that point into * the network buffer into dedicated memory. */ UA_StatusCode UA_SecureChannel_persistIncompleteMessages(UA_SecureChannel *channel); typedef void (UA_ProcessMessageCallback)(void *application, UA_SecureChannel *channel, UA_MessageType messageType, UA_UInt32 requestId, const UA_ByteString *message); /* Process received complete messages in-order. The callback function is called * with the complete message body if the message is complete. The message is * removed afterwards. * * Symmetric callback is ERR, MSG, CLO only * Asymmetric callback is OPN only * * @param channel the channel the chunks were received on. * @param application data pointer to application specific data that gets passed * on to the callback function. * @param callback the callback function that gets called with the complete * message body, once a final chunk is processed. * @return Returns if an irrecoverable error occured. Maybe close the channel. */ UA_StatusCode UA_SecureChannel_processCompleteMessages(UA_SecureChannel *channel, void *application, UA_ProcessMessageCallback callback); /** * Log Helper * ---------- * C99 requires at least one element for the variadic argument. If the log * statement has no variable arguments, supply an additional NULL. It will be * ignored by printf. * * We have to jump through some hoops to enable the use of format strings * without arguments since (pedantic) C99 does not allow variadic macros with * zero arguments. So we add a dummy argument that is not printed (%.0s is * string of length zero). */ #define UA_LOG_TRACE_CHANNEL_INTERNAL(LOGGER, CHANNEL, MSG, ...) \ UA_LOG_TRACE(LOGGER, UA_LOGCATEGORY_SECURECHANNEL, \ "Connection %i | SecureChannel %i | " MSG "%.0s", \ ((CHANNEL)->connection ? (int)((CHANNEL)->connection->sockfd) : 0), \ (CHANNEL)->securityToken.channelId, __VA_ARGS__) #define UA_LOG_TRACE_CHANNEL(LOGGER, CHANNEL, ...) \ UA_MACRO_EXPAND(UA_LOG_TRACE_CHANNEL_INTERNAL(LOGGER, CHANNEL, __VA_ARGS__, "")) #define UA_LOG_DEBUG_CHANNEL_INTERNAL(LOGGER, CHANNEL, MSG, ...) \ UA_LOG_DEBUG(LOGGER, UA_LOGCATEGORY_SECURECHANNEL, \ "Connection %i | SecureChannel %i | " MSG "%.0s", \ ((CHANNEL)->connection ? (int)((CHANNEL)->connection->sockfd) : 0), \ (CHANNEL)->securityToken.channelId, __VA_ARGS__) #define UA_LOG_DEBUG_CHANNEL(LOGGER, CHANNEL, ...) \ UA_MACRO_EXPAND(UA_LOG_DEBUG_CHANNEL_INTERNAL(LOGGER, CHANNEL, __VA_ARGS__, "")) #define UA_LOG_INFO_CHANNEL_INTERNAL(LOGGER, CHANNEL, MSG, ...) \ UA_LOG_INFO(LOGGER, UA_LOGCATEGORY_SECURECHANNEL, \ "Connection %i | SecureChannel %i | " MSG "%.0s", \ ((CHANNEL)->connection ? (int)((CHANNEL)->connection->sockfd) : 0), \ (CHANNEL)->securityToken.channelId, __VA_ARGS__) #define UA_LOG_INFO_CHANNEL(LOGGER, CHANNEL, ...) \ UA_MACRO_EXPAND(UA_LOG_INFO_CHANNEL_INTERNAL(LOGGER, CHANNEL, __VA_ARGS__, "")) #define UA_LOG_WARNING_CHANNEL_INTERNAL(LOGGER, CHANNEL, MSG, ...) \ UA_LOG_WARNING(LOGGER, UA_LOGCATEGORY_SECURECHANNEL, \ "Connection %i | SecureChannel %i | " MSG "%.0s", \ ((CHANNEL)->connection ? (int)((CHANNEL)->connection->sockfd) : 0), \ (CHANNEL)->securityToken.channelId, __VA_ARGS__) #define UA_LOG_WARNING_CHANNEL(LOGGER, CHANNEL, ...) \ UA_MACRO_EXPAND(UA_LOG_WARNING_CHANNEL_INTERNAL(LOGGER, CHANNEL, __VA_ARGS__, "")) #define UA_LOG_ERROR_CHANNEL_INTERNAL(LOGGER, CHANNEL, MSG, ...) \ UA_LOG_ERROR(LOGGER, UA_LOGCATEGORY_SECURECHANNEL, \ "Connection %i | SecureChannel %i | " MSG "%.0s", \ ((CHANNEL)->connection ? (int)((CHANNEL)->connection->sockfd) : 0), \ (CHANNEL)->securityToken.channelId, __VA_ARGS__) #define UA_LOG_ERROR_CHANNEL(LOGGER, CHANNEL, ...) \ UA_MACRO_EXPAND(UA_LOG_ERROR_CHANNEL_INTERNAL(LOGGER, CHANNEL, __VA_ARGS__, "")) #define UA_LOG_FATAL_CHANNEL_INTERNAL(LOGGER, CHANNEL, MSG, ...) \ UA_LOG_FATAL(LOGGER, UA_LOGCATEGORY_SECURECHANNEL, \ "Connection %i | SecureChannel %i | " MSG "%.0s", \ ((CHANNEL)->connection ? (CHANNEL)->connection->sockfd : 0), \ (CHANNEL)->securityToken.channelId, __VA_ARGS__) #define UA_LOG_FATAL_CHANNEL(LOGGER, CHANNEL, ...) \ UA_MACRO_EXPAND(UA_LOG_FATAL_CHANNEL_INTERNAL(LOGGER, CHANNEL, __VA_ARGS__, "")) _UA_END_DECLS /*********************************** amalgamated original file "/home/jvoe/open62541/src/ua_workqueue.h" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2014-2016 (c) Sten Grüner * Copyright 2015 (c) Chris Iatrou * Copyright 2015 (c) Nick Goossens * Copyright 2015 (c) Jörg Schüler-Maroldt * Copyright 2015-2016 (c) Oleksiy Vasylyev * Copyright 2016-2017 (c) Florian Palm * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2016 (c) Lorenz Haas * Copyright 2017 (c) Jonas Green */ #ifdef UA_ENABLE_MULTITHREADING #include #endif _UA_BEGIN_DECLS /* Callback where the application is either a client or a server */ typedef void (*UA_ApplicationCallback)(void *application, void *data); /* Delayed callbacks are executed when all previously enqueue work is finished. * This is used to free memory that might used by a parallel worker or where the * current threat has remaining pointers to until the current operation * finishes. */ typedef struct UA_DelayedCallback { SIMPLEQ_ENTRY(UA_DelayedCallback) next; UA_ApplicationCallback callback; void *application; void *data; } UA_DelayedCallback; struct UA_WorkQueue; typedef struct UA_WorkQueue UA_WorkQueue; #ifdef UA_ENABLE_MULTITHREADING /* Workers take out callbacks from the work queue and execute them. * * Future Plans: Use work-stealing to load-balance between cores. * Le, Nhat Minh, et al. "Correct and efficient work-stealing for weak memory * models." ACM SIGPLAN Notices. Vol. 48. No. 8. ACM, 2013. */ typedef struct { pthread_t thread; volatile UA_Boolean running; UA_WorkQueue *queue; UA_UInt32 counter; UA_UInt32 checkpointCounter; /* Counter when the last checkpoint was made * for the delayed callbacks */ /* separate cache lines */ char padding[64 - sizeof(void*) - sizeof(pthread_t) - sizeof(UA_UInt32) - sizeof(UA_Boolean)]; } UA_Worker; #endif struct UA_WorkQueue { /* Worker threads and work queue. Without multithreading, work is executed immediately. */ #ifdef UA_ENABLE_MULTITHREADING UA_Worker *workers; size_t workersSize; /* Work queue */ SIMPLEQ_HEAD(, UA_DelayedCallback) dispatchQueue; /* Dispatch queue for the worker threads */ pthread_mutex_t dispatchQueue_accessMutex; /* mutex for access to queue */ pthread_cond_t dispatchQueue_condition; /* so the workers don't spin if the queue is empty */ pthread_mutex_t dispatchQueue_conditionMutex; /* mutex for access to condition variable */ #endif /* Delayed callbacks * To be executed after all curretly dispatched works has finished */ SIMPLEQ_HEAD(, UA_DelayedCallback) delayedCallbacks; #ifdef UA_ENABLE_MULTITHREADING pthread_mutex_t delayedCallbacks_accessMutex; UA_DelayedCallback *delayedCallbacks_checkpoint; size_t delayedCallbacks_sinceDispatch; /* How many have been added since we * tried to dispatch callbacks? */ #endif }; void UA_WorkQueue_init(UA_WorkQueue *wq); /* Enqueue a delayed callback. It is executed when all previous work in the * queue has been finished. The ``cb`` pointer is freed afterwards. ``cb`` can * have a NULL callback that is not executed. * * This method checks internally if existing delayed work can be moved from the * delayed queue to the worker dispatch queue. */ void UA_WorkQueue_enqueueDelayed(UA_WorkQueue *wq, UA_DelayedCallback *cb); /* Stop the workers, process all enqueued work in the calling thread, clean up * mutexes etc. */ void UA_WorkQueue_cleanup(UA_WorkQueue *wq); #ifndef UA_ENABLE_MULTITHREADING /* Process all enqueued delayed work. This is not needed when workers are * running for the multithreading case. (UA_WorkQueue_cleanup still calls this * method during cleanup when the workers are shut down.) */ void UA_WorkQueue_manuallyProcessDelayed(UA_WorkQueue *wq); #else /* Spin up a number of worker threads that listen on the work queue */ UA_StatusCode UA_WorkQueue_start(UA_WorkQueue *wq, size_t workersCount); void UA_WorkQueue_stop(UA_WorkQueue *wq); /* Enqueue work for the worker threads */ void UA_WorkQueue_enqueue(UA_WorkQueue *wq, UA_ApplicationCallback cb, void *application, void *data); #endif _UA_END_DECLS /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_discovery_manager.h" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2014, 2017 (c) Florian Palm * Copyright 2015-2016 (c) Sten Grüner * Copyright 2015 (c) Chris Iatrou * Copyright 2015-2016 (c) Oleksiy Vasylyev * Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2017 (c) Julian Grothoff */ _UA_BEGIN_DECLS #ifdef UA_ENABLE_DISCOVERY typedef struct registeredServer_list_entry { #ifdef UA_ENABLE_MULTITHREADING UA_DelayedCallback delayedCleanup; #endif LIST_ENTRY(registeredServer_list_entry) pointers; UA_RegisteredServer registeredServer; UA_DateTime lastSeen; } registeredServer_list_entry; struct PeriodicServerRegisterCallback { UA_UInt64 id; UA_Double this_interval; UA_Double default_interval; UA_Boolean registered; struct UA_Client* client; char* discovery_server_url; }; typedef struct periodicServerRegisterCallback_entry { #ifdef UA_ENABLE_MULTITHREADING UA_DelayedCallback delayedCleanup; #endif LIST_ENTRY(periodicServerRegisterCallback_entry) pointers; struct PeriodicServerRegisterCallback *callback; } periodicServerRegisterCallback_entry; #ifdef UA_ENABLE_DISCOVERY_MULTICAST /** * TXT record: * [servername]-[hostname]._opcua-tcp._tcp.local. TXT path=/ caps=NA,DA,... * * A/AAAA record for all ip addresses: * [servername]-[hostname]._opcua-tcp._tcp.local. A [ip]. * [hostname]. A [ip]. */ typedef struct serverOnNetwork_list_entry { #ifdef UA_ENABLE_MULTITHREADING UA_DelayedCallback delayedCleanup; #endif LIST_ENTRY(serverOnNetwork_list_entry) pointers; UA_ServerOnNetwork serverOnNetwork; UA_DateTime created; UA_DateTime lastSeen; UA_Boolean txtSet; UA_Boolean srvSet; char* pathTmp; } serverOnNetwork_list_entry; #define SERVER_ON_NETWORK_HASH_PRIME 1009 typedef struct serverOnNetwork_hash_entry { serverOnNetwork_list_entry* entry; struct serverOnNetwork_hash_entry* next; } serverOnNetwork_hash_entry; #endif typedef struct { LIST_HEAD(, periodicServerRegisterCallback_entry) periodicServerRegisterCallbacks; LIST_HEAD(, registeredServer_list_entry) registeredServers; size_t registeredServersSize; UA_Server_registerServerCallback registerServerCallback; void* registerServerCallbackData; # ifdef UA_ENABLE_DISCOVERY_MULTICAST mdns_daemon_t *mdnsDaemon; UA_SOCKET mdnsSocket; UA_Boolean mdnsMainSrvAdded; LIST_HEAD(, serverOnNetwork_list_entry) serverOnNetwork; size_t serverOnNetworkSize; UA_UInt32 serverOnNetworkRecordIdCounter; UA_DateTime serverOnNetworkRecordIdLastReset; /* hash mapping domain name to serverOnNetwork list entry */ struct serverOnNetwork_hash_entry* serverOnNetworkHash[SERVER_ON_NETWORK_HASH_PRIME]; UA_Server_serverOnNetworkCallback serverOnNetworkCallback; void* serverOnNetworkCallbackData; # ifdef UA_ENABLE_MULTITHREADING pthread_t mdnsThread; UA_Boolean mdnsRunning; # endif # endif /* UA_ENABLE_DISCOVERY_MULTICAST */ } UA_DiscoveryManager; void UA_DiscoveryManager_init(UA_DiscoveryManager *dm, UA_Server *server); void UA_DiscoveryManager_deleteMembers(UA_DiscoveryManager *dm, UA_Server *server); /* Checks if a registration timed out and removes that registration. * Should be called periodically in main loop */ void UA_Discovery_cleanupTimedOut(UA_Server *server, UA_DateTime nowMonotonic); #ifdef UA_ENABLE_DISCOVERY_MULTICAST void UA_Server_updateMdnsForDiscoveryUrl(UA_Server *server, const UA_String *serverName, const UA_MdnsDiscoveryConfiguration *mdnsConfig, const UA_String *discoveryUrl, UA_Boolean isOnline, UA_Boolean updateTxt); void mdns_record_received(const struct resource *r, void *data); void mdns_create_txt(UA_Server *server, const char *fullServiceDomain, const char *path, const UA_String *capabilites, const size_t capabilitiesSize, void (*conflict)(char *host, int type, void *arg)); void mdns_set_address_record(UA_Server *server, const char *fullServiceDomain, const char *localDomain); mdns_record_t * mdns_find_record(mdns_daemon_t *mdnsDaemon, unsigned short type, const char *host, const char *rdname); void startMulticastDiscoveryServer(UA_Server *server); void stopMulticastDiscoveryServer(UA_Server *server); UA_StatusCode iterateMulticastDiscoveryServer(UA_Server* server, UA_DateTime *nextRepeat, UA_Boolean processIn); typedef enum { UA_DISCOVERY_TCP, /* OPC UA TCP mapping */ UA_DISCOVERY_TLS /* OPC UA HTTPS mapping */ } UA_DiscoveryProtocol; /* Send a multicast probe to find any other OPC UA server on the network through mDNS. */ UA_StatusCode UA_Discovery_multicastQuery(UA_Server* server); UA_StatusCode UA_Discovery_addRecord(UA_Server *server, const UA_String *servername, const UA_String *hostname, UA_UInt16 port, const UA_String *path, const UA_DiscoveryProtocol protocol, UA_Boolean createTxt, const UA_String* capabilites, const size_t capabilitiesSize); UA_StatusCode UA_Discovery_removeRecord(UA_Server *server, const UA_String *servername, const UA_String *hostname, UA_UInt16 port, UA_Boolean removeTxt); #endif /* UA_ENABLE_DISCOVERY_MULTICAST */ #endif /* UA_ENABLE_DISCOVERY */ _UA_END_DECLS /*********************************** amalgamated original file "/home/jvoe/open62541/src/ua_timer.h" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2017, 2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2017 (c) Stefan Profanter, fortiss GmbH */ _UA_BEGIN_DECLS struct UA_TimerEntry; typedef struct UA_TimerEntry UA_TimerEntry; ZIP_HEAD(UA_TimerZip, UA_TimerEntry); typedef struct UA_TimerZip UA_TimerZip; ZIP_HEAD(UA_TimerIdZip, UA_TimerEntry); typedef struct UA_TimerIdZip UA_TimerIdZip; /* Only for a single thread. Protect by a mutex if required. */ typedef struct { UA_TimerZip root; /* The root of the time-sorted zip tree */ UA_TimerIdZip idRoot; /* The root of the id-sorted zip tree */ UA_UInt64 idCounter; } UA_Timer; void UA_Timer_init(UA_Timer *t); UA_StatusCode UA_Timer_addTimedCallback(UA_Timer *t, UA_ApplicationCallback callback, void *application, void *data, UA_DateTime date, UA_UInt64 *callbackId); UA_StatusCode UA_Timer_addRepeatedCallback(UA_Timer *t, UA_ApplicationCallback callback, void *application, void *data, UA_Double interval_ms, UA_UInt64 *callbackId); /* Change the callback interval. If this is called from within the callback. The * adjustment is made during the next _process call. */ UA_StatusCode UA_Timer_changeRepeatedCallbackInterval(UA_Timer *t, UA_UInt64 callbackId, UA_Double interval_ms); void UA_Timer_removeCallback(UA_Timer *t, UA_UInt64 callbackId); /* Process (dispatch) the repeated callbacks that have timed out. Returns the * timestamp of the next scheduled repeated callback. Not thread-safe. * Application is a pointer to the client / server environment for the callback. * Dispatched is set to true when at least one callback was run / dispatched. */ typedef void (*UA_TimerExecutionCallback)(void *executionApplication, UA_ApplicationCallback cb, void *callbackApplication, void *data); UA_DateTime UA_Timer_process(UA_Timer *t, UA_DateTime nowMonotonic, UA_TimerExecutionCallback executionCallback, void *executionApplication); void UA_Timer_deleteMembers(UA_Timer *t); _UA_END_DECLS /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_session.h" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) */ _UA_BEGIN_DECLS #define UA_MAXCONTINUATIONPOINTS 5 struct ContinuationPoint; typedef struct ContinuationPoint ContinuationPoint; /* Returns the next entry in the linked list */ ContinuationPoint * ContinuationPoint_clear(ContinuationPoint *cp); struct UA_Subscription; typedef struct UA_Subscription UA_Subscription; #ifdef UA_ENABLE_SUBSCRIPTIONS typedef struct UA_PublishResponseEntry { SIMPLEQ_ENTRY(UA_PublishResponseEntry) listEntry; UA_UInt32 requestId; UA_PublishResponse response; } UA_PublishResponseEntry; #endif typedef struct { UA_SessionHeader header; UA_ApplicationDescription clientDescription; UA_String sessionName; UA_Boolean activated; void *sessionHandle; // pointer assigned in userland-callback UA_NodeId sessionId; UA_UInt32 maxRequestMessageSize; UA_UInt32 maxResponseMessageSize; UA_Double timeout; // [ms] UA_DateTime validTill; UA_ByteString serverNonce; UA_UInt16 availableContinuationPoints; ContinuationPoint *continuationPoints; #ifdef UA_ENABLE_SUBSCRIPTIONS UA_UInt32 lastSubscriptionId; UA_UInt32 lastSeenSubscriptionId; LIST_HEAD(UA_ListOfUASubscriptions, UA_Subscription) serverSubscriptions; SIMPLEQ_HEAD(UA_ListOfQueuedPublishResponses, UA_PublishResponseEntry) responseQueue; UA_UInt32 numSubscriptions; UA_UInt32 numPublishReq; size_t totalRetransmissionQueueSize; /* Retransmissions of all subscriptions */ #endif } UA_Session; /** * Session Lifecycle * ----------------- */ void UA_Session_init(UA_Session *session); void UA_Session_deleteMembersCleanup(UA_Session *session, UA_Server *server); void UA_Session_attachToSecureChannel(UA_Session *session, UA_SecureChannel *channel); void UA_Session_detachFromSecureChannel(UA_Session *session); UA_StatusCode UA_Session_generateNonce(UA_Session *session); /* If any activity on a session happens, the timeout is extended */ void UA_Session_updateLifetime(UA_Session *session); /** * Subscription handling * --------------------- */ #ifdef UA_ENABLE_SUBSCRIPTIONS void UA_Session_addSubscription(UA_Server *server, UA_Session *session, UA_Subscription *newSubscription); UA_Subscription * UA_Session_getSubscriptionById(UA_Session *session, UA_UInt32 subscriptionId); UA_StatusCode UA_Session_deleteSubscription(UA_Server *server, UA_Session *session, UA_UInt32 subscriptionId); void UA_Session_queuePublishReq(UA_Session *session, UA_PublishResponseEntry* entry, UA_Boolean head); UA_PublishResponseEntry * UA_Session_dequeuePublishReq(UA_Session *session); #endif /** * Log Helper * ---------- * We have to jump through some hoops to enable the use of format strings * without arguments since (pedantic) C99 does not allow variadic macros with * zero arguments. So we add a dummy argument that is not printed (%.0s is * string of length zero). */ #define UA_LOG_SESSION_INTERNAL(LOGGER, LEVEL, SESSION, MSG, ...) do { \ UA_String idString = UA_STRING_NULL; \ UA_NodeId_toString(&(SESSION)->sessionId, &idString); \ UA_LOG_##LEVEL(LOGGER, UA_LOGCATEGORY_SESSION, \ "Connection %i | SecureChannel %i | Session %.*s | " MSG "%.0s", \ ((SESSION)->header.channel ? \ ((SESSION)->header.channel->connection ? \ (int)((SESSION)->header.channel->connection->sockfd) : 0) : 0), \ ((SESSION)->header.channel ? \ (SESSION)->header.channel->securityToken.channelId : 0), \ (int)idString.length, idString.data, __VA_ARGS__); \ UA_String_deleteMembers(&idString); \ } while(0) #if UA_LOGLEVEL <= 100 #define UA_LOG_TRACE_SESSION(LOGGER, SESSION, ...) \ UA_MACRO_EXPAND(UA_LOG_SESSION_INTERNAL(LOGGER, TRACE, SESSION, __VA_ARGS__, "")) #else #define UA_LOG_TRACE_SESSION(LOGGER, SESSION, ...) do {} while(0) #endif #if UA_LOGLEVEL <= 200 #define UA_LOG_DEBUG_SESSION(LOGGER, SESSION, ...) \ UA_MACRO_EXPAND(UA_LOG_SESSION_INTERNAL(LOGGER, DEBUG, SESSION, __VA_ARGS__, "")) #else #define UA_LOG_DEBUG_SESSION(LOGGER, SESSION, ...) do {} while(0) #endif #if UA_LOGLEVEL <= 300 #define UA_LOG_INFO_SESSION(LOGGER, SESSION, ...) \ UA_MACRO_EXPAND(UA_LOG_SESSION_INTERNAL(LOGGER, INFO, SESSION, __VA_ARGS__, "")) #else #define UA_LOG_INFO_SESSION(LOGGER, SESSION, ...) do {} while(0) #endif #if UA_LOGLEVEL <= 400 #define UA_LOG_WARNING_SESSION(LOGGER, SESSION, ...) \ UA_MACRO_EXPAND(UA_LOG_SESSION_INTERNAL(LOGGER, WARNING, SESSION, __VA_ARGS__, "")) #else #define UA_LOG_WARNING_SESSION(LOGGER, SESSION, ...) do {} while(0) #endif #if UA_LOGLEVEL <= 500 #define UA_LOG_ERROR_SESSION(LOGGER, SESSION, ...) \ UA_MACRO_EXPAND(UA_LOG_SESSION_INTERNAL(LOGGER, ERROR, SESSION, __VA_ARGS__, "")) #else #define UA_LOG_ERROR_SESSION(LOGGER, SESSION, ...) do {} while(0) #endif #if UA_LOGLEVEL <= 600 #define UA_LOG_FATAL_SESSION(LOGGER, SESSION, ...) \ UA_MACRO_EXPAND(UA_LOG_SESSION_INTERNAL(LOGGER, FATAL, SESSION, __VA_ARGS__, "")) #else #define UA_LOG_FATAL_SESSION(LOGGER, SESSION, ...) do {} while(0) #endif _UA_END_DECLS /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_subscription.h" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2015-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2015 (c) Chris Iatrou * Copyright 2015-2016 (c) Sten Grüner * Copyright 2015 (c) Oleksiy Vasylyev * Copyright 2017 (c) Florian Palm * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2017 (c) Mattias Bornhager */ _UA_BEGIN_DECLS #ifdef UA_ENABLE_SUBSCRIPTIONS #define UA_BOUNDEDVALUE_SETWBOUNDS(BOUNDS, SRC, DST) { \ if(SRC > BOUNDS.max) DST = BOUNDS.max; \ else if(SRC < BOUNDS.min) DST = BOUNDS.min; \ else DST = SRC; \ } /* Set to the TAILQ_NEXT pointer of a notification, the sentinel that the * notification was not added to the global queue */ #define UA_SUBSCRIPTION_QUEUE_SENTINEL ((UA_Notification*)0x01) /** * MonitoredItems create Notifications. Subscriptions collect Notifications from * (several) MonitoredItems and publish them to the client. * * Notifications are put into two queues at the same time. One for the * MonitoredItem that generated the notification. Here we can remove it if the * space reserved for the MonitoredItem runs full. The second queue is the * "global" queue for all Notifications generated in a Subscription. For * publication, the notifications are taken out of the "global" queue in the * order of their creation. */ /*****************/ /* MonitoredItem */ /*****************/ struct UA_MonitoredItem; typedef struct UA_MonitoredItem UA_MonitoredItem; #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS typedef struct UA_EventNotification { UA_EventFieldList fields; /* EventFilterResult currently isn't being used UA_EventFilterResult result; */ } UA_EventNotification; #endif typedef struct UA_Notification { TAILQ_ENTRY(UA_Notification) listEntry; /* Notification list for the MonitoredItem */ TAILQ_ENTRY(UA_Notification) globalEntry; /* Notification list for the Subscription */ UA_MonitoredItem *mon; /* See the monitoredItemType of the MonitoredItem */ union { #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS UA_EventNotification event; #endif UA_DataValue value; } data; } UA_Notification; /* Ensure enough space is available; Add notification to the linked lists; * Increase the counters */ void UA_Notification_enqueue(UA_Server *server, UA_Subscription *sub, UA_MonitoredItem *mon, UA_Notification *n); /* Remove the notification from the MonitoredItem's queue and the Subscriptions * global queue. Reduce the respective counters. */ void UA_Notification_dequeue(UA_Server *server, UA_Notification *n); /* Delete the notification. Must be dequeued first. */ void UA_Notification_delete(UA_Notification *n); typedef TAILQ_HEAD(NotificationQueue, UA_Notification) NotificationQueue; struct UA_MonitoredItem { UA_DelayedCallback delayedFreePointers; LIST_ENTRY(UA_MonitoredItem) listEntry; UA_Subscription *subscription; /* Local MonitoredItem if the subscription is NULL */ UA_UInt32 monitoredItemId; UA_UInt32 clientHandle; UA_Boolean registered; /* Was the MonitoredItem registered in Userland with the callback? */ /* Settings */ UA_TimestampsToReturn timestampsToReturn; UA_MonitoringMode monitoringMode; UA_NodeId monitoredNodeId; UA_UInt32 attributeId; UA_String indexRange; UA_Double samplingInterval; // [ms] UA_Boolean discardOldest; union { #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS UA_EventFilter eventFilter; /* If attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER */ #endif UA_DataChangeFilter dataChangeFilter; } filter; UA_Variant lastValue; // TODO: dataEncoding is hardcoded to UA binary /* Sample Callback */ UA_UInt64 sampleCallbackId; UA_ByteString lastSampledValue; UA_Boolean sampleCallbackIsRegistered; /* Notification Queue */ NotificationQueue queue; UA_UInt32 maxQueueSize; /* The max number of enqueued notifications (not * counting overflow events) */ UA_UInt32 queueSize; UA_UInt32 eventOverflows; /* Separate counter for the queue. Can at most * double the queue size */ #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS UA_MonitoredItem *next; #endif #ifdef UA_ENABLE_DA UA_StatusCode lastStatus; #endif }; void UA_MonitoredItem_init(UA_MonitoredItem *mon, UA_Subscription *sub); void UA_MonitoredItem_delete(UA_Server *server, UA_MonitoredItem *monitoredItem); void UA_MonitoredItem_sampleCallback(UA_Server *server, UA_MonitoredItem *monitoredItem); UA_StatusCode UA_MonitoredItem_registerSampleCallback(UA_Server *server, UA_MonitoredItem *mon); void UA_MonitoredItem_unregisterSampleCallback(UA_Server *server, UA_MonitoredItem *mon); /* Remove entries until mon->maxQueueSize is reached. Sets infobits for lost * data if required. */ UA_StatusCode UA_MonitoredItem_ensureQueueSpace(UA_Server *server, UA_MonitoredItem *mon); UA_StatusCode UA_MonitoredItem_removeNodeEventCallback(UA_Server *server, UA_Session *session, UA_Node *node, void *data); /****************/ /* Subscription */ /****************/ typedef struct UA_NotificationMessageEntry { TAILQ_ENTRY(UA_NotificationMessageEntry) listEntry; UA_NotificationMessage message; } UA_NotificationMessageEntry; /* We use only a subset of the states defined in the standard */ typedef enum { /* UA_SUBSCRIPTIONSTATE_CLOSED */ /* UA_SUBSCRIPTIONSTATE_CREATING */ UA_SUBSCRIPTIONSTATE_NORMAL, UA_SUBSCRIPTIONSTATE_LATE, UA_SUBSCRIPTIONSTATE_KEEPALIVE } UA_SubscriptionState; typedef TAILQ_HEAD(ListOfNotificationMessages, UA_NotificationMessageEntry) ListOfNotificationMessages; struct UA_Subscription { UA_DelayedCallback delayedFreePointers; LIST_ENTRY(UA_Subscription) listEntry; UA_Session *session; UA_UInt32 subscriptionId; /* Settings */ UA_UInt32 lifeTimeCount; UA_UInt32 maxKeepAliveCount; UA_Double publishingInterval; /* in ms */ UA_UInt32 notificationsPerPublish; UA_Boolean publishingEnabled; UA_UInt32 priority; /* Runtime information */ UA_SubscriptionState state; UA_UInt32 nextSequenceNumber; UA_UInt32 currentKeepAliveCount; UA_UInt32 currentLifetimeCount; /* Publish Callback */ UA_UInt64 publishCallbackId; UA_Boolean publishCallbackIsRegistered; /* MonitoredItems */ UA_UInt32 lastMonitoredItemId; /* increase the identifiers */ LIST_HEAD(, UA_MonitoredItem) monitoredItems; UA_UInt32 monitoredItemsSize; /* Global list of notifications from the MonitoredItems */ NotificationQueue notificationQueue; UA_UInt32 notificationQueueSize; /* Total queue size */ UA_UInt32 dataChangeNotifications; UA_UInt32 eventNotifications; UA_UInt32 statusChangeNotifications; /* Notifications to be sent out now (already late). In a regular publish * callback, all queued notifications are sent out. In a late publish * response, only the notifications left from the last regular publish * callback are sent. */ UA_UInt32 readyNotifications; /* Retransmission Queue */ ListOfNotificationMessages retransmissionQueue; size_t retransmissionQueueSize; }; UA_Subscription * UA_Subscription_new(UA_Session *session, UA_UInt32 subscriptionId); void UA_Subscription_deleteMembers(UA_Server *server, UA_Subscription *sub); UA_StatusCode Subscription_registerPublishCallback(UA_Server *server, UA_Subscription *sub); void Subscription_unregisterPublishCallback(UA_Server *server, UA_Subscription *sub); void UA_Subscription_addMonitoredItem(UA_Server *server, UA_Subscription *sub, UA_MonitoredItem *newMon); UA_MonitoredItem * UA_Subscription_getMonitoredItem(UA_Subscription *sub, UA_UInt32 monitoredItemId); UA_StatusCode UA_Subscription_deleteMonitoredItem(UA_Server *server, UA_Subscription *sub, UA_UInt32 monitoredItemId); void UA_Subscription_publish(UA_Server *server, UA_Subscription *sub); UA_StatusCode UA_Subscription_removeRetransmissionMessage(UA_Subscription *sub, UA_UInt32 sequenceNumber); void UA_Subscription_answerPublishRequestsNoSubscription(UA_Server *server, UA_Session *session); UA_Boolean UA_Subscription_reachedPublishReqLimit(UA_Server *server, UA_Session *session); #endif /* UA_ENABLE_SUBSCRIPTIONS */ _UA_END_DECLS /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_session_manager.h" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2014, 2017 (c) Florian Palm * Copyright 2015 (c) Sten Grüner * Copyright 2015 (c) Oleksiy Vasylyev * Copyright 2017 (c) Stefan Profanter, fortiss GmbH */ _UA_BEGIN_DECLS typedef struct session_list_entry { UA_DelayedCallback cleanupCallback; LIST_ENTRY(session_list_entry) pointers; UA_Session session; } session_list_entry; typedef struct UA_SessionManager { LIST_HEAD(session_list, session_list_entry) sessions; // doubly-linked list of sessions UA_UInt32 currentSessionCount; UA_Server *server; } UA_SessionManager; UA_StatusCode UA_SessionManager_init(UA_SessionManager *sm, UA_Server *server); /* Deletes all sessions */ void UA_SessionManager_deleteMembers(UA_SessionManager *sm); /* Deletes all sessions that have timed out. Deletion is implemented via a * delayed callback. So all currently scheduled jobs with a pointer to the * session can complete. */ void UA_SessionManager_cleanupTimedOut(UA_SessionManager *sm, UA_DateTime nowMonotonic); UA_StatusCode UA_SessionManager_createSession(UA_SessionManager *sm, UA_SecureChannel *channel, const UA_CreateSessionRequest *request, UA_Session **session); UA_StatusCode UA_SessionManager_removeSession(UA_SessionManager *sm, const UA_NodeId *token); UA_Session * UA_SessionManager_getSessionByToken(UA_SessionManager *sm, const UA_NodeId *token); UA_Session * UA_SessionManager_getSessionById(UA_SessionManager *sm, const UA_NodeId *sessionId); _UA_END_DECLS /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_securechannel_manager.h" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2014, 2017 (c) Florian Palm * Copyright 2015 (c) Oleksiy Vasylyev * Copyright 2017 (c) Stefan Profanter, fortiss GmbH */ _UA_BEGIN_DECLS typedef struct channel_entry { UA_DelayedCallback cleanupCallback; TAILQ_ENTRY(channel_entry) pointers; UA_SecureChannel channel; } channel_entry; typedef struct { TAILQ_HEAD(, channel_entry) channels; // doubly-linked list of channels UA_UInt32 currentChannelCount; UA_UInt32 lastChannelId; UA_UInt32 lastTokenId; UA_Server *server; } UA_SecureChannelManager; UA_StatusCode UA_SecureChannelManager_init(UA_SecureChannelManager *cm, UA_Server *server); /* Remove a all securechannels */ void UA_SecureChannelManager_deleteMembers(UA_SecureChannelManager *cm); /* Remove timed out securechannels with a delayed callback. So all currently * scheduled jobs with a pointer to a securechannel can finish first. */ void UA_SecureChannelManager_cleanupTimedOut(UA_SecureChannelManager *cm, UA_DateTime nowMonotonic); UA_StatusCode UA_SecureChannelManager_create(UA_SecureChannelManager *const cm, UA_Connection *const connection, const UA_SecurityPolicy *const securityPolicy, const UA_AsymmetricAlgorithmSecurityHeader *const asymHeader); UA_StatusCode UA_SecureChannelManager_open(UA_SecureChannelManager *cm, UA_SecureChannel *channel, const UA_OpenSecureChannelRequest *request, UA_OpenSecureChannelResponse *response); UA_StatusCode UA_SecureChannelManager_renew(UA_SecureChannelManager *cm, UA_SecureChannel *channel, const UA_OpenSecureChannelRequest *request, UA_OpenSecureChannelResponse *response); UA_SecureChannel * UA_SecureChannelManager_get(UA_SecureChannelManager *cm, UA_UInt32 channelId); UA_StatusCode UA_SecureChannelManager_close(UA_SecureChannelManager *cm, UA_UInt32 channelId); _UA_END_DECLS /*********************************** amalgamated original file "/home/jvoe/open62541/src/pubsub/ua_pubsub_networkmessage.h" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright (c) 2017 - 2018 Fraunhofer IOSB (Author: Tino Bischoff) */ _UA_BEGIN_DECLS /* DataSet Payload Header */ typedef struct { UA_Byte count; UA_UInt16* dataSetWriterIds; } UA_DataSetPayloadHeader; /* FieldEncoding Enum */ typedef enum { UA_FIELDENCODING_VARIANT = 0, UA_FIELDENCODING_RAWDATA = 1, UA_FIELDENCODING_DATAVALUE = 2 } UA_FieldEncoding; /* DataSetMessage Type */ typedef enum { UA_DATASETMESSAGE_DATAKEYFRAME = 0, UA_DATASETMESSAGE_DATADELTAFRAME = 1, UA_DATASETMESSAGE_EVENT = 2, UA_DATASETMESSAGE_KEEPALIVE = 3 } UA_DataSetMessageType; /* DataSetMessage Header */ typedef struct { UA_Boolean dataSetMessageValid; UA_FieldEncoding fieldEncoding; UA_Boolean dataSetMessageSequenceNrEnabled; UA_Boolean timestampEnabled; UA_Boolean statusEnabled; UA_Boolean configVersionMajorVersionEnabled; UA_Boolean configVersionMinorVersionEnabled; UA_DataSetMessageType dataSetMessageType; UA_Boolean picoSecondsIncluded; UA_UInt16 dataSetMessageSequenceNr; UA_UtcTime timestamp; UA_UInt16 picoSeconds; UA_UInt16 status; UA_UInt32 configVersionMajorVersion; UA_UInt32 configVersionMinorVersion; } UA_DataSetMessageHeader; UA_StatusCode UA_DataSetMessageHeader_encodeBinary(const UA_DataSetMessageHeader* src, UA_Byte **bufPos, const UA_Byte *bufEnd); UA_StatusCode UA_DataSetMessageHeader_decodeBinary(const UA_ByteString *src, size_t *offset, UA_DataSetMessageHeader* dst); size_t UA_DataSetMessageHeader_calcSizeBinary(const UA_DataSetMessageHeader* p); /** * DataSetMessage * ^^^^^^^^^^^^^^ */ typedef struct { UA_UInt16 fieldCount; UA_DataValue* dataSetFields; /* Json keys for the dataSetFields: TODO: own dataSetMessageType for json? */ UA_String* fieldNames; } UA_DataSetMessage_DataKeyFrameData; typedef struct { UA_UInt16 fieldIndex; UA_DataValue fieldValue; } UA_DataSetMessage_DeltaFrameField; typedef struct { UA_UInt16 fieldCount; UA_DataSetMessage_DeltaFrameField* deltaFrameFields; } UA_DataSetMessage_DataDeltaFrameData; typedef struct { UA_DataSetMessageHeader header; union { UA_DataSetMessage_DataKeyFrameData keyFrameData; UA_DataSetMessage_DataDeltaFrameData deltaFrameData; } data; } UA_DataSetMessage; UA_StatusCode UA_DataSetMessage_encodeBinary(const UA_DataSetMessage* src, UA_Byte **bufPos, const UA_Byte *bufEnd); UA_StatusCode UA_DataSetMessage_decodeBinary(const UA_ByteString *src, size_t *offset, UA_DataSetMessage* dst); size_t UA_DataSetMessage_calcSizeBinary(const UA_DataSetMessage* p); void UA_DataSetMessage_free(const UA_DataSetMessage* p); typedef struct { UA_UInt16* sizes; UA_DataSetMessage* dataSetMessages; } UA_DataSetPayload; typedef enum { UA_PUBLISHERDATATYPE_BYTE = 0, UA_PUBLISHERDATATYPE_UINT16 = 1, UA_PUBLISHERDATATYPE_UINT32 = 2, UA_PUBLISHERDATATYPE_UINT64 = 3, UA_PUBLISHERDATATYPE_STRING = 4 } UA_PublisherIdDatatype; typedef enum { UA_NETWORKMESSAGE_DATASET = 0, UA_NETWORKMESSAGE_DISCOVERY_REQUEST = 1, UA_NETWORKMESSAGE_DISCOVERY_RESPONSE = 2 } UA_NetworkMessageType; /** * UA_NetworkMessageGroupHeader * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */ typedef struct { UA_Boolean writerGroupIdEnabled; UA_Boolean groupVersionEnabled; UA_Boolean networkMessageNumberEnabled; UA_Boolean sequenceNumberEnabled; UA_UInt16 writerGroupId; UA_UInt32 groupVersion; // spec: type "VersionTime" UA_UInt16 networkMessageNumber; UA_UInt16 sequenceNumber; } UA_NetworkMessageGroupHeader; /** * UA_NetworkMessageSecurityHeader * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */ typedef struct { UA_Boolean networkMessageSigned; UA_Boolean networkMessageEncrypted; UA_Boolean securityFooterEnabled; UA_Boolean forceKeyReset; UA_UInt32 securityTokenId; // spec: IntegerId UA_Byte nonceLength; UA_ByteString messageNonce; UA_UInt16 securityFooterSize; } UA_NetworkMessageSecurityHeader; /** * UA_NetworkMessage * ^^^^^^^^^^^^^^^^^ */ typedef struct { UA_Byte version; UA_Boolean messageIdEnabled; UA_String messageId; /* For Json NetworkMessage */ UA_Boolean publisherIdEnabled; UA_Boolean groupHeaderEnabled; UA_Boolean payloadHeaderEnabled; UA_PublisherIdDatatype publisherIdType; UA_Boolean dataSetClassIdEnabled; UA_Boolean securityEnabled; UA_Boolean timestampEnabled; UA_Boolean picosecondsEnabled; UA_Boolean chunkMessage; UA_Boolean promotedFieldsEnabled; UA_NetworkMessageType networkMessageType; union { UA_Byte publisherIdByte; UA_UInt16 publisherIdUInt16; UA_UInt32 publisherIdUInt32; UA_UInt64 publisherIdUInt64; UA_Guid publisherIdGuid; UA_String publisherIdString; } publisherId; UA_Guid dataSetClassId; UA_NetworkMessageGroupHeader groupHeader; union { UA_DataSetPayloadHeader dataSetPayloadHeader; } payloadHeader; UA_DateTime timestamp; UA_UInt16 picoseconds; UA_UInt16 promotedFieldsSize; UA_Variant* promotedFields; /* BaseDataType */ UA_NetworkMessageSecurityHeader securityHeader; union { UA_DataSetPayload dataSetPayload; } payload; UA_ByteString securityFooter; UA_ByteString signature; } UA_NetworkMessage; UA_StatusCode UA_NetworkMessage_encodeBinary(const UA_NetworkMessage* src, UA_Byte **bufPos, const UA_Byte *bufEnd); UA_StatusCode UA_NetworkMessage_decodeBinary(const UA_ByteString *src, size_t *offset, UA_NetworkMessage* dst); size_t UA_NetworkMessage_calcSizeBinary(const UA_NetworkMessage* p); void UA_NetworkMessage_deleteMembers(UA_NetworkMessage* p); #define UA_NetworkMessage_clear(p) UA_NetworkMessage_deleteMembers(p) void UA_NetworkMessage_delete(UA_NetworkMessage* p); #ifdef UA_ENABLE_JSON_ENCODING UA_StatusCode UA_NetworkMessage_encodeJson(const UA_NetworkMessage *src, UA_Byte **bufPos, const UA_Byte **bufEnd, UA_String *namespaces, size_t namespaceSize, UA_String *serverUris, size_t serverUriSize, UA_Boolean useReversible); size_t UA_NetworkMessage_calcSizeJson(const UA_NetworkMessage *src, UA_String *namespaces, size_t namespaceSize, UA_String *serverUris, size_t serverUriSize, UA_Boolean useReversible); UA_StatusCode UA_NetworkMessage_decodeJson(UA_NetworkMessage *dst, const UA_ByteString *src); #endif _UA_END_DECLS /*********************************** amalgamated original file "/home/jvoe/open62541/src/pubsub/ua_pubsub.h" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright (c) 2017-2018 Fraunhofer IOSB (Author: Andreas Ebner) * Copyright (c) 2019 Kalycito Infotech Private Limited */ _UA_BEGIN_DECLS #ifdef UA_ENABLE_PUBSUB /* conditional compilation */ /* forward declarations */ struct UA_WriterGroup; typedef struct UA_WriterGroup UA_WriterGroup; /* Declaration for ReaderGroup */ struct UA_ReaderGroup; typedef struct UA_ReaderGroup UA_ReaderGroup; /* The configuration structs (public part of PubSub entities) are defined in include/ua_plugin_pubsub.h */ /**********************************************/ /* PublishedDataSet */ /**********************************************/ typedef struct{ UA_PublishedDataSetConfig config; UA_DataSetMetaDataType dataSetMetaData; LIST_HEAD(UA_ListOfDataSetField, UA_DataSetField) fields; UA_NodeId identifier; UA_UInt16 fieldSize; UA_UInt16 promotedFieldsCount; } UA_PublishedDataSet; UA_StatusCode UA_PublishedDataSetConfig_copy(const UA_PublishedDataSetConfig *src, UA_PublishedDataSetConfig *dst); UA_PublishedDataSet * UA_PublishedDataSet_findPDSbyId(UA_Server *server, UA_NodeId identifier); void UA_PublishedDataSet_deleteMembers(UA_Server *server, UA_PublishedDataSet *publishedDataSet); /**********************************************/ /* Connection */ /**********************************************/ //the connection config (public part of connection) object is defined in include/ua_plugin_pubsub.h typedef struct{ UA_PubSubConnectionConfig *config; //internal fields UA_PubSubChannel *channel; UA_NodeId identifier; LIST_HEAD(UA_ListOfWriterGroup, UA_WriterGroup) writerGroups; LIST_HEAD(UA_ListOfPubSubReaderGroup, UA_ReaderGroup) readerGroups; size_t readerGroupsSize; } UA_PubSubConnection; UA_StatusCode UA_PubSubConnectionConfig_copy(const UA_PubSubConnectionConfig *src, UA_PubSubConnectionConfig *dst); UA_PubSubConnection * UA_PubSubConnection_findConnectionbyId(UA_Server *server, UA_NodeId connectionIdentifier); void UA_PubSubConnectionConfig_deleteMembers(UA_PubSubConnectionConfig *connectionConfig); void UA_PubSubConnection_deleteMembers(UA_Server *server, UA_PubSubConnection *connection); /**********************************************/ /* DataSetWriter */ /**********************************************/ #ifdef UA_ENABLE_PUBSUB_DELTAFRAMES typedef struct UA_DataSetWriterSample{ UA_Boolean valueChanged; UA_DataValue value; } UA_DataSetWriterSample; #endif typedef struct UA_DataSetWriter{ UA_DataSetWriterConfig config; //internal fields LIST_ENTRY(UA_DataSetWriter) listEntry; UA_NodeId identifier; UA_NodeId linkedWriterGroup; UA_NodeId connectedDataSet; UA_ConfigurationVersionDataType connectedDataSetVersion; #ifdef UA_ENABLE_PUBSUB_DELTAFRAMES UA_UInt16 deltaFrameCounter; //actual count of sent deltaFrames size_t lastSamplesCount; UA_DataSetWriterSample *lastSamples; #endif UA_UInt16 actualDataSetMessageSequenceCount; } UA_DataSetWriter; UA_StatusCode UA_DataSetWriterConfig_copy(const UA_DataSetWriterConfig *src, UA_DataSetWriterConfig *dst); UA_DataSetWriter * UA_DataSetWriter_findDSWbyId(UA_Server *server, UA_NodeId identifier); /**********************************************/ /* WriterGroup */ /**********************************************/ struct UA_WriterGroup{ UA_WriterGroupConfig config; //internal fields LIST_ENTRY(UA_WriterGroup) listEntry; UA_NodeId identifier; UA_NodeId linkedConnection; LIST_HEAD(UA_ListOfDataSetWriter, UA_DataSetWriter) writers; UA_UInt32 writersCount; UA_UInt64 publishCallbackId; UA_Boolean publishCallbackIsRegistered; }; UA_StatusCode UA_WriterGroupConfig_copy(const UA_WriterGroupConfig *src, UA_WriterGroupConfig *dst); UA_WriterGroup * UA_WriterGroup_findWGbyId(UA_Server *server, UA_NodeId identifier); /**********************************************/ /* DataSetField */ /**********************************************/ typedef struct UA_DataSetField{ UA_DataSetFieldConfig config; //internal fields LIST_ENTRY(UA_DataSetField) listEntry; UA_NodeId identifier; UA_NodeId publishedDataSet; //ref to parent pds UA_FieldMetaData fieldMetaData; UA_UInt64 sampleCallbackId; UA_Boolean sampleCallbackIsRegistered; } UA_DataSetField; UA_StatusCode UA_DataSetFieldConfig_copy(const UA_DataSetFieldConfig *src, UA_DataSetFieldConfig *dst); UA_DataSetField * UA_DataSetField_findDSFbyId(UA_Server *server, UA_NodeId identifier); /**********************************************/ /* DataSetReader */ /**********************************************/ /* SubscribedDataSetDataType Definition */ typedef enum { UA_PUBSUB_SDS_TARGET, UA_PUBSUB_SDS_MIRROR }UA_SubscribedDataSetEnumType; /* DataSetReader Type definition */ typedef struct UA_DataSetReader { UA_DataSetReaderConfig config; /* implementation defined fields */ UA_NodeId identifier; UA_NodeId linkedReaderGroup; LIST_ENTRY(UA_DataSetReader) listEntry; UA_SubscribedDataSetEnumType subscribedDataSetType; UA_TargetVariablesDataType subscribedDataSetTarget; /* To Do UA_SubscribedDataSetMirrorDataType subscribedDataSetMirror */ }UA_DataSetReader; /* Delete DataSetReader */ void UA_DataSetReader_delete(UA_Server *server, UA_DataSetReader *dataSetReader); /* Process Network Message using DataSetReader */ void UA_Server_DataSetReader_process(UA_Server *server, UA_DataSetReader *dataSetReader, UA_DataSetMessage* dataSetMsg); /* Copy the configuration of DataSetReader */ UA_StatusCode UA_DataSetReaderConfig_copy(const UA_DataSetReaderConfig *src, UA_DataSetReaderConfig *dst); /* Add TargetVariables */ UA_StatusCode UA_Server_DataSetReader_addTargetVariables(UA_Server* server, UA_NodeId* parentNode, UA_NodeId dataSetReaderIdentifier, UA_SubscribedDataSetEnumType sdsType); /**********************************************/ /* ReaderGroup */ /**********************************************/ /* ReaderGroup Type Definition*/ struct UA_ReaderGroup { UA_ReaderGroupConfig config; UA_NodeId identifier; UA_NodeId linkedConnection; LIST_ENTRY(UA_ReaderGroup) listEntry; LIST_HEAD(UA_ListOfPubSubDataSetReader, UA_DataSetReader) readers; /* for simplified information access */ UA_UInt32 readersCount; UA_UInt64 subscribeCallbackId; UA_Boolean subscribeCallbackIsRegistered; }; /* Delete ReaderGroup */ void UA_Server_ReaderGroup_delete(UA_Server *server, UA_ReaderGroup *readerGroup); /* Copy configuration of ReaderGroup */ UA_StatusCode UA_ReaderGroupConfig_copy(const UA_ReaderGroupConfig *src, UA_ReaderGroupConfig *dst); /* Process Network Message */ UA_StatusCode UA_Server_processNetworkMessage(UA_Server *server, UA_NetworkMessage* pMsg, UA_PubSubConnection *pConnection); /* Prototypes for internal util functions - some functions maybe removed later *(currently moved from public to internal)*/ UA_ReaderGroup *UA_ReaderGroup_findRGbyId(UA_Server *server, UA_NodeId identifier); UA_DataSetReader *UA_ReaderGroup_findDSRbyId(UA_Server *server, UA_NodeId identifier); /*********************************************************/ /* PublishValues handling */ /*********************************************************/ UA_StatusCode UA_WriterGroup_addPublishCallback(UA_Server *server, UA_WriterGroup *writerGroup); void UA_WriterGroup_publishCallback(UA_Server *server, UA_WriterGroup *writerGroup); /*********************************************************/ /* SubscribeValues handling */ /*********************************************************/ UA_StatusCode UA_ReaderGroup_addSubscribeCallback(UA_Server *server, UA_ReaderGroup *readerGroup); void UA_ReaderGroup_subscribeCallback(UA_Server *server, UA_ReaderGroup *readerGroup); #endif /* UA_ENABLE_PUBSUB */ _UA_END_DECLS /*********************************** amalgamated original file "/home/jvoe/open62541/src/pubsub/ua_pubsub_manager.h" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright (c) 2017-2018 Fraunhofer IOSB (Author: Andreas Ebner) */ _UA_BEGIN_DECLS #ifdef UA_ENABLE_PUBSUB /* conditional compilation */ typedef struct UA_PubSubManager{ //Connections and PublishedDataSets can exist alone (own lifecycle) -> top level components size_t connectionsSize; UA_PubSubConnection *connections; size_t publishedDataSetsSize; UA_PublishedDataSet *publishedDataSets; } UA_PubSubManager; void UA_PubSubManager_delete(UA_Server *server, UA_PubSubManager *pubSubManager); void UA_PubSubManager_generateUniqueNodeId(UA_Server *server, UA_NodeId *nodeId); UA_UInt32 UA_PubSubConfigurationVersionTimeDifference(void); /***********************************/ /* PubSub Jobs abstraction */ /***********************************/ UA_StatusCode UA_PubSubManager_addRepeatedCallback(UA_Server *server, UA_ServerCallback callback, void *data, UA_Double interval_ms, UA_UInt64 *callbackId); UA_StatusCode UA_PubSubManager_changeRepeatedCallbackInterval(UA_Server *server, UA_UInt64 callbackId, UA_Double interval_ms); void UA_PubSubManager_removeRepeatedPubSubCallback(UA_Server *server, UA_UInt64 callbackId); #endif /* UA_ENABLE_PUBSUB */ _UA_END_DECLS /*********************************** amalgamated original file "/home/jvoe/open62541/src/pubsub/ua_pubsub_ns0.h" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright (c) 2017-2018 Fraunhofer IOSB (Author: Andreas Ebner) * Copyright (c) 2019 Kalycito Infotech Private Limited */ #ifndef UA_PUBSUB_NS0_H_ #define UA_PUBSUB_NS0_H_ _UA_BEGIN_DECLS #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL /* conditional compilation */ UA_StatusCode UA_Server_initPubSubNS0(UA_Server *server); UA_StatusCode addPubSubConnectionRepresentation(UA_Server *server, UA_PubSubConnection *connection); UA_StatusCode removePubSubConnectionRepresentation(UA_Server *server, UA_PubSubConnection *connection); UA_StatusCode addWriterGroupRepresentation(UA_Server *server, UA_WriterGroup *writerGroup); UA_StatusCode addReaderGroupRepresentation(UA_Server *server, UA_ReaderGroup *readerGroup); UA_StatusCode removeGroupRepresentation(UA_Server *server, UA_WriterGroup *writerGroup); UA_StatusCode addDataSetWriterRepresentation(UA_Server *server, UA_DataSetWriter *dataSetWriter); UA_StatusCode removeDataSetWriterRepresentation(UA_Server *server, UA_DataSetWriter *dataSetWriter); UA_StatusCode addPublishedDataItemsRepresentation(UA_Server *server, UA_PublishedDataSet *publishedDataSet); UA_StatusCode removePublishedDataSetRepresentation(UA_Server *server, UA_PublishedDataSet *publishedDataSet); UA_StatusCode addDataSetReaderRepresentation(UA_Server *server, UA_DataSetReader *dataSetReader); UA_StatusCode removeDataSetReaderRepresentation(UA_Server *server, UA_DataSetReader *dataSetReader); #endif /* UA_ENABLE_PUBSUB_INFORMATIONMODEL */ _UA_END_DECLS #endif /* UA_PUBSUB_NS0_H_ */ /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_server_internal.h" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2014, 2017 (c) Florian Palm * Copyright 2015-2016 (c) Sten Grüner * Copyright 2015 (c) Chris Iatrou * Copyright 2015-2016 (c) Oleksiy Vasylyev * Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2017 (c) Julian Grothoff * Copyright 2019 (c) Kalycito Infotech Private Limited */ _UA_BEGIN_DECLS #ifdef UA_ENABLE_PUBSUB #endif #ifdef UA_ENABLE_DISCOVERY #endif #ifdef UA_ENABLE_SUBSCRIPTIONS typedef struct { UA_MonitoredItem monitoredItem; void *context; union { UA_Server_DataChangeNotificationCallback dataChangeCallback; /* UA_Server_EventNotificationCallback eventCallback; */ } callback; } UA_LocalMonitoredItem; #endif typedef enum { UA_SERVERLIFECYCLE_FRESH, UA_SERVERLIFECYLE_RUNNING } UA_ServerLifecycle; struct UA_Server { /* Config */ UA_ServerConfig config; UA_DateTime startTime; UA_DateTime endTime; /* Zeroed out. If a time is set, then the server shuts * down once the time has been reached */ /* Nodestore */ void *nsCtx; UA_ServerLifecycle state; /* Security */ UA_SecureChannelManager secureChannelManager; UA_SessionManager sessionManager; UA_Session adminSession; /* Local access to the services (for startup and * maintenance) uses this Session with all possible * access rights (Session Id: 1) */ /* Namespaces */ size_t namespacesSize; UA_String *namespaces; /* Callbacks with a repetition interval */ UA_Timer timer; /* WorkQueue and worker threads */ UA_WorkQueue workQueue; /* For bootstrapping, omit some consistency checks, creating a reference to * the parent and member instantiation */ UA_Boolean bootstrapNS0; /* Discovery */ #ifdef UA_ENABLE_DISCOVERY UA_DiscoveryManager discoveryManager; #endif /* DataChange Subscriptions */ #ifdef UA_ENABLE_SUBSCRIPTIONS /* Num active subscriptions */ UA_UInt32 numSubscriptions; /* Num active monitored items */ UA_UInt32 numMonitoredItems; /* To be cast to UA_LocalMonitoredItem to get the callback and context */ LIST_HEAD(LocalMonitoredItems, UA_MonitoredItem) localMonitoredItems; UA_UInt32 lastLocalMonitoredItemId; #endif /* Publish/Subscribe */ #ifdef UA_ENABLE_PUBSUB UA_PubSubManager pubSubManager; #endif }; /*****************/ /* Node Handling */ /*****************/ /* Deletes references from the node which are not matching any type in the given * array. Could be used to e.g. delete all the references, except * 'HASMODELINGRULE' */ void UA_Node_deleteReferencesSubset(UA_Node *node, size_t referencesSkipSize, UA_NodeId* referencesSkip); /* Calls the callback with the node retrieved from the nodestore on top of the * stack. Either a copy or the original node for in-situ editing. Depends on * multithreading and the nodestore.*/ typedef UA_StatusCode (*UA_EditNodeCallback)(UA_Server*, UA_Session*, UA_Node *node, void*); UA_StatusCode UA_Server_editNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId, UA_EditNodeCallback callback, void *data); /*********************/ /* Utility Functions */ /*********************/ /* A few global NodeId definitions */ extern const UA_NodeId subtypeId; extern const UA_NodeId hierarchicalReferences; void setupNs1Uri(UA_Server *server); UA_UInt16 addNamespace(UA_Server *server, const UA_String name); UA_Boolean UA_Node_hasSubTypeOrInstances(const UA_Node *node); /* Recursively searches "upwards" in the tree following specific reference types */ UA_Boolean isNodeInTree(void *nsCtx, const UA_NodeId *leafNode, const UA_NodeId *nodeToFind, const UA_NodeId *referenceTypeIds, size_t referenceTypeIdsSize); /* Returns an array with the hierarchy of nodes. The start nodes are returned as * well. The returned array starts at the leaf and continues "upwards" or * "downwards". Duplicate entries are removed. The parameter `walkDownwards` * indicates the direction of search. */ UA_StatusCode browseRecursive(UA_Server *server, size_t startNodesSize, const UA_NodeId *startNodes, size_t refTypesSize, const UA_NodeId *refTypes, UA_BrowseDirection browseDirection, UA_Boolean includeStartNodes, size_t *resultsSize, UA_ExpandedNodeId **results); /* If refTypes is non-NULL, tries to realloc and increase the length */ UA_StatusCode referenceSubtypes(UA_Server *server, const UA_NodeId *refType, size_t *refTypesSize, UA_NodeId **refTypes); /* Returns the recursive type and interface hierarchy of the node */ UA_StatusCode getParentTypeAndInterfaceHierarchy(UA_Server *server, const UA_NodeId *typeNode, UA_NodeId **typeHierarchy, size_t *typeHierarchySize); /* Returns the type node from the node on the stack top. The type node is pushed * on the stack and returned. */ const UA_Node * getNodeType(UA_Server *server, const UA_Node *node); /* Write a node attribute with a defined session */ UA_StatusCode UA_Server_writeWithSession(UA_Server *server, UA_Session *session, const UA_WriteValue *value); /* Many services come as an array of operations. This function generalizes the * processing of the operations. */ typedef void (*UA_ServiceOperation)(UA_Server *server, UA_Session *session, const void *context, const void *requestOperation, void *responseOperation); UA_StatusCode UA_Server_processServiceOperations(UA_Server *server, UA_Session *session, UA_ServiceOperation operationCallback, const void *context, const size_t *requestOperations, const UA_DataType *requestOperationsType, size_t *responseOperations, const UA_DataType *responseOperationsType) UA_FUNC_ATTR_WARN_UNUSED_RESULT; /***************************************/ /* Check Information Model Consistency */ /***************************************/ /* Read a node attribute in the context of a "checked-out" node. So the * attribute will not be copied when possible. The variant then points into the * node and has UA_VARIANT_DATA_NODELETE set. */ void ReadWithNode(const UA_Node *node, UA_Server *server, UA_Session *session, UA_TimestampsToReturn timestampsToReturn, const UA_ReadValueId *id, UA_DataValue *v); UA_StatusCode readValueAttribute(UA_Server *server, UA_Session *session, const UA_VariableNode *vn, UA_DataValue *v); /* Test whether the value matches a variable definition given by * - datatype * - valueranke * - array dimensions. * Sometimes it can be necessary to transform the content of the value, e.g. * byte array to bytestring or uint32 to some enum. If editableValue is non-NULL, * we try to create a matching variant that points to the original data. */ UA_Boolean compatibleValue(UA_Server *server, UA_Session *session, const UA_NodeId *targetDataTypeId, UA_Int32 targetValueRank, size_t targetArrayDimensionsSize, const UA_UInt32 *targetArrayDimensions, const UA_Variant *value, const UA_NumericRange *range); UA_Boolean compatibleArrayDimensions(size_t constraintArrayDimensionsSize, const UA_UInt32 *constraintArrayDimensions, size_t testArrayDimensionsSize, const UA_UInt32 *testArrayDimensions); UA_Boolean compatibleValueArrayDimensions(const UA_Variant *value, size_t targetArrayDimensionsSize, const UA_UInt32 *targetArrayDimensions); UA_Boolean compatibleValueRankArrayDimensions(UA_Server *server, UA_Session *session, UA_Int32 valueRank, size_t arrayDimensionsSize); UA_Boolean compatibleDataType(UA_Server *server, const UA_NodeId *dataType, const UA_NodeId *constraintDataType, UA_Boolean isValue); UA_Boolean compatibleValueRanks(UA_Int32 valueRank, UA_Int32 constraintValueRank); struct BrowseOpts { UA_UInt32 maxReferences; UA_Boolean recursive; }; void Operation_Browse(UA_Server *server, UA_Session *session, const UA_UInt32 *maxrefs, const UA_BrowseDescription *descr, UA_BrowseResult *result); UA_DataValue UA_Server_readWithSession(UA_Server *server, UA_Session *session, const UA_ReadValueId *item, UA_TimestampsToReturn timestampsToReturn); /*****************************/ /* AddNodes Begin and Finish */ /*****************************/ /* Creates a new node in the nodestore. */ UA_StatusCode AddNode_raw(UA_Server *server, UA_Session *session, void *nodeContext, const UA_AddNodesItem *item, UA_NodeId *outNewNodeId); /* Check the reference to the parent node; Add references. */ UA_StatusCode AddNode_addRefs(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId, const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId, const UA_NodeId *typeDefinitionId); /* Type-check type-definition; Run the constructors */ UA_StatusCode AddNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId); /**********************/ /* Create Namespace 0 */ /**********************/ UA_StatusCode UA_Server_initNS0(UA_Server *server); UA_StatusCode writeNs0VariableArray(UA_Server *server, UA_UInt32 id, void *v, size_t length, const UA_DataType *type); _UA_END_DECLS /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_services.h" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2014-2017 (c) Florian Palm * Copyright 2015 (c) Sten Grüner * Copyright 2014 (c) LEvertz * Copyright 2015 (c) Chris Iatrou * Copyright 2015 (c) Christian Fimmers * Copyright 2015-2016 (c) Oleksiy Vasylyev * Copyright 2017 (c) Stefan Profanter, fortiss GmbH */ _UA_BEGIN_DECLS /** * .. _services: * * Services * ======== * * In OPC UA, all communication is based on service calls, each consisting of a * request and a response message. These messages are defined as data structures * with a binary encoding and listed in :ref:`generated-types`. Since all * Services are pre-defined in the standard, they cannot be modified by the * user. But you can use the :ref:`Call ` service to invoke * user-defined methods on the server. * * The following service signatures are internal and *not visible to users*. * Still, we present them here for an overview of the capabilities of OPC UA. * Please refer to the :ref:`client` and :ref:`server` API where the services * are exposed to end users. Please see part 4 of the OPC UA standard for the * authoritative definition of the service and their behaviour. * * Most services take as input the server, the current session and pointers to * the request and response structures. Possible error codes are returned as * part of the response. */ typedef void (*UA_Service)(UA_Server*, UA_Session*, const void *request, void *response); /** * Discovery Service Set * --------------------- * This Service Set defines Services used to discover the Endpoints implemented * by a Server and to read the security configuration for those Endpoints. * * FindServers Service * ^^^^^^^^^^^^^^^^^^^ * Returns the Servers known to a Server or Discovery Server. The Client may * reduce the number of results returned by specifying filter criteria */ void Service_FindServers(UA_Server *server, UA_Session *session, const UA_FindServersRequest *request, UA_FindServersResponse *response); /** * GetEndpoints Service * ^^^^^^^^^^^^^^^^^^^^ * Returns the Endpoints supported by a Server and all of the configuration * information required to establish a SecureChannel and a Session. */ void Service_GetEndpoints(UA_Server *server, UA_Session *session, const UA_GetEndpointsRequest *request, UA_GetEndpointsResponse *response); #ifdef UA_ENABLE_DISCOVERY # ifdef UA_ENABLE_DISCOVERY_MULTICAST /** * FindServersOnNetwork Service * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Returns the Servers known to a Discovery Server. Unlike FindServer, * this Service is only implemented by Discovery Servers. It additionally * returns servers which may have been detected through Multicast. */ void Service_FindServersOnNetwork(UA_Server *server, UA_Session *session, const UA_FindServersOnNetworkRequest *request, UA_FindServersOnNetworkResponse *response); # endif /* UA_ENABLE_DISCOVERY_MULTICAST */ /** * RegisterServer * ^^^^^^^^^^^^^^ * Registers a remote server in the local discovery service. */ void Service_RegisterServer(UA_Server *server, UA_Session *session, const UA_RegisterServerRequest *request, UA_RegisterServerResponse *response); /** * RegisterServer2 * ^^^^^^^^^^^^^^^ * This Service allows a Server to register its DiscoveryUrls and capabilities * with a Discovery Server. It extends the registration information from * RegisterServer with information necessary for FindServersOnNetwork. */ void Service_RegisterServer2(UA_Server *server, UA_Session *session, const UA_RegisterServer2Request *request, UA_RegisterServer2Response *response); #endif /* UA_ENABLE_DISCOVERY */ /** * SecureChannel Service Set * ------------------------- * This Service Set defines Services used to open a communication channel that * ensures the confidentiality and Integrity of all Messages exchanged with the * Server. * * OpenSecureChannel Service * ^^^^^^^^^^^^^^^^^^^^^^^^^ * Open or renew a SecureChannel that can be used to ensure Confidentiality and * Integrity for Message exchange during a Session. */ void Service_OpenSecureChannel(UA_Server *server, UA_SecureChannel* channel, const UA_OpenSecureChannelRequest *request, UA_OpenSecureChannelResponse *response); /** * CloseSecureChannel Service * ^^^^^^^^^^^^^^^^^^^^^^^^^^ * Used to terminate a SecureChannel. */ void Service_CloseSecureChannel(UA_Server *server, UA_SecureChannel *channel); /** * Session Service Set * ------------------- * This Service Set defines Services for an application layer connection * establishment in the context of a Session. * * CreateSession Service * ^^^^^^^^^^^^^^^^^^^^^ * Used by an OPC UA Client to create a Session and the Server returns two * values which uniquely identify the Session. The first value is the sessionId * which is used to identify the Session in the audit logs and in the Server's * address space. The second is the authenticationToken which is used to * associate an incoming request with a Session. */ void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel, const UA_CreateSessionRequest *request, UA_CreateSessionResponse *response); /** * ActivateSession * ^^^^^^^^^^^^^^^ * Used by the Client to submit its SoftwareCertificates to the Server for * validation and to specify the identity of the user associated with the * Session. This Service request shall be issued by the Client before it issues * any other Service request after CreateSession. Failure to do so shall cause * the Server to close the Session. */ void Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel, UA_Session *session, const UA_ActivateSessionRequest *request, UA_ActivateSessionResponse *response); /** * CloseSession * ^^^^^^^^^^^^ * Used to terminate a Session. */ void Service_CloseSession(UA_Server *server, UA_Session *session, const UA_CloseSessionRequest *request, UA_CloseSessionResponse *response); /** * Cancel Service * ^^^^^^^^^^^^^^ * Used to cancel outstanding Service requests. Successfully cancelled service * requests shall respond with Bad_RequestCancelledByClient. */ /* Not Implemented */ /** * NodeManagement Service Set * -------------------------- * This Service Set defines Services to add and delete AddressSpace Nodes and * References between them. All added Nodes continue to exist in the * AddressSpace even if the Client that created them disconnects from the * Server. * * AddNodes Service * ^^^^^^^^^^^^^^^^ * Used to add one or more Nodes into the AddressSpace hierarchy. */ void Service_AddNodes(UA_Server *server, UA_Session *session, const UA_AddNodesRequest *request, UA_AddNodesResponse *response); /** * AddReferences Service * ^^^^^^^^^^^^^^^^^^^^^ * Used to add one or more References to one or more Nodes. */ void Service_AddReferences(UA_Server *server, UA_Session *session, const UA_AddReferencesRequest *request, UA_AddReferencesResponse *response); /** * DeleteNodes Service * ^^^^^^^^^^^^^^^^^^^ * Used to delete one or more Nodes from the AddressSpace. */ void Service_DeleteNodes(UA_Server *server, UA_Session *session, const UA_DeleteNodesRequest *request, UA_DeleteNodesResponse *response); /** * DeleteReferences * ^^^^^^^^^^^^^^^^ * Used to delete one or more References of a Node. */ void Service_DeleteReferences(UA_Server *server, UA_Session *session, const UA_DeleteReferencesRequest *request, UA_DeleteReferencesResponse *response); /** * .. _view-services: * * View Service Set * ---------------- * Clients use the browse Services of the View Service Set to navigate through * the AddressSpace or through a View which is a subset of the AddressSpace. * * Browse Service * ^^^^^^^^^^^^^^ * Used to discover the References of a specified Node. The browse can be * further limited by the use of a View. This Browse Service also supports a * primitive filtering capability. */ void Service_Browse(UA_Server *server, UA_Session *session, const UA_BrowseRequest *request, UA_BrowseResponse *response); /** * BrowseNext Service * ^^^^^^^^^^^^^^^^^^ * Used to request the next set of Browse or BrowseNext response information * that is too large to be sent in a single response. "Too large" in this * context means that the Server is not able to return a larger response or that * the number of results to return exceeds the maximum number of results to * return that was specified by the Client in the original Browse request. */ void Service_BrowseNext(UA_Server *server, UA_Session *session, const UA_BrowseNextRequest *request, UA_BrowseNextResponse *response); /** * TranslateBrowsePathsToNodeIds Service * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Used to translate textual node paths to their respective ids. */ void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session, const UA_TranslateBrowsePathsToNodeIdsRequest *request, UA_TranslateBrowsePathsToNodeIdsResponse *response); /** * RegisterNodes Service * ^^^^^^^^^^^^^^^^^^^^^ * Used by Clients to register the Nodes that they know they will access * repeatedly (e.g. Write, Call). It allows Servers to set up anything needed so * that the access operations will be more efficient. */ void Service_RegisterNodes(UA_Server *server, UA_Session *session, const UA_RegisterNodesRequest *request, UA_RegisterNodesResponse *response); /** * UnregisterNodes Service * ^^^^^^^^^^^^^^^^^^^^^^^ * This Service is used to unregister NodeIds that have been obtained via the * RegisterNodes service. */ void Service_UnregisterNodes(UA_Server *server, UA_Session *session, const UA_UnregisterNodesRequest *request, UA_UnregisterNodesResponse *response); /** * Query Service Set * ----------------- * This Service Set is used to issue a Query to a Server. OPC UA Query is * generic in that it provides an underlying storage mechanism independent Query * capability that can be used to access a wide variety of OPC UA data stores * and information management systems. OPC UA Query permits a Client to access * data maintained by a Server without any knowledge of the logical schema used * for internal storage of the data. Knowledge of the AddressSpace is * sufficient. * * QueryFirst Service * ^^^^^^^^^^^^^^^^^^ * This Service is used to issue a Query request to the Server. */ /* Not Implemented */ /** * QueryNext Service * ^^^^^^^^^^^^^^^^^ * This Service is used to request the next set of QueryFirst or QueryNext * response information that is too large to be sent in a single response. */ /* Not Impelemented */ /** * Attribute Service Set * --------------------- * This Service Set provides Services to access Attributes that are part of * Nodes. * * Read Service * ^^^^^^^^^^^^ * Used to read attributes of nodes. For constructed attribute values whose * elements are indexed, such as an array, this Service allows Clients to read * the entire set of indexed values as a composite, to read individual elements * or to read ranges of elements of the composite. */ void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *request, UA_ReadResponse *response); /** * Write Service * ^^^^^^^^^^^^^ * Used to write attributes of nodes. For constructed attribute values whose * elements are indexed, such as an array, this Service allows Clients to write * the entire set of indexed values as a composite, to write individual elements * or to write ranges of elements of the composite. */ void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request, UA_WriteResponse *response); /** * HistoryRead Service * ^^^^^^^^^^^^^^^^^^^ * Used to read historical values or Events of one or more Nodes. Servers may * make historical values available to Clients using this Service, although the * historical values themselves are not visible in the AddressSpace. */ #ifdef UA_ENABLE_HISTORIZING void Service_HistoryRead(UA_Server *server, UA_Session *session, const UA_HistoryReadRequest *request, UA_HistoryReadResponse *response); /** * HistoryUpdate Service * ^^^^^^^^^^^^^^^^^^^^^ * Used to update historical values or Events of one or more Nodes. Several * request parameters indicate how the Server is to update the historical value * or Event. Valid actions are Insert, Replace or Delete. */ void Service_HistoryUpdate(UA_Server *server, UA_Session *session, const UA_HistoryUpdateRequest *request, UA_HistoryUpdateResponse *response); #endif /** * .. _method-services: * * Method Service Set * ------------------ * The Method Service Set defines the means to invoke methods. A method shall be * a component of an Object. See the section on :ref:`MethodNodes ` * for more information. * * Call Service * ^^^^^^^^^^^^ * Used to call (invoke) a methods. Each method call is invoked within the * context of an existing Session. If the Session is terminated, the results of * the method's execution cannot be returned to the Client and are discarded. */ #ifdef UA_ENABLE_METHODCALLS void Service_Call(UA_Server *server, UA_Session *session, const UA_CallRequest *request, UA_CallResponse *response); #endif #ifdef UA_ENABLE_SUBSCRIPTIONS /** * MonitoredItem Service Set * ------------------------- * Clients define MonitoredItems to subscribe to data and Events. Each * MonitoredItem identifies the item to be monitored and the Subscription to use * to send Notifications. The item to be monitored may be any Node Attribute. * * CreateMonitoredItems Service * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Used to create and add one or more MonitoredItems to a Subscription. A * MonitoredItem is deleted automatically by the Server when the Subscription is * deleted. Deleting a MonitoredItem causes its entire set of triggered item * links to be deleted, but has no effect on the MonitoredItems referenced by * the triggered items. */ void Service_CreateMonitoredItems(UA_Server *server, UA_Session *session, const UA_CreateMonitoredItemsRequest *request, UA_CreateMonitoredItemsResponse *response); /** * DeleteMonitoredItems Service * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Used to remove one or more MonitoredItems of a Subscription. When a * MonitoredItem is deleted, its triggered item links are also deleted. */ void Service_DeleteMonitoredItems(UA_Server *server, UA_Session *session, const UA_DeleteMonitoredItemsRequest *request, UA_DeleteMonitoredItemsResponse *response); /** * ModifyMonitoredItems Service * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Used to modify MonitoredItems of a Subscription. Changes to the MonitoredItem * settings shall be applied immediately by the Server. They take effect as soon * as practical but not later than twice the new revisedSamplingInterval. * * Illegal request values for parameters that can be revised do not generate * errors. Instead the server will choose default values and indicate them in * the corresponding revised parameter. */ void Service_ModifyMonitoredItems(UA_Server *server, UA_Session *session, const UA_ModifyMonitoredItemsRequest *request, UA_ModifyMonitoredItemsResponse *response); /** * SetMonitoringMode Service * ^^^^^^^^^^^^^^^^^^^^^^^^^ * Used to set the monitoring mode for one or more MonitoredItems of a * Subscription. */ void Service_SetMonitoringMode(UA_Server *server, UA_Session *session, const UA_SetMonitoringModeRequest *request, UA_SetMonitoringModeResponse *response); /** * SetTriggering Service * ^^^^^^^^^^^^^^^^^^^^^ * Used to create and delete triggering links for a triggering item. */ /* Not Implemented */ /** * Subscription Service Set * ------------------------ * Subscriptions are used to report Notifications to the Client. * * CreateSubscription Service * ^^^^^^^^^^^^^^^^^^^^^^^^^^ * Used to create a Subscription. Subscriptions monitor a set of MonitoredItems * for Notifications and return them to the Client in response to Publish * requests. */ void Service_CreateSubscription(UA_Server *server, UA_Session *session, const UA_CreateSubscriptionRequest *request, UA_CreateSubscriptionResponse *response); /** * ModifySubscription Service * ^^^^^^^^^^^^^^^^^^^^^^^^^^ * Used to modify a Subscription. */ void Service_ModifySubscription(UA_Server *server, UA_Session *session, const UA_ModifySubscriptionRequest *request, UA_ModifySubscriptionResponse *response); /** * SetPublishingMode Service * ^^^^^^^^^^^^^^^^^^^^^^^^^ * Used to enable sending of Notifications on one or more Subscriptions. */ void Service_SetPublishingMode(UA_Server *server, UA_Session *session, const UA_SetPublishingModeRequest *request, UA_SetPublishingModeResponse *response); /** * Publish Service * ^^^^^^^^^^^^^^^ * Used for two purposes. First, it is used to acknowledge the receipt of * NotificationMessages for one or more Subscriptions. Second, it is used to * request the Server to return a NotificationMessage or a keep-alive * Message. * * Note that the service signature is an exception and does not contain a * pointer to a PublishResponse. That is because the service queues up publish * requests internally and sends responses asynchronously based on timeouts. */ void Service_Publish(UA_Server *server, UA_Session *session, const UA_PublishRequest *request, UA_UInt32 requestId); /** * Republish Service * ^^^^^^^^^^^^^^^^^ * Requests the Subscription to republish a NotificationMessage from its * retransmission queue. */ void Service_Republish(UA_Server *server, UA_Session *session, const UA_RepublishRequest *request, UA_RepublishResponse *response); /** * DeleteSubscriptions Service * ^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Invoked to delete one or more Subscriptions that belong to the Client's * Session. */ void Service_DeleteSubscriptions(UA_Server *server, UA_Session *session, const UA_DeleteSubscriptionsRequest *request, UA_DeleteSubscriptionsResponse *response); /** * TransferSubscription Service * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Used to transfer a Subscription and its MonitoredItems from one Session to * another. For example, a Client may need to reopen a Session and then transfer * its Subscriptions to that Session. It may also be used by one Client to take * over a Subscription from another Client by transferring the Subscription to * its Session. */ /* Not Implemented */ #endif /* UA_ENABLE_SUBSCRIPTIONS */ _UA_END_DECLS /*********************************** amalgamated original file "/home/jvoe/open62541/src/client/ua_client_internal.h" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2015-2016 (c) Sten Grüner * Copyright 2015-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2015 (c) Oleksiy Vasylyev * Copyright 2016-2017 (c) Florian Palm * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB */ #define UA_INTERNAL _UA_BEGIN_DECLS /**************************/ /* Subscriptions Handling */ /**************************/ #ifdef UA_ENABLE_SUBSCRIPTIONS typedef struct UA_Client_NotificationsAckNumber { LIST_ENTRY(UA_Client_NotificationsAckNumber) listEntry; UA_SubscriptionAcknowledgement subAck; } UA_Client_NotificationsAckNumber; typedef struct UA_Client_MonitoredItem { LIST_ENTRY(UA_Client_MonitoredItem) listEntry; UA_UInt32 monitoredItemId; UA_UInt32 clientHandle; void *context; UA_Client_DeleteMonitoredItemCallback deleteCallback; union { UA_Client_DataChangeNotificationCallback dataChangeCallback; UA_Client_EventNotificationCallback eventCallback; } handler; UA_Boolean isEventMonitoredItem; /* Otherwise a DataChange MoniitoredItem */ } UA_Client_MonitoredItem; typedef struct UA_Client_Subscription { LIST_ENTRY(UA_Client_Subscription) listEntry; UA_UInt32 subscriptionId; void *context; UA_Double publishingInterval; UA_UInt32 maxKeepAliveCount; UA_Client_StatusChangeNotificationCallback statusChangeCallback; UA_Client_DeleteSubscriptionCallback deleteCallback; UA_UInt32 sequenceNumber; UA_DateTime lastActivity; LIST_HEAD(UA_ListOfClientMonitoredItems, UA_Client_MonitoredItem) monitoredItems; } UA_Client_Subscription; void UA_Client_Subscriptions_clean(UA_Client *client); void UA_Client_MonitoredItem_remove(UA_Client *client, UA_Client_Subscription *sub, UA_Client_MonitoredItem *mon); void UA_Client_Subscriptions_processPublishResponse(UA_Client *client, UA_PublishRequest *request, UA_PublishResponse *response); UA_StatusCode UA_Client_preparePublishRequest(UA_Client *client, UA_PublishRequest *request); UA_StatusCode UA_Client_Subscriptions_backgroundPublish(UA_Client *client); void UA_Client_Subscriptions_backgroundPublishInactivityCheck(UA_Client *client); #endif /* UA_ENABLE_SUBSCRIPTIONS */ /**************/ /* Encryption */ /**************/ UA_StatusCode signActivateSessionRequest(UA_SecureChannel *channel, UA_ActivateSessionRequest *request); /**********/ /* Client */ /**********/ typedef struct AsyncServiceCall { LIST_ENTRY(AsyncServiceCall) pointers; UA_UInt32 requestId; UA_ClientAsyncServiceCallback callback; const UA_DataType *responseType; void *userdata; UA_DateTime start; UA_UInt32 timeout; void *responsedata; } AsyncServiceCall; void UA_Client_AsyncService_cancel(UA_Client *client, AsyncServiceCall *ac, UA_StatusCode statusCode); void UA_Client_AsyncService_removeAll(UA_Client *client, UA_StatusCode statusCode); typedef struct CustomCallback { LIST_ENTRY(CustomCallback) pointers; //to find the correct callback UA_UInt32 callbackId; UA_ClientAsyncServiceCallback callback; UA_AttributeId attributeId; const UA_DataType *outDataType; } CustomCallback; struct UA_Client { /* State */ UA_ClientState state; UA_ClientConfig config; UA_Timer timer; UA_StatusCode connectStatus; /* Connection */ UA_Connection connection; /* SecureChannel */ UA_SecureChannel channel; UA_UInt32 requestId; UA_DateTime nextChannelRenewal; /* Session */ UA_NodeId authenticationToken; UA_UInt32 requestHandle; UA_Boolean endpointsHandshake; UA_String endpointUrl; /* Only for the async connect */ /* Async Service */ AsyncServiceCall asyncConnectCall; LIST_HEAD(ListOfAsyncServiceCall, AsyncServiceCall) asyncServiceCalls; /*When using highlevel functions these are the callbacks that can be accessed by the user*/ LIST_HEAD(ListOfCustomCallback, CustomCallback) customCallbacks; /* Work queue */ UA_WorkQueue workQueue; /* Subscriptions */ #ifdef UA_ENABLE_SUBSCRIPTIONS UA_UInt32 monitoredItemHandles; LIST_HEAD(, UA_Client_NotificationsAckNumber) pendingNotificationsAcks; LIST_HEAD(, UA_Client_Subscription) subscriptions; UA_UInt16 currentlyOutStandingPublishRequests; #endif /* Connectivity check */ UA_DateTime lastConnectivityCheck; UA_Boolean pendingConnectivityCheck; }; void setClientState(UA_Client *client, UA_ClientState state); /* The endpointUrl must be set in the configuration. If the complete * endpointdescription is not set, a GetEndpoints is performed. */ UA_StatusCode UA_Client_connectInternal(UA_Client *client, const UA_String endpointUrl); UA_StatusCode UA_Client_connectTCPSecureChannel(UA_Client *client, const UA_String endpointUrl); UA_StatusCode UA_Client_connectSession(UA_Client *client); UA_StatusCode UA_Client_getEndpointsInternal(UA_Client *client, const UA_String endpointUrl, size_t *endpointDescriptionsSize, UA_EndpointDescription **endpointDescriptions); /* Receive and process messages until a synchronous message arrives or the * timout finishes */ UA_StatusCode receivePacketAsync(UA_Client *client); UA_StatusCode processACKResponseAsync(void *application, UA_Connection *connection, UA_ByteString *chunk); UA_StatusCode processOPNResponseAsync(void *application, UA_Connection *connection, UA_ByteString *chunk); UA_StatusCode openSecureChannel(UA_Client *client, UA_Boolean renew); UA_StatusCode receiveServiceResponse(UA_Client *client, void *response, const UA_DataType *responseType, UA_DateTime maxDate, const UA_UInt32 *synchronousRequestId); UA_StatusCode receiveServiceResponseAsync(UA_Client *client, void *response, const UA_DataType *responseType); UA_StatusCode UA_Client_connect_iterate (UA_Client *client); void setUserIdentityPolicyId(const UA_EndpointDescription *endpoint, const UA_DataType *tokenType, UA_String *policyId, UA_String *securityPolicyUri); UA_SecurityPolicy * getSecurityPolicy(UA_Client *client, UA_String policyUri); UA_StatusCode encryptUserIdentityToken(UA_Client *client, const UA_String *userTokenSecurityPolicy, UA_ExtensionObject *userIdentityToken); _UA_END_DECLS /*********************************** amalgamated original file "/home/jvoe/open62541/build/src_generated/open62541/namespace0_generated.h" ***********************************/ /* WARNING: This is a generated file. * Any manual changes will be overwritten. */ #ifndef NAMESPACE0_GENERATED_H_ #define NAMESPACE0_GENERATED_H_ #ifdef UA_ENABLE_AMALGAMATION /* The following declarations are in the open62541.c file so here's needed when compiling nodesets externally */ # ifndef UA_INTERNAL //this definition is needed to hide this code in the amalgamated .c file typedef UA_StatusCode (*UA_exchangeEncodeBuffer)(void *handle, UA_Byte **bufPos, const UA_Byte **bufEnd); UA_StatusCode UA_encodeBinary(const void *src, const UA_DataType *type, UA_Byte **bufPos, const UA_Byte **bufEnd, UA_exchangeEncodeBuffer exchangeCallback, void *exchangeHandle) UA_FUNC_ATTR_WARN_UNUSED_RESULT; UA_StatusCode UA_decodeBinary(const UA_ByteString *src, size_t *offset, void *dst, const UA_DataType *type, size_t customTypesSize, const UA_DataType *customTypes) UA_FUNC_ATTR_WARN_UNUSED_RESULT; size_t UA_calcSizeBinary(void *p, const UA_DataType *type); const UA_DataType * UA_findDataTypeByBinary(const UA_NodeId *typeId); # endif // UA_INTERNAL #else // UA_ENABLE_AMALGAMATION #endif _UA_BEGIN_DECLS extern UA_StatusCode namespace0_generated(UA_Server *server); _UA_END_DECLS #endif /* NAMESPACE0_GENERATED_H_ */ /*********************************** amalgamated original file "/home/jvoe/open62541/src/ua_types.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2014, 2016-2017 (c) Florian Palm * Copyright 2014-2016 (c) Sten Grüner * Copyright 2014 (c) Leon Urbas * Copyright 2015 (c) Chris Iatrou * Copyright 2015 (c) Markus Graube * Copyright 2015 (c) Reza Ebrahimi * Copyright 2015-2016 (c) Oleksiy Vasylyev * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2016 (c) Lorenz Haas */ /* Datatype Handling * ----------------- * This file contains handling functions for the builtin types and functions * handling of structured types and arrays. These need type descriptions in a * UA_DataType structure. The UA_DataType structures as well as all non-builtin * datatypes are autogenerated. */ /* Global definition of NULL type instances. These are always zeroed out, as * mandated by the C/C++ standard for global values with no initializer. */ const UA_String UA_STRING_NULL = {0, NULL}; const UA_ByteString UA_BYTESTRING_NULL = {0, NULL}; const UA_Guid UA_GUID_NULL = {0, 0, 0, {0,0,0,0,0,0,0,0}}; const UA_NodeId UA_NODEID_NULL = {0, UA_NODEIDTYPE_NUMERIC, {0}}; const UA_ExpandedNodeId UA_EXPANDEDNODEID_NULL = {{0, UA_NODEIDTYPE_NUMERIC, {0}}, {0, NULL}, 0}; typedef UA_StatusCode (*UA_copySignature)(const void *src, void *dst, const UA_DataType *type); typedef void (*UA_clearSignature)(void *p, const UA_DataType *type); extern const UA_copySignature copyJumpTable[UA_DATATYPEKINDS]; extern const UA_clearSignature clearJumpTable[UA_DATATYPEKINDS]; /* TODO: The standard-defined types are ordered. See if binary search is * more efficient. */ const UA_DataType * UA_findDataType(const UA_NodeId *typeId) { if(typeId->identifierType != UA_NODEIDTYPE_NUMERIC) return NULL; /* Always look in built-in types first * (may contain data types from all namespaces) */ for(size_t i = 0; i < UA_TYPES_COUNT; ++i) { if(UA_TYPES[i].typeId.identifier.numeric == typeId->identifier.numeric && UA_TYPES[i].typeId.namespaceIndex == typeId->namespaceIndex) return &UA_TYPES[i]; } /* TODO When other namespace look in custom types, too, requires access to custom types array here! */ /*if(typeId->namespaceIndex != 0) { size_t customTypesArraySize; const UA_DataType *customTypesArray; UA_getCustomTypes(&customTypesArraySize, &customTypesArray); for(size_t i = 0; i < customTypesArraySize; ++i) { if(customTypesArray[i].typeId.identifier.numeric == typeId->identifier.numeric && customTypesArray[i].typeId.namespaceIndex == typeId->namespaceIndex) return &customTypesArray[i]; } }*/ return NULL; } /***************************/ /* Random Number Generator */ /***************************/ //TODO is this safe for multithreading? static pcg32_random_t UA_rng = PCG32_INITIALIZER; void UA_random_seed(u64 seed) { pcg32_srandom_r(&UA_rng, seed, (u64)UA_DateTime_now()); } u32 UA_UInt32_random(void) { return (u32)pcg32_random_r(&UA_rng); } /*****************/ /* Builtin Types */ /*****************/ UA_String UA_String_fromChars(const char *src) { UA_String s; s.length = 0; s.data = NULL; if(!src) return s; s.length = strlen(src); if(s.length > 0) { s.data = (u8*)UA_malloc(s.length); if(!s.data) { s.length = 0; return s; } memcpy(s.data, src, s.length); } else { s.data = (u8*)UA_EMPTY_ARRAY_SENTINEL; } return s; } UA_Boolean UA_String_equal(const UA_String *s1, const UA_String *s2) { if(s1->length != s2->length) return false; if(s1->length == 0) return true; i32 is = memcmp((char const*)s1->data, (char const*)s2->data, s1->length); return (is == 0) ? true : false; } static UA_StatusCode String_copy(UA_String const *src, UA_String *dst, const UA_DataType *_) { UA_StatusCode retval = UA_Array_copy(src->data, src->length, (void**)&dst->data, &UA_TYPES[UA_TYPES_BYTE]); if(retval == UA_STATUSCODE_GOOD) dst->length = src->length; return retval; } static void String_clear(UA_String *s, const UA_DataType *_) { UA_Array_delete(s->data, s->length, &UA_TYPES[UA_TYPES_BYTE]); } /* QualifiedName */ static UA_StatusCode QualifiedName_copy(const UA_QualifiedName *src, UA_QualifiedName *dst, const UA_DataType *_) { dst->namespaceIndex = src->namespaceIndex; return String_copy(&src->name, &dst->name, NULL); } static void QualifiedName_clear(UA_QualifiedName *p, const UA_DataType *_) { String_clear(&p->name, NULL); } UA_Boolean UA_QualifiedName_equal(const UA_QualifiedName *qn1, const UA_QualifiedName *qn2) { if(qn1 == NULL || qn2 == NULL) return false; if(qn1->namespaceIndex != qn2->namespaceIndex) return false; if(qn1->name.length != qn2->name.length) return false; return (memcmp((char const*)qn1->name.data, (char const*)qn2->name.data, qn1->name.length) == 0); } /* DateTime */ UA_DateTimeStruct UA_DateTime_toStruct(UA_DateTime t) { /* Calculating the the milli-, micro- and nanoseconds */ UA_DateTimeStruct dateTimeStruct; if(t >= 0) { dateTimeStruct.nanoSec = (u16)((t % 10) * 100); dateTimeStruct.microSec = (u16)((t % 10000) / 10); dateTimeStruct.milliSec = (u16)((t % 10000000) / 10000); } else { dateTimeStruct.nanoSec = (u16)(((t % 10 + t) % 10) * 100); dateTimeStruct.microSec = (u16)(((t % 10000 + t) % 10000) / 10); dateTimeStruct.milliSec = (u16)(((t % 10000000 + t) % 10000000) / 10000); } /* Calculating the unix time with #include */ long long secSinceUnixEpoch = (long long)(t / UA_DATETIME_SEC) - (long long)(UA_DATETIME_UNIX_EPOCH / UA_DATETIME_SEC); struct mytm ts; memset(&ts, 0, sizeof(struct mytm)); __secs_to_tm(secSinceUnixEpoch, &ts); dateTimeStruct.sec = (u16)ts.tm_sec; dateTimeStruct.min = (u16)ts.tm_min; dateTimeStruct.hour = (u16)ts.tm_hour; dateTimeStruct.day = (u16)ts.tm_mday; dateTimeStruct.month = (u16)(ts.tm_mon + 1); dateTimeStruct.year = (u16)(ts.tm_year + 1900); return dateTimeStruct; } /* Guid */ UA_Boolean UA_Guid_equal(const UA_Guid *g1, const UA_Guid *g2) { if(memcmp(g1, g2, sizeof(UA_Guid)) == 0) return true; return false; } UA_Guid UA_Guid_random(void) { UA_Guid result; result.data1 = (u32)pcg32_random_r(&UA_rng); u32 r = (u32)pcg32_random_r(&UA_rng); result.data2 = (u16) r; result.data3 = (u16) (r >> 16); r = (u32)pcg32_random_r(&UA_rng); result.data4[0] = (u8)r; result.data4[1] = (u8)(r >> 4); result.data4[2] = (u8)(r >> 8); result.data4[3] = (u8)(r >> 12); r = (u32)pcg32_random_r(&UA_rng); result.data4[4] = (u8)r; result.data4[5] = (u8)(r >> 4); result.data4[6] = (u8)(r >> 8); result.data4[7] = (u8)(r >> 12); return result; } /* ByteString */ UA_StatusCode UA_ByteString_allocBuffer(UA_ByteString *bs, size_t length) { UA_ByteString_init(bs); if(length == 0) return UA_STATUSCODE_GOOD; bs->data = (u8*)UA_malloc(length); if(!bs->data) return UA_STATUSCODE_BADOUTOFMEMORY; bs->length = length; return UA_STATUSCODE_GOOD; } /* NodeId */ static void NodeId_clear(UA_NodeId *p, const UA_DataType *_) { switch(p->identifierType) { case UA_NODEIDTYPE_STRING: case UA_NODEIDTYPE_BYTESTRING: String_clear(&p->identifier.string, NULL); break; default: break; } } static UA_StatusCode NodeId_copy(UA_NodeId const *src, UA_NodeId *dst, const UA_DataType *_) { UA_StatusCode retval = UA_STATUSCODE_GOOD; switch(src->identifierType) { case UA_NODEIDTYPE_NUMERIC: *dst = *src; return UA_STATUSCODE_GOOD; case UA_NODEIDTYPE_STRING: retval |= UA_String_copy(&src->identifier.string, &dst->identifier.string); break; case UA_NODEIDTYPE_GUID: retval |= UA_Guid_copy(&src->identifier.guid, &dst->identifier.guid); break; case UA_NODEIDTYPE_BYTESTRING: retval |= UA_ByteString_copy(&src->identifier.byteString, &dst->identifier.byteString); break; default: return UA_STATUSCODE_BADINTERNALERROR; } dst->namespaceIndex = src->namespaceIndex; dst->identifierType = src->identifierType; return retval; } UA_Boolean UA_NodeId_isNull(const UA_NodeId *p) { if(p->namespaceIndex != 0) return false; switch (p->identifierType) { case UA_NODEIDTYPE_NUMERIC: return (p->identifier.numeric == 0); case UA_NODEIDTYPE_STRING: return UA_String_equal(&p->identifier.string, &UA_STRING_NULL); case UA_NODEIDTYPE_GUID: return UA_Guid_equal(&p->identifier.guid, &UA_GUID_NULL); case UA_NODEIDTYPE_BYTESTRING: return UA_ByteString_equal(&p->identifier.byteString, &UA_BYTESTRING_NULL); } return false; } /* Absolute ordering for NodeIds */ UA_Order UA_NodeId_order(const UA_NodeId *n1, const UA_NodeId *n2) { /* Compare namespaceIndex */ if(n1->namespaceIndex < n2->namespaceIndex) return UA_ORDER_LESS; if(n1->namespaceIndex > n2->namespaceIndex) return UA_ORDER_MORE; /* Compare identifierType */ if(n1->identifierType < n2->identifierType) return UA_ORDER_LESS; if(n1->identifierType > n2->identifierType) return UA_ORDER_MORE; /* Compare the identifier */ switch(n1->identifierType) { case UA_NODEIDTYPE_NUMERIC: if(n1->identifier.numeric < n2->identifier.numeric) return UA_ORDER_LESS; if(n1->identifier.numeric > n2->identifier.numeric) return UA_ORDER_MORE; break; case UA_NODEIDTYPE_GUID: if(n1->identifier.guid.data1 < n2->identifier.guid.data1) { return UA_ORDER_LESS; } else if(n1->identifier.guid.data1 > n2->identifier.guid.data1) { return UA_ORDER_MORE; } else if(n1->identifier.guid.data2 < n2->identifier.guid.data2) { return UA_ORDER_LESS; } else if(n1->identifier.guid.data2 > n2->identifier.guid.data2) { return UA_ORDER_MORE; } else if(n1->identifier.guid.data3 < n2->identifier.guid.data3) { return UA_ORDER_LESS; } else if(n1->identifier.guid.data3 > n2->identifier.guid.data3) { return UA_ORDER_MORE; } else { int cmp = memcmp(n1->identifier.guid.data4, n2->identifier.guid.data4, 8); if(cmp < 0) return UA_ORDER_LESS; if(cmp > 0) return UA_ORDER_MORE; } break; case UA_NODEIDTYPE_STRING: case UA_NODEIDTYPE_BYTESTRING: { size_t minLength = UA_MIN(n1->identifier.string.length, n2->identifier.string.length); int cmp = strncmp((const char*)n1->identifier.string.data, (const char*)n2->identifier.string.data, minLength); if(cmp < 0) return UA_ORDER_LESS; if(cmp > 0) return UA_ORDER_MORE; if(n1->identifier.string.length < n2->identifier.string.length) return UA_ORDER_LESS; if(n1->identifier.string.length > n2->identifier.string.length) return UA_ORDER_MORE; break; } default: break; } return UA_ORDER_EQ; } /* FNV non-cryptographic hash function. See * https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function */ #define FNV_PRIME_32 16777619 static u32 fnv32(u32 fnv, const u8 *buf, size_t size) { for(size_t i = 0; i < size; ++i) { fnv = fnv ^ (buf[i]); fnv = fnv * FNV_PRIME_32; } return fnv; } u32 UA_NodeId_hash(const UA_NodeId *n) { switch(n->identifierType) { case UA_NODEIDTYPE_NUMERIC: default: // shift knuth multiplication to use highest 32 bits and after addition make sure we don't have an integer overflow return (u32)((n->namespaceIndex + ((n->identifier.numeric * (u64)2654435761) >> (32))) & UINT32_C(4294967295)); /* Knuth's multiplicative hashing */ case UA_NODEIDTYPE_STRING: case UA_NODEIDTYPE_BYTESTRING: return fnv32(n->namespaceIndex, n->identifier.string.data, n->identifier.string.length); case UA_NODEIDTYPE_GUID: return fnv32(n->namespaceIndex, (const u8*)&n->identifier.guid, sizeof(UA_Guid)); } } /* ExpandedNodeId */ static void ExpandedNodeId_clear(UA_ExpandedNodeId *p, const UA_DataType *_) { NodeId_clear(&p->nodeId, _); String_clear(&p->namespaceUri, NULL); } static UA_StatusCode ExpandedNodeId_copy(UA_ExpandedNodeId const *src, UA_ExpandedNodeId *dst, const UA_DataType *_) { UA_StatusCode retval = NodeId_copy(&src->nodeId, &dst->nodeId, NULL); retval |= UA_String_copy(&src->namespaceUri, &dst->namespaceUri); dst->serverIndex = src->serverIndex; return retval; } UA_Order UA_ExpandedNodeId_order(const UA_ExpandedNodeId *n1, const UA_ExpandedNodeId *n2) { if(n1->serverIndex > n2->serverIndex) return UA_ORDER_MORE; if(n1->serverIndex < n2->serverIndex) return UA_ORDER_LESS; if(n1->namespaceUri.length > 0) { if(n1->namespaceUri.length > n2->namespaceUri.length) return UA_ORDER_MORE; if(n1->namespaceUri.length < n2->namespaceUri.length) return UA_ORDER_LESS; int cmp = strncmp((const char*)n1->namespaceUri.data, (const char*)n2->namespaceUri.data, n1->namespaceUri.length); if(cmp < 0) return UA_ORDER_LESS; if(cmp > 0) return UA_ORDER_MORE; } return UA_NodeId_order(&n1->nodeId, &n2->nodeId); } u32 UA_ExpandedNodeId_hash(const UA_ExpandedNodeId *n) { u32 h = UA_NodeId_hash(&n->nodeId); h = fnv32(h, (const UA_Byte*)&n->serverIndex, 4); return fnv32(h, n->namespaceUri.data, n->namespaceUri.length); } /* ExtensionObject */ static void ExtensionObject_clear(UA_ExtensionObject *p, const UA_DataType *_) { switch(p->encoding) { case UA_EXTENSIONOBJECT_ENCODED_NOBODY: case UA_EXTENSIONOBJECT_ENCODED_BYTESTRING: case UA_EXTENSIONOBJECT_ENCODED_XML: NodeId_clear(&p->content.encoded.typeId, NULL); String_clear(&p->content.encoded.body, NULL); break; case UA_EXTENSIONOBJECT_DECODED: if(p->content.decoded.data) UA_delete(p->content.decoded.data, p->content.decoded.type); break; default: break; } } static UA_StatusCode ExtensionObject_copy(UA_ExtensionObject const *src, UA_ExtensionObject *dst, const UA_DataType *_) { UA_StatusCode retval = UA_STATUSCODE_GOOD; switch(src->encoding) { case UA_EXTENSIONOBJECT_ENCODED_NOBODY: case UA_EXTENSIONOBJECT_ENCODED_BYTESTRING: case UA_EXTENSIONOBJECT_ENCODED_XML: dst->encoding = src->encoding; retval = NodeId_copy(&src->content.encoded.typeId, &dst->content.encoded.typeId, NULL); retval |= UA_ByteString_copy(&src->content.encoded.body, &dst->content.encoded.body); break; case UA_EXTENSIONOBJECT_DECODED: case UA_EXTENSIONOBJECT_DECODED_NODELETE: if(!src->content.decoded.type || !src->content.decoded.data) return UA_STATUSCODE_BADINTERNALERROR; dst->encoding = UA_EXTENSIONOBJECT_DECODED; dst->content.decoded.type = src->content.decoded.type; retval = UA_Array_copy(src->content.decoded.data, 1, &dst->content.decoded.data, src->content.decoded.type); break; default: break; } return retval; } /* Variant */ static void Variant_clear(UA_Variant *p, const UA_DataType *_) { if(p->storageType != UA_VARIANT_DATA) return; if(p->type && p->data > UA_EMPTY_ARRAY_SENTINEL) { if(p->arrayLength == 0) p->arrayLength = 1; UA_Array_delete(p->data, p->arrayLength, p->type); p->data = NULL; } if((void*)p->arrayDimensions > UA_EMPTY_ARRAY_SENTINEL) UA_free(p->arrayDimensions); } static UA_StatusCode Variant_copy(UA_Variant const *src, UA_Variant *dst, const UA_DataType *_) { size_t length = src->arrayLength; if(UA_Variant_isScalar(src)) length = 1; UA_StatusCode retval = UA_Array_copy(src->data, length, &dst->data, src->type); if(retval != UA_STATUSCODE_GOOD) return retval; dst->arrayLength = src->arrayLength; dst->type = src->type; if(src->arrayDimensions) { retval = UA_Array_copy(src->arrayDimensions, src->arrayDimensionsSize, (void**)&dst->arrayDimensions, &UA_TYPES[UA_TYPES_INT32]); if(retval != UA_STATUSCODE_GOOD) return retval; dst->arrayDimensionsSize = src->arrayDimensionsSize; } return UA_STATUSCODE_GOOD; } void UA_Variant_setScalar(UA_Variant *v, void * UA_RESTRICT p, const UA_DataType *type) { UA_Variant_init(v); v->type = type; v->arrayLength = 0; v->data = p; } UA_StatusCode UA_Variant_setScalarCopy(UA_Variant *v, const void *p, const UA_DataType *type) { void *n = UA_malloc(type->memSize); if(!n) return UA_STATUSCODE_BADOUTOFMEMORY; UA_StatusCode retval = UA_copy(p, n, type); if(retval != UA_STATUSCODE_GOOD) { UA_free(n); //cppcheck-suppress memleak return retval; } UA_Variant_setScalar(v, n, type); //cppcheck-suppress memleak return UA_STATUSCODE_GOOD; } void UA_Variant_setArray(UA_Variant *v, void * UA_RESTRICT array, size_t arraySize, const UA_DataType *type) { UA_Variant_init(v); v->data = array; v->arrayLength = arraySize; v->type = type; } UA_StatusCode UA_Variant_setArrayCopy(UA_Variant *v, const void *array, size_t arraySize, const UA_DataType *type) { UA_Variant_init(v); UA_StatusCode retval = UA_Array_copy(array, arraySize, &v->data, type); if(retval != UA_STATUSCODE_GOOD) return retval; v->arrayLength = arraySize; v->type = type; return UA_STATUSCODE_GOOD; } /* Test if a range is compatible with a variant. If yes, the following values * are set: * - total: how many elements are in the range * - block: how big is each contiguous block of elements in the variant that * maps into the range * - stride: how many elements are between the blocks (beginning to beginning) * - first: where does the first block begin */ static UA_StatusCode computeStrides(const UA_Variant *v, const UA_NumericRange range, size_t *total, size_t *block, size_t *stride, size_t *first) { /* Test for max array size (64bit only) */ #if (SIZE_MAX > 0xffffffff) if(v->arrayLength > UA_UINT32_MAX) return UA_STATUSCODE_BADINTERNALERROR; #endif /* Test the integrity of the source variant dimensions, make dimensions * vector of one dimension if none defined */ u32 arrayLength = (u32)v->arrayLength; const u32 *dims = &arrayLength; size_t dims_count = 1; if(v->arrayDimensionsSize > 0) { size_t elements = 1; dims_count = v->arrayDimensionsSize; dims = (u32*)v->arrayDimensions; for(size_t i = 0; i < dims_count; ++i) elements *= dims[i]; if(elements != v->arrayLength) return UA_STATUSCODE_BADINTERNALERROR; } UA_assert(dims_count > 0); /* Test the integrity of the range and compute the max index used for every * dimension. The standard says in Part 4, Section 7.22: * * When reading a value, the indexes may not specify a range that is within * the bounds of the array. The Server shall return a partial result if some * elements exist within the range. */ size_t count = 1; UA_STACKARRAY(UA_UInt32, realmax, dims_count); if(range.dimensionsSize != dims_count) return UA_STATUSCODE_BADINDEXRANGENODATA; for(size_t i = 0; i < dims_count; ++i) { if(range.dimensions[i].min > range.dimensions[i].max) return UA_STATUSCODE_BADINDEXRANGEINVALID; if(range.dimensions[i].min >= dims[i]) return UA_STATUSCODE_BADINDEXRANGENODATA; if(range.dimensions[i].max < dims[i]) realmax[i] = range.dimensions[i].max; else realmax[i] = dims[i] - 1; count *= (realmax[i] - range.dimensions[i].min) + 1; } *total = count; /* Compute the stride length and the position of the first element */ *block = count; /* Assume the range describes the entire array. */ *stride = v->arrayLength; /* So it can be copied as a contiguous block. */ *first = 0; size_t running_dimssize = 1; UA_Boolean found_contiguous = false; for(size_t k = dims_count; k > 0;) { --k; size_t dimrange = 1 + realmax[k] - range.dimensions[k].min; if(!found_contiguous && dimrange != dims[k]) { /* Found the maximum block that can be copied contiguously */ found_contiguous = true; *block = running_dimssize * dimrange; *stride = running_dimssize * dims[k]; } *first += running_dimssize * range.dimensions[k].min; running_dimssize *= dims[k]; } return UA_STATUSCODE_GOOD; } /* Is the type string-like? */ static UA_Boolean isStringLike(const UA_DataType *type) { if(type == &UA_TYPES[UA_TYPES_STRING] || type == &UA_TYPES[UA_TYPES_BYTESTRING] || type == &UA_TYPES[UA_TYPES_XMLELEMENT]) return true; return false; } /* Returns the part of the string that lies within the rangedimension */ static UA_StatusCode copySubString(const UA_String *src, UA_String *dst, const UA_NumericRangeDimension *dim) { if(dim->min > dim->max) return UA_STATUSCODE_BADINDEXRANGEINVALID; if(dim->min >= src->length) return UA_STATUSCODE_BADINDEXRANGENODATA; size_t length; if(dim->max < src->length) length = dim->max - dim->min + 1; else length = src->length - dim->min; UA_StatusCode retval = UA_ByteString_allocBuffer(dst, length); if(retval != UA_STATUSCODE_GOOD) return retval; memcpy(dst->data, &src->data[dim->min], length); return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst, const UA_NumericRange range) { if(!src->type) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_Boolean isScalar = UA_Variant_isScalar(src); UA_Boolean stringLike = isStringLike(src->type); UA_Variant arraySrc; /* Extract the range for copying at this level. The remaining range is dealt * with in the "scalar" type that may define an array by itself (string, * variant, ...). */ UA_NumericRange thisrange, nextrange; UA_NumericRangeDimension scalarThisDimension = {0,0}; /* a single entry */ if(isScalar) { /* Replace scalar src with array of length 1 */ arraySrc = *src; arraySrc.arrayLength = 1; src = &arraySrc; /* Deal with all range dimensions within the scalar */ thisrange.dimensions = &scalarThisDimension; thisrange.dimensionsSize = 1; nextrange = range; } else { /* Deal with as many range dimensions as possible right now */ size_t dims = src->arrayDimensionsSize; if(dims == 0) dims = 1; if(dims > range.dimensionsSize) return UA_STATUSCODE_BADINDEXRANGEINVALID; thisrange = range; thisrange.dimensionsSize = dims; nextrange.dimensions = &range.dimensions[dims]; nextrange.dimensionsSize = range.dimensionsSize - dims; } /* Compute the strides */ size_t count, block, stride, first; UA_StatusCode retval = computeStrides(src, thisrange, &count, &block, &stride, &first); if(retval != UA_STATUSCODE_GOOD) return retval; /* Allocate the array */ UA_Variant_init(dst); dst->data = UA_Array_new(count, src->type); if(!dst->data) return UA_STATUSCODE_BADOUTOFMEMORY; /* Copy the range */ size_t block_count = count / block; size_t elem_size = src->type->memSize; uintptr_t nextdst = (uintptr_t)dst->data; uintptr_t nextsrc = (uintptr_t)src->data + (elem_size * first); if(nextrange.dimensionsSize == 0) { /* no nextrange */ if(src->type->pointerFree) { for(size_t i = 0; i < block_count; ++i) { memcpy((void*)nextdst, (void*)nextsrc, elem_size * block); nextdst += block * elem_size; nextsrc += stride * elem_size; } } else { for(size_t i = 0; i < block_count; ++i) { for(size_t j = 0; j < block; ++j) { retval = UA_copy((const void*)nextsrc, (void*)nextdst, src->type); nextdst += elem_size; nextsrc += elem_size; } nextsrc += (stride - block) * elem_size; } } } else { /* nextrange can only be used for variants and stringlike with remaining * range of dimension 1 */ if(src->type != &UA_TYPES[UA_TYPES_VARIANT]) { if(!stringLike) retval = UA_STATUSCODE_BADINDEXRANGENODATA; if(nextrange.dimensionsSize != 1) retval = UA_STATUSCODE_BADINDEXRANGENODATA; } /* Copy the content */ for(size_t i = 0; i < block_count; ++i) { for(size_t j = 0; j < block && retval == UA_STATUSCODE_GOOD; ++j) { if(stringLike) retval = copySubString((const UA_String*)nextsrc, (UA_String*)nextdst, nextrange.dimensions); else retval = UA_Variant_copyRange((const UA_Variant*)nextsrc, (UA_Variant*)nextdst, nextrange); nextdst += elem_size; nextsrc += elem_size; } nextsrc += (stride - block) * elem_size; } } /* Clean up if copying failed */ if(retval != UA_STATUSCODE_GOOD) { UA_Array_delete(dst->data, count, src->type); dst->data = NULL; return retval; } /* Done if scalar */ dst->type = src->type; if(isScalar) return retval; /* Copy array dimensions */ dst->arrayLength = count; if(src->arrayDimensionsSize > 0) { dst->arrayDimensions = (u32*)UA_Array_new(thisrange.dimensionsSize, &UA_TYPES[UA_TYPES_UINT32]); if(!dst->arrayDimensions) { Variant_clear(dst, NULL); return UA_STATUSCODE_BADOUTOFMEMORY; } dst->arrayDimensionsSize = thisrange.dimensionsSize; for(size_t k = 0; k < thisrange.dimensionsSize; ++k) dst->arrayDimensions[k] = thisrange.dimensions[k].max - thisrange.dimensions[k].min + 1; } return UA_STATUSCODE_GOOD; } /* TODO: Allow ranges to reach inside a scalars that are array-like, e.g. * variant and strings. This is already possible for reading... */ static UA_StatusCode Variant_setRange(UA_Variant *v, void *array, size_t arraySize, const UA_NumericRange range, UA_Boolean copy) { /* Compute the strides */ size_t count, block, stride, first; UA_StatusCode retval = computeStrides(v, range, &count, &block, &stride, &first); if(retval != UA_STATUSCODE_GOOD) return retval; if(count != arraySize) return UA_STATUSCODE_BADINDEXRANGEINVALID; /* Move/copy the elements */ size_t block_count = count / block; size_t elem_size = v->type->memSize; uintptr_t nextdst = (uintptr_t)v->data + (first * elem_size); uintptr_t nextsrc = (uintptr_t)array; if(v->type->pointerFree || !copy) { for(size_t i = 0; i < block_count; ++i) { memcpy((void*)nextdst, (void*)nextsrc, elem_size * block); nextsrc += block * elem_size; nextdst += stride * elem_size; } } else { for(size_t i = 0; i < block_count; ++i) { for(size_t j = 0; j < block; ++j) { clearJumpTable[v->type->typeKind]((void*)nextdst, v->type); retval |= UA_copy((void*)nextsrc, (void*)nextdst, v->type); nextdst += elem_size; nextsrc += elem_size; } nextdst += (stride - block) * elem_size; } } /* If members were moved, initialize original array to prevent reuse */ if(!copy && !v->type->pointerFree) memset(array, 0, sizeof(elem_size)*arraySize); return retval; } UA_StatusCode UA_Variant_setRange(UA_Variant *v, void * UA_RESTRICT array, size_t arraySize, const UA_NumericRange range) { return Variant_setRange(v, array, arraySize, range, false); } UA_StatusCode UA_Variant_setRangeCopy(UA_Variant *v, const void *array, size_t arraySize, const UA_NumericRange range) { return Variant_setRange(v, (void*)(uintptr_t)array, arraySize, range, true); } /* LocalizedText */ static void LocalizedText_clear(UA_LocalizedText *p, const UA_DataType *_) { String_clear(&p->locale, NULL); String_clear(&p->text, NULL); } static UA_StatusCode LocalizedText_copy(UA_LocalizedText const *src, UA_LocalizedText *dst, const UA_DataType *_) { UA_StatusCode retval = UA_String_copy(&src->locale, &dst->locale); retval |= UA_String_copy(&src->text, &dst->text); return retval; } /* DataValue */ static void DataValue_clear(UA_DataValue *p, const UA_DataType *_) { Variant_clear(&p->value, NULL); } static UA_StatusCode DataValue_copy(UA_DataValue const *src, UA_DataValue *dst, const UA_DataType *_) { memcpy(dst, src, sizeof(UA_DataValue)); UA_Variant_init(&dst->value); UA_StatusCode retval = Variant_copy(&src->value, &dst->value, NULL); if(retval != UA_STATUSCODE_GOOD) DataValue_clear(dst, NULL); return retval; } /* DiagnosticInfo */ static void DiagnosticInfo_clear(UA_DiagnosticInfo *p, const UA_DataType *_) { String_clear(&p->additionalInfo, NULL); if(p->hasInnerDiagnosticInfo && p->innerDiagnosticInfo) { DiagnosticInfo_clear(p->innerDiagnosticInfo, NULL); UA_free(p->innerDiagnosticInfo); } } static UA_StatusCode DiagnosticInfo_copy(UA_DiagnosticInfo const *src, UA_DiagnosticInfo *dst, const UA_DataType *_) { memcpy(dst, src, sizeof(UA_DiagnosticInfo)); UA_String_init(&dst->additionalInfo); dst->innerDiagnosticInfo = NULL; UA_StatusCode retval = UA_STATUSCODE_GOOD; if(src->hasAdditionalInfo) retval = UA_String_copy(&src->additionalInfo, &dst->additionalInfo); if(src->hasInnerDiagnosticInfo && src->innerDiagnosticInfo) { dst->innerDiagnosticInfo = (UA_DiagnosticInfo*)UA_malloc(sizeof(UA_DiagnosticInfo)); if(dst->innerDiagnosticInfo) { retval |= DiagnosticInfo_copy(src->innerDiagnosticInfo, dst->innerDiagnosticInfo, NULL); dst->hasInnerDiagnosticInfo = true; } else { dst->hasInnerDiagnosticInfo = false; retval |= UA_STATUSCODE_BADOUTOFMEMORY; } } return retval; } /********************/ /* Structured Types */ /********************/ void * UA_new(const UA_DataType *type) { void *p = UA_calloc(1, type->memSize); return p; } static UA_StatusCode copyByte(const u8 *src, u8 *dst, const UA_DataType *_) { *dst = *src; return UA_STATUSCODE_GOOD; } static UA_StatusCode copy2Byte(const u16 *src, u16 *dst, const UA_DataType *_) { *dst = *src; return UA_STATUSCODE_GOOD; } static UA_StatusCode copy4Byte(const u32 *src, u32 *dst, const UA_DataType *_) { *dst = *src; return UA_STATUSCODE_GOOD; } static UA_StatusCode copy8Byte(const u64 *src, u64 *dst, const UA_DataType *_) { *dst = *src; return UA_STATUSCODE_GOOD; } static UA_StatusCode copyGuid(const UA_Guid *src, UA_Guid *dst, const UA_DataType *_) { *dst = *src; return UA_STATUSCODE_GOOD; } static UA_StatusCode copyStructure(const void *src, void *dst, const UA_DataType *type) { UA_StatusCode retval = UA_STATUSCODE_GOOD; uintptr_t ptrs = (uintptr_t)src; uintptr_t ptrd = (uintptr_t)dst; const UA_DataType *typelists[2] = { UA_TYPES, &type[-type->typeIndex] }; for(size_t i = 0; i < type->membersSize; ++i) { const UA_DataTypeMember *m= &type->members[i]; const UA_DataType *mt = &typelists[!m->namespaceZero][m->memberTypeIndex]; if(!m->isArray) { ptrs += m->padding; ptrd += m->padding; retval |= copyJumpTable[mt->typeKind]((const void*)ptrs, (void*)ptrd, mt); ptrs += mt->memSize; ptrd += mt->memSize; } else { ptrs += m->padding; ptrd += m->padding; size_t *dst_size = (size_t*)ptrd; const size_t size = *((const size_t*)ptrs); ptrs += sizeof(size_t); ptrd += sizeof(size_t); retval |= UA_Array_copy(*(void* const*)ptrs, size, (void**)ptrd, mt); if(retval == UA_STATUSCODE_GOOD) *dst_size = size; else *dst_size = 0; ptrs += sizeof(void*); ptrd += sizeof(void*); } } return retval; } static UA_StatusCode copyNotImplemented(const void *src, void *dst, const UA_DataType *type) { return UA_STATUSCODE_BADNOTIMPLEMENTED; } const UA_copySignature copyJumpTable[UA_DATATYPEKINDS] = { (UA_copySignature)copyByte, /* Boolean */ (UA_copySignature)copyByte, /* SByte */ (UA_copySignature)copyByte, /* Byte */ (UA_copySignature)copy2Byte, /* Int16 */ (UA_copySignature)copy2Byte, /* UInt16 */ (UA_copySignature)copy4Byte, /* Int32 */ (UA_copySignature)copy4Byte, /* UInt32 */ (UA_copySignature)copy8Byte, /* Int64 */ (UA_copySignature)copy8Byte, /* UInt64 */ (UA_copySignature)copy4Byte, /* Float */ (UA_copySignature)copy8Byte, /* Double */ (UA_copySignature)String_copy, (UA_copySignature)copy8Byte, /* DateTime */ (UA_copySignature)copyGuid, /* Guid */ (UA_copySignature)String_copy, /* ByteString */ (UA_copySignature)String_copy, /* XmlElement */ (UA_copySignature)NodeId_copy, (UA_copySignature)ExpandedNodeId_copy, (UA_copySignature)copy4Byte, /* StatusCode */ (UA_copySignature)QualifiedName_copy, (UA_copySignature)LocalizedText_copy, (UA_copySignature)ExtensionObject_copy, (UA_copySignature)DataValue_copy, (UA_copySignature)Variant_copy, (UA_copySignature)DiagnosticInfo_copy, (UA_copySignature)copyNotImplemented, /* Decimal */ (UA_copySignature)copy4Byte, /* Enumeration */ (UA_copySignature)copyStructure, (UA_copySignature)copyNotImplemented, /* Structure with Optional Fields */ (UA_copySignature)copyNotImplemented, /* Union */ (UA_copySignature)copyNotImplemented /* BitfieldCluster*/ }; UA_StatusCode UA_copy(const void *src, void *dst, const UA_DataType *type) { memset(dst, 0, type->memSize); /* init */ UA_StatusCode retval = copyJumpTable[type->typeKind](src, dst, type); if(retval != UA_STATUSCODE_GOOD) UA_clear(dst, type); return retval; } static void clearStructure(void *p, const UA_DataType *type) { uintptr_t ptr = (uintptr_t)p; const UA_DataType *typelists[2] = { UA_TYPES, &type[-type->typeIndex] }; for(size_t i = 0; i < type->membersSize; ++i) { const UA_DataTypeMember *m = &type->members[i]; const UA_DataType *mt = &typelists[!m->namespaceZero][m->memberTypeIndex]; if(!m->isArray) { ptr += m->padding; clearJumpTable[mt->typeKind]((void*)ptr, mt); ptr += mt->memSize; } else { ptr += m->padding; size_t length = *(size_t*)ptr; ptr += sizeof(size_t); UA_Array_delete(*(void**)ptr, length, mt); ptr += sizeof(void*); } } } static void nopClear(void *p, const UA_DataType *type) { } const UA_clearSignature clearJumpTable[UA_DATATYPEKINDS] = { (UA_clearSignature)nopClear, /* Boolean */ (UA_clearSignature)nopClear, /* SByte */ (UA_clearSignature)nopClear, /* Byte */ (UA_clearSignature)nopClear, /* Int16 */ (UA_clearSignature)nopClear, /* UInt16 */ (UA_clearSignature)nopClear, /* Int32 */ (UA_clearSignature)nopClear, /* UInt32 */ (UA_clearSignature)nopClear, /* Int64 */ (UA_clearSignature)nopClear, /* UInt64 */ (UA_clearSignature)nopClear, /* Float */ (UA_clearSignature)nopClear, /* Double */ (UA_clearSignature)String_clear, /* String */ (UA_clearSignature)nopClear, /* DateTime */ (UA_clearSignature)nopClear, /* Guid */ (UA_clearSignature)String_clear, /* ByteString */ (UA_clearSignature)String_clear, /* XmlElement */ (UA_clearSignature)NodeId_clear, (UA_clearSignature)ExpandedNodeId_clear, (UA_clearSignature)nopClear, /* StatusCode */ (UA_clearSignature)QualifiedName_clear, (UA_clearSignature)LocalizedText_clear, (UA_clearSignature)ExtensionObject_clear, (UA_clearSignature)DataValue_clear, (UA_clearSignature)Variant_clear, (UA_clearSignature)DiagnosticInfo_clear, (UA_clearSignature)nopClear, /* Decimal, not implemented */ (UA_clearSignature)nopClear, /* Enumeration */ (UA_clearSignature)clearStructure, (UA_clearSignature)nopClear, /* Struct with Optional Fields, not implemented*/ (UA_clearSignature)nopClear, /* Union, not implemented*/ (UA_clearSignature)nopClear /* BitfieldCluster, not implemented*/ }; void UA_clear(void *p, const UA_DataType *type) { clearJumpTable[type->typeKind](p, type); memset(p, 0, type->memSize); /* init */ } void UA_delete(void *p, const UA_DataType *type) { clearJumpTable[type->typeKind](p, type); UA_free(p); } /******************/ /* Array Handling */ /******************/ void * UA_Array_new(size_t size, const UA_DataType *type) { if(size > UA_INT32_MAX) return NULL; if(size == 0) return UA_EMPTY_ARRAY_SENTINEL; return UA_calloc(size, type->memSize); } UA_StatusCode UA_Array_copy(const void *src, size_t size, void **dst, const UA_DataType *type) { if(size == 0) { if(src == NULL) *dst = NULL; else *dst= UA_EMPTY_ARRAY_SENTINEL; return UA_STATUSCODE_GOOD; } if(!type) return UA_STATUSCODE_BADINTERNALERROR; /* calloc, so we don't have to check retval in every iteration of copying */ *dst = UA_calloc(size, type->memSize); if(!*dst) return UA_STATUSCODE_BADOUTOFMEMORY; if(type->pointerFree) { memcpy(*dst, src, type->memSize * size); return UA_STATUSCODE_GOOD; } uintptr_t ptrs = (uintptr_t)src; uintptr_t ptrd = (uintptr_t)*dst; UA_StatusCode retval = UA_STATUSCODE_GOOD; for(size_t i = 0; i < size; ++i) { retval |= UA_copy((void*)ptrs, (void*)ptrd, type); ptrs += type->memSize; ptrd += type->memSize; } if(retval != UA_STATUSCODE_GOOD) { UA_Array_delete(*dst, size, type); *dst = NULL; } return retval; } void UA_Array_delete(void *p, size_t size, const UA_DataType *type) { if(!type->pointerFree) { uintptr_t ptr = (uintptr_t)p; for(size_t i = 0; i < size; ++i) { UA_clear((void*)ptr, type); ptr += type->memSize; } } UA_free((void*)((uintptr_t)p & ~(uintptr_t)UA_EMPTY_ARRAY_SENTINEL)); } UA_Boolean UA_DataType_isNumeric(const UA_DataType *type) { /* All data types between UA_TYPES_BOOLEAN and UA_TYPES_DOUBLE are numeric */ for(size_t i = UA_TYPES_BOOLEAN; i <= UA_TYPES_DOUBLE; ++i) if(&UA_TYPES[i] == type) return true; return false; } /**********************/ /* Parse NumericRange */ /**********************/ static size_t readDimension(UA_Byte *buf, size_t buflen, UA_NumericRangeDimension *dim) { size_t progress = UA_readNumber(buf, buflen, &dim->min); if(progress == 0) return 0; if(buflen <= progress + 1 || buf[progress] != ':') { dim->max = dim->min; return progress; } ++progress; size_t progress2 = UA_readNumber(&buf[progress], buflen - progress, &dim->max); if(progress2 == 0) return 0; /* invalid range */ if(dim->min >= dim->max) return 0; return progress + progress2; } UA_StatusCode UA_NumericRange_parseFromString(UA_NumericRange *range, const UA_String *str) { size_t idx = 0; size_t dimensionsMax = 0; UA_NumericRangeDimension *dimensions = NULL; UA_StatusCode retval = UA_STATUSCODE_GOOD; size_t offset = 0; while(true) { /* alloc dimensions */ if(idx >= dimensionsMax) { UA_NumericRangeDimension *newds; size_t newdssize = sizeof(UA_NumericRangeDimension) * (dimensionsMax + 2); newds = (UA_NumericRangeDimension*)UA_realloc(dimensions, newdssize); if(!newds) { retval = UA_STATUSCODE_BADOUTOFMEMORY; break; } dimensions = newds; dimensionsMax = dimensionsMax + 2; } /* read the dimension */ size_t progress = readDimension(&str->data[offset], str->length - offset, &dimensions[idx]); if(progress == 0) { retval = UA_STATUSCODE_BADINDEXRANGEINVALID; break; } offset += progress; ++idx; /* loop into the next dimension */ if(offset >= str->length) break; if(str->data[offset] != ',') { retval = UA_STATUSCODE_BADINDEXRANGEINVALID; break; } ++offset; } if(retval == UA_STATUSCODE_GOOD && idx > 0) { range->dimensions = dimensions; range->dimensionsSize = idx; } else UA_free(dimensions); return retval; } /*********************************** amalgamated original file "/home/jvoe/open62541/src/ua_types_encoding_binary.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2014-2017 (c) Florian Palm * Copyright 2014-2016 (c) Sten Grüner * Copyright 2014 (c) Leon Urbas * Copyright 2015 (c) LEvertz * Copyright 2015 (c) Chris Iatrou * Copyright 2015-2016 (c) Oleksiy Vasylyev * Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2016 (c) Lorenz Haas * Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB * Copyright 2017 (c) Henrik Norrman */ /** * Type Encoding and Decoding * -------------------------- * The following methods contain encoding and decoding functions for the builtin * data types and generic functions that operate on all types and arrays. This * requires the type description from a UA_DataType structure. * * Encoding Context * ^^^^^^^^^^^^^^^^ * If possible, the encoding context is stored in a thread-local variable to * speed up encoding. If thread-local variables are not supported, the context * is "looped through" every method call. The ``_``-macro accesses either the * thread-local or the "looped through" context . */ /* Part 6 §5.1.5: Decoders shall support at least 100 nesting levels */ #define UA_ENCODING_MAX_RECURSION 100 typedef struct { /* Pointers to the current position and the last position in the buffer */ u8 *pos; const u8 *end; u8 **oldpos; /* Sentinel for a lower stacktrace exchanging the buffer */ u16 depth; /* How often did we en-/decoding recurse? */ const UA_DataTypeArray *customTypes; UA_exchangeEncodeBuffer exchangeBufferCallback; void *exchangeBufferCallbackHandle; } Ctx; typedef status (*encodeBinarySignature)(const void *UA_RESTRICT src, const UA_DataType *type, Ctx *UA_RESTRICT ctx); typedef status (*decodeBinarySignature)(void *UA_RESTRICT dst, const UA_DataType *type, Ctx *UA_RESTRICT ctx); typedef size_t (*calcSizeBinarySignature)(const void *UA_RESTRICT p, const UA_DataType *contenttype); #define ENCODE_BINARY(TYPE) static status \ TYPE##_encodeBinary(const UA_##TYPE *UA_RESTRICT src, \ const UA_DataType *type, Ctx *UA_RESTRICT ctx) #define DECODE_BINARY(TYPE) static status \ TYPE##_decodeBinary(UA_##TYPE *UA_RESTRICT dst, \ const UA_DataType *type, Ctx *UA_RESTRICT ctx) #define CALCSIZE_BINARY(TYPE) static size_t \ TYPE##_calcSizeBinary(const UA_##TYPE *UA_RESTRICT src, \ const UA_DataType *_) #define ENCODE_DIRECT(SRC, TYPE) TYPE##_encodeBinary((const UA_##TYPE*)SRC, NULL, ctx) #define DECODE_DIRECT(DST, TYPE) TYPE##_decodeBinary((UA_##TYPE*)DST, NULL, ctx) /* Jumptables for de-/encoding and computing the buffer length. The methods in * the decoding jumptable do not all clean up their allocated memory when an * error occurs. So a final _clear needs to be called before returning to the * user. */ extern const encodeBinarySignature encodeBinaryJumpTable[UA_DATATYPEKINDS]; extern const decodeBinarySignature decodeBinaryJumpTable[UA_DATATYPEKINDS]; extern const calcSizeBinarySignature calcSizeBinaryJumpTable[UA_DATATYPEKINDS]; /* Breaking a message up into chunks is integrated with the encoding. When the * end of a buffer is reached, a callback is executed that sends the current * buffer as a chunk and exchanges the encoding buffer "underneath" the ongoing * encoding. This reduces the RAM requirements and unnecessary copying. */ /* Send the current chunk and replace the buffer */ static status exchangeBuffer(Ctx *ctx) { if(!ctx->exchangeBufferCallback) return UA_STATUSCODE_BADENCODINGERROR; return ctx->exchangeBufferCallback(ctx->exchangeBufferCallbackHandle, &ctx->pos, &ctx->end); } /* If encoding fails, exchange the buffer and try again. */ static status encodeWithExchangeBuffer(const void *ptr, const UA_DataType *type, Ctx *ctx) { u8 *oldpos = ctx->pos; /* Last known good position */ ctx->oldpos = &oldpos; status ret = encodeBinaryJumpTable[type->typeKind](ptr, type, ctx); if(ret == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED && ctx->oldpos == &oldpos) { ctx->pos = oldpos; /* Send the position to the last known good position * and switch */ ret = exchangeBuffer(ctx); if(ret != UA_STATUSCODE_GOOD) return ret; ret = encodeBinaryJumpTable[type->typeKind](ptr, type, ctx); } return ret; } #define ENCODE_WITHEXCHANGE(VAR, TYPE) \ encodeWithExchangeBuffer((const void*)VAR, &UA_TYPES[TYPE], ctx) /*****************/ /* Integer Types */ /*****************/ #if !UA_BINARY_OVERLAYABLE_INTEGER #pragma message "Integer endianness could not be detected to be little endian. Use slow generic encoding." /* These en/decoding functions are only used when the architecture isn't little-endian. */ static void UA_encode16(const u16 v, u8 buf[2]) { buf[0] = (u8)v; buf[1] = (u8)(v >> 8); } static void UA_decode16(const u8 buf[2], u16 *v) { *v = (u16)((u16)buf[0] + (((u16)buf[1]) << 8)); } static void UA_encode32(const u32 v, u8 buf[4]) { buf[0] = (u8)v; buf[1] = (u8)(v >> 8); buf[2] = (u8)(v >> 16); buf[3] = (u8)(v >> 24); } static void UA_decode32(const u8 buf[4], u32 *v) { *v = (u32)((u32)buf[0] + (((u32)buf[1]) << 8) + (((u32)buf[2]) << 16) + (((u32)buf[3]) << 24)); } static void UA_encode64(const u64 v, u8 buf[8]) { buf[0] = (u8)v; buf[1] = (u8)(v >> 8); buf[2] = (u8)(v >> 16); buf[3] = (u8)(v >> 24); buf[4] = (u8)(v >> 32); buf[5] = (u8)(v >> 40); buf[6] = (u8)(v >> 48); buf[7] = (u8)(v >> 56); } static void UA_decode64(const u8 buf[8], u64 *v) { *v = (u64)((u64)buf[0] + (((u64)buf[1]) << 8) + (((u64)buf[2]) << 16) + (((u64)buf[3]) << 24) + (((u64)buf[4]) << 32) + (((u64)buf[5]) << 40) + (((u64)buf[6]) << 48) + (((u64)buf[7]) << 56)); } #endif /* !UA_BINARY_OVERLAYABLE_INTEGER */ /* Boolean */ /* Note that sizeof(bool) != 1 on some platforms. Overlayable integer encoding * is disabled in those cases. */ ENCODE_BINARY(Boolean) { if(ctx->pos + 1 > ctx->end) return UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED; *ctx->pos = *(const u8*)src; ++ctx->pos; return UA_STATUSCODE_GOOD; } DECODE_BINARY(Boolean) { if(ctx->pos + 1 > ctx->end) return UA_STATUSCODE_BADDECODINGERROR; *dst = (*ctx->pos > 0) ? true : false; ++ctx->pos; return UA_STATUSCODE_GOOD; } /* Byte */ ENCODE_BINARY(Byte) { if(ctx->pos + sizeof(u8) > ctx->end) return UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED; *ctx->pos = *(const u8*)src; ++ctx->pos; return UA_STATUSCODE_GOOD; } DECODE_BINARY(Byte) { if(ctx->pos + sizeof(u8) > ctx->end) return UA_STATUSCODE_BADDECODINGERROR; *dst = *ctx->pos; ++ctx->pos; return UA_STATUSCODE_GOOD; } /* UInt16 */ ENCODE_BINARY(UInt16) { if(ctx->pos + sizeof(u16) > ctx->end) return UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED; #if UA_BINARY_OVERLAYABLE_INTEGER memcpy(ctx->pos, src, sizeof(u16)); #else UA_encode16(*src, ctx->pos); #endif ctx->pos += 2; return UA_STATUSCODE_GOOD; } DECODE_BINARY(UInt16) { if(ctx->pos + sizeof(u16) > ctx->end) return UA_STATUSCODE_BADDECODINGERROR; #if UA_BINARY_OVERLAYABLE_INTEGER memcpy(dst, ctx->pos, sizeof(u16)); #else UA_decode16(ctx->pos, dst); #endif ctx->pos += 2; return UA_STATUSCODE_GOOD; } /* UInt32 */ ENCODE_BINARY(UInt32) { if(ctx->pos + sizeof(u32) > ctx->end) return UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED; #if UA_BINARY_OVERLAYABLE_INTEGER memcpy(ctx->pos, src, sizeof(u32)); #else UA_encode32(*src, ctx->pos); #endif ctx->pos += 4; return UA_STATUSCODE_GOOD; } DECODE_BINARY(UInt32) { if(ctx->pos + sizeof(u32) > ctx->end) return UA_STATUSCODE_BADDECODINGERROR; #if UA_BINARY_OVERLAYABLE_INTEGER memcpy(dst, ctx->pos, sizeof(u32)); #else UA_decode32(ctx->pos, dst); #endif ctx->pos += 4; return UA_STATUSCODE_GOOD; } /* UInt64 */ ENCODE_BINARY(UInt64) { if(ctx->pos + sizeof(u64) > ctx->end) return UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED; #if UA_BINARY_OVERLAYABLE_INTEGER memcpy(ctx->pos, src, sizeof(u64)); #else UA_encode64(*src, ctx->pos); #endif ctx->pos += 8; return UA_STATUSCODE_GOOD; } DECODE_BINARY(UInt64) { if(ctx->pos + sizeof(u64) > ctx->end) return UA_STATUSCODE_BADDECODINGERROR; #if UA_BINARY_OVERLAYABLE_INTEGER memcpy(dst, ctx->pos, sizeof(u64)); #else UA_decode64(ctx->pos, dst); #endif ctx->pos += 8; return UA_STATUSCODE_GOOD; } /************************/ /* Floating Point Types */ /************************/ /* Can we reuse the integer encoding mechanism by casting floating point * values? */ #if (UA_FLOAT_IEEE754 == 1) && (UA_LITTLE_ENDIAN == UA_FLOAT_LITTLE_ENDIAN) # define Float_encodeBinary UInt32_encodeBinary # define Float_decodeBinary UInt32_decodeBinary # define Double_encodeBinary UInt64_encodeBinary # define Double_decodeBinary UInt64_decodeBinary #else #include #pragma message "No native IEEE 754 format detected. Use slow generic encoding." /* Handling of IEEE754 floating point values was taken from Beej's Guide to * Network Programming (http://beej.us/guide/bgnet/) and enhanced to cover the * edge cases +/-0, +/-inf and nan. */ static uint64_t pack754(long double f, unsigned bits, unsigned expbits) { unsigned significandbits = bits - expbits - 1; long double fnorm; long long sign; if(f < 0) { sign = 1; fnorm = -f; } else { sign = 0; fnorm = f; } int shift = 0; while(fnorm >= 2.0) { fnorm /= 2.0; ++shift; } while(fnorm < 1.0) { fnorm *= 2.0; --shift; } fnorm = fnorm - 1.0; long long significand = (long long)(fnorm * ((float)(1LL<>significandbits) & (uint64_t)((1LL< 0) { result *= 2.0; --shift; } while(shift < 0) { result /= 2.0; ++shift; } result *= ((i>>(bits-1))&1)? -1.0: 1.0; return result; } /* Float */ #define FLOAT_NAN 0xffc00000 #define FLOAT_INF 0x7f800000 #define FLOAT_NEG_INF 0xff800000 #define FLOAT_NEG_ZERO 0x80000000 ENCODE_BINARY(Float) { UA_Float f = *src; u32 encoded; /* cppcheck-suppress duplicateExpression */ if(f != f) encoded = FLOAT_NAN; else if(f == 0.0f) encoded = signbit(f) ? FLOAT_NEG_ZERO : 0; else if(f/f != f/f) encoded = f > 0 ? FLOAT_INF : FLOAT_NEG_INF; else encoded = (u32)pack754(f, 32, 8); return ENCODE_DIRECT(&encoded, UInt32); } DECODE_BINARY(Float) { u32 decoded; status ret = DECODE_DIRECT(&decoded, UInt32); if(ret != UA_STATUSCODE_GOOD) return ret; if(decoded == 0) *dst = 0.0f; else if(decoded == FLOAT_NEG_ZERO) *dst = -0.0f; else if(decoded == FLOAT_INF) *dst = INFINITY; else if(decoded == FLOAT_NEG_INF) *dst = -INFINITY; else if((decoded >= 0x7f800001 && decoded <= 0x7fffffff) || (decoded >= 0xff800001)) *dst = NAN; else *dst = (UA_Float)unpack754(decoded, 32, 8); return UA_STATUSCODE_GOOD; } /* Double */ #define DOUBLE_NAN 0xfff8000000000000L #define DOUBLE_INF 0x7ff0000000000000L #define DOUBLE_NEG_INF 0xfff0000000000000L #define DOUBLE_NEG_ZERO 0x8000000000000000L ENCODE_BINARY(Double) { UA_Double d = *src; u64 encoded; /* cppcheck-suppress duplicateExpression */ if(d != d) encoded = DOUBLE_NAN; else if(d == 0.0) encoded = signbit(d) ? DOUBLE_NEG_ZERO : 0; else if(d/d != d/d) encoded = d > 0 ? DOUBLE_INF : DOUBLE_NEG_INF; else encoded = pack754(d, 64, 11); return ENCODE_DIRECT(&encoded, UInt64); } DECODE_BINARY(Double) { u64 decoded; status ret = DECODE_DIRECT(&decoded, UInt64); if(ret != UA_STATUSCODE_GOOD) return ret; if(decoded == 0) *dst = 0.0; else if(decoded == DOUBLE_NEG_ZERO) *dst = -0.0; else if(decoded == DOUBLE_INF) *dst = INFINITY; else if(decoded == DOUBLE_NEG_INF) *dst = -INFINITY; else if((decoded >= 0x7ff0000000000001L && decoded <= 0x7fffffffffffffffL) || (decoded >= 0xfff0000000000001L)) *dst = NAN; else *dst = (UA_Double)unpack754(decoded, 64, 11); return UA_STATUSCODE_GOOD; } #endif /******************/ /* Array Handling */ /******************/ static status Array_encodeBinaryOverlayable(uintptr_t ptr, size_t length, size_t elementMemSize, Ctx *ctx) { /* Store the number of already encoded elements */ size_t finished = 0; /* Loop as long as more elements remain than fit into the chunk */ while(ctx->end < ctx->pos + (elementMemSize * (length-finished))) { size_t possible = ((uintptr_t)ctx->end - (uintptr_t)ctx->pos) / (sizeof(u8) * elementMemSize); size_t possibleMem = possible * elementMemSize; memcpy(ctx->pos, (void*)ptr, possibleMem); ctx->pos += possibleMem; ptr += possibleMem; finished += possible; status ret = exchangeBuffer(ctx); ctx->oldpos = NULL; /* Set the sentinel so that no upper stack frame * with a saved pos attempts to exchange from an * invalid position in the old buffer. */ if(ret != UA_STATUSCODE_GOOD) return ret; } /* Encode the remaining elements */ memcpy(ctx->pos, (void*)ptr, elementMemSize * (length-finished)); ctx->pos += elementMemSize * (length-finished); return UA_STATUSCODE_GOOD; } static status Array_encodeBinaryComplex(uintptr_t ptr, size_t length, const UA_DataType *type, Ctx *ctx) { /* Encode every element */ for(size_t i = 0; i < length; ++i) { status ret = encodeWithExchangeBuffer((const void*)ptr, type, ctx); ptr += type->memSize; if(ret != UA_STATUSCODE_GOOD) return ret; /* Unrecoverable fail */ } return UA_STATUSCODE_GOOD; } static status Array_encodeBinary(const void *src, size_t length, const UA_DataType *type, Ctx *ctx) { /* Check and convert the array length to int32 */ i32 signed_length = -1; if(length > UA_INT32_MAX) return UA_STATUSCODE_BADINTERNALERROR; if(length > 0) signed_length = (i32)length; else if(src == UA_EMPTY_ARRAY_SENTINEL) signed_length = 0; /* Encode the array length */ status ret = ENCODE_WITHEXCHANGE(&signed_length, UA_TYPES_INT32); /* Quit early? */ if(ret != UA_STATUSCODE_GOOD || length == 0) return ret; /* Encode the content */ if(!type->overlayable) return Array_encodeBinaryComplex((uintptr_t)src, length, type, ctx); return Array_encodeBinaryOverlayable((uintptr_t)src, length, type->memSize, ctx); } static status Array_decodeBinary(void *UA_RESTRICT *UA_RESTRICT dst, size_t *out_length, const UA_DataType *type, Ctx *ctx) { /* Decode the length */ i32 signed_length; status ret = DECODE_DIRECT(&signed_length, UInt32); /* Int32 */ if(ret != UA_STATUSCODE_GOOD) return ret; /* Return early for empty arrays */ if(signed_length <= 0) { *out_length = 0; if(signed_length < 0) *dst = NULL; else *dst = UA_EMPTY_ARRAY_SENTINEL; return UA_STATUSCODE_GOOD; } /* Filter out arrays that can obviously not be decoded, because the message * is too small for the array length. This prevents the allocation of very * long arrays for bogus messages.*/ size_t length = (size_t)signed_length; if(ctx->pos + ((type->memSize * length) / 32) > ctx->end) return UA_STATUSCODE_BADDECODINGERROR; /* Allocate memory */ *dst = UA_calloc(length, type->memSize); if(!*dst) return UA_STATUSCODE_BADOUTOFMEMORY; if(type->overlayable) { /* memcpy overlayable array */ if(ctx->end < ctx->pos + (type->memSize * length)) { UA_free(*dst); *dst = NULL; return UA_STATUSCODE_BADDECODINGERROR; } memcpy(*dst, ctx->pos, type->memSize * length); ctx->pos += type->memSize * length; } else { /* Decode array members */ uintptr_t ptr = (uintptr_t)*dst; for(size_t i = 0; i < length; ++i) { ret = decodeBinaryJumpTable[type->typeKind]((void*)ptr, type, ctx); if(ret != UA_STATUSCODE_GOOD) { /* +1 because last element is also already initialized */ UA_Array_delete(*dst, i+1, type); *dst = NULL; return ret; } ptr += type->memSize; } } *out_length = length; return UA_STATUSCODE_GOOD; } /*****************/ /* Builtin Types */ /*****************/ ENCODE_BINARY(String) { return Array_encodeBinary(src->data, src->length, &UA_TYPES[UA_TYPES_BYTE], ctx); } DECODE_BINARY(String) { return Array_decodeBinary((void**)&dst->data, &dst->length, &UA_TYPES[UA_TYPES_BYTE], ctx); } /* Guid */ ENCODE_BINARY(Guid) { status ret = UA_STATUSCODE_GOOD; ret |= ENCODE_DIRECT(&src->data1, UInt32); ret |= ENCODE_DIRECT(&src->data2, UInt16); ret |= ENCODE_DIRECT(&src->data3, UInt16); if(ctx->pos + (8*sizeof(u8)) > ctx->end) return UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED; memcpy(ctx->pos, src->data4, 8*sizeof(u8)); ctx->pos += 8; return ret; } DECODE_BINARY(Guid) { status ret = UA_STATUSCODE_GOOD; ret |= DECODE_DIRECT(&dst->data1, UInt32); ret |= DECODE_DIRECT(&dst->data2, UInt16); ret |= DECODE_DIRECT(&dst->data3, UInt16); if(ctx->pos + (8*sizeof(u8)) > ctx->end) return UA_STATUSCODE_BADDECODINGERROR; memcpy(dst->data4, ctx->pos, 8*sizeof(u8)); ctx->pos += 8; return ret; } /* NodeId */ #define UA_NODEIDTYPE_NUMERIC_TWOBYTE 0u #define UA_NODEIDTYPE_NUMERIC_FOURBYTE 1u #define UA_NODEIDTYPE_NUMERIC_COMPLETE 2u #define UA_EXPANDEDNODEID_SERVERINDEX_FLAG 0x40u #define UA_EXPANDEDNODEID_NAMESPACEURI_FLAG 0x80u /* For ExpandedNodeId, we prefill the encoding mask. */ static status NodeId_encodeBinaryWithEncodingMask(UA_NodeId const *src, u8 encoding, Ctx *ctx) { status ret = UA_STATUSCODE_GOOD; switch(src->identifierType) { case UA_NODEIDTYPE_NUMERIC: if(src->identifier.numeric > UA_UINT16_MAX || src->namespaceIndex > UA_BYTE_MAX) { encoding |= UA_NODEIDTYPE_NUMERIC_COMPLETE; ret |= ENCODE_DIRECT(&encoding, Byte); ret |= ENCODE_DIRECT(&src->namespaceIndex, UInt16); ret |= ENCODE_DIRECT(&src->identifier.numeric, UInt32); } else if(src->identifier.numeric > UA_BYTE_MAX || src->namespaceIndex > 0) { encoding |= UA_NODEIDTYPE_NUMERIC_FOURBYTE; ret |= ENCODE_DIRECT(&encoding, Byte); u8 nsindex = (u8)src->namespaceIndex; ret |= ENCODE_DIRECT(&nsindex, Byte); u16 identifier16 = (u16)src->identifier.numeric; ret |= ENCODE_DIRECT(&identifier16, UInt16); } else { encoding |= UA_NODEIDTYPE_NUMERIC_TWOBYTE; ret |= ENCODE_DIRECT(&encoding, Byte); u8 identifier8 = (u8)src->identifier.numeric; ret |= ENCODE_DIRECT(&identifier8, Byte); } break; case UA_NODEIDTYPE_STRING:encoding |= (u8)UA_NODEIDTYPE_STRING; ret |= ENCODE_DIRECT(&encoding, Byte); ret |= ENCODE_DIRECT(&src->namespaceIndex, UInt16); if(ret != UA_STATUSCODE_GOOD) return ret; ret = ENCODE_DIRECT(&src->identifier.string, String); break; case UA_NODEIDTYPE_GUID:encoding |= (u8)UA_NODEIDTYPE_GUID; ret |= ENCODE_DIRECT(&encoding, Byte); ret |= ENCODE_DIRECT(&src->namespaceIndex, UInt16); ret |= ENCODE_DIRECT(&src->identifier.guid, Guid); break; case UA_NODEIDTYPE_BYTESTRING:encoding |= (u8)UA_NODEIDTYPE_BYTESTRING; ret |= ENCODE_DIRECT(&encoding, Byte); ret |= ENCODE_DIRECT(&src->namespaceIndex, UInt16); if(ret != UA_STATUSCODE_GOOD) return ret; ret = ENCODE_DIRECT(&src->identifier.byteString, String); /* ByteString */ break; default: return UA_STATUSCODE_BADINTERNALERROR; } return ret; } ENCODE_BINARY(NodeId) { return NodeId_encodeBinaryWithEncodingMask(src, 0, ctx); } DECODE_BINARY(NodeId) { u8 dstByte = 0, encodingByte = 0; u16 dstUInt16 = 0; /* Decode the encoding bitfield */ status ret = DECODE_DIRECT(&encodingByte, Byte); if(ret != UA_STATUSCODE_GOOD) return ret; /* Filter out the bits used only for ExpandedNodeIds */ encodingByte &= (u8)~(u8)(UA_EXPANDEDNODEID_SERVERINDEX_FLAG | UA_EXPANDEDNODEID_NAMESPACEURI_FLAG); /* Decode the namespace and identifier */ switch(encodingByte) { case UA_NODEIDTYPE_NUMERIC_TWOBYTE: dst->identifierType = UA_NODEIDTYPE_NUMERIC; ret = DECODE_DIRECT(&dstByte, Byte); dst->identifier.numeric = dstByte; dst->namespaceIndex = 0; break; case UA_NODEIDTYPE_NUMERIC_FOURBYTE: dst->identifierType = UA_NODEIDTYPE_NUMERIC; ret |= DECODE_DIRECT(&dstByte, Byte); dst->namespaceIndex = dstByte; ret |= DECODE_DIRECT(&dstUInt16, UInt16); dst->identifier.numeric = dstUInt16; break; case UA_NODEIDTYPE_NUMERIC_COMPLETE: dst->identifierType = UA_NODEIDTYPE_NUMERIC; ret |= DECODE_DIRECT(&dst->namespaceIndex, UInt16); ret |= DECODE_DIRECT(&dst->identifier.numeric, UInt32); break; case UA_NODEIDTYPE_STRING: dst->identifierType = UA_NODEIDTYPE_STRING; ret |= DECODE_DIRECT(&dst->namespaceIndex, UInt16); ret |= DECODE_DIRECT(&dst->identifier.string, String); break; case UA_NODEIDTYPE_GUID: dst->identifierType = UA_NODEIDTYPE_GUID; ret |= DECODE_DIRECT(&dst->namespaceIndex, UInt16); ret |= DECODE_DIRECT(&dst->identifier.guid, Guid); break; case UA_NODEIDTYPE_BYTESTRING: dst->identifierType = UA_NODEIDTYPE_BYTESTRING; ret |= DECODE_DIRECT(&dst->namespaceIndex, UInt16); ret |= DECODE_DIRECT(&dst->identifier.byteString, String); /* ByteString */ break; default: ret |= UA_STATUSCODE_BADINTERNALERROR; break; } return ret; } /* ExpandedNodeId */ ENCODE_BINARY(ExpandedNodeId) { /* Set up the encoding mask */ u8 encoding = 0; if((void*)src->namespaceUri.data > UA_EMPTY_ARRAY_SENTINEL) encoding |= UA_EXPANDEDNODEID_NAMESPACEURI_FLAG; if(src->serverIndex > 0) encoding |= UA_EXPANDEDNODEID_SERVERINDEX_FLAG; /* Encode the NodeId */ status ret = NodeId_encodeBinaryWithEncodingMask(&src->nodeId, encoding, ctx); if(ret != UA_STATUSCODE_GOOD) return ret; /* Encode the namespace. */ if((void*)src->namespaceUri.data > UA_EMPTY_ARRAY_SENTINEL) { ret = ENCODE_DIRECT(&src->namespaceUri, String); if(ret != UA_STATUSCODE_GOOD) return ret; } /* Encode the serverIndex */ if(src->serverIndex > 0) ret = ENCODE_WITHEXCHANGE(&src->serverIndex, UA_TYPES_UINT32); return ret; } DECODE_BINARY(ExpandedNodeId) { /* Decode the encoding mask */ if(ctx->pos >= ctx->end) return UA_STATUSCODE_BADDECODINGERROR; u8 encoding = *ctx->pos; /* Decode the NodeId */ status ret = DECODE_DIRECT(&dst->nodeId, NodeId); /* Decode the NamespaceUri */ if(encoding & UA_EXPANDEDNODEID_NAMESPACEURI_FLAG) { dst->nodeId.namespaceIndex = 0; ret |= DECODE_DIRECT(&dst->namespaceUri, String); } /* Decode the ServerIndex */ if(encoding & UA_EXPANDEDNODEID_SERVERINDEX_FLAG) ret |= DECODE_DIRECT(&dst->serverIndex, UInt32); return ret; } /* QualifiedName */ ENCODE_BINARY(QualifiedName) { status ret = ENCODE_DIRECT(&src->namespaceIndex, UInt16); ret |= ENCODE_DIRECT(&src->name, String); return ret; } DECODE_BINARY(QualifiedName) { status ret = DECODE_DIRECT(&dst->namespaceIndex, UInt16); ret |= DECODE_DIRECT(&dst->name, String); return ret; } /* LocalizedText */ #define UA_LOCALIZEDTEXT_ENCODINGMASKTYPE_LOCALE 0x01u #define UA_LOCALIZEDTEXT_ENCODINGMASKTYPE_TEXT 0x02u ENCODE_BINARY(LocalizedText) { /* Set up the encoding mask */ u8 encoding = 0; if(src->locale.data) encoding |= UA_LOCALIZEDTEXT_ENCODINGMASKTYPE_LOCALE; if(src->text.data) encoding |= UA_LOCALIZEDTEXT_ENCODINGMASKTYPE_TEXT; /* Encode the encoding byte */ status ret = ENCODE_DIRECT(&encoding, Byte); if(ret != UA_STATUSCODE_GOOD) return ret; /* Encode the strings */ if(encoding & UA_LOCALIZEDTEXT_ENCODINGMASKTYPE_LOCALE) ret |= ENCODE_DIRECT(&src->locale, String); if(encoding & UA_LOCALIZEDTEXT_ENCODINGMASKTYPE_TEXT) ret |= ENCODE_DIRECT(&src->text, String); return ret; } DECODE_BINARY(LocalizedText) { /* Decode the encoding mask */ u8 encoding = 0; status ret = DECODE_DIRECT(&encoding, Byte); /* Decode the content */ if(encoding & UA_LOCALIZEDTEXT_ENCODINGMASKTYPE_LOCALE) ret |= DECODE_DIRECT(&dst->locale, String); if(encoding & UA_LOCALIZEDTEXT_ENCODINGMASKTYPE_TEXT) ret |= DECODE_DIRECT(&dst->text, String); return ret; } /* The binary encoding has a different nodeid from the data type. So it is not * possible to reuse UA_findDataType */ static const UA_DataType * UA_findDataTypeByBinaryInternal(const UA_NodeId *typeId, Ctx *ctx) { /* We only store a numeric identifier for the encoding nodeid of data types */ if(typeId->identifierType != UA_NODEIDTYPE_NUMERIC) return NULL; /* Always look in built-in types first * (may contain data types from all namespaces) */ for(size_t i = 0; i < UA_TYPES_COUNT; ++i) { if(UA_TYPES[i].binaryEncodingId == typeId->identifier.numeric && UA_TYPES[i].typeId.namespaceIndex == typeId->namespaceIndex) return &UA_TYPES[i]; } const UA_DataTypeArray *customTypes = ctx->customTypes; while(customTypes) { for(size_t i = 0; i < customTypes->typesSize; ++i) { if(customTypes->types[i].binaryEncodingId == typeId->identifier.numeric && customTypes->types[i].typeId.namespaceIndex == typeId->namespaceIndex) return &customTypes->types[i]; } customTypes = customTypes->next; } return NULL; } const UA_DataType * UA_findDataTypeByBinary(const UA_NodeId *typeId) { Ctx ctx; ctx.customTypes = NULL; return UA_findDataTypeByBinaryInternal(typeId, &ctx); } /* ExtensionObject */ ENCODE_BINARY(ExtensionObject) { u8 encoding = (u8)src->encoding; /* No content or already encoded content. */ if(encoding <= UA_EXTENSIONOBJECT_ENCODED_XML) { status ret = ENCODE_DIRECT(&src->content.encoded.typeId, NodeId); if(ret != UA_STATUSCODE_GOOD) return ret; ret = ENCODE_WITHEXCHANGE(&encoding, UA_TYPES_BYTE); if(ret != UA_STATUSCODE_GOOD) return ret; switch(src->encoding) { case UA_EXTENSIONOBJECT_ENCODED_NOBODY: break; case UA_EXTENSIONOBJECT_ENCODED_BYTESTRING: case UA_EXTENSIONOBJECT_ENCODED_XML: ret = ENCODE_DIRECT(&src->content.encoded.body, String); /* ByteString */ break; default: ret = UA_STATUSCODE_BADINTERNALERROR; } return ret; } /* Cannot encode with no data or no type description */ if(!src->content.decoded.type || !src->content.decoded.data) return UA_STATUSCODE_BADENCODINGERROR; /* Write the NodeId for the binary encoded type. The NodeId is always * numeric, so no buffer replacement is taking place. */ UA_NodeId typeId = src->content.decoded.type->typeId; if(typeId.identifierType != UA_NODEIDTYPE_NUMERIC) return UA_STATUSCODE_BADENCODINGERROR; typeId.identifier.numeric = src->content.decoded.type->binaryEncodingId; status ret = ENCODE_DIRECT(&typeId, NodeId); /* Write the encoding byte */ encoding = UA_EXTENSIONOBJECT_ENCODED_BYTESTRING; ret |= ENCODE_DIRECT(&encoding, Byte); /* Compute the content length */ const UA_DataType *contentType = src->content.decoded.type; size_t len = UA_calcSizeBinary(src->content.decoded.data, contentType); /* Encode the content length */ if(len > UA_INT32_MAX) return UA_STATUSCODE_BADENCODINGERROR; i32 signed_len = (i32)len; ret |= ENCODE_DIRECT(&signed_len, UInt32); /* Int32 */ /* Return early upon failures (no buffer exchange until here) */ if(ret != UA_STATUSCODE_GOOD) return ret; /* Encode the content */ return encodeWithExchangeBuffer(src->content.decoded.data, contentType, ctx); } static status ExtensionObject_decodeBinaryContent(UA_ExtensionObject *dst, const UA_NodeId *typeId, Ctx *ctx) { /* Lookup the datatype */ const UA_DataType *type = UA_findDataTypeByBinaryInternal(typeId, ctx); /* Unknown type, just take the binary content */ if(!type) { dst->encoding = UA_EXTENSIONOBJECT_ENCODED_BYTESTRING; UA_NodeId_copy(typeId, &dst->content.encoded.typeId); return DECODE_DIRECT(&dst->content.encoded.body, String); /* ByteString */ } /* Allocate memory */ dst->content.decoded.data = UA_new(type); if(!dst->content.decoded.data) return UA_STATUSCODE_BADOUTOFMEMORY; /* Jump over the length field (TODO: check if the decoded length matches) */ ctx->pos += 4; /* Decode */ dst->encoding = UA_EXTENSIONOBJECT_DECODED; dst->content.decoded.type = type; return decodeBinaryJumpTable[type->typeKind](dst->content.decoded.data, type, ctx); } DECODE_BINARY(ExtensionObject) { u8 encoding = 0; UA_NodeId binTypeId; /* Can contain a string nodeid. But no corresponding * type is then found in open62541. We only store * numerical nodeids of the binary encoding identifier. * The extenionobject will be decoded to contain a * binary blob. */ UA_NodeId_init(&binTypeId); status ret = UA_STATUSCODE_GOOD; ret |= DECODE_DIRECT(&binTypeId, NodeId); ret |= DECODE_DIRECT(&encoding, Byte); if(ret != UA_STATUSCODE_GOOD) { UA_NodeId_clear(&binTypeId); return ret; } switch(encoding) { case UA_EXTENSIONOBJECT_ENCODED_BYTESTRING: ret = ExtensionObject_decodeBinaryContent(dst, &binTypeId, ctx); UA_NodeId_deleteMembers(&binTypeId); break; case UA_EXTENSIONOBJECT_ENCODED_NOBODY: dst->encoding = (UA_ExtensionObjectEncoding)encoding; dst->content.encoded.typeId = binTypeId; /* move to dst */ dst->content.encoded.body = UA_BYTESTRING_NULL; break; case UA_EXTENSIONOBJECT_ENCODED_XML: dst->encoding = (UA_ExtensionObjectEncoding)encoding; dst->content.encoded.typeId = binTypeId; /* move to dst */ ret = DECODE_DIRECT(&dst->content.encoded.body, String); /* ByteString */ if(ret != UA_STATUSCODE_GOOD) UA_NodeId_clear(&dst->content.encoded.typeId); break; default: UA_NodeId_clear(&binTypeId); ret = UA_STATUSCODE_BADDECODINGERROR; break; } return ret; } /* Variant */ static status Variant_encodeBinaryWrapExtensionObject(const UA_Variant *src, const UA_Boolean isArray, Ctx *ctx) { /* Default to 1 for a scalar. */ size_t length = 1; /* Encode the array length if required */ status ret = UA_STATUSCODE_GOOD; if(isArray) { if(src->arrayLength > UA_INT32_MAX) return UA_STATUSCODE_BADENCODINGERROR; length = src->arrayLength; i32 encodedLength = (i32)src->arrayLength; ret = ENCODE_DIRECT(&encodedLength, UInt32); /* Int32 */ if(ret != UA_STATUSCODE_GOOD) return ret; } /* Set up the ExtensionObject */ UA_ExtensionObject eo; UA_ExtensionObject_init(&eo); eo.encoding = UA_EXTENSIONOBJECT_DECODED; eo.content.decoded.type = src->type; const u16 memSize = src->type->memSize; uintptr_t ptr = (uintptr_t)src->data; /* Iterate over the array */ for(size_t i = 0; i < length && ret == UA_STATUSCODE_GOOD; ++i) { eo.content.decoded.data = (void*)ptr; ret = encodeWithExchangeBuffer(&eo, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT], ctx); ptr += memSize; } return ret; } enum UA_VARIANT_ENCODINGMASKTYPE { UA_VARIANT_ENCODINGMASKTYPE_TYPEID_MASK = 0x3Fu, /* bits 0:5 */ UA_VARIANT_ENCODINGMASKTYPE_DIMENSIONS = (u8)(0x01u << 6u), /* bit 6 */ UA_VARIANT_ENCODINGMASKTYPE_ARRAY = (u8)(0x01u << 7u) /* bit 7 */ }; ENCODE_BINARY(Variant) { /* Quit early for the empty variant */ u8 encoding = 0; if(!src->type) return ENCODE_DIRECT(&encoding, Byte); /* Set the content type in the encoding mask */ const UA_Boolean isBuiltin = (src->type->typeKind <= UA_DATATYPEKIND_DIAGNOSTICINFO); const UA_Boolean isEnum = (src->type->typeKind == UA_DATATYPEKIND_ENUM); if(isBuiltin) encoding = (u8)(encoding | (u8)((u8)UA_VARIANT_ENCODINGMASKTYPE_TYPEID_MASK & (u8)(src->type->typeKind + 1u))); else if(isEnum) encoding = (u8)(encoding | (u8)((u8)UA_VARIANT_ENCODINGMASKTYPE_TYPEID_MASK & (u8)(UA_TYPES_INT32 + 1u))); else encoding = (u8)(encoding | (u8)((u8)UA_VARIANT_ENCODINGMASKTYPE_TYPEID_MASK & (u8)(UA_TYPES_EXTENSIONOBJECT + 1u))); /* Set the array type in the encoding mask */ const UA_Boolean isArray = src->arrayLength > 0 || src->data <= UA_EMPTY_ARRAY_SENTINEL; const UA_Boolean hasDimensions = isArray && src->arrayDimensionsSize > 0; if(isArray) { encoding |= (u8)UA_VARIANT_ENCODINGMASKTYPE_ARRAY; if(hasDimensions) encoding |= (u8)UA_VARIANT_ENCODINGMASKTYPE_DIMENSIONS; } /* Encode the encoding byte */ status ret = ENCODE_DIRECT(&encoding, Byte); if(ret != UA_STATUSCODE_GOOD) return ret; /* Encode the content */ if(!isBuiltin && !isEnum) ret = Variant_encodeBinaryWrapExtensionObject(src, isArray, ctx); else if(!isArray) ret = encodeWithExchangeBuffer(src->data, src->type, ctx); else ret = Array_encodeBinary(src->data, src->arrayLength, src->type, ctx); /* Encode the array dimensions */ if(hasDimensions && ret == UA_STATUSCODE_GOOD) ret = Array_encodeBinary(src->arrayDimensions, src->arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32], ctx); return ret; } static status Variant_decodeBinaryUnwrapExtensionObject(UA_Variant *dst, Ctx *ctx) { /* Save the position in the ByteString. If unwrapping is not possible, start * from here to decode a normal ExtensionObject. */ u8 *old_pos = ctx->pos; /* Decode the DataType */ UA_NodeId typeId; UA_NodeId_init(&typeId); status ret = DECODE_DIRECT(&typeId, NodeId); if(ret != UA_STATUSCODE_GOOD) return ret; /* Decode the EncodingByte */ u8 encoding; ret = DECODE_DIRECT(&encoding, Byte); if(ret != UA_STATUSCODE_GOOD) { UA_NodeId_clear(&typeId); return ret; } /* Search for the datatype. Default to ExtensionObject. */ if(encoding == UA_EXTENSIONOBJECT_ENCODED_BYTESTRING && (dst->type = UA_findDataTypeByBinaryInternal(&typeId, ctx)) != NULL) { /* Jump over the length field (TODO: check if length matches) */ ctx->pos += 4; } else { /* Reset and decode as ExtensionObject */ dst->type = &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]; ctx->pos = old_pos; UA_NodeId_clear(&typeId); } /* Allocate memory */ dst->data = UA_new(dst->type); if(!dst->data) return UA_STATUSCODE_BADOUTOFMEMORY; /* Decode the content */ return decodeBinaryJumpTable[dst->type->typeKind](dst->data, dst->type, ctx); } /* The resulting variant always has the storagetype UA_VARIANT_DATA. */ DECODE_BINARY(Variant) { /* Decode the encoding byte */ u8 encodingByte; status ret = DECODE_DIRECT(&encodingByte, Byte); if(ret != UA_STATUSCODE_GOOD) return ret; /* Return early for an empty variant (was already _inited) */ if(encodingByte == 0) return UA_STATUSCODE_GOOD; /* Does the variant contain an array? */ const UA_Boolean isArray = (encodingByte & (u8)UA_VARIANT_ENCODINGMASKTYPE_ARRAY) > 0; /* Get the datatype of the content. The type must be a builtin data type. * All not-builtin types are wrapped in an ExtensionObject. The "type kind" * for types up to DiagnsticInfo equals to the index in the encoding * byte. */ size_t typeKind = (size_t)((encodingByte & (u8)UA_VARIANT_ENCODINGMASKTYPE_TYPEID_MASK) - 1); if(typeKind > UA_DATATYPEKIND_DIAGNOSTICINFO) return UA_STATUSCODE_BADDECODINGERROR; /* A variant cannot contain a variant. But it can contain an array of * variants */ if(typeKind == UA_DATATYPEKIND_VARIANT && !isArray) return UA_STATUSCODE_BADDECODINGERROR; /* Check the recursion limit */ if(ctx->depth > UA_ENCODING_MAX_RECURSION) return UA_STATUSCODE_BADENCODINGERROR; ctx->depth++; /* Decode the content */ dst->type = &UA_TYPES[typeKind]; if(isArray) { ret = Array_decodeBinary(&dst->data, &dst->arrayLength, dst->type, ctx); } else if(typeKind != UA_DATATYPEKIND_EXTENSIONOBJECT) { dst->data = UA_new(dst->type); if(!dst->data) return UA_STATUSCODE_BADOUTOFMEMORY; ret = decodeBinaryJumpTable[typeKind](dst->data, dst->type, ctx); } else { ret = Variant_decodeBinaryUnwrapExtensionObject(dst, ctx); } /* Decode array dimensions */ if(isArray && (encodingByte & (u8)UA_VARIANT_ENCODINGMASKTYPE_DIMENSIONS) > 0) ret |= Array_decodeBinary((void**)&dst->arrayDimensions, &dst->arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32], ctx); ctx->depth--; return ret; } /* DataValue */ ENCODE_BINARY(DataValue) { /* Set up the encoding mask */ u8 encodingMask = src->hasValue; encodingMask |= (u8)(src->hasStatus << 1u); encodingMask |= (u8)(src->hasSourceTimestamp << 2u); encodingMask |= (u8)(src->hasServerTimestamp << 3u); encodingMask |= (u8)(src->hasSourcePicoseconds << 4u); encodingMask |= (u8)(src->hasServerPicoseconds << 5u); /* Encode the encoding byte */ status ret = ENCODE_DIRECT(&encodingMask, Byte); if(ret != UA_STATUSCODE_GOOD) return ret; /* Encode the variant. */ if(src->hasValue) { ret = ENCODE_DIRECT(&src->value, Variant); if(ret != UA_STATUSCODE_GOOD) return ret; } if(src->hasStatus) ret |= ENCODE_WITHEXCHANGE(&src->status, UA_TYPES_STATUSCODE); if(src->hasSourceTimestamp) ret |= ENCODE_WITHEXCHANGE(&src->sourceTimestamp, UA_TYPES_DATETIME); if(src->hasSourcePicoseconds) ret |= ENCODE_WITHEXCHANGE(&src->sourcePicoseconds, UA_TYPES_UINT16); if(src->hasServerTimestamp) ret |= ENCODE_WITHEXCHANGE(&src->serverTimestamp, UA_TYPES_DATETIME); if(src->hasServerPicoseconds) ret |= ENCODE_WITHEXCHANGE(&src->serverPicoseconds, UA_TYPES_UINT16); return ret; } #define MAX_PICO_SECONDS 9999 DECODE_BINARY(DataValue) { /* Decode the encoding mask */ u8 encodingMask; status ret = DECODE_DIRECT(&encodingMask, Byte); if(ret != UA_STATUSCODE_GOOD) return ret; /* Check the recursion limit */ if(ctx->depth > UA_ENCODING_MAX_RECURSION) return UA_STATUSCODE_BADENCODINGERROR; ctx->depth++; /* Decode the content */ if(encodingMask & 0x01u) { dst->hasValue = true; ret |= DECODE_DIRECT(&dst->value, Variant); } if(encodingMask & 0x02u) { dst->hasStatus = true; ret |= DECODE_DIRECT(&dst->status, UInt32); /* StatusCode */ } if(encodingMask & 0x04u) { dst->hasSourceTimestamp = true; ret |= DECODE_DIRECT(&dst->sourceTimestamp, UInt64); /* DateTime */ } if(encodingMask & 0x10u) { dst->hasSourcePicoseconds = true; ret |= DECODE_DIRECT(&dst->sourcePicoseconds, UInt16); if(dst->sourcePicoseconds > MAX_PICO_SECONDS) dst->sourcePicoseconds = MAX_PICO_SECONDS; } if(encodingMask & 0x08u) { dst->hasServerTimestamp = true; ret |= DECODE_DIRECT(&dst->serverTimestamp, UInt64); /* DateTime */ } if(encodingMask & 0x20u) { dst->hasServerPicoseconds = true; ret |= DECODE_DIRECT(&dst->serverPicoseconds, UInt16); if(dst->serverPicoseconds > MAX_PICO_SECONDS) dst->serverPicoseconds = MAX_PICO_SECONDS; } ctx->depth--; return ret; } /* DiagnosticInfo */ ENCODE_BINARY(DiagnosticInfo) { /* Set up the encoding mask */ u8 encodingMask = src->hasSymbolicId; encodingMask |= (u8)(src->hasNamespaceUri << 1u); encodingMask |= (u8)(src->hasLocalizedText << 2u); encodingMask |= (u8)(src->hasLocale << 3u); encodingMask |= (u8)(src->hasAdditionalInfo << 4u); encodingMask |= (u8)(src->hasInnerDiagnosticInfo << 5u); /* Encode the numeric content */ status ret = ENCODE_DIRECT(&encodingMask, Byte); if(src->hasSymbolicId) ret |= ENCODE_DIRECT(&src->symbolicId, UInt32); /* Int32 */ if(src->hasNamespaceUri) ret |= ENCODE_DIRECT(&src->namespaceUri, UInt32); /* Int32 */ if(src->hasLocalizedText) ret |= ENCODE_DIRECT(&src->localizedText, UInt32); /* Int32 */ if(src->hasLocale) ret |= ENCODE_DIRECT(&src->locale, UInt32); /* Int32 */ if(ret != UA_STATUSCODE_GOOD) return ret; /* Encode the additional info */ if(src->hasAdditionalInfo) { ret = ENCODE_DIRECT(&src->additionalInfo, String); if(ret != UA_STATUSCODE_GOOD) return ret; } /* Encode the inner status code */ if(src->hasInnerStatusCode) { ret = ENCODE_WITHEXCHANGE(&src->innerStatusCode, UA_TYPES_UINT32); if(ret != UA_STATUSCODE_GOOD) return ret; } /* Encode the inner diagnostic info */ if(src->hasInnerDiagnosticInfo) // innerDiagnosticInfo is already a pointer, so don't use the & reference here ret = ENCODE_WITHEXCHANGE(src->innerDiagnosticInfo, UA_TYPES_DIAGNOSTICINFO); return ret; } DECODE_BINARY(DiagnosticInfo) { /* Decode the encoding mask */ u8 encodingMask; status ret = DECODE_DIRECT(&encodingMask, Byte); if(ret != UA_STATUSCODE_GOOD) return ret; /* Decode the content */ if(encodingMask & 0x01u) { dst->hasSymbolicId = true; ret |= DECODE_DIRECT(&dst->symbolicId, UInt32); /* Int32 */ } if(encodingMask & 0x02u) { dst->hasNamespaceUri = true; ret |= DECODE_DIRECT(&dst->namespaceUri, UInt32); /* Int32 */ } if(encodingMask & 0x04u) { dst->hasLocalizedText = true; ret |= DECODE_DIRECT(&dst->localizedText, UInt32); /* Int32 */ } if(encodingMask & 0x08u) { dst->hasLocale = true; ret |= DECODE_DIRECT(&dst->locale, UInt32); /* Int32 */ } if(encodingMask & 0x10u) { dst->hasAdditionalInfo = true; ret |= DECODE_DIRECT(&dst->additionalInfo, String); } if(encodingMask & 0x20u) { dst->hasInnerStatusCode = true; ret |= DECODE_DIRECT(&dst->innerStatusCode, UInt32); /* StatusCode */ } if(encodingMask & 0x40u) { /* innerDiagnosticInfo is allocated on the heap */ dst->innerDiagnosticInfo = (UA_DiagnosticInfo*) UA_calloc(1, sizeof(UA_DiagnosticInfo)); if(!dst->innerDiagnosticInfo) return UA_STATUSCODE_BADOUTOFMEMORY; dst->hasInnerDiagnosticInfo = true; /* Check the recursion limit */ if(ctx->depth > UA_ENCODING_MAX_RECURSION) return UA_STATUSCODE_BADENCODINGERROR; ctx->depth++; ret |= DECODE_DIRECT(dst->innerDiagnosticInfo, DiagnosticInfo); ctx->depth--; } return ret; } static status encodeBinaryStruct(const void *src, const UA_DataType *type, Ctx *ctx) { /* Check the recursion limit */ if(ctx->depth > UA_ENCODING_MAX_RECURSION) return UA_STATUSCODE_BADENCODINGERROR; ctx->depth++; uintptr_t ptr = (uintptr_t)src; status ret = UA_STATUSCODE_GOOD; u8 membersSize = type->membersSize; const UA_DataType *typelists[2] = { UA_TYPES, &type[-type->typeIndex] }; /* Loop over members */ for(size_t i = 0; i < membersSize; ++i) { const UA_DataTypeMember *m = &type->members[i]; const UA_DataType *mt = &typelists[!m->namespaceZero][m->memberTypeIndex]; ptr += m->padding; /* Array. Buffer-exchange is done inside Array_encodeBinary if required. */ if(m->isArray) { const size_t length = *((const size_t*)ptr); ptr += sizeof(size_t); ret = Array_encodeBinary(*(void *UA_RESTRICT const *)ptr, length, mt, ctx); ptr += sizeof(void*); continue; } /* Scalar */ ret = encodeWithExchangeBuffer((const void*)ptr, mt, ctx); ptr += mt->memSize; } ctx->depth--; return ret; } static status encodeBinaryNotImplemented(const void *src, const UA_DataType *type, Ctx *ctx) { (void)src, (void)type, (void)ctx; return UA_STATUSCODE_BADNOTIMPLEMENTED; } /********************/ /* Structured Types */ /********************/ const encodeBinarySignature encodeBinaryJumpTable[UA_DATATYPEKINDS] = { (encodeBinarySignature)Boolean_encodeBinary, (encodeBinarySignature)Byte_encodeBinary, /* SByte */ (encodeBinarySignature)Byte_encodeBinary, (encodeBinarySignature)UInt16_encodeBinary, /* Int16 */ (encodeBinarySignature)UInt16_encodeBinary, (encodeBinarySignature)UInt32_encodeBinary, /* Int32 */ (encodeBinarySignature)UInt32_encodeBinary, (encodeBinarySignature)UInt64_encodeBinary, /* Int64 */ (encodeBinarySignature)UInt64_encodeBinary, (encodeBinarySignature)Float_encodeBinary, (encodeBinarySignature)Double_encodeBinary, (encodeBinarySignature)String_encodeBinary, (encodeBinarySignature)UInt64_encodeBinary, /* DateTime */ (encodeBinarySignature)Guid_encodeBinary, (encodeBinarySignature)String_encodeBinary, /* ByteString */ (encodeBinarySignature)String_encodeBinary, /* XmlElement */ (encodeBinarySignature)NodeId_encodeBinary, (encodeBinarySignature)ExpandedNodeId_encodeBinary, (encodeBinarySignature)UInt32_encodeBinary, /* StatusCode */ (encodeBinarySignature)QualifiedName_encodeBinary, (encodeBinarySignature)LocalizedText_encodeBinary, (encodeBinarySignature)ExtensionObject_encodeBinary, (encodeBinarySignature)DataValue_encodeBinary, (encodeBinarySignature)Variant_encodeBinary, (encodeBinarySignature)DiagnosticInfo_encodeBinary, (encodeBinarySignature)encodeBinaryNotImplemented, /* Decimal */ (encodeBinarySignature)UInt32_encodeBinary, /* Enumeration */ (encodeBinarySignature)encodeBinaryStruct, (encodeBinarySignature)encodeBinaryNotImplemented, /* Structure with Optional Fields */ (encodeBinarySignature)encodeBinaryStruct, /* Union */ (encodeBinarySignature)encodeBinaryStruct /* BitfieldCluster */ }; status UA_encodeBinary(const void *src, const UA_DataType *type, u8 **bufPos, const u8 **bufEnd, UA_exchangeEncodeBuffer exchangeCallback, void *exchangeHandle) { /* Set up the context */ Ctx ctx; ctx.pos = *bufPos; ctx.end = *bufEnd; ctx.depth = 0; ctx.exchangeBufferCallback = exchangeCallback; ctx.exchangeBufferCallbackHandle = exchangeHandle; if(!ctx.pos) return UA_STATUSCODE_BADINVALIDARGUMENT; /* Encode */ status ret = encodeWithExchangeBuffer(src, type, &ctx); /* Set the new buffer position for the output. Beware that the buffer might * have been exchanged internally. */ *bufPos = ctx.pos; *bufEnd = ctx.end; return ret; } static status decodeBinaryNotImplemented(void *dst, const UA_DataType *type, Ctx *ctx) { (void)dst, (void)type, (void)ctx; return UA_STATUSCODE_BADNOTIMPLEMENTED; } static status decodeBinaryStructure(void *dst, const UA_DataType *type, Ctx *ctx) { /* Check the recursion limit */ if(ctx->depth > UA_ENCODING_MAX_RECURSION) return UA_STATUSCODE_BADENCODINGERROR; ctx->depth++; uintptr_t ptr = (uintptr_t)dst; status ret = UA_STATUSCODE_GOOD; u8 membersSize = type->membersSize; const UA_DataType *typelists[2] = { UA_TYPES, &type[-type->typeIndex] }; /* Loop over members */ for(size_t i = 0; i < membersSize && ret == UA_STATUSCODE_GOOD; ++i) { const UA_DataTypeMember *m = &type->members[i]; const UA_DataType *mt = &typelists[!m->namespaceZero][m->memberTypeIndex]; ptr += m->padding; /* Array */ if(m->isArray) { size_t *length = (size_t*)ptr; ptr += sizeof(size_t); ret = Array_decodeBinary((void *UA_RESTRICT *UA_RESTRICT)ptr, length, mt , ctx); ptr += sizeof(void*); continue; } /* Scalar */ ret = decodeBinaryJumpTable[mt->typeKind]((void *UA_RESTRICT)ptr, mt, ctx); ptr += mt->memSize; } ctx->depth--; return ret; } const decodeBinarySignature decodeBinaryJumpTable[UA_DATATYPEKINDS] = { (decodeBinarySignature)Boolean_decodeBinary, (decodeBinarySignature)Byte_decodeBinary, /* SByte */ (decodeBinarySignature)Byte_decodeBinary, (decodeBinarySignature)UInt16_decodeBinary, /* Int16 */ (decodeBinarySignature)UInt16_decodeBinary, (decodeBinarySignature)UInt32_decodeBinary, /* Int32 */ (decodeBinarySignature)UInt32_decodeBinary, (decodeBinarySignature)UInt64_decodeBinary, /* Int64 */ (decodeBinarySignature)UInt64_decodeBinary, (decodeBinarySignature)Float_decodeBinary, (decodeBinarySignature)Double_decodeBinary, (decodeBinarySignature)String_decodeBinary, (decodeBinarySignature)UInt64_decodeBinary, /* DateTime */ (decodeBinarySignature)Guid_decodeBinary, (decodeBinarySignature)String_decodeBinary, /* ByteString */ (decodeBinarySignature)String_decodeBinary, /* XmlElement */ (decodeBinarySignature)NodeId_decodeBinary, (decodeBinarySignature)ExpandedNodeId_decodeBinary, (decodeBinarySignature)UInt32_decodeBinary, /* StatusCode */ (decodeBinarySignature)QualifiedName_decodeBinary, (decodeBinarySignature)LocalizedText_decodeBinary, (decodeBinarySignature)ExtensionObject_decodeBinary, (decodeBinarySignature)DataValue_decodeBinary, (decodeBinarySignature)Variant_decodeBinary, (decodeBinarySignature)DiagnosticInfo_decodeBinary, (decodeBinarySignature)decodeBinaryNotImplemented, /* Decimal */ (decodeBinarySignature)UInt32_decodeBinary, /* Enumeration */ (decodeBinarySignature)decodeBinaryStructure, (decodeBinarySignature)decodeBinaryNotImplemented, /* Structure with optional fields */ (decodeBinarySignature)decodeBinaryNotImplemented, /* Union */ (decodeBinarySignature)decodeBinaryNotImplemented /* BitfieldCluster */ }; status UA_decodeBinary(const UA_ByteString *src, size_t *offset, void *dst, const UA_DataType *type, const UA_DataTypeArray *customTypes) { /* Set up the context */ Ctx ctx; ctx.pos = &src->data[*offset]; ctx.end = &src->data[src->length]; ctx.depth = 0; ctx.customTypes = customTypes; /* Decode */ memset(dst, 0, type->memSize); /* Initialize the value */ status ret = decodeBinaryJumpTable[type->typeKind](dst, type, &ctx); if(ret == UA_STATUSCODE_GOOD) { /* Set the new offset */ *offset = (size_t)(ctx.pos - src->data) / sizeof(u8); } else { /* Clean up */ UA_clear(dst, type); memset(dst, 0, type->memSize); } return ret; } /** * Compute the Message Size * ------------------------ * The following methods are used to compute the length of a datum in binary * encoding. */ static size_t Array_calcSizeBinary(const void *src, size_t length, const UA_DataType *type) { size_t s = 4; /* length */ if(type->overlayable) { s += type->memSize * length; return s; } uintptr_t ptr = (uintptr_t)src; for(size_t i = 0; i < length; ++i) { s += calcSizeBinaryJumpTable[type->typeKind]((const void*)ptr, type); ptr += type->memSize; } return s; } static size_t calcSizeBinary1(const void *_, const UA_DataType *__) { (void)_, (void)__; return 1; } static size_t calcSizeBinary2(const void *_, const UA_DataType *__) { (void)_, (void)__; return 2; } static size_t calcSizeBinary4(const void *_, const UA_DataType *__) { (void)_, (void)__; return 4; } static size_t calcSizeBinary8(const void *_, const UA_DataType *__) { (void)_, (void)__; return 8; } CALCSIZE_BINARY(String) { return 4 + src->length; } CALCSIZE_BINARY(Guid) { return 16; } CALCSIZE_BINARY(NodeId) { size_t s = 1; /* Encoding byte */ switch(src->identifierType) { case UA_NODEIDTYPE_NUMERIC: if(src->identifier.numeric > UA_UINT16_MAX || src->namespaceIndex > UA_BYTE_MAX) { s += 6; } else if(src->identifier.numeric > UA_BYTE_MAX || src->namespaceIndex > 0) { s += 3; } else { s += 1; } break; case UA_NODEIDTYPE_BYTESTRING: case UA_NODEIDTYPE_STRING: s += 2; s += String_calcSizeBinary(&src->identifier.string, NULL); break; case UA_NODEIDTYPE_GUID: s += 18; break; default: return 0; } return s; } CALCSIZE_BINARY(ExpandedNodeId) { size_t s = NodeId_calcSizeBinary(&src->nodeId, NULL); if(src->namespaceUri.length > 0) s += String_calcSizeBinary(&src->namespaceUri, NULL); if(src->serverIndex > 0) s += 4; return s; } CALCSIZE_BINARY(QualifiedName) { return 2 + String_calcSizeBinary(&src->name, NULL); } CALCSIZE_BINARY(LocalizedText) { size_t s = 1; /* Encoding byte */ if(src->locale.data) s += String_calcSizeBinary(&src->locale, NULL); if(src->text.data) s += String_calcSizeBinary(&src->text, NULL); return s; } CALCSIZE_BINARY(ExtensionObject) { size_t s = 1; /* Encoding byte */ /* Encoded content */ if(src->encoding <= UA_EXTENSIONOBJECT_ENCODED_XML) { s += NodeId_calcSizeBinary(&src->content.encoded.typeId, NULL); switch(src->encoding) { case UA_EXTENSIONOBJECT_ENCODED_NOBODY: break; case UA_EXTENSIONOBJECT_ENCODED_BYTESTRING: case UA_EXTENSIONOBJECT_ENCODED_XML: s += String_calcSizeBinary(&src->content.encoded.body, NULL); break; default: return 0; } return s; } /* Decoded content */ if(!src->content.decoded.type || !src->content.decoded.data) return 0; if(src->content.decoded.type->typeId.identifierType != UA_NODEIDTYPE_NUMERIC) return 0; s += NodeId_calcSizeBinary(&src->content.decoded.type->typeId, NULL); /* Type encoding length */ s += 4; /* Encoding length field */ const UA_DataType *type = src->content.decoded.type; s += calcSizeBinaryJumpTable[type->typeKind](src->content.decoded.data, type); /* Encoding length */ return s; } CALCSIZE_BINARY(Variant) { size_t s = 1; /* Encoding byte */ if(!src->type) return s; const UA_Boolean isArray = src->arrayLength > 0 || src->data <= UA_EMPTY_ARRAY_SENTINEL; if(isArray) s += Array_calcSizeBinary(src->data, src->arrayLength, src->type); else s += calcSizeBinaryJumpTable[src->type->typeKind](src->data, src->type); const UA_Boolean isBuiltin = (src->type->typeKind <= UA_DATATYPEKIND_DIAGNOSTICINFO); const UA_Boolean isEnum = (src->type->typeKind == UA_DATATYPEKIND_ENUM); if(!isBuiltin && !isEnum) { /* The type is wrapped inside an extensionobject */ /* (NodeId + encoding byte + extension object length) * array length */ size_t length = isArray ? src->arrayLength : 1; s += (NodeId_calcSizeBinary(&src->type->typeId, NULL) + 1 + 4) * length; } const UA_Boolean hasDimensions = isArray && src->arrayDimensionsSize > 0; if(hasDimensions) s += Array_calcSizeBinary(src->arrayDimensions, src->arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]); return s; } CALCSIZE_BINARY(DataValue) { size_t s = 1; /* Encoding byte */ if(src->hasValue) s += Variant_calcSizeBinary(&src->value, NULL); if(src->hasStatus) s += 4; if(src->hasSourceTimestamp) s += 8; if(src->hasSourcePicoseconds) s += 2; if(src->hasServerTimestamp) s += 8; if(src->hasServerPicoseconds) s += 2; return s; } CALCSIZE_BINARY(DiagnosticInfo) { size_t s = 1; /* Encoding byte */ if(src->hasSymbolicId) s += 4; if(src->hasNamespaceUri) s += 4; if(src->hasLocalizedText) s += 4; if(src->hasLocale) s += 4; if(src->hasAdditionalInfo) s += String_calcSizeBinary(&src->additionalInfo, NULL); if(src->hasInnerStatusCode) s += 4; if(src->hasInnerDiagnosticInfo) s += DiagnosticInfo_calcSizeBinary(src->innerDiagnosticInfo, NULL); return s; } static size_t calcSizeBinaryStructure(const void *p, const UA_DataType *type) { size_t s = 0; uintptr_t ptr = (uintptr_t)p; u8 membersSize = type->membersSize; const UA_DataType *typelists[2] = { UA_TYPES, &type[-type->typeIndex] }; /* Loop over members */ for(size_t i = 0; i < membersSize; ++i) { const UA_DataTypeMember *member = &type->members[i]; const UA_DataType *membertype = &typelists[!member->namespaceZero][member->memberTypeIndex]; ptr += member->padding; /* Array */ if(member->isArray) { const size_t length = *((const size_t*)ptr); ptr += sizeof(size_t); s += Array_calcSizeBinary(*(void *UA_RESTRICT const *)ptr, length, membertype); ptr += sizeof(void*); continue; } /* Scalar */ s += calcSizeBinaryJumpTable[membertype->typeKind]((const void*)ptr, membertype); ptr += membertype->memSize; } return s; } static size_t calcSizeBinaryNotImplemented(const void *p, const UA_DataType *type) { (void)p, (void)type; return 0; } const calcSizeBinarySignature calcSizeBinaryJumpTable[UA_DATATYPEKINDS] = { (calcSizeBinarySignature)calcSizeBinary1, /* Boolean */ (calcSizeBinarySignature)calcSizeBinary1, /* SByte */ (calcSizeBinarySignature)calcSizeBinary1, /* Byte */ (calcSizeBinarySignature)calcSizeBinary2, /* Int16 */ (calcSizeBinarySignature)calcSizeBinary2, /* UInt16 */ (calcSizeBinarySignature)calcSizeBinary4, /* Int32 */ (calcSizeBinarySignature)calcSizeBinary4, /* UInt32 */ (calcSizeBinarySignature)calcSizeBinary8, /* Int64 */ (calcSizeBinarySignature)calcSizeBinary8, /* UInt64 */ (calcSizeBinarySignature)calcSizeBinary4, /* Float */ (calcSizeBinarySignature)calcSizeBinary8, /* Double */ (calcSizeBinarySignature)String_calcSizeBinary, (calcSizeBinarySignature)calcSizeBinary8, /* DateTime */ (calcSizeBinarySignature)Guid_calcSizeBinary, (calcSizeBinarySignature)String_calcSizeBinary, /* ByteString */ (calcSizeBinarySignature)String_calcSizeBinary, /* XmlElement */ (calcSizeBinarySignature)NodeId_calcSizeBinary, (calcSizeBinarySignature)ExpandedNodeId_calcSizeBinary, (calcSizeBinarySignature)calcSizeBinary4, /* StatusCode */ (calcSizeBinarySignature)QualifiedName_calcSizeBinary, (calcSizeBinarySignature)LocalizedText_calcSizeBinary, (calcSizeBinarySignature)ExtensionObject_calcSizeBinary, (calcSizeBinarySignature)DataValue_calcSizeBinary, (calcSizeBinarySignature)Variant_calcSizeBinary, (calcSizeBinarySignature)DiagnosticInfo_calcSizeBinary, (calcSizeBinarySignature)calcSizeBinaryNotImplemented, /* Decimal */ (calcSizeBinarySignature)calcSizeBinary4, /* Enumeration */ (calcSizeBinarySignature)calcSizeBinaryStructure, (calcSizeBinarySignature)calcSizeBinaryNotImplemented, /* Structure with Optional Fields */ (calcSizeBinarySignature)calcSizeBinaryNotImplemented, /* Union */ (calcSizeBinarySignature)calcSizeBinaryNotImplemented /* BitfieldCluster */ }; size_t UA_calcSizeBinary(const void *p, const UA_DataType *type) { return calcSizeBinaryJumpTable[type->typeKind](p, type); } /*********************************** amalgamated original file "/home/jvoe/open62541/build/src_generated/open62541/types_generated.c" ***********************************/ /* Generated from Opc.Ua.Types.bsd with script /home/jvoe/open62541/tools/generate_datatypes.py * on host rigel by user jvoe at 2019-09-27 03:59:36 */ /* Boolean */ #define Boolean_members NULL /* SByte */ #define SByte_members NULL /* Byte */ #define Byte_members NULL /* Int16 */ #define Int16_members NULL /* UInt16 */ #define UInt16_members NULL /* Int32 */ #define Int32_members NULL /* UInt32 */ #define UInt32_members NULL /* Int64 */ #define Int64_members NULL /* UInt64 */ #define UInt64_members NULL /* Float */ #define Float_members NULL /* Double */ #define Double_members NULL /* String */ #define String_members NULL /* DateTime */ #define DateTime_members NULL /* Guid */ #define Guid_members NULL /* ByteString */ #define ByteString_members NULL /* XmlElement */ #define XmlElement_members NULL /* NodeId */ #define NodeId_members NULL /* ExpandedNodeId */ #define ExpandedNodeId_members NULL /* StatusCode */ #define StatusCode_members NULL /* QualifiedName */ #define QualifiedName_members NULL /* LocalizedText */ #define LocalizedText_members NULL /* ExtensionObject */ #define ExtensionObject_members NULL /* DataValue */ #define DataValue_members NULL /* Variant */ #define Variant_members NULL /* DiagnosticInfo */ #define DiagnosticInfo_members NULL /* ViewAttributes */ static UA_DataTypeMember ViewAttributes_members[7] = { { UA_TYPENAME("SpecifiedAttributes") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DisplayName") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_ViewAttributes, displayName) - offsetof(UA_ViewAttributes, specifiedAttributes) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Description") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_ViewAttributes, description) - offsetof(UA_ViewAttributes, displayName) - sizeof(UA_LocalizedText), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("WriteMask") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ViewAttributes, writeMask) - offsetof(UA_ViewAttributes, description) - sizeof(UA_LocalizedText), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("UserWriteMask") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ViewAttributes, userWriteMask) - offsetof(UA_ViewAttributes, writeMask) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ContainsNoLoops") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_ViewAttributes, containsNoLoops) - offsetof(UA_ViewAttributes, userWriteMask) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("EventNotifier") /* .memberName */ UA_TYPES_BYTE, /* .memberTypeIndex */ offsetof(UA_ViewAttributes, eventNotifier) - offsetof(UA_ViewAttributes, containsNoLoops) - sizeof(UA_Boolean), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* ElementOperand */ static UA_DataTypeMember ElementOperand_members[1] = { { UA_TYPENAME("Index") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* VariableAttributes */ static UA_DataTypeMember VariableAttributes_members[13] = { { UA_TYPENAME("SpecifiedAttributes") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DisplayName") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_VariableAttributes, displayName) - offsetof(UA_VariableAttributes, specifiedAttributes) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Description") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_VariableAttributes, description) - offsetof(UA_VariableAttributes, displayName) - sizeof(UA_LocalizedText), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("WriteMask") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_VariableAttributes, writeMask) - offsetof(UA_VariableAttributes, description) - sizeof(UA_LocalizedText), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("UserWriteMask") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_VariableAttributes, userWriteMask) - offsetof(UA_VariableAttributes, writeMask) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Value") /* .memberName */ UA_TYPES_VARIANT, /* .memberTypeIndex */ offsetof(UA_VariableAttributes, value) - offsetof(UA_VariableAttributes, userWriteMask) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DataType") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ offsetof(UA_VariableAttributes, dataType) - offsetof(UA_VariableAttributes, value) - sizeof(UA_Variant), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ValueRank") /* .memberName */ UA_TYPES_INT32, /* .memberTypeIndex */ offsetof(UA_VariableAttributes, valueRank) - offsetof(UA_VariableAttributes, dataType) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ArrayDimensions") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_VariableAttributes, arrayDimensionsSize) - offsetof(UA_VariableAttributes, valueRank) - sizeof(UA_Int32), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("AccessLevel") /* .memberName */ UA_TYPES_BYTE, /* .memberTypeIndex */ offsetof(UA_VariableAttributes, accessLevel) - offsetof(UA_VariableAttributes, arrayDimensions) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("UserAccessLevel") /* .memberName */ UA_TYPES_BYTE, /* .memberTypeIndex */ offsetof(UA_VariableAttributes, userAccessLevel) - offsetof(UA_VariableAttributes, accessLevel) - sizeof(UA_Byte), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("MinimumSamplingInterval") /* .memberName */ UA_TYPES_DOUBLE, /* .memberTypeIndex */ offsetof(UA_VariableAttributes, minimumSamplingInterval) - offsetof(UA_VariableAttributes, userAccessLevel) - sizeof(UA_Byte), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Historizing") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_VariableAttributes, historizing) - offsetof(UA_VariableAttributes, minimumSamplingInterval) - sizeof(UA_Double), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* EnumValueType */ static UA_DataTypeMember EnumValueType_members[3] = { { UA_TYPENAME("Value") /* .memberName */ UA_TYPES_INT64, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DisplayName") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_EnumValueType, displayName) - offsetof(UA_EnumValueType, value) - sizeof(UA_Int64), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Description") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_EnumValueType, description) - offsetof(UA_EnumValueType, displayName) - sizeof(UA_LocalizedText), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* EventFieldList */ static UA_DataTypeMember EventFieldList_members[2] = { { UA_TYPENAME("ClientHandle") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("EventFields") /* .memberName */ UA_TYPES_VARIANT, /* .memberTypeIndex */ offsetof(UA_EventFieldList, eventFieldsSize) - offsetof(UA_EventFieldList, clientHandle) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* MonitoredItemCreateResult */ static UA_DataTypeMember MonitoredItemCreateResult_members[5] = { { UA_TYPENAME("StatusCode") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("MonitoredItemId") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_MonitoredItemCreateResult, monitoredItemId) - offsetof(UA_MonitoredItemCreateResult, statusCode) - sizeof(UA_StatusCode), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RevisedSamplingInterval") /* .memberName */ UA_TYPES_DOUBLE, /* .memberTypeIndex */ offsetof(UA_MonitoredItemCreateResult, revisedSamplingInterval) - offsetof(UA_MonitoredItemCreateResult, monitoredItemId) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RevisedQueueSize") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_MonitoredItemCreateResult, revisedQueueSize) - offsetof(UA_MonitoredItemCreateResult, revisedSamplingInterval) - sizeof(UA_Double), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("FilterResult") /* .memberName */ UA_TYPES_EXTENSIONOBJECT, /* .memberTypeIndex */ offsetof(UA_MonitoredItemCreateResult, filterResult) - offsetof(UA_MonitoredItemCreateResult, revisedQueueSize) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* ServerDiagnosticsSummaryDataType */ static UA_DataTypeMember ServerDiagnosticsSummaryDataType_members[12] = { { UA_TYPENAME("ServerViewCount") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("CurrentSessionCount") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ServerDiagnosticsSummaryDataType, currentSessionCount) - offsetof(UA_ServerDiagnosticsSummaryDataType, serverViewCount) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("CumulatedSessionCount") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ServerDiagnosticsSummaryDataType, cumulatedSessionCount) - offsetof(UA_ServerDiagnosticsSummaryDataType, currentSessionCount) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SecurityRejectedSessionCount") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ServerDiagnosticsSummaryDataType, securityRejectedSessionCount) - offsetof(UA_ServerDiagnosticsSummaryDataType, cumulatedSessionCount) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RejectedSessionCount") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ServerDiagnosticsSummaryDataType, rejectedSessionCount) - offsetof(UA_ServerDiagnosticsSummaryDataType, securityRejectedSessionCount) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SessionTimeoutCount") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ServerDiagnosticsSummaryDataType, sessionTimeoutCount) - offsetof(UA_ServerDiagnosticsSummaryDataType, rejectedSessionCount) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SessionAbortCount") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ServerDiagnosticsSummaryDataType, sessionAbortCount) - offsetof(UA_ServerDiagnosticsSummaryDataType, sessionTimeoutCount) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("CurrentSubscriptionCount") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ServerDiagnosticsSummaryDataType, currentSubscriptionCount) - offsetof(UA_ServerDiagnosticsSummaryDataType, sessionAbortCount) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("CumulatedSubscriptionCount") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ServerDiagnosticsSummaryDataType, cumulatedSubscriptionCount) - offsetof(UA_ServerDiagnosticsSummaryDataType, currentSubscriptionCount) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("PublishingIntervalCount") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ServerDiagnosticsSummaryDataType, publishingIntervalCount) - offsetof(UA_ServerDiagnosticsSummaryDataType, cumulatedSubscriptionCount) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SecurityRejectedRequestsCount") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ServerDiagnosticsSummaryDataType, securityRejectedRequestsCount) - offsetof(UA_ServerDiagnosticsSummaryDataType, publishingIntervalCount) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RejectedRequestsCount") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ServerDiagnosticsSummaryDataType, rejectedRequestsCount) - offsetof(UA_ServerDiagnosticsSummaryDataType, securityRejectedRequestsCount) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* ContentFilterElementResult */ static UA_DataTypeMember ContentFilterElementResult_members[3] = { { UA_TYPENAME("StatusCode") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("OperandStatusCodes") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ offsetof(UA_ContentFilterElementResult, operandStatusCodesSize) - offsetof(UA_ContentFilterElementResult, statusCode) - sizeof(UA_StatusCode), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("OperandDiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_ContentFilterElementResult, operandDiagnosticInfosSize) - offsetof(UA_ContentFilterElementResult, operandStatusCodes) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* LiteralOperand */ static UA_DataTypeMember LiteralOperand_members[1] = { { UA_TYPENAME("Value") /* .memberName */ UA_TYPES_VARIANT, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* MessageSecurityMode */ #define MessageSecurityMode_members NULL /* UtcTime */ #define UtcTime_members NULL /* UserIdentityToken */ static UA_DataTypeMember UserIdentityToken_members[1] = { { UA_TYPENAME("PolicyId") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* X509IdentityToken */ static UA_DataTypeMember X509IdentityToken_members[2] = { { UA_TYPENAME("PolicyId") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("CertificateData") /* .memberName */ UA_TYPES_BYTESTRING, /* .memberTypeIndex */ offsetof(UA_X509IdentityToken, certificateData) - offsetof(UA_X509IdentityToken, policyId) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* MonitoredItemNotification */ static UA_DataTypeMember MonitoredItemNotification_members[2] = { { UA_TYPENAME("ClientHandle") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Value") /* .memberName */ UA_TYPES_DATAVALUE, /* .memberTypeIndex */ offsetof(UA_MonitoredItemNotification, value) - offsetof(UA_MonitoredItemNotification, clientHandle) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* ResponseHeader */ static UA_DataTypeMember ResponseHeader_members[6] = { { UA_TYPENAME("Timestamp") /* .memberName */ UA_TYPES_DATETIME, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RequestHandle") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ResponseHeader, requestHandle) - offsetof(UA_ResponseHeader, timestamp) - sizeof(UA_DateTime), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ServiceResult") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ offsetof(UA_ResponseHeader, serviceResult) - offsetof(UA_ResponseHeader, requestHandle) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ServiceDiagnostics") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_ResponseHeader, serviceDiagnostics) - offsetof(UA_ResponseHeader, serviceResult) - sizeof(UA_StatusCode), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("StringTable") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_ResponseHeader, stringTableSize) - offsetof(UA_ResponseHeader, serviceDiagnostics) - sizeof(UA_DiagnosticInfo), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("AdditionalHeader") /* .memberName */ UA_TYPES_EXTENSIONOBJECT, /* .memberTypeIndex */ offsetof(UA_ResponseHeader, additionalHeader) - offsetof(UA_ResponseHeader, stringTable) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* SignatureData */ static UA_DataTypeMember SignatureData_members[2] = { { UA_TYPENAME("Algorithm") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Signature") /* .memberName */ UA_TYPES_BYTESTRING, /* .memberTypeIndex */ offsetof(UA_SignatureData, signature) - offsetof(UA_SignatureData, algorithm) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* ModifySubscriptionResponse */ static UA_DataTypeMember ModifySubscriptionResponse_members[4] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RevisedPublishingInterval") /* .memberName */ UA_TYPES_DOUBLE, /* .memberTypeIndex */ offsetof(UA_ModifySubscriptionResponse, revisedPublishingInterval) - offsetof(UA_ModifySubscriptionResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RevisedLifetimeCount") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ModifySubscriptionResponse, revisedLifetimeCount) - offsetof(UA_ModifySubscriptionResponse, revisedPublishingInterval) - sizeof(UA_Double), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RevisedMaxKeepAliveCount") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ModifySubscriptionResponse, revisedMaxKeepAliveCount) - offsetof(UA_ModifySubscriptionResponse, revisedLifetimeCount) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* NodeAttributes */ static UA_DataTypeMember NodeAttributes_members[5] = { { UA_TYPENAME("SpecifiedAttributes") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DisplayName") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_NodeAttributes, displayName) - offsetof(UA_NodeAttributes, specifiedAttributes) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Description") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_NodeAttributes, description) - offsetof(UA_NodeAttributes, displayName) - sizeof(UA_LocalizedText), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("WriteMask") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_NodeAttributes, writeMask) - offsetof(UA_NodeAttributes, description) - sizeof(UA_LocalizedText), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("UserWriteMask") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_NodeAttributes, userWriteMask) - offsetof(UA_NodeAttributes, writeMask) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* ActivateSessionResponse */ static UA_DataTypeMember ActivateSessionResponse_members[4] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ServerNonce") /* .memberName */ UA_TYPES_BYTESTRING, /* .memberTypeIndex */ offsetof(UA_ActivateSessionResponse, serverNonce) - offsetof(UA_ActivateSessionResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Results") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ offsetof(UA_ActivateSessionResponse, resultsSize) - offsetof(UA_ActivateSessionResponse, serverNonce) - sizeof(UA_ByteString), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_ActivateSessionResponse, diagnosticInfosSize) - offsetof(UA_ActivateSessionResponse, results) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* VariableTypeAttributes */ static UA_DataTypeMember VariableTypeAttributes_members[10] = { { UA_TYPENAME("SpecifiedAttributes") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DisplayName") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_VariableTypeAttributes, displayName) - offsetof(UA_VariableTypeAttributes, specifiedAttributes) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Description") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_VariableTypeAttributes, description) - offsetof(UA_VariableTypeAttributes, displayName) - sizeof(UA_LocalizedText), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("WriteMask") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_VariableTypeAttributes, writeMask) - offsetof(UA_VariableTypeAttributes, description) - sizeof(UA_LocalizedText), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("UserWriteMask") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_VariableTypeAttributes, userWriteMask) - offsetof(UA_VariableTypeAttributes, writeMask) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Value") /* .memberName */ UA_TYPES_VARIANT, /* .memberTypeIndex */ offsetof(UA_VariableTypeAttributes, value) - offsetof(UA_VariableTypeAttributes, userWriteMask) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DataType") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ offsetof(UA_VariableTypeAttributes, dataType) - offsetof(UA_VariableTypeAttributes, value) - sizeof(UA_Variant), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ValueRank") /* .memberName */ UA_TYPES_INT32, /* .memberTypeIndex */ offsetof(UA_VariableTypeAttributes, valueRank) - offsetof(UA_VariableTypeAttributes, dataType) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ArrayDimensions") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_VariableTypeAttributes, arrayDimensionsSize) - offsetof(UA_VariableTypeAttributes, valueRank) - sizeof(UA_Int32), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("IsAbstract") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_VariableTypeAttributes, isAbstract) - offsetof(UA_VariableTypeAttributes, arrayDimensions) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* CallMethodResult */ static UA_DataTypeMember CallMethodResult_members[4] = { { UA_TYPENAME("StatusCode") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("InputArgumentResults") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ offsetof(UA_CallMethodResult, inputArgumentResultsSize) - offsetof(UA_CallMethodResult, statusCode) - sizeof(UA_StatusCode), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("InputArgumentDiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_CallMethodResult, inputArgumentDiagnosticInfosSize) - offsetof(UA_CallMethodResult, inputArgumentResults) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("OutputArguments") /* .memberName */ UA_TYPES_VARIANT, /* .memberTypeIndex */ offsetof(UA_CallMethodResult, outputArgumentsSize) - offsetof(UA_CallMethodResult, inputArgumentDiagnosticInfos) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* MonitoringMode */ #define MonitoringMode_members NULL /* SetMonitoringModeResponse */ static UA_DataTypeMember SetMonitoringModeResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Results") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ offsetof(UA_SetMonitoringModeResponse, resultsSize) - offsetof(UA_SetMonitoringModeResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_SetMonitoringModeResponse, diagnosticInfosSize) - offsetof(UA_SetMonitoringModeResponse, results) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* BrowseResultMask */ #define BrowseResultMask_members NULL /* RequestHeader */ static UA_DataTypeMember RequestHeader_members[7] = { { UA_TYPENAME("AuthenticationToken") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Timestamp") /* .memberName */ UA_TYPES_DATETIME, /* .memberTypeIndex */ offsetof(UA_RequestHeader, timestamp) - offsetof(UA_RequestHeader, authenticationToken) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RequestHandle") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_RequestHeader, requestHandle) - offsetof(UA_RequestHeader, timestamp) - sizeof(UA_DateTime), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ReturnDiagnostics") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_RequestHeader, returnDiagnostics) - offsetof(UA_RequestHeader, requestHandle) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("AuditEntryId") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_RequestHeader, auditEntryId) - offsetof(UA_RequestHeader, returnDiagnostics) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("TimeoutHint") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_RequestHeader, timeoutHint) - offsetof(UA_RequestHeader, auditEntryId) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("AdditionalHeader") /* .memberName */ UA_TYPES_EXTENSIONOBJECT, /* .memberTypeIndex */ offsetof(UA_RequestHeader, additionalHeader) - offsetof(UA_RequestHeader, timeoutHint) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* MonitoredItemModifyResult */ static UA_DataTypeMember MonitoredItemModifyResult_members[4] = { { UA_TYPENAME("StatusCode") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RevisedSamplingInterval") /* .memberName */ UA_TYPES_DOUBLE, /* .memberTypeIndex */ offsetof(UA_MonitoredItemModifyResult, revisedSamplingInterval) - offsetof(UA_MonitoredItemModifyResult, statusCode) - sizeof(UA_StatusCode), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RevisedQueueSize") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_MonitoredItemModifyResult, revisedQueueSize) - offsetof(UA_MonitoredItemModifyResult, revisedSamplingInterval) - sizeof(UA_Double), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("FilterResult") /* .memberName */ UA_TYPES_EXTENSIONOBJECT, /* .memberTypeIndex */ offsetof(UA_MonitoredItemModifyResult, filterResult) - offsetof(UA_MonitoredItemModifyResult, revisedQueueSize) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* CloseSecureChannelRequest */ static UA_DataTypeMember CloseSecureChannelRequest_members[1] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* NotificationMessage */ static UA_DataTypeMember NotificationMessage_members[3] = { { UA_TYPENAME("SequenceNumber") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("PublishTime") /* .memberName */ UA_TYPES_DATETIME, /* .memberTypeIndex */ offsetof(UA_NotificationMessage, publishTime) - offsetof(UA_NotificationMessage, sequenceNumber) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("NotificationData") /* .memberName */ UA_TYPES_EXTENSIONOBJECT, /* .memberTypeIndex */ offsetof(UA_NotificationMessage, notificationDataSize) - offsetof(UA_NotificationMessage, publishTime) - sizeof(UA_DateTime), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* CreateSubscriptionResponse */ static UA_DataTypeMember CreateSubscriptionResponse_members[5] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SubscriptionId") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_CreateSubscriptionResponse, subscriptionId) - offsetof(UA_CreateSubscriptionResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RevisedPublishingInterval") /* .memberName */ UA_TYPES_DOUBLE, /* .memberTypeIndex */ offsetof(UA_CreateSubscriptionResponse, revisedPublishingInterval) - offsetof(UA_CreateSubscriptionResponse, subscriptionId) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RevisedLifetimeCount") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_CreateSubscriptionResponse, revisedLifetimeCount) - offsetof(UA_CreateSubscriptionResponse, revisedPublishingInterval) - sizeof(UA_Double), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RevisedMaxKeepAliveCount") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_CreateSubscriptionResponse, revisedMaxKeepAliveCount) - offsetof(UA_CreateSubscriptionResponse, revisedLifetimeCount) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* MdnsDiscoveryConfiguration */ static UA_DataTypeMember MdnsDiscoveryConfiguration_members[2] = { { UA_TYPENAME("MdnsServerName") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ServerCapabilities") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_MdnsDiscoveryConfiguration, serverCapabilitiesSize) - offsetof(UA_MdnsDiscoveryConfiguration, mdnsServerName) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* BrowseDirection */ #define BrowseDirection_members NULL /* CallMethodRequest */ static UA_DataTypeMember CallMethodRequest_members[3] = { { UA_TYPENAME("ObjectId") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("MethodId") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ offsetof(UA_CallMethodRequest, methodId) - offsetof(UA_CallMethodRequest, objectId) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("InputArguments") /* .memberName */ UA_TYPES_VARIANT, /* .memberTypeIndex */ offsetof(UA_CallMethodRequest, inputArgumentsSize) - offsetof(UA_CallMethodRequest, methodId) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* ReadResponse */ static UA_DataTypeMember ReadResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Results") /* .memberName */ UA_TYPES_DATAVALUE, /* .memberTypeIndex */ offsetof(UA_ReadResponse, resultsSize) - offsetof(UA_ReadResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_ReadResponse, diagnosticInfosSize) - offsetof(UA_ReadResponse, results) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* TimestampsToReturn */ #define TimestampsToReturn_members NULL /* NodeClass */ #define NodeClass_members NULL /* ObjectTypeAttributes */ static UA_DataTypeMember ObjectTypeAttributes_members[6] = { { UA_TYPENAME("SpecifiedAttributes") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DisplayName") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_ObjectTypeAttributes, displayName) - offsetof(UA_ObjectTypeAttributes, specifiedAttributes) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Description") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_ObjectTypeAttributes, description) - offsetof(UA_ObjectTypeAttributes, displayName) - sizeof(UA_LocalizedText), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("WriteMask") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ObjectTypeAttributes, writeMask) - offsetof(UA_ObjectTypeAttributes, description) - sizeof(UA_LocalizedText), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("UserWriteMask") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ObjectTypeAttributes, userWriteMask) - offsetof(UA_ObjectTypeAttributes, writeMask) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("IsAbstract") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_ObjectTypeAttributes, isAbstract) - offsetof(UA_ObjectTypeAttributes, userWriteMask) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* SecurityTokenRequestType */ #define SecurityTokenRequestType_members NULL /* CloseSessionResponse */ static UA_DataTypeMember CloseSessionResponse_members[1] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* SetPublishingModeRequest */ static UA_DataTypeMember SetPublishingModeRequest_members[3] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("PublishingEnabled") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_SetPublishingModeRequest, publishingEnabled) - offsetof(UA_SetPublishingModeRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SubscriptionIds") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_SetPublishingModeRequest, subscriptionIdsSize) - offsetof(UA_SetPublishingModeRequest, publishingEnabled) - sizeof(UA_Boolean), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* IssuedIdentityToken */ static UA_DataTypeMember IssuedIdentityToken_members[3] = { { UA_TYPENAME("PolicyId") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("TokenData") /* .memberName */ UA_TYPES_BYTESTRING, /* .memberTypeIndex */ offsetof(UA_IssuedIdentityToken, tokenData) - offsetof(UA_IssuedIdentityToken, policyId) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("EncryptionAlgorithm") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_IssuedIdentityToken, encryptionAlgorithm) - offsetof(UA_IssuedIdentityToken, tokenData) - sizeof(UA_ByteString), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* ServerOnNetwork */ static UA_DataTypeMember ServerOnNetwork_members[4] = { { UA_TYPENAME("RecordId") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ServerName") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_ServerOnNetwork, serverName) - offsetof(UA_ServerOnNetwork, recordId) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DiscoveryUrl") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_ServerOnNetwork, discoveryUrl) - offsetof(UA_ServerOnNetwork, serverName) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ServerCapabilities") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_ServerOnNetwork, serverCapabilitiesSize) - offsetof(UA_ServerOnNetwork, discoveryUrl) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* DeleteMonitoredItemsResponse */ static UA_DataTypeMember DeleteMonitoredItemsResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Results") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ offsetof(UA_DeleteMonitoredItemsResponse, resultsSize) - offsetof(UA_DeleteMonitoredItemsResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_DeleteMonitoredItemsResponse, diagnosticInfosSize) - offsetof(UA_DeleteMonitoredItemsResponse, results) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* ApplicationType */ #define ApplicationType_members NULL /* DiscoveryConfiguration */ #define DiscoveryConfiguration_members NULL /* BrowseNextRequest */ static UA_DataTypeMember BrowseNextRequest_members[3] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ReleaseContinuationPoints") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_BrowseNextRequest, releaseContinuationPoints) - offsetof(UA_BrowseNextRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ContinuationPoints") /* .memberName */ UA_TYPES_BYTESTRING, /* .memberTypeIndex */ offsetof(UA_BrowseNextRequest, continuationPointsSize) - offsetof(UA_BrowseNextRequest, releaseContinuationPoints) - sizeof(UA_Boolean), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* ModifySubscriptionRequest */ static UA_DataTypeMember ModifySubscriptionRequest_members[7] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SubscriptionId") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ModifySubscriptionRequest, subscriptionId) - offsetof(UA_ModifySubscriptionRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RequestedPublishingInterval") /* .memberName */ UA_TYPES_DOUBLE, /* .memberTypeIndex */ offsetof(UA_ModifySubscriptionRequest, requestedPublishingInterval) - offsetof(UA_ModifySubscriptionRequest, subscriptionId) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RequestedLifetimeCount") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ModifySubscriptionRequest, requestedLifetimeCount) - offsetof(UA_ModifySubscriptionRequest, requestedPublishingInterval) - sizeof(UA_Double), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RequestedMaxKeepAliveCount") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ModifySubscriptionRequest, requestedMaxKeepAliveCount) - offsetof(UA_ModifySubscriptionRequest, requestedLifetimeCount) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("MaxNotificationsPerPublish") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ModifySubscriptionRequest, maxNotificationsPerPublish) - offsetof(UA_ModifySubscriptionRequest, requestedMaxKeepAliveCount) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Priority") /* .memberName */ UA_TYPES_BYTE, /* .memberTypeIndex */ offsetof(UA_ModifySubscriptionRequest, priority) - offsetof(UA_ModifySubscriptionRequest, maxNotificationsPerPublish) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* BrowseDescription */ static UA_DataTypeMember BrowseDescription_members[6] = { { UA_TYPENAME("NodeId") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("BrowseDirection") /* .memberName */ UA_TYPES_BROWSEDIRECTION, /* .memberTypeIndex */ offsetof(UA_BrowseDescription, browseDirection) - offsetof(UA_BrowseDescription, nodeId) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ReferenceTypeId") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ offsetof(UA_BrowseDescription, referenceTypeId) - offsetof(UA_BrowseDescription, browseDirection) - sizeof(UA_BrowseDirection), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("IncludeSubtypes") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_BrowseDescription, includeSubtypes) - offsetof(UA_BrowseDescription, referenceTypeId) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("NodeClassMask") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_BrowseDescription, nodeClassMask) - offsetof(UA_BrowseDescription, includeSubtypes) - sizeof(UA_Boolean), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ResultMask") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_BrowseDescription, resultMask) - offsetof(UA_BrowseDescription, nodeClassMask) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* SignedSoftwareCertificate */ static UA_DataTypeMember SignedSoftwareCertificate_members[2] = { { UA_TYPENAME("CertificateData") /* .memberName */ UA_TYPES_BYTESTRING, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Signature") /* .memberName */ UA_TYPES_BYTESTRING, /* .memberTypeIndex */ offsetof(UA_SignedSoftwareCertificate, signature) - offsetof(UA_SignedSoftwareCertificate, certificateData) - sizeof(UA_ByteString), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* BrowsePathTarget */ static UA_DataTypeMember BrowsePathTarget_members[2] = { { UA_TYPENAME("TargetId") /* .memberName */ UA_TYPES_EXPANDEDNODEID, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RemainingPathIndex") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_BrowsePathTarget, remainingPathIndex) - offsetof(UA_BrowsePathTarget, targetId) - sizeof(UA_ExpandedNodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* WriteResponse */ static UA_DataTypeMember WriteResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Results") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ offsetof(UA_WriteResponse, resultsSize) - offsetof(UA_WriteResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_WriteResponse, diagnosticInfosSize) - offsetof(UA_WriteResponse, results) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* AddNodesResult */ static UA_DataTypeMember AddNodesResult_members[2] = { { UA_TYPENAME("StatusCode") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("AddedNodeId") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ offsetof(UA_AddNodesResult, addedNodeId) - offsetof(UA_AddNodesResult, statusCode) - sizeof(UA_StatusCode), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* RegisterServerResponse */ static UA_DataTypeMember RegisterServerResponse_members[1] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* AddReferencesItem */ static UA_DataTypeMember AddReferencesItem_members[6] = { { UA_TYPENAME("SourceNodeId") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ReferenceTypeId") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ offsetof(UA_AddReferencesItem, referenceTypeId) - offsetof(UA_AddReferencesItem, sourceNodeId) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("IsForward") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_AddReferencesItem, isForward) - offsetof(UA_AddReferencesItem, referenceTypeId) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("TargetServerUri") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_AddReferencesItem, targetServerUri) - offsetof(UA_AddReferencesItem, isForward) - sizeof(UA_Boolean), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("TargetNodeId") /* .memberName */ UA_TYPES_EXPANDEDNODEID, /* .memberTypeIndex */ offsetof(UA_AddReferencesItem, targetNodeId) - offsetof(UA_AddReferencesItem, targetServerUri) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("TargetNodeClass") /* .memberName */ UA_TYPES_NODECLASS, /* .memberTypeIndex */ offsetof(UA_AddReferencesItem, targetNodeClass) - offsetof(UA_AddReferencesItem, targetNodeId) - sizeof(UA_ExpandedNodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* RegisterServer2Response */ static UA_DataTypeMember RegisterServer2Response_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ConfigurationResults") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ offsetof(UA_RegisterServer2Response, configurationResultsSize) - offsetof(UA_RegisterServer2Response, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_RegisterServer2Response, diagnosticInfosSize) - offsetof(UA_RegisterServer2Response, configurationResults) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* DeleteReferencesResponse */ static UA_DataTypeMember DeleteReferencesResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Results") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ offsetof(UA_DeleteReferencesResponse, resultsSize) - offsetof(UA_DeleteReferencesResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_DeleteReferencesResponse, diagnosticInfosSize) - offsetof(UA_DeleteReferencesResponse, results) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* RelativePathElement */ static UA_DataTypeMember RelativePathElement_members[4] = { { UA_TYPENAME("ReferenceTypeId") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("IsInverse") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_RelativePathElement, isInverse) - offsetof(UA_RelativePathElement, referenceTypeId) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("IncludeSubtypes") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_RelativePathElement, includeSubtypes) - offsetof(UA_RelativePathElement, isInverse) - sizeof(UA_Boolean), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("TargetName") /* .memberName */ UA_TYPES_QUALIFIEDNAME, /* .memberTypeIndex */ offsetof(UA_RelativePathElement, targetName) - offsetof(UA_RelativePathElement, includeSubtypes) - sizeof(UA_Boolean), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* SubscriptionAcknowledgement */ static UA_DataTypeMember SubscriptionAcknowledgement_members[2] = { { UA_TYPENAME("SubscriptionId") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SequenceNumber") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_SubscriptionAcknowledgement, sequenceNumber) - offsetof(UA_SubscriptionAcknowledgement, subscriptionId) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* CreateMonitoredItemsResponse */ static UA_DataTypeMember CreateMonitoredItemsResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Results") /* .memberName */ UA_TYPES_MONITOREDITEMCREATERESULT, /* .memberTypeIndex */ offsetof(UA_CreateMonitoredItemsResponse, resultsSize) - offsetof(UA_CreateMonitoredItemsResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_CreateMonitoredItemsResponse, diagnosticInfosSize) - offsetof(UA_CreateMonitoredItemsResponse, results) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* DeleteReferencesItem */ static UA_DataTypeMember DeleteReferencesItem_members[5] = { { UA_TYPENAME("SourceNodeId") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ReferenceTypeId") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ offsetof(UA_DeleteReferencesItem, referenceTypeId) - offsetof(UA_DeleteReferencesItem, sourceNodeId) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("IsForward") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_DeleteReferencesItem, isForward) - offsetof(UA_DeleteReferencesItem, referenceTypeId) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("TargetNodeId") /* .memberName */ UA_TYPES_EXPANDEDNODEID, /* .memberTypeIndex */ offsetof(UA_DeleteReferencesItem, targetNodeId) - offsetof(UA_DeleteReferencesItem, isForward) - sizeof(UA_Boolean), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DeleteBidirectional") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_DeleteReferencesItem, deleteBidirectional) - offsetof(UA_DeleteReferencesItem, targetNodeId) - sizeof(UA_ExpandedNodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* WriteValue */ static UA_DataTypeMember WriteValue_members[4] = { { UA_TYPENAME("NodeId") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("AttributeId") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_WriteValue, attributeId) - offsetof(UA_WriteValue, nodeId) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("IndexRange") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_WriteValue, indexRange) - offsetof(UA_WriteValue, attributeId) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Value") /* .memberName */ UA_TYPES_DATAVALUE, /* .memberTypeIndex */ offsetof(UA_WriteValue, value) - offsetof(UA_WriteValue, indexRange) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* DataTypeAttributes */ static UA_DataTypeMember DataTypeAttributes_members[6] = { { UA_TYPENAME("SpecifiedAttributes") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DisplayName") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_DataTypeAttributes, displayName) - offsetof(UA_DataTypeAttributes, specifiedAttributes) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Description") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_DataTypeAttributes, description) - offsetof(UA_DataTypeAttributes, displayName) - sizeof(UA_LocalizedText), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("WriteMask") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_DataTypeAttributes, writeMask) - offsetof(UA_DataTypeAttributes, description) - sizeof(UA_LocalizedText), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("UserWriteMask") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_DataTypeAttributes, userWriteMask) - offsetof(UA_DataTypeAttributes, writeMask) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("IsAbstract") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_DataTypeAttributes, isAbstract) - offsetof(UA_DataTypeAttributes, userWriteMask) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* AddReferencesResponse */ static UA_DataTypeMember AddReferencesResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Results") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ offsetof(UA_AddReferencesResponse, resultsSize) - offsetof(UA_AddReferencesResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_AddReferencesResponse, diagnosticInfosSize) - offsetof(UA_AddReferencesResponse, results) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* DeadbandType */ #define DeadbandType_members NULL /* DataChangeTrigger */ #define DataChangeTrigger_members NULL /* BuildInfo */ static UA_DataTypeMember BuildInfo_members[6] = { { UA_TYPENAME("ProductUri") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ManufacturerName") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_BuildInfo, manufacturerName) - offsetof(UA_BuildInfo, productUri) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ProductName") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_BuildInfo, productName) - offsetof(UA_BuildInfo, manufacturerName) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SoftwareVersion") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_BuildInfo, softwareVersion) - offsetof(UA_BuildInfo, productName) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("BuildNumber") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_BuildInfo, buildNumber) - offsetof(UA_BuildInfo, softwareVersion) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("BuildDate") /* .memberName */ UA_TYPES_DATETIME, /* .memberTypeIndex */ offsetof(UA_BuildInfo, buildDate) - offsetof(UA_BuildInfo, buildNumber) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* FilterOperand */ #define FilterOperand_members NULL /* MonitoringParameters */ static UA_DataTypeMember MonitoringParameters_members[5] = { { UA_TYPENAME("ClientHandle") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SamplingInterval") /* .memberName */ UA_TYPES_DOUBLE, /* .memberTypeIndex */ offsetof(UA_MonitoringParameters, samplingInterval) - offsetof(UA_MonitoringParameters, clientHandle) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Filter") /* .memberName */ UA_TYPES_EXTENSIONOBJECT, /* .memberTypeIndex */ offsetof(UA_MonitoringParameters, filter) - offsetof(UA_MonitoringParameters, samplingInterval) - sizeof(UA_Double), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("QueueSize") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_MonitoringParameters, queueSize) - offsetof(UA_MonitoringParameters, filter) - sizeof(UA_ExtensionObject), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DiscardOldest") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_MonitoringParameters, discardOldest) - offsetof(UA_MonitoringParameters, queueSize) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* DeleteNodesItem */ static UA_DataTypeMember DeleteNodesItem_members[2] = { { UA_TYPENAME("NodeId") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DeleteTargetReferences") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_DeleteNodesItem, deleteTargetReferences) - offsetof(UA_DeleteNodesItem, nodeId) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* ReadValueId */ static UA_DataTypeMember ReadValueId_members[4] = { { UA_TYPENAME("NodeId") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("AttributeId") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ReadValueId, attributeId) - offsetof(UA_ReadValueId, nodeId) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("IndexRange") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_ReadValueId, indexRange) - offsetof(UA_ReadValueId, attributeId) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DataEncoding") /* .memberName */ UA_TYPES_QUALIFIEDNAME, /* .memberTypeIndex */ offsetof(UA_ReadValueId, dataEncoding) - offsetof(UA_ReadValueId, indexRange) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* CallRequest */ static UA_DataTypeMember CallRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("MethodsToCall") /* .memberName */ UA_TYPES_CALLMETHODREQUEST, /* .memberTypeIndex */ offsetof(UA_CallRequest, methodsToCallSize) - offsetof(UA_CallRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* RelativePath */ static UA_DataTypeMember RelativePath_members[1] = { { UA_TYPENAME("Elements") /* .memberName */ UA_TYPES_RELATIVEPATHELEMENT, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* DeleteNodesRequest */ static UA_DataTypeMember DeleteNodesRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("NodesToDelete") /* .memberName */ UA_TYPES_DELETENODESITEM, /* .memberTypeIndex */ offsetof(UA_DeleteNodesRequest, nodesToDeleteSize) - offsetof(UA_DeleteNodesRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* MonitoredItemModifyRequest */ static UA_DataTypeMember MonitoredItemModifyRequest_members[2] = { { UA_TYPENAME("MonitoredItemId") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RequestedParameters") /* .memberName */ UA_TYPES_MONITORINGPARAMETERS, /* .memberTypeIndex */ offsetof(UA_MonitoredItemModifyRequest, requestedParameters) - offsetof(UA_MonitoredItemModifyRequest, monitoredItemId) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* UserTokenType */ #define UserTokenType_members NULL /* AggregateConfiguration */ static UA_DataTypeMember AggregateConfiguration_members[5] = { { UA_TYPENAME("UseServerCapabilitiesDefaults") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("TreatUncertainAsBad") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_AggregateConfiguration, treatUncertainAsBad) - offsetof(UA_AggregateConfiguration, useServerCapabilitiesDefaults) - sizeof(UA_Boolean), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("PercentDataBad") /* .memberName */ UA_TYPES_BYTE, /* .memberTypeIndex */ offsetof(UA_AggregateConfiguration, percentDataBad) - offsetof(UA_AggregateConfiguration, treatUncertainAsBad) - sizeof(UA_Boolean), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("PercentDataGood") /* .memberName */ UA_TYPES_BYTE, /* .memberTypeIndex */ offsetof(UA_AggregateConfiguration, percentDataGood) - offsetof(UA_AggregateConfiguration, percentDataBad) - sizeof(UA_Byte), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("UseSlopedExtrapolation") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_AggregateConfiguration, useSlopedExtrapolation) - offsetof(UA_AggregateConfiguration, percentDataGood) - sizeof(UA_Byte), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* LocaleId */ #define LocaleId_members NULL /* UnregisterNodesResponse */ static UA_DataTypeMember UnregisterNodesResponse_members[1] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* ContentFilterResult */ static UA_DataTypeMember ContentFilterResult_members[2] = { { UA_TYPENAME("ElementResults") /* .memberName */ UA_TYPES_CONTENTFILTERELEMENTRESULT, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("ElementDiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_ContentFilterResult, elementDiagnosticInfosSize) - offsetof(UA_ContentFilterResult, elementResults) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* UserTokenPolicy */ static UA_DataTypeMember UserTokenPolicy_members[5] = { { UA_TYPENAME("PolicyId") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("TokenType") /* .memberName */ UA_TYPES_USERTOKENTYPE, /* .memberTypeIndex */ offsetof(UA_UserTokenPolicy, tokenType) - offsetof(UA_UserTokenPolicy, policyId) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("IssuedTokenType") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_UserTokenPolicy, issuedTokenType) - offsetof(UA_UserTokenPolicy, tokenType) - sizeof(UA_UserTokenType), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("IssuerEndpointUrl") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_UserTokenPolicy, issuerEndpointUrl) - offsetof(UA_UserTokenPolicy, issuedTokenType) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SecurityPolicyUri") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_UserTokenPolicy, securityPolicyUri) - offsetof(UA_UserTokenPolicy, issuerEndpointUrl) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* DeleteMonitoredItemsRequest */ static UA_DataTypeMember DeleteMonitoredItemsRequest_members[3] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SubscriptionId") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_DeleteMonitoredItemsRequest, subscriptionId) - offsetof(UA_DeleteMonitoredItemsRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("MonitoredItemIds") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_DeleteMonitoredItemsRequest, monitoredItemIdsSize) - offsetof(UA_DeleteMonitoredItemsRequest, subscriptionId) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* SetMonitoringModeRequest */ static UA_DataTypeMember SetMonitoringModeRequest_members[4] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SubscriptionId") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_SetMonitoringModeRequest, subscriptionId) - offsetof(UA_SetMonitoringModeRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("MonitoringMode") /* .memberName */ UA_TYPES_MONITORINGMODE, /* .memberTypeIndex */ offsetof(UA_SetMonitoringModeRequest, monitoringMode) - offsetof(UA_SetMonitoringModeRequest, subscriptionId) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("MonitoredItemIds") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_SetMonitoringModeRequest, monitoredItemIdsSize) - offsetof(UA_SetMonitoringModeRequest, monitoringMode) - sizeof(UA_MonitoringMode), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* Duration */ #define Duration_members NULL /* ReferenceTypeAttributes */ static UA_DataTypeMember ReferenceTypeAttributes_members[8] = { { UA_TYPENAME("SpecifiedAttributes") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DisplayName") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_ReferenceTypeAttributes, displayName) - offsetof(UA_ReferenceTypeAttributes, specifiedAttributes) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Description") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_ReferenceTypeAttributes, description) - offsetof(UA_ReferenceTypeAttributes, displayName) - sizeof(UA_LocalizedText), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("WriteMask") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ReferenceTypeAttributes, writeMask) - offsetof(UA_ReferenceTypeAttributes, description) - sizeof(UA_LocalizedText), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("UserWriteMask") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ReferenceTypeAttributes, userWriteMask) - offsetof(UA_ReferenceTypeAttributes, writeMask) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("IsAbstract") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_ReferenceTypeAttributes, isAbstract) - offsetof(UA_ReferenceTypeAttributes, userWriteMask) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Symmetric") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_ReferenceTypeAttributes, symmetric) - offsetof(UA_ReferenceTypeAttributes, isAbstract) - sizeof(UA_Boolean), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("InverseName") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_ReferenceTypeAttributes, inverseName) - offsetof(UA_ReferenceTypeAttributes, symmetric) - sizeof(UA_Boolean), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* GetEndpointsRequest */ static UA_DataTypeMember GetEndpointsRequest_members[4] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("EndpointUrl") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_GetEndpointsRequest, endpointUrl) - offsetof(UA_GetEndpointsRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("LocaleIds") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_GetEndpointsRequest, localeIdsSize) - offsetof(UA_GetEndpointsRequest, endpointUrl) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("ProfileUris") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_GetEndpointsRequest, profileUrisSize) - offsetof(UA_GetEndpointsRequest, localeIds) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* CloseSecureChannelResponse */ static UA_DataTypeMember CloseSecureChannelResponse_members[1] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* ViewDescription */ static UA_DataTypeMember ViewDescription_members[3] = { { UA_TYPENAME("ViewId") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Timestamp") /* .memberName */ UA_TYPES_DATETIME, /* .memberTypeIndex */ offsetof(UA_ViewDescription, timestamp) - offsetof(UA_ViewDescription, viewId) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ViewVersion") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ViewDescription, viewVersion) - offsetof(UA_ViewDescription, timestamp) - sizeof(UA_DateTime), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* SetPublishingModeResponse */ static UA_DataTypeMember SetPublishingModeResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Results") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ offsetof(UA_SetPublishingModeResponse, resultsSize) - offsetof(UA_SetPublishingModeResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_SetPublishingModeResponse, diagnosticInfosSize) - offsetof(UA_SetPublishingModeResponse, results) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* StatusChangeNotification */ static UA_DataTypeMember StatusChangeNotification_members[2] = { { UA_TYPENAME("Status") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DiagnosticInfo") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_StatusChangeNotification, diagnosticInfo) - offsetof(UA_StatusChangeNotification, status) - sizeof(UA_StatusCode), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* NodeAttributesMask */ #define NodeAttributesMask_members NULL /* EventFilterResult */ static UA_DataTypeMember EventFilterResult_members[3] = { { UA_TYPENAME("SelectClauseResults") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("SelectClauseDiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_EventFilterResult, selectClauseDiagnosticInfosSize) - offsetof(UA_EventFilterResult, selectClauseResults) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("WhereClauseResult") /* .memberName */ UA_TYPES_CONTENTFILTERRESULT, /* .memberTypeIndex */ offsetof(UA_EventFilterResult, whereClauseResult) - offsetof(UA_EventFilterResult, selectClauseDiagnosticInfos) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* MonitoredItemCreateRequest */ static UA_DataTypeMember MonitoredItemCreateRequest_members[3] = { { UA_TYPENAME("ItemToMonitor") /* .memberName */ UA_TYPES_READVALUEID, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("MonitoringMode") /* .memberName */ UA_TYPES_MONITORINGMODE, /* .memberTypeIndex */ offsetof(UA_MonitoredItemCreateRequest, monitoringMode) - offsetof(UA_MonitoredItemCreateRequest, itemToMonitor) - sizeof(UA_ReadValueId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RequestedParameters") /* .memberName */ UA_TYPES_MONITORINGPARAMETERS, /* .memberTypeIndex */ offsetof(UA_MonitoredItemCreateRequest, requestedParameters) - offsetof(UA_MonitoredItemCreateRequest, monitoringMode) - sizeof(UA_MonitoringMode), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* Range */ static UA_DataTypeMember Range_members[2] = { { UA_TYPENAME("Low") /* .memberName */ UA_TYPES_DOUBLE, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("High") /* .memberName */ UA_TYPES_DOUBLE, /* .memberTypeIndex */ offsetof(UA_Range, high) - offsetof(UA_Range, low) - sizeof(UA_Double), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* DataChangeNotification */ static UA_DataTypeMember DataChangeNotification_members[2] = { { UA_TYPENAME("MonitoredItems") /* .memberName */ UA_TYPES_MONITOREDITEMNOTIFICATION, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_DataChangeNotification, diagnosticInfosSize) - offsetof(UA_DataChangeNotification, monitoredItems) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* Argument */ static UA_DataTypeMember Argument_members[5] = { { UA_TYPENAME("Name") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DataType") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ offsetof(UA_Argument, dataType) - offsetof(UA_Argument, name) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ValueRank") /* .memberName */ UA_TYPES_INT32, /* .memberTypeIndex */ offsetof(UA_Argument, valueRank) - offsetof(UA_Argument, dataType) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ArrayDimensions") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_Argument, arrayDimensionsSize) - offsetof(UA_Argument, valueRank) - sizeof(UA_Int32), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("Description") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_Argument, description) - offsetof(UA_Argument, arrayDimensions) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* ChannelSecurityToken */ static UA_DataTypeMember ChannelSecurityToken_members[4] = { { UA_TYPENAME("ChannelId") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("TokenId") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ChannelSecurityToken, tokenId) - offsetof(UA_ChannelSecurityToken, channelId) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("CreatedAt") /* .memberName */ UA_TYPES_DATETIME, /* .memberTypeIndex */ offsetof(UA_ChannelSecurityToken, createdAt) - offsetof(UA_ChannelSecurityToken, tokenId) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RevisedLifetime") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ChannelSecurityToken, revisedLifetime) - offsetof(UA_ChannelSecurityToken, createdAt) - sizeof(UA_DateTime), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* ServerState */ #define ServerState_members NULL /* EventNotificationList */ static UA_DataTypeMember EventNotificationList_members[1] = { { UA_TYPENAME("Events") /* .memberName */ UA_TYPES_EVENTFIELDLIST, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* AnonymousIdentityToken */ static UA_DataTypeMember AnonymousIdentityToken_members[1] = { { UA_TYPENAME("PolicyId") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* FilterOperator */ #define FilterOperator_members NULL /* AggregateFilter */ static UA_DataTypeMember AggregateFilter_members[4] = { { UA_TYPENAME("StartTime") /* .memberName */ UA_TYPES_DATETIME, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("AggregateType") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ offsetof(UA_AggregateFilter, aggregateType) - offsetof(UA_AggregateFilter, startTime) - sizeof(UA_DateTime), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ProcessingInterval") /* .memberName */ UA_TYPES_DOUBLE, /* .memberTypeIndex */ offsetof(UA_AggregateFilter, processingInterval) - offsetof(UA_AggregateFilter, aggregateType) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("AggregateConfiguration") /* .memberName */ UA_TYPES_AGGREGATECONFIGURATION, /* .memberTypeIndex */ offsetof(UA_AggregateFilter, aggregateConfiguration) - offsetof(UA_AggregateFilter, processingInterval) - sizeof(UA_Double), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* RepublishResponse */ static UA_DataTypeMember RepublishResponse_members[2] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("NotificationMessage") /* .memberName */ UA_TYPES_NOTIFICATIONMESSAGE, /* .memberTypeIndex */ offsetof(UA_RepublishResponse, notificationMessage) - offsetof(UA_RepublishResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* DeleteSubscriptionsResponse */ static UA_DataTypeMember DeleteSubscriptionsResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Results") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ offsetof(UA_DeleteSubscriptionsResponse, resultsSize) - offsetof(UA_DeleteSubscriptionsResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_DeleteSubscriptionsResponse, diagnosticInfosSize) - offsetof(UA_DeleteSubscriptionsResponse, results) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* RegisterNodesRequest */ static UA_DataTypeMember RegisterNodesRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("NodesToRegister") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ offsetof(UA_RegisterNodesRequest, nodesToRegisterSize) - offsetof(UA_RegisterNodesRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* MethodAttributes */ static UA_DataTypeMember MethodAttributes_members[7] = { { UA_TYPENAME("SpecifiedAttributes") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DisplayName") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_MethodAttributes, displayName) - offsetof(UA_MethodAttributes, specifiedAttributes) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Description") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_MethodAttributes, description) - offsetof(UA_MethodAttributes, displayName) - sizeof(UA_LocalizedText), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("WriteMask") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_MethodAttributes, writeMask) - offsetof(UA_MethodAttributes, description) - sizeof(UA_LocalizedText), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("UserWriteMask") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_MethodAttributes, userWriteMask) - offsetof(UA_MethodAttributes, writeMask) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Executable") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_MethodAttributes, executable) - offsetof(UA_MethodAttributes, userWriteMask) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("UserExecutable") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_MethodAttributes, userExecutable) - offsetof(UA_MethodAttributes, executable) - sizeof(UA_Boolean), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* UserNameIdentityToken */ static UA_DataTypeMember UserNameIdentityToken_members[4] = { { UA_TYPENAME("PolicyId") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("UserName") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_UserNameIdentityToken, userName) - offsetof(UA_UserNameIdentityToken, policyId) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Password") /* .memberName */ UA_TYPES_BYTESTRING, /* .memberTypeIndex */ offsetof(UA_UserNameIdentityToken, password) - offsetof(UA_UserNameIdentityToken, userName) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("EncryptionAlgorithm") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_UserNameIdentityToken, encryptionAlgorithm) - offsetof(UA_UserNameIdentityToken, password) - sizeof(UA_ByteString), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* UnregisterNodesRequest */ static UA_DataTypeMember UnregisterNodesRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("NodesToUnregister") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ offsetof(UA_UnregisterNodesRequest, nodesToUnregisterSize) - offsetof(UA_UnregisterNodesRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* OpenSecureChannelResponse */ static UA_DataTypeMember OpenSecureChannelResponse_members[4] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ServerProtocolVersion") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_OpenSecureChannelResponse, serverProtocolVersion) - offsetof(UA_OpenSecureChannelResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SecurityToken") /* .memberName */ UA_TYPES_CHANNELSECURITYTOKEN, /* .memberTypeIndex */ offsetof(UA_OpenSecureChannelResponse, securityToken) - offsetof(UA_OpenSecureChannelResponse, serverProtocolVersion) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ServerNonce") /* .memberName */ UA_TYPES_BYTESTRING, /* .memberTypeIndex */ offsetof(UA_OpenSecureChannelResponse, serverNonce) - offsetof(UA_OpenSecureChannelResponse, securityToken) - sizeof(UA_ChannelSecurityToken), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* SetTriggeringResponse */ static UA_DataTypeMember SetTriggeringResponse_members[5] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("AddResults") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ offsetof(UA_SetTriggeringResponse, addResultsSize) - offsetof(UA_SetTriggeringResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("AddDiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_SetTriggeringResponse, addDiagnosticInfosSize) - offsetof(UA_SetTriggeringResponse, addResults) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("RemoveResults") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ offsetof(UA_SetTriggeringResponse, removeResultsSize) - offsetof(UA_SetTriggeringResponse, addDiagnosticInfos) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("RemoveDiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_SetTriggeringResponse, removeDiagnosticInfosSize) - offsetof(UA_SetTriggeringResponse, removeResults) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* SimpleAttributeOperand */ static UA_DataTypeMember SimpleAttributeOperand_members[4] = { { UA_TYPENAME("TypeDefinitionId") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("BrowsePath") /* .memberName */ UA_TYPES_QUALIFIEDNAME, /* .memberTypeIndex */ offsetof(UA_SimpleAttributeOperand, browsePathSize) - offsetof(UA_SimpleAttributeOperand, typeDefinitionId) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("AttributeId") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_SimpleAttributeOperand, attributeId) - offsetof(UA_SimpleAttributeOperand, browsePath) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("IndexRange") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_SimpleAttributeOperand, indexRange) - offsetof(UA_SimpleAttributeOperand, attributeId) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* RepublishRequest */ static UA_DataTypeMember RepublishRequest_members[3] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SubscriptionId") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_RepublishRequest, subscriptionId) - offsetof(UA_RepublishRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RetransmitSequenceNumber") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_RepublishRequest, retransmitSequenceNumber) - offsetof(UA_RepublishRequest, subscriptionId) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* RegisterNodesResponse */ static UA_DataTypeMember RegisterNodesResponse_members[2] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RegisteredNodeIds") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ offsetof(UA_RegisterNodesResponse, registeredNodeIdsSize) - offsetof(UA_RegisterNodesResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* ModifyMonitoredItemsResponse */ static UA_DataTypeMember ModifyMonitoredItemsResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Results") /* .memberName */ UA_TYPES_MONITOREDITEMMODIFYRESULT, /* .memberTypeIndex */ offsetof(UA_ModifyMonitoredItemsResponse, resultsSize) - offsetof(UA_ModifyMonitoredItemsResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_ModifyMonitoredItemsResponse, diagnosticInfosSize) - offsetof(UA_ModifyMonitoredItemsResponse, results) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* DeleteSubscriptionsRequest */ static UA_DataTypeMember DeleteSubscriptionsRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SubscriptionIds") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_DeleteSubscriptionsRequest, subscriptionIdsSize) - offsetof(UA_DeleteSubscriptionsRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* RedundancySupport */ #define RedundancySupport_members NULL /* BrowsePath */ static UA_DataTypeMember BrowsePath_members[2] = { { UA_TYPENAME("StartingNode") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RelativePath") /* .memberName */ UA_TYPES_RELATIVEPATH, /* .memberTypeIndex */ offsetof(UA_BrowsePath, relativePath) - offsetof(UA_BrowsePath, startingNode) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* ObjectAttributes */ static UA_DataTypeMember ObjectAttributes_members[6] = { { UA_TYPENAME("SpecifiedAttributes") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DisplayName") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_ObjectAttributes, displayName) - offsetof(UA_ObjectAttributes, specifiedAttributes) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Description") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_ObjectAttributes, description) - offsetof(UA_ObjectAttributes, displayName) - sizeof(UA_LocalizedText), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("WriteMask") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ObjectAttributes, writeMask) - offsetof(UA_ObjectAttributes, description) - sizeof(UA_LocalizedText), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("UserWriteMask") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ObjectAttributes, userWriteMask) - offsetof(UA_ObjectAttributes, writeMask) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("EventNotifier") /* .memberName */ UA_TYPES_BYTE, /* .memberTypeIndex */ offsetof(UA_ObjectAttributes, eventNotifier) - offsetof(UA_ObjectAttributes, userWriteMask) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* PublishRequest */ static UA_DataTypeMember PublishRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SubscriptionAcknowledgements") /* .memberName */ UA_TYPES_SUBSCRIPTIONACKNOWLEDGEMENT, /* .memberTypeIndex */ offsetof(UA_PublishRequest, subscriptionAcknowledgementsSize) - offsetof(UA_PublishRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* FindServersRequest */ static UA_DataTypeMember FindServersRequest_members[4] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("EndpointUrl") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_FindServersRequest, endpointUrl) - offsetof(UA_FindServersRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("LocaleIds") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_FindServersRequest, localeIdsSize) - offsetof(UA_FindServersRequest, endpointUrl) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("ServerUris") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_FindServersRequest, serverUrisSize) - offsetof(UA_FindServersRequest, localeIds) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* FindServersOnNetworkResponse */ static UA_DataTypeMember FindServersOnNetworkResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("LastCounterResetTime") /* .memberName */ UA_TYPES_DATETIME, /* .memberTypeIndex */ offsetof(UA_FindServersOnNetworkResponse, lastCounterResetTime) - offsetof(UA_FindServersOnNetworkResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Servers") /* .memberName */ UA_TYPES_SERVERONNETWORK, /* .memberTypeIndex */ offsetof(UA_FindServersOnNetworkResponse, serversSize) - offsetof(UA_FindServersOnNetworkResponse, lastCounterResetTime) - sizeof(UA_DateTime), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* ReferenceDescription */ static UA_DataTypeMember ReferenceDescription_members[7] = { { UA_TYPENAME("ReferenceTypeId") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("IsForward") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_ReferenceDescription, isForward) - offsetof(UA_ReferenceDescription, referenceTypeId) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("NodeId") /* .memberName */ UA_TYPES_EXPANDEDNODEID, /* .memberTypeIndex */ offsetof(UA_ReferenceDescription, nodeId) - offsetof(UA_ReferenceDescription, isForward) - sizeof(UA_Boolean), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("BrowseName") /* .memberName */ UA_TYPES_QUALIFIEDNAME, /* .memberTypeIndex */ offsetof(UA_ReferenceDescription, browseName) - offsetof(UA_ReferenceDescription, nodeId) - sizeof(UA_ExpandedNodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DisplayName") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_ReferenceDescription, displayName) - offsetof(UA_ReferenceDescription, browseName) - sizeof(UA_QualifiedName), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("NodeClass") /* .memberName */ UA_TYPES_NODECLASS, /* .memberTypeIndex */ offsetof(UA_ReferenceDescription, nodeClass) - offsetof(UA_ReferenceDescription, displayName) - sizeof(UA_LocalizedText), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("TypeDefinition") /* .memberName */ UA_TYPES_EXPANDEDNODEID, /* .memberTypeIndex */ offsetof(UA_ReferenceDescription, typeDefinition) - offsetof(UA_ReferenceDescription, nodeClass) - sizeof(UA_NodeClass), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* CreateSubscriptionRequest */ static UA_DataTypeMember CreateSubscriptionRequest_members[7] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RequestedPublishingInterval") /* .memberName */ UA_TYPES_DOUBLE, /* .memberTypeIndex */ offsetof(UA_CreateSubscriptionRequest, requestedPublishingInterval) - offsetof(UA_CreateSubscriptionRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RequestedLifetimeCount") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_CreateSubscriptionRequest, requestedLifetimeCount) - offsetof(UA_CreateSubscriptionRequest, requestedPublishingInterval) - sizeof(UA_Double), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RequestedMaxKeepAliveCount") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_CreateSubscriptionRequest, requestedMaxKeepAliveCount) - offsetof(UA_CreateSubscriptionRequest, requestedLifetimeCount) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("MaxNotificationsPerPublish") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_CreateSubscriptionRequest, maxNotificationsPerPublish) - offsetof(UA_CreateSubscriptionRequest, requestedMaxKeepAliveCount) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("PublishingEnabled") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_CreateSubscriptionRequest, publishingEnabled) - offsetof(UA_CreateSubscriptionRequest, maxNotificationsPerPublish) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Priority") /* .memberName */ UA_TYPES_BYTE, /* .memberTypeIndex */ offsetof(UA_CreateSubscriptionRequest, priority) - offsetof(UA_CreateSubscriptionRequest, publishingEnabled) - sizeof(UA_Boolean), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* FindServersOnNetworkRequest */ static UA_DataTypeMember FindServersOnNetworkRequest_members[4] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("StartingRecordId") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_FindServersOnNetworkRequest, startingRecordId) - offsetof(UA_FindServersOnNetworkRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("MaxRecordsToReturn") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_FindServersOnNetworkRequest, maxRecordsToReturn) - offsetof(UA_FindServersOnNetworkRequest, startingRecordId) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ServerCapabilityFilter") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_FindServersOnNetworkRequest, serverCapabilityFilterSize) - offsetof(UA_FindServersOnNetworkRequest, maxRecordsToReturn) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* CallResponse */ static UA_DataTypeMember CallResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Results") /* .memberName */ UA_TYPES_CALLMETHODRESULT, /* .memberTypeIndex */ offsetof(UA_CallResponse, resultsSize) - offsetof(UA_CallResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_CallResponse, diagnosticInfosSize) - offsetof(UA_CallResponse, results) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* DeleteNodesResponse */ static UA_DataTypeMember DeleteNodesResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Results") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ offsetof(UA_DeleteNodesResponse, resultsSize) - offsetof(UA_DeleteNodesResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_DeleteNodesResponse, diagnosticInfosSize) - offsetof(UA_DeleteNodesResponse, results) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* ModifyMonitoredItemsRequest */ static UA_DataTypeMember ModifyMonitoredItemsRequest_members[4] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SubscriptionId") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ModifyMonitoredItemsRequest, subscriptionId) - offsetof(UA_ModifyMonitoredItemsRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("TimestampsToReturn") /* .memberName */ UA_TYPES_TIMESTAMPSTORETURN, /* .memberTypeIndex */ offsetof(UA_ModifyMonitoredItemsRequest, timestampsToReturn) - offsetof(UA_ModifyMonitoredItemsRequest, subscriptionId) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ItemsToModify") /* .memberName */ UA_TYPES_MONITOREDITEMMODIFYREQUEST, /* .memberTypeIndex */ offsetof(UA_ModifyMonitoredItemsRequest, itemsToModifySize) - offsetof(UA_ModifyMonitoredItemsRequest, timestampsToReturn) - sizeof(UA_TimestampsToReturn), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* ServiceFault */ static UA_DataTypeMember ServiceFault_members[1] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* PublishResponse */ static UA_DataTypeMember PublishResponse_members[7] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SubscriptionId") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_PublishResponse, subscriptionId) - offsetof(UA_PublishResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("AvailableSequenceNumbers") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_PublishResponse, availableSequenceNumbersSize) - offsetof(UA_PublishResponse, subscriptionId) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("MoreNotifications") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_PublishResponse, moreNotifications) - offsetof(UA_PublishResponse, availableSequenceNumbers) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("NotificationMessage") /* .memberName */ UA_TYPES_NOTIFICATIONMESSAGE, /* .memberTypeIndex */ offsetof(UA_PublishResponse, notificationMessage) - offsetof(UA_PublishResponse, moreNotifications) - sizeof(UA_Boolean), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Results") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ offsetof(UA_PublishResponse, resultsSize) - offsetof(UA_PublishResponse, notificationMessage) - sizeof(UA_NotificationMessage), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_PublishResponse, diagnosticInfosSize) - offsetof(UA_PublishResponse, results) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* CreateMonitoredItemsRequest */ static UA_DataTypeMember CreateMonitoredItemsRequest_members[4] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SubscriptionId") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_CreateMonitoredItemsRequest, subscriptionId) - offsetof(UA_CreateMonitoredItemsRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("TimestampsToReturn") /* .memberName */ UA_TYPES_TIMESTAMPSTORETURN, /* .memberTypeIndex */ offsetof(UA_CreateMonitoredItemsRequest, timestampsToReturn) - offsetof(UA_CreateMonitoredItemsRequest, subscriptionId) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ItemsToCreate") /* .memberName */ UA_TYPES_MONITOREDITEMCREATEREQUEST, /* .memberTypeIndex */ offsetof(UA_CreateMonitoredItemsRequest, itemsToCreateSize) - offsetof(UA_CreateMonitoredItemsRequest, timestampsToReturn) - sizeof(UA_TimestampsToReturn), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* OpenSecureChannelRequest */ static UA_DataTypeMember OpenSecureChannelRequest_members[6] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ClientProtocolVersion") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_OpenSecureChannelRequest, clientProtocolVersion) - offsetof(UA_OpenSecureChannelRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RequestType") /* .memberName */ UA_TYPES_SECURITYTOKENREQUESTTYPE, /* .memberTypeIndex */ offsetof(UA_OpenSecureChannelRequest, requestType) - offsetof(UA_OpenSecureChannelRequest, clientProtocolVersion) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SecurityMode") /* .memberName */ UA_TYPES_MESSAGESECURITYMODE, /* .memberTypeIndex */ offsetof(UA_OpenSecureChannelRequest, securityMode) - offsetof(UA_OpenSecureChannelRequest, requestType) - sizeof(UA_SecurityTokenRequestType), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ClientNonce") /* .memberName */ UA_TYPES_BYTESTRING, /* .memberTypeIndex */ offsetof(UA_OpenSecureChannelRequest, clientNonce) - offsetof(UA_OpenSecureChannelRequest, securityMode) - sizeof(UA_MessageSecurityMode), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RequestedLifetime") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_OpenSecureChannelRequest, requestedLifetime) - offsetof(UA_OpenSecureChannelRequest, clientNonce) - sizeof(UA_ByteString), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* CloseSessionRequest */ static UA_DataTypeMember CloseSessionRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DeleteSubscriptions") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_CloseSessionRequest, deleteSubscriptions) - offsetof(UA_CloseSessionRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* SetTriggeringRequest */ static UA_DataTypeMember SetTriggeringRequest_members[5] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SubscriptionId") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_SetTriggeringRequest, subscriptionId) - offsetof(UA_SetTriggeringRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("TriggeringItemId") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_SetTriggeringRequest, triggeringItemId) - offsetof(UA_SetTriggeringRequest, subscriptionId) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("LinksToAdd") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_SetTriggeringRequest, linksToAddSize) - offsetof(UA_SetTriggeringRequest, triggeringItemId) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("LinksToRemove") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_SetTriggeringRequest, linksToRemoveSize) - offsetof(UA_SetTriggeringRequest, linksToAdd) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* BrowseResult */ static UA_DataTypeMember BrowseResult_members[3] = { { UA_TYPENAME("StatusCode") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ContinuationPoint") /* .memberName */ UA_TYPES_BYTESTRING, /* .memberTypeIndex */ offsetof(UA_BrowseResult, continuationPoint) - offsetof(UA_BrowseResult, statusCode) - sizeof(UA_StatusCode), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("References") /* .memberName */ UA_TYPES_REFERENCEDESCRIPTION, /* .memberTypeIndex */ offsetof(UA_BrowseResult, referencesSize) - offsetof(UA_BrowseResult, continuationPoint) - sizeof(UA_ByteString), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* AddReferencesRequest */ static UA_DataTypeMember AddReferencesRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ReferencesToAdd") /* .memberName */ UA_TYPES_ADDREFERENCESITEM, /* .memberTypeIndex */ offsetof(UA_AddReferencesRequest, referencesToAddSize) - offsetof(UA_AddReferencesRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* AddNodesItem */ static UA_DataTypeMember AddNodesItem_members[7] = { { UA_TYPENAME("ParentNodeId") /* .memberName */ UA_TYPES_EXPANDEDNODEID, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ReferenceTypeId") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ offsetof(UA_AddNodesItem, referenceTypeId) - offsetof(UA_AddNodesItem, parentNodeId) - sizeof(UA_ExpandedNodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RequestedNewNodeId") /* .memberName */ UA_TYPES_EXPANDEDNODEID, /* .memberTypeIndex */ offsetof(UA_AddNodesItem, requestedNewNodeId) - offsetof(UA_AddNodesItem, referenceTypeId) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("BrowseName") /* .memberName */ UA_TYPES_QUALIFIEDNAME, /* .memberTypeIndex */ offsetof(UA_AddNodesItem, browseName) - offsetof(UA_AddNodesItem, requestedNewNodeId) - sizeof(UA_ExpandedNodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("NodeClass") /* .memberName */ UA_TYPES_NODECLASS, /* .memberTypeIndex */ offsetof(UA_AddNodesItem, nodeClass) - offsetof(UA_AddNodesItem, browseName) - sizeof(UA_QualifiedName), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("NodeAttributes") /* .memberName */ UA_TYPES_EXTENSIONOBJECT, /* .memberTypeIndex */ offsetof(UA_AddNodesItem, nodeAttributes) - offsetof(UA_AddNodesItem, nodeClass) - sizeof(UA_NodeClass), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("TypeDefinition") /* .memberName */ UA_TYPES_EXPANDEDNODEID, /* .memberTypeIndex */ offsetof(UA_AddNodesItem, typeDefinition) - offsetof(UA_AddNodesItem, nodeAttributes) - sizeof(UA_ExtensionObject), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* ServerStatusDataType */ static UA_DataTypeMember ServerStatusDataType_members[6] = { { UA_TYPENAME("StartTime") /* .memberName */ UA_TYPES_DATETIME, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("CurrentTime") /* .memberName */ UA_TYPES_DATETIME, /* .memberTypeIndex */ offsetof(UA_ServerStatusDataType, currentTime) - offsetof(UA_ServerStatusDataType, startTime) - sizeof(UA_DateTime), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("State") /* .memberName */ UA_TYPES_SERVERSTATE, /* .memberTypeIndex */ offsetof(UA_ServerStatusDataType, state) - offsetof(UA_ServerStatusDataType, currentTime) - sizeof(UA_DateTime), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("BuildInfo") /* .memberName */ UA_TYPES_BUILDINFO, /* .memberTypeIndex */ offsetof(UA_ServerStatusDataType, buildInfo) - offsetof(UA_ServerStatusDataType, state) - sizeof(UA_ServerState), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SecondsTillShutdown") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_ServerStatusDataType, secondsTillShutdown) - offsetof(UA_ServerStatusDataType, buildInfo) - sizeof(UA_BuildInfo), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ShutdownReason") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_ServerStatusDataType, shutdownReason) - offsetof(UA_ServerStatusDataType, secondsTillShutdown) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* BrowseNextResponse */ static UA_DataTypeMember BrowseNextResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Results") /* .memberName */ UA_TYPES_BROWSERESULT, /* .memberTypeIndex */ offsetof(UA_BrowseNextResponse, resultsSize) - offsetof(UA_BrowseNextResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_BrowseNextResponse, diagnosticInfosSize) - offsetof(UA_BrowseNextResponse, results) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* RegisteredServer */ static UA_DataTypeMember RegisteredServer_members[8] = { { UA_TYPENAME("ServerUri") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ProductUri") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_RegisteredServer, productUri) - offsetof(UA_RegisteredServer, serverUri) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ServerNames") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_RegisteredServer, serverNamesSize) - offsetof(UA_RegisteredServer, productUri) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("ServerType") /* .memberName */ UA_TYPES_APPLICATIONTYPE, /* .memberTypeIndex */ offsetof(UA_RegisteredServer, serverType) - offsetof(UA_RegisteredServer, serverNames) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("GatewayServerUri") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_RegisteredServer, gatewayServerUri) - offsetof(UA_RegisteredServer, serverType) - sizeof(UA_ApplicationType), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DiscoveryUrls") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_RegisteredServer, discoveryUrlsSize) - offsetof(UA_RegisteredServer, gatewayServerUri) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("SemaphoreFilePath") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_RegisteredServer, semaphoreFilePath) - offsetof(UA_RegisteredServer, discoveryUrls) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("IsOnline") /* .memberName */ UA_TYPES_BOOLEAN, /* .memberTypeIndex */ offsetof(UA_RegisteredServer, isOnline) - offsetof(UA_RegisteredServer, semaphoreFilePath) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* ApplicationDescription */ static UA_DataTypeMember ApplicationDescription_members[7] = { { UA_TYPENAME("ApplicationUri") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ProductUri") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_ApplicationDescription, productUri) - offsetof(UA_ApplicationDescription, applicationUri) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ApplicationName") /* .memberName */ UA_TYPES_LOCALIZEDTEXT, /* .memberTypeIndex */ offsetof(UA_ApplicationDescription, applicationName) - offsetof(UA_ApplicationDescription, productUri) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ApplicationType") /* .memberName */ UA_TYPES_APPLICATIONTYPE, /* .memberTypeIndex */ offsetof(UA_ApplicationDescription, applicationType) - offsetof(UA_ApplicationDescription, applicationName) - sizeof(UA_LocalizedText), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("GatewayServerUri") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_ApplicationDescription, gatewayServerUri) - offsetof(UA_ApplicationDescription, applicationType) - sizeof(UA_ApplicationType), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DiscoveryProfileUri") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_ApplicationDescription, discoveryProfileUri) - offsetof(UA_ApplicationDescription, gatewayServerUri) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DiscoveryUrls") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_ApplicationDescription, discoveryUrlsSize) - offsetof(UA_ApplicationDescription, discoveryProfileUri) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* ReadRequest */ static UA_DataTypeMember ReadRequest_members[4] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("MaxAge") /* .memberName */ UA_TYPES_DOUBLE, /* .memberTypeIndex */ offsetof(UA_ReadRequest, maxAge) - offsetof(UA_ReadRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("TimestampsToReturn") /* .memberName */ UA_TYPES_TIMESTAMPSTORETURN, /* .memberTypeIndex */ offsetof(UA_ReadRequest, timestampsToReturn) - offsetof(UA_ReadRequest, maxAge) - sizeof(UA_Double), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("NodesToRead") /* .memberName */ UA_TYPES_READVALUEID, /* .memberTypeIndex */ offsetof(UA_ReadRequest, nodesToReadSize) - offsetof(UA_ReadRequest, timestampsToReturn) - sizeof(UA_TimestampsToReturn), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* ActivateSessionRequest */ static UA_DataTypeMember ActivateSessionRequest_members[6] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ClientSignature") /* .memberName */ UA_TYPES_SIGNATUREDATA, /* .memberTypeIndex */ offsetof(UA_ActivateSessionRequest, clientSignature) - offsetof(UA_ActivateSessionRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ClientSoftwareCertificates") /* .memberName */ UA_TYPES_SIGNEDSOFTWARECERTIFICATE, /* .memberTypeIndex */ offsetof(UA_ActivateSessionRequest, clientSoftwareCertificatesSize) - offsetof(UA_ActivateSessionRequest, clientSignature) - sizeof(UA_SignatureData), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("LocaleIds") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_ActivateSessionRequest, localeIdsSize) - offsetof(UA_ActivateSessionRequest, clientSoftwareCertificates) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("UserIdentityToken") /* .memberName */ UA_TYPES_EXTENSIONOBJECT, /* .memberTypeIndex */ offsetof(UA_ActivateSessionRequest, userIdentityToken) - offsetof(UA_ActivateSessionRequest, localeIds) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("UserTokenSignature") /* .memberName */ UA_TYPES_SIGNATUREDATA, /* .memberTypeIndex */ offsetof(UA_ActivateSessionRequest, userTokenSignature) - offsetof(UA_ActivateSessionRequest, userIdentityToken) - sizeof(UA_ExtensionObject), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* BrowsePathResult */ static UA_DataTypeMember BrowsePathResult_members[2] = { { UA_TYPENAME("StatusCode") /* .memberName */ UA_TYPES_STATUSCODE, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Targets") /* .memberName */ UA_TYPES_BROWSEPATHTARGET, /* .memberTypeIndex */ offsetof(UA_BrowsePathResult, targetsSize) - offsetof(UA_BrowsePathResult, statusCode) - sizeof(UA_StatusCode), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* AddNodesRequest */ static UA_DataTypeMember AddNodesRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("NodesToAdd") /* .memberName */ UA_TYPES_ADDNODESITEM, /* .memberTypeIndex */ offsetof(UA_AddNodesRequest, nodesToAddSize) - offsetof(UA_AddNodesRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* BrowseRequest */ static UA_DataTypeMember BrowseRequest_members[4] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("View") /* .memberName */ UA_TYPES_VIEWDESCRIPTION, /* .memberTypeIndex */ offsetof(UA_BrowseRequest, view) - offsetof(UA_BrowseRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RequestedMaxReferencesPerNode") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_BrowseRequest, requestedMaxReferencesPerNode) - offsetof(UA_BrowseRequest, view) - sizeof(UA_ViewDescription), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("NodesToBrowse") /* .memberName */ UA_TYPES_BROWSEDESCRIPTION, /* .memberTypeIndex */ offsetof(UA_BrowseRequest, nodesToBrowseSize) - offsetof(UA_BrowseRequest, requestedMaxReferencesPerNode) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* WriteRequest */ static UA_DataTypeMember WriteRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("NodesToWrite") /* .memberName */ UA_TYPES_WRITEVALUE, /* .memberTypeIndex */ offsetof(UA_WriteRequest, nodesToWriteSize) - offsetof(UA_WriteRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* AddNodesResponse */ static UA_DataTypeMember AddNodesResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Results") /* .memberName */ UA_TYPES_ADDNODESRESULT, /* .memberTypeIndex */ offsetof(UA_AddNodesResponse, resultsSize) - offsetof(UA_AddNodesResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_AddNodesResponse, diagnosticInfosSize) - offsetof(UA_AddNodesResponse, results) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* RegisterServer2Request */ static UA_DataTypeMember RegisterServer2Request_members[3] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Server") /* .memberName */ UA_TYPES_REGISTEREDSERVER, /* .memberTypeIndex */ offsetof(UA_RegisterServer2Request, server) - offsetof(UA_RegisterServer2Request, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DiscoveryConfiguration") /* .memberName */ UA_TYPES_EXTENSIONOBJECT, /* .memberTypeIndex */ offsetof(UA_RegisterServer2Request, discoveryConfigurationSize) - offsetof(UA_RegisterServer2Request, server) - sizeof(UA_RegisteredServer), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* AttributeOperand */ static UA_DataTypeMember AttributeOperand_members[5] = { { UA_TYPENAME("NodeId") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Alias") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_AttributeOperand, alias) - offsetof(UA_AttributeOperand, nodeId) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("BrowsePath") /* .memberName */ UA_TYPES_RELATIVEPATH, /* .memberTypeIndex */ offsetof(UA_AttributeOperand, browsePath) - offsetof(UA_AttributeOperand, alias) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("AttributeId") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_AttributeOperand, attributeId) - offsetof(UA_AttributeOperand, browsePath) - sizeof(UA_RelativePath), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("IndexRange") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_AttributeOperand, indexRange) - offsetof(UA_AttributeOperand, attributeId) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* DataChangeFilter */ static UA_DataTypeMember DataChangeFilter_members[3] = { { UA_TYPENAME("Trigger") /* .memberName */ UA_TYPES_DATACHANGETRIGGER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DeadbandType") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_DataChangeFilter, deadbandType) - offsetof(UA_DataChangeFilter, trigger) - sizeof(UA_DataChangeTrigger), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("DeadbandValue") /* .memberName */ UA_TYPES_DOUBLE, /* .memberTypeIndex */ offsetof(UA_DataChangeFilter, deadbandValue) - offsetof(UA_DataChangeFilter, deadbandType) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* EndpointDescription */ static UA_DataTypeMember EndpointDescription_members[8] = { { UA_TYPENAME("EndpointUrl") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Server") /* .memberName */ UA_TYPES_APPLICATIONDESCRIPTION, /* .memberTypeIndex */ offsetof(UA_EndpointDescription, server) - offsetof(UA_EndpointDescription, endpointUrl) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ServerCertificate") /* .memberName */ UA_TYPES_BYTESTRING, /* .memberTypeIndex */ offsetof(UA_EndpointDescription, serverCertificate) - offsetof(UA_EndpointDescription, server) - sizeof(UA_ApplicationDescription), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SecurityMode") /* .memberName */ UA_TYPES_MESSAGESECURITYMODE, /* .memberTypeIndex */ offsetof(UA_EndpointDescription, securityMode) - offsetof(UA_EndpointDescription, serverCertificate) - sizeof(UA_ByteString), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SecurityPolicyUri") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_EndpointDescription, securityPolicyUri) - offsetof(UA_EndpointDescription, securityMode) - sizeof(UA_MessageSecurityMode), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("UserIdentityTokens") /* .memberName */ UA_TYPES_USERTOKENPOLICY, /* .memberTypeIndex */ offsetof(UA_EndpointDescription, userIdentityTokensSize) - offsetof(UA_EndpointDescription, securityPolicyUri) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("TransportProfileUri") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_EndpointDescription, transportProfileUri) - offsetof(UA_EndpointDescription, userIdentityTokens) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SecurityLevel") /* .memberName */ UA_TYPES_BYTE, /* .memberTypeIndex */ offsetof(UA_EndpointDescription, securityLevel) - offsetof(UA_EndpointDescription, transportProfileUri) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* DeleteReferencesRequest */ static UA_DataTypeMember DeleteReferencesRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ReferencesToDelete") /* .memberName */ UA_TYPES_DELETEREFERENCESITEM, /* .memberTypeIndex */ offsetof(UA_DeleteReferencesRequest, referencesToDeleteSize) - offsetof(UA_DeleteReferencesRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* TranslateBrowsePathsToNodeIdsRequest */ static UA_DataTypeMember TranslateBrowsePathsToNodeIdsRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("BrowsePaths") /* .memberName */ UA_TYPES_BROWSEPATH, /* .memberTypeIndex */ offsetof(UA_TranslateBrowsePathsToNodeIdsRequest, browsePathsSize) - offsetof(UA_TranslateBrowsePathsToNodeIdsRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* FindServersResponse */ static UA_DataTypeMember FindServersResponse_members[2] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Servers") /* .memberName */ UA_TYPES_APPLICATIONDESCRIPTION, /* .memberTypeIndex */ offsetof(UA_FindServersResponse, serversSize) - offsetof(UA_FindServersResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* CreateSessionRequest */ static UA_DataTypeMember CreateSessionRequest_members[9] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ClientDescription") /* .memberName */ UA_TYPES_APPLICATIONDESCRIPTION, /* .memberTypeIndex */ offsetof(UA_CreateSessionRequest, clientDescription) - offsetof(UA_CreateSessionRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ServerUri") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_CreateSessionRequest, serverUri) - offsetof(UA_CreateSessionRequest, clientDescription) - sizeof(UA_ApplicationDescription), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("EndpointUrl") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_CreateSessionRequest, endpointUrl) - offsetof(UA_CreateSessionRequest, serverUri) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SessionName") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_CreateSessionRequest, sessionName) - offsetof(UA_CreateSessionRequest, endpointUrl) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ClientNonce") /* .memberName */ UA_TYPES_BYTESTRING, /* .memberTypeIndex */ offsetof(UA_CreateSessionRequest, clientNonce) - offsetof(UA_CreateSessionRequest, sessionName) - sizeof(UA_String), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ClientCertificate") /* .memberName */ UA_TYPES_BYTESTRING, /* .memberTypeIndex */ offsetof(UA_CreateSessionRequest, clientCertificate) - offsetof(UA_CreateSessionRequest, clientNonce) - sizeof(UA_ByteString), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RequestedSessionTimeout") /* .memberName */ UA_TYPES_DOUBLE, /* .memberTypeIndex */ offsetof(UA_CreateSessionRequest, requestedSessionTimeout) - offsetof(UA_CreateSessionRequest, clientCertificate) - sizeof(UA_ByteString), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("MaxResponseMessageSize") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_CreateSessionRequest, maxResponseMessageSize) - offsetof(UA_CreateSessionRequest, requestedSessionTimeout) - sizeof(UA_Double), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* ContentFilterElement */ static UA_DataTypeMember ContentFilterElement_members[2] = { { UA_TYPENAME("FilterOperator") /* .memberName */ UA_TYPES_FILTEROPERATOR, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("FilterOperands") /* .memberName */ UA_TYPES_EXTENSIONOBJECT, /* .memberTypeIndex */ offsetof(UA_ContentFilterElement, filterOperandsSize) - offsetof(UA_ContentFilterElement, filterOperator) - sizeof(UA_FilterOperator), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* RegisterServerRequest */ static UA_DataTypeMember RegisterServerRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ UA_TYPES_REQUESTHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Server") /* .memberName */ UA_TYPES_REGISTEREDSERVER, /* .memberTypeIndex */ offsetof(UA_RegisterServerRequest, server) - offsetof(UA_RegisterServerRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* TranslateBrowsePathsToNodeIdsResponse */ static UA_DataTypeMember TranslateBrowsePathsToNodeIdsResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Results") /* .memberName */ UA_TYPES_BROWSEPATHRESULT, /* .memberTypeIndex */ offsetof(UA_TranslateBrowsePathsToNodeIdsResponse, resultsSize) - offsetof(UA_TranslateBrowsePathsToNodeIdsResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_TranslateBrowsePathsToNodeIdsResponse, diagnosticInfosSize) - offsetof(UA_TranslateBrowsePathsToNodeIdsResponse, results) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* BrowseResponse */ static UA_DataTypeMember BrowseResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Results") /* .memberName */ UA_TYPES_BROWSERESULT, /* .memberTypeIndex */ offsetof(UA_BrowseResponse, resultsSize) - offsetof(UA_BrowseResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ UA_TYPES_DIAGNOSTICINFO, /* .memberTypeIndex */ offsetof(UA_BrowseResponse, diagnosticInfosSize) - offsetof(UA_BrowseResponse, results) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* CreateSessionResponse */ static UA_DataTypeMember CreateSessionResponse_members[10] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SessionId") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ offsetof(UA_CreateSessionResponse, sessionId) - offsetof(UA_CreateSessionResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("AuthenticationToken") /* .memberName */ UA_TYPES_NODEID, /* .memberTypeIndex */ offsetof(UA_CreateSessionResponse, authenticationToken) - offsetof(UA_CreateSessionResponse, sessionId) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RevisedSessionTimeout") /* .memberName */ UA_TYPES_DOUBLE, /* .memberTypeIndex */ offsetof(UA_CreateSessionResponse, revisedSessionTimeout) - offsetof(UA_CreateSessionResponse, authenticationToken) - sizeof(UA_NodeId), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ServerNonce") /* .memberName */ UA_TYPES_BYTESTRING, /* .memberTypeIndex */ offsetof(UA_CreateSessionResponse, serverNonce) - offsetof(UA_CreateSessionResponse, revisedSessionTimeout) - sizeof(UA_Double), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ServerCertificate") /* .memberName */ UA_TYPES_BYTESTRING, /* .memberTypeIndex */ offsetof(UA_CreateSessionResponse, serverCertificate) - offsetof(UA_CreateSessionResponse, serverNonce) - sizeof(UA_ByteString), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ServerEndpoints") /* .memberName */ UA_TYPES_ENDPOINTDESCRIPTION, /* .memberTypeIndex */ offsetof(UA_CreateSessionResponse, serverEndpointsSize) - offsetof(UA_CreateSessionResponse, serverCertificate) - sizeof(UA_ByteString), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("ServerSoftwareCertificates") /* .memberName */ UA_TYPES_SIGNEDSOFTWARECERTIFICATE, /* .memberTypeIndex */ offsetof(UA_CreateSessionResponse, serverSoftwareCertificatesSize) - offsetof(UA_CreateSessionResponse, serverEndpoints) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("ServerSignature") /* .memberName */ UA_TYPES_SIGNATUREDATA, /* .memberTypeIndex */ offsetof(UA_CreateSessionResponse, serverSignature) - offsetof(UA_CreateSessionResponse, serverSoftwareCertificates) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("MaxRequestMessageSize") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_CreateSessionResponse, maxRequestMessageSize) - offsetof(UA_CreateSessionResponse, serverSignature) - sizeof(UA_SignatureData), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* ContentFilter */ static UA_DataTypeMember ContentFilter_members[1] = { { UA_TYPENAME("Elements") /* .memberName */ UA_TYPES_CONTENTFILTERELEMENT, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* GetEndpointsResponse */ static UA_DataTypeMember GetEndpointsResponse_members[2] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ UA_TYPES_RESPONSEHEADER, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Endpoints") /* .memberName */ UA_TYPES_ENDPOINTDESCRIPTION, /* .memberTypeIndex */ offsetof(UA_GetEndpointsResponse, endpointsSize) - offsetof(UA_GetEndpointsResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .namespaceZero */ true /* .isArray */ },}; /* EventFilter */ static UA_DataTypeMember EventFilter_members[2] = { { UA_TYPENAME("SelectClauses") /* .memberName */ UA_TYPES_SIMPLEATTRIBUTEOPERAND, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("WhereClause") /* .memberName */ UA_TYPES_CONTENTFILTER, /* .memberTypeIndex */ offsetof(UA_EventFilter, whereClause) - offsetof(UA_EventFilter, selectClauses) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; const UA_DataType UA_TYPES[UA_TYPES_COUNT] = { /* Boolean */ { UA_TYPENAME("Boolean") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {1}}, /* .typeId */ sizeof(UA_Boolean), /* .memSize */ UA_TYPES_BOOLEAN, /* .typeIndex */ UA_DATATYPEKIND_BOOLEAN, /* .typeKind */ true, /* .pointerFree */ true, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ Boolean_members /* .members */ }, /* SByte */ { UA_TYPENAME("SByte") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {2}}, /* .typeId */ sizeof(UA_SByte), /* .memSize */ UA_TYPES_SBYTE, /* .typeIndex */ UA_DATATYPEKIND_SBYTE, /* .typeKind */ true, /* .pointerFree */ true, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ SByte_members /* .members */ }, /* Byte */ { UA_TYPENAME("Byte") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {3}}, /* .typeId */ sizeof(UA_Byte), /* .memSize */ UA_TYPES_BYTE, /* .typeIndex */ UA_DATATYPEKIND_BYTE, /* .typeKind */ true, /* .pointerFree */ true, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ Byte_members /* .members */ }, /* Int16 */ { UA_TYPENAME("Int16") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {4}}, /* .typeId */ sizeof(UA_Int16), /* .memSize */ UA_TYPES_INT16, /* .typeIndex */ UA_DATATYPEKIND_INT16, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ Int16_members /* .members */ }, /* UInt16 */ { UA_TYPENAME("UInt16") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {5}}, /* .typeId */ sizeof(UA_UInt16), /* .memSize */ UA_TYPES_UINT16, /* .typeIndex */ UA_DATATYPEKIND_UINT16, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ UInt16_members /* .members */ }, /* Int32 */ { UA_TYPENAME("Int32") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {6}}, /* .typeId */ sizeof(UA_Int32), /* .memSize */ UA_TYPES_INT32, /* .typeIndex */ UA_DATATYPEKIND_INT32, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ Int32_members /* .members */ }, /* UInt32 */ { UA_TYPENAME("UInt32") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {7}}, /* .typeId */ sizeof(UA_UInt32), /* .memSize */ UA_TYPES_UINT32, /* .typeIndex */ UA_DATATYPEKIND_UINT32, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ UInt32_members /* .members */ }, /* Int64 */ { UA_TYPENAME("Int64") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {8}}, /* .typeId */ sizeof(UA_Int64), /* .memSize */ UA_TYPES_INT64, /* .typeIndex */ UA_DATATYPEKIND_INT64, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ Int64_members /* .members */ }, /* UInt64 */ { UA_TYPENAME("UInt64") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {9}}, /* .typeId */ sizeof(UA_UInt64), /* .memSize */ UA_TYPES_UINT64, /* .typeIndex */ UA_DATATYPEKIND_UINT64, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ UInt64_members /* .members */ }, /* Float */ { UA_TYPENAME("Float") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {10}}, /* .typeId */ sizeof(UA_Float), /* .memSize */ UA_TYPES_FLOAT, /* .typeIndex */ UA_DATATYPEKIND_FLOAT, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_FLOAT, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ Float_members /* .members */ }, /* Double */ { UA_TYPENAME("Double") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {11}}, /* .typeId */ sizeof(UA_Double), /* .memSize */ UA_TYPES_DOUBLE, /* .typeIndex */ UA_DATATYPEKIND_DOUBLE, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_FLOAT, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ Double_members /* .members */ }, /* String */ { UA_TYPENAME("String") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {12}}, /* .typeId */ sizeof(UA_String), /* .memSize */ UA_TYPES_STRING, /* .typeIndex */ UA_DATATYPEKIND_STRING, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ String_members /* .members */ }, /* DateTime */ { UA_TYPENAME("DateTime") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {13}}, /* .typeId */ sizeof(UA_DateTime), /* .memSize */ UA_TYPES_DATETIME, /* .typeIndex */ UA_DATATYPEKIND_DATETIME, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ DateTime_members /* .members */ }, /* Guid */ { UA_TYPENAME("Guid") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {14}}, /* .typeId */ sizeof(UA_Guid), /* .memSize */ UA_TYPES_GUID, /* .typeIndex */ UA_DATATYPEKIND_GUID, /* .typeKind */ true, /* .pointerFree */ (UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_Guid, data2) == sizeof(UA_UInt32) && offsetof(UA_Guid, data3) == (sizeof(UA_UInt16) + sizeof(UA_UInt32)) && offsetof(UA_Guid, data4) == (2*sizeof(UA_UInt32))), /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ Guid_members /* .members */ }, /* ByteString */ { UA_TYPENAME("ByteString") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {15}}, /* .typeId */ sizeof(UA_ByteString), /* .memSize */ UA_TYPES_BYTESTRING, /* .typeIndex */ UA_DATATYPEKIND_BYTESTRING, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ ByteString_members /* .members */ }, /* XmlElement */ { UA_TYPENAME("XmlElement") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {16}}, /* .typeId */ sizeof(UA_XmlElement), /* .memSize */ UA_TYPES_XMLELEMENT, /* .typeIndex */ UA_DATATYPEKIND_XMLELEMENT, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ XmlElement_members /* .members */ }, /* NodeId */ { UA_TYPENAME("NodeId") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {17}}, /* .typeId */ sizeof(UA_NodeId), /* .memSize */ UA_TYPES_NODEID, /* .typeIndex */ UA_DATATYPEKIND_NODEID, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ NodeId_members /* .members */ }, /* ExpandedNodeId */ { UA_TYPENAME("ExpandedNodeId") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {18}}, /* .typeId */ sizeof(UA_ExpandedNodeId), /* .memSize */ UA_TYPES_EXPANDEDNODEID, /* .typeIndex */ UA_DATATYPEKIND_EXPANDEDNODEID, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ ExpandedNodeId_members /* .members */ }, /* StatusCode */ { UA_TYPENAME("StatusCode") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {19}}, /* .typeId */ sizeof(UA_StatusCode), /* .memSize */ UA_TYPES_STATUSCODE, /* .typeIndex */ UA_DATATYPEKIND_STATUSCODE, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ StatusCode_members /* .members */ }, /* QualifiedName */ { UA_TYPENAME("QualifiedName") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {20}}, /* .typeId */ sizeof(UA_QualifiedName), /* .memSize */ UA_TYPES_QUALIFIEDNAME, /* .typeIndex */ UA_DATATYPEKIND_QUALIFIEDNAME, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ QualifiedName_members /* .members */ }, /* LocalizedText */ { UA_TYPENAME("LocalizedText") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {21}}, /* .typeId */ sizeof(UA_LocalizedText), /* .memSize */ UA_TYPES_LOCALIZEDTEXT, /* .typeIndex */ UA_DATATYPEKIND_LOCALIZEDTEXT, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ LocalizedText_members /* .members */ }, /* ExtensionObject */ { UA_TYPENAME("ExtensionObject") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {22}}, /* .typeId */ sizeof(UA_ExtensionObject), /* .memSize */ UA_TYPES_EXTENSIONOBJECT, /* .typeIndex */ UA_DATATYPEKIND_EXTENSIONOBJECT, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ ExtensionObject_members /* .members */ }, /* DataValue */ { UA_TYPENAME("DataValue") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {23}}, /* .typeId */ sizeof(UA_DataValue), /* .memSize */ UA_TYPES_DATAVALUE, /* .typeIndex */ UA_DATATYPEKIND_DATAVALUE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ DataValue_members /* .members */ }, /* Variant */ { UA_TYPENAME("Variant") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {24}}, /* .typeId */ sizeof(UA_Variant), /* .memSize */ UA_TYPES_VARIANT, /* .typeIndex */ UA_DATATYPEKIND_VARIANT, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ Variant_members /* .members */ }, /* DiagnosticInfo */ { UA_TYPENAME("DiagnosticInfo") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {25}}, /* .typeId */ sizeof(UA_DiagnosticInfo), /* .memSize */ UA_TYPES_DIAGNOSTICINFO, /* .typeIndex */ UA_DATATYPEKIND_DIAGNOSTICINFO, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ DiagnosticInfo_members /* .members */ }, /* ViewAttributes */ { UA_TYPENAME("ViewAttributes") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {373}}, /* .typeId */ sizeof(UA_ViewAttributes), /* .memSize */ UA_TYPES_VIEWATTRIBUTES, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 7, /* .membersSize */ 375, /* .binaryEncodingId */ ViewAttributes_members /* .members */ }, /* ElementOperand */ { UA_TYPENAME("ElementOperand") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {592}}, /* .typeId */ sizeof(UA_ElementOperand), /* .memSize */ UA_TYPES_ELEMENTOPERAND, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ true && UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 1, /* .membersSize */ 594, /* .binaryEncodingId */ ElementOperand_members /* .members */ }, /* VariableAttributes */ { UA_TYPENAME("VariableAttributes") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {355}}, /* .typeId */ sizeof(UA_VariableAttributes), /* .memSize */ UA_TYPES_VARIABLEATTRIBUTES, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 13, /* .membersSize */ 357, /* .binaryEncodingId */ VariableAttributes_members /* .members */ }, /* EnumValueType */ { UA_TYPENAME("EnumValueType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {7594}}, /* .typeId */ sizeof(UA_EnumValueType), /* .memSize */ UA_TYPES_ENUMVALUETYPE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 8251, /* .binaryEncodingId */ EnumValueType_members /* .members */ }, /* EventFieldList */ { UA_TYPENAME("EventFieldList") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {917}}, /* .typeId */ sizeof(UA_EventFieldList), /* .memSize */ UA_TYPES_EVENTFIELDLIST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 919, /* .binaryEncodingId */ EventFieldList_members /* .members */ }, /* MonitoredItemCreateResult */ { UA_TYPENAME("MonitoredItemCreateResult") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {746}}, /* .typeId */ sizeof(UA_MonitoredItemCreateResult), /* .memSize */ UA_TYPES_MONITOREDITEMCREATERESULT, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ 748, /* .binaryEncodingId */ MonitoredItemCreateResult_members /* .members */ }, /* ServerDiagnosticsSummaryDataType */ { UA_TYPENAME("ServerDiagnosticsSummaryDataType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {859}}, /* .typeId */ sizeof(UA_ServerDiagnosticsSummaryDataType), /* .memSize */ UA_TYPES_SERVERDIAGNOSTICSSUMMARYDATATYPE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ true && UA_BINARY_OVERLAYABLE_INTEGER && UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_ServerDiagnosticsSummaryDataType, currentSessionCount) == (offsetof(UA_ServerDiagnosticsSummaryDataType, serverViewCount) + sizeof(UA_UInt32)) && UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_ServerDiagnosticsSummaryDataType, cumulatedSessionCount) == (offsetof(UA_ServerDiagnosticsSummaryDataType, currentSessionCount) + sizeof(UA_UInt32)) && UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_ServerDiagnosticsSummaryDataType, securityRejectedSessionCount) == (offsetof(UA_ServerDiagnosticsSummaryDataType, cumulatedSessionCount) + sizeof(UA_UInt32)) && UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_ServerDiagnosticsSummaryDataType, rejectedSessionCount) == (offsetof(UA_ServerDiagnosticsSummaryDataType, securityRejectedSessionCount) + sizeof(UA_UInt32)) && UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_ServerDiagnosticsSummaryDataType, sessionTimeoutCount) == (offsetof(UA_ServerDiagnosticsSummaryDataType, rejectedSessionCount) + sizeof(UA_UInt32)) && UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_ServerDiagnosticsSummaryDataType, sessionAbortCount) == (offsetof(UA_ServerDiagnosticsSummaryDataType, sessionTimeoutCount) + sizeof(UA_UInt32)) && UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_ServerDiagnosticsSummaryDataType, currentSubscriptionCount) == (offsetof(UA_ServerDiagnosticsSummaryDataType, sessionAbortCount) + sizeof(UA_UInt32)) && UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_ServerDiagnosticsSummaryDataType, cumulatedSubscriptionCount) == (offsetof(UA_ServerDiagnosticsSummaryDataType, currentSubscriptionCount) + sizeof(UA_UInt32)) && UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_ServerDiagnosticsSummaryDataType, publishingIntervalCount) == (offsetof(UA_ServerDiagnosticsSummaryDataType, cumulatedSubscriptionCount) + sizeof(UA_UInt32)) && UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_ServerDiagnosticsSummaryDataType, securityRejectedRequestsCount) == (offsetof(UA_ServerDiagnosticsSummaryDataType, publishingIntervalCount) + sizeof(UA_UInt32)) && UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_ServerDiagnosticsSummaryDataType, rejectedRequestsCount) == (offsetof(UA_ServerDiagnosticsSummaryDataType, securityRejectedRequestsCount) + sizeof(UA_UInt32)), /* .overlayable */ 12, /* .membersSize */ 861, /* .binaryEncodingId */ ServerDiagnosticsSummaryDataType_members /* .members */ }, /* ContentFilterElementResult */ { UA_TYPENAME("ContentFilterElementResult") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {604}}, /* .typeId */ sizeof(UA_ContentFilterElementResult), /* .memSize */ UA_TYPES_CONTENTFILTERELEMENTRESULT, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 606, /* .binaryEncodingId */ ContentFilterElementResult_members /* .members */ }, /* LiteralOperand */ { UA_TYPENAME("LiteralOperand") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {595}}, /* .typeId */ sizeof(UA_LiteralOperand), /* .memSize */ UA_TYPES_LITERALOPERAND, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ 597, /* .binaryEncodingId */ LiteralOperand_members /* .members */ }, /* MessageSecurityMode */ { UA_TYPENAME("MessageSecurityMode") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {302}}, /* .typeId */ sizeof(UA_MessageSecurityMode), /* .memSize */ UA_TYPES_INT32, /* .typeIndex */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ MessageSecurityMode_members /* .members */ }, /* UtcTime */ { UA_TYPENAME("UtcTime") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {294}}, /* .typeId */ sizeof(UA_UtcTime), /* .memSize */ UA_TYPES_UTCTIME, /* .typeIndex */ UA_DATATYPEKIND_DATETIME, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ UtcTime_members /* .members */ }, /* UserIdentityToken */ { UA_TYPENAME("UserIdentityToken") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {316}}, /* .typeId */ sizeof(UA_UserIdentityToken), /* .memSize */ UA_TYPES_USERIDENTITYTOKEN, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ 318, /* .binaryEncodingId */ UserIdentityToken_members /* .members */ }, /* X509IdentityToken */ { UA_TYPENAME("X509IdentityToken") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {325}}, /* .typeId */ sizeof(UA_X509IdentityToken), /* .memSize */ UA_TYPES_X509IDENTITYTOKEN, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 327, /* .binaryEncodingId */ X509IdentityToken_members /* .members */ }, /* MonitoredItemNotification */ { UA_TYPENAME("MonitoredItemNotification") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {806}}, /* .typeId */ sizeof(UA_MonitoredItemNotification), /* .memSize */ UA_TYPES_MONITOREDITEMNOTIFICATION, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 808, /* .binaryEncodingId */ MonitoredItemNotification_members /* .members */ }, /* ResponseHeader */ { UA_TYPENAME("ResponseHeader") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {392}}, /* .typeId */ sizeof(UA_ResponseHeader), /* .memSize */ UA_TYPES_RESPONSEHEADER, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 6, /* .membersSize */ 394, /* .binaryEncodingId */ ResponseHeader_members /* .members */ }, /* SignatureData */ { UA_TYPENAME("SignatureData") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {456}}, /* .typeId */ sizeof(UA_SignatureData), /* .memSize */ UA_TYPES_SIGNATUREDATA, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 458, /* .binaryEncodingId */ SignatureData_members /* .members */ }, /* ModifySubscriptionResponse */ { UA_TYPENAME("ModifySubscriptionResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {794}}, /* .typeId */ sizeof(UA_ModifySubscriptionResponse), /* .memSize */ UA_TYPES_MODIFYSUBSCRIPTIONRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ 796, /* .binaryEncodingId */ ModifySubscriptionResponse_members /* .members */ }, /* NodeAttributes */ { UA_TYPENAME("NodeAttributes") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {349}}, /* .typeId */ sizeof(UA_NodeAttributes), /* .memSize */ UA_TYPES_NODEATTRIBUTES, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ 351, /* .binaryEncodingId */ NodeAttributes_members /* .members */ }, /* ActivateSessionResponse */ { UA_TYPENAME("ActivateSessionResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {468}}, /* .typeId */ sizeof(UA_ActivateSessionResponse), /* .memSize */ UA_TYPES_ACTIVATESESSIONRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ 470, /* .binaryEncodingId */ ActivateSessionResponse_members /* .members */ }, /* VariableTypeAttributes */ { UA_TYPENAME("VariableTypeAttributes") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {364}}, /* .typeId */ sizeof(UA_VariableTypeAttributes), /* .memSize */ UA_TYPES_VARIABLETYPEATTRIBUTES, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 10, /* .membersSize */ 366, /* .binaryEncodingId */ VariableTypeAttributes_members /* .members */ }, /* CallMethodResult */ { UA_TYPENAME("CallMethodResult") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {707}}, /* .typeId */ sizeof(UA_CallMethodResult), /* .memSize */ UA_TYPES_CALLMETHODRESULT, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ 709, /* .binaryEncodingId */ CallMethodResult_members /* .members */ }, /* MonitoringMode */ { UA_TYPENAME("MonitoringMode") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {716}}, /* .typeId */ sizeof(UA_MonitoringMode), /* .memSize */ UA_TYPES_INT32, /* .typeIndex */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ MonitoringMode_members /* .members */ }, /* SetMonitoringModeResponse */ { UA_TYPENAME("SetMonitoringModeResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {770}}, /* .typeId */ sizeof(UA_SetMonitoringModeResponse), /* .memSize */ UA_TYPES_SETMONITORINGMODERESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 772, /* .binaryEncodingId */ SetMonitoringModeResponse_members /* .members */ }, /* BrowseResultMask */ { UA_TYPENAME("BrowseResultMask") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {517}}, /* .typeId */ sizeof(UA_BrowseResultMask), /* .memSize */ UA_TYPES_INT32, /* .typeIndex */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ BrowseResultMask_members /* .members */ }, /* RequestHeader */ { UA_TYPENAME("RequestHeader") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {389}}, /* .typeId */ sizeof(UA_RequestHeader), /* .memSize */ UA_TYPES_REQUESTHEADER, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 7, /* .membersSize */ 391, /* .binaryEncodingId */ RequestHeader_members /* .members */ }, /* MonitoredItemModifyResult */ { UA_TYPENAME("MonitoredItemModifyResult") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {758}}, /* .typeId */ sizeof(UA_MonitoredItemModifyResult), /* .memSize */ UA_TYPES_MONITOREDITEMMODIFYRESULT, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ 760, /* .binaryEncodingId */ MonitoredItemModifyResult_members /* .members */ }, /* CloseSecureChannelRequest */ { UA_TYPENAME("CloseSecureChannelRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {450}}, /* .typeId */ sizeof(UA_CloseSecureChannelRequest), /* .memSize */ UA_TYPES_CLOSESECURECHANNELREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ 452, /* .binaryEncodingId */ CloseSecureChannelRequest_members /* .members */ }, /* NotificationMessage */ { UA_TYPENAME("NotificationMessage") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {803}}, /* .typeId */ sizeof(UA_NotificationMessage), /* .memSize */ UA_TYPES_NOTIFICATIONMESSAGE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 805, /* .binaryEncodingId */ NotificationMessage_members /* .members */ }, /* CreateSubscriptionResponse */ { UA_TYPENAME("CreateSubscriptionResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {788}}, /* .typeId */ sizeof(UA_CreateSubscriptionResponse), /* .memSize */ UA_TYPES_CREATESUBSCRIPTIONRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ 790, /* .binaryEncodingId */ CreateSubscriptionResponse_members /* .members */ }, /* MdnsDiscoveryConfiguration */ { UA_TYPENAME("MdnsDiscoveryConfiguration") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {12891}}, /* .typeId */ sizeof(UA_MdnsDiscoveryConfiguration), /* .memSize */ UA_TYPES_MDNSDISCOVERYCONFIGURATION, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 12901, /* .binaryEncodingId */ MdnsDiscoveryConfiguration_members /* .members */ }, /* BrowseDirection */ { UA_TYPENAME("BrowseDirection") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {510}}, /* .typeId */ sizeof(UA_BrowseDirection), /* .memSize */ UA_TYPES_INT32, /* .typeIndex */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ BrowseDirection_members /* .members */ }, /* CallMethodRequest */ { UA_TYPENAME("CallMethodRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {704}}, /* .typeId */ sizeof(UA_CallMethodRequest), /* .memSize */ UA_TYPES_CALLMETHODREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 706, /* .binaryEncodingId */ CallMethodRequest_members /* .members */ }, /* ReadResponse */ { UA_TYPENAME("ReadResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {632}}, /* .typeId */ sizeof(UA_ReadResponse), /* .memSize */ UA_TYPES_READRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 634, /* .binaryEncodingId */ ReadResponse_members /* .members */ }, /* TimestampsToReturn */ { UA_TYPENAME("TimestampsToReturn") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {625}}, /* .typeId */ sizeof(UA_TimestampsToReturn), /* .memSize */ UA_TYPES_INT32, /* .typeIndex */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ TimestampsToReturn_members /* .members */ }, /* NodeClass */ { UA_TYPENAME("NodeClass") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {257}}, /* .typeId */ sizeof(UA_NodeClass), /* .memSize */ UA_TYPES_INT32, /* .typeIndex */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ NodeClass_members /* .members */ }, /* ObjectTypeAttributes */ { UA_TYPENAME("ObjectTypeAttributes") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {361}}, /* .typeId */ sizeof(UA_ObjectTypeAttributes), /* .memSize */ UA_TYPES_OBJECTTYPEATTRIBUTES, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 6, /* .membersSize */ 363, /* .binaryEncodingId */ ObjectTypeAttributes_members /* .members */ }, /* SecurityTokenRequestType */ { UA_TYPENAME("SecurityTokenRequestType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {315}}, /* .typeId */ sizeof(UA_SecurityTokenRequestType), /* .memSize */ UA_TYPES_INT32, /* .typeIndex */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ SecurityTokenRequestType_members /* .members */ }, /* CloseSessionResponse */ { UA_TYPENAME("CloseSessionResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {474}}, /* .typeId */ sizeof(UA_CloseSessionResponse), /* .memSize */ UA_TYPES_CLOSESESSIONRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ 476, /* .binaryEncodingId */ CloseSessionResponse_members /* .members */ }, /* SetPublishingModeRequest */ { UA_TYPENAME("SetPublishingModeRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {797}}, /* .typeId */ sizeof(UA_SetPublishingModeRequest), /* .memSize */ UA_TYPES_SETPUBLISHINGMODEREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 799, /* .binaryEncodingId */ SetPublishingModeRequest_members /* .members */ }, /* IssuedIdentityToken */ { UA_TYPENAME("IssuedIdentityToken") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {938}}, /* .typeId */ sizeof(UA_IssuedIdentityToken), /* .memSize */ UA_TYPES_ISSUEDIDENTITYTOKEN, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 940, /* .binaryEncodingId */ IssuedIdentityToken_members /* .members */ }, /* ServerOnNetwork */ { UA_TYPENAME("ServerOnNetwork") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {12189}}, /* .typeId */ sizeof(UA_ServerOnNetwork), /* .memSize */ UA_TYPES_SERVERONNETWORK, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ 12207, /* .binaryEncodingId */ ServerOnNetwork_members /* .members */ }, /* DeleteMonitoredItemsResponse */ { UA_TYPENAME("DeleteMonitoredItemsResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {782}}, /* .typeId */ sizeof(UA_DeleteMonitoredItemsResponse), /* .memSize */ UA_TYPES_DELETEMONITOREDITEMSRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 784, /* .binaryEncodingId */ DeleteMonitoredItemsResponse_members /* .members */ }, /* ApplicationType */ { UA_TYPENAME("ApplicationType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {307}}, /* .typeId */ sizeof(UA_ApplicationType), /* .memSize */ UA_TYPES_INT32, /* .typeIndex */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ ApplicationType_members /* .members */ }, /* DiscoveryConfiguration */ { UA_TYPENAME("DiscoveryConfiguration") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {12890}}, /* .typeId */ sizeof(UA_DiscoveryConfiguration), /* .memSize */ UA_TYPES_DISCOVERYCONFIGURATION, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ true, /* .overlayable */ 0, /* .membersSize */ 12900, /* .binaryEncodingId */ DiscoveryConfiguration_members /* .members */ }, /* BrowseNextRequest */ { UA_TYPENAME("BrowseNextRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {531}}, /* .typeId */ sizeof(UA_BrowseNextRequest), /* .memSize */ UA_TYPES_BROWSENEXTREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 533, /* .binaryEncodingId */ BrowseNextRequest_members /* .members */ }, /* ModifySubscriptionRequest */ { UA_TYPENAME("ModifySubscriptionRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {791}}, /* .typeId */ sizeof(UA_ModifySubscriptionRequest), /* .memSize */ UA_TYPES_MODIFYSUBSCRIPTIONREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 7, /* .membersSize */ 793, /* .binaryEncodingId */ ModifySubscriptionRequest_members /* .members */ }, /* BrowseDescription */ { UA_TYPENAME("BrowseDescription") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {514}}, /* .typeId */ sizeof(UA_BrowseDescription), /* .memSize */ UA_TYPES_BROWSEDESCRIPTION, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 6, /* .membersSize */ 516, /* .binaryEncodingId */ BrowseDescription_members /* .members */ }, /* SignedSoftwareCertificate */ { UA_TYPENAME("SignedSoftwareCertificate") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {344}}, /* .typeId */ sizeof(UA_SignedSoftwareCertificate), /* .memSize */ UA_TYPES_SIGNEDSOFTWARECERTIFICATE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 346, /* .binaryEncodingId */ SignedSoftwareCertificate_members /* .members */ }, /* BrowsePathTarget */ { UA_TYPENAME("BrowsePathTarget") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {546}}, /* .typeId */ sizeof(UA_BrowsePathTarget), /* .memSize */ UA_TYPES_BROWSEPATHTARGET, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 548, /* .binaryEncodingId */ BrowsePathTarget_members /* .members */ }, /* WriteResponse */ { UA_TYPENAME("WriteResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {674}}, /* .typeId */ sizeof(UA_WriteResponse), /* .memSize */ UA_TYPES_WRITERESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 676, /* .binaryEncodingId */ WriteResponse_members /* .members */ }, /* AddNodesResult */ { UA_TYPENAME("AddNodesResult") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {483}}, /* .typeId */ sizeof(UA_AddNodesResult), /* .memSize */ UA_TYPES_ADDNODESRESULT, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 485, /* .binaryEncodingId */ AddNodesResult_members /* .members */ }, /* RegisterServerResponse */ { UA_TYPENAME("RegisterServerResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {438}}, /* .typeId */ sizeof(UA_RegisterServerResponse), /* .memSize */ UA_TYPES_REGISTERSERVERRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ 440, /* .binaryEncodingId */ RegisterServerResponse_members /* .members */ }, /* AddReferencesItem */ { UA_TYPENAME("AddReferencesItem") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {379}}, /* .typeId */ sizeof(UA_AddReferencesItem), /* .memSize */ UA_TYPES_ADDREFERENCESITEM, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 6, /* .membersSize */ 381, /* .binaryEncodingId */ AddReferencesItem_members /* .members */ }, /* RegisterServer2Response */ { UA_TYPENAME("RegisterServer2Response") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {12194}}, /* .typeId */ sizeof(UA_RegisterServer2Response), /* .memSize */ UA_TYPES_REGISTERSERVER2RESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 12212, /* .binaryEncodingId */ RegisterServer2Response_members /* .members */ }, /* DeleteReferencesResponse */ { UA_TYPENAME("DeleteReferencesResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {507}}, /* .typeId */ sizeof(UA_DeleteReferencesResponse), /* .memSize */ UA_TYPES_DELETEREFERENCESRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 509, /* .binaryEncodingId */ DeleteReferencesResponse_members /* .members */ }, /* RelativePathElement */ { UA_TYPENAME("RelativePathElement") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {537}}, /* .typeId */ sizeof(UA_RelativePathElement), /* .memSize */ UA_TYPES_RELATIVEPATHELEMENT, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ 539, /* .binaryEncodingId */ RelativePathElement_members /* .members */ }, /* SubscriptionAcknowledgement */ { UA_TYPENAME("SubscriptionAcknowledgement") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {821}}, /* .typeId */ sizeof(UA_SubscriptionAcknowledgement), /* .memSize */ UA_TYPES_SUBSCRIPTIONACKNOWLEDGEMENT, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ true && UA_BINARY_OVERLAYABLE_INTEGER && UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_SubscriptionAcknowledgement, sequenceNumber) == (offsetof(UA_SubscriptionAcknowledgement, subscriptionId) + sizeof(UA_UInt32)), /* .overlayable */ 2, /* .membersSize */ 823, /* .binaryEncodingId */ SubscriptionAcknowledgement_members /* .members */ }, /* CreateMonitoredItemsResponse */ { UA_TYPENAME("CreateMonitoredItemsResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {752}}, /* .typeId */ sizeof(UA_CreateMonitoredItemsResponse), /* .memSize */ UA_TYPES_CREATEMONITOREDITEMSRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 754, /* .binaryEncodingId */ CreateMonitoredItemsResponse_members /* .members */ }, /* DeleteReferencesItem */ { UA_TYPENAME("DeleteReferencesItem") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {385}}, /* .typeId */ sizeof(UA_DeleteReferencesItem), /* .memSize */ UA_TYPES_DELETEREFERENCESITEM, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ 387, /* .binaryEncodingId */ DeleteReferencesItem_members /* .members */ }, /* WriteValue */ { UA_TYPENAME("WriteValue") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {668}}, /* .typeId */ sizeof(UA_WriteValue), /* .memSize */ UA_TYPES_WRITEVALUE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ 670, /* .binaryEncodingId */ WriteValue_members /* .members */ }, /* DataTypeAttributes */ { UA_TYPENAME("DataTypeAttributes") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {370}}, /* .typeId */ sizeof(UA_DataTypeAttributes), /* .memSize */ UA_TYPES_DATATYPEATTRIBUTES, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 6, /* .membersSize */ 372, /* .binaryEncodingId */ DataTypeAttributes_members /* .members */ }, /* AddReferencesResponse */ { UA_TYPENAME("AddReferencesResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {495}}, /* .typeId */ sizeof(UA_AddReferencesResponse), /* .memSize */ UA_TYPES_ADDREFERENCESRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 497, /* .binaryEncodingId */ AddReferencesResponse_members /* .members */ }, /* DeadbandType */ { UA_TYPENAME("DeadbandType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {718}}, /* .typeId */ sizeof(UA_DeadbandType), /* .memSize */ UA_TYPES_INT32, /* .typeIndex */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ DeadbandType_members /* .members */ }, /* DataChangeTrigger */ { UA_TYPENAME("DataChangeTrigger") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {717}}, /* .typeId */ sizeof(UA_DataChangeTrigger), /* .memSize */ UA_TYPES_INT32, /* .typeIndex */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ DataChangeTrigger_members /* .members */ }, /* BuildInfo */ { UA_TYPENAME("BuildInfo") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {338}}, /* .typeId */ sizeof(UA_BuildInfo), /* .memSize */ UA_TYPES_BUILDINFO, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 6, /* .membersSize */ 340, /* .binaryEncodingId */ BuildInfo_members /* .members */ }, /* FilterOperand */ { UA_TYPENAME("FilterOperand") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {589}}, /* .typeId */ sizeof(UA_FilterOperand), /* .memSize */ UA_TYPES_FILTEROPERAND, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ true, /* .overlayable */ 0, /* .membersSize */ 591, /* .binaryEncodingId */ FilterOperand_members /* .members */ }, /* MonitoringParameters */ { UA_TYPENAME("MonitoringParameters") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {740}}, /* .typeId */ sizeof(UA_MonitoringParameters), /* .memSize */ UA_TYPES_MONITORINGPARAMETERS, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ 742, /* .binaryEncodingId */ MonitoringParameters_members /* .members */ }, /* DeleteNodesItem */ { UA_TYPENAME("DeleteNodesItem") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {382}}, /* .typeId */ sizeof(UA_DeleteNodesItem), /* .memSize */ UA_TYPES_DELETENODESITEM, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 384, /* .binaryEncodingId */ DeleteNodesItem_members /* .members */ }, /* ReadValueId */ { UA_TYPENAME("ReadValueId") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {626}}, /* .typeId */ sizeof(UA_ReadValueId), /* .memSize */ UA_TYPES_READVALUEID, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ 628, /* .binaryEncodingId */ ReadValueId_members /* .members */ }, /* CallRequest */ { UA_TYPENAME("CallRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {710}}, /* .typeId */ sizeof(UA_CallRequest), /* .memSize */ UA_TYPES_CALLREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 712, /* .binaryEncodingId */ CallRequest_members /* .members */ }, /* RelativePath */ { UA_TYPENAME("RelativePath") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {540}}, /* .typeId */ sizeof(UA_RelativePath), /* .memSize */ UA_TYPES_RELATIVEPATH, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ 542, /* .binaryEncodingId */ RelativePath_members /* .members */ }, /* DeleteNodesRequest */ { UA_TYPENAME("DeleteNodesRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {498}}, /* .typeId */ sizeof(UA_DeleteNodesRequest), /* .memSize */ UA_TYPES_DELETENODESREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 500, /* .binaryEncodingId */ DeleteNodesRequest_members /* .members */ }, /* MonitoredItemModifyRequest */ { UA_TYPENAME("MonitoredItemModifyRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {755}}, /* .typeId */ sizeof(UA_MonitoredItemModifyRequest), /* .memSize */ UA_TYPES_MONITOREDITEMMODIFYREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 757, /* .binaryEncodingId */ MonitoredItemModifyRequest_members /* .members */ }, /* UserTokenType */ { UA_TYPENAME("UserTokenType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {303}}, /* .typeId */ sizeof(UA_UserTokenType), /* .memSize */ UA_TYPES_INT32, /* .typeIndex */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ UserTokenType_members /* .members */ }, /* AggregateConfiguration */ { UA_TYPENAME("AggregateConfiguration") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {948}}, /* .typeId */ sizeof(UA_AggregateConfiguration), /* .memSize */ UA_TYPES_AGGREGATECONFIGURATION, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ true && true && true && offsetof(UA_AggregateConfiguration, treatUncertainAsBad) == (offsetof(UA_AggregateConfiguration, useServerCapabilitiesDefaults) + sizeof(UA_Boolean)) && true && offsetof(UA_AggregateConfiguration, percentDataBad) == (offsetof(UA_AggregateConfiguration, treatUncertainAsBad) + sizeof(UA_Boolean)) && true && offsetof(UA_AggregateConfiguration, percentDataGood) == (offsetof(UA_AggregateConfiguration, percentDataBad) + sizeof(UA_Byte)) && true && offsetof(UA_AggregateConfiguration, useSlopedExtrapolation) == (offsetof(UA_AggregateConfiguration, percentDataGood) + sizeof(UA_Byte)), /* .overlayable */ 5, /* .membersSize */ 950, /* .binaryEncodingId */ AggregateConfiguration_members /* .members */ }, /* LocaleId */ { UA_TYPENAME("LocaleId") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {295}}, /* .typeId */ sizeof(UA_LocaleId), /* .memSize */ UA_TYPES_LOCALEID, /* .typeIndex */ UA_DATATYPEKIND_STRING, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ LocaleId_members /* .members */ }, /* UnregisterNodesResponse */ { UA_TYPENAME("UnregisterNodesResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {567}}, /* .typeId */ sizeof(UA_UnregisterNodesResponse), /* .memSize */ UA_TYPES_UNREGISTERNODESRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ 569, /* .binaryEncodingId */ UnregisterNodesResponse_members /* .members */ }, /* ContentFilterResult */ { UA_TYPENAME("ContentFilterResult") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {607}}, /* .typeId */ sizeof(UA_ContentFilterResult), /* .memSize */ UA_TYPES_CONTENTFILTERRESULT, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 609, /* .binaryEncodingId */ ContentFilterResult_members /* .members */ }, /* UserTokenPolicy */ { UA_TYPENAME("UserTokenPolicy") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {304}}, /* .typeId */ sizeof(UA_UserTokenPolicy), /* .memSize */ UA_TYPES_USERTOKENPOLICY, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ 306, /* .binaryEncodingId */ UserTokenPolicy_members /* .members */ }, /* DeleteMonitoredItemsRequest */ { UA_TYPENAME("DeleteMonitoredItemsRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {779}}, /* .typeId */ sizeof(UA_DeleteMonitoredItemsRequest), /* .memSize */ UA_TYPES_DELETEMONITOREDITEMSREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 781, /* .binaryEncodingId */ DeleteMonitoredItemsRequest_members /* .members */ }, /* SetMonitoringModeRequest */ { UA_TYPENAME("SetMonitoringModeRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {767}}, /* .typeId */ sizeof(UA_SetMonitoringModeRequest), /* .memSize */ UA_TYPES_SETMONITORINGMODEREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ 769, /* .binaryEncodingId */ SetMonitoringModeRequest_members /* .members */ }, /* Duration */ { UA_TYPENAME("Duration") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {290}}, /* .typeId */ sizeof(UA_Duration), /* .memSize */ UA_TYPES_DURATION, /* .typeIndex */ UA_DATATYPEKIND_DOUBLE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ Duration_members /* .members */ }, /* ReferenceTypeAttributes */ { UA_TYPENAME("ReferenceTypeAttributes") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {367}}, /* .typeId */ sizeof(UA_ReferenceTypeAttributes), /* .memSize */ UA_TYPES_REFERENCETYPEATTRIBUTES, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 8, /* .membersSize */ 369, /* .binaryEncodingId */ ReferenceTypeAttributes_members /* .members */ }, /* GetEndpointsRequest */ { UA_TYPENAME("GetEndpointsRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {426}}, /* .typeId */ sizeof(UA_GetEndpointsRequest), /* .memSize */ UA_TYPES_GETENDPOINTSREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ 428, /* .binaryEncodingId */ GetEndpointsRequest_members /* .members */ }, /* CloseSecureChannelResponse */ { UA_TYPENAME("CloseSecureChannelResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {453}}, /* .typeId */ sizeof(UA_CloseSecureChannelResponse), /* .memSize */ UA_TYPES_CLOSESECURECHANNELRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ 455, /* .binaryEncodingId */ CloseSecureChannelResponse_members /* .members */ }, /* ViewDescription */ { UA_TYPENAME("ViewDescription") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {511}}, /* .typeId */ sizeof(UA_ViewDescription), /* .memSize */ UA_TYPES_VIEWDESCRIPTION, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 513, /* .binaryEncodingId */ ViewDescription_members /* .members */ }, /* SetPublishingModeResponse */ { UA_TYPENAME("SetPublishingModeResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {800}}, /* .typeId */ sizeof(UA_SetPublishingModeResponse), /* .memSize */ UA_TYPES_SETPUBLISHINGMODERESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 802, /* .binaryEncodingId */ SetPublishingModeResponse_members /* .members */ }, /* StatusChangeNotification */ { UA_TYPENAME("StatusChangeNotification") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {818}}, /* .typeId */ sizeof(UA_StatusChangeNotification), /* .memSize */ UA_TYPES_STATUSCHANGENOTIFICATION, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 820, /* .binaryEncodingId */ StatusChangeNotification_members /* .members */ }, /* NodeAttributesMask */ { UA_TYPENAME("NodeAttributesMask") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {348}}, /* .typeId */ sizeof(UA_NodeAttributesMask), /* .memSize */ UA_TYPES_INT32, /* .typeIndex */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ NodeAttributesMask_members /* .members */ }, /* EventFilterResult */ { UA_TYPENAME("EventFilterResult") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {734}}, /* .typeId */ sizeof(UA_EventFilterResult), /* .memSize */ UA_TYPES_EVENTFILTERRESULT, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 736, /* .binaryEncodingId */ EventFilterResult_members /* .members */ }, /* MonitoredItemCreateRequest */ { UA_TYPENAME("MonitoredItemCreateRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {743}}, /* .typeId */ sizeof(UA_MonitoredItemCreateRequest), /* .memSize */ UA_TYPES_MONITOREDITEMCREATEREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 745, /* .binaryEncodingId */ MonitoredItemCreateRequest_members /* .members */ }, /* Range */ { UA_TYPENAME("Range") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {884}}, /* .typeId */ sizeof(UA_Range), /* .memSize */ UA_TYPES_RANGE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ true && UA_BINARY_OVERLAYABLE_FLOAT && UA_BINARY_OVERLAYABLE_FLOAT && offsetof(UA_Range, high) == (offsetof(UA_Range, low) + sizeof(UA_Double)), /* .overlayable */ 2, /* .membersSize */ 886, /* .binaryEncodingId */ Range_members /* .members */ }, /* DataChangeNotification */ { UA_TYPENAME("DataChangeNotification") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {809}}, /* .typeId */ sizeof(UA_DataChangeNotification), /* .memSize */ UA_TYPES_DATACHANGENOTIFICATION, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 811, /* .binaryEncodingId */ DataChangeNotification_members /* .members */ }, /* Argument */ { UA_TYPENAME("Argument") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {296}}, /* .typeId */ sizeof(UA_Argument), /* .memSize */ UA_TYPES_ARGUMENT, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ 298, /* .binaryEncodingId */ Argument_members /* .members */ }, /* ChannelSecurityToken */ { UA_TYPENAME("ChannelSecurityToken") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {441}}, /* .typeId */ sizeof(UA_ChannelSecurityToken), /* .memSize */ UA_TYPES_CHANNELSECURITYTOKEN, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ true && UA_BINARY_OVERLAYABLE_INTEGER && UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_ChannelSecurityToken, tokenId) == (offsetof(UA_ChannelSecurityToken, channelId) + sizeof(UA_UInt32)) && UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_ChannelSecurityToken, createdAt) == (offsetof(UA_ChannelSecurityToken, tokenId) + sizeof(UA_UInt32)) && UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_ChannelSecurityToken, revisedLifetime) == (offsetof(UA_ChannelSecurityToken, createdAt) + sizeof(UA_DateTime)), /* .overlayable */ 4, /* .membersSize */ 443, /* .binaryEncodingId */ ChannelSecurityToken_members /* .members */ }, /* ServerState */ { UA_TYPENAME("ServerState") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {852}}, /* .typeId */ sizeof(UA_ServerState), /* .memSize */ UA_TYPES_INT32, /* .typeIndex */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ ServerState_members /* .members */ }, /* EventNotificationList */ { UA_TYPENAME("EventNotificationList") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {914}}, /* .typeId */ sizeof(UA_EventNotificationList), /* .memSize */ UA_TYPES_EVENTNOTIFICATIONLIST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ 916, /* .binaryEncodingId */ EventNotificationList_members /* .members */ }, /* AnonymousIdentityToken */ { UA_TYPENAME("AnonymousIdentityToken") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {319}}, /* .typeId */ sizeof(UA_AnonymousIdentityToken), /* .memSize */ UA_TYPES_ANONYMOUSIDENTITYTOKEN, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ 321, /* .binaryEncodingId */ AnonymousIdentityToken_members /* .members */ }, /* FilterOperator */ { UA_TYPENAME("FilterOperator") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {576}}, /* .typeId */ sizeof(UA_FilterOperator), /* .memSize */ UA_TYPES_INT32, /* .typeIndex */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ FilterOperator_members /* .members */ }, /* AggregateFilter */ { UA_TYPENAME("AggregateFilter") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {728}}, /* .typeId */ sizeof(UA_AggregateFilter), /* .memSize */ UA_TYPES_AGGREGATEFILTER, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ 730, /* .binaryEncodingId */ AggregateFilter_members /* .members */ }, /* RepublishResponse */ { UA_TYPENAME("RepublishResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {833}}, /* .typeId */ sizeof(UA_RepublishResponse), /* .memSize */ UA_TYPES_REPUBLISHRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 835, /* .binaryEncodingId */ RepublishResponse_members /* .members */ }, /* DeleteSubscriptionsResponse */ { UA_TYPENAME("DeleteSubscriptionsResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {848}}, /* .typeId */ sizeof(UA_DeleteSubscriptionsResponse), /* .memSize */ UA_TYPES_DELETESUBSCRIPTIONSRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 850, /* .binaryEncodingId */ DeleteSubscriptionsResponse_members /* .members */ }, /* RegisterNodesRequest */ { UA_TYPENAME("RegisterNodesRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {558}}, /* .typeId */ sizeof(UA_RegisterNodesRequest), /* .memSize */ UA_TYPES_REGISTERNODESREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 560, /* .binaryEncodingId */ RegisterNodesRequest_members /* .members */ }, /* MethodAttributes */ { UA_TYPENAME("MethodAttributes") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {358}}, /* .typeId */ sizeof(UA_MethodAttributes), /* .memSize */ UA_TYPES_METHODATTRIBUTES, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 7, /* .membersSize */ 360, /* .binaryEncodingId */ MethodAttributes_members /* .members */ }, /* UserNameIdentityToken */ { UA_TYPENAME("UserNameIdentityToken") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {322}}, /* .typeId */ sizeof(UA_UserNameIdentityToken), /* .memSize */ UA_TYPES_USERNAMEIDENTITYTOKEN, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ 324, /* .binaryEncodingId */ UserNameIdentityToken_members /* .members */ }, /* UnregisterNodesRequest */ { UA_TYPENAME("UnregisterNodesRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {564}}, /* .typeId */ sizeof(UA_UnregisterNodesRequest), /* .memSize */ UA_TYPES_UNREGISTERNODESREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 566, /* .binaryEncodingId */ UnregisterNodesRequest_members /* .members */ }, /* OpenSecureChannelResponse */ { UA_TYPENAME("OpenSecureChannelResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {447}}, /* .typeId */ sizeof(UA_OpenSecureChannelResponse), /* .memSize */ UA_TYPES_OPENSECURECHANNELRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ 449, /* .binaryEncodingId */ OpenSecureChannelResponse_members /* .members */ }, /* SetTriggeringResponse */ { UA_TYPENAME("SetTriggeringResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {776}}, /* .typeId */ sizeof(UA_SetTriggeringResponse), /* .memSize */ UA_TYPES_SETTRIGGERINGRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ 778, /* .binaryEncodingId */ SetTriggeringResponse_members /* .members */ }, /* SimpleAttributeOperand */ { UA_TYPENAME("SimpleAttributeOperand") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {601}}, /* .typeId */ sizeof(UA_SimpleAttributeOperand), /* .memSize */ UA_TYPES_SIMPLEATTRIBUTEOPERAND, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ 603, /* .binaryEncodingId */ SimpleAttributeOperand_members /* .members */ }, /* RepublishRequest */ { UA_TYPENAME("RepublishRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {830}}, /* .typeId */ sizeof(UA_RepublishRequest), /* .memSize */ UA_TYPES_REPUBLISHREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 832, /* .binaryEncodingId */ RepublishRequest_members /* .members */ }, /* RegisterNodesResponse */ { UA_TYPENAME("RegisterNodesResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {561}}, /* .typeId */ sizeof(UA_RegisterNodesResponse), /* .memSize */ UA_TYPES_REGISTERNODESRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 563, /* .binaryEncodingId */ RegisterNodesResponse_members /* .members */ }, /* ModifyMonitoredItemsResponse */ { UA_TYPENAME("ModifyMonitoredItemsResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {764}}, /* .typeId */ sizeof(UA_ModifyMonitoredItemsResponse), /* .memSize */ UA_TYPES_MODIFYMONITOREDITEMSRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 766, /* .binaryEncodingId */ ModifyMonitoredItemsResponse_members /* .members */ }, /* DeleteSubscriptionsRequest */ { UA_TYPENAME("DeleteSubscriptionsRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {845}}, /* .typeId */ sizeof(UA_DeleteSubscriptionsRequest), /* .memSize */ UA_TYPES_DELETESUBSCRIPTIONSREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 847, /* .binaryEncodingId */ DeleteSubscriptionsRequest_members /* .members */ }, /* RedundancySupport */ { UA_TYPENAME("RedundancySupport") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {851}}, /* .typeId */ sizeof(UA_RedundancySupport), /* .memSize */ UA_TYPES_INT32, /* .typeIndex */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ RedundancySupport_members /* .members */ }, /* BrowsePath */ { UA_TYPENAME("BrowsePath") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {543}}, /* .typeId */ sizeof(UA_BrowsePath), /* .memSize */ UA_TYPES_BROWSEPATH, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 545, /* .binaryEncodingId */ BrowsePath_members /* .members */ }, /* ObjectAttributes */ { UA_TYPENAME("ObjectAttributes") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {352}}, /* .typeId */ sizeof(UA_ObjectAttributes), /* .memSize */ UA_TYPES_OBJECTATTRIBUTES, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 6, /* .membersSize */ 354, /* .binaryEncodingId */ ObjectAttributes_members /* .members */ }, /* PublishRequest */ { UA_TYPENAME("PublishRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {824}}, /* .typeId */ sizeof(UA_PublishRequest), /* .memSize */ UA_TYPES_PUBLISHREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 826, /* .binaryEncodingId */ PublishRequest_members /* .members */ }, /* FindServersRequest */ { UA_TYPENAME("FindServersRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {420}}, /* .typeId */ sizeof(UA_FindServersRequest), /* .memSize */ UA_TYPES_FINDSERVERSREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ 422, /* .binaryEncodingId */ FindServersRequest_members /* .members */ }, /* FindServersOnNetworkResponse */ { UA_TYPENAME("FindServersOnNetworkResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {12191}}, /* .typeId */ sizeof(UA_FindServersOnNetworkResponse), /* .memSize */ UA_TYPES_FINDSERVERSONNETWORKRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 12209, /* .binaryEncodingId */ FindServersOnNetworkResponse_members /* .members */ }, /* ReferenceDescription */ { UA_TYPENAME("ReferenceDescription") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {518}}, /* .typeId */ sizeof(UA_ReferenceDescription), /* .memSize */ UA_TYPES_REFERENCEDESCRIPTION, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 7, /* .membersSize */ 520, /* .binaryEncodingId */ ReferenceDescription_members /* .members */ }, /* CreateSubscriptionRequest */ { UA_TYPENAME("CreateSubscriptionRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {785}}, /* .typeId */ sizeof(UA_CreateSubscriptionRequest), /* .memSize */ UA_TYPES_CREATESUBSCRIPTIONREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 7, /* .membersSize */ 787, /* .binaryEncodingId */ CreateSubscriptionRequest_members /* .members */ }, /* FindServersOnNetworkRequest */ { UA_TYPENAME("FindServersOnNetworkRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {12190}}, /* .typeId */ sizeof(UA_FindServersOnNetworkRequest), /* .memSize */ UA_TYPES_FINDSERVERSONNETWORKREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ 12208, /* .binaryEncodingId */ FindServersOnNetworkRequest_members /* .members */ }, /* CallResponse */ { UA_TYPENAME("CallResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {713}}, /* .typeId */ sizeof(UA_CallResponse), /* .memSize */ UA_TYPES_CALLRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 715, /* .binaryEncodingId */ CallResponse_members /* .members */ }, /* DeleteNodesResponse */ { UA_TYPENAME("DeleteNodesResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {501}}, /* .typeId */ sizeof(UA_DeleteNodesResponse), /* .memSize */ UA_TYPES_DELETENODESRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 503, /* .binaryEncodingId */ DeleteNodesResponse_members /* .members */ }, /* ModifyMonitoredItemsRequest */ { UA_TYPENAME("ModifyMonitoredItemsRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {761}}, /* .typeId */ sizeof(UA_ModifyMonitoredItemsRequest), /* .memSize */ UA_TYPES_MODIFYMONITOREDITEMSREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ 763, /* .binaryEncodingId */ ModifyMonitoredItemsRequest_members /* .members */ }, /* ServiceFault */ { UA_TYPENAME("ServiceFault") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {395}}, /* .typeId */ sizeof(UA_ServiceFault), /* .memSize */ UA_TYPES_SERVICEFAULT, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ 397, /* .binaryEncodingId */ ServiceFault_members /* .members */ }, /* PublishResponse */ { UA_TYPENAME("PublishResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {827}}, /* .typeId */ sizeof(UA_PublishResponse), /* .memSize */ UA_TYPES_PUBLISHRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 7, /* .membersSize */ 829, /* .binaryEncodingId */ PublishResponse_members /* .members */ }, /* CreateMonitoredItemsRequest */ { UA_TYPENAME("CreateMonitoredItemsRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {749}}, /* .typeId */ sizeof(UA_CreateMonitoredItemsRequest), /* .memSize */ UA_TYPES_CREATEMONITOREDITEMSREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ 751, /* .binaryEncodingId */ CreateMonitoredItemsRequest_members /* .members */ }, /* OpenSecureChannelRequest */ { UA_TYPENAME("OpenSecureChannelRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {444}}, /* .typeId */ sizeof(UA_OpenSecureChannelRequest), /* .memSize */ UA_TYPES_OPENSECURECHANNELREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 6, /* .membersSize */ 446, /* .binaryEncodingId */ OpenSecureChannelRequest_members /* .members */ }, /* CloseSessionRequest */ { UA_TYPENAME("CloseSessionRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {471}}, /* .typeId */ sizeof(UA_CloseSessionRequest), /* .memSize */ UA_TYPES_CLOSESESSIONREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 473, /* .binaryEncodingId */ CloseSessionRequest_members /* .members */ }, /* SetTriggeringRequest */ { UA_TYPENAME("SetTriggeringRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {773}}, /* .typeId */ sizeof(UA_SetTriggeringRequest), /* .memSize */ UA_TYPES_SETTRIGGERINGREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ 775, /* .binaryEncodingId */ SetTriggeringRequest_members /* .members */ }, /* BrowseResult */ { UA_TYPENAME("BrowseResult") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {522}}, /* .typeId */ sizeof(UA_BrowseResult), /* .memSize */ UA_TYPES_BROWSERESULT, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 524, /* .binaryEncodingId */ BrowseResult_members /* .members */ }, /* AddReferencesRequest */ { UA_TYPENAME("AddReferencesRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {492}}, /* .typeId */ sizeof(UA_AddReferencesRequest), /* .memSize */ UA_TYPES_ADDREFERENCESREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 494, /* .binaryEncodingId */ AddReferencesRequest_members /* .members */ }, /* AddNodesItem */ { UA_TYPENAME("AddNodesItem") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {376}}, /* .typeId */ sizeof(UA_AddNodesItem), /* .memSize */ UA_TYPES_ADDNODESITEM, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 7, /* .membersSize */ 378, /* .binaryEncodingId */ AddNodesItem_members /* .members */ }, /* ServerStatusDataType */ { UA_TYPENAME("ServerStatusDataType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {862}}, /* .typeId */ sizeof(UA_ServerStatusDataType), /* .memSize */ UA_TYPES_SERVERSTATUSDATATYPE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 6, /* .membersSize */ 864, /* .binaryEncodingId */ ServerStatusDataType_members /* .members */ }, /* BrowseNextResponse */ { UA_TYPENAME("BrowseNextResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {534}}, /* .typeId */ sizeof(UA_BrowseNextResponse), /* .memSize */ UA_TYPES_BROWSENEXTRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 536, /* .binaryEncodingId */ BrowseNextResponse_members /* .members */ }, /* RegisteredServer */ { UA_TYPENAME("RegisteredServer") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {432}}, /* .typeId */ sizeof(UA_RegisteredServer), /* .memSize */ UA_TYPES_REGISTEREDSERVER, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 8, /* .membersSize */ 434, /* .binaryEncodingId */ RegisteredServer_members /* .members */ }, /* ApplicationDescription */ { UA_TYPENAME("ApplicationDescription") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {308}}, /* .typeId */ sizeof(UA_ApplicationDescription), /* .memSize */ UA_TYPES_APPLICATIONDESCRIPTION, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 7, /* .membersSize */ 310, /* .binaryEncodingId */ ApplicationDescription_members /* .members */ }, /* ReadRequest */ { UA_TYPENAME("ReadRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {629}}, /* .typeId */ sizeof(UA_ReadRequest), /* .memSize */ UA_TYPES_READREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ 631, /* .binaryEncodingId */ ReadRequest_members /* .members */ }, /* ActivateSessionRequest */ { UA_TYPENAME("ActivateSessionRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {465}}, /* .typeId */ sizeof(UA_ActivateSessionRequest), /* .memSize */ UA_TYPES_ACTIVATESESSIONREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 6, /* .membersSize */ 467, /* .binaryEncodingId */ ActivateSessionRequest_members /* .members */ }, /* BrowsePathResult */ { UA_TYPENAME("BrowsePathResult") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {549}}, /* .typeId */ sizeof(UA_BrowsePathResult), /* .memSize */ UA_TYPES_BROWSEPATHRESULT, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 551, /* .binaryEncodingId */ BrowsePathResult_members /* .members */ }, /* AddNodesRequest */ { UA_TYPENAME("AddNodesRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {486}}, /* .typeId */ sizeof(UA_AddNodesRequest), /* .memSize */ UA_TYPES_ADDNODESREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 488, /* .binaryEncodingId */ AddNodesRequest_members /* .members */ }, /* BrowseRequest */ { UA_TYPENAME("BrowseRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {525}}, /* .typeId */ sizeof(UA_BrowseRequest), /* .memSize */ UA_TYPES_BROWSEREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ 527, /* .binaryEncodingId */ BrowseRequest_members /* .members */ }, /* WriteRequest */ { UA_TYPENAME("WriteRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {671}}, /* .typeId */ sizeof(UA_WriteRequest), /* .memSize */ UA_TYPES_WRITEREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 673, /* .binaryEncodingId */ WriteRequest_members /* .members */ }, /* AddNodesResponse */ { UA_TYPENAME("AddNodesResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {489}}, /* .typeId */ sizeof(UA_AddNodesResponse), /* .memSize */ UA_TYPES_ADDNODESRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 491, /* .binaryEncodingId */ AddNodesResponse_members /* .members */ }, /* RegisterServer2Request */ { UA_TYPENAME("RegisterServer2Request") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {12193}}, /* .typeId */ sizeof(UA_RegisterServer2Request), /* .memSize */ UA_TYPES_REGISTERSERVER2REQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 12211, /* .binaryEncodingId */ RegisterServer2Request_members /* .members */ }, /* AttributeOperand */ { UA_TYPENAME("AttributeOperand") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {598}}, /* .typeId */ sizeof(UA_AttributeOperand), /* .memSize */ UA_TYPES_ATTRIBUTEOPERAND, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ 600, /* .binaryEncodingId */ AttributeOperand_members /* .members */ }, /* DataChangeFilter */ { UA_TYPENAME("DataChangeFilter") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {722}}, /* .typeId */ sizeof(UA_DataChangeFilter), /* .memSize */ UA_TYPES_DATACHANGEFILTER, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ true && UA_BINARY_OVERLAYABLE_INTEGER && UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_DataChangeFilter, deadbandType) == (offsetof(UA_DataChangeFilter, trigger) + sizeof(UA_DataChangeTrigger)) && UA_BINARY_OVERLAYABLE_FLOAT && offsetof(UA_DataChangeFilter, deadbandValue) == (offsetof(UA_DataChangeFilter, deadbandType) + sizeof(UA_UInt32)), /* .overlayable */ 3, /* .membersSize */ 724, /* .binaryEncodingId */ DataChangeFilter_members /* .members */ }, /* EndpointDescription */ { UA_TYPENAME("EndpointDescription") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {312}}, /* .typeId */ sizeof(UA_EndpointDescription), /* .memSize */ UA_TYPES_ENDPOINTDESCRIPTION, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 8, /* .membersSize */ 314, /* .binaryEncodingId */ EndpointDescription_members /* .members */ }, /* DeleteReferencesRequest */ { UA_TYPENAME("DeleteReferencesRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {504}}, /* .typeId */ sizeof(UA_DeleteReferencesRequest), /* .memSize */ UA_TYPES_DELETEREFERENCESREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 506, /* .binaryEncodingId */ DeleteReferencesRequest_members /* .members */ }, /* TranslateBrowsePathsToNodeIdsRequest */ { UA_TYPENAME("TranslateBrowsePathsToNodeIdsRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {552}}, /* .typeId */ sizeof(UA_TranslateBrowsePathsToNodeIdsRequest), /* .memSize */ UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 554, /* .binaryEncodingId */ TranslateBrowsePathsToNodeIdsRequest_members /* .members */ }, /* FindServersResponse */ { UA_TYPENAME("FindServersResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {423}}, /* .typeId */ sizeof(UA_FindServersResponse), /* .memSize */ UA_TYPES_FINDSERVERSRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 425, /* .binaryEncodingId */ FindServersResponse_members /* .members */ }, /* CreateSessionRequest */ { UA_TYPENAME("CreateSessionRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {459}}, /* .typeId */ sizeof(UA_CreateSessionRequest), /* .memSize */ UA_TYPES_CREATESESSIONREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 9, /* .membersSize */ 461, /* .binaryEncodingId */ CreateSessionRequest_members /* .members */ }, /* ContentFilterElement */ { UA_TYPENAME("ContentFilterElement") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {583}}, /* .typeId */ sizeof(UA_ContentFilterElement), /* .memSize */ UA_TYPES_CONTENTFILTERELEMENT, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 585, /* .binaryEncodingId */ ContentFilterElement_members /* .members */ }, /* RegisterServerRequest */ { UA_TYPENAME("RegisterServerRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {435}}, /* .typeId */ sizeof(UA_RegisterServerRequest), /* .memSize */ UA_TYPES_REGISTERSERVERREQUEST, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 437, /* .binaryEncodingId */ RegisterServerRequest_members /* .members */ }, /* TranslateBrowsePathsToNodeIdsResponse */ { UA_TYPENAME("TranslateBrowsePathsToNodeIdsResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {555}}, /* .typeId */ sizeof(UA_TranslateBrowsePathsToNodeIdsResponse), /* .memSize */ UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 557, /* .binaryEncodingId */ TranslateBrowsePathsToNodeIdsResponse_members /* .members */ }, /* BrowseResponse */ { UA_TYPENAME("BrowseResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {528}}, /* .typeId */ sizeof(UA_BrowseResponse), /* .memSize */ UA_TYPES_BROWSERESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 530, /* .binaryEncodingId */ BrowseResponse_members /* .members */ }, /* CreateSessionResponse */ { UA_TYPENAME("CreateSessionResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {462}}, /* .typeId */ sizeof(UA_CreateSessionResponse), /* .memSize */ UA_TYPES_CREATESESSIONRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 10, /* .membersSize */ 464, /* .binaryEncodingId */ CreateSessionResponse_members /* .members */ }, /* ContentFilter */ { UA_TYPENAME("ContentFilter") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {586}}, /* .typeId */ sizeof(UA_ContentFilter), /* .memSize */ UA_TYPES_CONTENTFILTER, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ 588, /* .binaryEncodingId */ ContentFilter_members /* .members */ }, /* GetEndpointsResponse */ { UA_TYPENAME("GetEndpointsResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {429}}, /* .typeId */ sizeof(UA_GetEndpointsResponse), /* .memSize */ UA_TYPES_GETENDPOINTSRESPONSE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 431, /* .binaryEncodingId */ GetEndpointsResponse_members /* .members */ }, /* EventFilter */ { UA_TYPENAME("EventFilter") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {725}}, /* .typeId */ sizeof(UA_EventFilter), /* .memSize */ UA_TYPES_EVENTFILTER, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 727, /* .binaryEncodingId */ EventFilter_members /* .members */ }, }; /*********************************** amalgamated original file "/home/jvoe/open62541/build/src_generated/open62541/transport_generated.c" ***********************************/ /* Generated from Opc.Ua.Types.bsd, Custom.Opc.Ua.Transport.bsd with script /home/jvoe/open62541/tools/generate_datatypes.py * on host rigel by user jvoe at 2019-09-27 03:59:36 */ /* SecureConversationMessageAbortBody */ static UA_DataTypeMember SecureConversationMessageAbortBody_members[2] = { { UA_TYPENAME("Error") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Reason") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_SecureConversationMessageAbortBody, reason) - offsetof(UA_SecureConversationMessageAbortBody, error) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* SecureConversationMessageFooter */ static UA_DataTypeMember SecureConversationMessageFooter_members[2] = { { UA_TYPENAME("Padding") /* .memberName */ UA_TYPES_BYTE, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ true /* .isArray */ }, { UA_TYPENAME("Signature") /* .memberName */ UA_TYPES_BYTE, /* .memberTypeIndex */ offsetof(UA_SecureConversationMessageFooter, signature) - offsetof(UA_SecureConversationMessageFooter, padding) - sizeof(void*), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* TcpHelloMessage */ static UA_DataTypeMember TcpHelloMessage_members[6] = { { UA_TYPENAME("ProtocolVersion") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ReceiveBufferSize") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_TcpHelloMessage, receiveBufferSize) - offsetof(UA_TcpHelloMessage, protocolVersion) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SendBufferSize") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_TcpHelloMessage, sendBufferSize) - offsetof(UA_TcpHelloMessage, receiveBufferSize) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("MaxMessageSize") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_TcpHelloMessage, maxMessageSize) - offsetof(UA_TcpHelloMessage, sendBufferSize) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("MaxChunkCount") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_TcpHelloMessage, maxChunkCount) - offsetof(UA_TcpHelloMessage, maxMessageSize) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("EndpointUrl") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_TcpHelloMessage, endpointUrl) - offsetof(UA_TcpHelloMessage, maxChunkCount) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* TcpErrorMessage */ static UA_DataTypeMember TcpErrorMessage_members[2] = { { UA_TYPENAME("Error") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("Reason") /* .memberName */ UA_TYPES_STRING, /* .memberTypeIndex */ offsetof(UA_TcpErrorMessage, reason) - offsetof(UA_TcpErrorMessage, error) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* MessageType */ #define MessageType_members NULL /* AsymmetricAlgorithmSecurityHeader */ static UA_DataTypeMember AsymmetricAlgorithmSecurityHeader_members[3] = { { UA_TYPENAME("SecurityPolicyUri") /* .memberName */ UA_TYPES_BYTESTRING, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SenderCertificate") /* .memberName */ UA_TYPES_BYTESTRING, /* .memberTypeIndex */ offsetof(UA_AsymmetricAlgorithmSecurityHeader, senderCertificate) - offsetof(UA_AsymmetricAlgorithmSecurityHeader, securityPolicyUri) - sizeof(UA_ByteString), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ReceiverCertificateThumbprint") /* .memberName */ UA_TYPES_BYTESTRING, /* .memberTypeIndex */ offsetof(UA_AsymmetricAlgorithmSecurityHeader, receiverCertificateThumbprint) - offsetof(UA_AsymmetricAlgorithmSecurityHeader, senderCertificate) - sizeof(UA_ByteString), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* TcpAcknowledgeMessage */ static UA_DataTypeMember TcpAcknowledgeMessage_members[5] = { { UA_TYPENAME("ProtocolVersion") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("ReceiveBufferSize") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_TcpAcknowledgeMessage, receiveBufferSize) - offsetof(UA_TcpAcknowledgeMessage, protocolVersion) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SendBufferSize") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_TcpAcknowledgeMessage, sendBufferSize) - offsetof(UA_TcpAcknowledgeMessage, receiveBufferSize) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("MaxMessageSize") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_TcpAcknowledgeMessage, maxMessageSize) - offsetof(UA_TcpAcknowledgeMessage, sendBufferSize) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("MaxChunkCount") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_TcpAcknowledgeMessage, maxChunkCount) - offsetof(UA_TcpAcknowledgeMessage, maxMessageSize) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* SequenceHeader */ static UA_DataTypeMember SequenceHeader_members[2] = { { UA_TYPENAME("SequenceNumber") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("RequestId") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_SequenceHeader, requestId) - offsetof(UA_SequenceHeader, sequenceNumber) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* TcpMessageHeader */ static UA_DataTypeMember TcpMessageHeader_members[2] = { { UA_TYPENAME("MessageTypeAndChunkType") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("MessageSize") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_TcpMessageHeader, messageSize) - offsetof(UA_TcpMessageHeader, messageTypeAndChunkType) - sizeof(UA_UInt32), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* ChunkType */ #define ChunkType_members NULL /* SymmetricAlgorithmSecurityHeader */ static UA_DataTypeMember SymmetricAlgorithmSecurityHeader_members[1] = { { UA_TYPENAME("TokenId") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ 0, /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; /* SecureConversationMessageHeader */ static UA_DataTypeMember SecureConversationMessageHeader_members[2] = { { UA_TYPENAME("MessageHeader") /* .memberName */ UA_TRANSPORT_TCPMESSAGEHEADER, /* .memberTypeIndex */ 0, /* .padding */ false, /* .namespaceZero */ false /* .isArray */ }, { UA_TYPENAME("SecureChannelId") /* .memberName */ UA_TYPES_UINT32, /* .memberTypeIndex */ offsetof(UA_SecureConversationMessageHeader, secureChannelId) - offsetof(UA_SecureConversationMessageHeader, messageHeader) - sizeof(UA_TcpMessageHeader), /* .padding */ true, /* .namespaceZero */ false /* .isArray */ },}; const UA_DataType UA_TRANSPORT[UA_TRANSPORT_COUNT] = { /* SecureConversationMessageAbortBody */ { UA_TYPENAME("SecureConversationMessageAbortBody") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .typeId */ sizeof(UA_SecureConversationMessageAbortBody), /* .memSize */ UA_TRANSPORT_SECURECONVERSATIONMESSAGEABORTBODY, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 0, /* .binaryEncodingId */ SecureConversationMessageAbortBody_members /* .members */ }, /* SecureConversationMessageFooter */ { UA_TYPENAME("SecureConversationMessageFooter") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .typeId */ sizeof(UA_SecureConversationMessageFooter), /* .memSize */ UA_TRANSPORT_SECURECONVERSATIONMESSAGEFOOTER, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 0, /* .binaryEncodingId */ SecureConversationMessageFooter_members /* .members */ }, /* TcpHelloMessage */ { UA_TYPENAME("TcpHelloMessage") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .typeId */ sizeof(UA_TcpHelloMessage), /* .memSize */ UA_TRANSPORT_TCPHELLOMESSAGE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 6, /* .membersSize */ 0, /* .binaryEncodingId */ TcpHelloMessage_members /* .members */ }, /* TcpErrorMessage */ { UA_TYPENAME("TcpErrorMessage") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .typeId */ sizeof(UA_TcpErrorMessage), /* .memSize */ UA_TRANSPORT_TCPERRORMESSAGE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ 0, /* .binaryEncodingId */ TcpErrorMessage_members /* .members */ }, /* MessageType */ { UA_TYPENAME("MessageType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .typeId */ sizeof(UA_MessageType), /* .memSize */ UA_TYPES_INT32, /* .typeIndex */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ MessageType_members /* .members */ }, /* AsymmetricAlgorithmSecurityHeader */ { UA_TYPENAME("AsymmetricAlgorithmSecurityHeader") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .typeId */ sizeof(UA_AsymmetricAlgorithmSecurityHeader), /* .memSize */ UA_TRANSPORT_ASYMMETRICALGORITHMSECURITYHEADER, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ 0, /* .binaryEncodingId */ AsymmetricAlgorithmSecurityHeader_members /* .members */ }, /* TcpAcknowledgeMessage */ { UA_TYPENAME("TcpAcknowledgeMessage") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .typeId */ sizeof(UA_TcpAcknowledgeMessage), /* .memSize */ UA_TRANSPORT_TCPACKNOWLEDGEMESSAGE, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ true && UA_BINARY_OVERLAYABLE_INTEGER && UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_TcpAcknowledgeMessage, receiveBufferSize) == (offsetof(UA_TcpAcknowledgeMessage, protocolVersion) + sizeof(UA_UInt32)) && UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_TcpAcknowledgeMessage, sendBufferSize) == (offsetof(UA_TcpAcknowledgeMessage, receiveBufferSize) + sizeof(UA_UInt32)) && UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_TcpAcknowledgeMessage, maxMessageSize) == (offsetof(UA_TcpAcknowledgeMessage, sendBufferSize) + sizeof(UA_UInt32)) && UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_TcpAcknowledgeMessage, maxChunkCount) == (offsetof(UA_TcpAcknowledgeMessage, maxMessageSize) + sizeof(UA_UInt32)), /* .overlayable */ 5, /* .membersSize */ 0, /* .binaryEncodingId */ TcpAcknowledgeMessage_members /* .members */ }, /* SequenceHeader */ { UA_TYPENAME("SequenceHeader") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .typeId */ sizeof(UA_SequenceHeader), /* .memSize */ UA_TRANSPORT_SEQUENCEHEADER, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ true && UA_BINARY_OVERLAYABLE_INTEGER && UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_SequenceHeader, requestId) == (offsetof(UA_SequenceHeader, sequenceNumber) + sizeof(UA_UInt32)), /* .overlayable */ 2, /* .membersSize */ 0, /* .binaryEncodingId */ SequenceHeader_members /* .members */ }, /* TcpMessageHeader */ { UA_TYPENAME("TcpMessageHeader") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .typeId */ sizeof(UA_TcpMessageHeader), /* .memSize */ UA_TRANSPORT_TCPMESSAGEHEADER, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ true && UA_BINARY_OVERLAYABLE_INTEGER && UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_TcpMessageHeader, messageSize) == (offsetof(UA_TcpMessageHeader, messageTypeAndChunkType) + sizeof(UA_UInt32)), /* .overlayable */ 2, /* .membersSize */ 0, /* .binaryEncodingId */ TcpMessageHeader_members /* .members */ }, /* ChunkType */ { UA_TYPENAME("ChunkType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .typeId */ sizeof(UA_ChunkType), /* .memSize */ UA_TYPES_INT32, /* .typeIndex */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ 0, /* .binaryEncodingId */ ChunkType_members /* .members */ }, /* SymmetricAlgorithmSecurityHeader */ { UA_TYPENAME("SymmetricAlgorithmSecurityHeader") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .typeId */ sizeof(UA_SymmetricAlgorithmSecurityHeader), /* .memSize */ UA_TRANSPORT_SYMMETRICALGORITHMSECURITYHEADER, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ true && UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 1, /* .membersSize */ 0, /* .binaryEncodingId */ SymmetricAlgorithmSecurityHeader_members /* .members */ }, /* SecureConversationMessageHeader */ { UA_TYPENAME("SecureConversationMessageHeader") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .typeId */ sizeof(UA_SecureConversationMessageHeader), /* .memSize */ UA_TRANSPORT_SECURECONVERSATIONMESSAGEHEADER, /* .typeIndex */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ true && true && UA_BINARY_OVERLAYABLE_INTEGER && UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_TcpMessageHeader, messageSize) == (offsetof(UA_TcpMessageHeader, messageTypeAndChunkType) + sizeof(UA_UInt32)) && UA_BINARY_OVERLAYABLE_INTEGER && offsetof(UA_SecureConversationMessageHeader, secureChannelId) == (offsetof(UA_SecureConversationMessageHeader, messageHeader) + sizeof(UA_TcpMessageHeader)), /* .overlayable */ 2, /* .membersSize */ 0, /* .binaryEncodingId */ SecureConversationMessageHeader_members /* .members */ }, }; /*********************************** amalgamated original file "/home/jvoe/open62541/build/src_generated/open62541/statuscodes.c" ***********************************/ /********************************************************** * Autogenerated -- do not modify * Generated from /home/jvoe/open62541/tools/schema/StatusCode.csv with script /home/jvoe/open62541/tools/generate_statuscode_descriptions.py *********************************************************/ typedef struct { UA_StatusCode code; const char *name; } UA_StatusCodeName; #ifndef UA_ENABLE_STATUSCODE_DESCRIPTIONS static const char * emptyStatusCodeName = ""; const char * UA_StatusCode_name(UA_StatusCode code) { return emptyStatusCodeName; } #else static const size_t statusCodeDescriptionsSize = 237; static const UA_StatusCodeName statusCodeDescriptions[237] = { {UA_STATUSCODE_GOOD, "Good"}, {UA_STATUSCODE_BADUNEXPECTEDERROR, "BadUnexpectedError"}, {UA_STATUSCODE_BADINTERNALERROR, "BadInternalError"}, {UA_STATUSCODE_BADOUTOFMEMORY, "BadOutOfMemory"}, {UA_STATUSCODE_BADRESOURCEUNAVAILABLE, "BadResourceUnavailable"}, {UA_STATUSCODE_BADCOMMUNICATIONERROR, "BadCommunicationError"}, {UA_STATUSCODE_BADENCODINGERROR, "BadEncodingError"}, {UA_STATUSCODE_BADDECODINGERROR, "BadDecodingError"}, {UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED, "BadEncodingLimitsExceeded"}, {UA_STATUSCODE_BADREQUESTTOOLARGE, "BadRequestTooLarge"}, {UA_STATUSCODE_BADRESPONSETOOLARGE, "BadResponseTooLarge"}, {UA_STATUSCODE_BADUNKNOWNRESPONSE, "BadUnknownResponse"}, {UA_STATUSCODE_BADTIMEOUT, "BadTimeout"}, {UA_STATUSCODE_BADSERVICEUNSUPPORTED, "BadServiceUnsupported"}, {UA_STATUSCODE_BADSHUTDOWN, "BadShutdown"}, {UA_STATUSCODE_BADSERVERNOTCONNECTED, "BadServerNotConnected"}, {UA_STATUSCODE_BADSERVERHALTED, "BadServerHalted"}, {UA_STATUSCODE_BADNOTHINGTODO, "BadNothingToDo"}, {UA_STATUSCODE_BADTOOMANYOPERATIONS, "BadTooManyOperations"}, {UA_STATUSCODE_BADTOOMANYMONITOREDITEMS, "BadTooManyMonitoredItems"}, {UA_STATUSCODE_BADDATATYPEIDUNKNOWN, "BadDataTypeIdUnknown"}, {UA_STATUSCODE_BADCERTIFICATEINVALID, "BadCertificateInvalid"}, {UA_STATUSCODE_BADSECURITYCHECKSFAILED, "BadSecurityChecksFailed"}, {UA_STATUSCODE_BADCERTIFICATEPOLICYCHECKFAILED, "BadCertificatePolicyCheckFailed"}, {UA_STATUSCODE_BADCERTIFICATETIMEINVALID, "BadCertificateTimeInvalid"}, {UA_STATUSCODE_BADCERTIFICATEISSUERTIMEINVALID, "BadCertificateIssuerTimeInvalid"}, {UA_STATUSCODE_BADCERTIFICATEHOSTNAMEINVALID, "BadCertificateHostNameInvalid"}, {UA_STATUSCODE_BADCERTIFICATEURIINVALID, "BadCertificateUriInvalid"}, {UA_STATUSCODE_BADCERTIFICATEUSENOTALLOWED, "BadCertificateUseNotAllowed"}, {UA_STATUSCODE_BADCERTIFICATEISSUERUSENOTALLOWED, "BadCertificateIssuerUseNotAllowed"}, {UA_STATUSCODE_BADCERTIFICATEUNTRUSTED, "BadCertificateUntrusted"}, {UA_STATUSCODE_BADCERTIFICATEREVOCATIONUNKNOWN, "BadCertificateRevocationUnknown"}, {UA_STATUSCODE_BADCERTIFICATEISSUERREVOCATIONUNKNOWN, "BadCertificateIssuerRevocationUnknown"}, {UA_STATUSCODE_BADCERTIFICATEREVOKED, "BadCertificateRevoked"}, {UA_STATUSCODE_BADCERTIFICATEISSUERREVOKED, "BadCertificateIssuerRevoked"}, {UA_STATUSCODE_BADCERTIFICATECHAININCOMPLETE, "BadCertificateChainIncomplete"}, {UA_STATUSCODE_BADUSERACCESSDENIED, "BadUserAccessDenied"}, {UA_STATUSCODE_BADIDENTITYTOKENINVALID, "BadIdentityTokenInvalid"}, {UA_STATUSCODE_BADIDENTITYTOKENREJECTED, "BadIdentityTokenRejected"}, {UA_STATUSCODE_BADSECURECHANNELIDINVALID, "BadSecureChannelIdInvalid"}, {UA_STATUSCODE_BADINVALIDTIMESTAMP, "BadInvalidTimestamp"}, {UA_STATUSCODE_BADNONCEINVALID, "BadNonceInvalid"}, {UA_STATUSCODE_BADSESSIONIDINVALID, "BadSessionIdInvalid"}, {UA_STATUSCODE_BADSESSIONCLOSED, "BadSessionClosed"}, {UA_STATUSCODE_BADSESSIONNOTACTIVATED, "BadSessionNotActivated"}, {UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID, "BadSubscriptionIdInvalid"}, {UA_STATUSCODE_BADREQUESTHEADERINVALID, "BadRequestHeaderInvalid"}, {UA_STATUSCODE_BADTIMESTAMPSTORETURNINVALID, "BadTimestampsToReturnInvalid"}, {UA_STATUSCODE_BADREQUESTCANCELLEDBYCLIENT, "BadRequestCancelledByClient"}, {UA_STATUSCODE_BADTOOMANYARGUMENTS, "BadTooManyArguments"}, {UA_STATUSCODE_BADLICENSEEXPIRED, "BadLicenseExpired"}, {UA_STATUSCODE_BADLICENSELIMITSEXCEEDED, "BadLicenseLimitsExceeded"}, {UA_STATUSCODE_BADLICENSENOTAVAILABLE, "BadLicenseNotAvailable"}, {UA_STATUSCODE_GOODSUBSCRIPTIONTRANSFERRED, "GoodSubscriptionTransferred"}, {UA_STATUSCODE_GOODCOMPLETESASYNCHRONOUSLY, "GoodCompletesAsynchronously"}, {UA_STATUSCODE_GOODOVERLOAD, "GoodOverload"}, {UA_STATUSCODE_GOODCLAMPED, "GoodClamped"}, {UA_STATUSCODE_BADNOCOMMUNICATION, "BadNoCommunication"}, {UA_STATUSCODE_BADWAITINGFORINITIALDATA, "BadWaitingForInitialData"}, {UA_STATUSCODE_BADNODEIDINVALID, "BadNodeIdInvalid"}, {UA_STATUSCODE_BADNODEIDUNKNOWN, "BadNodeIdUnknown"}, {UA_STATUSCODE_BADATTRIBUTEIDINVALID, "BadAttributeIdInvalid"}, {UA_STATUSCODE_BADINDEXRANGEINVALID, "BadIndexRangeInvalid"}, {UA_STATUSCODE_BADINDEXRANGENODATA, "BadIndexRangeNoData"}, {UA_STATUSCODE_BADDATAENCODINGINVALID, "BadDataEncodingInvalid"}, {UA_STATUSCODE_BADDATAENCODINGUNSUPPORTED, "BadDataEncodingUnsupported"}, {UA_STATUSCODE_BADNOTREADABLE, "BadNotReadable"}, {UA_STATUSCODE_BADNOTWRITABLE, "BadNotWritable"}, {UA_STATUSCODE_BADOUTOFRANGE, "BadOutOfRange"}, {UA_STATUSCODE_BADNOTSUPPORTED, "BadNotSupported"}, {UA_STATUSCODE_BADNOTFOUND, "BadNotFound"}, {UA_STATUSCODE_BADOBJECTDELETED, "BadObjectDeleted"}, {UA_STATUSCODE_BADNOTIMPLEMENTED, "BadNotImplemented"}, {UA_STATUSCODE_BADMONITORINGMODEINVALID, "BadMonitoringModeInvalid"}, {UA_STATUSCODE_BADMONITOREDITEMIDINVALID, "BadMonitoredItemIdInvalid"}, {UA_STATUSCODE_BADMONITOREDITEMFILTERINVALID, "BadMonitoredItemFilterInvalid"}, {UA_STATUSCODE_BADMONITOREDITEMFILTERUNSUPPORTED, "BadMonitoredItemFilterUnsupported"}, {UA_STATUSCODE_BADFILTERNOTALLOWED, "BadFilterNotAllowed"}, {UA_STATUSCODE_BADSTRUCTUREMISSING, "BadStructureMissing"}, {UA_STATUSCODE_BADEVENTFILTERINVALID, "BadEventFilterInvalid"}, {UA_STATUSCODE_BADCONTENTFILTERINVALID, "BadContentFilterInvalid"}, {UA_STATUSCODE_BADFILTEROPERATORINVALID, "BadFilterOperatorInvalid"}, {UA_STATUSCODE_BADFILTEROPERATORUNSUPPORTED, "BadFilterOperatorUnsupported"}, {UA_STATUSCODE_BADFILTEROPERANDCOUNTMISMATCH, "BadFilterOperandCountMismatch"}, {UA_STATUSCODE_BADFILTEROPERANDINVALID, "BadFilterOperandInvalid"}, {UA_STATUSCODE_BADFILTERELEMENTINVALID, "BadFilterElementInvalid"}, {UA_STATUSCODE_BADFILTERLITERALINVALID, "BadFilterLiteralInvalid"}, {UA_STATUSCODE_BADCONTINUATIONPOINTINVALID, "BadContinuationPointInvalid"}, {UA_STATUSCODE_BADNOCONTINUATIONPOINTS, "BadNoContinuationPoints"}, {UA_STATUSCODE_BADREFERENCETYPEIDINVALID, "BadReferenceTypeIdInvalid"}, {UA_STATUSCODE_BADBROWSEDIRECTIONINVALID, "BadBrowseDirectionInvalid"}, {UA_STATUSCODE_BADNODENOTINVIEW, "BadNodeNotInView"}, {UA_STATUSCODE_BADNUMERICOVERFLOW, "BadNumericOverflow"}, {UA_STATUSCODE_BADSERVERURIINVALID, "BadServerUriInvalid"}, {UA_STATUSCODE_BADSERVERNAMEMISSING, "BadServerNameMissing"}, {UA_STATUSCODE_BADDISCOVERYURLMISSING, "BadDiscoveryUrlMissing"}, {UA_STATUSCODE_BADSEMPAHOREFILEMISSING, "BadSempahoreFileMissing"}, {UA_STATUSCODE_BADREQUESTTYPEINVALID, "BadRequestTypeInvalid"}, {UA_STATUSCODE_BADSECURITYMODEREJECTED, "BadSecurityModeRejected"}, {UA_STATUSCODE_BADSECURITYPOLICYREJECTED, "BadSecurityPolicyRejected"}, {UA_STATUSCODE_BADTOOMANYSESSIONS, "BadTooManySessions"}, {UA_STATUSCODE_BADUSERSIGNATUREINVALID, "BadUserSignatureInvalid"}, {UA_STATUSCODE_BADAPPLICATIONSIGNATUREINVALID, "BadApplicationSignatureInvalid"}, {UA_STATUSCODE_BADNOVALIDCERTIFICATES, "BadNoValidCertificates"}, {UA_STATUSCODE_BADIDENTITYCHANGENOTSUPPORTED, "BadIdentityChangeNotSupported"}, {UA_STATUSCODE_BADREQUESTCANCELLEDBYREQUEST, "BadRequestCancelledByRequest"}, {UA_STATUSCODE_BADPARENTNODEIDINVALID, "BadParentNodeIdInvalid"}, {UA_STATUSCODE_BADREFERENCENOTALLOWED, "BadReferenceNotAllowed"}, {UA_STATUSCODE_BADNODEIDREJECTED, "BadNodeIdRejected"}, {UA_STATUSCODE_BADNODEIDEXISTS, "BadNodeIdExists"}, {UA_STATUSCODE_BADNODECLASSINVALID, "BadNodeClassInvalid"}, {UA_STATUSCODE_BADBROWSENAMEINVALID, "BadBrowseNameInvalid"}, {UA_STATUSCODE_BADBROWSENAMEDUPLICATED, "BadBrowseNameDuplicated"}, {UA_STATUSCODE_BADNODEATTRIBUTESINVALID, "BadNodeAttributesInvalid"}, {UA_STATUSCODE_BADTYPEDEFINITIONINVALID, "BadTypeDefinitionInvalid"}, {UA_STATUSCODE_BADSOURCENODEIDINVALID, "BadSourceNodeIdInvalid"}, {UA_STATUSCODE_BADTARGETNODEIDINVALID, "BadTargetNodeIdInvalid"}, {UA_STATUSCODE_BADDUPLICATEREFERENCENOTALLOWED, "BadDuplicateReferenceNotAllowed"}, {UA_STATUSCODE_BADINVALIDSELFREFERENCE, "BadInvalidSelfReference"}, {UA_STATUSCODE_BADREFERENCELOCALONLY, "BadReferenceLocalOnly"}, {UA_STATUSCODE_BADNODELETERIGHTS, "BadNoDeleteRights"}, {UA_STATUSCODE_UNCERTAINREFERENCENOTDELETED, "UncertainReferenceNotDeleted"}, {UA_STATUSCODE_BADSERVERINDEXINVALID, "BadServerIndexInvalid"}, {UA_STATUSCODE_BADVIEWIDUNKNOWN, "BadViewIdUnknown"}, {UA_STATUSCODE_BADVIEWTIMESTAMPINVALID, "BadViewTimestampInvalid"}, {UA_STATUSCODE_BADVIEWPARAMETERMISMATCH, "BadViewParameterMismatch"}, {UA_STATUSCODE_BADVIEWVERSIONINVALID, "BadViewVersionInvalid"}, {UA_STATUSCODE_UNCERTAINNOTALLNODESAVAILABLE, "UncertainNotAllNodesAvailable"}, {UA_STATUSCODE_GOODRESULTSMAYBEINCOMPLETE, "GoodResultsMayBeIncomplete"}, {UA_STATUSCODE_BADNOTTYPEDEFINITION, "BadNotTypeDefinition"}, {UA_STATUSCODE_UNCERTAINREFERENCEOUTOFSERVER, "UncertainReferenceOutOfServer"}, {UA_STATUSCODE_BADTOOMANYMATCHES, "BadTooManyMatches"}, {UA_STATUSCODE_BADQUERYTOOCOMPLEX, "BadQueryTooComplex"}, {UA_STATUSCODE_BADNOMATCH, "BadNoMatch"}, {UA_STATUSCODE_BADMAXAGEINVALID, "BadMaxAgeInvalid"}, {UA_STATUSCODE_BADSECURITYMODEINSUFFICIENT, "BadSecurityModeInsufficient"}, {UA_STATUSCODE_BADHISTORYOPERATIONINVALID, "BadHistoryOperationInvalid"}, {UA_STATUSCODE_BADHISTORYOPERATIONUNSUPPORTED, "BadHistoryOperationUnsupported"}, {UA_STATUSCODE_BADINVALIDTIMESTAMPARGUMENT, "BadInvalidTimestampArgument"}, {UA_STATUSCODE_BADWRITENOTSUPPORTED, "BadWriteNotSupported"}, {UA_STATUSCODE_BADTYPEMISMATCH, "BadTypeMismatch"}, {UA_STATUSCODE_BADMETHODINVALID, "BadMethodInvalid"}, {UA_STATUSCODE_BADARGUMENTSMISSING, "BadArgumentsMissing"}, {UA_STATUSCODE_BADNOTEXECUTABLE, "BadNotExecutable"}, {UA_STATUSCODE_BADTOOMANYSUBSCRIPTIONS, "BadTooManySubscriptions"}, {UA_STATUSCODE_BADTOOMANYPUBLISHREQUESTS, "BadTooManyPublishRequests"}, {UA_STATUSCODE_BADNOSUBSCRIPTION, "BadNoSubscription"}, {UA_STATUSCODE_BADSEQUENCENUMBERUNKNOWN, "BadSequenceNumberUnknown"}, {UA_STATUSCODE_BADMESSAGENOTAVAILABLE, "BadMessageNotAvailable"}, {UA_STATUSCODE_BADINSUFFICIENTCLIENTPROFILE, "BadInsufficientClientProfile"}, {UA_STATUSCODE_BADSTATENOTACTIVE, "BadStateNotActive"}, {UA_STATUSCODE_BADALREADYEXISTS, "BadAlreadyExists"}, {UA_STATUSCODE_BADTCPSERVERTOOBUSY, "BadTcpServerTooBusy"}, {UA_STATUSCODE_BADTCPMESSAGETYPEINVALID, "BadTcpMessageTypeInvalid"}, {UA_STATUSCODE_BADTCPSECURECHANNELUNKNOWN, "BadTcpSecureChannelUnknown"}, {UA_STATUSCODE_BADTCPMESSAGETOOLARGE, "BadTcpMessageTooLarge"}, {UA_STATUSCODE_BADTCPNOTENOUGHRESOURCES, "BadTcpNotEnoughResources"}, {UA_STATUSCODE_BADTCPINTERNALERROR, "BadTcpInternalError"}, {UA_STATUSCODE_BADTCPENDPOINTURLINVALID, "BadTcpEndpointUrlInvalid"}, {UA_STATUSCODE_BADREQUESTINTERRUPTED, "BadRequestInterrupted"}, {UA_STATUSCODE_BADREQUESTTIMEOUT, "BadRequestTimeout"}, {UA_STATUSCODE_BADSECURECHANNELCLOSED, "BadSecureChannelClosed"}, {UA_STATUSCODE_BADSECURECHANNELTOKENUNKNOWN, "BadSecureChannelTokenUnknown"}, {UA_STATUSCODE_BADSEQUENCENUMBERINVALID, "BadSequenceNumberInvalid"}, {UA_STATUSCODE_BADPROTOCOLVERSIONUNSUPPORTED, "BadProtocolVersionUnsupported"}, {UA_STATUSCODE_BADCONFIGURATIONERROR, "BadConfigurationError"}, {UA_STATUSCODE_BADNOTCONNECTED, "BadNotConnected"}, {UA_STATUSCODE_BADDEVICEFAILURE, "BadDeviceFailure"}, {UA_STATUSCODE_BADSENSORFAILURE, "BadSensorFailure"}, {UA_STATUSCODE_BADOUTOFSERVICE, "BadOutOfService"}, {UA_STATUSCODE_BADDEADBANDFILTERINVALID, "BadDeadbandFilterInvalid"}, {UA_STATUSCODE_UNCERTAINNOCOMMUNICATIONLASTUSABLEVALUE, "UncertainNoCommunicationLastUsableValue"}, {UA_STATUSCODE_UNCERTAINLASTUSABLEVALUE, "UncertainLastUsableValue"}, {UA_STATUSCODE_UNCERTAINSUBSTITUTEVALUE, "UncertainSubstituteValue"}, {UA_STATUSCODE_UNCERTAININITIALVALUE, "UncertainInitialValue"}, {UA_STATUSCODE_UNCERTAINSENSORNOTACCURATE, "UncertainSensorNotAccurate"}, {UA_STATUSCODE_UNCERTAINENGINEERINGUNITSEXCEEDED, "UncertainEngineeringUnitsExceeded"}, {UA_STATUSCODE_UNCERTAINSUBNORMAL, "UncertainSubNormal"}, {UA_STATUSCODE_GOODLOCALOVERRIDE, "GoodLocalOverride"}, {UA_STATUSCODE_BADREFRESHINPROGRESS, "BadRefreshInProgress"}, {UA_STATUSCODE_BADCONDITIONALREADYDISABLED, "BadConditionAlreadyDisabled"}, {UA_STATUSCODE_BADCONDITIONALREADYENABLED, "BadConditionAlreadyEnabled"}, {UA_STATUSCODE_BADCONDITIONDISABLED, "BadConditionDisabled"}, {UA_STATUSCODE_BADEVENTIDUNKNOWN, "BadEventIdUnknown"}, {UA_STATUSCODE_BADEVENTNOTACKNOWLEDGEABLE, "BadEventNotAcknowledgeable"}, {UA_STATUSCODE_BADDIALOGNOTACTIVE, "BadDialogNotActive"}, {UA_STATUSCODE_BADDIALOGRESPONSEINVALID, "BadDialogResponseInvalid"}, {UA_STATUSCODE_BADCONDITIONBRANCHALREADYACKED, "BadConditionBranchAlreadyAcked"}, {UA_STATUSCODE_BADCONDITIONBRANCHALREADYCONFIRMED, "BadConditionBranchAlreadyConfirmed"}, {UA_STATUSCODE_BADCONDITIONALREADYSHELVED, "BadConditionAlreadyShelved"}, {UA_STATUSCODE_BADCONDITIONNOTSHELVED, "BadConditionNotShelved"}, {UA_STATUSCODE_BADSHELVINGTIMEOUTOFRANGE, "BadShelvingTimeOutOfRange"}, {UA_STATUSCODE_BADNODATA, "BadNoData"}, {UA_STATUSCODE_BADBOUNDNOTFOUND, "BadBoundNotFound"}, {UA_STATUSCODE_BADBOUNDNOTSUPPORTED, "BadBoundNotSupported"}, {UA_STATUSCODE_BADDATALOST, "BadDataLost"}, {UA_STATUSCODE_BADDATAUNAVAILABLE, "BadDataUnavailable"}, {UA_STATUSCODE_BADENTRYEXISTS, "BadEntryExists"}, {UA_STATUSCODE_BADNOENTRYEXISTS, "BadNoEntryExists"}, {UA_STATUSCODE_BADTIMESTAMPNOTSUPPORTED, "BadTimestampNotSupported"}, {UA_STATUSCODE_GOODENTRYINSERTED, "GoodEntryInserted"}, {UA_STATUSCODE_GOODENTRYREPLACED, "GoodEntryReplaced"}, {UA_STATUSCODE_UNCERTAINDATASUBNORMAL, "UncertainDataSubNormal"}, {UA_STATUSCODE_GOODNODATA, "GoodNoData"}, {UA_STATUSCODE_GOODMOREDATA, "GoodMoreData"}, {UA_STATUSCODE_BADAGGREGATELISTMISMATCH, "BadAggregateListMismatch"}, {UA_STATUSCODE_BADAGGREGATENOTSUPPORTED, "BadAggregateNotSupported"}, {UA_STATUSCODE_BADAGGREGATEINVALIDINPUTS, "BadAggregateInvalidInputs"}, {UA_STATUSCODE_BADAGGREGATECONFIGURATIONREJECTED, "BadAggregateConfigurationRejected"}, {UA_STATUSCODE_GOODDATAIGNORED, "GoodDataIgnored"}, {UA_STATUSCODE_BADREQUESTNOTALLOWED, "BadRequestNotAllowed"}, {UA_STATUSCODE_BADREQUESTNOTCOMPLETE, "BadRequestNotComplete"}, {UA_STATUSCODE_GOODEDITED, "GoodEdited"}, {UA_STATUSCODE_GOODPOSTACTIONFAILED, "GoodPostActionFailed"}, {UA_STATUSCODE_UNCERTAINDOMINANTVALUECHANGED, "UncertainDominantValueChanged"}, {UA_STATUSCODE_GOODDEPENDENTVALUECHANGED, "GoodDependentValueChanged"}, {UA_STATUSCODE_BADDOMINANTVALUECHANGED, "BadDominantValueChanged"}, {UA_STATUSCODE_UNCERTAINDEPENDENTVALUECHANGED, "UncertainDependentValueChanged"}, {UA_STATUSCODE_BADDEPENDENTVALUECHANGED, "BadDependentValueChanged"}, {UA_STATUSCODE_GOODCOMMUNICATIONEVENT, "GoodCommunicationEvent"}, {UA_STATUSCODE_GOODSHUTDOWNEVENT, "GoodShutdownEvent"}, {UA_STATUSCODE_GOODCALLAGAIN, "GoodCallAgain"}, {UA_STATUSCODE_GOODNONCRITICALTIMEOUT, "GoodNonCriticalTimeout"}, {UA_STATUSCODE_BADINVALIDARGUMENT, "BadInvalidArgument"}, {UA_STATUSCODE_BADCONNECTIONREJECTED, "BadConnectionRejected"}, {UA_STATUSCODE_BADDISCONNECT, "BadDisconnect"}, {UA_STATUSCODE_BADCONNECTIONCLOSED, "BadConnectionClosed"}, {UA_STATUSCODE_BADINVALIDSTATE, "BadInvalidState"}, {UA_STATUSCODE_BADENDOFSTREAM, "BadEndOfStream"}, {UA_STATUSCODE_BADNODATAAVAILABLE, "BadNoDataAvailable"}, {UA_STATUSCODE_BADWAITINGFORRESPONSE, "BadWaitingForResponse"}, {UA_STATUSCODE_BADOPERATIONABANDONED, "BadOperationAbandoned"}, {UA_STATUSCODE_BADEXPECTEDSTREAMTOBLOCK, "BadExpectedStreamToBlock"}, {UA_STATUSCODE_BADWOULDBLOCK, "BadWouldBlock"}, {UA_STATUSCODE_BADSYNTAXERROR, "BadSyntaxError"}, {UA_STATUSCODE_BADMAXCONNECTIONSREACHED, "BadMaxConnectionsReached"}, {0xffffffff, "Unknown StatusCode"} }; const char * UA_StatusCode_name(UA_StatusCode code) { for (size_t i = 0; i < statusCodeDescriptionsSize; ++i) { if (statusCodeDescriptions[i].code == code) return statusCodeDescriptions[i].name; } return statusCodeDescriptions[statusCodeDescriptionsSize-1].name; } #endif /*********************************** amalgamated original file "/home/jvoe/open62541/src/ua_util.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014, 2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2014 (c) Florian Palm * Copyright 2017 (c) Stefan Profanter, fortiss GmbH */ size_t UA_readNumberWithBase(const UA_Byte *buf, size_t buflen, UA_UInt32 *number, UA_Byte base) { UA_assert(buf); UA_assert(number); u32 n = 0; size_t progress = 0; /* read numbers until the end or a non-number character appears */ while(progress < buflen) { u8 c = buf[progress]; if(c >= '0' && c <= '9' && c <= '0' + (base-1)) n = (n * base) + c - '0'; else if(base > 9 && c >= 'a' && c <= 'z' && c <= 'a' + (base-11)) n = (n * base) + c-'a' + 10; else if(base > 9 && c >= 'A' && c <= 'Z' && c <= 'A' + (base-11)) n = (n * base) + c-'A' + 10; else break; ++progress; } *number = n; return progress; } size_t UA_readNumber(UA_Byte *buf, size_t buflen, UA_UInt32 *number) { return UA_readNumberWithBase(buf, buflen, number, 10); } UA_StatusCode UA_parseEndpointUrl(const UA_String *endpointUrl, UA_String *outHostname, u16 *outPort, UA_String *outPath) { /* Url must begin with "opc.tcp://" or opc.udp:// (if pubsub enabled) */ if(endpointUrl->length < 11) { return UA_STATUSCODE_BADTCPENDPOINTURLINVALID; } if (strncmp((char*)endpointUrl->data, "opc.tcp://", 10) != 0) { #ifdef UA_ENABLE_PUBSUB if (strncmp((char*)endpointUrl->data, "opc.udp://", 10) != 0 && strncmp((char*)endpointUrl->data, "opc.mqtt://", 11) != 0) { return UA_STATUSCODE_BADTCPENDPOINTURLINVALID; } #else return UA_STATUSCODE_BADTCPENDPOINTURLINVALID; #endif } /* Where does the hostname end? */ size_t curr = 10; if(endpointUrl->data[curr] == '[') { /* IPv6: opc.tcp://[2001:0db8:85a3::8a2e:0370:7334]:1234/path */ for(; curr < endpointUrl->length; ++curr) { if(endpointUrl->data[curr] == ']') break; } if(curr == endpointUrl->length) return UA_STATUSCODE_BADTCPENDPOINTURLINVALID; curr++; } else { /* IPv4 or hostname: opc.tcp://something.something:1234/path */ for(; curr < endpointUrl->length; ++curr) { if(endpointUrl->data[curr] == ':' || endpointUrl->data[curr] == '/') break; } } /* Set the hostname */ outHostname->data = &endpointUrl->data[10]; outHostname->length = curr - 10; if(curr == endpointUrl->length) return UA_STATUSCODE_GOOD; /* Set the port */ if(endpointUrl->data[curr] == ':') { if(++curr == endpointUrl->length) return UA_STATUSCODE_BADTCPENDPOINTURLINVALID; u32 largeNum; size_t progress = UA_readNumber(&endpointUrl->data[curr], endpointUrl->length - curr, &largeNum); if(progress == 0 || largeNum > 65535) return UA_STATUSCODE_BADTCPENDPOINTURLINVALID; /* Test if the end of a valid port was reached */ curr += progress; if(curr == endpointUrl->length || endpointUrl->data[curr] == '/') *outPort = (u16)largeNum; if(curr == endpointUrl->length) return UA_STATUSCODE_GOOD; } /* Set the path */ UA_assert(curr < endpointUrl->length); if(endpointUrl->data[curr] != '/') return UA_STATUSCODE_BADTCPENDPOINTURLINVALID; if(++curr == endpointUrl->length) return UA_STATUSCODE_GOOD; outPath->data = &endpointUrl->data[curr]; outPath->length = endpointUrl->length - curr; /* Remove trailing slash from the path */ if(endpointUrl->data[endpointUrl->length - 1] == '/') outPath->length--; return UA_STATUSCODE_GOOD; } UA_StatusCode UA_parseEndpointUrlEthernet(const UA_String *endpointUrl, UA_String *target, UA_UInt16 *vid, UA_Byte *pcp) { /* Url must begin with "opc.eth://" */ if(endpointUrl->length < 11) { return UA_STATUSCODE_BADINTERNALERROR; } if(strncmp((char*) endpointUrl->data, "opc.eth://", 10) != 0) { return UA_STATUSCODE_BADINTERNALERROR; } /* Where does the host address end? */ size_t curr = 10; for(; curr < endpointUrl->length; ++curr) { if(endpointUrl->data[curr] == ':') { break; } } /* set host address */ target->data = &endpointUrl->data[10]; target->length = curr - 10; if(curr == endpointUrl->length) { return UA_STATUSCODE_GOOD; } /* Set VLAN */ u32 value = 0; curr++; /* skip ':' */ size_t progress = UA_readNumber(&endpointUrl->data[curr], endpointUrl->length - curr, &value); if(progress == 0 || value > 4096) { return UA_STATUSCODE_BADINTERNALERROR; } curr += progress; if(curr == endpointUrl->length || endpointUrl->data[curr] == '.') { *vid = (UA_UInt16) value; } if(curr == endpointUrl->length) { return UA_STATUSCODE_GOOD; } /* Set priority */ if(endpointUrl->data[curr] != '.') { return UA_STATUSCODE_BADINTERNALERROR; } curr++; /* skip '.' */ progress = UA_readNumber(&endpointUrl->data[curr], endpointUrl->length - curr, &value); if(progress == 0 || value > 7) { return UA_STATUSCODE_BADINTERNALERROR; } curr += progress; if(curr != endpointUrl->length) { return UA_STATUSCODE_BADINTERNALERROR; } *pcp = (UA_Byte) value; return UA_STATUSCODE_GOOD; } UA_StatusCode UA_ByteString_toBase64String(const UA_ByteString *byteString, UA_String *str) { if (str->length != 0) { UA_free(str->data); str->data = NULL; str->length = 0; } if (byteString == NULL || byteString->data == NULL) return UA_STATUSCODE_GOOD; if (byteString == str) return UA_STATUSCODE_BADINVALIDARGUMENT; str->data = (UA_Byte*)UA_base64(byteString->data, byteString->length, &str->length); if(str->data == NULL) return UA_STATUSCODE_BADOUTOFMEMORY; return UA_STATUSCODE_GOOD; } UA_StatusCode UA_NodeId_toString(const UA_NodeId *nodeId, UA_String *nodeIdStr) { if (nodeIdStr->length != 0) { UA_free(nodeIdStr->data); nodeIdStr->data = NULL; nodeIdStr->length = 0; } if (nodeId == NULL) return UA_STATUSCODE_GOOD; char *nsStr = NULL; long snprintfLen = 0; size_t nsLen = 0; if (nodeId->namespaceIndex != 0) { nsStr = (char*)UA_malloc(9+1); // strlen("ns=XXXXX;") = 9 + Nullbyte snprintfLen = UA_snprintf(nsStr, 10, "ns=%d;", nodeId->namespaceIndex); if (snprintfLen < 0 || snprintfLen >= 10) { UA_free(nsStr); return UA_STATUSCODE_BADINTERNALERROR; } nsLen = (size_t)(snprintfLen); } UA_ByteString byteStr = UA_BYTESTRING_NULL; switch (nodeId->identifierType) { case UA_NODEIDTYPE_NUMERIC: /* ns (2 byte, 65535) = 5 chars, numeric (4 byte, 4294967295) = 10 chars, delim = 1 , nullbyte = 1-> 17 chars */ nodeIdStr->length = nsLen + 2 + 10 + 1; nodeIdStr->data = (UA_Byte*)UA_malloc(nodeIdStr->length); if (nodeIdStr->data == NULL) { nodeIdStr->length = 0; UA_free(nsStr); return UA_STATUSCODE_BADOUTOFMEMORY; } snprintfLen =UA_snprintf((char*)nodeIdStr->data, nodeIdStr->length, "%si=%lu", nsLen > 0 ? nsStr : "", (unsigned long )nodeId->identifier.numeric); break; case UA_NODEIDTYPE_STRING: /* ns (16bit) = 5 chars, strlen + nullbyte */ nodeIdStr->length = nsLen + 2 + nodeId->identifier.string.length + 1; nodeIdStr->data = (UA_Byte*)UA_malloc(nodeIdStr->length); if (nodeIdStr->data == NULL) { nodeIdStr->length = 0; UA_free(nsStr); return UA_STATUSCODE_BADOUTOFMEMORY; } snprintfLen =UA_snprintf((char*)nodeIdStr->data, nodeIdStr->length, "%ss=%.*s", nsLen > 0 ? nsStr : "", (int)nodeId->identifier.string.length, nodeId->identifier.string.data); break; case UA_NODEIDTYPE_GUID: /* ns (16bit) = 5 chars + strlen(A123456C-0ABC-1A2B-815F-687212AAEE1B)=36 + nullbyte */ nodeIdStr->length = nsLen + 2 + 36 + 1; nodeIdStr->data = (UA_Byte*)UA_malloc(nodeIdStr->length); if (nodeIdStr->data == NULL) { nodeIdStr->length = 0; UA_free(nsStr); return UA_STATUSCODE_BADOUTOFMEMORY; } snprintfLen = UA_snprintf((char*)nodeIdStr->data, nodeIdStr->length, "%sg=" UA_PRINTF_GUID_FORMAT, nsLen > 0 ? nsStr : "", UA_PRINTF_GUID_DATA(nodeId->identifier.guid)); break; case UA_NODEIDTYPE_BYTESTRING: UA_ByteString_toBase64String(&nodeId->identifier.byteString, &byteStr); /* ns (16bit) = 5 chars + LEN + nullbyte */ nodeIdStr->length = nsLen + 2 + byteStr.length + 1; nodeIdStr->data = (UA_Byte*)UA_malloc(nodeIdStr->length); if (nodeIdStr->data == NULL) { nodeIdStr->length = 0; UA_String_deleteMembers(&byteStr); UA_free(nsStr); return UA_STATUSCODE_BADOUTOFMEMORY; } snprintfLen = UA_snprintf((char*)nodeIdStr->data, nodeIdStr->length, "%sb=%.*s", nsLen > 0 ? nsStr : "", (int)byteStr.length, byteStr.data); UA_String_deleteMembers(&byteStr); break; } UA_free(nsStr); if (snprintfLen < 0 || snprintfLen >= (long) nodeIdStr->length) { UA_free(nodeIdStr->data); nodeIdStr->data = NULL; nodeIdStr->length = 0; return UA_STATUSCODE_BADINTERNALERROR; } nodeIdStr->length = (size_t)snprintfLen; return UA_STATUSCODE_GOOD; } /*********************************** amalgamated original file "/home/jvoe/open62541/src/ua_workqueue.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2014-2016 (c) Sten Grüner * Copyright 2015 (c) Chris Iatrou * Copyright 2015 (c) Nick Goossens * Copyright 2015 (c) Jörg Schüler-Maroldt * Copyright 2015-2016 (c) Oleksiy Vasylyev * Copyright 2016-2017 (c) Florian Palm * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2016 (c) Lorenz Haas * Copyright 2017 (c) Jonas Green */ void UA_WorkQueue_init(UA_WorkQueue *wq) { /* Initialized the linked list for delayed callbacks */ SIMPLEQ_INIT(&wq->delayedCallbacks); #ifdef UA_ENABLE_MULTITHREADING wq->delayedCallbacks_checkpoint = NULL; pthread_mutex_init(&wq->delayedCallbacks_accessMutex, NULL); /* Initialize the dispatch queue for worker threads */ SIMPLEQ_INIT(&wq->dispatchQueue); pthread_mutex_init(&wq->dispatchQueue_accessMutex, NULL); pthread_cond_init(&wq->dispatchQueue_condition, NULL); pthread_mutex_init(&wq->dispatchQueue_conditionMutex, NULL); #endif } #ifdef UA_ENABLE_MULTITHREADING /* Forward declaration */ static void UA_WorkQueue_manuallyProcessDelayed(UA_WorkQueue *wq); #endif void UA_WorkQueue_cleanup(UA_WorkQueue *wq) { #ifdef UA_ENABLE_MULTITHREADING /* Shut down workers */ UA_WorkQueue_stop(wq); /* Execute remaining work in the dispatch queue */ while(true) { pthread_mutex_lock(&wq->dispatchQueue_accessMutex); UA_DelayedCallback *dc = SIMPLEQ_FIRST(&wq->dispatchQueue); if(!dc) { pthread_mutex_unlock(&wq->dispatchQueue_accessMutex); break; } SIMPLEQ_REMOVE_HEAD(&wq->dispatchQueue, next); pthread_mutex_unlock(&wq->dispatchQueue_accessMutex); dc->callback(dc->application, dc->data); UA_free(dc); } #endif /* All workers are shut down. Execute remaining delayed work here. */ UA_WorkQueue_manuallyProcessDelayed(wq); #ifdef UA_ENABLE_MULTITHREADING wq->delayedCallbacks_checkpoint = NULL; pthread_mutex_destroy(&wq->dispatchQueue_accessMutex); pthread_cond_destroy(&wq->dispatchQueue_condition); pthread_mutex_destroy(&wq->dispatchQueue_conditionMutex); pthread_mutex_destroy(&wq->delayedCallbacks_accessMutex); #endif } /***********/ /* Workers */ /***********/ #ifdef UA_ENABLE_MULTITHREADING static void * workerLoop(UA_Worker *worker) { UA_WorkQueue *wq = worker->queue; UA_UInt32 *counter = &worker->counter; volatile UA_Boolean *running = &worker->running; /* Initialize the (thread local) random seed with the ram address * of the worker. Not for security-critical entropy! */ UA_random_seed((uintptr_t)worker); while(*running) { UA_atomic_addUInt32(counter, 1); /* Remove a callback from the queue */ pthread_mutex_lock(&wq->dispatchQueue_accessMutex); UA_DelayedCallback *dc = SIMPLEQ_FIRST(&wq->dispatchQueue); if(dc) SIMPLEQ_REMOVE_HEAD(&wq->dispatchQueue, next); pthread_mutex_unlock(&wq->dispatchQueue_accessMutex); /* Nothing to do. Sleep until a callback is dispatched */ if(!dc) { pthread_mutex_lock(&wq->dispatchQueue_conditionMutex); pthread_cond_wait(&wq->dispatchQueue_condition, &wq->dispatchQueue_conditionMutex); pthread_mutex_unlock(&wq->dispatchQueue_conditionMutex); continue; } /* Execute */ if(dc->callback) dc->callback(dc->application, dc->data); UA_free(dc); } return NULL; } /* Can be called repeatedly and starts additional workers */ UA_StatusCode UA_WorkQueue_start(UA_WorkQueue *wq, size_t workersCount) { if(wq->workersSize > 0 || workersCount == 0) return UA_STATUSCODE_BADINTERNALERROR; /* Create the worker array */ wq->workers = (UA_Worker*)UA_calloc(workersCount, sizeof(UA_Worker)); if(!wq->workers) return UA_STATUSCODE_BADOUTOFMEMORY; wq->workersSize = workersCount; /* Spin up the workers */ for(size_t i = 0; i < workersCount; ++i) { UA_Worker *w = &wq->workers[i]; w->queue = wq; w->counter = 0; w->running = true; pthread_create(&w->thread, NULL, (void* (*)(void*))workerLoop, w); } return UA_STATUSCODE_GOOD; } void UA_WorkQueue_stop(UA_WorkQueue *wq) { if(wq->workersSize == 0) return; /* Signal the workers to stop */ for(size_t i = 0; i < wq->workersSize; ++i) wq->workers[i].running = false; /* Wake up all workers */ pthread_cond_broadcast(&wq->dispatchQueue_condition); /* Wait for the workers to finish, then clean up */ for(size_t i = 0; i < wq->workersSize; ++i) pthread_join(wq->workers[i].thread, NULL); UA_free(wq->workers); wq->workers = NULL; wq->workersSize = 0; } void UA_WorkQueue_enqueue(UA_WorkQueue *wq, UA_ApplicationCallback cb, void *application, void *data) { UA_DelayedCallback *dc = (UA_DelayedCallback*)UA_malloc(sizeof(UA_DelayedCallback)); if(!dc) { cb(application, data); /* Execute immediately if the memory could not be allocated */ return; } dc->callback = cb; dc->application = application; dc->data = data; /* Enqueue for the worker threads */ pthread_mutex_lock(&wq->dispatchQueue_accessMutex); SIMPLEQ_INSERT_TAIL(&wq->dispatchQueue, dc, next); pthread_mutex_unlock(&wq->dispatchQueue_accessMutex); /* Wake up sleeping workers */ pthread_cond_broadcast(&wq->dispatchQueue_condition); } #endif /*********************/ /* Delayed Callbacks */ /*********************/ #ifdef UA_ENABLE_MULTITHREADING /* Delayed Callbacks are called only when all callbacks that were dispatched * prior are finished. After every UA_MAX_DELAYED_SAMPLE delayed Callbacks that * were added to the queue, we sample the counters from the workers. The * counters are compared to the last counters that were sampled. If every worker * has proceeded the counter, then we know that all delayed callbacks prior to * the last sample-point are safe to execute. */ /* Sample the worker counter for every nth delayed callback. This is used to * test that all workers have **finished** their current job before the delayed * callback is processed. */ #define UA_MAX_DELAYED_SAMPLE 100 /* Call only with a held mutex for the delayed callbacks */ static void dispatchDelayedCallbacks(UA_WorkQueue *wq, UA_DelayedCallback *cb) { /* Are callbacks before the last checkpoint ready? */ for(size_t i = 0; i < wq->workersSize; ++i) { if(wq->workers[i].counter == wq->workers[i].checkpointCounter) return; } /* Dispatch all delayed callbacks up to the checkpoint. * TODO: Move over the entire queue up to the checkpoint in one step. */ if(wq->delayedCallbacks_checkpoint != NULL) { UA_DelayedCallback *iter, *tmp_iter; SIMPLEQ_FOREACH_SAFE(iter, &wq->delayedCallbacks, next, tmp_iter) { pthread_mutex_lock(&wq->dispatchQueue_accessMutex); SIMPLEQ_INSERT_TAIL(&wq->dispatchQueue, iter, next); pthread_mutex_unlock(&wq->dispatchQueue_accessMutex); if(iter == wq->delayedCallbacks_checkpoint) break; } } /* Create the new sample point */ for(size_t i = 0; i < wq->workersSize; ++i) wq->workers[i].checkpointCounter = wq->workers[i].counter; wq->delayedCallbacks_checkpoint = cb; } #endif void UA_WorkQueue_enqueueDelayed(UA_WorkQueue *wq, UA_DelayedCallback *cb) { #ifdef UA_ENABLE_MULTITHREADING pthread_mutex_lock(&wq->dispatchQueue_accessMutex); #endif SIMPLEQ_INSERT_HEAD(&wq->delayedCallbacks, cb, next); #ifdef UA_ENABLE_MULTITHREADING wq->delayedCallbacks_sinceDispatch++; if(wq->delayedCallbacks_sinceDispatch > UA_MAX_DELAYED_SAMPLE) { dispatchDelayedCallbacks(wq, cb); wq->delayedCallbacks_sinceDispatch = 0; } pthread_mutex_unlock(&wq->dispatchQueue_accessMutex); #endif } /* Assumes all workers are shut down */ void UA_WorkQueue_manuallyProcessDelayed(UA_WorkQueue *wq) { UA_DelayedCallback *dc, *dc_tmp; SIMPLEQ_FOREACH_SAFE(dc, &wq->delayedCallbacks, next, dc_tmp) { SIMPLEQ_REMOVE_HEAD(&wq->delayedCallbacks, next); if(dc->callback) dc->callback(dc->application, dc->data); UA_free(dc); } #ifdef UA_ENABLE_MULTITHREADING wq->delayedCallbacks_checkpoint = NULL; #endif } /*********************************** amalgamated original file "/home/jvoe/open62541/src/ua_timer.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2017, 2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2017 (c) Stefan Profanter, fortiss GmbH */ struct UA_TimerEntry { ZIP_ENTRY(UA_TimerEntry) zipfields; UA_DateTime nextTime; /* The next time when the callback * is to be executed */ UA_UInt64 interval; /* Interval in 100ns resolution */ UA_Boolean repeated; /* Repeated callback? */ UA_ApplicationCallback callback; void *application; void *data; ZIP_ENTRY(UA_TimerEntry) idZipfields; UA_UInt64 id; /* Id of the entry */ }; /* There may be several entries with the same nextTime in the tree. We give them * an absolute order by considering the memory address to break ties. Because of * this, the nextTime property cannot be used to lookup specific entries. */ static enum ZIP_CMP cmpDateTime(const UA_DateTime *a, const UA_DateTime *b) { if(*a < *b) return ZIP_CMP_LESS; if(*a > *b) return ZIP_CMP_MORE; if(a == b) return ZIP_CMP_EQ; if(a < b) return ZIP_CMP_LESS; return ZIP_CMP_MORE; } ZIP_PROTTYPE(UA_TimerZip, UA_TimerEntry, UA_DateTime) ZIP_IMPL(UA_TimerZip, UA_TimerEntry, zipfields, UA_DateTime, nextTime, cmpDateTime) /* The identifiers of entries are unique */ static enum ZIP_CMP cmpId(const UA_UInt64 *a, const UA_UInt64 *b) { if(*a < *b) return ZIP_CMP_LESS; if(*a == *b) return ZIP_CMP_EQ; return ZIP_CMP_MORE; } ZIP_PROTTYPE(UA_TimerIdZip, UA_TimerEntry, UA_UInt64) ZIP_IMPL(UA_TimerIdZip, UA_TimerEntry, idZipfields, UA_UInt64, id, cmpId) void UA_Timer_init(UA_Timer *t) { memset(t, 0, sizeof(UA_Timer)); } static UA_StatusCode addCallback(UA_Timer *t, UA_ApplicationCallback callback, void *application, void *data, UA_DateTime nextTime, UA_UInt64 interval, UA_Boolean repeated, UA_UInt64 *callbackId) { /* A callback method needs to be present */ if(!callback) return UA_STATUSCODE_BADINTERNALERROR; /* Allocate the repeated callback structure */ UA_TimerEntry *te = (UA_TimerEntry*)UA_malloc(sizeof(UA_TimerEntry)); if(!te) return UA_STATUSCODE_BADOUTOFMEMORY; /* Set the repeated callback */ te->interval = (UA_UInt64)interval; te->id = ++t->idCounter; te->callback = callback; te->application = application; te->data = data; te->repeated = repeated; te->nextTime = nextTime; /* Set the output identifier */ if(callbackId) *callbackId = te->id; ZIP_INSERT(UA_TimerZip, &t->root, te, ZIP_FFS32(UA_UInt32_random())); ZIP_INSERT(UA_TimerIdZip, &t->idRoot, te, ZIP_RANK(te, zipfields)); return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Timer_addTimedCallback(UA_Timer *t, UA_ApplicationCallback callback, void *application, void *data, UA_DateTime date, UA_UInt64 *callbackId) { return addCallback(t, callback, application, data, date, 0, false, callbackId); } /* Adding repeated callbacks: Add an entry with the "nextTime" timestamp in the * future. This will be picked up in the next iteration and inserted at the * correct place. So that the next execution takes place ät "nextTime". */ UA_StatusCode UA_Timer_addRepeatedCallback(UA_Timer *t, UA_ApplicationCallback callback, void *application, void *data, UA_Double interval_ms, UA_UInt64 *callbackId) { /* The interval needs to be positive */ if(interval_ms <= 0.0) return UA_STATUSCODE_BADINTERNALERROR; UA_UInt64 interval = (UA_UInt64)(interval_ms * UA_DATETIME_MSEC); UA_DateTime nextTime = UA_DateTime_nowMonotonic() + (UA_DateTime)interval; return addCallback(t, callback, application, data, nextTime, interval, true, callbackId); } UA_StatusCode UA_Timer_changeRepeatedCallbackInterval(UA_Timer *t, UA_UInt64 callbackId, UA_Double interval_ms) { /* The interval needs to be positive */ if(interval_ms <= 0.0) return UA_STATUSCODE_BADINTERNALERROR; /* Remove from the sorted list */ UA_TimerEntry *te = ZIP_FIND(UA_TimerIdZip, &t->idRoot, &callbackId); if(!te) return UA_STATUSCODE_BADNOTFOUND; /* Set the repeated callback */ ZIP_REMOVE(UA_TimerZip, &t->root, te); te->interval = (UA_UInt64)(interval_ms * UA_DATETIME_MSEC); /* in 100ns resolution */ te->nextTime = UA_DateTime_nowMonotonic() + (UA_DateTime)te->interval; ZIP_INSERT(UA_TimerZip, &t->root, te, ZIP_RANK(te, zipfields)); return UA_STATUSCODE_GOOD; } void UA_Timer_removeCallback(UA_Timer *t, UA_UInt64 callbackId) { UA_TimerEntry *te = ZIP_FIND(UA_TimerIdZip, &t->idRoot, &callbackId); if(!te) return; ZIP_REMOVE(UA_TimerZip, &t->root, te); ZIP_REMOVE(UA_TimerIdZip, &t->idRoot, te); UA_free(te); } UA_DateTime UA_Timer_process(UA_Timer *t, UA_DateTime nowMonotonic, UA_TimerExecutionCallback executionCallback, void *executionApplication) { UA_TimerEntry *first; while((first = ZIP_MIN(UA_TimerZip, &t->root)) && first->nextTime <= nowMonotonic) { ZIP_REMOVE(UA_TimerZip, &t->root, first); /* Reinsert / remove to their new position first. Because the callback * can interact with the zip tree and expects the same entries in the * root and idRoot trees. */ if(!first->repeated) { ZIP_REMOVE(UA_TimerIdZip, &t->idRoot, first); executionCallback(executionApplication, first->callback, first->application, first->data); UA_free(first); continue; } /* Set the time for the next execution. Prevent an infinite loop by * forcing the next processing into the next iteration. */ first->nextTime += (UA_Int64)first->interval; if(first->nextTime < nowMonotonic) first->nextTime = nowMonotonic + 1; ZIP_INSERT(UA_TimerZip, &t->root, first, ZIP_RANK(first, zipfields)); executionCallback(executionApplication, first->callback, first->application, first->data); } /* Return the timestamp of the earliest next callback */ first = ZIP_MIN(UA_TimerZip, &t->root); return (first) ? first->nextTime : UA_INT64_MAX; } static void freeEntry(UA_TimerEntry *te, void *data) { UA_free(te); } void UA_Timer_deleteMembers(UA_Timer *t) { /* Free all nodes and reset the root */ ZIP_ITER(UA_TimerZip, &t->root, freeEntry, NULL); ZIP_INIT(&t->root); } /*********************************** amalgamated original file "/home/jvoe/open62541/src/ua_connection.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2014, 2016-2017 (c) Florian Palm * Copyright 2015-2016 (c) Sten Grüner * Copyright 2015 (c) Oleksiy Vasylyev * Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB * Copyright 2019 (c) Kalycito Infotech Private Limited */ void UA_Connection_deleteMembers(UA_Connection *connection) { UA_ByteString_deleteMembers(&connection->incompleteChunk); } UA_StatusCode UA_Connection_processHELACK(UA_Connection *connection, const UA_ConnectionConfig *localConfig, const UA_ConnectionConfig *remoteConfig) { connection->config = *remoteConfig; /* The lowest common version is used by both sides */ if(connection->config.protocolVersion > localConfig->protocolVersion) connection->config.protocolVersion = localConfig->protocolVersion; /* Can we receive the max send size? */ if(connection->config.sendBufferSize > localConfig->recvBufferSize) connection->config.sendBufferSize = localConfig->recvBufferSize; /* Can we send the max receive size? */ if(connection->config.recvBufferSize > localConfig->sendBufferSize) connection->config.recvBufferSize = localConfig->sendBufferSize; /* Chunks of at least 8192 bytes must be permissible. * See Part 6, Clause 6.7.1 */ if(connection->config.recvBufferSize < 8192 || connection->config.sendBufferSize < 8192 || (connection->config.maxMessageSize != 0 && connection->config.maxMessageSize < 8192)) return UA_STATUSCODE_BADINTERNALERROR; connection->state = UA_CONNECTION_ESTABLISHED; return UA_STATUSCODE_GOOD; } /* Hides some errors before sending them to a client according to the * standard. */ static void hideErrors(UA_TcpErrorMessage *const error) { switch(error->error) { case UA_STATUSCODE_BADCERTIFICATEUNTRUSTED: case UA_STATUSCODE_BADCERTIFICATEREVOKED: error->error = UA_STATUSCODE_BADSECURITYCHECKSFAILED; error->reason = UA_STRING_NULL; break; // TODO: Check if these are all cases that need to be covered. default: break; } } void UA_Connection_sendError(UA_Connection *connection, UA_TcpErrorMessage *error) { hideErrors(error); UA_TcpMessageHeader header; header.messageTypeAndChunkType = UA_MESSAGETYPE_ERR + UA_CHUNKTYPE_FINAL; // Header + ErrorMessage (error + reasonLength_field + length) header.messageSize = 8 + (4 + 4 + (UA_UInt32)error->reason.length); /* Get the send buffer from the network layer */ UA_ByteString msg = UA_BYTESTRING_NULL; UA_StatusCode retval = connection->getSendBuffer(connection, header.messageSize, &msg); if(retval != UA_STATUSCODE_GOOD) return; /* Encode and send the response */ UA_Byte *bufPos = msg.data; const UA_Byte *bufEnd = &msg.data[msg.length]; UA_TcpMessageHeader_encodeBinary(&header, &bufPos, bufEnd); UA_TcpErrorMessage_encodeBinary(error, &bufPos, bufEnd); msg.length = header.messageSize; connection->send(connection, &msg); } static UA_StatusCode bufferIncompleteChunk(UA_Connection *connection, const UA_Byte *pos, const UA_Byte *end) { UA_assert(connection->incompleteChunk.length == 0); UA_assert(pos < end); size_t length = (uintptr_t)end - (uintptr_t)pos; UA_StatusCode retval = UA_ByteString_allocBuffer(&connection->incompleteChunk, length); if(retval != UA_STATUSCODE_GOOD) return retval; memcpy(connection->incompleteChunk.data, pos, length); return UA_STATUSCODE_GOOD; } static UA_StatusCode processChunk(UA_Connection *connection, void *application, UA_Connection_processChunk processCallback, const UA_Byte **posp, const UA_Byte *end, UA_Boolean *done) { const UA_Byte *pos = *posp; const size_t remaining = (uintptr_t)end - (uintptr_t)pos; /* At least 8 byte needed for the header. Wait for the next chunk. */ if(remaining < 8) { *done = true; return UA_STATUSCODE_GOOD; } /* Check the message type */ UA_MessageType msgtype = (UA_MessageType) ((UA_UInt32)pos[0] + ((UA_UInt32)pos[1] << 8) + ((UA_UInt32)pos[2] << 16)); if(msgtype != UA_MESSAGETYPE_MSG && msgtype != UA_MESSAGETYPE_ERR && msgtype != UA_MESSAGETYPE_OPN && msgtype != UA_MESSAGETYPE_HEL && msgtype != UA_MESSAGETYPE_ACK && msgtype != UA_MESSAGETYPE_CLO) { /* The message type is not recognized */ return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; } UA_Byte isFinal = pos[3]; if(isFinal != 'C' && isFinal != 'F' && isFinal != 'A') { /* The message type is not recognized */ return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; } UA_UInt32 chunk_length = 0; UA_ByteString temp = { 8, (UA_Byte*)(uintptr_t)pos }; /* At least 8 byte left */ size_t temp_offset = 4; /* Decoding the UInt32 cannot fail */ UA_UInt32_decodeBinary(&temp, &temp_offset, &chunk_length); /* The message size is not allowed */ if(chunk_length < 16 || chunk_length > connection->config.recvBufferSize) return UA_STATUSCODE_BADTCPMESSAGETOOLARGE; /* Have an the complete chunk */ if(chunk_length > remaining) { *done = true; return UA_STATUSCODE_GOOD; } /* Process the chunk; forward the position pointer */ temp.length = chunk_length; *posp += chunk_length; *done = false; return processCallback(application, connection, &temp); } UA_StatusCode UA_Connection_processChunks(UA_Connection *connection, void *application, UA_Connection_processChunk processCallback, const UA_ByteString *packet) { const UA_Byte *pos = packet->data; const UA_Byte *end = &packet->data[packet->length]; UA_ByteString appended = connection->incompleteChunk; /* Prepend the incomplete last chunk. This is usually done in the * networklayer. But we test for a buffered incomplete chunk here again to * work around "lazy" network layers. */ if(appended.length > 0) { connection->incompleteChunk = UA_BYTESTRING_NULL; UA_Byte *t = (UA_Byte*)UA_realloc(appended.data, appended.length + packet->length); if(!t) { UA_ByteString_deleteMembers(&appended); return UA_STATUSCODE_BADOUTOFMEMORY; } memcpy(&t[appended.length], pos, packet->length); appended.data = t; appended.length += packet->length; pos = t; end = &t[appended.length]; } UA_assert(connection->incompleteChunk.length == 0); /* Loop over the received chunks. pos is increased with each chunk. */ UA_Boolean done = false; UA_StatusCode retval = UA_STATUSCODE_GOOD; while(!done) { retval = processChunk(connection, application, processCallback, &pos, end, &done); /* If an irrecoverable error happens: do not buffer incomplete chunk */ if(retval != UA_STATUSCODE_GOOD) goto cleanup; } if(end > pos) retval = bufferIncompleteChunk(connection, pos, end); cleanup: UA_ByteString_deleteMembers(&appended); return retval; } /* In order to know whether a chunk was processed, we insert an redirection into * the callback. */ struct completeChunkTrampolineData { UA_Boolean called; void *application; UA_Connection_processChunk processCallback; }; static UA_StatusCode completeChunkTrampoline(void *application, UA_Connection *connection, UA_ByteString *chunk) { struct completeChunkTrampolineData *data = (struct completeChunkTrampolineData*)application; data->called = true; return data->processCallback(data->application, connection, chunk); } UA_StatusCode UA_Connection_receiveChunksBlocking(UA_Connection *connection, void *application, UA_Connection_processChunk processCallback, UA_UInt32 timeout) { UA_DateTime now = UA_DateTime_nowMonotonic(); UA_DateTime maxDate = now + (timeout * UA_DATETIME_MSEC); struct completeChunkTrampolineData data; data.called = false; data.application = application; data.processCallback = processCallback; UA_StatusCode retval = UA_STATUSCODE_GOOD; while(true) { /* Listen for messages to arrive */ UA_ByteString packet = UA_BYTESTRING_NULL; retval = connection->recv(connection, &packet, timeout); if(retval != UA_STATUSCODE_GOOD) break; /* Try to process one complete chunk */ retval = UA_Connection_processChunks(connection, &data, completeChunkTrampoline, &packet); connection->releaseRecvBuffer(connection, &packet); if(data.called) break; /* We received a message. But the chunk is incomplete. Compute the * remaining timeout. */ now = UA_DateTime_nowMonotonic(); /* >= avoid timeout to be set to 0 */ if(now >= maxDate) return UA_STATUSCODE_GOODNONCRITICALTIMEOUT; /* round always to upper value to avoid timeout to be set to 0 * if(maxDate - now) < (UA_DATETIME_MSEC/2) */ timeout = (UA_UInt32)(((maxDate - now) + (UA_DATETIME_MSEC - 1)) / UA_DATETIME_MSEC); } return retval; } UA_StatusCode UA_Connection_receiveChunksNonBlocking(UA_Connection *connection, void *application, UA_Connection_processChunk processCallback) { struct completeChunkTrampolineData data; data.called = false; data.application = application; data.processCallback = processCallback; /* Listen for messages to arrive */ UA_ByteString packet = UA_BYTESTRING_NULL; UA_StatusCode retval = connection->recv(connection, &packet, 1); if((retval != UA_STATUSCODE_GOOD) && (retval != UA_STATUSCODE_GOODNONCRITICALTIMEOUT)) return retval; /* Try to process one complete chunk */ retval = UA_Connection_processChunks(connection, &data, completeChunkTrampoline, &packet); connection->releaseRecvBuffer(connection, &packet); return retval; } void UA_Connection_detachSecureChannel(UA_Connection *connection) { UA_SecureChannel *channel = connection->channel; if(channel) /* only replace when the channel points to this connection */ UA_atomic_cmpxchg((void**)&channel->connection, connection, NULL); UA_atomic_xchg((void**)&connection->channel, NULL); } // TODO: Return an error code void UA_Connection_attachSecureChannel(UA_Connection *connection, UA_SecureChannel *channel) { if(UA_atomic_cmpxchg((void**)&channel->connection, NULL, connection) == NULL) UA_atomic_xchg((void**)&connection->channel, (void*)channel); } /*********************************** amalgamated original file "/home/jvoe/open62541/src/ua_securechannel.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2014, 2016-2017 (c) Florian Palm * Copyright 2015-2016 (c) Sten Grüner * Copyright 2015 (c) Oleksiy Vasylyev * Copyright 2016 (c) TorbenD * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2017-2018 (c) Mark Giraud, Fraunhofer IOSB */ #define UA_BITMASK_MESSAGETYPE 0x00ffffffu #define UA_BITMASK_CHUNKTYPE 0xff000000u #define UA_ASYMMETRIC_ALG_SECURITY_HEADER_FIXED_LENGTH 12 #define UA_SYMMETRIC_ALG_SECURITY_HEADER_LENGTH 4 #define UA_SEQUENCE_HEADER_LENGTH 8 #define UA_SECUREMH_AND_SYMALGH_LENGTH \ (UA_SECURE_CONVERSATION_MESSAGE_HEADER_LENGTH + \ UA_SYMMETRIC_ALG_SECURITY_HEADER_LENGTH) const UA_ByteString UA_SECURITY_POLICY_NONE_URI = {47, (UA_Byte *)"http://opcfoundation.org/UA/SecurityPolicy#None"}; #ifdef UA_ENABLE_UNIT_TEST_FAILURE_HOOKS UA_StatusCode decrypt_verifySignatureFailure; UA_StatusCode sendAsym_sendFailure; UA_StatusCode processSym_seqNumberFailure; #endif void UA_SecureChannel_init(UA_SecureChannel *channel) { /* Linked lists are also initialized by zeroing out */ memset(channel, 0, sizeof(UA_SecureChannel)); channel->state = UA_SECURECHANNELSTATE_FRESH; TAILQ_INIT(&channel->messages); } UA_StatusCode UA_SecureChannel_setSecurityPolicy(UA_SecureChannel *channel, const UA_SecurityPolicy *securityPolicy, const UA_ByteString *remoteCertificate) { /* Is a policy already configured? */ if(channel->securityPolicy) { UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Security policy already configured"); return UA_STATUSCODE_BADINTERNALERROR; } UA_StatusCode retval; if(securityPolicy->certificateVerification != NULL) { retval = securityPolicy->certificateVerification-> verifyCertificate(securityPolicy->certificateVerification->context, remoteCertificate); if(retval != UA_STATUSCODE_GOOD) return retval; } else { UA_LOG_WARNING(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Security policy None is used to create SecureChannel. Accepting all certificates"); } retval = securityPolicy->channelModule. newContext(securityPolicy, remoteCertificate, &channel->channelContext); if(retval != UA_STATUSCODE_GOOD) return retval; retval = UA_ByteString_copy(remoteCertificate, &channel->remoteCertificate); if(retval != UA_STATUSCODE_GOOD) return retval; UA_ByteString remoteCertificateThumbprint = {20, channel->remoteCertificateThumbprint}; retval = securityPolicy->asymmetricModule. makeCertificateThumbprint(securityPolicy, &channel->remoteCertificate, &remoteCertificateThumbprint); if(retval == UA_STATUSCODE_GOOD) channel->securityPolicy = securityPolicy; return retval; } static void deleteMessage(UA_Message *me) { UA_ChunkPayload *cp; while((cp = SIMPLEQ_FIRST(&me->chunkPayloads))) { if(cp->copied) UA_ByteString_deleteMembers(&cp->bytes); SIMPLEQ_REMOVE_HEAD(&me->chunkPayloads, pointers); UA_free(cp); } UA_free(me); } static void deleteLatestMessage(UA_SecureChannel *channel, UA_UInt32 requestId) { UA_Message *me = TAILQ_LAST(&channel->messages, UA_MessageQueue); if(!me) return; if(me->requestId != requestId) return; TAILQ_REMOVE(&channel->messages, me, pointers); deleteMessage(me); } void UA_SecureChannel_deleteMessages(UA_SecureChannel *channel) { UA_Message *me, *me_tmp; TAILQ_FOREACH_SAFE(me, &channel->messages, pointers, me_tmp) { TAILQ_REMOVE(&channel->messages, me, pointers); deleteMessage(me); } } void UA_SecureChannel_deleteMembers(UA_SecureChannel *channel) { /* Delete members */ UA_ByteString_deleteMembers(&channel->remoteCertificate); UA_ByteString_deleteMembers(&channel->localNonce); UA_ByteString_deleteMembers(&channel->remoteNonce); UA_ChannelSecurityToken_deleteMembers(&channel->securityToken); UA_ChannelSecurityToken_deleteMembers(&channel->nextSecurityToken); /* Delete the channel context for the security policy */ if(channel->securityPolicy) { channel->securityPolicy->channelModule.deleteContext(channel->channelContext); channel->securityPolicy = NULL; } /* Remove the buffered messages */ UA_SecureChannel_deleteMessages(channel); UA_SecureChannel_init(channel); } void UA_SecureChannel_close(UA_SecureChannel *channel) { /* Set the status to closed */ channel->state = UA_SECURECHANNELSTATE_CLOSED; /* Detach from the connection and close the connection */ if(channel->connection) { if(channel->connection->state != UA_CONNECTION_CLOSED) channel->connection->close(channel->connection); UA_Connection_detachSecureChannel(channel->connection); } /* Remove session pointers (not the sessions) and NULL the pointers back to * the SecureChannel in the Session */ UA_SessionHeader *sh, *temp; LIST_FOREACH_SAFE(sh, &channel->sessions, pointers, temp) { sh->channel = NULL; LIST_REMOVE(sh, pointers); } } UA_StatusCode UA_SecureChannel_generateLocalNonce(UA_SecureChannel *channel) { if(!channel->securityPolicy) return UA_STATUSCODE_BADINTERNALERROR; /* Is the length of the previous nonce correct? */ size_t nonceLength = channel->securityPolicy->symmetricModule.secureChannelNonceLength; if(channel->localNonce.length != nonceLength) { UA_ByteString_deleteMembers(&channel->localNonce); UA_StatusCode retval = UA_ByteString_allocBuffer(&channel->localNonce, nonceLength); if(retval != UA_STATUSCODE_GOOD) return retval; } return channel->securityPolicy->symmetricModule. generateNonce(channel->securityPolicy, &channel->localNonce); } static UA_StatusCode UA_SecureChannel_generateLocalKeys(const UA_SecureChannel *const channel, const UA_SecurityPolicy *const securityPolicy) { UA_LOG_TRACE_CHANNEL(securityPolicy->logger, channel, "Generating new local keys"); const UA_SecurityPolicyChannelModule *channelModule = &securityPolicy->channelModule; const UA_SecurityPolicySymmetricModule *symmetricModule = &securityPolicy->symmetricModule; const UA_SecurityPolicyCryptoModule *const cryptoModule = &securityPolicy->symmetricModule.cryptoModule; /* Symmetric key length */ size_t encryptionKeyLength = cryptoModule->encryptionAlgorithm.getLocalKeyLength(securityPolicy, channel->channelContext); size_t encryptionBlockSize = cryptoModule->encryptionAlgorithm.getLocalBlockSize(securityPolicy, channel->channelContext); size_t signingKeyLength = cryptoModule->signatureAlgorithm.getLocalKeyLength(securityPolicy, channel->channelContext); const size_t bufSize = encryptionBlockSize + signingKeyLength + encryptionKeyLength; UA_STACKARRAY(UA_Byte, bufBytes, bufSize); UA_ByteString buffer = {bufSize, bufBytes}; /* Local keys */ UA_StatusCode retval = symmetricModule->generateKey(securityPolicy, &channel->remoteNonce, &channel->localNonce, &buffer); if(retval != UA_STATUSCODE_GOOD) return retval; const UA_ByteString localSigningKey = {signingKeyLength, buffer.data}; const UA_ByteString localEncryptingKey = {encryptionKeyLength, buffer.data + signingKeyLength}; const UA_ByteString localIv = {encryptionBlockSize, buffer.data + signingKeyLength + encryptionKeyLength}; retval = channelModule->setLocalSymSigningKey(channel->channelContext, &localSigningKey); if(retval != UA_STATUSCODE_GOOD) return retval; retval = channelModule->setLocalSymEncryptingKey(channel->channelContext, &localEncryptingKey); if(retval != UA_STATUSCODE_GOOD) return retval; retval = channelModule->setLocalSymIv(channel->channelContext, &localIv); if(retval != UA_STATUSCODE_GOOD) return retval; return retval; } static UA_StatusCode UA_SecureChannel_generateRemoteKeys(const UA_SecureChannel *const channel, const UA_SecurityPolicy *const securityPolicy) { UA_LOG_TRACE_CHANNEL(securityPolicy->logger, channel, "Generating new remote keys"); const UA_SecurityPolicyChannelModule *channelModule = &securityPolicy->channelModule; const UA_SecurityPolicySymmetricModule *symmetricModule = &securityPolicy->symmetricModule; const UA_SecurityPolicyCryptoModule *const cryptoModule = &securityPolicy->symmetricModule.cryptoModule; /* Symmetric key length */ size_t encryptionKeyLength = cryptoModule->encryptionAlgorithm.getRemoteKeyLength(securityPolicy, channel->channelContext); size_t encryptionBlockSize = cryptoModule->encryptionAlgorithm.getRemoteBlockSize(securityPolicy, channel->channelContext); size_t signingKeyLength = cryptoModule->signatureAlgorithm.getRemoteKeyLength(securityPolicy, channel->channelContext); const size_t bufSize = encryptionBlockSize + signingKeyLength + encryptionKeyLength; UA_STACKARRAY(UA_Byte, bufBytes, bufSize); UA_ByteString buffer = {bufSize, bufBytes}; /* Remote keys */ UA_StatusCode retval = symmetricModule->generateKey(securityPolicy, &channel->localNonce, &channel->remoteNonce, &buffer); if(retval != UA_STATUSCODE_GOOD) return retval; const UA_ByteString remoteSigningKey = {signingKeyLength, buffer.data}; const UA_ByteString remoteEncryptingKey = {encryptionKeyLength, buffer.data + signingKeyLength}; const UA_ByteString remoteIv = {encryptionBlockSize, buffer.data + signingKeyLength + encryptionKeyLength}; retval = channelModule->setRemoteSymSigningKey(channel->channelContext, &remoteSigningKey); if(retval != UA_STATUSCODE_GOOD) return retval; retval = channelModule->setRemoteSymEncryptingKey(channel->channelContext, &remoteEncryptingKey); if(retval != UA_STATUSCODE_GOOD) return retval; retval = channelModule->setRemoteSymIv(channel->channelContext, &remoteIv); if(retval != UA_STATUSCODE_GOOD) return retval; return retval; } UA_StatusCode UA_SecureChannel_generateNewKeys(UA_SecureChannel *channel) { UA_StatusCode retval = UA_SecureChannel_generateLocalKeys(channel, channel->securityPolicy); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(channel->securityPolicy->logger, UA_LOGCATEGORY_SECURECHANNEL, "Could not generate a local key"); return retval; } retval = UA_SecureChannel_generateRemoteKeys(channel, channel->securityPolicy); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(channel->securityPolicy->logger, UA_LOGCATEGORY_SECURECHANNEL, "Could not generate a remote key"); return retval; } return retval; } UA_SessionHeader * UA_SecureChannel_getSession(UA_SecureChannel *channel, const UA_NodeId *authenticationToken) { UA_SessionHeader *sh; LIST_FOREACH(sh, &channel->sessions, pointers) { if(UA_NodeId_equal(&sh->authenticationToken, authenticationToken)) break; } return sh; } UA_StatusCode UA_SecureChannel_revolveTokens(UA_SecureChannel *channel) { if(channel->nextSecurityToken.tokenId == 0) // no security token issued return UA_STATUSCODE_BADSECURECHANNELTOKENUNKNOWN; //FIXME: not thread-safe ???? Why is this not thread safe? UA_ChannelSecurityToken_deleteMembers(&channel->previousSecurityToken); UA_ChannelSecurityToken_copy(&channel->securityToken, &channel->previousSecurityToken); UA_ChannelSecurityToken_deleteMembers(&channel->securityToken); UA_ChannelSecurityToken_copy(&channel->nextSecurityToken, &channel->securityToken); UA_ChannelSecurityToken_deleteMembers(&channel->nextSecurityToken); UA_ChannelSecurityToken_init(&channel->nextSecurityToken); /* remote keys are generated later on */ return UA_SecureChannel_generateLocalKeys(channel, channel->securityPolicy); } /***************************/ /* Send Asymmetric Message */ /***************************/ static size_t calculateAsymAlgSecurityHeaderLength(const UA_SecureChannel *channel) { size_t asymHeaderLength = UA_ASYMMETRIC_ALG_SECURITY_HEADER_FIXED_LENGTH + channel->securityPolicy->policyUri.length; if(channel->securityMode != UA_MESSAGESECURITYMODE_SIGN && channel->securityMode != UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) return asymHeaderLength; /* OPN is always encrypted even if the mode is sign only */ asymHeaderLength += 20; /* Thumbprints are always 20 byte long */ asymHeaderLength += channel->securityPolicy->localCertificate.length; return asymHeaderLength; } static UA_StatusCode prependHeadersAsym(UA_SecureChannel *const channel, UA_Byte *header_pos, const UA_Byte *buf_end, size_t totalLength, size_t securityHeaderLength, UA_UInt32 requestId, size_t *const finalLength) { UA_StatusCode retval; size_t dataToEncryptLength = totalLength - (UA_SECURE_CONVERSATION_MESSAGE_HEADER_LENGTH + securityHeaderLength); UA_SecureConversationMessageHeader respHeader; respHeader.messageHeader.messageTypeAndChunkType = UA_MESSAGETYPE_OPN + UA_CHUNKTYPE_FINAL; respHeader.messageHeader.messageSize = (UA_UInt32) (totalLength + UA_SecurityPolicy_getRemoteAsymEncryptionBufferLengthOverhead(channel->securityPolicy, channel->channelContext, dataToEncryptLength)); respHeader.secureChannelId = channel->securityToken.channelId; retval = UA_encodeBinary(&respHeader, &UA_TRANSPORT[UA_TRANSPORT_SECURECONVERSATIONMESSAGEHEADER], &header_pos, &buf_end, NULL, NULL); if(retval != UA_STATUSCODE_GOOD) return retval; UA_AsymmetricAlgorithmSecurityHeader asymHeader; UA_AsymmetricAlgorithmSecurityHeader_init(&asymHeader); asymHeader.securityPolicyUri = channel->securityPolicy->policyUri; if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGN || channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) { asymHeader.senderCertificate = channel->securityPolicy->localCertificate; asymHeader.receiverCertificateThumbprint.length = 20; asymHeader.receiverCertificateThumbprint.data = channel->remoteCertificateThumbprint; } retval = UA_encodeBinary(&asymHeader, &UA_TRANSPORT[UA_TRANSPORT_ASYMMETRICALGORITHMSECURITYHEADER], &header_pos, &buf_end, NULL, NULL); if(retval != UA_STATUSCODE_GOOD) return retval; UA_SequenceHeader seqHeader; seqHeader.requestId = requestId; seqHeader.sequenceNumber = UA_atomic_addUInt32(&channel->sendSequenceNumber, 1); retval = UA_encodeBinary(&seqHeader, &UA_TRANSPORT[UA_TRANSPORT_SEQUENCEHEADER], &header_pos, &buf_end, NULL, NULL); *finalLength = respHeader.messageHeader.messageSize; return retval; } static void hideBytesAsym(const UA_SecureChannel *channel, UA_Byte **buf_start, const UA_Byte **buf_end) { *buf_start += UA_SECURE_CONVERSATION_MESSAGE_HEADER_LENGTH; *buf_start += calculateAsymAlgSecurityHeaderLength(channel); *buf_start += UA_SEQUENCE_HEADER_LENGTH; #ifdef UA_ENABLE_ENCRYPTION if(channel->securityMode != UA_MESSAGESECURITYMODE_SIGN && channel->securityMode != UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) return; const UA_SecurityPolicy *securityPolicy = channel->securityPolicy; /* Hide bytes for signature and padding */ size_t potentialEncryptMaxSize = (size_t)(*buf_end - *buf_start) + UA_SEQUENCE_HEADER_LENGTH; *buf_end -= securityPolicy->asymmetricModule.cryptoModule.signatureAlgorithm. getLocalSignatureSize(securityPolicy, channel->channelContext); *buf_end -= 2; /* padding byte and extraPadding byte */ /* Add some overhead length due to RSA implementations adding a signature themselves */ *buf_end -= UA_SecurityPolicy_getRemoteAsymEncryptionBufferLengthOverhead(securityPolicy, channel->channelContext, potentialEncryptMaxSize); #endif } #ifdef UA_ENABLE_ENCRYPTION static void padChunkAsym(UA_SecureChannel *channel, const UA_ByteString *const buf, size_t securityHeaderLength, UA_Byte **buf_pos) { const UA_SecurityPolicy *const securityPolicy = channel->securityPolicy; /* Also pad if the securityMode is SIGN_ONLY, since we are using * asymmetric communication to exchange keys and thus need to encrypt. */ if(channel->securityMode != UA_MESSAGESECURITYMODE_SIGN && channel->securityMode != UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) return; const UA_Byte *buf_body_start = &buf->data[UA_SECURE_CONVERSATION_MESSAGE_HEADER_LENGTH + UA_SEQUENCE_HEADER_LENGTH + securityHeaderLength]; const size_t bytesToWrite = (uintptr_t)*buf_pos - (uintptr_t)buf_body_start + UA_SEQUENCE_HEADER_LENGTH; /* Compute the padding length */ size_t plainTextBlockSize = securityPolicy->asymmetricModule.cryptoModule.encryptionAlgorithm. getRemotePlainTextBlockSize(securityPolicy, channel->channelContext); size_t signatureSize = securityPolicy->asymmetricModule.cryptoModule.signatureAlgorithm. getLocalSignatureSize(securityPolicy, channel->channelContext); size_t paddingBytes = 1; if(securityPolicy->asymmetricModule.cryptoModule.encryptionAlgorithm. getRemoteKeyLength(securityPolicy, channel->channelContext) > 2048) ++paddingBytes; /* extra padding */ size_t totalPaddingSize = (plainTextBlockSize - ((bytesToWrite + signatureSize + paddingBytes) % plainTextBlockSize)); /* Write the padding. This is <= because the paddingSize byte also has to be written */ UA_Byte paddingSize = (UA_Byte)(totalPaddingSize & 0xffu); for(UA_UInt16 i = 0; i <= totalPaddingSize; ++i) { **buf_pos = paddingSize; ++*buf_pos; } /* Write the extra padding byte if required */ if(securityPolicy->asymmetricModule.cryptoModule.encryptionAlgorithm. getRemoteKeyLength(securityPolicy, channel->channelContext) > 2048) { UA_Byte extraPaddingSize = (UA_Byte)(totalPaddingSize >> 8u); **buf_pos = extraPaddingSize; ++*buf_pos; } } static UA_StatusCode signAndEncryptAsym(UA_SecureChannel *const channel, size_t preSignLength, UA_ByteString *buf, size_t securityHeaderLength, size_t totalLength) { if(channel->securityMode != UA_MESSAGESECURITYMODE_SIGN && channel->securityMode != UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) return UA_STATUSCODE_GOOD; const UA_SecurityPolicy *const securityPolicy = channel->securityPolicy; /* Sign message */ const UA_ByteString dataToSign = {preSignLength, buf->data}; size_t sigsize = securityPolicy->asymmetricModule.cryptoModule.signatureAlgorithm. getLocalSignatureSize(securityPolicy, channel->channelContext); UA_ByteString signature = {sigsize, buf->data + preSignLength}; UA_StatusCode retval = securityPolicy->asymmetricModule.cryptoModule.signatureAlgorithm. sign(securityPolicy, channel->channelContext, &dataToSign, &signature); if(retval != UA_STATUSCODE_GOOD) return retval; /* Specification part 6, 6.7.4: The OpenSecureChannel Messages are * signed and encrypted if the SecurityMode is not None (even if the * SecurityMode is SignOnly). */ size_t unencrypted_length = UA_SECURE_CONVERSATION_MESSAGE_HEADER_LENGTH + securityHeaderLength; UA_ByteString dataToEncrypt = {totalLength - unencrypted_length, &buf->data[unencrypted_length]}; return securityPolicy->asymmetricModule.cryptoModule.encryptionAlgorithm. encrypt(securityPolicy, channel->channelContext, &dataToEncrypt); } #endif /* UA_ENABLE_ENCRYPTION */ /* Sends an OPN message using asymmetric encryption if defined */ UA_StatusCode UA_SecureChannel_sendAsymmetricOPNMessage(UA_SecureChannel *channel, UA_UInt32 requestId, const void *content, const UA_DataType *contentType) { if(channel->securityMode == UA_MESSAGESECURITYMODE_INVALID) return UA_STATUSCODE_BADSECURITYMODEREJECTED; const UA_SecurityPolicy *const securityPolicy = channel->securityPolicy; UA_Connection *connection = channel->connection; if(!connection) return UA_STATUSCODE_BADINTERNALERROR; /* Allocate the message buffer */ UA_ByteString buf = UA_BYTESTRING_NULL; UA_StatusCode retval = connection->getSendBuffer(connection, connection->config.sendBufferSize, &buf); if(retval != UA_STATUSCODE_GOOD) return retval; /* Restrict buffer to the available space for the payload */ UA_Byte *buf_pos = buf.data; const UA_Byte *buf_end = &buf.data[buf.length]; hideBytesAsym(channel, &buf_pos, &buf_end); /* Encode the message type and content */ UA_NodeId typeId = UA_NODEID_NUMERIC(0, contentType->binaryEncodingId); retval |= UA_encodeBinary(&typeId, &UA_TYPES[UA_TYPES_NODEID], &buf_pos, &buf_end, NULL, NULL); retval |= UA_encodeBinary(content, contentType, &buf_pos, &buf_end, NULL, NULL); if(retval != UA_STATUSCODE_GOOD) { connection->releaseSendBuffer(connection, &buf); return retval; } const size_t securityHeaderLength = calculateAsymAlgSecurityHeaderLength(channel); /* Add padding to the chunk */ #ifdef UA_ENABLE_ENCRYPTION padChunkAsym(channel, &buf, securityHeaderLength, &buf_pos); #endif /* The total message length */ size_t pre_sig_length = (uintptr_t)buf_pos - (uintptr_t)buf.data; size_t total_length = pre_sig_length; if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGN || channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) total_length += securityPolicy->asymmetricModule.cryptoModule.signatureAlgorithm. getLocalSignatureSize(securityPolicy, channel->channelContext); /* The total message length is known here which is why we encode the headers * at this step and not earlier. */ size_t finalLength = 0; retval = prependHeadersAsym(channel, buf.data, buf_end, total_length, securityHeaderLength, requestId, &finalLength); if(retval != UA_STATUSCODE_GOOD) goto error; #ifdef UA_ENABLE_ENCRYPTION retval = signAndEncryptAsym(channel, pre_sig_length, &buf, securityHeaderLength, total_length); if(retval != UA_STATUSCODE_GOOD) goto error; #endif /* Send the message, the buffer is freed in the network layer */ buf.length = finalLength; retval = connection->send(connection, &buf); #ifdef UA_ENABLE_UNIT_TEST_FAILURE_HOOKS retval |= sendAsym_sendFailure; #endif return retval; error: connection->releaseSendBuffer(connection, &buf); return retval; } /**************************/ /* Send Symmetric Message */ /**************************/ #ifdef UA_ENABLE_ENCRYPTION static UA_UInt16 calculatePaddingSym(const UA_SecurityPolicy *securityPolicy, const void *channelContext, size_t bytesToWrite, UA_Byte *paddingSize, UA_Byte *extraPaddingSize) { size_t encryptionBlockSize = securityPolicy->symmetricModule.cryptoModule. encryptionAlgorithm.getLocalBlockSize(securityPolicy, channelContext); size_t signatureSize = securityPolicy->symmetricModule.cryptoModule.signatureAlgorithm. getLocalSignatureSize(securityPolicy, channelContext); size_t padding = (encryptionBlockSize - ((bytesToWrite + signatureSize + 1) % encryptionBlockSize)); *paddingSize = (UA_Byte)padding; *extraPaddingSize = (UA_Byte)(padding >> 8u); return (UA_UInt16)padding; } static void padChunkSym(UA_MessageContext *messageContext, size_t bodyLength) { if(messageContext->channel->securityMode != UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) return; /* The bytes for the padding and signature were removed from buf_end before * encoding the payload. So we don't have to check if there is enough * space. */ size_t bytesToWrite = bodyLength + UA_SEQUENCE_HEADER_LENGTH; UA_Byte paddingSize = 0; UA_Byte extraPaddingSize = 0; UA_UInt16 totalPaddingSize = calculatePaddingSym(messageContext->channel->securityPolicy, messageContext->channel->channelContext, bytesToWrite, &paddingSize, &extraPaddingSize); /* This is <= because the paddingSize byte also has to be written. */ for(UA_UInt16 i = 0; i <= totalPaddingSize; ++i) { *messageContext->buf_pos = paddingSize; ++(messageContext->buf_pos); } if(extraPaddingSize > 0) { *messageContext->buf_pos = extraPaddingSize; ++(messageContext->buf_pos); } } static UA_StatusCode signChunkSym(UA_MessageContext *const messageContext, size_t preSigLength) { const UA_SecureChannel *channel = messageContext->channel; if(channel->securityMode != UA_MESSAGESECURITYMODE_SIGN && channel->securityMode != UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) return UA_STATUSCODE_GOOD; const UA_SecurityPolicy *securityPolicy = channel->securityPolicy; UA_ByteString dataToSign = messageContext->messageBuffer; dataToSign.length = preSigLength; UA_ByteString signature; signature.length = securityPolicy->symmetricModule.cryptoModule.signatureAlgorithm. getLocalSignatureSize(securityPolicy, channel->channelContext); signature.data = messageContext->buf_pos; return securityPolicy->symmetricModule.cryptoModule.signatureAlgorithm. sign(securityPolicy, channel->channelContext, &dataToSign, &signature); } static UA_StatusCode encryptChunkSym(UA_MessageContext *const messageContext, size_t totalLength) { const UA_SecureChannel *channel = messageContext->channel; if(channel->securityMode != UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) return UA_STATUSCODE_GOOD; UA_ByteString dataToEncrypt; dataToEncrypt.data = messageContext->messageBuffer.data + UA_SECUREMH_AND_SYMALGH_LENGTH; dataToEncrypt.length = totalLength - UA_SECUREMH_AND_SYMALGH_LENGTH; const UA_SecurityPolicy *securityPolicy = channel->securityPolicy; return securityPolicy->symmetricModule.cryptoModule.encryptionAlgorithm. encrypt(securityPolicy, channel->channelContext, &dataToEncrypt); } #endif /* UA_ENABLE_ENCRYPTION */ static void setBufPos(UA_MessageContext *mc) { /* Forward the data pointer so that the payload is encoded after the * message header */ mc->buf_pos = &mc->messageBuffer.data[UA_SECURE_MESSAGE_HEADER_LENGTH]; mc->buf_end = &mc->messageBuffer.data[mc->messageBuffer.length]; #ifdef UA_ENABLE_ENCRYPTION const UA_SecureChannel *channel = mc->channel; const UA_SecurityPolicy *securityPolicy = channel->securityPolicy; /* Reserve space for the message footer at the end of the chunk if the chunk * is signed and/or encrypted. The footer includes the fields PaddingSize, * Padding, ExtraPadding and Signature. The padding fields are only present * if the chunk is encrypted. */ if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGN || channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) mc->buf_end -= securityPolicy->symmetricModule.cryptoModule.signatureAlgorithm. getLocalSignatureSize(securityPolicy, channel->channelContext); /* The size of the padding depends on the amount of data that shall be sent * and is unknown at this point. Reserve space for the PaddingSize byte, * the maximum amount of Padding which equals the block size of the * symmetric encryption algorithm and last 1 byte for the ExtraPaddingSize * field that is present if the encryption key is larger than 2048 bits. * The actual padding size is later calculated by the function * calculatePaddingSym(). */ if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) { /* PaddingSize and ExtraPaddingSize fields */ size_t encryptionBlockSize = securityPolicy->symmetricModule.cryptoModule. encryptionAlgorithm.getLocalBlockSize(securityPolicy, channel->channelContext); mc->buf_end -= 1 + ((encryptionBlockSize >> 8u) ? 1 : 0); /* Reduce the message body size with the remainder of the operation * maxEncryptedDataSize modulo EncryptionBlockSize to get a whole * number of blocks to encrypt later. Also reserve one byte for * padding (1 <= paddingSize <= encryptionBlockSize). */ size_t maxEncryptDataSize = mc->messageBuffer.length - UA_SECURE_CONVERSATION_MESSAGE_HEADER_LENGTH - UA_SYMMETRIC_ALG_SECURITY_HEADER_LENGTH; mc->buf_end -= (maxEncryptDataSize % encryptionBlockSize) + 1; } #endif } static UA_StatusCode checkLimitsSym(UA_MessageContext *const messageContext, size_t *const bodyLength) { /* Will this chunk surpass the capacity of the SecureChannel for the message? */ UA_Connection *const connection = messageContext->channel->connection; if(!connection) return UA_STATUSCODE_BADINTERNALERROR; UA_Byte *buf_body_start = messageContext->messageBuffer.data + UA_SECURE_MESSAGE_HEADER_LENGTH; const UA_Byte *buf_body_end = messageContext->buf_pos; *bodyLength = (uintptr_t)buf_body_end - (uintptr_t)buf_body_start; messageContext->messageSizeSoFar += *bodyLength; messageContext->chunksSoFar++; if(messageContext->messageSizeSoFar > connection->config.maxMessageSize && connection->config.maxMessageSize != 0) return UA_STATUSCODE_BADRESPONSETOOLARGE; if(messageContext->chunksSoFar > connection->config.maxChunkCount && connection->config.maxChunkCount != 0) return UA_STATUSCODE_BADRESPONSETOOLARGE; return UA_STATUSCODE_GOOD; } static UA_StatusCode encodeHeadersSym(UA_MessageContext *const messageContext, size_t totalLength) { UA_SecureChannel *channel = messageContext->channel; UA_Byte *header_pos = messageContext->messageBuffer.data; UA_SecureConversationMessageHeader respHeader; respHeader.secureChannelId = channel->securityToken.channelId; respHeader.messageHeader.messageTypeAndChunkType = messageContext->messageType; respHeader.messageHeader.messageSize = (UA_UInt32)totalLength; if(messageContext->final) respHeader.messageHeader.messageTypeAndChunkType += UA_CHUNKTYPE_FINAL; else respHeader.messageHeader.messageTypeAndChunkType += UA_CHUNKTYPE_INTERMEDIATE; UA_StatusCode res = UA_encodeBinary(&respHeader, &UA_TRANSPORT[UA_TRANSPORT_SECURECONVERSATIONMESSAGEHEADER], &header_pos, &messageContext->buf_end, NULL, NULL); UA_SymmetricAlgorithmSecurityHeader symSecHeader; symSecHeader.tokenId = channel->securityToken.tokenId; res |= UA_encodeBinary(&symSecHeader.tokenId, &UA_TRANSPORT[UA_TRANSPORT_SYMMETRICALGORITHMSECURITYHEADER], &header_pos, &messageContext->buf_end, NULL, NULL); UA_SequenceHeader seqHeader; seqHeader.requestId = messageContext->requestId; seqHeader.sequenceNumber = UA_atomic_addUInt32(&channel->sendSequenceNumber, 1); res |= UA_encodeBinary(&seqHeader, &UA_TRANSPORT[UA_TRANSPORT_SEQUENCEHEADER], &header_pos, &messageContext->buf_end, NULL, NULL); return res; } static UA_StatusCode sendSymmetricChunk(UA_MessageContext *messageContext) { UA_SecureChannel *const channel = messageContext->channel; const UA_SecurityPolicy *securityPolicy = channel->securityPolicy; UA_Connection *const connection = channel->connection; if(!connection) return UA_STATUSCODE_BADINTERNALERROR; size_t bodyLength = 0; UA_StatusCode res = checkLimitsSym(messageContext, &bodyLength); if(res != UA_STATUSCODE_GOOD) goto error; /* Add padding */ #ifdef UA_ENABLE_ENCRYPTION padChunkSym(messageContext, bodyLength); #endif /* The total message length */ size_t pre_sig_length = (uintptr_t)(messageContext->buf_pos) - (uintptr_t)messageContext->messageBuffer.data; size_t total_length = pre_sig_length; if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGN || channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) total_length += securityPolicy->symmetricModule.cryptoModule.signatureAlgorithm. getLocalSignatureSize(securityPolicy, channel->channelContext); /* Space for the padding and the signature have been reserved in setBufPos() */ UA_assert(total_length <= connection->config.sendBufferSize); /* For giving the buffer to the network layer */ messageContext->messageBuffer.length = total_length; UA_assert(res == UA_STATUSCODE_GOOD); res = encodeHeadersSym(messageContext, total_length); if(res != UA_STATUSCODE_GOOD) goto error; #ifdef UA_ENABLE_ENCRYPTION res = signChunkSym(messageContext, pre_sig_length); if(res != UA_STATUSCODE_GOOD) goto error; res = encryptChunkSym(messageContext, total_length); if(res != UA_STATUSCODE_GOOD) goto error; #endif /* Send the chunk, the buffer is freed in the network layer */ return connection->send(channel->connection, &messageContext->messageBuffer); error: connection->releaseSendBuffer(channel->connection, &messageContext->messageBuffer); return res; } /* Callback from the encoding layer. Send the chunk and replace the buffer. */ static UA_StatusCode sendSymmetricEncodingCallback(void *data, UA_Byte **buf_pos, const UA_Byte **buf_end) { /* Set buf values from encoding in the messagecontext */ UA_MessageContext *mc = (UA_MessageContext *)data; mc->buf_pos = *buf_pos; mc->buf_end = *buf_end; /* Send out */ UA_StatusCode retval = sendSymmetricChunk(mc); if(retval != UA_STATUSCODE_GOOD) return retval; /* Set a new buffer for the next chunk */ UA_Connection *connection = mc->channel->connection; if(!connection) return UA_STATUSCODE_BADINTERNALERROR; retval = connection->getSendBuffer(connection, connection->config.sendBufferSize, &mc->messageBuffer); if(retval != UA_STATUSCODE_GOOD) return retval; /* Hide bytes for header, padding and signature */ setBufPos(mc); *buf_pos = mc->buf_pos; *buf_end = mc->buf_end; return UA_STATUSCODE_GOOD; } UA_StatusCode UA_MessageContext_begin(UA_MessageContext *mc, UA_SecureChannel *channel, UA_UInt32 requestId, UA_MessageType messageType) { UA_Connection *connection = channel->connection; if(!connection) return UA_STATUSCODE_BADINTERNALERROR; if(messageType != UA_MESSAGETYPE_MSG && messageType != UA_MESSAGETYPE_CLO) return UA_STATUSCODE_BADINTERNALERROR; /* Create the chunking info structure */ mc->channel = channel; mc->requestId = requestId; mc->chunksSoFar = 0; mc->messageSizeSoFar = 0; mc->final = false; mc->messageBuffer = UA_BYTESTRING_NULL; mc->messageType = messageType; /* Allocate the message buffer */ UA_StatusCode retval = connection->getSendBuffer(connection, connection->config.sendBufferSize, &mc->messageBuffer); if(retval != UA_STATUSCODE_GOOD) return retval; /* Hide bytes for header, padding and signature */ setBufPos(mc); return UA_STATUSCODE_GOOD; } UA_StatusCode UA_MessageContext_encode(UA_MessageContext *mc, const void *content, const UA_DataType *contentType) { UA_StatusCode retval = UA_encodeBinary(content, contentType, &mc->buf_pos, &mc->buf_end, sendSymmetricEncodingCallback, mc); if(retval != UA_STATUSCODE_GOOD && mc->messageBuffer.length > 0) UA_MessageContext_abort(mc); return retval; } UA_StatusCode UA_MessageContext_finish(UA_MessageContext *mc) { mc->final = true; return sendSymmetricChunk(mc); } void UA_MessageContext_abort(UA_MessageContext *mc) { UA_Connection *connection = mc->channel->connection; connection->releaseSendBuffer(connection, &mc->messageBuffer); } UA_StatusCode UA_SecureChannel_sendSymmetricMessage(UA_SecureChannel *channel, UA_UInt32 requestId, UA_MessageType messageType, void *payload, const UA_DataType *payloadType) { if(!channel || !channel->connection || !payload || !payloadType) return UA_STATUSCODE_BADINTERNALERROR; if(channel->connection->state == UA_CONNECTION_CLOSED) return UA_STATUSCODE_BADCONNECTIONCLOSED; UA_MessageContext mc; UA_StatusCode retval = UA_MessageContext_begin(&mc, channel, requestId, messageType); if(retval != UA_STATUSCODE_GOOD) return retval; /* Assert's required for clang-analyzer */ UA_assert(mc.buf_pos == &mc.messageBuffer.data[UA_SECURE_MESSAGE_HEADER_LENGTH]); UA_assert(mc.buf_end <= &mc.messageBuffer.data[mc.messageBuffer.length]); UA_NodeId typeId = UA_NODEID_NUMERIC(0, payloadType->binaryEncodingId); retval = UA_MessageContext_encode(&mc, &typeId, &UA_TYPES[UA_TYPES_NODEID]); if(retval != UA_STATUSCODE_GOOD) return retval; retval = UA_MessageContext_encode(&mc, payload, payloadType); if(retval != UA_STATUSCODE_GOOD) return retval; return UA_MessageContext_finish(&mc); } /*****************************/ /* Assemble Complete Message */ /*****************************/ static UA_StatusCode addChunkPayload(UA_SecureChannel *channel, UA_UInt32 requestId, UA_MessageType messageType, UA_ByteString *chunkPayload, UA_Boolean final) { UA_Message *latest = TAILQ_LAST(&channel->messages, UA_MessageQueue); if(latest) { if(latest->requestId != requestId) { /* Start of a new message */ if(!latest->final) return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; latest = NULL; } else { if(latest->messageType != messageType) /* MessageType mismatch */ return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; if(latest->final) /* Correct message, but already finalized */ return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; } } /* Create a new message entry */ if(!latest) { latest = (UA_Message *)UA_malloc(sizeof(UA_Message)); if(!latest) return UA_STATUSCODE_BADOUTOFMEMORY; memset(latest, 0, sizeof(UA_Message)); latest->requestId = requestId; latest->messageType = messageType; SIMPLEQ_INIT(&latest->chunkPayloads); TAILQ_INSERT_TAIL(&channel->messages, latest, pointers); } /* Test against the connection settings */ const UA_ConnectionConfig *config = &channel->connection->config; UA_assert(config != NULL); /* clang-analyzer false positive */ if(config->maxChunkCount > 0 && config->maxChunkCount <= latest->chunkPayloadsSize) return UA_STATUSCODE_BADRESPONSETOOLARGE; if(config->maxMessageSize > 0 && config->maxMessageSize < latest->messageSize + chunkPayload->length) return UA_STATUSCODE_BADRESPONSETOOLARGE; /* Create a new chunk entry */ UA_ChunkPayload *cp = (UA_ChunkPayload *)UA_malloc(sizeof(UA_ChunkPayload)); if(!cp) return UA_STATUSCODE_BADOUTOFMEMORY; cp->bytes = *chunkPayload; cp->copied = false; /* Add the chunk */ SIMPLEQ_INSERT_TAIL(&latest->chunkPayloads, cp, pointers); latest->chunkPayloadsSize += 1; latest->messageSize += chunkPayload->length; latest->final = final; return UA_STATUSCODE_GOOD; } static UA_StatusCode processMessage(UA_SecureChannel *channel, const UA_Message *message, void *application, UA_ProcessMessageCallback callback) { if(message->chunkPayloadsSize == 1) { /* No need to combine chunks */ UA_ChunkPayload *cp = SIMPLEQ_FIRST(&message->chunkPayloads); callback(application, channel, message->messageType, message->requestId, &cp->bytes); } else { /* Allocate memory */ UA_ByteString bytes; bytes.data = (UA_Byte *)UA_malloc(message->messageSize); if(!bytes.data) { UA_LOG_ERROR(channel->securityPolicy->logger, UA_LOGCATEGORY_SECURECHANNEL, "Could not allocate the memory to assemble the message"); return UA_STATUSCODE_BADOUTOFMEMORY; } bytes.length = message->messageSize; /* Assemble the full message */ size_t curPos = 0; UA_ChunkPayload *cp; SIMPLEQ_FOREACH(cp, &message->chunkPayloads, pointers) { memcpy(&bytes.data[curPos], cp->bytes.data, cp->bytes.length); curPos += cp->bytes.length; } /* Process the message */ callback(application, channel, message->messageType, message->requestId, &bytes); UA_ByteString_deleteMembers(&bytes); } return UA_STATUSCODE_GOOD; } UA_StatusCode UA_SecureChannel_processCompleteMessages(UA_SecureChannel *channel, void *application, UA_ProcessMessageCallback callback) { UA_Message *message, *tmp_message; UA_StatusCode retval = UA_STATUSCODE_GOOD; TAILQ_FOREACH_SAFE(message, &channel->messages, pointers, tmp_message) { /* Stop at the first incomplete message */ if(!message->final) break; /* Has the channel been closed (during the last message)? */ if(channel->state == UA_SECURECHANNELSTATE_CLOSED) break; /* Remove the current message before processing */ TAILQ_REMOVE(&channel->messages, message, pointers); /* Process */ retval = processMessage(channel, message, application, callback); if(retval != UA_STATUSCODE_GOOD) break; /* Clean up the message */ UA_ChunkPayload *payload; while((payload = SIMPLEQ_FIRST(&message->chunkPayloads))) { if(payload->copied) UA_ByteString_deleteMembers(&payload->bytes); SIMPLEQ_REMOVE_HEAD(&message->chunkPayloads, pointers); UA_free(payload); } UA_free(message); } return retval; } /****************************/ /* Process a received Chunk */ /****************************/ static UA_StatusCode decryptChunk(const UA_SecureChannel *const channel, const UA_SecurityPolicyCryptoModule *const cryptoModule, UA_MessageType const messageType, const UA_ByteString *const chunk, size_t const offset, size_t *const chunkSizeAfterDecryption) { UA_LOG_TRACE_CHANNEL(channel->securityPolicy->logger, channel, "Decrypting chunk"); UA_ByteString cipherText = {chunk->length - offset, chunk->data + offset}; size_t sizeBeforeDecryption = cipherText.length; size_t chunkSizeBeforeDecryption = *chunkSizeAfterDecryption; /* Always decrypt opn messages if mode not none */ if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT || messageType == UA_MESSAGETYPE_OPN) { UA_StatusCode retval = cryptoModule->encryptionAlgorithm. decrypt(channel->securityPolicy, channel->channelContext, &cipherText); *chunkSizeAfterDecryption -= (sizeBeforeDecryption - cipherText.length); if(retval != UA_STATUSCODE_GOOD) { return retval; } } UA_LOG_TRACE_CHANNEL(channel->securityPolicy->logger, channel, "Chunk size before and after decryption: %lu, %lu", (long unsigned int)chunkSizeBeforeDecryption, (long unsigned int)*chunkSizeAfterDecryption); return UA_STATUSCODE_GOOD; } static UA_UInt16 decodeChunkPaddingSize(const UA_SecureChannel *const channel, const UA_SecurityPolicyCryptoModule *const cryptoModule, UA_MessageType const messageType, const UA_ByteString *const chunk, size_t const chunkSizeAfterDecryption, size_t sigsize) { /* Is padding used? */ if(channel->securityMode != UA_MESSAGESECURITYMODE_SIGNANDENCRYPT && !(messageType == UA_MESSAGETYPE_OPN && channel->securityMode > UA_MESSAGESECURITYMODE_NONE)) return 0; size_t paddingSize = chunk->data[chunkSizeAfterDecryption - sigsize - 1]; /* Extra padding size */ size_t keyLength = cryptoModule->encryptionAlgorithm. getRemoteKeyLength(channel->securityPolicy, channel->channelContext); if(keyLength > 2048) { paddingSize <<= 8u; paddingSize += 1; paddingSize += chunk->data[chunkSizeAfterDecryption - sigsize - 2]; } /* We need to add one to the padding size since the paddingSize byte itself * need to be removed as well. */ paddingSize += 1; UA_LOG_TRACE_CHANNEL(channel->securityPolicy->logger, channel, "Calculated padding size to be %lu", (long unsigned int)paddingSize); return (UA_UInt16)paddingSize; } static UA_StatusCode verifyChunk(const UA_SecureChannel *const channel, const UA_SecurityPolicyCryptoModule *const cryptoModule, const UA_ByteString *const chunk, size_t const chunkSizeAfterDecryption, size_t sigsize) { UA_LOG_TRACE_CHANNEL(channel->securityPolicy->logger, channel, "Verifying chunk signature"); /* Verify the signature */ const UA_ByteString chunkDataToVerify = {chunkSizeAfterDecryption - sigsize, chunk->data}; const UA_ByteString signature = {sigsize, chunk->data + chunkSizeAfterDecryption - sigsize}; UA_StatusCode retval = cryptoModule->signatureAlgorithm. verify(channel->securityPolicy, channel->channelContext, &chunkDataToVerify, &signature); #ifdef UA_ENABLE_UNIT_TEST_FAILURE_HOOKS retval |= decrypt_verifySignatureFailure; #endif return retval; } /* Sets the payload to a pointer inside the chunk buffer. Returns the requestId * and the sequenceNumber */ static UA_StatusCode decryptAndVerifyChunk(const UA_SecureChannel *channel, const UA_SecurityPolicyCryptoModule *cryptoModule, UA_MessageType messageType, const UA_ByteString *chunk, size_t offset, UA_UInt32 *requestId, UA_UInt32 *sequenceNumber, UA_ByteString *payload) { size_t chunkSizeAfterDecryption = chunk->length; UA_StatusCode retval = decryptChunk(channel, cryptoModule, messageType, chunk, offset, &chunkSizeAfterDecryption); if(retval != UA_STATUSCODE_GOOD) return retval; /* Verify the chunk signature */ size_t sigsize = 0; size_t paddingSize = 0; const UA_SecurityPolicy *securityPolicy = channel->securityPolicy; if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGN || channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT || messageType == UA_MESSAGETYPE_OPN) { sigsize = cryptoModule->signatureAlgorithm. getRemoteSignatureSize(securityPolicy, channel->channelContext); paddingSize = decodeChunkPaddingSize(channel, cryptoModule, messageType, chunk, chunkSizeAfterDecryption, sigsize); if(retval != UA_STATUSCODE_GOOD) return retval; if(offset + paddingSize + sigsize >= chunkSizeAfterDecryption) return UA_STATUSCODE_BADSECURITYCHECKSFAILED; retval = verifyChunk(channel, cryptoModule, chunk, chunkSizeAfterDecryption, sigsize); if(retval != UA_STATUSCODE_GOOD) return retval; } /* Decode the sequence header */ UA_SequenceHeader sequenceHeader; retval = UA_SequenceHeader_decodeBinary(chunk, &offset, &sequenceHeader); if(retval != UA_STATUSCODE_GOOD) return retval; if(offset + paddingSize + sigsize >= chunk->length) return UA_STATUSCODE_BADSECURITYCHECKSFAILED; *requestId = sequenceHeader.requestId; *sequenceNumber = sequenceHeader.sequenceNumber; payload->data = chunk->data + offset; payload->length = chunkSizeAfterDecryption - offset - sigsize - paddingSize; UA_LOG_TRACE_CHANNEL(channel->securityPolicy->logger, channel, "Decrypted and verified chunk with request id %u and " "sequence number %u", *requestId, *sequenceNumber); return UA_STATUSCODE_GOOD; } typedef UA_StatusCode (*UA_SequenceNumberCallback)(UA_SecureChannel *channel, UA_UInt32 sequenceNumber); static UA_StatusCode processSequenceNumberAsym(UA_SecureChannel *channel, UA_UInt32 sequenceNumber) { UA_LOG_TRACE_CHANNEL(channel->securityPolicy->logger, channel, "Sequence Number processed: %i", sequenceNumber); channel->receiveSequenceNumber = sequenceNumber; return UA_STATUSCODE_GOOD; } static UA_StatusCode processSequenceNumberSym(UA_SecureChannel *channel, UA_UInt32 sequenceNumber) { /* Failure mode hook for unit tests */ #ifdef UA_ENABLE_UNIT_TEST_FAILURE_HOOKS if(processSym_seqNumberFailure != UA_STATUSCODE_GOOD) return processSym_seqNumberFailure; #endif UA_LOG_TRACE_CHANNEL(channel->securityPolicy->logger, channel, "Sequence Number processed: %i", sequenceNumber); /* Does the sequence number match? */ if(sequenceNumber != channel->receiveSequenceNumber + 1) { /* FIXME: Remove magic numbers :( */ if(channel->receiveSequenceNumber + 1 > 4294966271 && sequenceNumber < 1024) channel->receiveSequenceNumber = sequenceNumber - 1; /* Roll over */ else return UA_STATUSCODE_BADSECURITYCHECKSFAILED; } ++channel->receiveSequenceNumber; return UA_STATUSCODE_GOOD; } static UA_StatusCode checkAsymHeader(UA_SecureChannel *const channel, UA_AsymmetricAlgorithmSecurityHeader *const asymHeader) { const UA_SecurityPolicy *const securityPolicy = channel->securityPolicy; if(!UA_ByteString_equal(&securityPolicy->policyUri, &asymHeader->securityPolicyUri)) { return UA_STATUSCODE_BADSECURITYPOLICYREJECTED; } // TODO: Verify certificate using certificate plugin. This will come with a new PR /* Something like this retval = certificateManager->verify(certificateStore??, &asymHeader->senderCertificate); if(retval != UA_STATUSCODE_GOOD) return retval; */ UA_StatusCode retval = securityPolicy->asymmetricModule. compareCertificateThumbprint(securityPolicy, &asymHeader->receiverCertificateThumbprint); if(retval != UA_STATUSCODE_GOOD) { return retval; } return UA_STATUSCODE_GOOD; } static UA_StatusCode checkPreviousToken(UA_SecureChannel *const channel, const UA_UInt32 tokenId) { if(tokenId != channel->previousSecurityToken.tokenId) return UA_STATUSCODE_BADSECURECHANNELTOKENUNKNOWN; UA_DateTime timeout = channel->previousSecurityToken.createdAt + (UA_DateTime)((UA_Double)channel->previousSecurityToken.revisedLifetime * (UA_Double)UA_DATETIME_MSEC * 1.25); if(timeout < UA_DateTime_nowMonotonic()) return UA_STATUSCODE_BADSECURECHANNELTOKENUNKNOWN; return UA_STATUSCODE_GOOD; } static UA_StatusCode checkSymHeader(UA_SecureChannel *const channel, const UA_UInt32 tokenId, UA_Boolean allowPreviousToken) { /* If the message uses the currently active token, check if it is still valid */ if(tokenId == channel->securityToken.tokenId) { if(channel->state == UA_SECURECHANNELSTATE_OPEN && (channel->securityToken.createdAt + (channel->securityToken.revisedLifetime * UA_DATETIME_MSEC)) < UA_DateTime_nowMonotonic()) { UA_SecureChannel_close(channel); return UA_STATUSCODE_BADSECURECHANNELCLOSED; } } /* If the message uses a different token, check if it is the next token. */ if(tokenId != channel->securityToken.tokenId) { /* If it isn't the next token, we might be dealing with a message, that * still uses the old token, so check if the old one is still valid.*/ if(tokenId != channel->nextSecurityToken.tokenId) { if(allowPreviousToken) return checkPreviousToken(channel, tokenId); return UA_STATUSCODE_BADSECURECHANNELTOKENUNKNOWN; } /* If the token is indeed the next token, revolve the tokens */ UA_StatusCode retval = UA_SecureChannel_revolveTokens(channel); if(retval != UA_STATUSCODE_GOOD) return retval; /* If the message now uses the currently active token also generate * new remote keys to correctly decrypt. */ if(channel->securityToken.tokenId == tokenId) { retval = UA_SecureChannel_generateRemoteKeys(channel, channel->securityPolicy); UA_ChannelSecurityToken_deleteMembers(&channel->previousSecurityToken); return retval; } } /* It is possible that the sent messages already use the new token, but * the received messages still use the old token. If we receive a message * with the new token, we will need to generate the keys and discard the * old token now*/ if(channel->previousSecurityToken.tokenId != 0) { UA_StatusCode retval = UA_SecureChannel_generateRemoteKeys(channel, channel->securityPolicy); UA_ChannelSecurityToken_deleteMembers(&channel->previousSecurityToken); return retval; } return UA_STATUSCODE_GOOD; } static UA_StatusCode putPayload(UA_SecureChannel *const channel, UA_UInt32 const requestId, UA_MessageType const messageType, UA_ChunkType const chunkType, UA_ByteString *chunkPayload) { switch(chunkType) { case UA_CHUNKTYPE_INTERMEDIATE: case UA_CHUNKTYPE_FINAL: return addChunkPayload(channel, requestId, messageType, chunkPayload, chunkType == UA_CHUNKTYPE_FINAL); case UA_CHUNKTYPE_ABORT: deleteLatestMessage(channel, requestId); return UA_STATUSCODE_GOOD; default: return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; } } /* The chunk body begins after the SecureConversationMessageHeader */ static UA_StatusCode decryptAddChunk(UA_SecureChannel *channel, const UA_ByteString *chunk, UA_Boolean allowPreviousToken) { /* Decode the MessageHeader */ size_t offset = 0; UA_SecureConversationMessageHeader messageHeader; UA_StatusCode retval = UA_SecureConversationMessageHeader_decodeBinary(chunk, &offset, &messageHeader); if(retval != UA_STATUSCODE_GOOD) return retval; #if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) /* The wrong ChannelId. Non-opened channels have the id zero. */ if(messageHeader.secureChannelId != channel->securityToken.channelId && channel->state != UA_SECURECHANNELSTATE_FRESH) return UA_STATUSCODE_BADSECURECHANNELIDINVALID; #endif UA_MessageType messageType = (UA_MessageType) (messageHeader.messageHeader.messageTypeAndChunkType & UA_BITMASK_MESSAGETYPE); UA_ChunkType chunkType = (UA_ChunkType) (messageHeader.messageHeader.messageTypeAndChunkType & UA_BITMASK_CHUNKTYPE); UA_UInt32 requestId = 0; UA_UInt32 sequenceNumber = 0; UA_ByteString chunkPayload; const UA_SecurityPolicyCryptoModule *cryptoModule = NULL; UA_SequenceNumberCallback sequenceNumberCallback = NULL; switch(messageType) { /* ERR message (not encrypted) */ case UA_MESSAGETYPE_ERR: if(chunkType != UA_CHUNKTYPE_FINAL) return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; chunkPayload.length = chunk->length - offset; chunkPayload.data = chunk->data + offset; return putPayload(channel, requestId, messageType, chunkType, &chunkPayload); /* MSG and CLO: Symmetric encryption */ case UA_MESSAGETYPE_MSG: case UA_MESSAGETYPE_CLO: { /* Decode and check the symmetric security header (tokenId) */ UA_SymmetricAlgorithmSecurityHeader symmetricSecurityHeader; UA_SymmetricAlgorithmSecurityHeader_init(&symmetricSecurityHeader); retval = UA_SymmetricAlgorithmSecurityHeader_decodeBinary(chunk, &offset, &symmetricSecurityHeader); if(retval != UA_STATUSCODE_GOOD) return retval; #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION /* Help fuzzing by always setting the correct tokenId */ symmetricSecurityHeader.tokenId = channel->securityToken.tokenId; #endif retval = checkSymHeader(channel, symmetricSecurityHeader.tokenId, allowPreviousToken); if(retval != UA_STATUSCODE_GOOD) return retval; cryptoModule = &channel->securityPolicy->symmetricModule.cryptoModule; sequenceNumberCallback = processSequenceNumberSym; break; } /* OPN: Asymmetric encryption */ case UA_MESSAGETYPE_OPN: { /* Chunking not allowed for OPN */ if(chunkType != UA_CHUNKTYPE_FINAL) return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; /* Decode the asymmetric algorithm security header and call the callback * to perform checks. */ UA_AsymmetricAlgorithmSecurityHeader asymHeader; UA_AsymmetricAlgorithmSecurityHeader_init(&asymHeader); offset = UA_SECURE_CONVERSATION_MESSAGE_HEADER_LENGTH; retval = UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(chunk, &offset, &asymHeader); if(retval != UA_STATUSCODE_GOOD) return retval; retval = checkAsymHeader(channel, &asymHeader); UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader); if(retval != UA_STATUSCODE_GOOD) return retval; cryptoModule = &channel->securityPolicy->asymmetricModule.cryptoModule; sequenceNumberCallback = processSequenceNumberAsym; break; } /* Invalid message type */ default:return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; } UA_assert(cryptoModule != NULL); retval = decryptAndVerifyChunk(channel, cryptoModule, messageType, chunk, offset, &requestId, &sequenceNumber, &chunkPayload); if(retval != UA_STATUSCODE_GOOD) return retval; /* Check the sequence number. Skip sequence number checking for fuzzer to * improve coverage */ if(sequenceNumberCallback == NULL) return UA_STATUSCODE_BADINTERNALERROR; #if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) retval = UA_STATUSCODE_GOOD; #else retval = sequenceNumberCallback(channel, sequenceNumber); #endif if(retval != UA_STATUSCODE_GOOD) return retval; return putPayload(channel, requestId, messageType, chunkType, &chunkPayload); } UA_StatusCode UA_SecureChannel_decryptAddChunk(UA_SecureChannel *channel, const UA_ByteString *chunk, UA_Boolean allowPreviousToken) { /* Has the SecureChannel timed out? */ if(channel->state == UA_SECURECHANNELSTATE_CLOSED) return UA_STATUSCODE_BADSECURECHANNELCLOSED; /* Is the SecureChannel configured? */ if(!channel->connection) return UA_STATUSCODE_BADINTERNALERROR; UA_StatusCode retval = decryptAddChunk(channel, chunk, allowPreviousToken); if(retval != UA_STATUSCODE_GOOD) UA_SecureChannel_close(channel); return retval; } UA_StatusCode UA_SecureChannel_persistIncompleteMessages(UA_SecureChannel *channel) { UA_Message *me; TAILQ_FOREACH(me, &channel->messages, pointers) { UA_ChunkPayload *cp; SIMPLEQ_FOREACH(cp, &me->chunkPayloads, pointers) { if(cp->copied) continue; UA_ByteString copy; UA_StatusCode retval = UA_ByteString_copy(&cp->bytes, ©); if(retval != UA_STATUSCODE_GOOD) { UA_SecureChannel_close(channel); return retval; } cp->bytes = copy; cp->copied = true; } } return UA_STATUSCODE_GOOD; } /* Functionality used by both the SecureChannel and the SecurityPolicy */ size_t UA_SecurityPolicy_getRemoteAsymEncryptionBufferLengthOverhead(const UA_SecurityPolicy *securityPolicy, const void *channelContext, size_t maxEncryptionLength) { if(maxEncryptionLength == 0) return 0; size_t plainTextBlockSize = securityPolicy->asymmetricModule.cryptoModule. encryptionAlgorithm.getRemotePlainTextBlockSize(securityPolicy, channelContext); size_t encryptedBlockSize = securityPolicy->asymmetricModule.cryptoModule. encryptionAlgorithm.getRemoteBlockSize(securityPolicy, channelContext); if(plainTextBlockSize == 0) return 0; size_t maxNumberOfBlocks = maxEncryptionLength / plainTextBlockSize; return maxNumberOfBlocks * (encryptedBlockSize - plainTextBlockSize); } /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_session.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2018 (c) Thomas Stalder, Blue Time Concept SA */ #ifdef UA_ENABLE_SUBSCRIPTIONS #endif #define UA_SESSION_NONCELENTH 32 void UA_Session_init(UA_Session *session) { memset(session, 0, sizeof(UA_Session)); session->availableContinuationPoints = UA_MAXCONTINUATIONPOINTS; #ifdef UA_ENABLE_SUBSCRIPTIONS SIMPLEQ_INIT(&session->responseQueue); #endif } void UA_Session_deleteMembersCleanup(UA_Session *session, UA_Server* server) { UA_Session_detachFromSecureChannel(session); UA_ApplicationDescription_deleteMembers(&session->clientDescription); UA_NodeId_deleteMembers(&session->header.authenticationToken); UA_NodeId_deleteMembers(&session->sessionId); UA_String_deleteMembers(&session->sessionName); UA_ByteString_deleteMembers(&session->serverNonce); struct ContinuationPoint *cp, *next = session->continuationPoints; while((cp = next)) { next = ContinuationPoint_clear(cp); UA_free(cp); } session->continuationPoints = NULL; session->availableContinuationPoints = UA_MAXCONTINUATIONPOINTS; } void UA_Session_attachToSecureChannel(UA_Session *session, UA_SecureChannel *channel) { LIST_INSERT_HEAD(&channel->sessions, &session->header, pointers); session->header.channel = channel; } void UA_Session_detachFromSecureChannel(UA_Session *session) { if(!session->header.channel) return; session->header.channel = NULL; LIST_REMOVE(&session->header, pointers); } UA_StatusCode UA_Session_generateNonce(UA_Session *session) { UA_SecureChannel *channel = session->header.channel; if(!channel || !channel->securityPolicy) return UA_STATUSCODE_BADINTERNALERROR; /* Is the length of the previous nonce correct? */ if(session->serverNonce.length != UA_SESSION_NONCELENTH) { UA_ByteString_deleteMembers(&session->serverNonce); UA_StatusCode retval = UA_ByteString_allocBuffer(&session->serverNonce, UA_SESSION_NONCELENTH); if(retval != UA_STATUSCODE_GOOD) return retval; } return channel->securityPolicy->symmetricModule. generateNonce(channel->securityPolicy, &session->serverNonce); } void UA_Session_updateLifetime(UA_Session *session) { session->validTill = UA_DateTime_nowMonotonic() + (UA_DateTime)(session->timeout * UA_DATETIME_MSEC); } #ifdef UA_ENABLE_SUBSCRIPTIONS void UA_Session_addSubscription(UA_Server *server, UA_Session *session, UA_Subscription *newSubscription) { newSubscription->subscriptionId = ++session->lastSubscriptionId; LIST_INSERT_HEAD(&session->serverSubscriptions, newSubscription, listEntry); session->numSubscriptions++; server->numSubscriptions++; } UA_StatusCode UA_Session_deleteSubscription(UA_Server *server, UA_Session *session, UA_UInt32 subscriptionId) { UA_Subscription *sub = UA_Session_getSubscriptionById(session, subscriptionId); if(!sub) return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; UA_Subscription_deleteMembers(server, sub); /* Add a delayed callback to remove the subscription when the currently * scheduled jobs have completed. There is no actual delayed callback. Just * free the structure. */ sub->delayedFreePointers.callback = NULL; UA_WorkQueue_enqueueDelayed(&server->workQueue, &sub->delayedFreePointers); /* Remove from the session */ LIST_REMOVE(sub, listEntry); UA_assert(session->numSubscriptions > 0); UA_assert(server->numSubscriptions > 0); session->numSubscriptions--; server->numSubscriptions--; return UA_STATUSCODE_GOOD; } UA_Subscription * UA_Session_getSubscriptionById(UA_Session *session, UA_UInt32 subscriptionId) { UA_Subscription *sub; LIST_FOREACH(sub, &session->serverSubscriptions, listEntry) { if(sub->subscriptionId == subscriptionId) break; } return sub; } UA_PublishResponseEntry* UA_Session_dequeuePublishReq(UA_Session *session) { UA_PublishResponseEntry* entry = SIMPLEQ_FIRST(&session->responseQueue); if(entry) { SIMPLEQ_REMOVE_HEAD(&session->responseQueue, listEntry); session->numPublishReq--; } return entry; } void UA_Session_queuePublishReq(UA_Session *session, UA_PublishResponseEntry* entry, UA_Boolean head) { if(!head) SIMPLEQ_INSERT_TAIL(&session->responseQueue, entry, listEntry); else SIMPLEQ_INSERT_HEAD(&session->responseQueue, entry, listEntry); session->numPublishReq++; } #endif /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_nodes.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2015-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2015-2016 (c) Sten Grüner * Copyright 2015 (c) Chris Iatrou * Copyright 2015, 2017 (c) Florian Palm * Copyright 2015 (c) Oleksiy Vasylyev * Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2017 (c) Julian Grothoff */ /* There is no UA_Node_new() method here. Creating nodes is part of the * NodeStore layer */ static enum ZIP_CMP cmpRefTarget(const void *a, const void *b) { const UA_ReferenceTarget *aa = (const UA_ReferenceTarget*)a; const UA_ReferenceTarget *bb = (const UA_ReferenceTarget*)b; if(aa->targetHash < bb->targetHash) return ZIP_CMP_LESS; if(aa->targetHash > bb->targetHash) return ZIP_CMP_MORE; return (enum ZIP_CMP)UA_ExpandedNodeId_order(&aa->target, &bb->target); } ZIP_IMPL(UA_ReferenceTargetHead, UA_ReferenceTarget, zipfields, UA_ReferenceTarget, zipfields, cmpRefTarget) void UA_Node_deleteMembers(UA_Node *node) { /* Delete standard content */ UA_NodeId_deleteMembers(&node->nodeId); UA_QualifiedName_deleteMembers(&node->browseName); UA_LocalizedText_deleteMembers(&node->displayName); UA_LocalizedText_deleteMembers(&node->description); /* Delete references */ UA_Node_deleteReferences(node); /* Delete unique content of the nodeclass */ switch(node->nodeClass) { case UA_NODECLASS_OBJECT: break; case UA_NODECLASS_METHOD: break; case UA_NODECLASS_OBJECTTYPE: break; case UA_NODECLASS_VARIABLE: case UA_NODECLASS_VARIABLETYPE: { UA_VariableNode *p = (UA_VariableNode*)node; UA_NodeId_deleteMembers(&p->dataType); UA_Array_delete(p->arrayDimensions, p->arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]); p->arrayDimensions = NULL; p->arrayDimensionsSize = 0; if(p->valueSource == UA_VALUESOURCE_DATA) UA_DataValue_deleteMembers(&p->value.data.value); break; } case UA_NODECLASS_REFERENCETYPE: { UA_ReferenceTypeNode *p = (UA_ReferenceTypeNode*)node; UA_LocalizedText_deleteMembers(&p->inverseName); break; } case UA_NODECLASS_DATATYPE: break; case UA_NODECLASS_VIEW: break; default: break; } } static UA_StatusCode UA_ObjectNode_copy(const UA_ObjectNode *src, UA_ObjectNode *dst) { dst->eventNotifier = src->eventNotifier; return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_CommonVariableNode_copy(const UA_VariableNode *src, UA_VariableNode *dst) { UA_StatusCode retval = UA_Array_copy(src->arrayDimensions, src->arrayDimensionsSize, (void**)&dst->arrayDimensions, &UA_TYPES[UA_TYPES_INT32]); if(retval != UA_STATUSCODE_GOOD) return retval; dst->arrayDimensionsSize = src->arrayDimensionsSize; retval = UA_NodeId_copy(&src->dataType, &dst->dataType); dst->valueRank = src->valueRank; dst->valueSource = src->valueSource; if(src->valueSource == UA_VALUESOURCE_DATA) { retval |= UA_DataValue_copy(&src->value.data.value, &dst->value.data.value); dst->value.data.callback = src->value.data.callback; } else dst->value.dataSource = src->value.dataSource; return retval; } static UA_StatusCode UA_VariableNode_copy(const UA_VariableNode *src, UA_VariableNode *dst) { UA_StatusCode retval = UA_CommonVariableNode_copy(src, dst); dst->accessLevel = src->accessLevel; dst->minimumSamplingInterval = src->minimumSamplingInterval; dst->historizing = src->historizing; return retval; } static UA_StatusCode UA_VariableTypeNode_copy(const UA_VariableTypeNode *src, UA_VariableTypeNode *dst) { UA_StatusCode retval = UA_CommonVariableNode_copy((const UA_VariableNode*)src, (UA_VariableNode*)dst); dst->isAbstract = src->isAbstract; return retval; } static UA_StatusCode UA_MethodNode_copy(const UA_MethodNode *src, UA_MethodNode *dst) { dst->executable = src->executable; dst->method = src->method; return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_ObjectTypeNode_copy(const UA_ObjectTypeNode *src, UA_ObjectTypeNode *dst) { dst->isAbstract = src->isAbstract; dst->lifecycle = src->lifecycle; return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_ReferenceTypeNode_copy(const UA_ReferenceTypeNode *src, UA_ReferenceTypeNode *dst) { UA_StatusCode retval = UA_LocalizedText_copy(&src->inverseName, &dst->inverseName); dst->isAbstract = src->isAbstract; dst->symmetric = src->symmetric; return retval; } static UA_StatusCode UA_DataTypeNode_copy(const UA_DataTypeNode *src, UA_DataTypeNode *dst) { dst->isAbstract = src->isAbstract; return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_ViewNode_copy(const UA_ViewNode *src, UA_ViewNode *dst) { dst->containsNoLoops = src->containsNoLoops; dst->eventNotifier = src->eventNotifier; return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Node_copy(const UA_Node *src, UA_Node *dst) { if(src->nodeClass != dst->nodeClass) return UA_STATUSCODE_BADINTERNALERROR; /* Copy standard content */ UA_StatusCode retval = UA_NodeId_copy(&src->nodeId, &dst->nodeId); retval |= UA_QualifiedName_copy(&src->browseName, &dst->browseName); retval |= UA_LocalizedText_copy(&src->displayName, &dst->displayName); retval |= UA_LocalizedText_copy(&src->description, &dst->description); dst->writeMask = src->writeMask; dst->context = src->context; dst->constructed = src->constructed; if(retval != UA_STATUSCODE_GOOD) { UA_Node_deleteMembers(dst); return retval; } /* Copy the references */ dst->references = NULL; if(src->referencesSize > 0) { dst->references = (UA_NodeReferenceKind*) UA_calloc(src->referencesSize, sizeof(UA_NodeReferenceKind)); if(!dst->references) { UA_Node_deleteMembers(dst); return UA_STATUSCODE_BADOUTOFMEMORY; } dst->referencesSize = src->referencesSize; for(size_t i = 0; i < src->referencesSize; ++i) { UA_NodeReferenceKind *srefs = &src->references[i]; UA_NodeReferenceKind *drefs = &dst->references[i]; drefs->isInverse = srefs->isInverse; ZIP_INIT(&drefs->refTargetsTree); retval = UA_NodeId_copy(&srefs->referenceTypeId, &drefs->referenceTypeId); if(retval != UA_STATUSCODE_GOOD) break; drefs->refTargets = (UA_ReferenceTarget*) UA_malloc(srefs->refTargetsSize* sizeof(UA_ReferenceTarget)); if(!drefs->refTargets) { UA_NodeId_deleteMembers(&drefs->referenceTypeId); break; } uintptr_t arraydiff = (uintptr_t)drefs->refTargets - (uintptr_t)srefs->refTargets; for(size_t j = 0; j < srefs->refTargetsSize; j++) { retval |= UA_ExpandedNodeId_copy(&srefs->refTargets[j].target, &drefs->refTargets[j].target); drefs->refTargets[j].targetHash = srefs->refTargets[j].targetHash; drefs->refTargets[j].zipfields.zip_right = NULL; if(srefs->refTargets[j].zipfields.zip_right) *(uintptr_t*)&drefs->refTargets[j].zipfields.zip_left = (uintptr_t)srefs->refTargets[j].zipfields.zip_right + arraydiff; drefs->refTargets[j].zipfields.zip_left = NULL; if(srefs->refTargets[j].zipfields.zip_left) *(uintptr_t*)&drefs->refTargets[j].zipfields.zip_left = (uintptr_t)srefs->refTargets[j].zipfields.zip_left + arraydiff; } srefs->refTargetsTree.zip_root = NULL; if(drefs->refTargetsTree.zip_root) *(uintptr_t*)&drefs->refTargetsTree.zip_root = (uintptr_t)srefs->refTargetsTree.zip_root + arraydiff; drefs->refTargetsSize= srefs->refTargetsSize; if(retval != UA_STATUSCODE_GOOD) break; } if(retval != UA_STATUSCODE_GOOD) { UA_Node_deleteMembers(dst); return retval; } } /* Copy unique content of the nodeclass */ switch(src->nodeClass) { case UA_NODECLASS_OBJECT: retval = UA_ObjectNode_copy((const UA_ObjectNode*)src, (UA_ObjectNode*)dst); break; case UA_NODECLASS_VARIABLE: retval = UA_VariableNode_copy((const UA_VariableNode*)src, (UA_VariableNode*)dst); break; case UA_NODECLASS_METHOD: retval = UA_MethodNode_copy((const UA_MethodNode*)src, (UA_MethodNode*)dst); break; case UA_NODECLASS_OBJECTTYPE: retval = UA_ObjectTypeNode_copy((const UA_ObjectTypeNode*)src, (UA_ObjectTypeNode*)dst); break; case UA_NODECLASS_VARIABLETYPE: retval = UA_VariableTypeNode_copy((const UA_VariableTypeNode*)src, (UA_VariableTypeNode*)dst); break; case UA_NODECLASS_REFERENCETYPE: retval = UA_ReferenceTypeNode_copy((const UA_ReferenceTypeNode*)src, (UA_ReferenceTypeNode*)dst); break; case UA_NODECLASS_DATATYPE: retval = UA_DataTypeNode_copy((const UA_DataTypeNode*)src, (UA_DataTypeNode*)dst); break; case UA_NODECLASS_VIEW: retval = UA_ViewNode_copy((const UA_ViewNode*)src, (UA_ViewNode*)dst); break; default: break; } if(retval != UA_STATUSCODE_GOOD) UA_Node_deleteMembers(dst); return retval; } UA_Node * UA_Node_copy_alloc(const UA_Node *src) { /* use dstPtr to trick static code analysis in accepting dirty cast */ size_t nodesize = 0; switch(src->nodeClass) { case UA_NODECLASS_OBJECT: nodesize = sizeof(UA_ObjectNode); break; case UA_NODECLASS_VARIABLE: nodesize = sizeof(UA_VariableNode); break; case UA_NODECLASS_METHOD: nodesize = sizeof(UA_MethodNode); break; case UA_NODECLASS_OBJECTTYPE: nodesize = sizeof(UA_ObjectTypeNode); break; case UA_NODECLASS_VARIABLETYPE: nodesize = sizeof(UA_VariableTypeNode); break; case UA_NODECLASS_REFERENCETYPE: nodesize = sizeof(UA_ReferenceTypeNode); break; case UA_NODECLASS_DATATYPE: nodesize = sizeof(UA_DataTypeNode); break; case UA_NODECLASS_VIEW: nodesize = sizeof(UA_ViewNode); break; default: return NULL; } UA_Node *dst = (UA_Node*)UA_calloc(1,nodesize); if(!dst) return NULL; dst->nodeClass = src->nodeClass; UA_StatusCode retval = UA_Node_copy(src, dst); if(retval != UA_STATUSCODE_GOOD) { UA_free(dst); return NULL; } return dst; } /******************************/ /* Copy Attributes into Nodes */ /******************************/ static UA_StatusCode copyStandardAttributes(UA_Node *node, const UA_NodeAttributes *attr) { /* retval = UA_NodeId_copy(&item->requestedNewNodeId.nodeId, &node->nodeId); */ /* retval |= UA_QualifiedName_copy(&item->browseName, &node->browseName); */ UA_StatusCode retval; /* The new nodeset format has optional display name. * See https://github.com/open62541/open62541/issues/2627 * If display name is NULL, then we take the name part of the browse name */ if (attr->displayName.text.length == 0) { retval = UA_String_copy(&node->browseName.name, &node->displayName.text); } else { retval = UA_LocalizedText_copy(&attr->displayName, &node->displayName); retval |= UA_LocalizedText_copy(&attr->description, &node->description); } node->writeMask = attr->writeMask; return retval; } static UA_StatusCode copyCommonVariableAttributes(UA_VariableNode *node, const UA_VariableAttributes *attr) { /* Copy the array dimensions */ UA_StatusCode retval = UA_Array_copy(attr->arrayDimensions, attr->arrayDimensionsSize, (void**)&node->arrayDimensions, &UA_TYPES[UA_TYPES_UINT32]); if(retval != UA_STATUSCODE_GOOD) return retval; node->arrayDimensionsSize = attr->arrayDimensionsSize; /* Data type and value rank */ retval = UA_NodeId_copy(&attr->dataType, &node->dataType); if(retval != UA_STATUSCODE_GOOD) return retval; node->valueRank = attr->valueRank; /* Copy the value */ retval = UA_Variant_copy(&attr->value, &node->value.data.value.value); node->valueSource = UA_VALUESOURCE_DATA; node->value.data.value.hasValue = (node->value.data.value.value.type != NULL); return retval; } static UA_StatusCode copyVariableNodeAttributes(UA_VariableNode *vnode, const UA_VariableAttributes *attr) { vnode->accessLevel = attr->accessLevel; vnode->historizing = attr->historizing; vnode->minimumSamplingInterval = attr->minimumSamplingInterval; return copyCommonVariableAttributes(vnode, attr); } static UA_StatusCode copyVariableTypeNodeAttributes(UA_VariableTypeNode *vtnode, const UA_VariableTypeAttributes *attr) { vtnode->isAbstract = attr->isAbstract; return copyCommonVariableAttributes((UA_VariableNode*)vtnode, (const UA_VariableAttributes*)attr); } static UA_StatusCode copyObjectNodeAttributes(UA_ObjectNode *onode, const UA_ObjectAttributes *attr) { onode->eventNotifier = attr->eventNotifier; return UA_STATUSCODE_GOOD; } static UA_StatusCode copyReferenceTypeNodeAttributes(UA_ReferenceTypeNode *rtnode, const UA_ReferenceTypeAttributes *attr) { rtnode->isAbstract = attr->isAbstract; rtnode->symmetric = attr->symmetric; return UA_LocalizedText_copy(&attr->inverseName, &rtnode->inverseName); } static UA_StatusCode copyObjectTypeNodeAttributes(UA_ObjectTypeNode *otnode, const UA_ObjectTypeAttributes *attr) { otnode->isAbstract = attr->isAbstract; return UA_STATUSCODE_GOOD; } static UA_StatusCode copyViewNodeAttributes(UA_ViewNode *vnode, const UA_ViewAttributes *attr) { vnode->containsNoLoops = attr->containsNoLoops; vnode->eventNotifier = attr->eventNotifier; return UA_STATUSCODE_GOOD; } static UA_StatusCode copyDataTypeNodeAttributes(UA_DataTypeNode *dtnode, const UA_DataTypeAttributes *attr) { dtnode->isAbstract = attr->isAbstract; return UA_STATUSCODE_GOOD; } static UA_StatusCode copyMethodNodeAttributes(UA_MethodNode *mnode, const UA_MethodAttributes *attr) { mnode->executable = attr->executable; return UA_STATUSCODE_GOOD; } #define CHECK_ATTRIBUTES(TYPE) \ if(attributeType != &UA_TYPES[UA_TYPES_##TYPE]) { \ retval = UA_STATUSCODE_BADNODEATTRIBUTESINVALID; \ break; \ } UA_StatusCode UA_Node_setAttributes(UA_Node *node, const void *attributes, const UA_DataType *attributeType) { /* Copy the attributes into the node */ UA_StatusCode retval = UA_STATUSCODE_GOOD; switch(node->nodeClass) { case UA_NODECLASS_OBJECT: CHECK_ATTRIBUTES(OBJECTATTRIBUTES); retval = copyObjectNodeAttributes((UA_ObjectNode*)node, (const UA_ObjectAttributes*)attributes); break; case UA_NODECLASS_VARIABLE: CHECK_ATTRIBUTES(VARIABLEATTRIBUTES); retval = copyVariableNodeAttributes((UA_VariableNode*)node, (const UA_VariableAttributes*)attributes); break; case UA_NODECLASS_OBJECTTYPE: CHECK_ATTRIBUTES(OBJECTTYPEATTRIBUTES); retval = copyObjectTypeNodeAttributes((UA_ObjectTypeNode*)node, (const UA_ObjectTypeAttributes*)attributes); break; case UA_NODECLASS_VARIABLETYPE: CHECK_ATTRIBUTES(VARIABLETYPEATTRIBUTES); retval = copyVariableTypeNodeAttributes((UA_VariableTypeNode*)node, (const UA_VariableTypeAttributes*)attributes); break; case UA_NODECLASS_REFERENCETYPE: CHECK_ATTRIBUTES(REFERENCETYPEATTRIBUTES); retval = copyReferenceTypeNodeAttributes((UA_ReferenceTypeNode*)node, (const UA_ReferenceTypeAttributes*)attributes); break; case UA_NODECLASS_DATATYPE: CHECK_ATTRIBUTES(DATATYPEATTRIBUTES); retval = copyDataTypeNodeAttributes((UA_DataTypeNode*)node, (const UA_DataTypeAttributes*)attributes); break; case UA_NODECLASS_VIEW: CHECK_ATTRIBUTES(VIEWATTRIBUTES); retval = copyViewNodeAttributes((UA_ViewNode*)node, (const UA_ViewAttributes*)attributes); break; case UA_NODECLASS_METHOD: CHECK_ATTRIBUTES(METHODATTRIBUTES); retval = copyMethodNodeAttributes((UA_MethodNode*)node, (const UA_MethodAttributes*)attributes); break; case UA_NODECLASS_UNSPECIFIED: default: retval = UA_STATUSCODE_BADNODECLASSINVALID; } if(retval == UA_STATUSCODE_GOOD) retval = copyStandardAttributes(node, (const UA_NodeAttributes*)attributes); if(retval != UA_STATUSCODE_GOOD) UA_Node_deleteMembers(node); return retval; } /*********************/ /* Manage References */ /*********************/ static UA_StatusCode addReferenceTarget(UA_NodeReferenceKind *refs, const UA_ExpandedNodeId *target, UA_UInt32 targetHash) { UA_ReferenceTarget *targets = (UA_ReferenceTarget*) UA_realloc(refs->refTargets, (refs->refTargetsSize + 1) * sizeof(UA_ReferenceTarget)); if(!targets) return UA_STATUSCODE_BADOUTOFMEMORY; /* Repair the pointers in the tree for the realloced array */ uintptr_t arraydiff = (uintptr_t)targets - (uintptr_t)refs->refTargets; if(arraydiff != 0) { for(size_t i = 0; i < refs->refTargetsSize; i++) { if(targets[i].zipfields.zip_left) *(uintptr_t*)&targets[i].zipfields.zip_left += arraydiff; if(targets[i].zipfields.zip_right) *(uintptr_t*)&targets[i].zipfields.zip_right += arraydiff; } } if(refs->refTargetsTree.zip_root) *(uintptr_t*)&refs->refTargetsTree.zip_root += arraydiff; refs->refTargets = targets; UA_ReferenceTarget *entry = &refs->refTargets[refs->refTargetsSize]; UA_StatusCode retval = UA_ExpandedNodeId_copy(target, &entry->target); if(retval != UA_STATUSCODE_GOOD) { if(refs->refTargetsSize== 0) { /* We had zero references before (realloc was a malloc) */ UA_free(refs->refTargets); refs->refTargets = NULL; } return retval; } entry->targetHash = targetHash; ZIP_INSERT(UA_ReferenceTargetHead, &refs->refTargetsTree, entry, ZIP_FFS32(UA_UInt32_random())); refs->refTargetsSize++; return UA_STATUSCODE_GOOD; } static UA_StatusCode addReferenceKind(UA_Node *node, const UA_AddReferencesItem *item) { UA_NodeReferenceKind *refs = (UA_NodeReferenceKind*) UA_realloc(node->references, sizeof(UA_NodeReferenceKind) * (node->referencesSize+1)); if(!refs) return UA_STATUSCODE_BADOUTOFMEMORY; node->references = refs; UA_NodeReferenceKind *newRef = &refs[node->referencesSize]; memset(newRef, 0, sizeof(UA_NodeReferenceKind)); ZIP_INIT(&newRef->refTargetsTree); newRef->isInverse = !item->isForward; UA_StatusCode retval = UA_NodeId_copy(&item->referenceTypeId, &newRef->referenceTypeId); UA_UInt32 targetHash = UA_ExpandedNodeId_hash(&item->targetNodeId); retval |= addReferenceTarget(newRef, &item->targetNodeId, targetHash); if(retval != UA_STATUSCODE_GOOD) { UA_NodeId_deleteMembers(&newRef->referenceTypeId); if(node->referencesSize == 0) { UA_free(node->references); node->references = NULL; } return retval; } node->referencesSize++; return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Node_addReference(UA_Node *node, const UA_AddReferencesItem *item) { /* Find the matching refkind */ UA_NodeReferenceKind *existingRefs = NULL; for(size_t i = 0; i < node->referencesSize; ++i) { UA_NodeReferenceKind *refs = &node->references[i]; if(refs->isInverse != item->isForward && UA_NodeId_equal(&refs->referenceTypeId, &item->referenceTypeId)) { existingRefs = refs; break; } } if(!existingRefs) return addReferenceKind(node, item); UA_ReferenceTarget tmpTarget; tmpTarget.target = item->targetNodeId; tmpTarget.targetHash = UA_ExpandedNodeId_hash(&item->targetNodeId); UA_ReferenceTarget *found = ZIP_FIND(UA_ReferenceTargetHead, &existingRefs->refTargetsTree, &tmpTarget); if(found) return UA_STATUSCODE_BADDUPLICATEREFERENCENOTALLOWED; return addReferenceTarget(existingRefs, &item->targetNodeId, tmpTarget.targetHash); } UA_StatusCode UA_Node_deleteReference(UA_Node *node, const UA_DeleteReferencesItem *item) { for(size_t i = node->referencesSize; i > 0; --i) { UA_NodeReferenceKind *refs = &node->references[i-1]; if(item->isForward == refs->isInverse) continue; if(!UA_NodeId_equal(&item->referenceTypeId, &refs->referenceTypeId)) continue; for(size_t j = refs->refTargetsSize; j > 0; --j) { UA_ReferenceTarget *target = &refs->refTargets[j-1]; if(!UA_NodeId_equal(&item->targetNodeId.nodeId, &target->target.nodeId)) continue; /* Ok, delete the reference */ ZIP_REMOVE(UA_ReferenceTargetHead, &refs->refTargetsTree, target); UA_ExpandedNodeId_deleteMembers(&target->target); refs->refTargetsSize--; /* One matching target remaining */ if(refs->refTargetsSize > 0) { if(j-1 != refs->refTargetsSize) { /* avoid valgrind error: Source and destination overlap in * memcpy */ ZIP_REMOVE(UA_ReferenceTargetHead, &refs->refTargetsTree, &refs->refTargets[refs->refTargetsSize]); *target = refs->refTargets[refs->refTargetsSize]; ZIP_INSERT(UA_ReferenceTargetHead, &refs->refTargetsTree, target, ZIP_RANK(target, zipfields)); } return UA_STATUSCODE_GOOD; } /* No target for the ReferenceType remaining. Remove entry. */ UA_free(refs->refTargets); UA_NodeId_deleteMembers(&refs->referenceTypeId); node->referencesSize--; if(node->referencesSize > 0) { if(i-1 != node->referencesSize) { /* avoid valgrind error: Source and destination overlap in * memcpy */ node->references[i-1] = node->references[node->referencesSize]; } return UA_STATUSCODE_GOOD; } /* No remaining references of any ReferenceType */ UA_free(node->references); node->references = NULL; return UA_STATUSCODE_GOOD; } } return UA_STATUSCODE_UNCERTAINREFERENCENOTDELETED; } void UA_Node_deleteReferencesSubset(UA_Node *node, size_t referencesSkipSize, UA_NodeId* referencesSkip) { /* Nothing to do */ if(node->referencesSize == 0 || node->references == NULL) return; for(size_t i = node->referencesSize; i > 0; --i) { UA_NodeReferenceKind *refs = &node->references[i-1]; /* Shall we keep the references of this type? */ UA_Boolean skip = false; for(size_t j = 0; j < referencesSkipSize; j++) { if(UA_NodeId_equal(&refs->referenceTypeId, &referencesSkip[j])) { skip = true; break; } } if(skip) continue; /* Remove references */ for(size_t j = 0; j < refs->refTargetsSize; j++) UA_ExpandedNodeId_deleteMembers(&refs->refTargets[j].target); UA_free(refs->refTargets); UA_NodeId_deleteMembers(&refs->referenceTypeId); node->referencesSize--; /* Move last references-kind entry to this position */ if(i-1 == node->referencesSize) /* Don't memcpy over the same position */ continue; node->references[i-1] = node->references[node->referencesSize]; } if(node->referencesSize > 0) { /* Realloc to save memory */ UA_NodeReferenceKind *refs = (UA_NodeReferenceKind*) UA_realloc(node->references, sizeof(UA_NodeReferenceKind) * node->referencesSize); if(refs) /* Do nothing if realloc fails */ node->references = refs; return; } /* The array is empty. Remove. */ UA_free(node->references); node->references = NULL; } void UA_Node_deleteReferences(UA_Node *node) { UA_Node_deleteReferencesSubset(node, 0, NULL); } /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_server.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2014-2017 (c) Florian Palm * Copyright 2015-2016 (c) Sten Grüner * Copyright 2015-2016 (c) Chris Iatrou * Copyright 2015 (c) LEvertz * Copyright 2015-2016 (c) Oleksiy Vasylyev * Copyright 2016 (c) Julian Grothoff * Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2016 (c) Lorenz Haas * Copyright 2017 (c) frax2222 * Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB * Copyright 2018 (c) Hilscher Gesellschaft für Systemautomation mbH (Author: Martin Lang) * Copyright 2019 (c) Kalycito Infotech Private Limited */ #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL #endif #ifdef UA_ENABLE_SUBSCRIPTIONS #endif #ifdef UA_ENABLE_VALGRIND_INTERACTIVE #include #endif /**********************/ /* Namespace Handling */ /**********************/ /* * The NS1 Uri can be changed by the user to some custom string. * This method is called to initialize the NS1 Uri if it is not set before to the default Application URI. * * This is done as soon as the Namespace Array is read or written via node value read / write services, * or UA_Server_addNamespace, UA_Server_getNamespaceByName or UA_Server_run_startup is called. * * Therefore one has to set the custom NS1 URI before one of the previously mentioned steps. */ void setupNs1Uri(UA_Server *server) { if (!server->namespaces[1].data) { UA_String_copy(&server->config.applicationDescription.applicationUri, &server->namespaces[1]); } } UA_UInt16 addNamespace(UA_Server *server, const UA_String name) { /* ensure that the uri for ns1 is set up from the app description */ setupNs1Uri(server); /* Check if the namespace already exists in the server's namespace array */ for(UA_UInt16 i = 0; i < server->namespacesSize; ++i) { if(UA_String_equal(&name, &server->namespaces[i])) return i; } /* Make the array bigger */ UA_String *newNS = (UA_String*)UA_realloc(server->namespaces, sizeof(UA_String) * (server->namespacesSize + 1)); if(!newNS) return 0; server->namespaces = newNS; /* Copy the namespace string */ UA_StatusCode retval = UA_String_copy(&name, &server->namespaces[server->namespacesSize]); if(retval != UA_STATUSCODE_GOOD) return 0; /* Announce the change (otherwise, the array appears unchanged) */ ++server->namespacesSize; return (UA_UInt16)(server->namespacesSize - 1); } UA_UInt16 UA_Server_addNamespace(UA_Server *server, const char* name) { /* Override const attribute to get string (dirty hack) */ UA_String nameString; nameString.length = strlen(name); nameString.data = (UA_Byte*)(uintptr_t)name; return addNamespace(server, nameString); } UA_ServerConfig* UA_Server_getConfig(UA_Server *server) { if(!server) return NULL; return &server->config; } UA_StatusCode UA_Server_getNamespaceByName(UA_Server *server, const UA_String namespaceUri, size_t* foundIndex) { /* ensure that the uri for ns1 is set up from the app description */ setupNs1Uri(server); for(size_t idx = 0; idx < server->namespacesSize; idx++) { if(!UA_String_equal(&server->namespaces[idx], &namespaceUri)) continue; (*foundIndex) = idx; return UA_STATUSCODE_GOOD; } return UA_STATUSCODE_BADNOTFOUND; } UA_StatusCode UA_Server_forEachChildNodeCall(UA_Server *server, UA_NodeId parentNodeId, UA_NodeIteratorCallback callback, void *handle) { const UA_Node *parent = UA_Nodestore_getNode(server->nsCtx, &parentNodeId); if(!parent) return UA_STATUSCODE_BADNODEIDINVALID; /* TODO: We need to do an ugly copy of the references array since users may * delete references from within the callback. In single-threaded mode this * changes the same node we point at here. In multi-threaded mode, this * creates a new copy as nodes are truly immutable. * The callback could remove a node via the regular public API. * This can remove a member of the nodes-array we iterate over... * */ UA_Node *parentCopy = UA_Node_copy_alloc(parent); if(!parentCopy) { UA_Nodestore_releaseNode(server->nsCtx, parent); return UA_STATUSCODE_BADUNEXPECTEDERROR; } UA_StatusCode retval = UA_STATUSCODE_GOOD; for(size_t i = parentCopy->referencesSize; i > 0; --i) { UA_NodeReferenceKind *ref = &parentCopy->references[i - 1]; for(size_t j = 0; jrefTargetsSize; j++) { retval = callback(ref->refTargets[j].target.nodeId, ref->isInverse, ref->referenceTypeId, handle); if(retval != UA_STATUSCODE_GOOD) goto cleanup; } } cleanup: UA_Node_deleteMembers(parentCopy); UA_free(parentCopy); UA_Nodestore_releaseNode(server->nsCtx, parent); return retval; } /********************/ /* Server Lifecycle */ /********************/ /* The server needs to be stopped before it can be deleted */ void UA_Server_delete(UA_Server *server) { /* Delete all internal data */ UA_SecureChannelManager_deleteMembers(&server->secureChannelManager); UA_SessionManager_deleteMembers(&server->sessionManager); UA_Array_delete(server->namespaces, server->namespacesSize, &UA_TYPES[UA_TYPES_STRING]); #ifdef UA_ENABLE_SUBSCRIPTIONS UA_MonitoredItem *mon, *mon_tmp; LIST_FOREACH_SAFE(mon, &server->localMonitoredItems, listEntry, mon_tmp) { LIST_REMOVE(mon, listEntry); UA_MonitoredItem_delete(server, mon); } #endif #ifdef UA_ENABLE_PUBSUB UA_PubSubManager_delete(server, &server->pubSubManager); #endif #ifdef UA_ENABLE_DISCOVERY UA_DiscoveryManager_deleteMembers(&server->discoveryManager, server); #endif /* Clean up the Admin Session */ UA_Session_deleteMembersCleanup(&server->adminSession, server); /* Clean up the work queue */ UA_WorkQueue_cleanup(&server->workQueue); /* Delete the timed work */ UA_Timer_deleteMembers(&server->timer); /* Clean up the nodestore */ UA_Nodestore_delete(server->nsCtx); /* Clean up the config */ UA_ServerConfig_clean(&server->config); /* Delete the server itself */ UA_free(server); } /* Recurring cleanup. Removing unused and timed-out channels and sessions */ static void UA_Server_cleanup(UA_Server *server, void *_) { UA_DateTime nowMonotonic = UA_DateTime_nowMonotonic(); UA_SessionManager_cleanupTimedOut(&server->sessionManager, nowMonotonic); UA_SecureChannelManager_cleanupTimedOut(&server->secureChannelManager, nowMonotonic); #ifdef UA_ENABLE_DISCOVERY UA_Discovery_cleanupTimedOut(server, nowMonotonic); #endif } /********************/ /* Server Lifecycle */ /********************/ static UA_Server * UA_Server_init(UA_Server *server) { /* Init start time to zero, the actual start time will be sampled in * UA_Server_run_startup() */ server->startTime = 0; /* Set a seed for non-cyptographic randomness */ #ifndef UA_ENABLE_DETERMINISTIC_RNG UA_random_seed((UA_UInt64)UA_DateTime_now()); #endif /* Initialize the handling of repeated callbacks */ UA_Timer_init(&server->timer); UA_WorkQueue_init(&server->workQueue); /* Initialize the adminSession */ UA_Session_init(&server->adminSession); server->adminSession.sessionId.identifierType = UA_NODEIDTYPE_GUID; server->adminSession.sessionId.identifier.guid.data1 = 1; server->adminSession.validTill = UA_INT64_MAX; /* Create Namespaces 0 and 1 * Ns1 will be filled later with the uri from the app description */ server->namespaces = (UA_String *)UA_Array_new(2, &UA_TYPES[UA_TYPES_STRING]); if(!server->namespaces) { UA_Server_delete(server); return NULL; } server->namespaces[0] = UA_STRING_ALLOC("http://opcfoundation.org/UA/"); server->namespaces[1] = UA_STRING_NULL; server->namespacesSize = 2; /* Initialized SecureChannel and Session managers */ UA_SecureChannelManager_init(&server->secureChannelManager, server); UA_SessionManager_init(&server->sessionManager, server); /* Add a regular callback for cleanup and maintenance. With a 10s interval. */ UA_Server_addRepeatedCallback(server, (UA_ServerCallback)UA_Server_cleanup, NULL, 10000.0, NULL); /* Initialize namespace 0*/ UA_StatusCode retVal = UA_Nodestore_new(&server->nsCtx); if(retVal != UA_STATUSCODE_GOOD) goto cleanup; retVal = UA_Server_initNS0(server); if(retVal != UA_STATUSCODE_GOOD) goto cleanup; /* Build PubSub information model */ #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL UA_Server_initPubSubNS0(server); #endif return server; cleanup: UA_Server_delete(server); return NULL; } UA_Server * UA_Server_new() { /* Allocate the server */ UA_Server *server = (UA_Server *)UA_calloc(1, sizeof(UA_Server)); if(!server) return NULL; return UA_Server_init(server); } UA_Server * UA_Server_newWithConfig(const UA_ServerConfig *config) { UA_Server *server = (UA_Server *)UA_calloc(1, sizeof(UA_Server)); if(!server) return NULL; if(config) server->config = *config; return UA_Server_init(server); } /* Returns if the server should be shut down immediately */ static UA_Boolean setServerShutdown(UA_Server *server) { if(server->endTime != 0) return false; if(server->config.shutdownDelay == 0) return true; UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Shutting down the server with a delay of %i ms", (int)server->config.shutdownDelay); server->endTime = UA_DateTime_now() + (UA_DateTime)(server->config.shutdownDelay * UA_DATETIME_MSEC); return false; } /*******************/ /* Timed Callbacks */ /*******************/ UA_StatusCode UA_Server_addTimedCallback(UA_Server *server, UA_ServerCallback callback, void *data, UA_DateTime date, UA_UInt64 *callbackId) { return UA_Timer_addTimedCallback(&server->timer, (UA_ApplicationCallback)callback, server, data, date, callbackId); } UA_StatusCode UA_Server_addRepeatedCallback(UA_Server *server, UA_ServerCallback callback, void *data, UA_Double interval_ms, UA_UInt64 *callbackId) { return UA_Timer_addRepeatedCallback(&server->timer, (UA_ApplicationCallback)callback, server, data, interval_ms, callbackId); } UA_StatusCode UA_Server_changeRepeatedCallbackInterval(UA_Server *server, UA_UInt64 callbackId, UA_Double interval_ms) { return UA_Timer_changeRepeatedCallbackInterval(&server->timer, callbackId, interval_ms); } void UA_Server_removeCallback(UA_Server *server, UA_UInt64 callbackId) { UA_Timer_removeCallback(&server->timer, callbackId); } UA_StatusCode UA_EXPORT UA_Server_updateCertificate(UA_Server *server, const UA_ByteString *oldCertificate, const UA_ByteString *newCertificate, const UA_ByteString *newPrivateKey, UA_Boolean closeSessions, UA_Boolean closeSecureChannels) { if (server == NULL || oldCertificate == NULL || newCertificate == NULL || newPrivateKey == NULL) { return UA_STATUSCODE_BADINTERNALERROR; } if (closeSessions) { UA_SessionManager *sm = &server->sessionManager; session_list_entry *current; LIST_FOREACH(current, &sm->sessions, pointers) { if (UA_ByteString_equal(oldCertificate, ¤t->session.header.channel->securityPolicy->localCertificate)) { UA_SessionManager_removeSession(sm, ¤t->session.header.authenticationToken); } } } if (closeSecureChannels) { UA_SecureChannelManager *cm = &server->secureChannelManager; channel_entry *entry; TAILQ_FOREACH(entry, &cm->channels, pointers) { if(UA_ByteString_equal(&entry->channel.securityPolicy->localCertificate, oldCertificate)){ UA_SecureChannelManager_close(cm, entry->channel.securityToken.channelId); } } } size_t i = 0; while (i < server->config.endpointsSize) { UA_EndpointDescription *ed = &server->config.endpoints[i]; if (UA_ByteString_equal(&ed->serverCertificate, oldCertificate)) { UA_String_deleteMembers(&ed->serverCertificate); UA_String_copy(newCertificate, &ed->serverCertificate); UA_SecurityPolicy *sp = UA_SecurityPolicy_getSecurityPolicyByUri(server, &server->config.endpoints[i].securityPolicyUri); if(!sp) return UA_STATUSCODE_BADINTERNALERROR; sp->updateCertificateAndPrivateKey(sp, *newCertificate, *newPrivateKey); } i++; } return UA_STATUSCODE_GOOD; } /***************************/ /* Server lookup functions */ /***************************/ UA_SecurityPolicy * UA_SecurityPolicy_getSecurityPolicyByUri(const UA_Server *server, const UA_ByteString *securityPolicyUri) { for(size_t i = 0; i < server->config.securityPoliciesSize; i++) { UA_SecurityPolicy *securityPolicyCandidate = &server->config.securityPolicies[i]; if(UA_ByteString_equal(securityPolicyUri, &securityPolicyCandidate->policyUri)) return securityPolicyCandidate; } return NULL; } #ifdef UA_ENABLE_ENCRYPTION /* The local ApplicationURI has to match the certificates of the * SecurityPolicies */ static void verifyServerApplicationURI(const UA_Server *server) { #if UA_LOGLEVEL <= 400 for(size_t i = 0; i < server->config.securityPoliciesSize; i++) { UA_SecurityPolicy *sp = &server->config.securityPolicies[i]; if(!sp->certificateVerification) continue; UA_StatusCode retval = sp->certificateVerification-> verifyApplicationURI(sp->certificateVerification->context, &sp->localCertificate, &server->config.applicationDescription.applicationUri); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "The configured ApplicationURI does not match the URI " "specified in the certificate for the SecurityPolicy %.*s", (int)sp->policyUri.length, sp->policyUri.data); } } #endif } #endif /********************/ /* Main Server Loop */ /********************/ #define UA_MAXTIMEOUT 50 /* Max timeout in ms between main-loop iterations */ /* Start: Spin up the workers and the network layer and sample the server's * start time. * Iterate: Process repeated callbacks and events in the network layer. This * part can be driven from an external main-loop in an event-driven * single-threaded architecture. * Stop: Stop workers, finish all callbacks, stop the network layer, clean up */ UA_StatusCode UA_Server_run_startup(UA_Server *server) { /* ensure that the uri for ns1 is set up from the app description */ setupNs1Uri(server); /* write ServerArray with same ApplicationURI value as NamespaceArray */ UA_StatusCode retVal = writeNs0VariableArray(server, UA_NS0ID_SERVER_SERVERARRAY, &server->config.applicationDescription.applicationUri, 1, &UA_TYPES[UA_TYPES_STRING]); if(retVal != UA_STATUSCODE_GOOD) return retVal; if(server->state > UA_SERVERLIFECYCLE_FRESH) return UA_STATUSCODE_GOOD; /* At least one endpoint has to be configured */ if(server->config.endpointsSize == 0) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "There has to be at least one endpoint."); } /* Initialized discovery */ #ifdef UA_ENABLE_DISCOVERY UA_DiscoveryManager_init(&server->discoveryManager, server); #endif /* Does the ApplicationURI match the local certificates? */ #ifdef UA_ENABLE_ENCRYPTION verifyServerApplicationURI(server); #endif /* Sample the start time and set it to the Server object */ server->startTime = UA_DateTime_now(); UA_Variant var; UA_Variant_init(&var); UA_Variant_setScalar(&var, &server->startTime, &UA_TYPES[UA_TYPES_DATETIME]); UA_Server_writeValue(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STARTTIME), var); /* Start the networklayers */ UA_StatusCode result = UA_STATUSCODE_GOOD; for(size_t i = 0; i < server->config.networkLayersSize; ++i) { UA_ServerNetworkLayer *nl = &server->config.networkLayers[i]; result |= nl->start(nl, &server->config.customHostname); } /* Update the application description to match the previously added discovery urls. * We can only do this after the network layer is started since it inits the discovery url */ if (server->config.applicationDescription.discoveryUrlsSize != 0) { UA_Array_delete(server->config.applicationDescription.discoveryUrls, server->config.applicationDescription.discoveryUrlsSize, &UA_TYPES[UA_TYPES_STRING]); server->config.applicationDescription.discoveryUrlsSize = 0; } server->config.applicationDescription.discoveryUrls = (UA_String *) UA_Array_new(server->config.networkLayersSize, &UA_TYPES[UA_TYPES_STRING]); if (!server->config.applicationDescription.discoveryUrls) { return UA_STATUSCODE_BADOUTOFMEMORY; } server->config.applicationDescription.discoveryUrlsSize = server->config.networkLayersSize; for (size_t i=0; i< server->config.applicationDescription.discoveryUrlsSize; i++) { UA_ServerNetworkLayer *nl = &server->config.networkLayers[i]; UA_String_copy(&nl->discoveryUrl, &server->config.applicationDescription.discoveryUrls[i]); } /* Spin up the worker threads */ #ifdef UA_ENABLE_MULTITHREADING UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "Spinning up %u worker thread(s)", server->config.nThreads); UA_WorkQueue_start(&server->workQueue, server->config.nThreads); #endif /* Start the multicast discovery server */ #ifdef UA_ENABLE_DISCOVERY_MULTICAST if(server->config.discovery.mdnsEnable) startMulticastDiscoveryServer(server); #endif server->state = UA_SERVERLIFECYCLE_FRESH; return result; } static void serverExecuteRepeatedCallback(UA_Server *server, UA_ApplicationCallback cb, void *callbackApplication, void *data) { #ifndef UA_ENABLE_MULTITHREADING cb(callbackApplication, data); #else UA_WorkQueue_enqueue(&server->workQueue, cb, callbackApplication, data); #endif } UA_UInt16 UA_Server_run_iterate(UA_Server *server, UA_Boolean waitInternal) { /* Process repeated work */ UA_DateTime now = UA_DateTime_nowMonotonic(); UA_DateTime nextRepeated = UA_Timer_process(&server->timer, now, (UA_TimerExecutionCallback)serverExecuteRepeatedCallback, server); UA_DateTime latest = now + (UA_MAXTIMEOUT * UA_DATETIME_MSEC); if(nextRepeated > latest) nextRepeated = latest; UA_UInt16 timeout = 0; /* round always to upper value to avoid timeout to be set to 0 * if(nextRepeated - now) < (UA_DATETIME_MSEC/2) */ if(waitInternal) timeout = (UA_UInt16)(((nextRepeated - now) + (UA_DATETIME_MSEC - 1)) / UA_DATETIME_MSEC); /* Listen on the networklayer */ for(size_t i = 0; i < server->config.networkLayersSize; ++i) { UA_ServerNetworkLayer *nl = &server->config.networkLayers[i]; nl->listen(nl, server, timeout); } #if defined(UA_ENABLE_DISCOVERY_MULTICAST) && !defined(UA_ENABLE_MULTITHREADING) if(server->config.discovery.mdnsEnable) { // TODO multicastNextRepeat does not consider new input data (requests) // on the socket. It will be handled on the next call. if needed, we // need to use select with timeout on the multicast socket // server->mdnsSocket (see example in mdnsd library) on higher level. UA_DateTime multicastNextRepeat = 0; UA_StatusCode hasNext = iterateMulticastDiscoveryServer(server, &multicastNextRepeat, true); if(hasNext == UA_STATUSCODE_GOOD && multicastNextRepeat < nextRepeated) nextRepeated = multicastNextRepeat; } #endif #ifndef UA_ENABLE_MULTITHREADING UA_WorkQueue_manuallyProcessDelayed(&server->workQueue); #endif now = UA_DateTime_nowMonotonic(); timeout = 0; if(nextRepeated > now) timeout = (UA_UInt16)((nextRepeated - now) / UA_DATETIME_MSEC); return timeout; } UA_StatusCode UA_Server_run_shutdown(UA_Server *server) { /* Stop the netowrk layer */ for(size_t i = 0; i < server->config.networkLayersSize; ++i) { UA_ServerNetworkLayer *nl = &server->config.networkLayers[i]; nl->stop(nl, server); } #ifdef UA_ENABLE_MULTITHREADING /* Shut down the workers */ UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "Shutting down %u worker thread(s)", (UA_UInt32)server->workQueue.workersSize); UA_WorkQueue_stop(&server->workQueue); #endif #ifdef UA_ENABLE_DISCOVERY_MULTICAST /* Stop multicast discovery */ if(server->config.discovery.mdnsEnable) stopMulticastDiscoveryServer(server); #endif /* Execute all delayed callbacks */ UA_WorkQueue_cleanup(&server->workQueue); return UA_STATUSCODE_GOOD; } static UA_Boolean testShutdownCondition(UA_Server *server) { if(server->endTime == 0) return false; return (UA_DateTime_now() > server->endTime); } UA_StatusCode UA_Server_run(UA_Server *server, const volatile UA_Boolean *running) { UA_StatusCode retval = UA_Server_run_startup(server); if(retval != UA_STATUSCODE_GOOD) return retval; #ifdef UA_ENABLE_VALGRIND_INTERACTIVE size_t loopCount = 0; #endif while(!testShutdownCondition(server)) { #ifdef UA_ENABLE_VALGRIND_INTERACTIVE if(loopCount == 0) { VALGRIND_DO_LEAK_CHECK; } ++loopCount; loopCount %= UA_VALGRIND_INTERACTIVE_INTERVAL; #endif UA_Server_run_iterate(server, true); if(!*running) { if(setServerShutdown(server)) break; } } return UA_Server_run_shutdown(server); } #ifdef UA_ENABLE_HISTORIZING /* Allow insert of historical data */ UA_Boolean UA_Server_AccessControl_allowHistoryUpdateUpdateData(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, UA_PerformUpdateType performInsertReplace, const UA_DataValue *value) { if(server->config.accessControl.allowHistoryUpdateUpdateData && !server->config.accessControl.allowHistoryUpdateUpdateData(server, &server->config.accessControl, sessionId, sessionContext, nodeId, performInsertReplace, value)) { return false; } return true; } /* Allow delete of historical data */ UA_Boolean UA_Server_AccessControl_allowHistoryUpdateDeleteRawModified(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, UA_DateTime startTimestamp, UA_DateTime endTimestamp, bool isDeleteModified) { if(server->config.accessControl.allowHistoryUpdateDeleteRawModified && !server->config.accessControl.allowHistoryUpdateDeleteRawModified(server, &server->config.accessControl, sessionId, sessionContext, nodeId, startTimestamp, endTimestamp, isDeleteModified)) { return false; } return true; } #endif /* UA_ENABLE_HISTORIZING */ /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_server_ns0.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2017 (c) Thomas Bender * Copyright 2017 (c) Julian Grothoff * Copyright 2017 (c) Henrik Norrman * Copyright 2018 (c) Fabian Arndt, Root-Core * Copyright 2019 (c) Kalycito Infotech Private Limited */ static UA_StatusCode addNode_raw(UA_Server *server, UA_NodeClass nodeClass, UA_UInt32 nodeId, char *name, void *attributes, const UA_DataType *attributesType) { UA_AddNodesItem item; UA_AddNodesItem_init(&item); item.nodeClass = nodeClass; item.requestedNewNodeId.nodeId = UA_NODEID_NUMERIC(0, nodeId); item.browseName = UA_QUALIFIEDNAME(0, name); item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE; item.nodeAttributes.content.decoded.data = attributes; item.nodeAttributes.content.decoded.type = attributesType; return AddNode_raw(server, &server->adminSession, NULL, &item, NULL); } static UA_StatusCode addNode_finish(UA_Server *server, UA_UInt32 nodeId, UA_UInt32 parentNodeId, UA_UInt32 referenceTypeId) { const UA_NodeId sourceId = UA_NODEID_NUMERIC(0, nodeId); const UA_NodeId refTypeId = UA_NODEID_NUMERIC(0, referenceTypeId); const UA_ExpandedNodeId targetId = UA_EXPANDEDNODEID_NUMERIC(0, parentNodeId); UA_StatusCode retval = UA_Server_addReference(server, sourceId, refTypeId, targetId, false); if (retval != UA_STATUSCODE_GOOD) return retval; return AddNode_finish(server, &server->adminSession, &sourceId); } static UA_StatusCode addObjectNode(UA_Server *server, char* name, UA_UInt32 objectid, UA_UInt32 parentid, UA_UInt32 referenceid, UA_UInt32 type_id) { UA_ObjectAttributes object_attr = UA_ObjectAttributes_default; object_attr.displayName = UA_LOCALIZEDTEXT("", name); return UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(0, objectid), UA_NODEID_NUMERIC(0, parentid), UA_NODEID_NUMERIC(0, referenceid), UA_QUALIFIEDNAME(0, name), UA_NODEID_NUMERIC(0, type_id), object_attr, NULL, NULL); } static UA_StatusCode addReferenceTypeNode(UA_Server *server, char* name, char *inverseName, UA_UInt32 referencetypeid, UA_Boolean isabstract, UA_Boolean symmetric, UA_UInt32 parentid) { UA_ReferenceTypeAttributes reference_attr = UA_ReferenceTypeAttributes_default; reference_attr.displayName = UA_LOCALIZEDTEXT("", name); reference_attr.isAbstract = isabstract; reference_attr.symmetric = symmetric; if(inverseName) reference_attr.inverseName = UA_LOCALIZEDTEXT("", inverseName); return UA_Server_addReferenceTypeNode(server, UA_NODEID_NUMERIC(0, referencetypeid), UA_NODEID_NUMERIC(0, parentid), UA_NODEID_NULL, UA_QUALIFIEDNAME(0, name), reference_attr, NULL, NULL); } /***************************/ /* Bootstrap NS0 hierarchy */ /***************************/ /* Creates the basic nodes which are expected by the nodeset compiler to be * already created. This is necessary to reduce the dependencies for the nodeset * compiler. */ static UA_StatusCode UA_Server_createNS0_base(UA_Server *server) { /* Bootstrap References and HasSubtype */ UA_StatusCode ret = UA_STATUSCODE_GOOD; UA_ReferenceTypeAttributes references_attr = UA_ReferenceTypeAttributes_default; references_attr.displayName = UA_LOCALIZEDTEXT("", "References"); references_attr.isAbstract = true; references_attr.symmetric = true; references_attr.inverseName = UA_LOCALIZEDTEXT("", "References"); ret |= addNode_raw(server, UA_NODECLASS_REFERENCETYPE, UA_NS0ID_REFERENCES, "References", &references_attr, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES]); UA_ReferenceTypeAttributes hassubtype_attr = UA_ReferenceTypeAttributes_default; hassubtype_attr.displayName = UA_LOCALIZEDTEXT("", "HasSubtype"); hassubtype_attr.isAbstract = false; hassubtype_attr.symmetric = false; hassubtype_attr.inverseName = UA_LOCALIZEDTEXT("", "HasSupertype"); ret |= addNode_raw(server, UA_NODECLASS_REFERENCETYPE, UA_NS0ID_HASSUBTYPE, "HasSubtype", &hassubtype_attr, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES]); UA_ReferenceTypeAttributes aggregates_attr = UA_ReferenceTypeAttributes_default; aggregates_attr.displayName = UA_LOCALIZEDTEXT("", "Aggregates"); aggregates_attr.isAbstract = false; aggregates_attr.symmetric = false; aggregates_attr.inverseName = UA_LOCALIZEDTEXT("", "AggregatedBy"); ret |= addNode_raw(server, UA_NODECLASS_REFERENCETYPE, UA_NS0ID_AGGREGATES, "Aggregates", &aggregates_attr, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES]); ret |= addReferenceTypeNode(server, "HierarchicalReferences", NULL, UA_NS0ID_HIERARCHICALREFERENCES, true, false, UA_NS0ID_REFERENCES); ret |= addReferenceTypeNode(server, "NonHierarchicalReferences", NULL, UA_NS0ID_NONHIERARCHICALREFERENCES, true, false, UA_NS0ID_REFERENCES); ret |= addReferenceTypeNode(server, "HasChild", NULL, UA_NS0ID_HASCHILD, true, false, UA_NS0ID_HIERARCHICALREFERENCES); ret |= addReferenceTypeNode(server, "Organizes", "OrganizedBy", UA_NS0ID_ORGANIZES, false, false, UA_NS0ID_HIERARCHICALREFERENCES); ret |= addReferenceTypeNode(server, "HasEventSource", "EventSourceOf", UA_NS0ID_HASEVENTSOURCE, false, false, UA_NS0ID_HIERARCHICALREFERENCES); ret |= addReferenceTypeNode(server, "HasModellingRule", "ModellingRuleOf", UA_NS0ID_HASMODELLINGRULE, false, false, UA_NS0ID_NONHIERARCHICALREFERENCES); ret |= addReferenceTypeNode(server, "HasEncoding", "EncodingOf", UA_NS0ID_HASENCODING, false, false, UA_NS0ID_NONHIERARCHICALREFERENCES); ret |= addReferenceTypeNode(server, "HasDescription", "DescriptionOf", UA_NS0ID_HASDESCRIPTION, false, false, UA_NS0ID_NONHIERARCHICALREFERENCES); ret |= addReferenceTypeNode(server, "HasTypeDefinition", "TypeDefinitionOf", UA_NS0ID_HASTYPEDEFINITION, false, false, UA_NS0ID_NONHIERARCHICALREFERENCES); ret |= addReferenceTypeNode(server, "GeneratesEvent", "GeneratedBy", UA_NS0ID_GENERATESEVENT, false, false, UA_NS0ID_NONHIERARCHICALREFERENCES); /* Complete bootstrap of Aggregates */ ret |= addNode_finish(server, UA_NS0ID_AGGREGATES, UA_NS0ID_HASCHILD, UA_NS0ID_HASSUBTYPE); /* Complete bootstrap of HasSubtype */ ret |= addNode_finish(server, UA_NS0ID_HASSUBTYPE, UA_NS0ID_HASCHILD, UA_NS0ID_HASSUBTYPE); ret |= addReferenceTypeNode(server, "HasProperty", "PropertyOf", UA_NS0ID_HASPROPERTY, false, false, UA_NS0ID_AGGREGATES); ret |= addReferenceTypeNode(server, "HasComponent", "ComponentOf", UA_NS0ID_HASCOMPONENT, false, false, UA_NS0ID_AGGREGATES); ret |= addReferenceTypeNode(server, "HasNotifier", "NotifierOf", UA_NS0ID_HASNOTIFIER, false, false, UA_NS0ID_HASEVENTSOURCE); ret |= addReferenceTypeNode(server, "HasOrderedComponent", "OrderedComponentOf", UA_NS0ID_HASORDEREDCOMPONENT, false, false, UA_NS0ID_HASCOMPONENT); /**************/ /* Data Types */ /**************/ /* Bootstrap BaseDataType */ UA_DataTypeAttributes basedatatype_attr = UA_DataTypeAttributes_default; basedatatype_attr.displayName = UA_LOCALIZEDTEXT("", "BaseDataType"); basedatatype_attr.isAbstract = true; ret |= addNode_raw(server, UA_NODECLASS_DATATYPE, UA_NS0ID_BASEDATATYPE, "BaseDataType", &basedatatype_attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES]); /*****************/ /* VariableTypes */ /*****************/ UA_VariableTypeAttributes basevar_attr = UA_VariableTypeAttributes_default; basevar_attr.displayName = UA_LOCALIZEDTEXT("", "BaseVariableType"); basevar_attr.isAbstract = true; basevar_attr.valueRank = UA_VALUERANK_ANY; basevar_attr.dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE); ret |= addNode_raw(server, UA_NODECLASS_VARIABLETYPE, UA_NS0ID_BASEVARIABLETYPE, "BaseVariableType", &basevar_attr, &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES]); UA_VariableTypeAttributes bdv_attr = UA_VariableTypeAttributes_default; bdv_attr.displayName = UA_LOCALIZEDTEXT("", "BaseDataVariableType"); bdv_attr.dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE); bdv_attr.valueRank = UA_VALUERANK_ANY; ret |= UA_Server_addVariableTypeNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEVARIABLETYPE), UA_NODEID_NULL, UA_QUALIFIEDNAME(0, "BaseDataVariableType"), UA_NODEID_NULL, bdv_attr, NULL, NULL); UA_VariableTypeAttributes prop_attr = UA_VariableTypeAttributes_default; prop_attr.displayName = UA_LOCALIZEDTEXT("", "PropertyType"); prop_attr.dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE); prop_attr.valueRank = UA_VALUERANK_ANY; ret |= UA_Server_addVariableTypeNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEVARIABLETYPE), UA_NODEID_NULL, UA_QUALIFIEDNAME(0, "PropertyType"), UA_NODEID_NULL, prop_attr, NULL, NULL); /***************/ /* ObjectTypes */ /***************/ UA_ObjectTypeAttributes baseobj_attr = UA_ObjectTypeAttributes_default; baseobj_attr.displayName = UA_LOCALIZEDTEXT("", "BaseObjectType"); ret |= addNode_raw(server, UA_NODECLASS_OBJECTTYPE, UA_NS0ID_BASEOBJECTTYPE, "BaseObjectType", &baseobj_attr, &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES]); UA_ObjectTypeAttributes folder_attr = UA_ObjectTypeAttributes_default; folder_attr.displayName = UA_LOCALIZEDTEXT("", "FolderType"); ret |= UA_Server_addObjectTypeNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE), UA_NODEID_NULL, UA_QUALIFIEDNAME(0, "FolderType"), folder_attr, NULL, NULL); /******************/ /* Root and below */ /******************/ ret |= addObjectNode(server, "Root", UA_NS0ID_ROOTFOLDER, 0, 0, UA_NS0ID_FOLDERTYPE); ret |= addObjectNode(server, "Objects", UA_NS0ID_OBJECTSFOLDER, UA_NS0ID_ROOTFOLDER, UA_NS0ID_ORGANIZES, UA_NS0ID_FOLDERTYPE); ret |= addObjectNode(server, "Types", UA_NS0ID_TYPESFOLDER, UA_NS0ID_ROOTFOLDER, UA_NS0ID_ORGANIZES, UA_NS0ID_FOLDERTYPE); ret |= addObjectNode(server, "ReferenceTypes", UA_NS0ID_REFERENCETYPESFOLDER, UA_NS0ID_TYPESFOLDER, UA_NS0ID_ORGANIZES, UA_NS0ID_FOLDERTYPE); ret |= addNode_finish(server, UA_NS0ID_REFERENCES, UA_NS0ID_REFERENCETYPESFOLDER, UA_NS0ID_ORGANIZES); ret |= addObjectNode(server, "DataTypes", UA_NS0ID_DATATYPESFOLDER, UA_NS0ID_TYPESFOLDER, UA_NS0ID_ORGANIZES, UA_NS0ID_FOLDERTYPE); ret |= addNode_finish(server, UA_NS0ID_BASEDATATYPE, UA_NS0ID_DATATYPESFOLDER, UA_NS0ID_ORGANIZES); ret |= addObjectNode(server, "VariableTypes", UA_NS0ID_VARIABLETYPESFOLDER, UA_NS0ID_TYPESFOLDER, UA_NS0ID_ORGANIZES, UA_NS0ID_FOLDERTYPE); ret |= addNode_finish(server, UA_NS0ID_BASEVARIABLETYPE, UA_NS0ID_VARIABLETYPESFOLDER, UA_NS0ID_ORGANIZES); ret |= addObjectNode(server, "ObjectTypes", UA_NS0ID_OBJECTTYPESFOLDER, UA_NS0ID_TYPESFOLDER, UA_NS0ID_ORGANIZES, UA_NS0ID_FOLDERTYPE); ret |= addNode_finish(server, UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_OBJECTTYPESFOLDER, UA_NS0ID_ORGANIZES); ret |= addObjectNode(server, "EventTypes", UA_NS0ID_EVENTTYPESFOLDER, UA_NS0ID_TYPESFOLDER, UA_NS0ID_ORGANIZES, UA_NS0ID_FOLDERTYPE); ret |= addObjectNode(server, "Views", UA_NS0ID_VIEWSFOLDER, UA_NS0ID_ROOTFOLDER, UA_NS0ID_ORGANIZES, UA_NS0ID_FOLDERTYPE); if(ret != UA_STATUSCODE_GOOD) ret = UA_STATUSCODE_BADINTERNALERROR; return ret; } /****************/ /* Data Sources */ /****************/ static UA_StatusCode readStatus(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, void *nodeContext, UA_Boolean sourceTimestamp, const UA_NumericRange *range, UA_DataValue *value) { if(range) { value->hasStatus = true; value->status = UA_STATUSCODE_BADINDEXRANGEINVALID; return UA_STATUSCODE_GOOD; } if(sourceTimestamp) { value->hasSourceTimestamp = true; value->sourceTimestamp = UA_DateTime_now(); } void *data = NULL; UA_assert(nodeId->identifierType == UA_NODEIDTYPE_NUMERIC); switch(nodeId->identifier.numeric) { case UA_NS0ID_SERVER_SERVERSTATUS_SECONDSTILLSHUTDOWN: { UA_UInt32 *shutdown = UA_UInt32_new(); if(!shutdown) return UA_STATUSCODE_BADOUTOFMEMORY; if(server->endTime != 0) *shutdown = (UA_UInt32)((server->endTime - UA_DateTime_now()) / UA_DATETIME_SEC); value->value.data = shutdown; value->value.type = &UA_TYPES[UA_TYPES_UINT32]; value->hasValue = true; return UA_STATUSCODE_GOOD; } case UA_NS0ID_SERVER_SERVERSTATUS_STATE: { UA_ServerState *state = UA_ServerState_new(); if(!state) return UA_STATUSCODE_BADOUTOFMEMORY; if(server->endTime != 0) *state = UA_SERVERSTATE_SHUTDOWN; value->value.data = state; value->value.type = &UA_TYPES[UA_TYPES_SERVERSTATE]; value->hasValue = true; return UA_STATUSCODE_GOOD; } case UA_NS0ID_SERVER_SERVERSTATUS: { UA_ServerStatusDataType *statustype = UA_ServerStatusDataType_new(); if(!statustype) return UA_STATUSCODE_BADOUTOFMEMORY; statustype->startTime = server->startTime; statustype->currentTime = UA_DateTime_now(); statustype->state = UA_SERVERSTATE_RUNNING; statustype->secondsTillShutdown = 0; if(server->endTime != 0) { statustype->state = UA_SERVERSTATE_SHUTDOWN; statustype->secondsTillShutdown = (UA_UInt32)((server->endTime - UA_DateTime_now()) / UA_DATETIME_SEC); } value->value.data = statustype; value->value.type = &UA_TYPES[UA_TYPES_SERVERSTATUSDATATYPE]; value->hasValue = true; return UA_BuildInfo_copy(&server->config.buildInfo, &statustype->buildInfo); } case UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO: value->value.type = &UA_TYPES[UA_TYPES_BUILDINFO]; data = &server->config.buildInfo; break; case UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTURI: value->value.type = &UA_TYPES[UA_TYPES_STRING]; data = &server->config.buildInfo.productUri; break; case UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_MANUFACTURERNAME: value->value.type = &UA_TYPES[UA_TYPES_STRING]; data = &server->config.buildInfo.manufacturerName; break; case UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTNAME: value->value.type = &UA_TYPES[UA_TYPES_STRING]; data = &server->config.buildInfo.productName; break; case UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_SOFTWAREVERSION: value->value.type = &UA_TYPES[UA_TYPES_STRING]; data = &server->config.buildInfo.softwareVersion; break; case UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER: value->value.type = &UA_TYPES[UA_TYPES_STRING]; data = &server->config.buildInfo.buildNumber; break; case UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDDATE: value->value.type = &UA_TYPES[UA_TYPES_DATETIME]; data = &server->config.buildInfo.buildDate; break; default: value->hasStatus = true; value->status = UA_STATUSCODE_BADINTERNALERROR; return UA_STATUSCODE_GOOD; } value->value.data = UA_new(value->value.type); if(!value->value.data) { value->value.type = NULL; return UA_STATUSCODE_BADOUTOFMEMORY; } value->hasValue = true; return UA_copy(data, value->value.data, value->value.type); } #ifdef UA_GENERATED_NAMESPACE_ZERO static UA_StatusCode readServiceLevel(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, void *nodeContext, UA_Boolean includeSourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value) { if(range) { value->hasStatus = true; value->status = UA_STATUSCODE_BADINDEXRANGEINVALID; return UA_STATUSCODE_GOOD; } value->value.type = &UA_TYPES[UA_TYPES_BYTE]; value->value.arrayLength = 0; UA_Byte *byte = UA_Byte_new(); *byte = 255; value->value.data = byte; value->value.arrayDimensionsSize = 0; value->value.arrayDimensions = NULL; value->hasValue = true; if(includeSourceTimeStamp) { value->hasSourceTimestamp = true; value->sourceTimestamp = UA_DateTime_now(); } return UA_STATUSCODE_GOOD; } static UA_StatusCode readAuditing(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, void *nodeContext, UA_Boolean includeSourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value) { if(range) { value->hasStatus = true; value->status = UA_STATUSCODE_BADINDEXRANGEINVALID; return UA_STATUSCODE_GOOD; } value->value.type = &UA_TYPES[UA_TYPES_BOOLEAN]; value->value.arrayLength = 0; UA_Boolean *boolean = UA_Boolean_new(); *boolean = false; value->value.data = boolean; value->value.arrayDimensionsSize = 0; value->value.arrayDimensions = NULL; value->hasValue = true; if(includeSourceTimeStamp) { value->hasSourceTimestamp = true; value->sourceTimestamp = UA_DateTime_now(); } return UA_STATUSCODE_GOOD; } #endif static UA_StatusCode readNamespaces(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeid, void *nodeContext, UA_Boolean includeSourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value) { /* ensure that the uri for ns1 is set up from the app description */ setupNs1Uri(server); if(range) { value->hasStatus = true; value->status = UA_STATUSCODE_BADINDEXRANGEINVALID; return UA_STATUSCODE_GOOD; } UA_StatusCode retval; retval = UA_Variant_setArrayCopy(&value->value, server->namespaces, server->namespacesSize, &UA_TYPES[UA_TYPES_STRING]); if(retval != UA_STATUSCODE_GOOD) return retval; value->hasValue = true; if(includeSourceTimeStamp) { value->hasSourceTimestamp = true; value->sourceTimestamp = UA_DateTime_now(); } return UA_STATUSCODE_GOOD; } static UA_StatusCode writeNamespaces(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeid, void *nodeContext, const UA_NumericRange *range, const UA_DataValue *value) { /* Check the data type */ if(!value->hasValue || value->value.type != &UA_TYPES[UA_TYPES_STRING]) return UA_STATUSCODE_BADTYPEMISMATCH; /* Check that the variant is not empty */ if(!value->value.data) return UA_STATUSCODE_BADTYPEMISMATCH; /* TODO: Writing with a range is not implemented */ if(range) return UA_STATUSCODE_BADINTERNALERROR; UA_String *newNamespaces = (UA_String*)value->value.data; size_t newNamespacesSize = value->value.arrayLength; /* Test if we append to the existing namespaces */ if(newNamespacesSize <= server->namespacesSize) return UA_STATUSCODE_BADTYPEMISMATCH; /* ensure that the uri for ns1 is set up from the app description */ setupNs1Uri(server); /* Test if the existing namespaces are unchanged */ for(size_t i = 0; i < server->namespacesSize; ++i) { if(!UA_String_equal(&server->namespaces[i], &newNamespaces[i])) return UA_STATUSCODE_BADINTERNALERROR; } /* Add namespaces */ for(size_t i = server->namespacesSize; i < newNamespacesSize; ++i) addNamespace(server, newNamespaces[i]); return UA_STATUSCODE_GOOD; } static UA_StatusCode readCurrentTime(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeid, void *nodeContext, UA_Boolean sourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value) { if(range) { value->hasStatus = true; value->status = UA_STATUSCODE_BADINDEXRANGEINVALID; return UA_STATUSCODE_GOOD; } UA_DateTime currentTime = UA_DateTime_now(); UA_StatusCode retval = UA_Variant_setScalarCopy(&value->value, ¤tTime, &UA_TYPES[UA_TYPES_DATETIME]); if(retval != UA_STATUSCODE_GOOD) return retval; value->hasValue = true; if(sourceTimeStamp) { value->hasSourceTimestamp = true; value->sourceTimestamp = currentTime; } return UA_STATUSCODE_GOOD; } #ifdef UA_GENERATED_NAMESPACE_ZERO static UA_StatusCode readMinSamplingInterval(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeid, void *nodeContext, UA_Boolean includeSourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value) { if(range) { value->hasStatus = true; value->status = UA_STATUSCODE_BADINDEXRANGEINVALID; return UA_STATUSCODE_GOOD; } UA_StatusCode retval; retval = UA_Variant_setScalarCopy(&value->value, &server->config.samplingIntervalLimits.min, &UA_TYPES[UA_TYPES_DURATION]); if(retval != UA_STATUSCODE_GOOD) return retval; value->hasValue = true; if(includeSourceTimeStamp) { value->hasSourceTimestamp = true; value->sourceTimestamp = UA_DateTime_now(); } return UA_STATUSCODE_GOOD; } #endif #if defined(UA_GENERATED_NAMESPACE_ZERO) && defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS) static UA_StatusCode readMonitoredItems(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId, void *objectContext, size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output) { UA_Session *session = UA_SessionManager_getSessionById(&server->sessionManager, sessionId); if(!session) return UA_STATUSCODE_BADINTERNALERROR; if (inputSize == 0 || !input[0].data) return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; UA_UInt32 subscriptionId = *((UA_UInt32*)(input[0].data)); UA_Subscription* subscription = UA_Session_getSubscriptionById(session, subscriptionId); if(!subscription) { if(LIST_EMPTY(&session->serverSubscriptions)) { UA_Variant_setArray(&output[0], UA_Array_new(0, &UA_TYPES[UA_TYPES_UINT32]), 0, &UA_TYPES[UA_TYPES_UINT32]); UA_Variant_setArray(&output[1], UA_Array_new(0, &UA_TYPES[UA_TYPES_UINT32]), 0, &UA_TYPES[UA_TYPES_UINT32]); return UA_STATUSCODE_BADNOMATCH; } return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; } UA_UInt32 sizeOfOutput = 0; UA_MonitoredItem* monitoredItem; LIST_FOREACH(monitoredItem, &subscription->monitoredItems, listEntry) { ++sizeOfOutput; } if(sizeOfOutput==0) return UA_STATUSCODE_GOOD; UA_UInt32* clientHandles = (UA_UInt32 *)UA_Array_new(sizeOfOutput, &UA_TYPES[UA_TYPES_UINT32]); UA_UInt32* serverHandles = (UA_UInt32 *)UA_Array_new(sizeOfOutput, &UA_TYPES[UA_TYPES_UINT32]); UA_UInt32 i = 0; LIST_FOREACH(monitoredItem, &subscription->monitoredItems, listEntry) { clientHandles[i] = monitoredItem->clientHandle; serverHandles[i] = monitoredItem->monitoredItemId; ++i; } UA_Variant_setArray(&output[0], serverHandles, sizeOfOutput, &UA_TYPES[UA_TYPES_UINT32]); UA_Variant_setArray(&output[1], clientHandles, sizeOfOutput, &UA_TYPES[UA_TYPES_UINT32]); return UA_STATUSCODE_GOOD; } #endif /* defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS) */ UA_StatusCode writeNs0VariableArray(UA_Server *server, UA_UInt32 id, void *v, size_t length, const UA_DataType *type) { UA_Variant var; UA_Variant_init(&var); UA_Variant_setArray(&var, v, length, type); return UA_Server_writeValue(server, UA_NODEID_NUMERIC(0, id), var); } #ifndef UA_GENERATED_NAMESPACE_ZERO static UA_StatusCode addVariableNode(UA_Server *server, char* name, UA_UInt32 variableid, UA_UInt32 parentid, UA_UInt32 referenceid, UA_Int32 valueRank, UA_UInt32 dataType) { UA_VariableAttributes attr = UA_VariableAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", name); attr.dataType = UA_NODEID_NUMERIC(0, dataType); attr.valueRank = valueRank; attr.accessLevel = UA_ACCESSLEVELMASK_READ; return UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(0, variableid), UA_NODEID_NUMERIC(0, parentid), UA_NODEID_NUMERIC(0, referenceid), UA_QUALIFIEDNAME(0, name), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr, NULL, NULL); } /* A minimal server object that is not complete and does not use the mandated * references to a server type. To be used on very constrained devices. */ static UA_StatusCode UA_Server_minimalServerObject(UA_Server *server) { /* Server */ UA_StatusCode retval = addObjectNode(server, "Server", UA_NS0ID_SERVER, UA_NS0ID_OBJECTSFOLDER, UA_NS0ID_ORGANIZES, UA_NS0ID_BASEOBJECTTYPE); /* Use a valuerank of -2 for now. The array is added later on and the valuerank set to 1. */ retval |= addVariableNode(server, "ServerArray", UA_NS0ID_SERVER_SERVERARRAY, UA_NS0ID_SERVER, UA_NS0ID_HASPROPERTY, UA_VALUERANK_ANY, UA_NS0ID_BASEDATATYPE); retval |= addVariableNode(server, "NamespaceArray", UA_NS0ID_SERVER_NAMESPACEARRAY, UA_NS0ID_SERVER, UA_NS0ID_HASPROPERTY, UA_VALUERANK_ANY, UA_NS0ID_BASEDATATYPE); retval |= addVariableNode(server, "ServerStatus", UA_NS0ID_SERVER_SERVERSTATUS, UA_NS0ID_SERVER, UA_NS0ID_HASCOMPONENT, UA_VALUERANK_SCALAR, UA_NS0ID_BASEDATATYPE); retval |= addVariableNode(server, "CurrentTime", UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME, UA_NS0ID_SERVER_SERVERSTATUS, UA_NS0ID_HASCOMPONENT, UA_VALUERANK_SCALAR, UA_NS0ID_BASEDATATYPE); retval |= addVariableNode(server, "State", UA_NS0ID_SERVER_SERVERSTATUS_STATE, UA_NS0ID_SERVER_SERVERSTATUS, UA_NS0ID_HASCOMPONENT, UA_VALUERANK_SCALAR, UA_NS0ID_BASEDATATYPE); retval |= addVariableNode(server, "BuildInfo", UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO, UA_NS0ID_SERVER_SERVERSTATUS, UA_NS0ID_HASCOMPONENT, UA_VALUERANK_SCALAR, UA_NS0ID_BASEDATATYPE); retval |= addVariableNode(server, "ProductUri", UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTURI, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO, UA_NS0ID_HASCOMPONENT, UA_VALUERANK_SCALAR, UA_NS0ID_BASEDATATYPE); retval |= addVariableNode(server, "ManufacturerName", UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_MANUFACTURERNAME, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO, UA_NS0ID_HASCOMPONENT, UA_VALUERANK_SCALAR, UA_NS0ID_BASEDATATYPE); retval |= addVariableNode(server, "ProductName", UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTNAME, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO, UA_NS0ID_HASCOMPONENT, UA_VALUERANK_SCALAR, UA_NS0ID_BASEDATATYPE); retval |= addVariableNode(server, "SoftwareVersion", UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_SOFTWAREVERSION, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO, UA_NS0ID_HASCOMPONENT, UA_VALUERANK_SCALAR, UA_NS0ID_BASEDATATYPE); retval |= addVariableNode(server, "BuildNumber", UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO, UA_NS0ID_HASCOMPONENT, UA_VALUERANK_SCALAR, UA_NS0ID_BASEDATATYPE); retval |= addVariableNode(server, "BuildDate", UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDDATE, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO, UA_NS0ID_HASCOMPONENT, UA_VALUERANK_SCALAR, UA_NS0ID_BASEDATATYPE); return retval; } #else static UA_StatusCode writeNs0Variable(UA_Server *server, UA_UInt32 id, void *v, const UA_DataType *type) { UA_Variant var; UA_Variant_init(&var); UA_Variant_setScalar(&var, v, type); return UA_Server_writeValue(server, UA_NODEID_NUMERIC(0, id), var); } static void addModellingRules(UA_Server *server) { /* Test if the ModellingRules folder was added. (Only for the full ns0.) */ UA_NodeClass mrnc = UA_NODECLASS_UNSPECIFIED; UA_StatusCode retval = UA_Server_readNodeClass(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MODELLINGRULES), &mrnc); if(retval != UA_STATUSCODE_GOOD) return; /* Add ExposesItsArray */ UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MODELLINGRULES), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_EXPOSESITSARRAY), true); /* Add Mandatory */ UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MODELLINGRULES), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY), true); /* Add MandatoryPlaceholder */ UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MODELLINGRULES), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORYPLACEHOLDER), true); /* Add Optional */ UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MODELLINGRULES), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_OPTIONAL), true); /* Add OptionalPlaceholder */ UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MODELLINGRULES), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_OPTIONALPLACEHOLDER), true); } #endif /* Initialize the nodeset 0 by using the generated code of the nodeset compiler. * This also initialized the data sources for various variables, such as for * example server time. */ UA_StatusCode UA_Server_initNS0(UA_Server *server) { /* Initialize base nodes which are always required an cannot be created * through the NS compiler */ server->bootstrapNS0 = true; UA_StatusCode retVal = UA_Server_createNS0_base(server); server->bootstrapNS0 = false; if(retVal != UA_STATUSCODE_GOOD) return retVal; #ifdef UA_GENERATED_NAMESPACE_ZERO /* Load nodes and references generated from the XML ns0 definition */ retVal = namespace0_generated(server); #else /* Create a minimal server object */ retVal = UA_Server_minimalServerObject(server); #endif if(retVal != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Initialization of Namespace 0 (before bootstrapping) " "failed with %s. See previous outputs for any error messages.", UA_StatusCode_name(retVal)); return UA_STATUSCODE_BADINTERNALERROR; } /* NamespaceArray */ UA_DataSource namespaceDataSource = {readNamespaces, writeNamespaces}; retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY), namespaceDataSource); retVal |= UA_Server_writeValueRank(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY), 1); /* ServerArray */ retVal |= writeNs0VariableArray(server, UA_NS0ID_SERVER_SERVERARRAY, &server->config.applicationDescription.applicationUri, 1, &UA_TYPES[UA_TYPES_STRING]); retVal |= UA_Server_writeValueRank(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERARRAY), 1); /* ServerStatus */ UA_DataSource serverStatus = {readStatus, NULL}; retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), serverStatus); /* StartTime will be sampled in UA_Server_run_startup()*/ /* CurrentTime */ UA_DataSource currentTime = {readCurrentTime, NULL}; retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME), currentTime); /* State */ retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STATE), serverStatus); /* BuildInfo */ retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO), serverStatus); /* BuildInfo - ProductUri */ retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTURI), serverStatus); /* BuildInfo - ManufacturerName */ retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_MANUFACTURERNAME), serverStatus); /* BuildInfo - ProductName */ retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTNAME), serverStatus); /* BuildInfo - SoftwareVersion */ retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_SOFTWAREVERSION), serverStatus); /* BuildInfo - BuildNumber */ retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER), serverStatus); /* BuildInfo - BuildDate */ retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDDATE), serverStatus); #ifdef UA_GENERATED_NAMESPACE_ZERO /* SecondsTillShutdown */ retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SECONDSTILLSHUTDOWN), serverStatus); /* ShutDownReason */ UA_LocalizedText shutdownReason; UA_LocalizedText_init(&shutdownReason); retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERSTATUS_SHUTDOWNREASON, &shutdownReason, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]); /* ServiceLevel */ UA_DataSource serviceLevel = {readServiceLevel, NULL}; retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVICELEVEL), serviceLevel); /* ServerDiagnostics - ServerDiagnosticsSummary */ UA_ServerDiagnosticsSummaryDataType serverDiagnosticsSummary; UA_ServerDiagnosticsSummaryDataType_init(&serverDiagnosticsSummary); retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY, &serverDiagnosticsSummary, &UA_TYPES[UA_TYPES_SERVERDIAGNOSTICSSUMMARYDATATYPE]); /* ServerDiagnostics - EnabledFlag */ UA_Boolean enabledFlag = false; retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_ENABLEDFLAG, &enabledFlag, &UA_TYPES[UA_TYPES_BOOLEAN]); /* According to Specification part-5 - pg.no-11(PDF pg.no-29), when the ServerDiagnostics is disabled the client * may modify the value of enabledFlag=true in the server. By default, this node have CurrentRead/Write access. * In CTT, Subscription_Minimum_1/002.js test will modify the above flag. This will not be a problem when build * configuration is set at UA_NAMESPACE_ZERO="REDUCED" as NodeIds will not be present. When UA_NAMESPACE_ZERO="FULL", * the test will fail. Hence made the NodeId as read only */ retVal |= UA_Server_writeAccessLevel(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_ENABLEDFLAG), UA_ACCESSLEVELMASK_READ); /* Auditing */ UA_DataSource auditing = {readAuditing, NULL}; retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_AUDITING), auditing); /* Redundancy Support */ UA_RedundancySupport redundancySupport = UA_REDUNDANCYSUPPORT_NONE; retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERREDUNDANCY_REDUNDANCYSUPPORT, &redundancySupport, &UA_TYPES[UA_TYPES_REDUNDANCYSUPPORT]); /* Remove unused subtypes of ServerRedundancy */ UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERREDUNDANCY_CURRENTSERVERID), true); UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERREDUNDANCY_REDUNDANTSERVERARRAY), true); UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERREDUNDANCY_SERVERURIARRAY), true); UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERREDUNDANCY_SERVERNETWORKGROUPS), true); /* ServerCapabilities - LocaleIdArray */ UA_LocaleId locale_en = UA_STRING("en"); retVal |= writeNs0VariableArray(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_LOCALEIDARRAY, &locale_en, 1, &UA_TYPES[UA_TYPES_LOCALEID]); /* ServerCapabilities - MaxBrowseContinuationPoints */ UA_UInt16 maxBrowseContinuationPoints = UA_MAXCONTINUATIONPOINTS; retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXBROWSECONTINUATIONPOINTS, &maxBrowseContinuationPoints, &UA_TYPES[UA_TYPES_UINT16]); /* ServerProfileArray */ UA_String profileArray[3]; UA_UInt16 profileArraySize = 0; #define ADDPROFILEARRAY(x) profileArray[profileArraySize++] = UA_STRING(x) ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/MicroEmbeddedDevice"); #ifdef UA_ENABLE_NODEMANAGEMENT ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/NodeManagement"); #endif #ifdef UA_ENABLE_METHODCALLS ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/Methods"); #endif retVal |= writeNs0VariableArray(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_SERVERPROFILEARRAY, profileArray, profileArraySize, &UA_TYPES[UA_TYPES_STRING]); /* ServerCapabilities - MaxQueryContinuationPoints */ UA_UInt16 maxQueryContinuationPoints = 0; retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXQUERYCONTINUATIONPOINTS, &maxQueryContinuationPoints, &UA_TYPES[UA_TYPES_UINT16]); /* ServerCapabilities - MaxHistoryContinuationPoints */ UA_UInt16 maxHistoryContinuationPoints = 0; retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXHISTORYCONTINUATIONPOINTS, &maxHistoryContinuationPoints, &UA_TYPES[UA_TYPES_UINT16]); /* ServerCapabilities - MinSupportedSampleRate */ UA_DataSource samplingInterval = {readMinSamplingInterval, NULL}; retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MINSUPPORTEDSAMPLERATE), samplingInterval); /* ServerCapabilities - OperationLimits - MaxNodesPerRead */ retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERREAD, &server->config.maxNodesPerRead, &UA_TYPES[UA_TYPES_UINT32]); /* ServerCapabilities - OperationLimits - maxNodesPerWrite */ retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERWRITE, &server->config.maxNodesPerWrite, &UA_TYPES[UA_TYPES_UINT32]); /* ServerCapabilities - OperationLimits - MaxNodesPerMethodCall */ retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERMETHODCALL, &server->config.maxNodesPerMethodCall, &UA_TYPES[UA_TYPES_UINT32]); /* ServerCapabilities - OperationLimits - MaxNodesPerBrowse */ retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERBROWSE, &server->config.maxNodesPerBrowse, &UA_TYPES[UA_TYPES_UINT32]); /* ServerCapabilities - OperationLimits - MaxNodesPerRegisterNodes */ retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERREGISTERNODES, &server->config.maxNodesPerRegisterNodes, &UA_TYPES[UA_TYPES_UINT32]); /* ServerCapabilities - OperationLimits - MaxNodesPerTranslateBrowsePathsToNodeIds */ retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERTRANSLATEBROWSEPATHSTONODEIDS, &server->config.maxNodesPerTranslateBrowsePathsToNodeIds, &UA_TYPES[UA_TYPES_UINT32]); /* ServerCapabilities - OperationLimits - MaxNodesPerNodeManagement */ retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERNODEMANAGEMENT, &server->config.maxNodesPerNodeManagement, &UA_TYPES[UA_TYPES_UINT32]); /* ServerCapabilities - OperationLimits - MaxMonitoredItemsPerCall */ retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXMONITOREDITEMSPERCALL, &server->config.maxMonitoredItemsPerCall, &UA_TYPES[UA_TYPES_UINT32]); #ifdef UA_ENABLE_MICRO_EMB_DEV_PROFILE /* Remove unused operation limit components */ UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERHISTORYREADDATA), true); UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERHISTORYREADEVENTS), true); UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERHISTORYUPDATEDATA), true); UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERHISTORYUPDATEEVENTS), true); UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_ROLESET), true); UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXSTRINGLENGTH), true); UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXARRAYLENGTH), true); UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXBYTESTRINGLENGTH), true); /* Remove not supported Server Instance */ UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_DICTIONARIES), true); UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_ESTIMATEDRETURNTIME), true); UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_LOCALTIME), true); UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE), true); UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACES), true); UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_REQUESTSERVERSTATECHANGE), true); UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_RESENDDATA), true); UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION), true); UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SETSUBSCRIPTIONDURABLE), true); UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SAMPLINGINTERVALDIAGNOSTICSARRAY), true); UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SESSIONSDIAGNOSTICSSUMMARY), true); /* Removing these NodeIds make Server Object to be non-complaint with UA 1.03 in CTT (Base Inforamtion/Base Info Core Structure/ 001.js) * In the 1.04 specification this has been resolved by allowing to remove these static nodes as well */ UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY), true); UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SUBSCRIPTIONDIAGNOSTICSARRAY), true); #endif #ifndef UA_ENABLE_HISTORIZING UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_HISTORYSERVERCAPABILITIES), true); #else /* ServerCapabilities - HistoryServerCapabilities - AccessHistoryDataCapability */ retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_ACCESSHISTORYDATACAPABILITY, &server->config.accessHistoryDataCapability, &UA_TYPES[UA_TYPES_BOOLEAN]); /* ServerCapabilities - HistoryServerCapabilities - MaxReturnDataValues */ retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_MAXRETURNDATAVALUES, &server->config.maxReturnDataValues, &UA_TYPES[UA_TYPES_UINT32]); /* ServerCapabilities - HistoryServerCapabilities - AccessHistoryEventsCapability */ retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_ACCESSHISTORYEVENTSCAPABILITY, &server->config.accessHistoryEventsCapability, &UA_TYPES[UA_TYPES_BOOLEAN]); /* ServerCapabilities - HistoryServerCapabilities - MaxReturnEventValues */ retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_MAXRETURNEVENTVALUES, &server->config.maxReturnEventValues, &UA_TYPES[UA_TYPES_UINT32]); /* ServerCapabilities - HistoryServerCapabilities - InsertDataCapability */ retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_INSERTDATACAPABILITY, &server->config.insertDataCapability, &UA_TYPES[UA_TYPES_BOOLEAN]); /* ServerCapabilities - HistoryServerCapabilities - InsertEventCapability */ retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_INSERTEVENTCAPABILITY, &server->config.insertEventCapability, &UA_TYPES[UA_TYPES_BOOLEAN]); /* ServerCapabilities - HistoryServerCapabilities - InsertAnnotationsCapability */ retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_INSERTANNOTATIONCAPABILITY, &server->config.insertAnnotationsCapability, &UA_TYPES[UA_TYPES_BOOLEAN]); /* ServerCapabilities - HistoryServerCapabilities - ReplaceDataCapability */ retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_REPLACEDATACAPABILITY, &server->config.replaceDataCapability, &UA_TYPES[UA_TYPES_BOOLEAN]); /* ServerCapabilities - HistoryServerCapabilities - ReplaceEventCapability */ retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_REPLACEEVENTCAPABILITY, &server->config.replaceEventCapability, &UA_TYPES[UA_TYPES_BOOLEAN]); /* ServerCapabilities - HistoryServerCapabilities - UpdateDataCapability */ retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_UPDATEDATACAPABILITY, &server->config.updateDataCapability, &UA_TYPES[UA_TYPES_BOOLEAN]); /* ServerCapabilities - HistoryServerCapabilities - UpdateEventCapability */ retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_UPDATEEVENTCAPABILITY, &server->config.updateEventCapability, &UA_TYPES[UA_TYPES_BOOLEAN]); /* ServerCapabilities - HistoryServerCapabilities - DeleteRawCapability */ retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_DELETERAWCAPABILITY, &server->config.deleteRawCapability, &UA_TYPES[UA_TYPES_BOOLEAN]); /* ServerCapabilities - HistoryServerCapabilities - DeleteEventCapability */ retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_DELETEEVENTCAPABILITY, &server->config.deleteEventCapability, &UA_TYPES[UA_TYPES_BOOLEAN]); /* ServerCapabilities - HistoryServerCapabilities - DeleteAtTimeDataCapability */ retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_DELETEATTIMECAPABILITY, &server->config.deleteAtTimeDataCapability, &UA_TYPES[UA_TYPES_BOOLEAN]); #endif #if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS) retVal |= UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS), readMonitoredItems); #endif /* The HasComponent references to the ModellingRules are not part of the * Nodeset2.xml. So we add the references manually. */ addModellingRules(server); #endif /* UA_GENERATED_NAMESPACE_ZERO */ /* create the OverFlowEventType * The EventQueueOverflowEventType is defined as abstract, therefore we can not create an instance of that type * directly, but need to create a subtype. This is already posted on the OPC Foundation bug tracker under the * following link for clarification: https://opcfoundation-onlineapplications.org/mantis/view.php?id=4206 */ #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS UA_ObjectTypeAttributes overflowAttr = UA_ObjectTypeAttributes_default; overflowAttr.description = UA_LOCALIZEDTEXT("en-US", "A simple event for indicating a queue overflow."); overflowAttr.displayName = UA_LOCALIZEDTEXT("en-US", "SimpleOverflowEventType"); retVal |= UA_Server_addObjectTypeNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SIMPLEOVERFLOWEVENTTYPE), UA_NODEID_NUMERIC(0, UA_NS0ID_EVENTQUEUEOVERFLOWEVENTTYPE), UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE), UA_QUALIFIEDNAME(0, "SimpleOverflowEventType"), overflowAttr, NULL, NULL); #endif if(retVal != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Initialization of Namespace 0 (after bootstrapping) " "failed with %s. See previous outputs for any error messages.", UA_StatusCode_name(retVal)); return UA_STATUSCODE_BADINTERNALERROR; } return UA_STATUSCODE_GOOD; } /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_server_config.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2019 (c) Fraunhofer IOSB (Author: Julius Pfrommer) */ void UA_ServerConfig_clean(UA_ServerConfig *config) { if(!config) return; /* Server Description */ UA_BuildInfo_deleteMembers(&config->buildInfo); UA_ApplicationDescription_deleteMembers(&config->applicationDescription); #ifdef UA_ENABLE_DISCOVERY_MULTICAST UA_MdnsDiscoveryConfiguration_clear(&config->discovery.mdns); UA_String_clear(&config->discovery.mdnsInterfaceIP); #endif /* Custom DataTypes */ /* nothing to do */ /* Networking */ for(size_t i = 0; i < config->networkLayersSize; ++i) config->networkLayers[i].deleteMembers(&config->networkLayers[i]); UA_free(config->networkLayers); config->networkLayers = NULL; config->networkLayersSize = 0; UA_String_deleteMembers(&config->customHostname); config->customHostname = UA_STRING_NULL; for(size_t i = 0; i < config->securityPoliciesSize; ++i) { UA_SecurityPolicy *policy = &config->securityPolicies[i]; policy->deleteMembers(policy); } UA_free(config->securityPolicies); config->securityPolicies = NULL; config->securityPoliciesSize = 0; for(size_t i = 0; i < config->endpointsSize; ++i) UA_EndpointDescription_deleteMembers(&config->endpoints[i]); UA_free(config->endpoints); config->endpoints = NULL; config->endpointsSize = 0; /* Certificate Validation */ if(config->certificateVerification.deleteMembers) config->certificateVerification.deleteMembers(&config->certificateVerification); /* Access Control */ if(config->accessControl.deleteMembers) config->accessControl.deleteMembers(&config->accessControl); /* Historical data */ #ifdef UA_ENABLE_HISTORIZING if(config->historyDatabase.deleteMembers) config->historyDatabase.deleteMembers(&config->historyDatabase); #endif /* Logger */ if(config->logger.clear) config->logger.clear(config->logger.context); } void UA_ServerConfig_setCustomHostname(UA_ServerConfig *config, const UA_String customHostname) { if(!config) return; UA_String_deleteMembers(&config->customHostname); UA_String_copy(&customHostname, &config->customHostname); } #ifdef UA_ENABLE_PUBSUB /* Add a pubsubTransportLayer to the configuration. Memory is reallocated on * demand. */ UA_StatusCode UA_ServerConfig_addPubSubTransportLayer(UA_ServerConfig *config, UA_PubSubTransportLayer *pubsubTransportLayer) { if(config->pubsubTransportLayersSize == 0) { config->pubsubTransportLayers = (UA_PubSubTransportLayer *) UA_malloc(sizeof(UA_PubSubTransportLayer)); } else { config->pubsubTransportLayers = (UA_PubSubTransportLayer*) UA_realloc(config->pubsubTransportLayers, sizeof(UA_PubSubTransportLayer) * (config->pubsubTransportLayersSize + 1)); } if(config->pubsubTransportLayers == NULL) return UA_STATUSCODE_BADOUTOFMEMORY; memcpy(&config->pubsubTransportLayers[config->pubsubTransportLayersSize], pubsubTransportLayer, sizeof(UA_PubSubTransportLayer)); config->pubsubTransportLayersSize++; return UA_STATUSCODE_GOOD; } #endif /* UA_ENABLE_PUBSUB */ /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_server_binary.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2014-2016 (c) Sten Grüner * Copyright 2014-2015, 2017 (c) Florian Palm * Copyright 2015-2016 (c) Chris Iatrou * Copyright 2015-2016 (c) Oleksiy Vasylyev * Copyright 2016 (c) Joakim L. Gilje * Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2016 (c) TorbenD * Copyright 2017 (c) frax2222 * Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB * Copyright 2019 (c) Kalycito Infotech Private Limited */ #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION // store the authentication token and session ID so we can help fuzzing by setting // these values in the next request automatically UA_NodeId unsafe_fuzz_authenticationToken = {0, UA_NODEIDTYPE_NUMERIC, {0}}; #endif #ifdef UA_DEBUG_DUMP_PKGS_FILE void UA_debug_dumpCompleteChunk(UA_Server *const server, UA_Connection *const connection, UA_ByteString *messageBuffer); #endif /********************/ /* Helper Functions */ /********************/ static UA_StatusCode sendServiceFaultWithRequest(UA_SecureChannel *channel, const UA_RequestHeader *requestHeader, const UA_DataType *responseType, UA_UInt32 requestId, UA_StatusCode error) { UA_STACKARRAY(UA_Byte, response, responseType->memSize); UA_init(response, responseType); UA_ResponseHeader *responseHeader = (UA_ResponseHeader*)response; responseHeader->requestHandle = requestHeader->requestHandle; responseHeader->timestamp = UA_DateTime_now(); responseHeader->serviceResult = error; /* Send error message. Message type is MSG and not ERR, since we are on a * SecureChannel! */ UA_StatusCode retval = UA_SecureChannel_sendSymmetricMessage(channel, requestId, UA_MESSAGETYPE_MSG, response, responseType); UA_LOG_DEBUG(channel->securityPolicy->logger, UA_LOGCATEGORY_SERVER, "Sent ServiceFault with error code %s", UA_StatusCode_name(error)); return retval; } /* This is not an ERR message, the connection is not closed afterwards */ static UA_StatusCode sendServiceFault(UA_SecureChannel *channel, const UA_ByteString *msg, size_t offset, const UA_DataType *responseType, UA_UInt32 requestId, UA_StatusCode error) { UA_RequestHeader requestHeader; UA_StatusCode retval = UA_RequestHeader_decodeBinary(msg, &offset, &requestHeader); if(retval != UA_STATUSCODE_GOOD) return retval; retval = sendServiceFaultWithRequest(channel, &requestHeader, responseType, requestId, error); UA_RequestHeader_deleteMembers(&requestHeader); return retval; } static void getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType, const UA_DataType **responseType, UA_Service *service, UA_Boolean *requiresSession) { switch(requestTypeId) { case UA_NS0ID_GETENDPOINTSREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_GetEndpoints; *requestType = &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST]; *responseType = &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE]; *requiresSession = false; break; case UA_NS0ID_FINDSERVERSREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_FindServers; *requestType = &UA_TYPES[UA_TYPES_FINDSERVERSREQUEST]; *responseType = &UA_TYPES[UA_TYPES_FINDSERVERSRESPONSE]; *requiresSession = false; break; #ifdef UA_ENABLE_DISCOVERY # ifdef UA_ENABLE_DISCOVERY_MULTICAST case UA_NS0ID_FINDSERVERSONNETWORKREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_FindServersOnNetwork; *requestType = &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKREQUEST]; *responseType = &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKRESPONSE]; *requiresSession = false; break; # endif case UA_NS0ID_REGISTERSERVERREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_RegisterServer; *requestType = &UA_TYPES[UA_TYPES_REGISTERSERVERREQUEST]; *responseType = &UA_TYPES[UA_TYPES_REGISTERSERVERRESPONSE]; *requiresSession = false; break; case UA_NS0ID_REGISTERSERVER2REQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_RegisterServer2; *requestType = &UA_TYPES[UA_TYPES_REGISTERSERVER2REQUEST]; *responseType = &UA_TYPES[UA_TYPES_REGISTERSERVER2RESPONSE]; *requiresSession = false; break; #endif case UA_NS0ID_CREATESESSIONREQUEST_ENCODING_DEFAULTBINARY: *service = NULL; //(UA_Service)Service_CreateSession; *requestType = &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST]; *responseType = &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE]; *requiresSession = false; break; case UA_NS0ID_ACTIVATESESSIONREQUEST_ENCODING_DEFAULTBINARY: *service = NULL; //(UA_Service)Service_ActivateSession; *requestType = &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST]; *responseType = &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE]; break; case UA_NS0ID_CLOSESESSIONREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_CloseSession; *requestType = &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST]; *responseType = &UA_TYPES[UA_TYPES_CLOSESESSIONRESPONSE]; break; case UA_NS0ID_READREQUEST_ENCODING_DEFAULTBINARY: *service = NULL; *service = (UA_Service)Service_Read; *requestType = &UA_TYPES[UA_TYPES_READREQUEST]; *responseType = &UA_TYPES[UA_TYPES_READRESPONSE]; break; case UA_NS0ID_WRITEREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_Write; *requestType = &UA_TYPES[UA_TYPES_WRITEREQUEST]; *responseType = &UA_TYPES[UA_TYPES_WRITERESPONSE]; break; case UA_NS0ID_BROWSEREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_Browse; *requestType = &UA_TYPES[UA_TYPES_BROWSEREQUEST]; *responseType = &UA_TYPES[UA_TYPES_BROWSERESPONSE]; break; case UA_NS0ID_BROWSENEXTREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_BrowseNext; *requestType = &UA_TYPES[UA_TYPES_BROWSENEXTREQUEST]; *responseType = &UA_TYPES[UA_TYPES_BROWSENEXTRESPONSE]; break; case UA_NS0ID_REGISTERNODESREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_RegisterNodes; *requestType = &UA_TYPES[UA_TYPES_REGISTERNODESREQUEST]; *responseType = &UA_TYPES[UA_TYPES_REGISTERNODESRESPONSE]; break; case UA_NS0ID_UNREGISTERNODESREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_UnregisterNodes; *requestType = &UA_TYPES[UA_TYPES_UNREGISTERNODESREQUEST]; *responseType = &UA_TYPES[UA_TYPES_UNREGISTERNODESRESPONSE]; break; case UA_NS0ID_TRANSLATEBROWSEPATHSTONODEIDSREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_TranslateBrowsePathsToNodeIds; *requestType = &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSREQUEST]; *responseType = &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE]; break; #ifdef UA_ENABLE_SUBSCRIPTIONS case UA_NS0ID_CREATESUBSCRIPTIONREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_CreateSubscription; *requestType = &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONREQUEST]; *responseType = &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONRESPONSE]; break; case UA_NS0ID_PUBLISHREQUEST_ENCODING_DEFAULTBINARY: *requestType = &UA_TYPES[UA_TYPES_PUBLISHREQUEST]; *responseType = &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]; break; case UA_NS0ID_REPUBLISHREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_Republish; *requestType = &UA_TYPES[UA_TYPES_REPUBLISHREQUEST]; *responseType = &UA_TYPES[UA_TYPES_REPUBLISHRESPONSE]; break; case UA_NS0ID_MODIFYSUBSCRIPTIONREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_ModifySubscription; *requestType = &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONREQUEST]; *responseType = &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONRESPONSE]; break; case UA_NS0ID_SETPUBLISHINGMODEREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_SetPublishingMode; *requestType = &UA_TYPES[UA_TYPES_SETPUBLISHINGMODEREQUEST]; *responseType = &UA_TYPES[UA_TYPES_SETPUBLISHINGMODERESPONSE]; break; case UA_NS0ID_DELETESUBSCRIPTIONSREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_DeleteSubscriptions; *requestType = &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSREQUEST]; *responseType = &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSRESPONSE]; break; case UA_NS0ID_CREATEMONITOREDITEMSREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_CreateMonitoredItems; *requestType = &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSREQUEST]; *responseType = &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSRESPONSE]; break; case UA_NS0ID_DELETEMONITOREDITEMSREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_DeleteMonitoredItems; *requestType = &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSREQUEST]; *responseType = &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSRESPONSE]; break; case UA_NS0ID_MODIFYMONITOREDITEMSREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_ModifyMonitoredItems; *requestType = &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSREQUEST]; *responseType = &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSRESPONSE]; break; case UA_NS0ID_SETMONITORINGMODEREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_SetMonitoringMode; *requestType = &UA_TYPES[UA_TYPES_SETMONITORINGMODEREQUEST]; *responseType = &UA_TYPES[UA_TYPES_SETMONITORINGMODERESPONSE]; break; #endif #ifdef UA_ENABLE_HISTORIZING /* For History read */ case UA_NS0ID_HISTORYREADREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_HistoryRead; *requestType = &UA_TYPES[UA_TYPES_HISTORYREADREQUEST]; *responseType = &UA_TYPES[UA_TYPES_HISTORYREADRESPONSE]; break; /* For History update */ case UA_NS0ID_HISTORYUPDATEREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_HistoryUpdate; *requestType = &UA_TYPES[UA_TYPES_HISTORYUPDATEREQUEST]; *responseType = &UA_TYPES[UA_TYPES_HISTORYUPDATERESPONSE]; break; #endif #ifdef UA_ENABLE_METHODCALLS case UA_NS0ID_CALLREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_Call; *requestType = &UA_TYPES[UA_TYPES_CALLREQUEST]; *responseType = &UA_TYPES[UA_TYPES_CALLRESPONSE]; break; #endif #ifdef UA_ENABLE_NODEMANAGEMENT case UA_NS0ID_ADDNODESREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_AddNodes; *requestType = &UA_TYPES[UA_TYPES_ADDNODESREQUEST]; *responseType = &UA_TYPES[UA_TYPES_ADDNODESRESPONSE]; break; case UA_NS0ID_ADDREFERENCESREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_AddReferences; *requestType = &UA_TYPES[UA_TYPES_ADDREFERENCESREQUEST]; *responseType = &UA_TYPES[UA_TYPES_ADDREFERENCESRESPONSE]; break; case UA_NS0ID_DELETENODESREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_DeleteNodes; *requestType = &UA_TYPES[UA_TYPES_DELETENODESREQUEST]; *responseType = &UA_TYPES[UA_TYPES_DELETENODESRESPONSE]; break; case UA_NS0ID_DELETEREFERENCESREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_DeleteReferences; *requestType = &UA_TYPES[UA_TYPES_DELETEREFERENCESREQUEST]; *responseType = &UA_TYPES[UA_TYPES_DELETEREFERENCESRESPONSE]; break; #endif default: break; } } /*************************/ /* Process Message Types */ /*************************/ /* HEL -> Open up the connection */ static UA_StatusCode processHEL(UA_Server *server, UA_Connection *connection, const UA_ByteString *msg, size_t *offset) { UA_TcpHelloMessage helloMessage; UA_StatusCode retval = UA_TcpHelloMessage_decodeBinary(msg, offset, &helloMessage); if(retval != UA_STATUSCODE_GOOD) return retval; /* Currently not checked */ UA_String_deleteMembers(&helloMessage.endpointUrl); /* TODO: Use the config of the exact NetworkLayer */ if(server->config.networkLayersSize == 0) return UA_STATUSCODE_BADOUTOFMEMORY; const UA_ConnectionConfig *localConfig = &server->config.networkLayers[0].localConnectionConfig; /* Parameterize the connection */ UA_ConnectionConfig remoteConfig; remoteConfig.protocolVersion = helloMessage.protocolVersion; remoteConfig.sendBufferSize = helloMessage.sendBufferSize; remoteConfig.recvBufferSize = helloMessage.receiveBufferSize; remoteConfig.maxMessageSize = helloMessage.maxMessageSize; remoteConfig.maxChunkCount = helloMessage.maxChunkCount; retval = UA_Connection_processHELACK(connection, localConfig, &remoteConfig); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_NETWORK, "Connection %i | Error during the HEL/ACK handshake", (int)(connection->sockfd)); return retval; } /* Build acknowledge response */ UA_TcpAcknowledgeMessage ackMessage; memcpy(&ackMessage, localConfig, sizeof(UA_TcpAcknowledgeMessage)); /* Same struct layout.. */ UA_TcpMessageHeader ackHeader; ackHeader.messageTypeAndChunkType = UA_MESSAGETYPE_ACK + UA_CHUNKTYPE_FINAL; ackHeader.messageSize = 8 + 20; /* ackHeader + ackMessage */ /* Get the send buffer from the network layer */ UA_ByteString ack_msg; UA_ByteString_init(&ack_msg); retval = connection->getSendBuffer(connection, connection->config.sendBufferSize, &ack_msg); if(retval != UA_STATUSCODE_GOOD) return retval; /* Encode and send the response */ UA_Byte *bufPos = ack_msg.data; const UA_Byte *bufEnd = &ack_msg.data[ack_msg.length]; retval = UA_TcpMessageHeader_encodeBinary(&ackHeader, &bufPos, bufEnd); if(retval != UA_STATUSCODE_GOOD) { connection->releaseSendBuffer(connection, &ack_msg); return retval; } retval = UA_TcpAcknowledgeMessage_encodeBinary(&ackMessage, &bufPos, bufEnd); if(retval != UA_STATUSCODE_GOOD) { connection->releaseSendBuffer(connection, &ack_msg); return retval; } ack_msg.length = ackHeader.messageSize; return connection->send(connection, &ack_msg); } /* OPN -> Open up/renew the securechannel */ static UA_StatusCode processOPN(UA_Server *server, UA_SecureChannel *channel, const UA_UInt32 requestId, const UA_ByteString *msg) { /* Decode the request */ size_t offset = 0; UA_NodeId requestType; UA_OpenSecureChannelRequest openSecureChannelRequest; UA_StatusCode retval = UA_NodeId_decodeBinary(msg, &offset, &requestType); if(retval != UA_STATUSCODE_GOOD) { UA_NodeId_deleteMembers(&requestType); UA_LOG_INFO_CHANNEL(&server->config.logger, channel, "Could not decode the NodeId. Closing the connection"); UA_SecureChannelManager_close(&server->secureChannelManager, channel->securityToken.channelId); return retval; } retval = UA_OpenSecureChannelRequest_decodeBinary(msg, &offset, &openSecureChannelRequest); /* Error occurred */ if(retval != UA_STATUSCODE_GOOD || requestType.identifier.numeric != UA_TYPES[UA_TYPES_OPENSECURECHANNELREQUEST].binaryEncodingId) { UA_NodeId_deleteMembers(&requestType); UA_OpenSecureChannelRequest_deleteMembers(&openSecureChannelRequest); UA_LOG_INFO_CHANNEL(&server->config.logger, channel, "Could not decode the OPN message. Closing the connection."); UA_SecureChannelManager_close(&server->secureChannelManager, channel->securityToken.channelId); return retval; } UA_NodeId_deleteMembers(&requestType); /* Call the service */ UA_OpenSecureChannelResponse openScResponse; UA_OpenSecureChannelResponse_init(&openScResponse); Service_OpenSecureChannel(server, channel, &openSecureChannelRequest, &openScResponse); UA_OpenSecureChannelRequest_deleteMembers(&openSecureChannelRequest); if(openScResponse.responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_LOG_INFO_CHANNEL(&server->config.logger, channel, "Could not open a SecureChannel. " "Closing the connection."); UA_SecureChannelManager_close(&server->secureChannelManager, channel->securityToken.channelId); return openScResponse.responseHeader.serviceResult; } /* Send the response */ retval = UA_SecureChannel_sendAsymmetricOPNMessage(channel, requestId, &openScResponse, &UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE]); UA_OpenSecureChannelResponse_deleteMembers(&openScResponse); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_INFO_CHANNEL(&server->config.logger, channel, "Could not send the OPN answer with error code %s", UA_StatusCode_name(retval)); UA_SecureChannelManager_close(&server->secureChannelManager, channel->securityToken.channelId); return retval; } return retval; } static UA_StatusCode sendResponse(UA_SecureChannel *channel, UA_UInt32 requestId, UA_UInt32 requestHandle, UA_ResponseHeader *responseHeader, const UA_DataType *responseType) { /* Prepare the ResponseHeader */ responseHeader->requestHandle = requestHandle; responseHeader->timestamp = UA_DateTime_now(); /* Start the message context */ UA_MessageContext mc; UA_StatusCode retval = UA_MessageContext_begin(&mc, channel, requestId, UA_MESSAGETYPE_MSG); if(retval != UA_STATUSCODE_GOOD) return retval; /* Assert's required for clang-analyzer */ UA_assert(mc.buf_pos == &mc.messageBuffer.data[UA_SECURE_MESSAGE_HEADER_LENGTH]); UA_assert(mc.buf_end <= &mc.messageBuffer.data[mc.messageBuffer.length]); /* Encode the response type */ UA_NodeId typeId = UA_NODEID_NUMERIC(0, responseType->binaryEncodingId); retval = UA_MessageContext_encode(&mc, &typeId, &UA_TYPES[UA_TYPES_NODEID]); if(retval != UA_STATUSCODE_GOOD) return retval; /* Encode the response */ retval = UA_MessageContext_encode(&mc, responseHeader, responseType); if(retval != UA_STATUSCODE_GOOD) return retval; /* Finish / send out */ return UA_MessageContext_finish(&mc); } static UA_StatusCode processMSGDecoded(UA_Server *server, UA_SecureChannel *channel, UA_UInt32 requestId, UA_Service service, const UA_RequestHeader *requestHeader, const UA_DataType *requestType, UA_ResponseHeader *responseHeader, const UA_DataType *responseType, UA_Boolean sessionRequired) { /* CreateSession doesn't need a session */ if(requestType == &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST]) { Service_CreateSession(server, channel, (const UA_CreateSessionRequest *)requestHeader, (UA_CreateSessionResponse *)responseHeader); #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION /* Store the authentication token and session ID so we can help fuzzing * by setting these values in the next request automatically */ UA_CreateSessionResponse *res = (UA_CreateSessionResponse *)responseHeader; UA_NodeId_copy(&res->authenticationToken, &unsafe_fuzz_authenticationToken); #endif return sendResponse(channel, requestId, requestHeader->requestHandle, responseHeader, responseType); } /* Find the matching session */ UA_Session *session = (UA_Session*) UA_SecureChannel_getSession(channel, &requestHeader->authenticationToken); if(!session && !UA_NodeId_isNull(&requestHeader->authenticationToken)) session = UA_SessionManager_getSessionByToken(&server->sessionManager, &requestHeader->authenticationToken); if(requestType == &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST]) { if(!session) { UA_LOG_DEBUG_CHANNEL(&server->config.logger, channel, "Trying to activate a session that is " \ "not known in the server"); return sendServiceFaultWithRequest(channel, requestHeader, responseType, requestId, UA_STATUSCODE_BADSESSIONIDINVALID); } Service_ActivateSession(server, channel, session, (const UA_ActivateSessionRequest*)requestHeader, (UA_ActivateSessionResponse*)responseHeader); return sendResponse(channel, requestId, requestHeader->requestHandle, responseHeader, responseType); } /* Set an anonymous, inactive session for services that need no session */ UA_Session anonymousSession; if(!session) { if(sessionRequired) { #ifdef UA_ENABLE_TYPENAMES UA_LOG_WARNING_CHANNEL(&server->config.logger, channel, "%s refused without a valid session", requestType->typeName); #else UA_LOG_WARNING_CHANNEL(&server->config.logger, channel, "Service %i refused without a valid session", requestType->binaryEncodingId); #endif return sendServiceFaultWithRequest(channel, requestHeader, responseType, requestId, UA_STATUSCODE_BADSESSIONIDINVALID); } UA_Session_init(&anonymousSession); anonymousSession.sessionId = UA_NODEID_GUID(0, UA_GUID_NULL); anonymousSession.header.channel = channel; session = &anonymousSession; } /* Trying to use a non-activated session? Do not allow if request is of type * CloseSessionRequest */ if(sessionRequired && !session->activated && requestType != &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST]) { #ifdef UA_ENABLE_TYPENAMES UA_LOG_WARNING_SESSION(&server->config.logger, session, "%s refused on a non-activated session", requestType->typeName); #else UA_LOG_WARNING_SESSION(&server->config.logger, session, "Service %i refused on a non-activated session", requestType->binaryEncodingId); #endif UA_SessionManager_removeSession(&server->sessionManager, &session->header.authenticationToken); return sendServiceFaultWithRequest(channel, requestHeader, responseType, requestId, UA_STATUSCODE_BADSESSIONNOTACTIVATED); } /* The session is bound to another channel */ if(session != &anonymousSession && session->header.channel != channel) { UA_LOG_WARNING_CHANNEL(&server->config.logger, channel, "Client tries to use a Session that is not " "bound to this SecureChannel"); return sendServiceFaultWithRequest(channel, requestHeader, responseType, requestId, UA_STATUSCODE_BADSECURECHANNELIDINVALID); } /* Update the session lifetime */ UA_Session_updateLifetime(session); #ifdef UA_ENABLE_SUBSCRIPTIONS /* The publish request is not answered immediately */ if(requestType == &UA_TYPES[UA_TYPES_PUBLISHREQUEST]) { Service_Publish(server, session, (const UA_PublishRequest*)requestHeader, requestId); return UA_STATUSCODE_GOOD; } #endif /* Dispatch the synchronous service call and send the response */ service(server, session, requestHeader, responseHeader); return sendResponse(channel, requestId, requestHeader->requestHandle, responseHeader, responseType); } static UA_StatusCode processMSG(UA_Server *server, UA_SecureChannel *channel, UA_UInt32 requestId, const UA_ByteString *msg) { /* Decode the nodeid */ size_t offset = 0; UA_NodeId requestTypeId; UA_StatusCode retval = UA_NodeId_decodeBinary(msg, &offset, &requestTypeId); if(retval != UA_STATUSCODE_GOOD) return retval; if(requestTypeId.namespaceIndex != 0 || requestTypeId.identifierType != UA_NODEIDTYPE_NUMERIC) UA_NodeId_deleteMembers(&requestTypeId); /* leads to badserviceunsupported */ size_t requestPos = offset; /* Store the offset (for sendServiceFault) */ /* Get the service pointers */ UA_Service service = NULL; UA_Boolean sessionRequired = true; const UA_DataType *requestType = NULL; const UA_DataType *responseType = NULL; getServicePointers(requestTypeId.identifier.numeric, &requestType, &responseType, &service, &sessionRequired); if(!requestType) { if(requestTypeId.identifier.numeric == 787) { UA_LOG_INFO_CHANNEL(&server->config.logger, channel, "Client requested a subscription, " \ "but those are not enabled in the build"); } else { UA_LOG_INFO_CHANNEL(&server->config.logger, channel, "Unknown request with type identifier %i", requestTypeId.identifier.numeric); } return sendServiceFault(channel, msg, requestPos, &UA_TYPES[UA_TYPES_SERVICEFAULT], requestId, UA_STATUSCODE_BADSERVICEUNSUPPORTED); } UA_assert(responseType); /* Decode the request */ UA_STACKARRAY(UA_Byte, request, requestType->memSize); retval = UA_decodeBinary(msg, &offset, request, requestType, server->config.customDataTypes); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_DEBUG_CHANNEL(&server->config.logger, channel, "Could not decode the request"); return sendServiceFault(channel, msg, requestPos, responseType, requestId, retval); } /* Check timestamp in the request header */ UA_RequestHeader *requestHeader = (UA_RequestHeader*)request; if(requestHeader->timestamp == 0) { if(server->config.verifyRequestTimestamp <= UA_RULEHANDLING_WARN) { UA_LOG_WARNING_CHANNEL(&server->config.logger, channel, "The server sends no timestamp in the request header. " "See the 'verifyRequestTimestamp' setting."); if(server->config.verifyRequestTimestamp <= UA_RULEHANDLING_ABORT) { retval = sendServiceFaultWithRequest(channel, requestHeader, responseType, requestId, UA_STATUSCODE_BADINVALIDTIMESTAMP); UA_deleteMembers(request, requestType); return retval; } } } #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION /* Set the authenticationToken from the create session request to help * fuzzing cover more lines */ UA_NodeId_deleteMembers(&requestHeader->authenticationToken); if(!UA_NodeId_isNull(&unsafe_fuzz_authenticationToken)) UA_NodeId_copy(&unsafe_fuzz_authenticationToken, &requestHeader->authenticationToken); #endif /* Prepare the respone */ UA_STACKARRAY(UA_Byte, response, responseType->memSize); UA_ResponseHeader *responseHeader = (UA_ResponseHeader*)response; UA_init(response, responseType); /* Continue with the decoded Request */ retval = processMSGDecoded(server, channel, requestId, service, requestHeader, requestType, responseHeader, responseType, sessionRequired); /* Clean up */ UA_deleteMembers(request, requestType); UA_deleteMembers(responseHeader, responseType); return retval; } /* Takes decoded messages starting at the nodeid of the content type. */ static void processSecureChannelMessage(void *application, UA_SecureChannel *channel, UA_MessageType messagetype, UA_UInt32 requestId, const UA_ByteString *message) { UA_Server *server = (UA_Server*)application; UA_StatusCode retval = UA_STATUSCODE_GOOD; switch(messagetype) { case UA_MESSAGETYPE_OPN: UA_LOG_TRACE_CHANNEL(&server->config.logger, channel, "Process an OPN on an open channel"); retval = processOPN(server, channel, requestId, message); break; case UA_MESSAGETYPE_MSG: UA_LOG_TRACE_CHANNEL(&server->config.logger, channel, "Process a MSG"); retval = processMSG(server, channel, requestId, message); break; case UA_MESSAGETYPE_CLO: UA_LOG_TRACE_CHANNEL(&server->config.logger, channel, "Process a CLO"); Service_CloseSecureChannel(server, channel); break; default: UA_LOG_TRACE_CHANNEL(&server->config.logger, channel, "Invalid message type"); retval = UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; break; } if(retval != UA_STATUSCODE_GOOD) { UA_LOG_INFO_CHANNEL(&server->config.logger, channel, "Processing the message failed with StatusCode %s. " "Closing the channel.", UA_StatusCode_name(retval)); Service_CloseSecureChannel(server, channel); } } static UA_StatusCode createSecureChannel(void *application, UA_Connection *connection, UA_AsymmetricAlgorithmSecurityHeader *asymHeader) { UA_Server *server = (UA_Server*)application; /* Iterate over available endpoints and choose the correct one */ UA_SecurityPolicy *securityPolicy = NULL; for(size_t i = 0; i < server->config.securityPoliciesSize; ++i) { UA_SecurityPolicy *policy = &server->config.securityPolicies[i]; if(!UA_ByteString_equal(&asymHeader->securityPolicyUri, &policy->policyUri)) continue; UA_StatusCode retval = policy->asymmetricModule. compareCertificateThumbprint(policy, &asymHeader->receiverCertificateThumbprint); if(retval != UA_STATUSCODE_GOOD) continue; /* We found the correct policy (except for security mode). The endpoint * needs to be selected by the client / server to match the security * mode in the endpoint for the session. */ securityPolicy = policy; break; } if(!securityPolicy) return UA_STATUSCODE_BADSECURITYPOLICYREJECTED; /* Create a new channel */ return UA_SecureChannelManager_create(&server->secureChannelManager, connection, securityPolicy, asymHeader); } static UA_StatusCode processCompleteChunkWithoutChannel(UA_Server *server, UA_Connection *connection, UA_ByteString *message) { /* Process chunk without a channel; must be OPN */ UA_LOG_TRACE(&server->config.logger, UA_LOGCATEGORY_NETWORK, "Connection %i | No channel attached to the connection. " "Process the chunk directly", (int)(connection->sockfd)); size_t offset = 0; UA_TcpMessageHeader tcpMessageHeader; UA_StatusCode retval = UA_TcpMessageHeader_decodeBinary(message, &offset, &tcpMessageHeader); if(retval != UA_STATUSCODE_GOOD) return retval; // Only HEL and OPN messages possible without a channel (on the server side) switch(tcpMessageHeader.messageTypeAndChunkType & 0x00ffffffu) { case UA_MESSAGETYPE_HEL: retval = processHEL(server, connection, message, &offset); break; case UA_MESSAGETYPE_OPN: { UA_LOG_TRACE(&server->config.logger, UA_LOGCATEGORY_NETWORK, "Connection %i | Process OPN message", (int)(connection->sockfd)); /* Called before HEL */ if(connection->state != UA_CONNECTION_ESTABLISHED) { retval = UA_STATUSCODE_BADCOMMUNICATIONERROR; break; } // Decode the asymmetric algorithm security header since it is not encrypted and // needed to decide what security policy to use. UA_AsymmetricAlgorithmSecurityHeader asymHeader; UA_AsymmetricAlgorithmSecurityHeader_init(&asymHeader); size_t messageHeaderOffset = UA_SECURE_CONVERSATION_MESSAGE_HEADER_LENGTH; retval = UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(message, &messageHeaderOffset, &asymHeader); if(retval != UA_STATUSCODE_GOOD) break; retval = createSecureChannel(server, connection, &asymHeader); UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader); if(retval != UA_STATUSCODE_GOOD) break; retval = UA_SecureChannel_decryptAddChunk(connection->channel, message, false); if(retval != UA_STATUSCODE_GOOD) break; UA_SecureChannel_processCompleteMessages(connection->channel, server, processSecureChannelMessage); break; } default: UA_LOG_TRACE(&server->config.logger, UA_LOGCATEGORY_NETWORK, "Connection %i | Expected OPN or HEL message on a connection " "without a SecureChannel", (int)(connection->sockfd)); retval = UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; break; } return retval; } static UA_StatusCode processCompleteChunk(void *const application, UA_Connection *connection, UA_ByteString *chunk) { UA_Server *server = (UA_Server*)application; #ifdef UA_DEBUG_DUMP_PKGS_FILE UA_debug_dumpCompleteChunk(server, connection, chunk); #endif if(!connection->channel) return processCompleteChunkWithoutChannel(server, connection, chunk); return UA_SecureChannel_decryptAddChunk(connection->channel, chunk, false); } void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection, UA_ByteString *message) { UA_LOG_TRACE(&server->config.logger, UA_LOGCATEGORY_NETWORK, "Connection %i | Received a packet.", (int)(connection->sockfd)); #ifdef UA_DEBUG_DUMP_PKGS UA_dump_hex_pkg(message->data, message->length); #endif UA_StatusCode retval = UA_Connection_processChunks(connection, server, processCompleteChunk, message); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_NETWORK, "Connection %i | Processing the message failed with " "error %s", (int)(connection->sockfd), UA_StatusCode_name(retval)); /* Send an ERR message and close the connection */ UA_TcpErrorMessage error; error.error = retval; error.reason = UA_STRING_NULL; UA_Connection_sendError(connection, &error); connection->close(connection); return; } UA_SecureChannel *channel = connection->channel; if(!channel) return; /* Process complete messages */ UA_SecureChannel_processCompleteMessages(channel, server, processSecureChannelMessage); /* Is the channel still open? */ if(channel->state == UA_SECURECHANNELSTATE_CLOSED) return; /* Store unused decoded chunks internally in the SecureChannel */ UA_SecureChannel_persistIncompleteMessages(connection->channel); } #ifdef UA_ENABLE_MULTITHREADING static void deleteConnection(UA_Server *server, UA_Connection *connection) { connection->free(connection); } #endif void UA_Server_removeConnection(UA_Server *server, UA_Connection *connection) { UA_Connection_detachSecureChannel(connection); #ifndef UA_ENABLE_MULTITHREADING connection->free(connection); #else UA_DelayedCallback *dc = (UA_DelayedCallback*)UA_malloc(sizeof(UA_DelayedCallback)); if(!dc) return; /* Malloc cannot fail on OS's that support multithreading. They * rather kill the process. */ dc->callback = (UA_ApplicationCallback)deleteConnection; dc->application = server; dc->data = connection; UA_WorkQueue_enqueueDelayed(&server->workQueue, dc); #endif } /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_server_utils.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2016-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2016 (c) Lorenz Haas * Copyright 2017 (c) frax2222 * Copyright 2017 (c) Florian Palm * Copyright 2017-2018 (c) Stefan Profanter, fortiss GmbH * Copyright 2017 (c) Julian Grothoff */ #define UA_MAX_TREE_RECURSE 50 /* How deep up/down the tree do we recurse at most? */ /********************************/ /* Information Model Operations */ /********************************/ /* Keeps track of already visited nodes to detect circular references */ struct ref_history { struct ref_history *parent; /* the previous element */ const UA_NodeId *id; /* the id of the node at this depth */ UA_UInt16 depth; }; 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->refTargetsSize; ++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->refTargets[j].target.nodeId)) skip = true; last = last->parent; } if(skip) continue; } /* Stack-allocate the visitedRefs structure for the next depth */ struct ref_history nextVisitedRefs = {visitedRefs, &refs->refTargets[j].target.nodeId, (UA_UInt16)(visitedRefs->depth+1)}; /* Recurse */ UA_Boolean foundRecursive = isNodeInTreeNoCircular(nsCtx, &refs->refTargets[j].target.nodeId, nodeToFind, &nextVisitedRefs, referenceTypeIds, referenceTypeIdsSize); if(foundRecursive) { UA_Nodestore_releaseNode(nsCtx, node); return true; } } } UA_Nodestore_releaseNode(nsCtx, node); return false; } UA_Boolean isNodeInTree(void *nsCtx, const UA_NodeId *leafNode, const UA_NodeId *nodeToFind, const UA_NodeId *referenceTypeIds, size_t referenceTypeIdsSize) { struct ref_history visitedRefs = {NULL, leafNode, 0}; return isNodeInTreeNoCircular(nsCtx, leafNode, nodeToFind, &visitedRefs, referenceTypeIds, referenceTypeIdsSize); } 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].refTargetsSize> 0); const UA_NodeId *targetId = &node->references[i].refTargets[0].target.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; } 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 const UA_NodeId hasInterfaceNodeId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASINTERFACE}}; UA_StatusCode getParentTypeAndInterfaceHierarchy(UA_Server *server, const UA_NodeId *typeNode, UA_NodeId **typeHierarchy, size_t *typeHierarchySize) { UA_ExpandedNodeId *subTypes = NULL; size_t subTypesSize = 0; UA_StatusCode retval = browseRecursive(server, 1, typeNode, 1, &subtypeId, UA_BROWSEDIRECTION_INVERSE, false, &subTypesSize, &subTypes); if(retval != UA_STATUSCODE_GOOD) return retval; UA_assert(subTypesSize < 1000); UA_ExpandedNodeId *interfaces = NULL; size_t interfacesSize = 0; retval = browseRecursive(server, 1, typeNode, 1, &hasInterfaceNodeId, UA_BROWSEDIRECTION_FORWARD, false, &interfacesSize, &interfaces); if(retval != UA_STATUSCODE_GOOD) { UA_Array_delete(subTypes, subTypesSize, &UA_TYPES[UA_TYPES_NODEID]); return retval; } UA_assert(interfacesSize < 1000); UA_NodeId *hierarchy = (UA_NodeId*) UA_malloc(sizeof(UA_NodeId) * (1 + subTypesSize + interfacesSize)); if(!hierarchy) { UA_Array_delete(subTypes, subTypesSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); UA_Array_delete(interfaces, interfacesSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); return UA_STATUSCODE_BADOUTOFMEMORY; } retval = UA_NodeId_copy(typeNode, hierarchy); if(retval != UA_STATUSCODE_GOOD) { UA_free(hierarchy); UA_Array_delete(subTypes, subTypesSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); UA_Array_delete(interfaces, interfacesSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); return UA_STATUSCODE_BADOUTOFMEMORY; } for(size_t i = 0; i < subTypesSize; i++) { hierarchy[i+1] = subTypes[i].nodeId; UA_NodeId_init(&subTypes[i].nodeId); } for(size_t i = 0; i < interfacesSize; i++) { hierarchy[i+1+subTypesSize] = interfaces[i].nodeId; UA_NodeId_init(&interfaces[i].nodeId); } *typeHierarchy = hierarchy; *typeHierarchySize = subTypesSize + interfacesSize + 1; UA_assert(*typeHierarchySize < 1000); UA_Array_delete(subTypes, subTypesSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); UA_Array_delete(interfaces, interfacesSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); return UA_STATUSCODE_GOOD; } /* For mulithreading: make a copy of the node, edit and replace. * For singlethreading: edit the original */ UA_StatusCode UA_Server_editNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId, UA_EditNodeCallback callback, void *data) { #ifndef UA_ENABLE_IMMUTABLE_NODES /* Get the node and process it in-situ */ const UA_Node *node = UA_Nodestore_getNode(server->nsCtx, nodeId); if(!node) return UA_STATUSCODE_BADNODEIDUNKNOWN; UA_StatusCode retval = callback(server, session, (UA_Node*)(uintptr_t)node, data); UA_Nodestore_releaseNode(server->nsCtx, node); return retval; #else UA_StatusCode retval; do { /* Get an editable copy of the node */ UA_Node *node; retval = UA_Nodestore_getNodeCopy(server->nsCtx, nodeId, &node); if(retval != UA_STATUSCODE_GOOD) return retval; /* Run the operation on the copy */ retval = callback(server, session, node, data); if(retval != UA_STATUSCODE_GOOD) { UA_Nodestore_deleteNode(server->nsCtx, node); return retval; } /* Replace the node */ retval = UA_Nodestore_replaceNode(server->nsCtx, node); } while(retval != UA_STATUSCODE_GOOD); return retval; #endif } UA_StatusCode UA_Server_processServiceOperations(UA_Server *server, UA_Session *session, UA_ServiceOperation operationCallback, const void *context, const size_t *requestOperations, const UA_DataType *requestOperationsType, size_t *responseOperations, const UA_DataType *responseOperationsType) { size_t ops = *requestOperations; if(ops == 0) return UA_STATUSCODE_BADNOTHINGTODO; /* No padding after size_t */ void **respPos = (void**)((uintptr_t)responseOperations + sizeof(size_t)); *respPos = UA_Array_new(ops, responseOperationsType); if(!(*respPos)) return UA_STATUSCODE_BADOUTOFMEMORY; *responseOperations = ops; uintptr_t respOp = (uintptr_t)*respPos; /* No padding after size_t */ uintptr_t reqOp = *(uintptr_t*)((uintptr_t)requestOperations + sizeof(size_t)); for(size_t i = 0; i < ops; i++) { operationCallback(server, session, context, (void*)reqOp, (void*)respOp); reqOp += requestOperationsType->memSize; respOp += responseOperationsType->memSize; } return UA_STATUSCODE_GOOD; } /* A few global NodeId definitions */ const UA_NodeId subtypeId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASSUBTYPE}}; const UA_NodeId hierarchicalReferences = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HIERARCHICALREFERENCES}}; /*********************************/ /* Default attribute definitions */ /*********************************/ const UA_ObjectAttributes UA_ObjectAttributes_default = { 0, /* specifiedAttributes */ {{0, NULL}, {0, NULL}}, /* displayName */ {{0, NULL}, {0, NULL}}, /* description */ 0, 0, /* writeMask (userWriteMask) */ 0 /* eventNotifier */ }; const UA_VariableAttributes UA_VariableAttributes_default = { 0, /* specifiedAttributes */ {{0, NULL}, {0, NULL}}, /* displayName */ {{0, NULL}, {0, NULL}}, /* description */ 0, 0, /* writeMask (userWriteMask) */ {NULL, UA_VARIANT_DATA, 0, NULL, 0, NULL}, /* value */ {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_BASEDATATYPE}}, /* dataType */ UA_VALUERANK_ANY, /* valueRank */ 0, NULL, /* arrayDimensions */ UA_ACCESSLEVELMASK_READ, 0, /* accessLevel (userAccessLevel) */ 0.0, /* minimumSamplingInterval */ false /* historizing */ }; const UA_MethodAttributes UA_MethodAttributes_default = { 0, /* specifiedAttributes */ {{0, NULL}, {0, NULL}}, /* displayName */ {{0, NULL}, {0, NULL}}, /* description */ 0, 0, /* writeMask (userWriteMask) */ true, true /* executable (userExecutable) */ }; const UA_ObjectTypeAttributes UA_ObjectTypeAttributes_default = { 0, /* specifiedAttributes */ {{0, NULL}, {0, NULL}}, /* displayName */ {{0, NULL}, {0, NULL}}, /* description */ 0, 0, /* writeMask (userWriteMask) */ false /* isAbstract */ }; const UA_VariableTypeAttributes UA_VariableTypeAttributes_default = { 0, /* specifiedAttributes */ {{0, NULL}, {0, NULL}}, /* displayName */ {{0, NULL}, {0, NULL}}, /* description */ 0, 0, /* writeMask (userWriteMask) */ {NULL, UA_VARIANT_DATA, 0, NULL, 0, NULL}, /* value */ {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_BASEDATATYPE}}, /* dataType */ UA_VALUERANK_ANY, /* valueRank */ 0, NULL, /* arrayDimensions */ false /* isAbstract */ }; const UA_ReferenceTypeAttributes UA_ReferenceTypeAttributes_default = { 0, /* specifiedAttributes */ {{0, NULL}, {0, NULL}}, /* displayName */ {{0, NULL}, {0, NULL}}, /* description */ 0, 0, /* writeMask (userWriteMask) */ false, /* isAbstract */ false, /* symmetric */ {{0, NULL}, {0, NULL}} /* inverseName */ }; const UA_DataTypeAttributes UA_DataTypeAttributes_default = { 0, /* specifiedAttributes */ {{0, NULL}, {0, NULL}}, /* displayName */ {{0, NULL}, {0, NULL}}, /* description */ 0, 0, /* writeMask (userWriteMask) */ false /* isAbstract */ }; const UA_ViewAttributes UA_ViewAttributes_default = { 0, /* specifiedAttributes */ {{0, NULL}, {0, NULL}}, /* displayName */ {{0, NULL}, {0, NULL}}, /* description */ 0, 0, /* writeMask (userWriteMask) */ false, /* containsNoLoops */ 0 /* eventNotifier */ }; /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_server_discovery.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2017 (c) Stefan Profanter, fortiss GmbH */ #ifdef UA_ENABLE_DISCOVERY static UA_StatusCode register_server_with_discovery_server(UA_Server *server, UA_Client *client, const UA_Boolean isUnregister, const char* semaphoreFilePath) { /* Prepare the request. Do not cleanup the request after the service call, * as the members are stack-allocated or point into the server config. */ UA_RegisterServer2Request request; UA_RegisterServer2Request_init(&request); request.requestHeader.timestamp = UA_DateTime_now(); request.requestHeader.timeoutHint = 10000; request.server.isOnline = !isUnregister; request.server.serverUri = server->config.applicationDescription.applicationUri; request.server.productUri = server->config.applicationDescription.productUri; request.server.serverType = server->config.applicationDescription.applicationType; request.server.gatewayServerUri = server->config.applicationDescription.gatewayServerUri; if(semaphoreFilePath) { #ifdef UA_ENABLE_DISCOVERY_SEMAPHORE request.server.semaphoreFilePath = UA_STRING((char*)(uintptr_t)semaphoreFilePath); /* dirty cast */ #else UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_CLIENT, "Ignoring semaphore file path. open62541 not compiled " "with UA_ENABLE_DISCOVERY_SEMAPHORE=ON"); #endif } request.server.serverNames = &server->config.applicationDescription.applicationName; request.server.serverNamesSize = 1; /* Copy the discovery urls from the server config and the network layers*/ size_t config_discurls = server->config.applicationDescription.discoveryUrlsSize; size_t nl_discurls = server->config.networkLayersSize; size_t total_discurls = config_discurls + nl_discurls; UA_STACKARRAY(UA_String, urlsBuf, total_discurls); request.server.discoveryUrls = urlsBuf; request.server.discoveryUrlsSize = total_discurls; for(size_t i = 0; i < config_discurls; ++i) request.server.discoveryUrls[i] = server->config.applicationDescription.discoveryUrls[i]; /* TODO: Add nl only if discoveryUrl not already present */ for(size_t i = 0; i < nl_discurls; ++i) { UA_ServerNetworkLayer *nl = &server->config.networkLayers[i]; request.server.discoveryUrls[config_discurls + i] = nl->discoveryUrl; } #ifdef UA_ENABLE_DISCOVERY_MULTICAST request.discoveryConfigurationSize = 1; request.discoveryConfiguration = UA_ExtensionObject_new(); UA_ExtensionObject_init(&request.discoveryConfiguration[0]); // Set to NODELETE so that we can just use a pointer to the mdns config request.discoveryConfiguration[0].encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE; request.discoveryConfiguration[0].content.decoded.type = &UA_TYPES[UA_TYPES_MDNSDISCOVERYCONFIGURATION]; request.discoveryConfiguration[0].content.decoded.data = &server->config.discovery.mdns; #endif // First try with RegisterServer2, if that isn't implemented, use RegisterServer UA_RegisterServer2Response response; __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_REGISTERSERVER2REQUEST], &response, &UA_TYPES[UA_TYPES_REGISTERSERVER2RESPONSE]); UA_StatusCode serviceResult = response.responseHeader.serviceResult; UA_RegisterServer2Response_deleteMembers(&response); UA_Array_delete(request.discoveryConfiguration, request.discoveryConfigurationSize, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]); request.discoveryConfiguration = NULL; request.discoveryConfigurationSize = 0; if(serviceResult == UA_STATUSCODE_BADNOTIMPLEMENTED || serviceResult == UA_STATUSCODE_BADSERVICEUNSUPPORTED) { /* Try RegisterServer */ UA_RegisterServerRequest request_fallback; UA_RegisterServerRequest_init(&request_fallback); /* Copy from RegisterServer2 request */ request_fallback.requestHeader = request.requestHeader; request_fallback.server = request.server; UA_RegisterServerResponse response_fallback; __UA_Client_Service(client, &request_fallback, &UA_TYPES[UA_TYPES_REGISTERSERVERREQUEST], &response_fallback, &UA_TYPES[UA_TYPES_REGISTERSERVERRESPONSE]); serviceResult = response_fallback.responseHeader.serviceResult; UA_RegisterServerResponse_deleteMembers(&response_fallback); } if(serviceResult != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_CLIENT, "RegisterServer/RegisterServer2 failed with statuscode %s", UA_StatusCode_name(serviceResult)); } return serviceResult; } UA_StatusCode UA_Server_register_discovery(UA_Server *server, UA_Client *client, const char* semaphoreFilePath) { return register_server_with_discovery_server(server, client, false, semaphoreFilePath); } UA_StatusCode UA_Server_unregister_discovery(UA_Server *server, UA_Client *client) { return register_server_with_discovery_server(server, client, true, NULL); } #endif /* UA_ENABLE_DISCOVERY */ /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_securechannel_manager.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2014-2017 (c) Florian Palm * Copyright 2015-2016 (c) Sten Grüner * Copyright 2015 (c) Oleksiy Vasylyev * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB */ #define STARTCHANNELID 1 #define STARTTOKENID 1 UA_StatusCode UA_SecureChannelManager_init(UA_SecureChannelManager *cm, UA_Server *server) { TAILQ_INIT(&cm->channels); // TODO: use an ID that is likely to be unique after a restart cm->lastChannelId = STARTCHANNELID; cm->lastTokenId = STARTTOKENID; cm->currentChannelCount = 0; cm->server = server; return UA_STATUSCODE_GOOD; } void UA_SecureChannelManager_deleteMembers(UA_SecureChannelManager *cm) { channel_entry *entry, *temp; TAILQ_FOREACH_SAFE(entry, &cm->channels, pointers, temp) { TAILQ_REMOVE(&cm->channels, entry, pointers); UA_SecureChannel_close(&entry->channel); UA_SecureChannel_deleteMembers(&entry->channel); UA_free(entry); } } static void removeSecureChannelCallback(void *_, channel_entry *entry) { UA_SecureChannel_deleteMembers(&entry->channel); } static void removeSecureChannel(UA_SecureChannelManager *cm, channel_entry *entry) { /* Close the SecureChannel */ UA_SecureChannel_close(&entry->channel); /* Detach the channel and make the capacity available */ TAILQ_REMOVE(&cm->channels, entry, pointers); UA_atomic_subUInt32(&cm->currentChannelCount, 1); /* Add a delayed callback to remove the channel when the currently * scheduled jobs have completed */ entry->cleanupCallback.callback = (UA_ApplicationCallback)removeSecureChannelCallback; entry->cleanupCallback.application = NULL; entry->cleanupCallback.data = entry; UA_WorkQueue_enqueueDelayed(&cm->server->workQueue, &entry->cleanupCallback); } /* remove channels that were not renewed or who have no connection attached */ void UA_SecureChannelManager_cleanupTimedOut(UA_SecureChannelManager *cm, UA_DateTime nowMonotonic) { channel_entry *entry, *temp; TAILQ_FOREACH_SAFE(entry, &cm->channels, pointers, temp) { /* The channel was closed internally */ if(entry->channel.state == UA_SECURECHANNELSTATE_CLOSED || !entry->channel.connection) { removeSecureChannel(cm, entry); continue; } /* The channel has timed out */ UA_DateTime timeout = entry->channel.securityToken.createdAt + (UA_DateTime)(entry->channel.securityToken.revisedLifetime * UA_DATETIME_MSEC); if(timeout < nowMonotonic) { UA_LOG_INFO_CHANNEL(&cm->server->config.logger, &entry->channel, "SecureChannel has timed out"); removeSecureChannel(cm, entry); continue; } /* Revolve the channel tokens */ if(entry->channel.nextSecurityToken.tokenId > 0) { UA_SecureChannel_revolveTokens(&entry->channel); } } } /* remove the first channel that has no session attached */ static UA_Boolean purgeFirstChannelWithoutSession(UA_SecureChannelManager *cm) { channel_entry *entry; TAILQ_FOREACH(entry, &cm->channels, pointers) { if(LIST_EMPTY(&entry->channel.sessions)) { UA_LOG_INFO_CHANNEL(&cm->server->config.logger, &entry->channel, "Channel was purged since maxSecureChannels was " "reached and channel had no session attached"); removeSecureChannel(cm, entry); return true; } } return false; } UA_StatusCode UA_SecureChannelManager_create(UA_SecureChannelManager *const cm, UA_Connection *const connection, const UA_SecurityPolicy *const securityPolicy, const UA_AsymmetricAlgorithmSecurityHeader *const asymHeader) { /* connection already has a channel attached. */ if(connection->channel != NULL) return UA_STATUSCODE_BADINTERNALERROR; /* Check if there exists a free SC, otherwise try to purge one SC without a * session the purge has been introduced to pass CTT, it is not clear what * strategy is expected here */ if(cm->currentChannelCount >= cm->server->config.maxSecureChannels && !purgeFirstChannelWithoutSession(cm)) return UA_STATUSCODE_BADOUTOFMEMORY; UA_LOG_INFO(&cm->server->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "Creating a new SecureChannel"); channel_entry *entry = (channel_entry *)UA_malloc(sizeof(channel_entry)); if(!entry) return UA_STATUSCODE_BADOUTOFMEMORY; /* Create the channel context and parse the sender (remote) certificate used for the * secureChannel. */ UA_SecureChannel_init(&entry->channel); UA_StatusCode retval = UA_SecureChannel_setSecurityPolicy(&entry->channel, securityPolicy, &asymHeader->senderCertificate); if(retval != UA_STATUSCODE_GOOD) { UA_free(entry); return retval; } /* Channel state is fresh (0) */ entry->channel.securityToken.channelId = 0; entry->channel.securityToken.tokenId = cm->lastTokenId++; entry->channel.securityToken.createdAt = UA_DateTime_now(); entry->channel.securityToken.revisedLifetime = cm->server->config.maxSecurityTokenLifetime; TAILQ_INSERT_TAIL(&cm->channels, entry, pointers); UA_atomic_addUInt32(&cm->currentChannelCount, 1); UA_Connection_attachSecureChannel(connection, &entry->channel); return UA_STATUSCODE_GOOD; } UA_StatusCode UA_SecureChannelManager_open(UA_SecureChannelManager *cm, UA_SecureChannel *channel, const UA_OpenSecureChannelRequest *request, UA_OpenSecureChannelResponse *response) { if(channel->state != UA_SECURECHANNELSTATE_FRESH) { UA_LOG_ERROR_CHANNEL(&cm->server->config.logger, channel, "Called open on already open or closed channel"); return UA_STATUSCODE_BADINTERNALERROR; } if(request->securityMode != UA_MESSAGESECURITYMODE_NONE && UA_ByteString_equal(&channel->securityPolicy->policyUri, &UA_SECURITY_POLICY_NONE_URI)) { return UA_STATUSCODE_BADSECURITYMODEREJECTED; } channel->securityMode = request->securityMode; channel->securityToken.createdAt = UA_DateTime_nowMonotonic(); channel->securityToken.channelId = cm->lastChannelId++; channel->securityToken.createdAt = UA_DateTime_now(); /* Set the lifetime. Lifetime 0 -> set the maximum possible */ channel->securityToken.revisedLifetime = (request->requestedLifetime > cm->server->config.maxSecurityTokenLifetime) ? cm->server->config.maxSecurityTokenLifetime : request->requestedLifetime; if(channel->securityToken.revisedLifetime == 0) channel->securityToken.revisedLifetime = cm->server->config.maxSecurityTokenLifetime; /* Set the nonces and generate the keys */ UA_StatusCode retval = UA_ByteString_copy(&request->clientNonce, &channel->remoteNonce); if(retval != UA_STATUSCODE_GOOD) return retval; retval = UA_SecureChannel_generateLocalNonce(channel); if(retval != UA_STATUSCODE_GOOD) return retval; retval = UA_SecureChannel_generateNewKeys(channel); if(retval != UA_STATUSCODE_GOOD) return retval; /* Set the response */ retval = UA_ByteString_copy(&channel->localNonce, &response->serverNonce); if(retval != UA_STATUSCODE_GOOD) return retval; retval = UA_ChannelSecurityToken_copy(&channel->securityToken, &response->securityToken); if(retval != UA_STATUSCODE_GOOD) return retval; response->responseHeader.timestamp = UA_DateTime_now(); response->responseHeader.requestHandle = request->requestHeader.requestHandle; /* The channel is open */ channel->state = UA_SECURECHANNELSTATE_OPEN; return UA_STATUSCODE_GOOD; } UA_StatusCode UA_SecureChannelManager_renew(UA_SecureChannelManager *cm, UA_SecureChannel *channel, const UA_OpenSecureChannelRequest *request, UA_OpenSecureChannelResponse *response) { if(channel->state != UA_SECURECHANNELSTATE_OPEN) { UA_LOG_ERROR_CHANNEL(&cm->server->config.logger, channel, "Called renew on channel which is not open"); return UA_STATUSCODE_BADINTERNALERROR; } /* If no security token is already issued */ if(channel->nextSecurityToken.tokenId == 0) { channel->nextSecurityToken.channelId = channel->securityToken.channelId; channel->nextSecurityToken.tokenId = cm->lastTokenId++; channel->nextSecurityToken.createdAt = UA_DateTime_now(); channel->nextSecurityToken.revisedLifetime = (request->requestedLifetime > cm->server->config.maxSecurityTokenLifetime) ? cm->server->config.maxSecurityTokenLifetime : request->requestedLifetime; if(channel->nextSecurityToken.revisedLifetime == 0) /* lifetime 0 -> return the max lifetime */ channel->nextSecurityToken.revisedLifetime = cm->server->config.maxSecurityTokenLifetime; } /* Replace the nonces */ UA_ByteString_deleteMembers(&channel->remoteNonce); UA_StatusCode retval = UA_ByteString_copy(&request->clientNonce, &channel->remoteNonce); if(retval != UA_STATUSCODE_GOOD) return retval; retval = UA_SecureChannel_generateLocalNonce(channel); if(retval != UA_STATUSCODE_GOOD) return retval; /* Set the response */ response->responseHeader.requestHandle = request->requestHeader.requestHandle; retval = UA_ByteString_copy(&channel->localNonce, &response->serverNonce); if(retval != UA_STATUSCODE_GOOD) return retval; retval = UA_ChannelSecurityToken_copy(&channel->nextSecurityToken, &response->securityToken); if(retval != UA_STATUSCODE_GOOD) return retval; /* Reset the internal creation date to the monotonic clock */ channel->nextSecurityToken.createdAt = UA_DateTime_nowMonotonic(); return UA_STATUSCODE_GOOD; } UA_SecureChannel * UA_SecureChannelManager_get(UA_SecureChannelManager *cm, UA_UInt32 channelId) { channel_entry *entry; TAILQ_FOREACH(entry, &cm->channels, pointers) { if(entry->channel.securityToken.channelId == channelId) return &entry->channel; } return NULL; } UA_StatusCode UA_SecureChannelManager_close(UA_SecureChannelManager *cm, UA_UInt32 channelId) { channel_entry *entry; TAILQ_FOREACH(entry, &cm->channels, pointers) { if(entry->channel.securityToken.channelId == channelId) break; } if(!entry) return UA_STATUSCODE_BADINTERNALERROR; removeSecureChannel(cm, entry); return UA_STATUSCODE_GOOD; } /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_session_manager.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2019 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2014, 2017 (c) Florian Palm * Copyright 2015 (c) Sten Grüner * Copyright 2015 (c) Oleksiy Vasylyev * Copyright 2017 (c) Stefan Profanter, fortiss GmbH */ UA_StatusCode UA_SessionManager_init(UA_SessionManager *sm, UA_Server *server) { LIST_INIT(&sm->sessions); sm->currentSessionCount = 0; sm->server = server; return UA_STATUSCODE_GOOD; } /* Delayed callback to free the session memory */ static void removeSessionCallback(UA_Server *server, session_list_entry *entry) { UA_Session_deleteMembersCleanup(&entry->session, server); } static void removeSession(UA_SessionManager *sm, session_list_entry *sentry) { UA_Server *server = sm->server; UA_Session *session = &sentry->session; /* Remove the Subscriptions */ #ifdef UA_ENABLE_SUBSCRIPTIONS UA_Subscription *sub, *tempsub; LIST_FOREACH_SAFE(sub, &session->serverSubscriptions, listEntry, tempsub) { UA_Session_deleteSubscription(server, session, sub->subscriptionId); } UA_PublishResponseEntry *entry; while((entry = UA_Session_dequeuePublishReq(session))) { UA_PublishResponse_deleteMembers(&entry->response); UA_free(entry); } #endif /* Callback into userland access control */ if(server->config.accessControl.closeSession) server->config.accessControl.closeSession(server, &server->config.accessControl, &session->sessionId, session->sessionHandle); /* Detach the Session from the SecureChannel */ UA_Session_detachFromSecureChannel(session); /* Deactivate the session */ sentry->session.activated = false; /* Detach the session from the session manager and make the capacity * available */ LIST_REMOVE(sentry, pointers); UA_atomic_subUInt32(&sm->currentSessionCount, 1); /* Add a delayed callback to remove the session when the currently * scheduled jobs have completed */ sentry->cleanupCallback.callback = (UA_ApplicationCallback)removeSessionCallback; sentry->cleanupCallback.application = sm->server; sentry->cleanupCallback.data = sentry; UA_WorkQueue_enqueueDelayed(&server->workQueue, &sentry->cleanupCallback); } void UA_SessionManager_deleteMembers(UA_SessionManager *sm) { session_list_entry *current, *temp; LIST_FOREACH_SAFE(current, &sm->sessions, pointers, temp) { removeSession(sm, current); } } void UA_SessionManager_cleanupTimedOut(UA_SessionManager *sm, UA_DateTime nowMonotonic) { session_list_entry *sentry, *temp; LIST_FOREACH_SAFE(sentry, &sm->sessions, pointers, temp) { /* Session has timed out? */ if(sentry->session.validTill >= nowMonotonic) continue; UA_LOG_INFO_SESSION(&sm->server->config.logger, &sentry->session, "Session has timed out"); removeSession(sm, sentry); } } UA_Session * UA_SessionManager_getSessionByToken(UA_SessionManager *sm, const UA_NodeId *token) { session_list_entry *current = NULL; LIST_FOREACH(current, &sm->sessions, pointers) { /* Token does not match */ if(!UA_NodeId_equal(¤t->session.header.authenticationToken, token)) continue; /* Session has timed out */ if(UA_DateTime_nowMonotonic() > current->session.validTill) { UA_LOG_INFO_SESSION(&sm->server->config.logger, ¤t->session, "Client tries to use a session that has timed out"); return NULL; } /* Ok, return */ return ¤t->session; } /* Session not found */ #if UA_LOGLEVEL <= 300 UA_String nodeIdStr = UA_STRING_NULL; UA_NodeId_toString(token, &nodeIdStr); UA_LOG_INFO(&sm->server->config.logger, UA_LOGCATEGORY_SESSION, "Try to use Session with token %.*s but is not found", (int)nodeIdStr.length, nodeIdStr.data); UA_String_deleteMembers(&nodeIdStr); #endif return NULL; } UA_Session * UA_SessionManager_getSessionById(UA_SessionManager *sm, const UA_NodeId *sessionId) { session_list_entry *current = NULL; LIST_FOREACH(current, &sm->sessions, pointers) { /* Token does not match */ if(!UA_NodeId_equal(¤t->session.sessionId, sessionId)) continue; /* Session has timed out */ if(UA_DateTime_nowMonotonic() > current->session.validTill) { UA_LOG_INFO_SESSION(&sm->server->config.logger, ¤t->session, "Client tries to use a session that has timed out"); return NULL; } /* Ok, return */ return ¤t->session; } /* Session not found */ UA_String sessionIdStr = UA_STRING_NULL; UA_NodeId_toString(sessionId, &sessionIdStr); UA_LOG_INFO(&sm->server->config.logger, UA_LOGCATEGORY_SESSION, "Try to use Session with identifier %.*s but is not found", (int)sessionIdStr.length, sessionIdStr.data); UA_String_deleteMembers(&sessionIdStr); return NULL; } /* Creates and adds a session. But it is not yet attached to a secure channel. */ UA_StatusCode UA_SessionManager_createSession(UA_SessionManager *sm, UA_SecureChannel *channel, const UA_CreateSessionRequest *request, UA_Session **session) { if(sm->currentSessionCount >= sm->server->config.maxSessions) return UA_STATUSCODE_BADTOOMANYSESSIONS; session_list_entry *newentry = (session_list_entry *)UA_malloc(sizeof(session_list_entry)); if(!newentry) return UA_STATUSCODE_BADOUTOFMEMORY; UA_atomic_addUInt32(&sm->currentSessionCount, 1); UA_Session_init(&newentry->session); newentry->session.sessionId = UA_NODEID_GUID(1, UA_Guid_random()); newentry->session.header.authenticationToken = UA_NODEID_GUID(1, UA_Guid_random()); if(request->requestedSessionTimeout <= sm->server->config.maxSessionTimeout && request->requestedSessionTimeout > 0) newentry->session.timeout = request->requestedSessionTimeout; else newentry->session.timeout = sm->server->config.maxSessionTimeout; UA_Session_updateLifetime(&newentry->session); LIST_INSERT_HEAD(&sm->sessions, newentry, pointers); *session = &newentry->session; return UA_STATUSCODE_GOOD; } UA_StatusCode UA_SessionManager_removeSession(UA_SessionManager *sm, const UA_NodeId *token) { session_list_entry *current; LIST_FOREACH(current, &sm->sessions, pointers) { if(UA_NodeId_equal(¤t->session.header.authenticationToken, token)) break; } if(!current) return UA_STATUSCODE_BADSESSIONIDINVALID; removeSession(sm, current); return UA_STATUSCODE_GOOD; } /*********************************** amalgamated original file "/home/jvoe/open62541/src/pubsub/ua_pubsub_networkmessage.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright (c) 2017 - 2018 Fraunhofer IOSB (Author: Tino Bischoff) */ #ifdef UA_ENABLE_PUBSUB /* conditional compilation */ const UA_Byte NM_VERSION_MASK = 15; const UA_Byte NM_PUBLISHER_ID_ENABLED_MASK = 16; const UA_Byte NM_GROUP_HEADER_ENABLED_MASK = 32; const UA_Byte NM_PAYLOAD_HEADER_ENABLED_MASK = 64; const UA_Byte NM_EXTENDEDFLAGS1_ENABLED_MASK = 128; const UA_Byte NM_PUBLISHER_ID_MASK = 7; const UA_Byte NM_DATASET_CLASSID_ENABLED_MASK = 8; const UA_Byte NM_SECURITY_ENABLED_MASK = 16; const UA_Byte NM_TIMESTAMP_ENABLED_MASK = 32; const UA_Byte NM_PICOSECONDS_ENABLED_MASK = 64; const UA_Byte NM_EXTENDEDFLAGS2_ENABLED_MASK = 128; const UA_Byte NM_NETWORK_MSG_TYPE_MASK = 28; const UA_Byte NM_CHUNK_MESSAGE_MASK = 1; const UA_Byte NM_PROMOTEDFIELDS_ENABLED_MASK = 2; const UA_Byte GROUP_HEADER_WRITER_GROUPID_ENABLED = 1; const UA_Byte GROUP_HEADER_GROUP_VERSION_ENABLED = 2; const UA_Byte GROUP_HEADER_NM_NUMBER_ENABLED = 4; const UA_Byte GROUP_HEADER_SEQUENCE_NUMBER_ENABLED = 8; const UA_Byte SECURITY_HEADER_NM_SIGNED = 1; const UA_Byte SECURITY_HEADER_NM_ENCRYPTED = 2; const UA_Byte SECURITY_HEADER_SEC_FOOTER_ENABLED = 4; const UA_Byte SECURITY_HEADER_FORCE_KEY_RESET = 8; const UA_Byte DS_MESSAGEHEADER_DS_MSG_VALID = 1; const UA_Byte DS_MESSAGEHEADER_FIELD_ENCODING_MASK = 6; const UA_Byte DS_MESSAGEHEADER_SEQ_NR_ENABLED_MASK = 8; const UA_Byte DS_MESSAGEHEADER_STATUS_ENABLED_MASK = 16; const UA_Byte DS_MESSAGEHEADER_CONFIGMAJORVERSION_ENABLED_MASK = 32; const UA_Byte DS_MESSAGEHEADER_CONFIGMINORVERSION_ENABLED_MASK = 64; const UA_Byte DS_MESSAGEHEADER_FLAGS2_ENABLED_MASK = 128; const UA_Byte DS_MESSAGEHEADER_DS_MESSAGE_TYPE_MASK = 15; const UA_Byte DS_MESSAGEHEADER_TIMESTAMP_ENABLED_MASK = 16; const UA_Byte DS_MESSAGEHEADER_PICOSECONDS_INCLUDED_MASK = 32; const UA_Byte NM_SHIFT_LEN = 2; const UA_Byte DS_MH_SHIFT_LEN = 1; static UA_Boolean UA_NetworkMessage_ExtendedFlags1Enabled(const UA_NetworkMessage* src); static UA_Boolean UA_NetworkMessage_ExtendedFlags2Enabled(const UA_NetworkMessage* src); static UA_Boolean UA_DataSetMessageHeader_DataSetFlags2Enabled(const UA_DataSetMessageHeader* src); UA_StatusCode UA_NetworkMessage_encodeBinary(const UA_NetworkMessage* src, UA_Byte **bufPos, const UA_Byte *bufEnd) { /* UADPVersion + UADP Flags */ UA_Byte v = src->version; if(src->publisherIdEnabled) v |= NM_PUBLISHER_ID_ENABLED_MASK; if(src->groupHeaderEnabled) v |= NM_GROUP_HEADER_ENABLED_MASK; if(src->payloadHeaderEnabled) v |= NM_PAYLOAD_HEADER_ENABLED_MASK; if(UA_NetworkMessage_ExtendedFlags1Enabled(src)) v |= NM_EXTENDEDFLAGS1_ENABLED_MASK; UA_StatusCode rv = UA_Byte_encodeBinary(&v, bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; // ExtendedFlags1 if(UA_NetworkMessage_ExtendedFlags1Enabled(src)) { v = (UA_Byte)src->publisherIdType; if(src->dataSetClassIdEnabled) v |= NM_DATASET_CLASSID_ENABLED_MASK; if(src->securityEnabled) v |= NM_SECURITY_ENABLED_MASK; if(src->timestampEnabled) v |= NM_TIMESTAMP_ENABLED_MASK; if(src->picosecondsEnabled) v |= NM_PICOSECONDS_ENABLED_MASK; if(UA_NetworkMessage_ExtendedFlags2Enabled(src)) v |= NM_EXTENDEDFLAGS2_ENABLED_MASK; rv = UA_Byte_encodeBinary(&v, bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; // ExtendedFlags2 if(UA_NetworkMessage_ExtendedFlags2Enabled(src)) { v = (UA_Byte)src->networkMessageType; // shift left 2 bit v = (UA_Byte) (v << NM_SHIFT_LEN); if(src->chunkMessage) v |= NM_CHUNK_MESSAGE_MASK; if(src->promotedFieldsEnabled) v |= NM_PROMOTEDFIELDS_ENABLED_MASK; rv = UA_Byte_encodeBinary(&v, bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; } } // PublisherId if(src->publisherIdEnabled) { switch (src->publisherIdType) { case UA_PUBLISHERDATATYPE_BYTE: rv = UA_Byte_encodeBinary(&(src->publisherId.publisherIdByte), bufPos, bufEnd); break; case UA_PUBLISHERDATATYPE_UINT16: rv = UA_UInt16_encodeBinary(&(src->publisherId.publisherIdUInt16), bufPos, bufEnd); break; case UA_PUBLISHERDATATYPE_UINT32: rv = UA_UInt32_encodeBinary(&(src->publisherId.publisherIdUInt32), bufPos, bufEnd); break; case UA_PUBLISHERDATATYPE_UINT64: rv = UA_UInt64_encodeBinary(&(src->publisherId.publisherIdUInt64), bufPos, bufEnd); break; case UA_PUBLISHERDATATYPE_STRING: rv = UA_String_encodeBinary(&(src->publisherId.publisherIdString), bufPos, bufEnd); break; default: rv = UA_STATUSCODE_BADINTERNALERROR; break; } if(rv != UA_STATUSCODE_GOOD) return rv; } // DataSetClassId if(src->dataSetClassIdEnabled) { rv = UA_Guid_encodeBinary(&(src->dataSetClassId), bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; } // Group Header if(src->groupHeaderEnabled) { v = 0; if(src->groupHeader.writerGroupIdEnabled) v |= GROUP_HEADER_WRITER_GROUPID_ENABLED; if(src->groupHeader.groupVersionEnabled) v |= GROUP_HEADER_GROUP_VERSION_ENABLED; if(src->groupHeader.networkMessageNumberEnabled) v |= GROUP_HEADER_NM_NUMBER_ENABLED; if(src->groupHeader.sequenceNumberEnabled) v |= GROUP_HEADER_SEQUENCE_NUMBER_ENABLED; rv = UA_Byte_encodeBinary(&v, bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; if(src->groupHeader.writerGroupIdEnabled) { rv = UA_UInt16_encodeBinary(&(src->groupHeader.writerGroupId), bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; } if(src->groupHeader.groupVersionEnabled) { rv = UA_UInt32_encodeBinary(&(src->groupHeader.groupVersion), bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; } if(src->groupHeader.networkMessageNumberEnabled) { rv = UA_UInt16_encodeBinary(&(src->groupHeader.networkMessageNumber), bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; } if(src->groupHeader.sequenceNumberEnabled) { rv = UA_UInt16_encodeBinary(&(src->groupHeader.sequenceNumber), bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; } } // Payload-Header if(src->payloadHeaderEnabled) { if(src->networkMessageType != UA_NETWORKMESSAGE_DATASET) return UA_STATUSCODE_BADNOTIMPLEMENTED; rv = UA_Byte_encodeBinary(&(src->payloadHeader.dataSetPayloadHeader.count), bufPos, bufEnd); if(src->payloadHeader.dataSetPayloadHeader.dataSetWriterIds == NULL) return UA_STATUSCODE_BADENCODINGERROR; for(UA_Byte i = 0; i < src->payloadHeader.dataSetPayloadHeader.count; i++) { rv = UA_UInt16_encodeBinary(&(src->payloadHeader.dataSetPayloadHeader.dataSetWriterIds[i]), bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; } } // Timestamp if(src->timestampEnabled) rv = UA_DateTime_encodeBinary(&(src->timestamp), bufPos, bufEnd); // Picoseconds if(src->picosecondsEnabled) rv = UA_UInt16_encodeBinary(&(src->picoseconds), bufPos, bufEnd); // PromotedFields if(src->promotedFieldsEnabled) { /* Size (calculate & encode) */ UA_UInt16 pfSize = 0; for(UA_UInt16 i = 0; i < src->promotedFieldsSize; i++) pfSize = (UA_UInt16) (pfSize + UA_Variant_calcSizeBinary(&src->promotedFields[i])); rv |= UA_UInt16_encodeBinary(&pfSize, bufPos, bufEnd); for (UA_UInt16 i = 0; i < src->promotedFieldsSize; i++) rv |= UA_Variant_encodeBinary(&(src->promotedFields[i]), bufPos, bufEnd); } // SecurityHeader if(src->securityEnabled) { // SecurityFlags v = 0; if(src->securityHeader.networkMessageSigned) v |= SECURITY_HEADER_NM_SIGNED; if(src->securityHeader.networkMessageEncrypted) v |= SECURITY_HEADER_NM_ENCRYPTED; if(src->securityHeader.securityFooterEnabled) v |= SECURITY_HEADER_SEC_FOOTER_ENABLED; if(src->securityHeader.forceKeyReset) v |= SECURITY_HEADER_FORCE_KEY_RESET; rv = UA_Byte_encodeBinary(&v, bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; // SecurityTokenId rv = UA_UInt32_encodeBinary(&src->securityHeader.securityTokenId, bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; // NonceLength rv = UA_Byte_encodeBinary(&src->securityHeader.nonceLength, bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; // MessageNonce for (UA_Byte i = 0; i < src->securityHeader.nonceLength; i++) { rv = UA_Byte_encodeBinary(&(src->securityHeader.messageNonce.data[i]), bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; } // SecurityFooterSize if(src->securityHeader.securityFooterEnabled) { rv = UA_UInt16_encodeBinary(&src->securityHeader.securityFooterSize, bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; } } // Payload if(src->networkMessageType != UA_NETWORKMESSAGE_DATASET) return UA_STATUSCODE_BADNOTIMPLEMENTED; UA_Byte count = 1; if(src->payloadHeaderEnabled) { count = src->payloadHeader.dataSetPayloadHeader.count; if(count > 1) { for (UA_Byte i = 0; i < count; i++) { // initially calculate the size, if not specified UA_UInt16 sz = 0; if((src->payload.dataSetPayload.sizes != NULL) && (src->payload.dataSetPayload.sizes[i] != 0)) { sz = src->payload.dataSetPayload.sizes[i]; } else { sz = (UA_UInt16)UA_DataSetMessage_calcSizeBinary(&src->payload.dataSetPayload.dataSetMessages[i]); } rv = UA_UInt16_encodeBinary(&sz, bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; } } } for(UA_Byte i = 0; i < count; i++) { rv = UA_DataSetMessage_encodeBinary(&(src->payload.dataSetPayload.dataSetMessages[i]), bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; } if(src->securityEnabled) { // SecurityFooter if(src->securityHeader.securityFooterEnabled) { for(UA_Byte i = 0; i < src->securityHeader.securityFooterSize; i++) { rv = UA_Byte_encodeBinary(&(src->securityFooter.data[i]), bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; } } // Signature if(src->securityHeader.networkMessageSigned) { rv = UA_ByteString_encodeBinary(&(src->signature), bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; } } return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_NetworkMessage_decodeBinaryInternal(const UA_ByteString *src, size_t *offset, UA_NetworkMessage* dst) { memset(dst, 0, sizeof(UA_NetworkMessage)); UA_Byte v = 0; UA_StatusCode rv = UA_Byte_decodeBinary(src, offset, &v); if(rv != UA_STATUSCODE_GOOD) return rv; dst->version = v & NM_VERSION_MASK; if((v & NM_PUBLISHER_ID_ENABLED_MASK) != 0) dst->publisherIdEnabled = true; if((v & NM_GROUP_HEADER_ENABLED_MASK) != 0) dst->groupHeaderEnabled = true; if((v & NM_PAYLOAD_HEADER_ENABLED_MASK) != 0) dst->payloadHeaderEnabled = true; if((v & NM_EXTENDEDFLAGS1_ENABLED_MASK) != 0) { v = 0; rv = UA_Byte_decodeBinary(src, offset, &v); if(rv != UA_STATUSCODE_GOOD) return rv; dst->publisherIdType = (UA_PublisherIdDatatype)(v & NM_PUBLISHER_ID_MASK); if((v & NM_DATASET_CLASSID_ENABLED_MASK) != 0) dst->dataSetClassIdEnabled = true; if((v & NM_SECURITY_ENABLED_MASK) != 0) dst->securityEnabled = true; if((v & NM_TIMESTAMP_ENABLED_MASK) != 0) dst->timestampEnabled = true; if((v & NM_PICOSECONDS_ENABLED_MASK) != 0) dst->picosecondsEnabled = true; if((v & NM_EXTENDEDFLAGS2_ENABLED_MASK) != 0) { v = 0; rv = UA_Byte_decodeBinary(src, offset, &v); if(rv != UA_STATUSCODE_GOOD) return rv; if((v & NM_CHUNK_MESSAGE_MASK) != 0) dst->chunkMessage = true; if((v & NM_PROMOTEDFIELDS_ENABLED_MASK) != 0) dst->promotedFieldsEnabled = true; v = v & NM_NETWORK_MSG_TYPE_MASK; v = (UA_Byte) (v >> NM_SHIFT_LEN); dst->networkMessageType = (UA_NetworkMessageType)v; } } if(dst->publisherIdEnabled) { switch (dst->publisherIdType) { case UA_PUBLISHERDATATYPE_BYTE: rv = UA_Byte_decodeBinary(src, offset, &(dst->publisherId.publisherIdByte)); break; case UA_PUBLISHERDATATYPE_UINT16: rv = UA_UInt16_decodeBinary(src, offset, &(dst->publisherId.publisherIdUInt16)); break; case UA_PUBLISHERDATATYPE_UINT32: rv = UA_UInt32_decodeBinary(src, offset, &(dst->publisherId.publisherIdUInt32)); break; case UA_PUBLISHERDATATYPE_UINT64: rv = UA_UInt64_decodeBinary(src, offset, &(dst->publisherId.publisherIdUInt64)); break; case UA_PUBLISHERDATATYPE_STRING: rv = UA_String_decodeBinary(src, offset, &(dst->publisherId.publisherIdString)); break; default: rv = UA_STATUSCODE_BADINTERNALERROR; break; } if(rv != UA_STATUSCODE_GOOD) return rv; } if(dst->dataSetClassIdEnabled) { rv = UA_Guid_decodeBinary(src, offset, &(dst->dataSetClassId)); if(rv != UA_STATUSCODE_GOOD) return rv; } // GroupHeader if(dst->groupHeaderEnabled) { v = 0; rv = UA_Byte_decodeBinary(src, offset, &v); if(rv != UA_STATUSCODE_GOOD) return rv; if((v & GROUP_HEADER_WRITER_GROUPID_ENABLED) != 0) dst->groupHeader.writerGroupIdEnabled = true; if((v & GROUP_HEADER_GROUP_VERSION_ENABLED) != 0) dst->groupHeader.groupVersionEnabled = true; if((v & GROUP_HEADER_NM_NUMBER_ENABLED) != 0) dst->groupHeader.networkMessageNumberEnabled = true; if((v & GROUP_HEADER_SEQUENCE_NUMBER_ENABLED) != 0) dst->groupHeader.sequenceNumberEnabled = true; if(dst->groupHeader.writerGroupIdEnabled) { rv = UA_UInt16_decodeBinary(src, offset, &dst->groupHeader.writerGroupId); if(rv != UA_STATUSCODE_GOOD) return rv; } if(dst->groupHeader.groupVersionEnabled) { rv = UA_UInt32_decodeBinary(src, offset, &dst->groupHeader.groupVersion); if(rv != UA_STATUSCODE_GOOD) return rv; } if(dst->groupHeader.networkMessageNumberEnabled) { rv = UA_UInt16_decodeBinary(src, offset, &dst->groupHeader.networkMessageNumber); if(rv != UA_STATUSCODE_GOOD) return rv; } if(dst->groupHeader.sequenceNumberEnabled) { rv = UA_UInt16_decodeBinary(src, offset, &dst->groupHeader.sequenceNumber); if(rv != UA_STATUSCODE_GOOD) return rv; } } // Payload-Header if(dst->payloadHeaderEnabled) { if(dst->networkMessageType != UA_NETWORKMESSAGE_DATASET) return UA_STATUSCODE_BADNOTIMPLEMENTED; rv = UA_Byte_decodeBinary(src, offset, &dst->payloadHeader.dataSetPayloadHeader.count); if(rv != UA_STATUSCODE_GOOD) return rv; dst->payloadHeader.dataSetPayloadHeader.dataSetWriterIds = (UA_UInt16 *)UA_Array_new(dst->payloadHeader.dataSetPayloadHeader.count, &UA_TYPES[UA_TYPES_UINT16]); for (UA_Byte i = 0; i < dst->payloadHeader.dataSetPayloadHeader.count; i++) { rv = UA_UInt16_decodeBinary(src, offset, &dst->payloadHeader.dataSetPayloadHeader.dataSetWriterIds[i]); if(rv != UA_STATUSCODE_GOOD) return rv; } } // Timestamp if(dst->timestampEnabled) { rv = UA_DateTime_decodeBinary(src, offset, &(dst->timestamp)); if(rv != UA_STATUSCODE_GOOD) return rv; } // Picoseconds if(dst->picosecondsEnabled) { rv = UA_UInt16_decodeBinary(src, offset, &(dst->picoseconds)); if(rv != UA_STATUSCODE_GOOD) return rv; } // PromotedFields if(dst->promotedFieldsEnabled) { // Size UA_UInt16 promotedFieldsSize = 0; rv = UA_UInt16_decodeBinary(src, offset, &promotedFieldsSize); if(rv != UA_STATUSCODE_GOOD) return rv; // promotedFieldsSize: here size in Byte, not the number of objects! if(promotedFieldsSize > 0) { // store offset, later compared with promotedFieldsSize size_t offsetEnd = (*offset) + promotedFieldsSize; unsigned int counter = 0; do { if(counter == 0) { dst->promotedFields = (UA_Variant*)UA_malloc(UA_TYPES[UA_TYPES_VARIANT].memSize); // set promotedFieldsSize to the number of objects dst->promotedFieldsSize = (UA_UInt16) (counter + 1); } else { dst->promotedFields = (UA_Variant*) UA_realloc(dst->promotedFields, UA_TYPES[UA_TYPES_VARIANT].memSize * (counter + 1)); // set promotedFieldsSize to the number of objects dst->promotedFieldsSize = (UA_UInt16) (counter + 1); } UA_Variant_init(&dst->promotedFields[counter]); rv = UA_Variant_decodeBinary(src, offset, &dst->promotedFields[counter]); if(rv != UA_STATUSCODE_GOOD) return rv; counter++; } while ((*offset) < offsetEnd); } } // SecurityHeader if(dst->securityEnabled) { // SecurityFlags v = 0; rv = UA_Byte_decodeBinary(src, offset, &v); if(rv != UA_STATUSCODE_GOOD) return rv; if((v & SECURITY_HEADER_NM_SIGNED) != 0) dst->securityHeader.networkMessageSigned = true; if((v & SECURITY_HEADER_NM_ENCRYPTED) != 0) dst->securityHeader.networkMessageEncrypted = true; if((v & SECURITY_HEADER_SEC_FOOTER_ENABLED) != 0) dst->securityHeader.securityFooterEnabled = true; if((v & SECURITY_HEADER_FORCE_KEY_RESET) != 0) dst->securityHeader.forceKeyReset = true; // SecurityTokenId rv = UA_UInt32_decodeBinary(src, offset, &dst->securityHeader.securityTokenId); if(rv != UA_STATUSCODE_GOOD) return rv; // NonceLength rv = UA_Byte_decodeBinary(src, offset, &dst->securityHeader.nonceLength); if(rv != UA_STATUSCODE_GOOD) return rv; // MessageNonce if(dst->securityHeader.nonceLength > 0) { rv = UA_ByteString_allocBuffer(&dst->securityHeader.messageNonce, dst->securityHeader.nonceLength); if(rv != UA_STATUSCODE_GOOD) return rv; for (UA_Byte i = 0; i < dst->securityHeader.nonceLength; i++) { rv = UA_Byte_decodeBinary(src, offset, &(dst->securityHeader.messageNonce.data[i])); if(rv != UA_STATUSCODE_GOOD) return rv; } } // SecurityFooterSize if(dst->securityHeader.securityFooterEnabled) { rv = UA_UInt16_decodeBinary(src, offset, &dst->securityHeader.securityFooterSize); if(rv != UA_STATUSCODE_GOOD) return rv; } } // Payload if(dst->networkMessageType != UA_NETWORKMESSAGE_DATASET) return UA_STATUSCODE_BADNOTIMPLEMENTED; UA_Byte count = 1; if(dst->payloadHeaderEnabled) { count = dst->payloadHeader.dataSetPayloadHeader.count; if(count > 1) { dst->payload.dataSetPayload.sizes = (UA_UInt16 *)UA_Array_new(count, &UA_TYPES[UA_TYPES_UINT16]); for (UA_Byte i = 0; i < count; i++) { rv = UA_UInt16_decodeBinary(src, offset, &(dst->payload.dataSetPayload.sizes[i])); if(rv != UA_STATUSCODE_GOOD) return rv; } } } dst->payload.dataSetPayload.dataSetMessages = (UA_DataSetMessage*) UA_calloc(count, sizeof(UA_DataSetMessage)); for(UA_Byte i = 0; i < count; i++) { rv = UA_DataSetMessage_decodeBinary(src, offset, &(dst->payload.dataSetPayload.dataSetMessages[i])); if(rv != UA_STATUSCODE_GOOD) return rv; } if(rv != UA_STATUSCODE_GOOD) return rv; if(dst->securityEnabled) { // SecurityFooter if(dst->securityHeader.securityFooterEnabled && (dst->securityHeader.securityFooterSize > 0)) { rv = UA_ByteString_allocBuffer(&dst->securityFooter, dst->securityHeader.securityFooterSize); if (rv != UA_STATUSCODE_GOOD) return rv; for (UA_Byte i = 0; i < dst->securityHeader.securityFooterSize; i++) { rv = UA_Byte_decodeBinary(src, offset, &(dst->securityFooter.data[i])); if (rv != UA_STATUSCODE_GOOD) return rv; } } // Signature if(dst->securityHeader.networkMessageSigned) { rv = UA_ByteString_decodeBinary(src, offset, &(dst->signature)); if (rv != UA_STATUSCODE_GOOD) return rv; } } return UA_STATUSCODE_GOOD; } UA_StatusCode UA_NetworkMessage_decodeBinary(const UA_ByteString *src, size_t *offset, UA_NetworkMessage* dst) { UA_StatusCode retval = UA_NetworkMessage_decodeBinaryInternal(src, offset, dst); if(retval != UA_STATUSCODE_GOOD) UA_NetworkMessage_deleteMembers(dst); return retval; } size_t UA_NetworkMessage_calcSizeBinary(const UA_NetworkMessage* p) { size_t retval = 0; UA_Byte byte; size_t size = UA_Byte_calcSizeBinary(&byte); // UADPVersion + UADPFlags if(UA_NetworkMessage_ExtendedFlags1Enabled(p)) { size += UA_Byte_calcSizeBinary(&byte); if(UA_NetworkMessage_ExtendedFlags2Enabled(p)) size += UA_Byte_calcSizeBinary(&byte); } if(p->publisherIdEnabled) { switch (p->publisherIdType) { case UA_PUBLISHERDATATYPE_BYTE: size += UA_Byte_calcSizeBinary(&p->publisherId.publisherIdByte); break; case UA_PUBLISHERDATATYPE_UINT16: size += UA_UInt16_calcSizeBinary(&p->publisherId.publisherIdUInt16); break; case UA_PUBLISHERDATATYPE_UINT32: size += UA_UInt32_calcSizeBinary(&p->publisherId.publisherIdUInt32); break; case UA_PUBLISHERDATATYPE_UINT64: size += UA_UInt64_calcSizeBinary(&p->publisherId.publisherIdUInt64); break; case UA_PUBLISHERDATATYPE_STRING: size += UA_String_calcSizeBinary(&p->publisherId.publisherIdString); break; } } if(p->dataSetClassIdEnabled) size += UA_Guid_calcSizeBinary(&p->dataSetClassId); // Group Header if(p->groupHeaderEnabled) { size += UA_Byte_calcSizeBinary(&byte); if(p->groupHeader.writerGroupIdEnabled) size += UA_UInt16_calcSizeBinary(&p->groupHeader.writerGroupId); if(p->groupHeader.groupVersionEnabled) size += UA_UInt32_calcSizeBinary(&p->groupHeader.groupVersion); if(p->groupHeader.networkMessageNumberEnabled) size += UA_UInt16_calcSizeBinary(&p->groupHeader.networkMessageNumber); if(p->groupHeader.sequenceNumberEnabled) size += UA_UInt16_calcSizeBinary(&p->groupHeader.sequenceNumber); } // Payload Header if(p->payloadHeaderEnabled) { if(p->networkMessageType == UA_NETWORKMESSAGE_DATASET) { size += UA_Byte_calcSizeBinary(&p->payloadHeader.dataSetPayloadHeader.count); if(p->payloadHeader.dataSetPayloadHeader.dataSetWriterIds != NULL) { size += UA_UInt16_calcSizeBinary(&p->payloadHeader.dataSetPayloadHeader.dataSetWriterIds[0]) * p->payloadHeader.dataSetPayloadHeader.count; } else { return 0; /* no dataSetWriterIds given! */ } } else { // not implemented } } if(p->timestampEnabled) size += UA_DateTime_calcSizeBinary(&p->timestamp); if(p->picosecondsEnabled) size += UA_UInt16_calcSizeBinary(&p->picoseconds); if(p->promotedFieldsEnabled) { size += UA_UInt16_calcSizeBinary(&p->promotedFieldsSize); for (UA_UInt16 i = 0; i < p->promotedFieldsSize; i++) size += UA_Variant_calcSizeBinary(&p->promotedFields[i]); } if(p->securityEnabled) { size += UA_Byte_calcSizeBinary(&byte); size += UA_UInt32_calcSizeBinary(&p->securityHeader.securityTokenId); size += UA_Byte_calcSizeBinary(&p->securityHeader.nonceLength); if(p->securityHeader.nonceLength > 0) size += (UA_Byte_calcSizeBinary(&p->securityHeader.messageNonce.data[0]) * p->securityHeader.nonceLength); if(p->securityHeader.securityFooterEnabled) size += UA_UInt16_calcSizeBinary(&p->securityHeader.securityFooterSize); } if(p->networkMessageType == UA_NETWORKMESSAGE_DATASET) { UA_Byte count = 1; if(p->payloadHeaderEnabled) { count = p->payloadHeader.dataSetPayloadHeader.count; if(count > 1) size += UA_UInt16_calcSizeBinary(&(p->payload.dataSetPayload.sizes[0])) * count; } for (size_t i = 0; i < count; i++) size += UA_DataSetMessage_calcSizeBinary(&(p->payload.dataSetPayload.dataSetMessages[i])); } if (p->securityEnabled) { if (p->securityHeader.securityFooterEnabled) size += p->securityHeader.securityFooterSize; if (p->securityHeader.networkMessageSigned) size += UA_ByteString_calcSizeBinary(&p->signature); } retval = size; return retval; } void UA_NetworkMessage_deleteMembers(UA_NetworkMessage* p) { if(p->promotedFieldsEnabled) UA_Array_delete(p->promotedFields, p->promotedFieldsSize, &UA_TYPES[UA_TYPES_VARIANT]); if(p->securityEnabled && (p->securityHeader.nonceLength > 0)) UA_ByteString_deleteMembers(&p->securityHeader.messageNonce); if(p->networkMessageType == UA_NETWORKMESSAGE_DATASET) { if(p->payloadHeaderEnabled) { if(p->payloadHeader.dataSetPayloadHeader.dataSetWriterIds != NULL) { UA_Array_delete(p->payloadHeader.dataSetPayloadHeader.dataSetWriterIds, p->payloadHeader.dataSetPayloadHeader.count, &UA_TYPES[UA_TYPES_UINT16]); } if(p->payload.dataSetPayload.sizes != NULL) { UA_Array_delete(p->payload.dataSetPayload.sizes, p->payloadHeader.dataSetPayloadHeader.count, &UA_TYPES[UA_TYPES_UINT16]); } } if(p->payload.dataSetPayload.dataSetMessages != NULL) { UA_Byte count = 1; if(p->payloadHeaderEnabled) count = p->payloadHeader.dataSetPayloadHeader.count; for (size_t i = 0; i < count; i++) UA_DataSetMessage_free(&(p->payload.dataSetPayload.dataSetMessages[i])); UA_free(p->payload.dataSetPayload.dataSetMessages); } } if(p->securityHeader.securityFooterEnabled && (p->securityHeader.securityFooterSize > 0)) UA_ByteString_deleteMembers(&p->securityFooter); if(p->messageIdEnabled){ UA_String_deleteMembers(&p->messageId); } if(p->publisherIdEnabled && p->publisherIdType == UA_PUBLISHERDATATYPE_STRING){ UA_String_deleteMembers(&p->publisherId.publisherIdString); } memset(p, 0, sizeof(UA_NetworkMessage)); } void UA_NetworkMessage_delete(UA_NetworkMessage* p) { UA_NetworkMessage_deleteMembers(p); } UA_Boolean UA_NetworkMessage_ExtendedFlags1Enabled(const UA_NetworkMessage* src) { UA_Boolean retval = false; if((src->publisherIdType != UA_PUBLISHERDATATYPE_BYTE) || src->dataSetClassIdEnabled || src->securityEnabled || src->timestampEnabled || src->picosecondsEnabled || UA_NetworkMessage_ExtendedFlags2Enabled(src)) { retval = true; } return retval; } UA_Boolean UA_NetworkMessage_ExtendedFlags2Enabled(const UA_NetworkMessage* src) { if(src->chunkMessage || src->promotedFieldsEnabled || src->networkMessageType != UA_NETWORKMESSAGE_DATASET) return true; return false; } UA_Boolean UA_DataSetMessageHeader_DataSetFlags2Enabled(const UA_DataSetMessageHeader* src) { if(src->dataSetMessageType != UA_DATASETMESSAGE_DATAKEYFRAME || src->timestampEnabled || src->picoSecondsIncluded) return true; return false; } UA_StatusCode UA_DataSetMessageHeader_encodeBinary(const UA_DataSetMessageHeader* src, UA_Byte **bufPos, const UA_Byte *bufEnd) { UA_Byte v; // DataSetFlags1 v = (UA_Byte)src->fieldEncoding; // shift left 1 bit v = (UA_Byte)(v << DS_MH_SHIFT_LEN); if(src->dataSetMessageValid) v |= DS_MESSAGEHEADER_DS_MSG_VALID; if(src->dataSetMessageSequenceNrEnabled) v |= DS_MESSAGEHEADER_SEQ_NR_ENABLED_MASK; if(src->statusEnabled) v |= DS_MESSAGEHEADER_STATUS_ENABLED_MASK; if(src->configVersionMajorVersionEnabled) v |= DS_MESSAGEHEADER_CONFIGMAJORVERSION_ENABLED_MASK; if(src->configVersionMinorVersionEnabled) v |= DS_MESSAGEHEADER_CONFIGMINORVERSION_ENABLED_MASK; if(UA_DataSetMessageHeader_DataSetFlags2Enabled(src)) v |= DS_MESSAGEHEADER_FLAGS2_ENABLED_MASK; UA_StatusCode rv = UA_Byte_encodeBinary(&v, bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; // DataSetFlags2 if(UA_DataSetMessageHeader_DataSetFlags2Enabled(src)) { v = (UA_Byte)src->dataSetMessageType; if(src->timestampEnabled) v |= DS_MESSAGEHEADER_TIMESTAMP_ENABLED_MASK; if(src->picoSecondsIncluded) v |= DS_MESSAGEHEADER_PICOSECONDS_INCLUDED_MASK; rv = UA_Byte_encodeBinary(&v, bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; } // DataSetMessageSequenceNr if(src->dataSetMessageSequenceNrEnabled) { rv = UA_UInt16_encodeBinary(&src->dataSetMessageSequenceNr, bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; } // Timestamp if(src->timestampEnabled) { rv = UA_DateTime_encodeBinary(&(src->timestamp), bufPos, bufEnd); /* UtcTime */ if(rv != UA_STATUSCODE_GOOD) return rv; } // PicoSeconds if(src->picoSecondsIncluded) { rv = UA_UInt16_encodeBinary(&(src->picoSeconds), bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; } // Status if(src->statusEnabled) { rv = UA_UInt16_encodeBinary(&(src->status), bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; } // ConfigVersionMajorVersion if(src->configVersionMajorVersionEnabled) { rv = UA_UInt32_encodeBinary(&(src->configVersionMajorVersion), bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; } // ConfigVersionMinorVersion if(src->configVersionMinorVersionEnabled) { rv = UA_UInt32_encodeBinary(&(src->configVersionMinorVersion), bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; } return UA_STATUSCODE_GOOD; } UA_StatusCode UA_DataSetMessageHeader_decodeBinary(const UA_ByteString *src, size_t *offset, UA_DataSetMessageHeader* dst) { memset(dst, 0, sizeof(UA_DataSetMessageHeader)); UA_Byte v = 0; UA_StatusCode rv = UA_Byte_decodeBinary(src, offset, &v); if(rv != UA_STATUSCODE_GOOD) return rv; UA_Byte v2 = v & DS_MESSAGEHEADER_FIELD_ENCODING_MASK; v2 = (UA_Byte)(v2 >> DS_MH_SHIFT_LEN); dst->fieldEncoding = (UA_FieldEncoding)v2; if((v & DS_MESSAGEHEADER_DS_MSG_VALID) != 0) dst->dataSetMessageValid = true; if((v & DS_MESSAGEHEADER_SEQ_NR_ENABLED_MASK) != 0) dst->dataSetMessageSequenceNrEnabled = true; if((v & DS_MESSAGEHEADER_STATUS_ENABLED_MASK) != 0) dst->statusEnabled = true; if((v & DS_MESSAGEHEADER_CONFIGMAJORVERSION_ENABLED_MASK) != 0) dst->configVersionMajorVersionEnabled = true; if((v & DS_MESSAGEHEADER_CONFIGMINORVERSION_ENABLED_MASK) != 0) dst->configVersionMinorVersionEnabled = true; if((v & DS_MESSAGEHEADER_FLAGS2_ENABLED_MASK) != 0) { v = 0; rv = UA_Byte_decodeBinary(src, offset, &v); if(rv != UA_STATUSCODE_GOOD) return rv; dst->dataSetMessageType = (UA_DataSetMessageType)(v & DS_MESSAGEHEADER_DS_MESSAGE_TYPE_MASK); if((v & DS_MESSAGEHEADER_TIMESTAMP_ENABLED_MASK) != 0) dst->timestampEnabled = true; if((v & DS_MESSAGEHEADER_PICOSECONDS_INCLUDED_MASK) != 0) dst->picoSecondsIncluded = true; } else { dst->dataSetMessageType = UA_DATASETMESSAGE_DATAKEYFRAME; dst->picoSecondsIncluded = false; } if(dst->dataSetMessageSequenceNrEnabled) { rv = UA_UInt16_decodeBinary(src, offset, &dst->dataSetMessageSequenceNr); if(rv != UA_STATUSCODE_GOOD) return rv; } else { dst->dataSetMessageSequenceNr = 0; } if(dst->timestampEnabled) { rv = UA_DateTime_decodeBinary(src, offset, &dst->timestamp); /* UtcTime */ if(rv != UA_STATUSCODE_GOOD) return rv; } else { dst->timestamp = 0; } if(dst->picoSecondsIncluded) { rv = UA_UInt16_decodeBinary(src, offset, &dst->picoSeconds); if(rv != UA_STATUSCODE_GOOD) return rv; } else { dst->picoSeconds = 0; } if(dst->statusEnabled) { rv = UA_UInt16_decodeBinary(src, offset, &dst->status); if(rv != UA_STATUSCODE_GOOD) return rv; } else { dst->status = 0; } if(dst->configVersionMajorVersionEnabled) { rv = UA_UInt32_decodeBinary(src, offset, &dst->configVersionMajorVersion); if(rv != UA_STATUSCODE_GOOD) return rv; } else { dst->configVersionMajorVersion = 0; } if(dst->configVersionMinorVersionEnabled) { rv = UA_UInt32_decodeBinary(src, offset, &dst->configVersionMinorVersion); if(rv != UA_STATUSCODE_GOOD) return rv; } else { dst->configVersionMinorVersion = 0; } return UA_STATUSCODE_GOOD; } size_t UA_DataSetMessageHeader_calcSizeBinary(const UA_DataSetMessageHeader* p) { UA_Byte byte; size_t size = UA_Byte_calcSizeBinary(&byte); // DataSetMessage Type + Flags if(UA_DataSetMessageHeader_DataSetFlags2Enabled(p)) size += UA_Byte_calcSizeBinary(&byte); if(p->dataSetMessageSequenceNrEnabled) size += UA_UInt16_calcSizeBinary(&p->dataSetMessageSequenceNr); if(p->timestampEnabled) size += UA_DateTime_calcSizeBinary(&p->timestamp); /* UtcTime */ if(p->picoSecondsIncluded) size += UA_UInt16_calcSizeBinary(&p->picoSeconds); if(p->statusEnabled) size += UA_UInt16_calcSizeBinary(&p->status); if(p->configVersionMajorVersionEnabled) size += UA_UInt32_calcSizeBinary(&p->configVersionMajorVersion); if(p->configVersionMinorVersionEnabled) size += UA_UInt32_calcSizeBinary(&p->configVersionMinorVersion); return size; } UA_StatusCode UA_DataSetMessage_encodeBinary(const UA_DataSetMessage* src, UA_Byte **bufPos, const UA_Byte *bufEnd) { UA_StatusCode rv = UA_DataSetMessageHeader_encodeBinary(&src->header, bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; if(src->header.dataSetMessageType == UA_DATASETMESSAGE_DATAKEYFRAME) { if(src->header.fieldEncoding != UA_FIELDENCODING_RAWDATA) { rv = UA_UInt16_encodeBinary(&(src->data.keyFrameData.fieldCount), bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; } if(src->header.fieldEncoding == UA_FIELDENCODING_VARIANT) { for (UA_UInt16 i = 0; i < src->data.keyFrameData.fieldCount; i++) { rv = UA_Variant_encodeBinary(&(src->data.keyFrameData.dataSetFields[i].value), bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; } } else if(src->header.fieldEncoding == UA_FIELDENCODING_RAWDATA) { return UA_STATUSCODE_BADNOTIMPLEMENTED; } else if(src->header.fieldEncoding == UA_FIELDENCODING_DATAVALUE) { for (UA_UInt16 i = 0; i < src->data.keyFrameData.fieldCount; i++) { rv = UA_DataValue_encodeBinary(&(src->data.keyFrameData.dataSetFields[i]), bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; } } } else if(src->header.dataSetMessageType == UA_DATASETMESSAGE_DATADELTAFRAME) { // Encode Delta Frame // Here the FieldCount is always present rv = UA_UInt16_encodeBinary(&(src->data.keyFrameData.fieldCount), bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; if(src->header.fieldEncoding == UA_FIELDENCODING_VARIANT) { for (UA_UInt16 i = 0; i < src->data.deltaFrameData.fieldCount; i++) { rv = UA_UInt16_encodeBinary(&(src->data.deltaFrameData.deltaFrameFields[i].fieldIndex), bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; rv = UA_Variant_encodeBinary(&(src->data.deltaFrameData.deltaFrameFields[i].fieldValue.value), bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; } } else if(src->header.fieldEncoding == UA_FIELDENCODING_RAWDATA) { return UA_STATUSCODE_BADNOTIMPLEMENTED; } else if(src->header.fieldEncoding == UA_FIELDENCODING_DATAVALUE) { for (UA_UInt16 i = 0; i < src->data.deltaFrameData.fieldCount; i++) { rv = UA_UInt16_encodeBinary(&(src->data.deltaFrameData.deltaFrameFields[i].fieldIndex), bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; rv = UA_DataValue_encodeBinary(&(src->data.deltaFrameData.deltaFrameFields[i].fieldValue), bufPos, bufEnd); if(rv != UA_STATUSCODE_GOOD) return rv; } } } else if(src->header.dataSetMessageType != UA_DATASETMESSAGE_KEEPALIVE) { return UA_STATUSCODE_BADNOTIMPLEMENTED; } /* Keep-Alive Message contains no Payload Data */ return UA_STATUSCODE_GOOD; } UA_StatusCode UA_DataSetMessage_decodeBinary(const UA_ByteString *src, size_t *offset, UA_DataSetMessage* dst) { memset(dst, 0, sizeof(UA_DataSetMessage)); UA_StatusCode rv = UA_DataSetMessageHeader_decodeBinary(src, offset, &dst->header); if(rv != UA_STATUSCODE_GOOD) return rv; if(dst->header.dataSetMessageType == UA_DATASETMESSAGE_DATAKEYFRAME) { if(dst->header.fieldEncoding != UA_FIELDENCODING_RAWDATA) { rv = UA_UInt16_decodeBinary(src, offset, &dst->data.keyFrameData.fieldCount); if(rv != UA_STATUSCODE_GOOD) return rv; if(dst->header.fieldEncoding == UA_FIELDENCODING_VARIANT) { dst->data.keyFrameData.dataSetFields = (UA_DataValue *)UA_Array_new(dst->data.keyFrameData.fieldCount, &UA_TYPES[UA_TYPES_DATAVALUE]); for (UA_UInt16 i = 0; i < dst->data.keyFrameData.fieldCount; i++) { UA_DataValue_init(&dst->data.keyFrameData.dataSetFields[i]); rv = UA_Variant_decodeBinary(src, offset, &dst->data.keyFrameData.dataSetFields[i].value); if(rv != UA_STATUSCODE_GOOD) return rv; dst->data.keyFrameData.dataSetFields[i].hasValue = true; } } else if(dst->header.fieldEncoding == UA_FIELDENCODING_RAWDATA) { return UA_STATUSCODE_BADNOTIMPLEMENTED; } else if(dst->header.fieldEncoding == UA_FIELDENCODING_DATAVALUE) { dst->data.keyFrameData.dataSetFields = (UA_DataValue *)UA_Array_new(dst->data.keyFrameData.fieldCount, &UA_TYPES[UA_TYPES_DATAVALUE]); for (UA_UInt16 i = 0; i < dst->data.keyFrameData.fieldCount; i++) { rv = UA_DataValue_decodeBinary(src, offset, &(dst->data.keyFrameData.dataSetFields[i])); if(rv != UA_STATUSCODE_GOOD) return rv; } } } } else if(dst->header.dataSetMessageType == UA_DATASETMESSAGE_DATADELTAFRAME) { if(dst->header.fieldEncoding != UA_FIELDENCODING_RAWDATA) { rv = UA_UInt16_decodeBinary(src, offset, &dst->data.deltaFrameData.fieldCount); if(rv != UA_STATUSCODE_GOOD) return rv; if(dst->header.fieldEncoding == UA_FIELDENCODING_VARIANT) { size_t memsize = sizeof(UA_DataSetMessage_DeltaFrameField) * dst->data.deltaFrameData.fieldCount; dst->data.deltaFrameData.deltaFrameFields = (UA_DataSetMessage_DeltaFrameField*)UA_malloc(memsize); for (UA_UInt16 i = 0; i < dst->data.deltaFrameData.fieldCount; i++) { rv = UA_UInt16_decodeBinary(src, offset, &dst->data.deltaFrameData.deltaFrameFields[i].fieldIndex); if(rv != UA_STATUSCODE_GOOD) return rv; UA_DataValue_init(&dst->data.deltaFrameData.deltaFrameFields[i].fieldValue); rv = UA_Variant_decodeBinary(src, offset, &dst->data.deltaFrameData.deltaFrameFields[i].fieldValue.value); if(rv != UA_STATUSCODE_GOOD) return rv; dst->data.deltaFrameData.deltaFrameFields[i].fieldValue.hasValue = true; } } else if(dst->header.fieldEncoding == UA_FIELDENCODING_RAWDATA) { return UA_STATUSCODE_BADNOTIMPLEMENTED; } else if(dst->header.fieldEncoding == UA_FIELDENCODING_DATAVALUE) { size_t memsize = sizeof(UA_DataSetMessage_DeltaFrameField) * dst->data.deltaFrameData.fieldCount; dst->data.deltaFrameData.deltaFrameFields = (UA_DataSetMessage_DeltaFrameField*)UA_malloc(memsize); for (UA_UInt16 i = 0; i < dst->data.deltaFrameData.fieldCount; i++) { rv = UA_UInt16_decodeBinary(src, offset, &dst->data.deltaFrameData.deltaFrameFields[i].fieldIndex); if(rv != UA_STATUSCODE_GOOD) return rv; rv = UA_DataValue_decodeBinary(src, offset, &(dst->data.deltaFrameData.deltaFrameFields[i].fieldValue)); if(rv != UA_STATUSCODE_GOOD) return rv; } } } } else if(dst->header.dataSetMessageType != UA_DATASETMESSAGE_KEEPALIVE) { return UA_STATUSCODE_BADNOTIMPLEMENTED; } /* Keep-Alive Message contains no Payload Data */ return UA_STATUSCODE_GOOD; } size_t UA_DataSetMessage_calcSizeBinary(const UA_DataSetMessage* p) { size_t size = UA_DataSetMessageHeader_calcSizeBinary(&p->header); if(p->header.dataSetMessageType == UA_DATASETMESSAGE_DATAKEYFRAME) { if(p->header.fieldEncoding != UA_FIELDENCODING_RAWDATA) size += UA_calcSizeBinary(&p->data.keyFrameData.fieldCount, &UA_TYPES[UA_TYPES_UINT16]); if(p->header.fieldEncoding == UA_FIELDENCODING_VARIANT) { for (UA_UInt16 i = 0; i < p->data.keyFrameData.fieldCount; i++) size += UA_calcSizeBinary(&p->data.keyFrameData.dataSetFields[i].value, &UA_TYPES[UA_TYPES_VARIANT]); } else if(p->header.fieldEncoding == UA_FIELDENCODING_RAWDATA) { // not implemented } else if(p->header.fieldEncoding == UA_FIELDENCODING_DATAVALUE) { for (UA_UInt16 i = 0; i < p->data.keyFrameData.fieldCount; i++) size += UA_calcSizeBinary(&p->data.keyFrameData.dataSetFields[i], &UA_TYPES[UA_TYPES_DATAVALUE]); } } else if(p->header.dataSetMessageType == UA_DATASETMESSAGE_DATADELTAFRAME) { if(p->header.fieldEncoding != UA_FIELDENCODING_RAWDATA) size += UA_calcSizeBinary(&p->data.deltaFrameData.fieldCount, &UA_TYPES[UA_TYPES_UINT16]); if(p->header.fieldEncoding == UA_FIELDENCODING_VARIANT) { for (UA_UInt16 i = 0; i < p->data.deltaFrameData.fieldCount; i++) { size += UA_calcSizeBinary(&p->data.deltaFrameData.deltaFrameFields[i].fieldIndex, &UA_TYPES[UA_TYPES_UINT16]); size += UA_calcSizeBinary(&p->data.deltaFrameData.deltaFrameFields[i].fieldValue.value, &UA_TYPES[UA_TYPES_VARIANT]); } } else if(p->header.fieldEncoding == UA_FIELDENCODING_RAWDATA) { // not implemented } else if(p->header.fieldEncoding == UA_FIELDENCODING_DATAVALUE) { for (UA_UInt16 i = 0; i < p->data.deltaFrameData.fieldCount; i++) { size += UA_calcSizeBinary(&p->data.deltaFrameData.deltaFrameFields[i].fieldIndex, &UA_TYPES[UA_TYPES_UINT16]); size += UA_calcSizeBinary(&p->data.deltaFrameData.deltaFrameFields[i].fieldValue, &UA_TYPES[UA_TYPES_DATAVALUE]); } } } /* KeepAlive-Message contains no Payload Data */ return size; } void UA_DataSetMessage_free(const UA_DataSetMessage* p) { if(p->header.dataSetMessageType == UA_DATASETMESSAGE_DATAKEYFRAME) { if(p->data.keyFrameData.dataSetFields != NULL) UA_Array_delete(p->data.keyFrameData.dataSetFields, p->data.keyFrameData.fieldCount, &UA_TYPES[UA_TYPES_DATAVALUE]); /* Json keys */ if(p->data.keyFrameData.fieldNames != NULL){ UA_Array_delete(p->data.keyFrameData.fieldNames, p->data.keyFrameData.fieldCount, &UA_TYPES[UA_TYPES_STRING]); } } else if(p->header.dataSetMessageType == UA_DATASETMESSAGE_DATADELTAFRAME) { if(p->data.deltaFrameData.deltaFrameFields != NULL) { for(UA_UInt16 i = 0; i < p->data.deltaFrameData.fieldCount; i++) { if(p->header.fieldEncoding == UA_FIELDENCODING_DATAVALUE) { UA_DataValue_deleteMembers(&p->data.deltaFrameData.deltaFrameFields[i].fieldValue); } else if(p->header.fieldEncoding == UA_FIELDENCODING_VARIANT) { UA_Variant_deleteMembers(&p->data.deltaFrameData.deltaFrameFields[i].fieldValue.value); } } UA_free(p->data.deltaFrameData.deltaFrameFields); } } } #endif /* UA_ENABLE_PUBSUB */ /*********************************** amalgamated original file "/home/jvoe/open62541/src/pubsub/ua_pubsub.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright (c) 2017-2018 Fraunhofer IOSB (Author: Andreas Ebner) * Copyright (c) 2019 Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright (c) 2019 Kalycito Infotech Private Limited */ #ifdef UA_ENABLE_PUBSUB /* conditional compilation */ #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL #endif #ifdef UA_ENABLE_PUBSUB_DELTAFRAMES #endif #define UA_MAX_STACKBUF 512 /* Max size of network messages on the stack */ #define UA_MAX_SIZENAME 64 /* Max size of Qualified Name of Subscribed Variable */ /* Forward declaration */ static void UA_WriterGroup_deleteMembers(UA_Server *server, UA_WriterGroup *writerGroup); static void UA_DataSetField_deleteMembers(UA_DataSetField *field); /**********************************************/ /* Connection */ /**********************************************/ UA_StatusCode UA_PubSubConnectionConfig_copy(const UA_PubSubConnectionConfig *src, UA_PubSubConnectionConfig *dst) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; memcpy(dst, src, sizeof(UA_PubSubConnectionConfig)); retVal |= UA_String_copy(&src->name, &dst->name); retVal |= UA_Variant_copy(&src->address, &dst->address); retVal |= UA_String_copy(&src->transportProfileUri, &dst->transportProfileUri); retVal |= UA_Variant_copy(&src->connectionTransportSettings, &dst->connectionTransportSettings); if(src->connectionPropertiesSize > 0){ dst->connectionProperties = (UA_KeyValuePair *) UA_calloc(src->connectionPropertiesSize, sizeof(UA_KeyValuePair)); if(!dst->connectionProperties){ return UA_STATUSCODE_BADOUTOFMEMORY; } for(size_t i = 0; i < src->connectionPropertiesSize; i++){ retVal |= UA_QualifiedName_copy(&src->connectionProperties[i].key, &dst->connectionProperties[i].key); retVal |= UA_Variant_copy(&src->connectionProperties[i].value, &dst->connectionProperties[i].value); } } return retVal; } UA_StatusCode UA_Server_getPubSubConnectionConfig(UA_Server *server, const UA_NodeId connection, UA_PubSubConnectionConfig *config) { if(!config) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_PubSubConnection *currentPubSubConnection = UA_PubSubConnection_findConnectionbyId(server, connection); if(!currentPubSubConnection) return UA_STATUSCODE_BADNOTFOUND; UA_PubSubConnectionConfig tmpPubSubConnectionConfig; //deep copy of the actual config UA_PubSubConnectionConfig_copy(currentPubSubConnection->config, &tmpPubSubConnectionConfig); *config = tmpPubSubConnectionConfig; return UA_STATUSCODE_GOOD; } UA_PubSubConnection * UA_PubSubConnection_findConnectionbyId(UA_Server *server, UA_NodeId connectionIdentifier) { for(size_t i = 0; i < server->pubSubManager.connectionsSize; i++){ if(UA_NodeId_equal(&connectionIdentifier, &server->pubSubManager.connections[i].identifier)){ return &server->pubSubManager.connections[i]; } } return NULL; } void UA_PubSubConnectionConfig_deleteMembers(UA_PubSubConnectionConfig *connectionConfig) { UA_String_deleteMembers(&connectionConfig->name); UA_String_deleteMembers(&connectionConfig->transportProfileUri); UA_Variant_deleteMembers(&connectionConfig->connectionTransportSettings); UA_Variant_deleteMembers(&connectionConfig->address); for(size_t i = 0; i < connectionConfig->connectionPropertiesSize; i++){ UA_QualifiedName_deleteMembers(&connectionConfig->connectionProperties[i].key); UA_Variant_deleteMembers(&connectionConfig->connectionProperties[i].value); } UA_free(connectionConfig->connectionProperties); } void UA_PubSubConnection_deleteMembers(UA_Server *server, UA_PubSubConnection *connection) { //delete connection config UA_PubSubConnectionConfig_deleteMembers(connection->config); //remove contained WriterGroups UA_WriterGroup *writerGroup, *tmpWriterGroup; LIST_FOREACH_SAFE(writerGroup, &connection->writerGroups, listEntry, tmpWriterGroup){ UA_Server_removeWriterGroup(server, writerGroup->identifier); } /* remove contained ReaderGroups */ UA_ReaderGroup *readerGroups, *tmpReaderGroup; LIST_FOREACH_SAFE(readerGroups, &connection->readerGroups, listEntry, tmpReaderGroup){ UA_Server_removeReaderGroup(server, readerGroups->identifier); } UA_NodeId_deleteMembers(&connection->identifier); if(connection->channel){ connection->channel->close(connection->channel); } UA_free(connection->config); } UA_StatusCode UA_Server_addWriterGroup(UA_Server *server, const UA_NodeId connection, const UA_WriterGroupConfig *writerGroupConfig, UA_NodeId *writerGroupIdentifier) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; if(!writerGroupConfig) return UA_STATUSCODE_BADINVALIDARGUMENT; //search the connection by the given connectionIdentifier UA_PubSubConnection *currentConnectionContext = UA_PubSubConnection_findConnectionbyId(server, connection); if(!currentConnectionContext) return UA_STATUSCODE_BADNOTFOUND; //allocate memory for new WriterGroup UA_WriterGroup *newWriterGroup = (UA_WriterGroup *) UA_calloc(1, sizeof(UA_WriterGroup)); if(!newWriterGroup) return UA_STATUSCODE_BADOUTOFMEMORY; newWriterGroup->linkedConnection = currentConnectionContext->identifier; UA_PubSubManager_generateUniqueNodeId(server, &newWriterGroup->identifier); if(writerGroupIdentifier){ UA_NodeId_copy(&newWriterGroup->identifier, writerGroupIdentifier); } //deep copy of the config UA_WriterGroupConfig tmpWriterGroupConfig; retVal |= UA_WriterGroupConfig_copy(writerGroupConfig, &tmpWriterGroupConfig); if(!tmpWriterGroupConfig.messageSettings.content.decoded.type) { UA_UadpWriterGroupMessageDataType *wgm = UA_UadpWriterGroupMessageDataType_new(); tmpWriterGroupConfig.messageSettings.content.decoded.data = wgm; tmpWriterGroupConfig.messageSettings.content.decoded.type = &UA_TYPES[UA_TYPES_UADPWRITERGROUPMESSAGEDATATYPE]; tmpWriterGroupConfig.messageSettings.encoding = UA_EXTENSIONOBJECT_DECODED; } newWriterGroup->config = tmpWriterGroupConfig; retVal |= UA_WriterGroup_addPublishCallback(server, newWriterGroup); LIST_INSERT_HEAD(¤tConnectionContext->writerGroups, newWriterGroup, listEntry); #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL addWriterGroupRepresentation(server, newWriterGroup); #endif return retVal; } UA_StatusCode UA_Server_removeWriterGroup(UA_Server *server, const UA_NodeId writerGroup){ UA_WriterGroup *wg = UA_WriterGroup_findWGbyId(server, writerGroup); if(!wg) return UA_STATUSCODE_BADNOTFOUND; UA_PubSubConnection *connection = UA_PubSubConnection_findConnectionbyId(server, wg->linkedConnection); if(!connection) return UA_STATUSCODE_BADNOTFOUND; //unregister the publish callback UA_PubSubManager_removeRepeatedPubSubCallback(server, wg->publishCallbackId); #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL removeGroupRepresentation(server, wg); #endif UA_WriterGroup_deleteMembers(server, wg); LIST_REMOVE(wg, listEntry); UA_free(wg); return UA_STATUSCODE_GOOD; } /**********************************************/ /* ReaderGroup */ /**********************************************/ /** * Add ReaderGroup to connection. * * @param server * @param connectionIdentifier * @param readerGroupConfiguration * @param readerGroupIdentifier * @return UA_STATUSCODE_GOOD on success */ UA_StatusCode UA_Server_addReaderGroup(UA_Server *server, UA_NodeId connectionIdentifier, const UA_ReaderGroupConfig *readerGroupConfig, UA_NodeId *readerGroupIdentifier) { UA_StatusCode retval = UA_STATUSCODE_GOOD; UA_ReaderGroupConfig tmpReaderGroupConfig; /* Search the connection by the given connectionIdentifier */ if(!readerGroupConfig) { return UA_STATUSCODE_BADINVALIDARGUMENT; } /* Search the connection by the given connectionIdentifier */ UA_PubSubConnection *currentConnectionContext = UA_PubSubConnection_findConnectionbyId(server, connectionIdentifier); if(!currentConnectionContext) { return UA_STATUSCODE_BADNOTFOUND; } /* Allocate memory for new reader group */ UA_ReaderGroup *newGroup = (UA_ReaderGroup *)UA_calloc(1, sizeof(UA_ReaderGroup)); if(!newGroup) { return UA_STATUSCODE_BADOUTOFMEMORY; } /* Generate nodeid for the readergroup identifier */ newGroup->linkedConnection = currentConnectionContext->identifier; UA_PubSubManager_generateUniqueNodeId(server, &newGroup->identifier); if(readerGroupIdentifier) { UA_NodeId_copy(&newGroup->identifier, readerGroupIdentifier); } /* Deep copy of the config */ retval |= UA_ReaderGroupConfig_copy(readerGroupConfig, &tmpReaderGroupConfig); newGroup->config = tmpReaderGroupConfig; retval |= UA_ReaderGroup_addSubscribeCallback(server, newGroup); LIST_INSERT_HEAD(¤tConnectionContext->readerGroups, newGroup, listEntry); currentConnectionContext->readerGroupsSize++; #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL addReaderGroupRepresentation(server, newGroup); #endif return retval; } /** * Remove ReaderGroup from connection and delete contained readers. * * @param server * @param groupIdentifier * @return UA_STATUSCODE_GOOD on success */ UA_StatusCode UA_Server_removeReaderGroup(UA_Server *server, UA_NodeId groupIdentifier) { UA_ReaderGroup* readerGroup = UA_ReaderGroup_findRGbyId(server, groupIdentifier); if(readerGroup == NULL) { return UA_STATUSCODE_BADNOTFOUND; } /* Search the connection to which the given readergroup is connected to */ UA_PubSubConnection *connection = UA_PubSubConnection_findConnectionbyId(server, readerGroup->linkedConnection); if(connection == NULL) { return UA_STATUSCODE_BADNOTFOUND; } /* Unregister subscribe callback */ UA_PubSubManager_removeRepeatedPubSubCallback(server, readerGroup->subscribeCallbackId); #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL /* To Do:RemoveGroupRepresentation(server, &readerGroup->identifier) */ #endif /* UA_Server_ReaderGroup_delete also removes itself from the list */ UA_Server_ReaderGroup_delete(server, readerGroup); /* Remove readerGroup from Connection */ LIST_REMOVE(readerGroup, listEntry); UA_free(readerGroup); return UA_STATUSCODE_GOOD; } /** * To Do: * Update ReaderGroup configuration. * * @param server * @param readerGroupIdentifier * @param readerGroupConfiguration * @return UA_STATUSCODE_GOOD on success */ UA_StatusCode UA_Server_ReaderGroup_updateConfig(UA_Server *server, UA_NodeId readerGroupIdentifier, const UA_ReaderGroupConfig *config) { return UA_STATUSCODE_BADNOTIMPLEMENTED; } /** * Get ReaderGroup configuration. * * @param server * @param groupIdentifier * @param readerGroupConfiguration * @return UA_STATUSCODE_GOOD on success */ UA_StatusCode UA_Server_ReaderGroup_getConfig(UA_Server *server, UA_NodeId readerGroupIdentifier, UA_ReaderGroupConfig *config) { if(!config) { return UA_STATUSCODE_BADINVALIDARGUMENT; } /* Identify the readergroup through the readerGroupIdentifier */ UA_ReaderGroup *currentReaderGroup = UA_ReaderGroup_findRGbyId(server, readerGroupIdentifier); if(!currentReaderGroup) { return UA_STATUSCODE_BADNOTFOUND; } UA_ReaderGroupConfig tmpReaderGroupConfig; /* deep copy of the actual config */ UA_ReaderGroupConfig_copy(¤tReaderGroup->config, &tmpReaderGroupConfig); *config = tmpReaderGroupConfig; return UA_STATUSCODE_GOOD; } /* To Do UA_ReaderGroupConfig delete */ /** * Delete ReaderGroup. * * @param server * @param groupIdentifier */ void UA_Server_ReaderGroup_delete(UA_Server* server, UA_ReaderGroup *readerGroup) { /* To Do Call UA_ReaderGroupConfig_delete */ UA_DataSetReader *dataSetReader, *tmpDataSetReader; LIST_FOREACH_SAFE(dataSetReader, &readerGroup->readers, listEntry, tmpDataSetReader) { UA_DataSetReader_delete(server, dataSetReader); } UA_PubSubConnection* pConn = UA_PubSubConnection_findConnectionbyId(server, readerGroup->linkedConnection); if(pConn != NULL) { pConn->readerGroupsSize--; } /* Delete ReaderGroup and its members */ UA_String_deleteMembers(&readerGroup->config.name); UA_NodeId_deleteMembers(&readerGroup->linkedConnection); UA_NodeId_deleteMembers(&readerGroup->identifier); } /** * Copy ReaderGroup configuration. * * @param source * @param destination * @return UA_STATUSCODE_GOOD on success */ UA_StatusCode UA_ReaderGroupConfig_copy(const UA_ReaderGroupConfig *src, UA_ReaderGroupConfig *dst) { UA_String_copy(&src->name, &dst->name); /* Currently simple memcpy only */ memcpy(&dst->securityParameters, &src->securityParameters, sizeof(UA_PubSubSecurityParameters)); return UA_STATUSCODE_GOOD; } static UA_DataSetReader * getReaderFromIdentifier(UA_Server *server, UA_NetworkMessage *pMsg, UA_PubSubConnection *pConnection) { if(pConnection->readerGroupsSize == 1) { if(LIST_FIRST(&pConnection->readerGroups)->readersCount == 1) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "only 1 DataSetReader available. This one will be used."); return LIST_FIRST(&LIST_FIRST(&pConnection->readerGroups)->readers); } } if(!pMsg->publisherIdEnabled) return NULL; UA_ReaderGroup* readerGroup; LIST_FOREACH(readerGroup, &pConnection->readerGroups, listEntry) { UA_DataSetReader *tmpReader; LIST_FOREACH(tmpReader, &readerGroup->readers, listEntry) { switch (pMsg->publisherIdType) { case UA_PUBLISHERDATATYPE_BYTE: if(tmpReader->config.publisherId.type == &UA_TYPES[UA_TYPES_BYTE] && pMsg->publisherIdType == UA_PUBLISHERDATATYPE_BYTE && pMsg->publisherId.publisherIdByte == *(UA_Byte*)tmpReader->config.publisherId.data) { return tmpReader; } break; case UA_PUBLISHERDATATYPE_UINT16: if(tmpReader->config.publisherId.type == &UA_TYPES[UA_TYPES_UINT16] && pMsg->publisherIdType == UA_PUBLISHERDATATYPE_UINT16 && pMsg->publisherId.publisherIdUInt16 == *(UA_UInt16*)tmpReader->config.publisherId.data) { return tmpReader; } break; case UA_PUBLISHERDATATYPE_UINT32: if(tmpReader->config.publisherId.type == &UA_TYPES[UA_TYPES_UINT32] && pMsg->publisherIdType == UA_PUBLISHERDATATYPE_UINT32 && pMsg->publisherId.publisherIdUInt32 == *(UA_UInt32*)tmpReader->config.publisherId.data) { return tmpReader; } break; case UA_PUBLISHERDATATYPE_UINT64: if(tmpReader->config.publisherId.type == &UA_TYPES[UA_TYPES_UINT64] && pMsg->publisherIdType == UA_PUBLISHERDATATYPE_UINT64 && pMsg->publisherId.publisherIdUInt64 == *(UA_UInt64*)tmpReader->config.publisherId.data) { return tmpReader; } break; case UA_PUBLISHERDATATYPE_STRING: if(tmpReader->config.publisherId.type == &UA_TYPES[UA_TYPES_STRING] && pMsg->publisherIdType == UA_PUBLISHERDATATYPE_STRING && UA_String_equal(&pMsg->publisherId.publisherIdString, (UA_String*)tmpReader->config.publisherId.data)) { return tmpReader; } break; default: return NULL; } } } return NULL; } /** * Process NetworkMessage. * * @param server * @param networkmessage * @return UA_STATUSCODE_GOOD on success */ UA_StatusCode UA_Server_processNetworkMessage(UA_Server *server, UA_NetworkMessage *pMsg, UA_PubSubConnection *pConnection) { if(!pMsg || !pConnection) return UA_STATUSCODE_BADINVALIDARGUMENT; /* To Do The condition with dataSetWriterIdAvailable and WriterGroupIdAvailable to be handled * when pMsg->groupHeaderEnabled, pMsg->dataSetClassIdEnabled, pMsg->payloadHeaderEnabled * Here some filtering is possible */ UA_DataSetReader* dataSetReaderErg = getReaderFromIdentifier(server, pMsg, pConnection); /* No Reader with the specified id found */ if(!dataSetReaderErg) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "No DataSetReader found with PublisherId"); return UA_STATUSCODE_BADNOTFOUND; /* TODO: Check the return code */ } UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "DataSetReader found with PublisherId"); UA_Byte anzDataSets = 1; if(pMsg->payloadHeaderEnabled) anzDataSets = pMsg->payloadHeader.dataSetPayloadHeader.count; for(UA_Byte iterator = 0; iterator < anzDataSets; iterator++) { UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "Process Msg with DataSetReader!"); UA_Server_DataSetReader_process(server, dataSetReaderErg, &pMsg->payload.dataSetPayload.dataSetMessages[iterator]); } /* To Do the condition with dataSetWriterId and WriterGroupId * else condition for dataSetWriterIdAvailable and writerGroupIdAvailable) */ return UA_STATUSCODE_GOOD; } /** * Find ReaderGroup with its identifier. * * @param server * @param groupIdentifier * @return the ReaderGroup or NULL if not found */ UA_ReaderGroup * UA_ReaderGroup_findRGbyId(UA_Server *server, UA_NodeId identifier) { for (size_t iteratorConn = 0; iteratorConn < server->pubSubManager.connectionsSize; iteratorConn++) { UA_ReaderGroup* readerGroup = NULL; LIST_FOREACH(readerGroup, &server->pubSubManager.connections[iteratorConn].readerGroups, listEntry) { if(UA_NodeId_equal(&identifier, &readerGroup->identifier)) { return readerGroup; } } } return NULL; } /** * Find a DataSetReader with its identifier * * @param server * @param identifier * @return the DataSetReader or NULL if not found */ UA_DataSetReader *UA_ReaderGroup_findDSRbyId(UA_Server *server, UA_NodeId identifier) { for (size_t iteratorConn = 0; iteratorConn < server->pubSubManager.connectionsSize; iteratorConn++) { UA_ReaderGroup* readerGroup = NULL; LIST_FOREACH(readerGroup, &server->pubSubManager.connections[iteratorConn].readerGroups, listEntry) { UA_DataSetReader *tmpReader; LIST_FOREACH(tmpReader, &readerGroup->readers, listEntry) { if(UA_NodeId_equal(&tmpReader->identifier, &identifier)) { return tmpReader; } } } } return NULL; } /**********************************************/ /* DataSetReader */ /**********************************************/ /** * Add a DataSetReader to ReaderGroup * * @param server * @param readerGroupIdentifier * @param dataSetReaderConfig * @param readerIdentifier * @return UA_STATUSCODE_GOOD on success */ UA_StatusCode UA_Server_addDataSetReader(UA_Server *server, UA_NodeId readerGroupIdentifier, const UA_DataSetReaderConfig *dataSetReaderConfig, UA_NodeId *readerIdentifier) { /* Search the reader group by the given readerGroupIdentifier */ UA_ReaderGroup *readerGroup = UA_ReaderGroup_findRGbyId(server, readerGroupIdentifier); if(!dataSetReaderConfig) { return UA_STATUSCODE_BADNOTFOUND; } if(readerGroup == NULL) { return UA_STATUSCODE_BADNOTFOUND; } /* Allocate memory for new DataSetReader */ UA_DataSetReader *newDataSetReader = (UA_DataSetReader *)UA_calloc(1, sizeof(UA_DataSetReader)); /* Copy the config into the new dataSetReader */ UA_DataSetReaderConfig_copy(dataSetReaderConfig, &newDataSetReader->config); newDataSetReader->linkedReaderGroup = readerGroup->identifier; UA_PubSubManager_generateUniqueNodeId(server, &newDataSetReader->identifier); if(readerIdentifier != NULL) { UA_NodeId_copy(&newDataSetReader->identifier, readerIdentifier); } /* Add the new reader to the group */ LIST_INSERT_HEAD(&readerGroup->readers, newDataSetReader, listEntry); readerGroup->readersCount++; #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL addDataSetReaderRepresentation(server, newDataSetReader); #endif return UA_STATUSCODE_GOOD; } /** * Remove a DataSetReader from ReaderGroup * * @param server * @param readerGroupIdentifier * @return UA_STATUSCODE_GOOD on success */ UA_StatusCode UA_Server_removeDataSetReader(UA_Server *server, UA_NodeId readerIdentifier) { /* Remove datasetreader given by the identifier */ UA_DataSetReader *dataSetReader = UA_ReaderGroup_findDSRbyId(server, readerIdentifier); if(!dataSetReader) { return UA_STATUSCODE_BADNOTFOUND; } #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL removeDataSetReaderRepresentation(server, dataSetReader); #endif UA_DataSetReader_delete(server, dataSetReader); return UA_STATUSCODE_GOOD; } /** * Update the config of the DataSetReader. * * @param server * @param dataSetReaderIdentifier * @param readerGroupIdentifier * @param config * @return UA_STATUSCODE_GOOD on success */ UA_StatusCode UA_Server_DataSetReader_updateConfig(UA_Server *server, UA_NodeId dataSetReaderIdentifier, UA_NodeId readerGroupIdentifier, const UA_DataSetReaderConfig *config) { if(config == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } UA_DataSetReader *currentDataSetReader = UA_ReaderGroup_findDSRbyId(server, dataSetReaderIdentifier); UA_ReaderGroup *currentReaderGroup = UA_ReaderGroup_findRGbyId(server, readerGroupIdentifier); if(!currentDataSetReader) { return UA_STATUSCODE_BADNOTFOUND; } /* The update functionality will be extended during the next PubSub batches. * Currently is only a change of the publishing interval possible. */ if(currentDataSetReader->config.writerGroupId != config->writerGroupId) { UA_PubSubManager_removeRepeatedPubSubCallback(server, currentReaderGroup->subscribeCallbackId); currentDataSetReader->config.writerGroupId = config->writerGroupId; UA_ReaderGroup_subscribeCallback(server, currentReaderGroup); } else { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "No or unsupported ReaderGroup update."); } return UA_STATUSCODE_GOOD; } /** * Get the current config of the UA_DataSetReader. * * @param server * @param dataSetReaderIdentifier * @param config * @return UA_STATUSCODE_GOOD on success */ UA_StatusCode UA_Server_DataSetReader_getConfig(UA_Server *server, UA_NodeId dataSetReaderIdentifier, UA_DataSetReaderConfig *config) { if(!config) { return UA_STATUSCODE_BADINVALIDARGUMENT; } UA_DataSetReader *currentDataSetReader = UA_ReaderGroup_findDSRbyId(server, dataSetReaderIdentifier); if(!currentDataSetReader) { return UA_STATUSCODE_BADNOTFOUND; } UA_DataSetReaderConfig tmpReaderConfig; /* Deep copy of the actual config */ UA_DataSetReaderConfig_copy(¤tDataSetReader->config, &tmpReaderConfig); *config = tmpReaderConfig; return UA_STATUSCODE_GOOD; } /** * This Method is used to initially set the SubscribedDataSet to TargetVariablesType and to create the list of target Variables of a SubscribedDataSetType. * * @param server * @param dataSetReaderIdentifier * @param targetVariables * @return UA_STATUSCODE_GOOD on success */ UA_StatusCode UA_Server_DataSetReader_createTargetVariables(UA_Server *server, UA_NodeId dataSetReaderIdentifier, UA_TargetVariablesDataType *targetVariables) { UA_StatusCode retval = UA_STATUSCODE_BADUNEXPECTEDERROR; UA_DataSetReader* pDS = UA_ReaderGroup_findDSRbyId(server, dataSetReaderIdentifier); if(pDS == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } if(pDS->subscribedDataSetTarget.targetVariablesSize > 0) { UA_TargetVariablesDataType_deleteMembers(&pDS->subscribedDataSetTarget); pDS->subscribedDataSetTarget.targetVariablesSize = 0; pDS->subscribedDataSetTarget.targetVariables = NULL; } /* Set subscribed dataset to TargetVariableType */ pDS->subscribedDataSetType = UA_PUBSUB_SDS_TARGET; retval = UA_TargetVariablesDataType_copy(targetVariables, &pDS->subscribedDataSetTarget); return retval; } /** * Adds Subscribed Variables from the DataSetMetaData for the given DataSet into the given parent node * and creates the corresponding data in the targetVariables of the DataSetReader * * @param server * @param parentNode * @param dataSetReaderIdentifier * @return UA_STATUSCODE_GOOD on success */ UA_StatusCode UA_Server_DataSetReader_addTargetVariables(UA_Server *server, UA_NodeId *parentNode, UA_NodeId dataSetReaderIdentifier, UA_SubscribedDataSetEnumType sdsType) { if((server == NULL) || (parentNode == NULL)) { return UA_STATUSCODE_BADINVALIDARGUMENT; } UA_StatusCode retval = UA_STATUSCODE_GOOD; UA_DataSetReader* pDataSetReader = UA_ReaderGroup_findDSRbyId(server, dataSetReaderIdentifier); if(pDataSetReader == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } UA_TargetVariablesDataType targetVars; targetVars.targetVariablesSize = pDataSetReader->config.dataSetMetaData.fieldsSize; targetVars.targetVariables = (UA_FieldTargetDataType *)UA_calloc(targetVars.targetVariablesSize, sizeof(UA_FieldTargetDataType)); for (size_t iteratorField = 0; iteratorField < pDataSetReader->config.dataSetMetaData.fieldsSize; iteratorField++) { UA_VariableAttributes vAttr = UA_VariableAttributes_default; vAttr.valueRank = pDataSetReader->config.dataSetMetaData.fields[iteratorField].valueRank; if(pDataSetReader->config.dataSetMetaData.fields[iteratorField].arrayDimensionsSize > 0) { retval = UA_Array_copy(pDataSetReader->config.dataSetMetaData.fields[iteratorField].arrayDimensions, pDataSetReader->config.dataSetMetaData.fields[iteratorField].arrayDimensionsSize, (void**)&vAttr.arrayDimensions, &UA_TYPES[UA_TYPES_UINT32]); if(retval == UA_STATUSCODE_GOOD) { vAttr.arrayDimensionsSize = pDataSetReader->config.dataSetMetaData.fields[iteratorField].arrayDimensionsSize; } } vAttr.dataType = pDataSetReader->config.dataSetMetaData.fields[iteratorField].dataType; vAttr.accessLevel = UA_ACCESSLEVELMASK_READ; UA_LocalizedText_copy(&pDataSetReader->config.dataSetMetaData.fields[iteratorField].description, &vAttr.description); UA_QualifiedName qn; UA_QualifiedName_init(&qn); char szTmpName[UA_MAX_SIZENAME]; if(pDataSetReader->config.dataSetMetaData.fields[iteratorField].name.length > 0) { UA_UInt16 slen = UA_MAX_SIZENAME -1; vAttr.displayName.locale = UA_STRING("en-US"); vAttr.displayName.text = pDataSetReader->config.dataSetMetaData.fields[iteratorField].name; if(pDataSetReader->config.dataSetMetaData.fields[iteratorField].name.length < slen) { slen = (UA_UInt16)pDataSetReader->config.dataSetMetaData.fields[iteratorField].name.length; UA_snprintf(szTmpName, sizeof(szTmpName), "%.*s", (int)slen, (const char*)pDataSetReader->config.dataSetMetaData.fields[iteratorField].name.data); } szTmpName[slen] = '\0'; qn = UA_QUALIFIEDNAME(1, szTmpName); } else { strcpy(szTmpName, "SubscribedVariable"); vAttr.displayName = UA_LOCALIZEDTEXT("en-US", szTmpName); qn = UA_QUALIFIEDNAME(1, "SubscribedVariable"); } /* Add variable to the given parent node */ UA_NodeId newNode; retval = UA_Server_addVariableNode(server, UA_NODEID_NULL, *parentNode, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), qn, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), vAttr, NULL, &newNode); if(retval == UA_STATUSCODE_GOOD) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_USERLAND, "addVariableNode %s succeeded", szTmpName); } else { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_USERLAND, "addVariableNode: error 0x%x", retval); } UA_FieldTargetDataType_init(&targetVars.targetVariables[iteratorField]); targetVars.targetVariables[iteratorField].attributeId = UA_ATTRIBUTEID_VALUE; UA_NodeId_copy(&newNode, &targetVars.targetVariables[iteratorField].targetNodeId); UA_NodeId_deleteMembers(&newNode); if(vAttr.arrayDimensionsSize > 0) { UA_Array_delete(vAttr.arrayDimensions, vAttr.arrayDimensionsSize, &UA_TYPES[UA_TYPES_UINT32]); } } if(sdsType == UA_PUBSUB_SDS_TARGET) { retval = UA_Server_DataSetReader_createTargetVariables(server, pDataSetReader->identifier, &targetVars); } UA_TargetVariablesDataType_deleteMembers(&targetVars); return retval; } /** * Process a NetworkMessage with a DataSetReader. * * @param server * @param dataSetReader * @param dataSetMsg */ void UA_Server_DataSetReader_process(UA_Server *server, UA_DataSetReader *dataSetReader, UA_DataSetMessage* dataSetMsg) { if((dataSetReader == NULL) || (dataSetMsg == NULL) || (server == NULL)) { return; } if(!dataSetMsg->header.dataSetMessageValid) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "DataSetMessage is discarded: message is not valid"); /* To Do check ConfigurationVersion*/ /*if(dataSetMsg->header.configVersionMajorVersionEnabled) * { * if(dataSetMsg->header.configVersionMajorVersion != dataSetReader->config.dataSetMetaData.configurationVersion.majorVersion) * { * UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER, "DataSetMessage is discarded: ConfigurationVersion MajorVersion does not match"); * return; * } } */ } else { if(dataSetMsg->header.dataSetMessageType == UA_DATASETMESSAGE_DATAKEYFRAME) { if(dataSetMsg->header.fieldEncoding != UA_FIELDENCODING_RAWDATA) { size_t anzFields = dataSetMsg->data.keyFrameData.fieldCount; if(dataSetReader->config.dataSetMetaData.fieldsSize < anzFields) { anzFields = dataSetReader->config.dataSetMetaData.fieldsSize; } if(dataSetReader->subscribedDataSetTarget.targetVariablesSize < anzFields) { anzFields = dataSetReader->subscribedDataSetTarget.targetVariablesSize; } UA_StatusCode retVal = UA_STATUSCODE_GOOD; for (UA_UInt16 iteratorField = 0; iteratorField < anzFields; iteratorField++) { if(dataSetMsg->data.keyFrameData.dataSetFields[iteratorField].hasValue) { if(dataSetReader->subscribedDataSetTarget.targetVariables[iteratorField].attributeId == UA_ATTRIBUTEID_VALUE) { retVal = UA_Server_writeValue(server, dataSetReader->subscribedDataSetTarget.targetVariables[iteratorField].targetNodeId, dataSetMsg->data.keyFrameData.dataSetFields[iteratorField].value); if(retVal != UA_STATUSCODE_GOOD) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "Error Write Value KF %u: 0x%x", iteratorField, retVal); } } else { UA_WriteValue writeVal; UA_WriteValue_init(&writeVal); writeVal.attributeId = dataSetReader->subscribedDataSetTarget.targetVariables[iteratorField].attributeId; writeVal.indexRange = dataSetReader->subscribedDataSetTarget.targetVariables[iteratorField].receiverIndexRange; writeVal.nodeId = dataSetReader->subscribedDataSetTarget.targetVariables[iteratorField].targetNodeId; UA_DataValue_copy(&dataSetMsg->data.keyFrameData.dataSetFields[iteratorField], &writeVal.value); retVal = UA_Server_write(server, &writeVal); if(retVal != UA_STATUSCODE_GOOD) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "Error Write KF %u: 0x%x", iteratorField, retVal); } } } } } } } } /** * Copy the config of the DataSetReader. * * @param src * @param dst * @return UA_STATUSCODE_GOOD on success */ UA_StatusCode UA_DataSetReaderConfig_copy(const UA_DataSetReaderConfig *src, UA_DataSetReaderConfig *dst) { memset(dst, 0, sizeof(UA_DataSetReaderConfig)); UA_StatusCode retVal = UA_String_copy(&src->name, &dst->name); if(retVal != UA_STATUSCODE_GOOD) { return retVal; } retVal = UA_Variant_copy(&src->publisherId, &dst->publisherId); if(retVal != UA_STATUSCODE_GOOD) { return retVal; } dst->writerGroupId = src->writerGroupId; dst->dataSetWriterId = src->dataSetWriterId; retVal = UA_DataSetMetaDataType_copy(&src->dataSetMetaData, &dst->dataSetMetaData); if(retVal != UA_STATUSCODE_GOOD) { return retVal; } dst->dataSetFieldContentMask = src->dataSetFieldContentMask; dst->messageReceiveTimeout = src->messageReceiveTimeout; /* Currently memcpy is used to copy the securityParameters */ memcpy(&dst->securityParameters, &src->securityParameters, sizeof(UA_PubSubSecurityParameters)); retVal = UA_UadpDataSetReaderMessageDataType_copy(&src->messageSettings, &dst->messageSettings); if(retVal != UA_STATUSCODE_GOOD) { return retVal; } return UA_STATUSCODE_GOOD; } /** * Delete the DataSetReader. * * @param server * @param dataSetReader */ void UA_DataSetReader_delete(UA_Server *server, UA_DataSetReader *dataSetReader) { /* Delete DataSetReader config */ UA_String_deleteMembers(&dataSetReader->config.name); UA_Variant_deleteMembers(&dataSetReader->config.publisherId); UA_DataSetMetaDataType_deleteMembers(&dataSetReader->config.dataSetMetaData); UA_UadpDataSetReaderMessageDataType_deleteMembers(&dataSetReader->config.messageSettings); UA_TargetVariablesDataType_deleteMembers(&dataSetReader->subscribedDataSetTarget); /* Delete DataSetReader */ UA_ReaderGroup* pGroup = UA_ReaderGroup_findRGbyId(server, dataSetReader->linkedReaderGroup); if(pGroup != NULL) { pGroup->readersCount--; } UA_NodeId_deleteMembers(&dataSetReader->identifier); UA_NodeId_deleteMembers(&dataSetReader->linkedReaderGroup); /* Remove DataSetReader from group */ LIST_REMOVE(dataSetReader, listEntry); /* Free memory allocated for DataSetReader */ UA_free(dataSetReader); } /**********************************************/ /* PublishedDataSet */ /**********************************************/ UA_StatusCode UA_PublishedDataSetConfig_copy(const UA_PublishedDataSetConfig *src, UA_PublishedDataSetConfig *dst) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; memcpy(dst, src, sizeof(UA_PublishedDataSetConfig)); retVal |= UA_String_copy(&src->name, &dst->name); switch(src->publishedDataSetType){ case UA_PUBSUB_DATASET_PUBLISHEDITEMS: //no additional items break; case UA_PUBSUB_DATASET_PUBLISHEDITEMS_TEMPLATE: if(src->config.itemsTemplate.variablesToAddSize > 0){ dst->config.itemsTemplate.variablesToAdd = (UA_PublishedVariableDataType *) UA_calloc( src->config.itemsTemplate.variablesToAddSize, sizeof(UA_PublishedVariableDataType)); } for(size_t i = 0; i < src->config.itemsTemplate.variablesToAddSize; i++){ retVal |= UA_PublishedVariableDataType_copy(&src->config.itemsTemplate.variablesToAdd[i], &dst->config.itemsTemplate.variablesToAdd[i]); } retVal |= UA_DataSetMetaDataType_copy(&src->config.itemsTemplate.metaData, &dst->config.itemsTemplate.metaData); break; default: return UA_STATUSCODE_BADINVALIDARGUMENT; } return retVal; } UA_StatusCode UA_Server_getPublishedDataSetConfig(UA_Server *server, const UA_NodeId pds, UA_PublishedDataSetConfig *config){ if(!config) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_PublishedDataSet *currentPublishedDataSet = UA_PublishedDataSet_findPDSbyId(server, pds); if(!currentPublishedDataSet) return UA_STATUSCODE_BADNOTFOUND; UA_PublishedDataSetConfig tmpPublishedDataSetConfig; //deep copy of the actual config UA_PublishedDataSetConfig_copy(¤tPublishedDataSet->config, &tmpPublishedDataSetConfig); *config = tmpPublishedDataSetConfig; return UA_STATUSCODE_GOOD; } UA_PublishedDataSet * UA_PublishedDataSet_findPDSbyId(UA_Server *server, UA_NodeId identifier){ for(size_t i = 0; i < server->pubSubManager.publishedDataSetsSize; i++){ if(UA_NodeId_equal(&server->pubSubManager.publishedDataSets[i].identifier, &identifier)){ return &server->pubSubManager.publishedDataSets[i]; } } return NULL; } void UA_PublishedDataSetConfig_deleteMembers(UA_PublishedDataSetConfig *pdsConfig){ //delete pds config UA_String_deleteMembers(&pdsConfig->name); switch (pdsConfig->publishedDataSetType){ case UA_PUBSUB_DATASET_PUBLISHEDITEMS: //no additional items break; case UA_PUBSUB_DATASET_PUBLISHEDITEMS_TEMPLATE: if(pdsConfig->config.itemsTemplate.variablesToAddSize > 0){ for(size_t i = 0; i < pdsConfig->config.itemsTemplate.variablesToAddSize; i++){ UA_PublishedVariableDataType_deleteMembers(&pdsConfig->config.itemsTemplate.variablesToAdd[i]); } UA_free(pdsConfig->config.itemsTemplate.variablesToAdd); } UA_DataSetMetaDataType_deleteMembers(&pdsConfig->config.itemsTemplate.metaData); break; default: break; } } void UA_PublishedDataSet_deleteMembers(UA_Server *server, UA_PublishedDataSet *publishedDataSet){ UA_PublishedDataSetConfig_deleteMembers(&publishedDataSet->config); //delete PDS UA_DataSetMetaDataType_deleteMembers(&publishedDataSet->dataSetMetaData); UA_DataSetField *field, *tmpField; LIST_FOREACH_SAFE(field, &publishedDataSet->fields, listEntry, tmpField) { UA_Server_removeDataSetField(server, field->identifier); } UA_NodeId_deleteMembers(&publishedDataSet->identifier); } UA_DataSetFieldResult UA_Server_addDataSetField(UA_Server *server, const UA_NodeId publishedDataSet, const UA_DataSetFieldConfig *fieldConfig, UA_NodeId *fieldIdentifier) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataSetFieldResult result = {UA_STATUSCODE_BADINVALIDARGUMENT, {0, 0}}; if(!fieldConfig) return result; UA_PublishedDataSet *currentDataSet = UA_PublishedDataSet_findPDSbyId(server, publishedDataSet); if(currentDataSet == NULL){ result.result = UA_STATUSCODE_BADNOTFOUND; return result; } if(currentDataSet->config.publishedDataSetType != UA_PUBSUB_DATASET_PUBLISHEDITEMS){ result.result = UA_STATUSCODE_BADNOTIMPLEMENTED; return result; } UA_DataSetField *newField = (UA_DataSetField *) UA_calloc(1, sizeof(UA_DataSetField)); if(!newField){ result.result = UA_STATUSCODE_BADINTERNALERROR; return result; } UA_DataSetFieldConfig tmpFieldConfig; retVal |= UA_DataSetFieldConfig_copy(fieldConfig, &tmpFieldConfig); newField->config = tmpFieldConfig; UA_PubSubManager_generateUniqueNodeId(server, &newField->identifier); if(fieldIdentifier != NULL){ UA_NodeId_copy(&newField->identifier, fieldIdentifier); } newField->publishedDataSet = currentDataSet->identifier; //update major version of parent published data set currentDataSet->dataSetMetaData.configurationVersion.majorVersion = UA_PubSubConfigurationVersionTimeDifference(); LIST_INSERT_HEAD(¤tDataSet->fields, newField, listEntry); if(newField->config.field.variable.promotedField) currentDataSet->promotedFieldsCount++; currentDataSet->fieldSize++; result.result = retVal; result.configurationVersion.majorVersion = currentDataSet->dataSetMetaData.configurationVersion.majorVersion; result.configurationVersion.minorVersion = currentDataSet->dataSetMetaData.configurationVersion.minorVersion; return result; } UA_DataSetFieldResult UA_Server_removeDataSetField(UA_Server *server, const UA_NodeId dsf) { UA_DataSetField *currentField = UA_DataSetField_findDSFbyId(server, dsf); UA_DataSetFieldResult result = {UA_STATUSCODE_BADNOTFOUND, {0, 0}}; if(!currentField) return result; UA_PublishedDataSet *parentPublishedDataSet = UA_PublishedDataSet_findPDSbyId(server, currentField->publishedDataSet); if(!parentPublishedDataSet) return result; parentPublishedDataSet->fieldSize--; if(currentField->config.field.variable.promotedField) parentPublishedDataSet->promotedFieldsCount--; /* update major version of PublishedDataSet */ parentPublishedDataSet->dataSetMetaData.configurationVersion.majorVersion = UA_PubSubConfigurationVersionTimeDifference(); UA_DataSetField_deleteMembers(currentField); LIST_REMOVE(currentField, listEntry); UA_free(currentField); result.result = UA_STATUSCODE_GOOD; result.configurationVersion.majorVersion = parentPublishedDataSet->dataSetMetaData.configurationVersion.majorVersion; result.configurationVersion.minorVersion = parentPublishedDataSet->dataSetMetaData.configurationVersion.minorVersion; return result; } /**********************************************/ /* DataSetWriter */ /**********************************************/ UA_StatusCode UA_DataSetWriterConfig_copy(const UA_DataSetWriterConfig *src, UA_DataSetWriterConfig *dst){ UA_StatusCode retVal = UA_STATUSCODE_GOOD; memcpy(dst, src, sizeof(UA_DataSetWriterConfig)); retVal |= UA_String_copy(&src->name, &dst->name); retVal |= UA_String_copy(&src->dataSetName, &dst->dataSetName); retVal |= UA_ExtensionObject_copy(&src->messageSettings, &dst->messageSettings); dst->dataSetWriterProperties = (UA_KeyValuePair *) UA_calloc(src->dataSetWriterPropertiesSize, sizeof(UA_KeyValuePair)); if(!dst->dataSetWriterProperties) return UA_STATUSCODE_BADOUTOFMEMORY; for(size_t i = 0; i < src->dataSetWriterPropertiesSize; i++){ retVal |= UA_KeyValuePair_copy(&src->dataSetWriterProperties[i], &dst->dataSetWriterProperties[i]); } return retVal; } UA_StatusCode UA_Server_getDataSetWriterConfig(UA_Server *server, const UA_NodeId dsw, UA_DataSetWriterConfig *config){ UA_StatusCode retVal = UA_STATUSCODE_GOOD; if(!config) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_DataSetWriter *currentDataSetWriter = UA_DataSetWriter_findDSWbyId(server, dsw); if(!currentDataSetWriter) return UA_STATUSCODE_BADNOTFOUND; UA_DataSetWriterConfig tmpWriterConfig; //deep copy of the actual config retVal |= UA_DataSetWriterConfig_copy(¤tDataSetWriter->config, &tmpWriterConfig); *config = tmpWriterConfig; return retVal; } UA_DataSetWriter * UA_DataSetWriter_findDSWbyId(UA_Server *server, UA_NodeId identifier) { for(size_t i = 0; i < server->pubSubManager.connectionsSize; i++){ UA_WriterGroup *tmpWriterGroup; LIST_FOREACH(tmpWriterGroup, &server->pubSubManager.connections[i].writerGroups, listEntry){ UA_DataSetWriter *tmpWriter; LIST_FOREACH(tmpWriter, &tmpWriterGroup->writers, listEntry){ if(UA_NodeId_equal(&tmpWriter->identifier, &identifier)){ return tmpWriter; } } } } return NULL; } void UA_DataSetWriterConfig_deleteMembers(UA_DataSetWriterConfig *pdsConfig) { UA_String_deleteMembers(&pdsConfig->name); UA_String_deleteMembers(&pdsConfig->dataSetName); for(size_t i = 0; i < pdsConfig->dataSetWriterPropertiesSize; i++){ UA_KeyValuePair_deleteMembers(&pdsConfig->dataSetWriterProperties[i]); } UA_free(pdsConfig->dataSetWriterProperties); UA_ExtensionObject_deleteMembers(&pdsConfig->messageSettings); } static void UA_DataSetWriter_deleteMembers(UA_Server *server, UA_DataSetWriter *dataSetWriter) { UA_DataSetWriterConfig_deleteMembers(&dataSetWriter->config); //delete DataSetWriter UA_NodeId_deleteMembers(&dataSetWriter->identifier); UA_NodeId_deleteMembers(&dataSetWriter->linkedWriterGroup); UA_NodeId_deleteMembers(&dataSetWriter->connectedDataSet); #ifdef UA_ENABLE_PUBSUB_DELTAFRAMES //delete lastSamples store for(size_t i = 0; i < dataSetWriter->lastSamplesCount; i++) { UA_DataValue_deleteMembers(&dataSetWriter->lastSamples[i].value); } UA_free(dataSetWriter->lastSamples); dataSetWriter->lastSamples = NULL; dataSetWriter->lastSamplesCount = 0; #endif } /**********************************************/ /* WriterGroup */ /**********************************************/ UA_StatusCode UA_WriterGroupConfig_copy(const UA_WriterGroupConfig *src, UA_WriterGroupConfig *dst){ UA_StatusCode retVal = UA_STATUSCODE_GOOD; memcpy(dst, src, sizeof(UA_WriterGroupConfig)); retVal |= UA_String_copy(&src->name, &dst->name); retVal |= UA_ExtensionObject_copy(&src->transportSettings, &dst->transportSettings); retVal |= UA_ExtensionObject_copy(&src->messageSettings, &dst->messageSettings); dst->groupProperties = (UA_KeyValuePair *) UA_calloc(src->groupPropertiesSize, sizeof(UA_KeyValuePair)); if(!dst->groupProperties) return UA_STATUSCODE_BADOUTOFMEMORY; for(size_t i = 0; i < src->groupPropertiesSize; i++){ retVal |= UA_KeyValuePair_copy(&src->groupProperties[i], &dst->groupProperties[i]); } return retVal; } UA_StatusCode UA_Server_getWriterGroupConfig(UA_Server *server, const UA_NodeId writerGroup, UA_WriterGroupConfig *config){ UA_StatusCode retVal = UA_STATUSCODE_GOOD; if(!config) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_WriterGroup *currentWriterGroup = UA_WriterGroup_findWGbyId(server, writerGroup); if(!currentWriterGroup){ return UA_STATUSCODE_BADNOTFOUND; } UA_WriterGroupConfig tmpWriterGroupConfig; //deep copy of the actual config retVal |= UA_WriterGroupConfig_copy(¤tWriterGroup->config, &tmpWriterGroupConfig); *config = tmpWriterGroupConfig; return retVal; } UA_StatusCode UA_Server_updateWriterGroupConfig(UA_Server *server, UA_NodeId writerGroupIdentifier, const UA_WriterGroupConfig *config){ if(!config) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_WriterGroup *currentWriterGroup = UA_WriterGroup_findWGbyId(server, writerGroupIdentifier); if(!currentWriterGroup) return UA_STATUSCODE_BADNOTFOUND; //The update functionality will be extended during the next PubSub batches. //Currently is only a change of the publishing interval possible. if(currentWriterGroup->config.maxEncapsulatedDataSetMessageCount != config->maxEncapsulatedDataSetMessageCount){ currentWriterGroup->config.maxEncapsulatedDataSetMessageCount = config->maxEncapsulatedDataSetMessageCount; if(currentWriterGroup->config.messageSettings.encoding == UA_EXTENSIONOBJECT_ENCODED_NOBODY) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "MaxEncapsulatedDataSetMessag need enabled 'PayloadHeader' within the message settings."); } } if(currentWriterGroup->config.publishingInterval != config->publishingInterval) { UA_PubSubManager_removeRepeatedPubSubCallback(server, currentWriterGroup->publishCallbackId); currentWriterGroup->config.publishingInterval = config->publishingInterval; UA_WriterGroup_addPublishCallback(server, currentWriterGroup); } if(currentWriterGroup->config.priority != config->priority) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "No or unsupported WriterGroup update."); } return UA_STATUSCODE_GOOD; } UA_WriterGroup * UA_WriterGroup_findWGbyId(UA_Server *server, UA_NodeId identifier){ for(size_t i = 0; i < server->pubSubManager.connectionsSize; i++){ UA_WriterGroup *tmpWriterGroup; LIST_FOREACH(tmpWriterGroup, &server->pubSubManager.connections[i].writerGroups, listEntry) { if(UA_NodeId_equal(&identifier, &tmpWriterGroup->identifier)){ return tmpWriterGroup; } } } return NULL; } void UA_WriterGroupConfig_deleteMembers(UA_WriterGroupConfig *writerGroupConfig){ //delete writerGroup config UA_String_deleteMembers(&writerGroupConfig->name); UA_ExtensionObject_deleteMembers(&writerGroupConfig->transportSettings); UA_ExtensionObject_deleteMembers(&writerGroupConfig->messageSettings); for(size_t i = 0; i < writerGroupConfig->groupPropertiesSize; i++){ UA_KeyValuePair_deleteMembers(&writerGroupConfig->groupProperties[i]); } UA_free(writerGroupConfig->groupProperties); } static void UA_WriterGroup_deleteMembers(UA_Server *server, UA_WriterGroup *writerGroup) { UA_WriterGroupConfig_deleteMembers(&writerGroup->config); //delete WriterGroup //delete all writers. Therefore removeDataSetWriter is called from PublishedDataSet UA_DataSetWriter *dataSetWriter, *tmpDataSetWriter; LIST_FOREACH_SAFE(dataSetWriter, &writerGroup->writers, listEntry, tmpDataSetWriter){ UA_Server_removeDataSetWriter(server, dataSetWriter->identifier); } UA_NodeId_deleteMembers(&writerGroup->linkedConnection); UA_NodeId_deleteMembers(&writerGroup->identifier); } UA_StatusCode UA_Server_addDataSetWriter(UA_Server *server, const UA_NodeId writerGroup, const UA_NodeId dataSet, const UA_DataSetWriterConfig *dataSetWriterConfig, UA_NodeId *writerIdentifier) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; if(!dataSetWriterConfig) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_PublishedDataSet *currentDataSetContext = UA_PublishedDataSet_findPDSbyId(server, dataSet); if(!currentDataSetContext) return UA_STATUSCODE_BADNOTFOUND; UA_WriterGroup *wg = UA_WriterGroup_findWGbyId(server, writerGroup); if(!wg) return UA_STATUSCODE_BADNOTFOUND; UA_DataSetWriter *newDataSetWriter = (UA_DataSetWriter *) UA_calloc(1, sizeof(UA_DataSetWriter)); if(!newDataSetWriter) return UA_STATUSCODE_BADOUTOFMEMORY; //copy the config into the new dataSetWriter UA_DataSetWriterConfig tmpDataSetWriterConfig; retVal |= UA_DataSetWriterConfig_copy(dataSetWriterConfig, &tmpDataSetWriterConfig); newDataSetWriter->config = tmpDataSetWriterConfig; //save the current version of the connected PublishedDataSet newDataSetWriter->connectedDataSetVersion = currentDataSetContext->dataSetMetaData.configurationVersion; #ifdef UA_ENABLE_PUBSUB_DELTAFRAMES //initialize the queue for the last values newDataSetWriter->lastSamples = (UA_DataSetWriterSample * ) UA_calloc(currentDataSetContext->fieldSize, sizeof(UA_DataSetWriterSample)); if(!newDataSetWriter->lastSamples) { UA_DataSetWriterConfig_deleteMembers(&newDataSetWriter->config); UA_free(newDataSetWriter); return UA_STATUSCODE_BADOUTOFMEMORY; } newDataSetWriter->lastSamplesCount = currentDataSetContext->fieldSize; #endif //connect PublishedDataSet with DataSetWriter newDataSetWriter->connectedDataSet = currentDataSetContext->identifier; newDataSetWriter->linkedWriterGroup = wg->identifier; UA_PubSubManager_generateUniqueNodeId(server, &newDataSetWriter->identifier); if(writerIdentifier != NULL) UA_NodeId_copy(&newDataSetWriter->identifier, writerIdentifier); //add the new writer to the group LIST_INSERT_HEAD(&wg->writers, newDataSetWriter, listEntry); wg->writersCount++; #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL addDataSetWriterRepresentation(server, newDataSetWriter); #endif return retVal; } UA_StatusCode UA_Server_removeDataSetWriter(UA_Server *server, const UA_NodeId dsw){ UA_DataSetWriter *dataSetWriter = UA_DataSetWriter_findDSWbyId(server, dsw); if(!dataSetWriter) return UA_STATUSCODE_BADNOTFOUND; UA_WriterGroup *linkedWriterGroup = UA_WriterGroup_findWGbyId(server, dataSetWriter->linkedWriterGroup); if(!linkedWriterGroup) return UA_STATUSCODE_BADNOTFOUND; linkedWriterGroup->writersCount--; #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL removeDataSetWriterRepresentation(server, dataSetWriter); #endif //remove DataSetWriter from group UA_DataSetWriter_deleteMembers(server, dataSetWriter); LIST_REMOVE(dataSetWriter, listEntry); UA_free(dataSetWriter); return UA_STATUSCODE_GOOD; } /**********************************************/ /* DataSetField */ /**********************************************/ UA_StatusCode UA_DataSetFieldConfig_copy(const UA_DataSetFieldConfig *src, UA_DataSetFieldConfig *dst){ memcpy(dst, src, sizeof(UA_DataSetFieldConfig)); if(src->dataSetFieldType == UA_PUBSUB_DATASETFIELD_VARIABLE) { UA_String_copy(&src->field.variable.fieldNameAlias, &dst->field.variable.fieldNameAlias); UA_PublishedVariableDataType_copy(&src->field.variable.publishParameters, &dst->field.variable.publishParameters); } else { return UA_STATUSCODE_BADNOTSUPPORTED; } return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Server_getDataSetFieldConfig(UA_Server *server, const UA_NodeId dsf, UA_DataSetFieldConfig *config) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; if(!config) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_DataSetField *currentDataSetField = UA_DataSetField_findDSFbyId(server, dsf); if(!currentDataSetField) return UA_STATUSCODE_BADNOTFOUND; UA_DataSetFieldConfig tmpFieldConfig; //deep copy of the actual config retVal |= UA_DataSetFieldConfig_copy(¤tDataSetField->config, &tmpFieldConfig); *config = tmpFieldConfig; return retVal; } UA_DataSetField * UA_DataSetField_findDSFbyId(UA_Server *server, UA_NodeId identifier) { for(size_t i = 0; i < server->pubSubManager.publishedDataSetsSize; i++){ UA_DataSetField *tmpField; LIST_FOREACH(tmpField, &server->pubSubManager.publishedDataSets[i].fields, listEntry){ if(UA_NodeId_equal(&tmpField->identifier, &identifier)){ return tmpField; } } } return NULL; } void UA_DataSetFieldConfig_deleteMembers(UA_DataSetFieldConfig *dataSetFieldConfig){ if(dataSetFieldConfig->dataSetFieldType == UA_PUBSUB_DATASETFIELD_VARIABLE){ UA_String_deleteMembers(&dataSetFieldConfig->field.variable.fieldNameAlias); UA_PublishedVariableDataType_deleteMembers(&dataSetFieldConfig->field.variable.publishParameters); } } static void UA_DataSetField_deleteMembers(UA_DataSetField *field) { UA_DataSetFieldConfig_deleteMembers(&field->config); //delete DataSetField UA_NodeId_deleteMembers(&field->identifier); UA_NodeId_deleteMembers(&field->publishedDataSet); UA_FieldMetaData_deleteMembers(&field->fieldMetaData); } /*********************************************************/ /* PublishValues handling */ /*********************************************************/ /** * Compare two variants. Internally used for value change detection. * * @return true if the value has changed */ #ifdef UA_ENABLE_PUBSUB_DELTAFRAMES static UA_Boolean valueChangedVariant(UA_Variant *oldValue, UA_Variant *newValue){ if(! (oldValue && newValue)) return false; UA_ByteString *oldValueEncoding = UA_ByteString_new(), *newValueEncoding = UA_ByteString_new(); size_t oldValueEncodingSize, newValueEncodingSize; oldValueEncodingSize = UA_calcSizeBinary(oldValue, &UA_TYPES[UA_TYPES_VARIANT]); newValueEncodingSize = UA_calcSizeBinary(newValue, &UA_TYPES[UA_TYPES_VARIANT]); if((oldValueEncodingSize == 0) || (newValueEncodingSize == 0)) return false; if(oldValueEncodingSize != newValueEncodingSize) return true; if(UA_ByteString_allocBuffer(oldValueEncoding, oldValueEncodingSize) != UA_STATUSCODE_GOOD) return false; if(UA_ByteString_allocBuffer(newValueEncoding, newValueEncodingSize) != UA_STATUSCODE_GOOD) return false; UA_Byte *bufPosOldValue = oldValueEncoding->data; const UA_Byte *bufEndOldValue = &oldValueEncoding->data[oldValueEncoding->length]; UA_Byte *bufPosNewValue = newValueEncoding->data; const UA_Byte *bufEndNewValue = &newValueEncoding->data[newValueEncoding->length]; if(UA_encodeBinary(oldValue, &UA_TYPES[UA_TYPES_VARIANT], &bufPosOldValue, &bufEndOldValue, NULL, NULL) != UA_STATUSCODE_GOOD){ return false; } if(UA_encodeBinary(newValue, &UA_TYPES[UA_TYPES_VARIANT], &bufPosNewValue, &bufEndNewValue, NULL, NULL) != UA_STATUSCODE_GOOD){ return false; } oldValueEncoding->length = (uintptr_t)bufPosOldValue - (uintptr_t)oldValueEncoding->data; newValueEncoding->length = (uintptr_t)bufPosNewValue - (uintptr_t)newValueEncoding->data; UA_Boolean compareResult = !UA_ByteString_equal(oldValueEncoding, newValueEncoding); UA_ByteString_delete(oldValueEncoding); UA_ByteString_delete(newValueEncoding); return compareResult; } #endif /** * Obtain the latest value for a specific DataSetField. This method is currently * called inside the DataSetMessage generation process. */ static void UA_PubSubDataSetField_sampleValue(UA_Server *server, UA_DataSetField *field, UA_DataValue *value) { /* Read the value */ UA_ReadValueId rvid; UA_ReadValueId_init(&rvid); rvid.nodeId = field->config.field.variable.publishParameters.publishedVariable; rvid.attributeId = field->config.field.variable.publishParameters.attributeId; rvid.indexRange = field->config.field.variable.publishParameters.indexRange; *value = UA_Server_read(server, &rvid, UA_TIMESTAMPSTORETURN_BOTH); } static UA_StatusCode UA_PubSubDataSetWriter_generateKeyFrameMessage(UA_Server *server, UA_DataSetMessage *dataSetMessage, UA_DataSetWriter *dataSetWriter) { UA_PublishedDataSet *currentDataSet = UA_PublishedDataSet_findPDSbyId(server, dataSetWriter->connectedDataSet); if(!currentDataSet) return UA_STATUSCODE_BADNOTFOUND; /* Prepare DataSetMessageContent */ dataSetMessage->header.dataSetMessageValid = true; dataSetMessage->header.dataSetMessageType = UA_DATASETMESSAGE_DATAKEYFRAME; dataSetMessage->data.keyFrameData.fieldCount = currentDataSet->fieldSize; dataSetMessage->data.keyFrameData.dataSetFields = (UA_DataValue *) UA_Array_new(currentDataSet->fieldSize, &UA_TYPES[UA_TYPES_DATAVALUE]); if(!dataSetMessage->data.keyFrameData.dataSetFields) return UA_STATUSCODE_BADOUTOFMEMORY; #ifdef UA_ENABLE_JSON_ENCODING /* json: insert fieldnames used as json keys */ dataSetMessage->data.keyFrameData.fieldNames = (UA_String *)UA_Array_new(currentDataSet->fieldSize, &UA_TYPES[UA_TYPES_STRING]); if(!dataSetMessage->data.keyFrameData.fieldNames) return UA_STATUSCODE_BADOUTOFMEMORY; #endif /* Loop over the fields */ size_t counter = 0; UA_DataSetField *dsf; LIST_FOREACH(dsf, ¤tDataSet->fields, listEntry) { #ifdef UA_ENABLE_JSON_ENCODING /* json: store the fieldNameAlias*/ UA_String_copy(&dsf->config.field.variable.fieldNameAlias, &dataSetMessage->data.keyFrameData.fieldNames[counter]); #endif /* Sample the value */ UA_DataValue *dfv = &dataSetMessage->data.keyFrameData.dataSetFields[counter]; UA_PubSubDataSetField_sampleValue(server, dsf, dfv); /* Deactivate statuscode? */ if(((u64)dataSetWriter->config.dataSetFieldContentMask & (u64)UA_DATASETFIELDCONTENTMASK_STATUSCODE) == 0) dfv->hasStatus = false; /* Deactivate timestamps */ if(((u64)dataSetWriter->config.dataSetFieldContentMask & (u64)UA_DATASETFIELDCONTENTMASK_SOURCETIMESTAMP) == 0) dfv->hasSourceTimestamp = false; if(((u64)dataSetWriter->config.dataSetFieldContentMask & (u64)UA_DATASETFIELDCONTENTMASK_SOURCEPICOSECONDS) == 0) dfv->hasSourcePicoseconds = false; if(((u64)dataSetWriter->config.dataSetFieldContentMask & (u64)UA_DATASETFIELDCONTENTMASK_SERVERTIMESTAMP) == 0) dfv->hasServerTimestamp = false; if(((u64)dataSetWriter->config.dataSetFieldContentMask & (u64)UA_DATASETFIELDCONTENTMASK_SERVERPICOSECONDS) == 0) dfv->hasServerPicoseconds = false; #ifdef UA_ENABLE_PUBSUB_DELTAFRAMES /* Update lastValue store */ UA_DataValue_deleteMembers(&dataSetWriter->lastSamples[counter].value); UA_DataValue_copy(dfv, &dataSetWriter->lastSamples[counter].value); #endif counter++; } return UA_STATUSCODE_GOOD; } #ifdef UA_ENABLE_PUBSUB_DELTAFRAMES static UA_StatusCode UA_PubSubDataSetWriter_generateDeltaFrameMessage(UA_Server *server, UA_DataSetMessage *dataSetMessage, UA_DataSetWriter *dataSetWriter) { UA_PublishedDataSet *currentDataSet = UA_PublishedDataSet_findPDSbyId(server, dataSetWriter->connectedDataSet); if(!currentDataSet) return UA_STATUSCODE_BADNOTFOUND; /* Prepare DataSetMessageContent */ memset(dataSetMessage, 0, sizeof(UA_DataSetMessage)); dataSetMessage->header.dataSetMessageValid = true; dataSetMessage->header.dataSetMessageType = UA_DATASETMESSAGE_DATADELTAFRAME; UA_DataSetField *dsf; size_t counter = 0; LIST_FOREACH(dsf, ¤tDataSet->fields, listEntry) { /* Sample the value */ UA_DataValue value; UA_DataValue_init(&value); UA_PubSubDataSetField_sampleValue(server, dsf, &value); /* Check if the value has changed */ if(valueChangedVariant(&dataSetWriter->lastSamples[counter].value.value, &value.value)) { /* increase fieldCount for current delta message */ dataSetMessage->data.deltaFrameData.fieldCount++; dataSetWriter->lastSamples[counter].valueChanged = true; /* Update last stored sample */ UA_DataValue_deleteMembers(&dataSetWriter->lastSamples[counter].value); dataSetWriter->lastSamples[counter].value = value; } else { UA_DataValue_deleteMembers(&value); dataSetWriter->lastSamples[counter].valueChanged = false; } counter++; } /* Allocate DeltaFrameFields */ UA_DataSetMessage_DeltaFrameField *deltaFields = (UA_DataSetMessage_DeltaFrameField *) UA_calloc(dataSetMessage->data.deltaFrameData.fieldCount, sizeof(UA_DataSetMessage_DeltaFrameField)); if(!deltaFields) return UA_STATUSCODE_BADOUTOFMEMORY; dataSetMessage->data.deltaFrameData.deltaFrameFields = deltaFields; size_t currentDeltaField = 0; for(size_t i = 0; i < currentDataSet->fieldSize; i++) { if(!dataSetWriter->lastSamples[i].valueChanged) continue; UA_DataSetMessage_DeltaFrameField *dff = &deltaFields[currentDeltaField]; dff->fieldIndex = (UA_UInt16) i; UA_DataValue_copy(&dataSetWriter->lastSamples[i].value, &dff->fieldValue); dataSetWriter->lastSamples[i].valueChanged = false; /* Deactivate statuscode? */ if(((u64)dataSetWriter->config.dataSetFieldContentMask & (u64)UA_DATASETFIELDCONTENTMASK_STATUSCODE) == 0) dff->fieldValue.hasStatus = false; /* Deactivate timestamps? */ if(((u64)dataSetWriter->config.dataSetFieldContentMask & (u64)UA_DATASETFIELDCONTENTMASK_SOURCETIMESTAMP) == 0) dff->fieldValue.hasSourceTimestamp = false; if(((u64)dataSetWriter->config.dataSetFieldContentMask & (u64)UA_DATASETFIELDCONTENTMASK_SOURCEPICOSECONDS) == 0) dff->fieldValue.hasServerPicoseconds = false; if(((u64)dataSetWriter->config.dataSetFieldContentMask & (u64)UA_DATASETFIELDCONTENTMASK_SERVERTIMESTAMP) == 0) dff->fieldValue.hasServerTimestamp = false; if(((u64)dataSetWriter->config.dataSetFieldContentMask & (u64)UA_DATASETFIELDCONTENTMASK_SERVERPICOSECONDS) == 0) dff->fieldValue.hasServerPicoseconds = false; currentDeltaField++; } return UA_STATUSCODE_GOOD; } #endif /** * Generate a DataSetMessage for the given writer. * * @param dataSetWriter ptr to corresponding writer * @return ptr to generated DataSetMessage */ static UA_StatusCode UA_DataSetWriter_generateDataSetMessage(UA_Server *server, UA_DataSetMessage *dataSetMessage, UA_DataSetWriter *dataSetWriter) { UA_PublishedDataSet *currentDataSet = UA_PublishedDataSet_findPDSbyId(server, dataSetWriter->connectedDataSet); if(!currentDataSet) return UA_STATUSCODE_BADNOTFOUND; /* Reset the message */ memset(dataSetMessage, 0, sizeof(UA_DataSetMessage)); /* store messageType to switch between json or uadp (default) */ UA_UInt16 messageType = UA_TYPES_UADPDATASETWRITERMESSAGEDATATYPE; UA_JsonDataSetWriterMessageDataType *jsonDataSetWriterMessageDataType = NULL; /* The configuration Flags are included * inside the std. defined UA_UadpDataSetWriterMessageDataType */ UA_UadpDataSetWriterMessageDataType defaultUadpConfiguration; UA_UadpDataSetWriterMessageDataType *dataSetWriterMessageDataType = NULL; if((dataSetWriter->config.messageSettings.encoding == UA_EXTENSIONOBJECT_DECODED || dataSetWriter->config.messageSettings.encoding == UA_EXTENSIONOBJECT_DECODED_NODELETE) && (dataSetWriter->config.messageSettings.content.decoded.type == &UA_TYPES[UA_TYPES_UADPDATASETWRITERMESSAGEDATATYPE])) { dataSetWriterMessageDataType = (UA_UadpDataSetWriterMessageDataType *) dataSetWriter->config.messageSettings.content.decoded.data; /* type is UADP */ messageType = UA_TYPES_UADPDATASETWRITERMESSAGEDATATYPE; } else if((dataSetWriter->config.messageSettings.encoding == UA_EXTENSIONOBJECT_DECODED || dataSetWriter->config.messageSettings.encoding == UA_EXTENSIONOBJECT_DECODED_NODELETE) && (dataSetWriter->config.messageSettings.content.decoded.type == &UA_TYPES[UA_TYPES_JSONDATASETWRITERMESSAGEDATATYPE])) { jsonDataSetWriterMessageDataType = (UA_JsonDataSetWriterMessageDataType *) dataSetWriter->config.messageSettings.content.decoded.data; /* type is JSON */ messageType = UA_TYPES_JSONDATASETWRITERMESSAGEDATATYPE; } else { /* create default flag configuration if no * UadpDataSetWriterMessageDataType was passed in */ memset(&defaultUadpConfiguration, 0, sizeof(UA_UadpDataSetWriterMessageDataType)); defaultUadpConfiguration.dataSetMessageContentMask = (UA_UadpDataSetMessageContentMask) ((u64)UA_UADPDATASETMESSAGECONTENTMASK_TIMESTAMP | (u64)UA_UADPDATASETMESSAGECONTENTMASK_MAJORVERSION | (u64)UA_UADPDATASETMESSAGECONTENTMASK_MINORVERSION); dataSetWriterMessageDataType = &defaultUadpConfiguration; } /* Sanity-test the configuration */ if(dataSetWriterMessageDataType && (dataSetWriterMessageDataType->networkMessageNumber != 0 || dataSetWriterMessageDataType->dataSetOffset != 0 || dataSetWriterMessageDataType->configuredSize != 0)) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Static DSM configuration not supported. Using defaults"); dataSetWriterMessageDataType->networkMessageNumber = 0; dataSetWriterMessageDataType->dataSetOffset = 0; dataSetWriterMessageDataType->configuredSize = 0; } /* The field encoding depends on the flags inside the writer config. * TODO: This can be moved to the encoding layer. */ if(dataSetWriter->config.dataSetFieldContentMask & (u64)UA_DATASETFIELDCONTENTMASK_RAWDATA ) { dataSetMessage->header.fieldEncoding = UA_FIELDENCODING_RAWDATA; } else if((u64)dataSetWriter->config.dataSetFieldContentMask & ((u64)UA_DATASETFIELDCONTENTMASK_SOURCETIMESTAMP | (u64)UA_DATASETFIELDCONTENTMASK_SERVERPICOSECONDS | (u64)UA_DATASETFIELDCONTENTMASK_SOURCEPICOSECONDS | (u64)UA_DATASETFIELDCONTENTMASK_STATUSCODE)) { dataSetMessage->header.fieldEncoding = UA_FIELDENCODING_DATAVALUE; } else { dataSetMessage->header.fieldEncoding = UA_FIELDENCODING_VARIANT; } if(messageType == UA_TYPES_UADPDATASETWRITERMESSAGEDATATYPE) { /* Std: 'The DataSetMessageContentMask defines the flags for the content of the DataSetMessage header.' */ if((u64)dataSetWriterMessageDataType->dataSetMessageContentMask & (u64)UA_UADPDATASETMESSAGECONTENTMASK_MAJORVERSION) { dataSetMessage->header.configVersionMajorVersionEnabled = true; dataSetMessage->header.configVersionMajorVersion = currentDataSet->dataSetMetaData.configurationVersion.majorVersion; } if((u64)dataSetWriterMessageDataType->dataSetMessageContentMask & (u64)UA_UADPDATASETMESSAGECONTENTMASK_MINORVERSION) { dataSetMessage->header.configVersionMinorVersionEnabled = true; dataSetMessage->header.configVersionMinorVersion = currentDataSet->dataSetMetaData.configurationVersion.minorVersion; } if((u64)dataSetWriterMessageDataType->dataSetMessageContentMask & (u64)UA_UADPDATASETMESSAGECONTENTMASK_SEQUENCENUMBER) { dataSetMessage->header.dataSetMessageSequenceNrEnabled = true; dataSetMessage->header.dataSetMessageSequenceNr = dataSetWriter->actualDataSetMessageSequenceCount; } if((u64)dataSetWriterMessageDataType->dataSetMessageContentMask & (u64)UA_UADPDATASETMESSAGECONTENTMASK_TIMESTAMP) { dataSetMessage->header.timestampEnabled = true; dataSetMessage->header.timestamp = UA_DateTime_now(); } /* TODO: Picoseconds resolution not supported atm */ if((u64)dataSetWriterMessageDataType->dataSetMessageContentMask & (u64)UA_UADPDATASETMESSAGECONTENTMASK_PICOSECONDS) { dataSetMessage->header.picoSecondsIncluded = false; } /* TODO: Statuscode not supported yet */ if((u64)dataSetWriterMessageDataType->dataSetMessageContentMask & (u64)UA_UADPDATASETMESSAGECONTENTMASK_STATUS) { dataSetMessage->header.statusEnabled = false; } } else if(messageType == UA_TYPES_JSONDATASETWRITERMESSAGEDATATYPE) { if((u64)jsonDataSetWriterMessageDataType->dataSetMessageContentMask & (u64)UA_JSONDATASETMESSAGECONTENTMASK_METADATAVERSION) { dataSetMessage->header.configVersionMajorVersionEnabled = true; dataSetMessage->header.configVersionMajorVersion = currentDataSet->dataSetMetaData.configurationVersion.majorVersion; } if((u64)jsonDataSetWriterMessageDataType->dataSetMessageContentMask & (u64)UA_JSONDATASETMESSAGECONTENTMASK_METADATAVERSION) { dataSetMessage->header.configVersionMinorVersionEnabled = true; dataSetMessage->header.configVersionMinorVersion = currentDataSet->dataSetMetaData.configurationVersion.minorVersion; } if((u64)jsonDataSetWriterMessageDataType->dataSetMessageContentMask & (u64)UA_JSONDATASETMESSAGECONTENTMASK_SEQUENCENUMBER) { dataSetMessage->header.dataSetMessageSequenceNrEnabled = true; dataSetMessage->header.dataSetMessageSequenceNr = dataSetWriter->actualDataSetMessageSequenceCount; } if((u64)jsonDataSetWriterMessageDataType->dataSetMessageContentMask & (u64)UA_JSONDATASETMESSAGECONTENTMASK_TIMESTAMP) { dataSetMessage->header.timestampEnabled = true; dataSetMessage->header.timestamp = UA_DateTime_now(); } /* TODO: Statuscode not supported yet */ if((u64)jsonDataSetWriterMessageDataType->dataSetMessageContentMask & (u64)UA_JSONDATASETMESSAGECONTENTMASK_STATUS) { dataSetMessage->header.statusEnabled = false; } } /* Set the sequence count. Automatically rolls over to zero */ dataSetWriter->actualDataSetMessageSequenceCount++; /* JSON does not differ between deltaframes and keyframes, only keyframes are currently used. */ if(messageType != UA_TYPES_JSONDATASETWRITERMESSAGEDATATYPE){ #ifdef UA_ENABLE_PUBSUB_DELTAFRAMES /* Check if the PublishedDataSet version has changed -> if yes flush the lastValue store and send a KeyFrame */ if(dataSetWriter->connectedDataSetVersion.majorVersion != currentDataSet->dataSetMetaData.configurationVersion.majorVersion || dataSetWriter->connectedDataSetVersion.minorVersion != currentDataSet->dataSetMetaData.configurationVersion.minorVersion) { /* Remove old samples */ for(size_t i = 0; i < dataSetWriter->lastSamplesCount; i++) UA_DataValue_deleteMembers(&dataSetWriter->lastSamples[i].value); /* Realloc pds dependent memory */ dataSetWriter->lastSamplesCount = currentDataSet->fieldSize; UA_DataSetWriterSample *newSamplesArray = (UA_DataSetWriterSample * ) UA_realloc(dataSetWriter->lastSamples, sizeof(UA_DataSetWriterSample) * dataSetWriter->lastSamplesCount); if(!newSamplesArray) return UA_STATUSCODE_BADOUTOFMEMORY; dataSetWriter->lastSamples = newSamplesArray; memset(dataSetWriter->lastSamples, 0, sizeof(UA_DataSetWriterSample) * dataSetWriter->lastSamplesCount); dataSetWriter->connectedDataSetVersion = currentDataSet->dataSetMetaData.configurationVersion; UA_PubSubDataSetWriter_generateKeyFrameMessage(server, dataSetMessage, dataSetWriter); dataSetWriter->deltaFrameCounter = 0; return UA_STATUSCODE_GOOD; } /* The standard defines: if a PDS contains only one fields no delta messages * should be generated because they need more memory than a keyframe with 1 * field. */ if(currentDataSet->fieldSize > 1 && dataSetWriter->deltaFrameCounter > 0 && dataSetWriter->deltaFrameCounter <= dataSetWriter->config.keyFrameCount) { UA_PubSubDataSetWriter_generateDeltaFrameMessage(server, dataSetMessage, dataSetWriter); dataSetWriter->deltaFrameCounter++; return UA_STATUSCODE_GOOD; } dataSetWriter->deltaFrameCounter = 1; #endif } UA_PubSubDataSetWriter_generateKeyFrameMessage(server, dataSetMessage, dataSetWriter); return UA_STATUSCODE_GOOD; } static UA_StatusCode sendNetworkMessageJson(UA_PubSubConnection *connection, UA_DataSetMessage *dsm, UA_UInt16 *writerIds, UA_Byte dsmCount, UA_ExtensionObject *transportSettings) { UA_StatusCode retval = UA_STATUSCODE_BADNOTSUPPORTED; #ifdef UA_ENABLE_JSON_ENCODING UA_NetworkMessage nm; memset(&nm, 0, sizeof(UA_NetworkMessage)); nm.version = 1; nm.networkMessageType = UA_NETWORKMESSAGE_DATASET; nm.payloadHeaderEnabled = true; nm.payloadHeader.dataSetPayloadHeader.count = dsmCount; nm.payloadHeader.dataSetPayloadHeader.dataSetWriterIds = writerIds; nm.payload.dataSetPayload.dataSetMessages = dsm; /* Allocate the buffer. Allocate on the stack if the buffer is small. */ UA_ByteString buf; size_t msgSize = UA_NetworkMessage_calcSizeJson(&nm, NULL, 0, NULL, 0, true); size_t stackSize = 1; if(msgSize <= UA_MAX_STACKBUF) stackSize = msgSize; UA_STACKARRAY(UA_Byte, stackBuf, stackSize); buf.data = stackBuf; buf.length = msgSize; if(msgSize > UA_MAX_STACKBUF) { retval = UA_ByteString_allocBuffer(&buf, msgSize); if(retval != UA_STATUSCODE_GOOD) return retval; } /* Encode the message */ UA_Byte *bufPos = buf.data; memset(bufPos, 0, msgSize); const UA_Byte *bufEnd = &buf.data[buf.length]; retval = UA_NetworkMessage_encodeJson(&nm, &bufPos, &bufEnd, NULL, 0, NULL, 0, true); if(retval != UA_STATUSCODE_GOOD) { if(msgSize > UA_MAX_STACKBUF) UA_ByteString_deleteMembers(&buf); return retval; } /* Send the prepared messages */ retval = connection->channel->send(connection->channel, transportSettings, &buf); if(msgSize > UA_MAX_STACKBUF) UA_ByteString_deleteMembers(&buf); #endif return retval; } static UA_StatusCode sendNetworkMessage(UA_PubSubConnection *connection, UA_WriterGroup *wg, UA_DataSetMessage *dsm, UA_UInt16 *writerIds, UA_Byte dsmCount, UA_ExtensionObject *messageSettings, UA_ExtensionObject *transportSettings) { if(messageSettings->content.decoded.type != &UA_TYPES[UA_TYPES_UADPWRITERGROUPMESSAGEDATATYPE]) return UA_STATUSCODE_BADINTERNALERROR; UA_UadpWriterGroupMessageDataType *wgm = (UA_UadpWriterGroupMessageDataType*) messageSettings->content.decoded.data; UA_NetworkMessage nm; memset(&nm, 0, sizeof(UA_NetworkMessage)); nm.publisherIdEnabled = ((u64)wgm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_PUBLISHERID) != 0; nm.groupHeaderEnabled = ((u64)wgm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_GROUPHEADER) != 0; nm.groupHeader.writerGroupIdEnabled = ((u64)wgm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_WRITERGROUPID) != 0; nm.groupHeader.groupVersionEnabled = ((u64)wgm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_GROUPVERSION) != 0; nm.groupHeader.networkMessageNumberEnabled = ((u64)wgm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_NETWORKMESSAGENUMBER) != 0; nm.groupHeader.sequenceNumberEnabled = ((u64)wgm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_SEQUENCENUMBER) != 0; nm.payloadHeaderEnabled = ((u64)wgm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_PAYLOADHEADER) != 0; nm.timestampEnabled = ((u64)wgm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_TIMESTAMP) != 0; nm.picosecondsEnabled = ((u64)wgm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_PICOSECONDS) != 0; nm.dataSetClassIdEnabled = ((u64)wgm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_DATASETCLASSID) != 0; nm.promotedFieldsEnabled = ((u64)wgm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_PROMOTEDFIELDS) != 0; nm.version = 1; nm.networkMessageType = UA_NETWORKMESSAGE_DATASET; if(connection->config->publisherIdType == UA_PUBSUB_PUBLISHERID_NUMERIC) { nm.publisherIdType = UA_PUBLISHERDATATYPE_UINT16; nm.publisherId.publisherIdUInt32 = connection->config->publisherId.numeric; } else if(connection->config->publisherIdType == UA_PUBSUB_PUBLISHERID_STRING){ nm.publisherIdType = UA_PUBLISHERDATATYPE_STRING; nm.publisherId.publisherIdString = connection->config->publisherId.string; } /* Compute the length of the dsm separately for the header */ UA_STACKARRAY(UA_UInt16, dsmLengths, dsmCount); for(UA_Byte i = 0; i < dsmCount; i++) dsmLengths[i] = (UA_UInt16)UA_DataSetMessage_calcSizeBinary(&dsm[i]); nm.payloadHeader.dataSetPayloadHeader.count = dsmCount; nm.payloadHeader.dataSetPayloadHeader.dataSetWriterIds = writerIds; nm.groupHeader.writerGroupId = wg->config.writerGroupId; nm.groupHeader.networkMessageNumber = 1; nm.payload.dataSetPayload.sizes = dsmLengths; nm.payload.dataSetPayload.dataSetMessages = dsm; /* Allocate the buffer. Allocate on the stack if the buffer is small. */ UA_ByteString buf; size_t msgSize = UA_NetworkMessage_calcSizeBinary(&nm); size_t stackSize = 1; if(msgSize <= UA_MAX_STACKBUF) stackSize = msgSize; UA_STACKARRAY(UA_Byte, stackBuf, stackSize); buf.data = stackBuf; buf.length = msgSize; UA_StatusCode retval; if(msgSize > UA_MAX_STACKBUF) { retval = UA_ByteString_allocBuffer(&buf, msgSize); if(retval != UA_STATUSCODE_GOOD) return retval; } /* Encode the message */ UA_Byte *bufPos = buf.data; memset(bufPos, 0, msgSize); const UA_Byte *bufEnd = &buf.data[buf.length]; retval = UA_NetworkMessage_encodeBinary(&nm, &bufPos, bufEnd); if(retval != UA_STATUSCODE_GOOD) { if(msgSize > UA_MAX_STACKBUF) UA_ByteString_deleteMembers(&buf); return retval; } /* Send the prepared messages */ retval = connection->channel->send(connection->channel, transportSettings, &buf); if(msgSize > UA_MAX_STACKBUF) UA_ByteString_deleteMembers(&buf); return retval; } /* This callback triggers the collection and publish of NetworkMessages and the * contained DataSetMessages. */ void UA_WriterGroup_publishCallback(UA_Server *server, UA_WriterGroup *writerGroup) { UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "Publish Callback"); if(!writerGroup) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Publish failed. WriterGroup not found"); return; } /* Nothing to do? */ if(writerGroup->writersCount <= 0) return; /* Binary or Json encoding? */ if(writerGroup->config.encodingMimeType != UA_PUBSUB_ENCODING_UADP && writerGroup->config.encodingMimeType != UA_PUBSUB_ENCODING_JSON) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Publish failed: Unknown encoding type."); return; } /* Find the connection associated with the writer */ UA_PubSubConnection *connection = UA_PubSubConnection_findConnectionbyId(server, writerGroup->linkedConnection); if(!connection) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Publish failed. PubSubConnection invalid."); return; } /* How many DSM can be sent in one NM? */ UA_Byte maxDSM = (UA_Byte)writerGroup->config.maxEncapsulatedDataSetMessageCount; if(writerGroup->config.maxEncapsulatedDataSetMessageCount > UA_BYTE_MAX) maxDSM = UA_BYTE_MAX; /* If the maxEncapsulatedDataSetMessageCount is set to 0->1 */ if(maxDSM == 0) maxDSM = 1; /* It is possible to put several DataSetMessages into one NetworkMessage. * But only if they do not contain promoted fields. NM with only DSM are * sent out right away. The others are kept in a buffer for "batching". */ size_t dsmCount = 0; UA_DataSetWriter *dsw; UA_STACKARRAY(UA_UInt16, dsWriterIds, writerGroup->writersCount); UA_STACKARRAY(UA_DataSetMessage, dsmStore, writerGroup->writersCount); LIST_FOREACH(dsw, &writerGroup->writers, listEntry) { /* Find the dataset */ UA_PublishedDataSet *pds = UA_PublishedDataSet_findPDSbyId(server, dsw->connectedDataSet); if(!pds) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub Publish: PublishedDataSet not found"); continue; } /* Generate the DSM */ UA_StatusCode res = UA_DataSetWriter_generateDataSetMessage(server, &dsmStore[dsmCount], dsw); if(res != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub Publish: DataSetMessage creation failed"); continue; } /* Send right away if there is only this DSM in a NM. If promoted fields * are contained in the PublishedDataSet, then this DSM must go into a * dedicated NM as well. */ if(pds->promotedFieldsCount > 0 || maxDSM == 1) { if(writerGroup->config.encodingMimeType == UA_PUBSUB_ENCODING_UADP){ res = sendNetworkMessage(connection, writerGroup, &dsmStore[dsmCount], &dsw->config.dataSetWriterId, 1, &writerGroup->config.messageSettings, &writerGroup->config.transportSettings); }else if(writerGroup->config.encodingMimeType == UA_PUBSUB_ENCODING_JSON){ res = sendNetworkMessageJson(connection, &dsmStore[dsmCount], &dsw->config.dataSetWriterId, 1, &writerGroup->config.transportSettings); } if(res != UA_STATUSCODE_GOOD) UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub Publish: Could not send a NetworkMessage"); UA_DataSetMessage_free(&dsmStore[dsmCount]); continue; } dsWriterIds[dsmCount] = dsw->config.dataSetWriterId; dsmCount++; } /* Send the NetworkMessages with batched DataSetMessages */ size_t nmCount = (dsmCount / maxDSM) + ((dsmCount % maxDSM) == 0 ? 0 : 1); for(UA_UInt32 i = 0; i < nmCount; i++) { UA_Byte nmDsmCount = maxDSM; if(i == nmCount - 1 && (dsmCount % maxDSM)) nmDsmCount = (UA_Byte)dsmCount % maxDSM; UA_StatusCode res3 = UA_STATUSCODE_GOOD; if(writerGroup->config.encodingMimeType == UA_PUBSUB_ENCODING_UADP){ res3 = sendNetworkMessage(connection, writerGroup, &dsmStore[i * maxDSM], &dsWriterIds[i * maxDSM], nmDsmCount, &writerGroup->config.messageSettings, &writerGroup->config.transportSettings); }else if(writerGroup->config.encodingMimeType == UA_PUBSUB_ENCODING_JSON){ res3 = sendNetworkMessageJson(connection, &dsmStore[i * maxDSM], &dsWriterIds[i * maxDSM], nmDsmCount, &writerGroup->config.transportSettings); } if(res3 != UA_STATUSCODE_GOOD) UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub Publish: Sending a NetworkMessage failed"); } /* Clean up DSM */ for(size_t i = 0; i < dsmCount; i++) UA_DataSetMessage_free(&dsmStore[i]); } /* Add new publishCallback. The first execution is triggered directly after * creation. */ UA_StatusCode UA_WriterGroup_addPublishCallback(UA_Server *server, UA_WriterGroup *writerGroup) { UA_StatusCode retval = UA_PubSubManager_addRepeatedCallback(server, (UA_ServerCallback) UA_WriterGroup_publishCallback, writerGroup, writerGroup->config.publishingInterval, &writerGroup->publishCallbackId); if(retval == UA_STATUSCODE_GOOD) writerGroup->publishCallbackIsRegistered = true; /* Run once after creation */ UA_WriterGroup_publishCallback(server, writerGroup); return retval; } /* This callback triggers the collection and reception of NetworkMessages and the * contained DataSetMessages. */ void UA_ReaderGroup_subscribeCallback(UA_Server *server, UA_ReaderGroup *readerGroup) { UA_PubSubConnection *connection = UA_PubSubConnection_findConnectionbyId(server, readerGroup->linkedConnection); UA_ByteString buffer; if(UA_ByteString_allocBuffer(&buffer, 512) != UA_STATUSCODE_GOOD) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "Message buffer alloc failed!"); return; } connection->channel->receive(connection->channel, &buffer, NULL, 300000); if(buffer.length > 0) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_USERLAND, "Message received:"); UA_NetworkMessage currentNetworkMessage; memset(¤tNetworkMessage, 0, sizeof(UA_NetworkMessage)); size_t currentPosition = 0; UA_NetworkMessage_decodeBinary(&buffer, ¤tPosition, ¤tNetworkMessage); UA_Server_processNetworkMessage(server, ¤tNetworkMessage, connection); UA_NetworkMessage_deleteMembers(¤tNetworkMessage); } UA_ByteString_deleteMembers(&buffer); } /* Add new subscribeCallback. The first execution is triggered directly after * creation. */ UA_StatusCode UA_ReaderGroup_addSubscribeCallback(UA_Server *server, UA_ReaderGroup *readerGroup) { UA_StatusCode retval = UA_STATUSCODE_GOOD; UA_PubSubConnection *connection = UA_PubSubConnection_findConnectionbyId(server, readerGroup->linkedConnection); if(connection != NULL) { retval = connection->channel->regist(connection->channel, NULL, NULL); if(retval == UA_STATUSCODE_GOOD) { retval = UA_PubSubManager_addRepeatedCallback(server, (UA_ServerCallback) UA_ReaderGroup_subscribeCallback, readerGroup, 5, &readerGroup->subscribeCallbackId); } else { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "register channel failed: 0x%x!", retval); } } if(retval == UA_STATUSCODE_GOOD) { readerGroup->subscribeCallbackIsRegistered = true; } /* Run once after creation */ UA_ReaderGroup_subscribeCallback(server, readerGroup); return retval; } #endif /* UA_ENABLE_PUBSUB */ /*********************************** amalgamated original file "/home/jvoe/open62541/src/pubsub/ua_pubsub_manager.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright (c) 2017-2018 Fraunhofer IOSB (Author: Andreas Ebner) * Copyright (c) 2018 Fraunhofer IOSB (Author: Julius Pfrommer) */ #ifdef UA_ENABLE_PUBSUB /* conditional compilation */ #define UA_DATETIMESTAMP_2000 125911584000000000 UA_StatusCode UA_Server_addPubSubConnection(UA_Server *server, const UA_PubSubConnectionConfig *connectionConfig, UA_NodeId *connectionIdentifier) { /* Find the matching UA_PubSubTransportLayers */ UA_PubSubTransportLayer *tl = NULL; for(size_t i = 0; i < server->config.pubsubTransportLayersSize; i++) { if(connectionConfig && UA_String_equal(&server->config.pubsubTransportLayers[i].transportProfileUri, &connectionConfig->transportProfileUri)) { tl = &server->config.pubsubTransportLayers[i]; } } if(!tl) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub Connection creation failed. Requested transport layer not found."); return UA_STATUSCODE_BADNOTFOUND; } /* Create a copy of the connection config */ UA_PubSubConnectionConfig *tmpConnectionConfig = (UA_PubSubConnectionConfig *) UA_calloc(1, sizeof(UA_PubSubConnectionConfig)); if(!tmpConnectionConfig){ UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub Connection creation failed. Out of Memory."); return UA_STATUSCODE_BADOUTOFMEMORY; } UA_StatusCode retval = UA_PubSubConnectionConfig_copy(connectionConfig, tmpConnectionConfig); if(retval != UA_STATUSCODE_GOOD){ UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub Connection creation failed. Could not copy the config."); return retval; } /* Create new connection and add to UA_PubSubManager */ UA_PubSubConnection *newConnectionsField = (UA_PubSubConnection *) UA_realloc(server->pubSubManager.connections, sizeof(UA_PubSubConnection) * (server->pubSubManager.connectionsSize + 1)); if(!newConnectionsField) { UA_PubSubConnectionConfig_deleteMembers(tmpConnectionConfig); UA_free(tmpConnectionConfig); UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub Connection creation failed. Out of Memory."); return UA_STATUSCODE_BADOUTOFMEMORY; } server->pubSubManager.connections = newConnectionsField; server->pubSubManager.connectionsSize++; UA_PubSubConnection *newConnection = &server->pubSubManager.connections[server->pubSubManager.connectionsSize-1]; /* Initialize the new connection */ memset(newConnection, 0, sizeof(UA_PubSubConnection)); LIST_INIT(&newConnection->writerGroups); //workaround - fixing issue with queue.h and realloc. for(size_t n = 0; n < server->pubSubManager.connectionsSize; n++){ if(server->pubSubManager.connections[n].writerGroups.lh_first){ server->pubSubManager.connections[n].writerGroups.lh_first->listEntry.le_prev = &server->pubSubManager.connections[n].writerGroups.lh_first; } } newConnection->config = tmpConnectionConfig; /* Open the channel */ newConnection->channel = tl->createPubSubChannel(newConnection->config); if(!newConnection->channel) { UA_PubSubConnection_deleteMembers(server, newConnection); server->pubSubManager.connectionsSize--; /* Keep the realloced (longer) array if entries remain */ if(server->pubSubManager.connectionsSize == 0) { UA_free(server->pubSubManager.connections); server->pubSubManager.connections = NULL; } UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub Connection creation failed. Transport layer creation problem."); return UA_STATUSCODE_BADINTERNALERROR; } UA_PubSubManager_generateUniqueNodeId(server, &newConnection->identifier); if(connectionIdentifier) UA_NodeId_copy(&newConnection->identifier, connectionIdentifier); #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL addPubSubConnectionRepresentation(server, newConnection); #endif return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Server_removePubSubConnection(UA_Server *server, const UA_NodeId connection) { //search the identified Connection and store the Connection index size_t connectionIndex; UA_PubSubConnection *currentConnection = NULL; for(connectionIndex = 0; connectionIndex < server->pubSubManager.connectionsSize; connectionIndex++){ if(UA_NodeId_equal(&connection, &server->pubSubManager.connections[connectionIndex].identifier)){ currentConnection = &server->pubSubManager.connections[connectionIndex]; break; } } if(!currentConnection) return UA_STATUSCODE_BADNOTFOUND; #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL removePubSubConnectionRepresentation(server, currentConnection); #endif UA_PubSubConnection_deleteMembers(server, currentConnection); server->pubSubManager.connectionsSize--; //remove the connection from the pubSubManager, move the last connection //into the allocated memory of the deleted connection if(server->pubSubManager.connectionsSize != connectionIndex){ memcpy(&server->pubSubManager.connections[connectionIndex], &server->pubSubManager.connections[server->pubSubManager.connectionsSize], sizeof(UA_PubSubConnection)); } if(server->pubSubManager.connectionsSize <= 0){ UA_free(server->pubSubManager.connections); server->pubSubManager.connections = NULL; } else { server->pubSubManager.connections = (UA_PubSubConnection *) UA_realloc(server->pubSubManager.connections, sizeof(UA_PubSubConnection) * server->pubSubManager.connectionsSize); if(!server->pubSubManager.connections){ return UA_STATUSCODE_BADINTERNALERROR; } //workaround - fixing issue with queue.h and realloc. for(size_t n = 0; n < server->pubSubManager.connectionsSize; n++){ if(server->pubSubManager.connections[n].writerGroups.lh_first){ server->pubSubManager.connections[n].writerGroups.lh_first->listEntry.le_prev = &server->pubSubManager.connections[n].writerGroups.lh_first; } } } return UA_STATUSCODE_GOOD; } UA_AddPublishedDataSetResult UA_Server_addPublishedDataSet(UA_Server *server, const UA_PublishedDataSetConfig *publishedDataSetConfig, UA_NodeId *pdsIdentifier) { UA_AddPublishedDataSetResult result = {UA_STATUSCODE_BADINVALIDARGUMENT, 0, NULL, {0, 0}}; if(!publishedDataSetConfig){ UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "PublishedDataSet creation failed. No config passed in."); return result; } if(publishedDataSetConfig->publishedDataSetType != UA_PUBSUB_DATASET_PUBLISHEDITEMS){ UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "PublishedDataSet creation failed. Unsupported PublishedDataSet type."); return result; } //deep copy the given connection config UA_PublishedDataSetConfig tmpPublishedDataSetConfig; memset(&tmpPublishedDataSetConfig, 0, sizeof(UA_PublishedDataSetConfig)); if(UA_PublishedDataSetConfig_copy(publishedDataSetConfig, &tmpPublishedDataSetConfig) != UA_STATUSCODE_GOOD){ UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "PublishedDataSet creation failed. Configuration copy failed."); result.addResult = UA_STATUSCODE_BADINTERNALERROR; return result; } //create new PDS and add to UA_PubSubManager UA_PublishedDataSet *newPubSubDataSetField = (UA_PublishedDataSet *) UA_realloc(server->pubSubManager.publishedDataSets, sizeof(UA_PublishedDataSet) * (server->pubSubManager.publishedDataSetsSize + 1)); if(!newPubSubDataSetField) { UA_PublishedDataSetConfig_deleteMembers(&tmpPublishedDataSetConfig); UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "PublishedDataSet creation failed. Out of Memory."); result.addResult = UA_STATUSCODE_BADOUTOFMEMORY; return result; } server->pubSubManager.publishedDataSets = newPubSubDataSetField; UA_PublishedDataSet *newPubSubDataSet = &server->pubSubManager.publishedDataSets[(server->pubSubManager.publishedDataSetsSize)]; memset(newPubSubDataSet, 0, sizeof(UA_PublishedDataSet)); LIST_INIT(&newPubSubDataSet->fields); //workaround - fixing issue with queue.h and realloc. for(size_t n = 0; n < server->pubSubManager.publishedDataSetsSize; n++){ if(server->pubSubManager.publishedDataSets[n].fields.lh_first){ server->pubSubManager.publishedDataSets[n].fields.lh_first->listEntry.le_prev = &server->pubSubManager.publishedDataSets[n].fields.lh_first; } } newPubSubDataSet->config = tmpPublishedDataSetConfig; if(tmpPublishedDataSetConfig.publishedDataSetType == UA_PUBSUB_DATASET_PUBLISHEDITEMS_TEMPLATE){ //parse template config and add fields (later PubSub batch) } //generate unique nodeId UA_PubSubManager_generateUniqueNodeId(server, &newPubSubDataSet->identifier); if(pdsIdentifier != NULL){ UA_NodeId_copy(&newPubSubDataSet->identifier, pdsIdentifier); } server->pubSubManager.publishedDataSetsSize++; result.addResult = UA_STATUSCODE_GOOD; result.fieldAddResults = NULL; result.fieldAddResultsSize = 0; result.configurationVersion.majorVersion = UA_PubSubConfigurationVersionTimeDifference(); result.configurationVersion.minorVersion = UA_PubSubConfigurationVersionTimeDifference(); #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL addPublishedDataItemsRepresentation(server, newPubSubDataSet); #endif return result; } UA_StatusCode UA_Server_removePublishedDataSet(UA_Server *server, const UA_NodeId pds) { //search the identified PublishedDataSet and store the PDS index UA_PublishedDataSet *publishedDataSet = NULL; size_t publishedDataSetIndex; for(publishedDataSetIndex = 0; publishedDataSetIndex < server->pubSubManager.publishedDataSetsSize; publishedDataSetIndex++){ if(UA_NodeId_equal(&server->pubSubManager.publishedDataSets[publishedDataSetIndex].identifier, &pds)){ publishedDataSet = &server->pubSubManager.publishedDataSets[publishedDataSetIndex]; break; } } if(!publishedDataSet){ return UA_STATUSCODE_BADNOTFOUND; } //search for referenced writers -> delete this writers. (Standard: writer must be connected with PDS) for(size_t i = 0; i < server->pubSubManager.connectionsSize; i++){ UA_WriterGroup *writerGroup; LIST_FOREACH(writerGroup, &server->pubSubManager.connections[i].writerGroups, listEntry){ UA_DataSetWriter *currentWriter, *tmpWriterGroup; LIST_FOREACH_SAFE(currentWriter, &writerGroup->writers, listEntry, tmpWriterGroup){ if(UA_NodeId_equal(¤tWriter->connectedDataSet, &publishedDataSet->identifier)){ UA_Server_removeDataSetWriter(server, currentWriter->identifier); } } } } #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL removePublishedDataSetRepresentation(server, publishedDataSet); #endif UA_PublishedDataSet_deleteMembers(server, publishedDataSet); server->pubSubManager.publishedDataSetsSize--; //copy the last PDS to the removed PDS inside the allocated memory block if(server->pubSubManager.publishedDataSetsSize != publishedDataSetIndex){ memcpy(&server->pubSubManager.publishedDataSets[publishedDataSetIndex], &server->pubSubManager.publishedDataSets[server->pubSubManager.publishedDataSetsSize], sizeof(UA_PublishedDataSet)); } if(server->pubSubManager.publishedDataSetsSize <= 0){ UA_free(server->pubSubManager.publishedDataSets); server->pubSubManager.publishedDataSets = NULL; } else { server->pubSubManager.publishedDataSets = (UA_PublishedDataSet *) UA_realloc(server->pubSubManager.publishedDataSets, sizeof(UA_PublishedDataSet) * server->pubSubManager.publishedDataSetsSize); if(!server->pubSubManager.publishedDataSets){ return UA_STATUSCODE_BADINTERNALERROR; } //workaround - fixing issue with queue.h and realloc. for(size_t n = 0; n < server->pubSubManager.publishedDataSetsSize; n++){ if(server->pubSubManager.publishedDataSets[n].fields.lh_first){ server->pubSubManager.publishedDataSets[n].fields.lh_first->listEntry.le_prev = &server->pubSubManager.publishedDataSets[n].fields.lh_first; } } } return UA_STATUSCODE_GOOD; } /* Calculate the time difference between current time and UTC (00:00) on January * 1, 2000. */ UA_UInt32 UA_PubSubConfigurationVersionTimeDifference() { UA_UInt32 timeDiffSince2000 = (UA_UInt32) (UA_DateTime_now() - UA_DATETIMESTAMP_2000); return timeDiffSince2000; } /* Generate a new unique NodeId. This NodeId will be used for the information * model representation of PubSub entities. */ void UA_PubSubManager_generateUniqueNodeId(UA_Server *server, UA_NodeId *nodeId) { UA_NodeId newNodeId = UA_NODEID_NUMERIC(0, 0); UA_Node *newNode = UA_Nodestore_newNode(server->nsCtx, UA_NODECLASS_OBJECT); UA_Nodestore_insertNode(server->nsCtx, newNode, &newNodeId); UA_NodeId_copy(&newNodeId, nodeId); } /* Delete the current PubSub configuration including all nested members. This * action also delete the configured PubSub transport Layers. */ void UA_PubSubManager_delete(UA_Server *server, UA_PubSubManager *pubSubManager) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub cleanup was called."); //free the currently configured transport layers UA_free(server->config.pubsubTransportLayers); server->config.pubsubTransportLayersSize = 0; //remove Connections and WriterGroups while(pubSubManager->connectionsSize > 0){ UA_Server_removePubSubConnection(server, pubSubManager->connections[pubSubManager->connectionsSize-1].identifier); } while(pubSubManager->publishedDataSetsSize > 0){ UA_Server_removePublishedDataSet(server, pubSubManager->publishedDataSets[pubSubManager->publishedDataSetsSize-1].identifier); } } /***********************************/ /* PubSub Jobs abstraction */ /***********************************/ #ifndef UA_ENABLE_PUBSUB_CUSTOM_PUBLISH_HANDLING /* If UA_ENABLE_PUBSUB_CUSTOM_PUBLISH_INTERRUPT is enabled, a custom callback * management must be linked to the application */ UA_StatusCode UA_PubSubManager_addRepeatedCallback(UA_Server *server, UA_ServerCallback callback, void *data, UA_Double interval_ms, UA_UInt64 *callbackId) { return UA_Timer_addRepeatedCallback(&server->timer, (UA_ApplicationCallback)callback, server, data, interval_ms, callbackId); } UA_StatusCode UA_PubSubManager_changeRepeatedCallbackInterval(UA_Server *server, UA_UInt64 callbackId, UA_Double interval_ms) { return UA_Timer_changeRepeatedCallbackInterval(&server->timer, callbackId, interval_ms); } void UA_PubSubManager_removeRepeatedPubSubCallback(UA_Server *server, UA_UInt64 callbackId) { UA_Timer_removeCallback(&server->timer, callbackId); } #endif /* UA_ENABLE_PUBSUB_CUSTOM_PUBLISH_HANDLING */ #endif /* UA_ENABLE_PUBSUB */ /*********************************** amalgamated original file "/home/jvoe/open62541/src/pubsub/ua_pubsub_ns0.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright (c) 2017-2018 Fraunhofer IOSB (Author: Andreas Ebner) * Copyright (c) 2019 Kalycito Infotech Private Limited */ #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL /* conditional compilation */ typedef struct{ UA_NodeId parentNodeId; UA_UInt32 parentClassifier; UA_UInt32 elementClassiefier; } UA_NodePropertyContext; //Prototypes #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS static UA_StatusCode addWriterGroupAction(UA_Server *server, const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId, void *objectContext, size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output); static UA_StatusCode removeGroupAction(UA_Server *server, const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId, void *objectContext, size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output); static UA_StatusCode addDataSetWriterAction(UA_Server *server, const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId, void *objectContext, size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output); #endif static UA_StatusCode addPubSubObjectNode(UA_Server *server, char* name, UA_UInt32 objectid, UA_UInt32 parentid, UA_UInt32 referenceid, UA_UInt32 type_id) { UA_ObjectAttributes object_attr = UA_ObjectAttributes_default; object_attr.displayName = UA_LOCALIZEDTEXT("", name); return UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(0, objectid), UA_NODEID_NUMERIC(0, parentid), UA_NODEID_NUMERIC(0, referenceid), UA_QUALIFIEDNAME(0, name), UA_NODEID_NUMERIC(0, type_id), object_attr, NULL, NULL); } static UA_StatusCode writePubSubNs0VariableArray(UA_Server *server, UA_UInt32 id, void *v, size_t length, const UA_DataType *type) { UA_Variant var; UA_Variant_init(&var); UA_Variant_setArray(&var, v, length, type); return UA_Server_writeValue(server, UA_NODEID_NUMERIC(0, id), var); } static UA_NodeId findSingleChildNode(UA_Server *server, UA_QualifiedName targetName, UA_NodeId referenceTypeId, UA_NodeId startingNode){ UA_NodeId resultNodeId; UA_RelativePathElement rpe; UA_RelativePathElement_init(&rpe); rpe.referenceTypeId = referenceTypeId; rpe.isInverse = false; rpe.includeSubtypes = false; rpe.targetName = targetName; UA_BrowsePath bp; UA_BrowsePath_init(&bp); bp.startingNode = startingNode; bp.relativePath.elementsSize = 1; bp.relativePath.elements = &rpe; UA_BrowsePathResult bpr = UA_Server_translateBrowsePathToNodeIds(server, &bp); if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) return UA_NODEID_NULL; if(UA_NodeId_copy(&bpr.targets[0].targetId.nodeId, &resultNodeId) != UA_STATUSCODE_GOOD){ UA_BrowsePathResult_deleteMembers(&bpr); return UA_NODEID_NULL; } UA_BrowsePathResult_deleteMembers(&bpr); return resultNodeId; } static void onRead(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeid, void *context, const UA_NumericRange *range, const UA_DataValue *data) { UA_Variant value; UA_Variant_init(&value); const UA_NodePropertyContext *nodeContext = (const UA_NodePropertyContext*)context; const UA_NodeId *myNodeId = &nodeContext->parentNodeId; switch(nodeContext->parentClassifier){ case UA_NS0ID_PUBSUBCONNECTIONTYPE: { UA_PubSubConnection *pubSubConnection = UA_PubSubConnection_findConnectionbyId(server, *myNodeId); switch(nodeContext->elementClassiefier) { case UA_NS0ID_PUBSUBCONNECTIONTYPE_PUBLISHERID: if(pubSubConnection->config->publisherIdType == UA_PUBSUB_PUBLISHERID_STRING) { UA_Variant_setScalar(&value, &pubSubConnection->config->publisherId.numeric, &UA_TYPES[UA_TYPES_STRING]); } else { UA_Variant_setScalar(&value, &pubSubConnection->config->publisherId.numeric, &UA_TYPES[UA_TYPES_UINT32]); } break; default: UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Read error! Unknown property."); } break; } case UA_NS0ID_WRITERGROUPTYPE: { UA_WriterGroup *writerGroup = UA_WriterGroup_findWGbyId(server, *myNodeId); if(!writerGroup) return; switch(nodeContext->elementClassiefier){ case UA_NS0ID_WRITERGROUPTYPE_PUBLISHINGINTERVAL: UA_Variant_setScalar(&value, &writerGroup->config.publishingInterval, &UA_TYPES[UA_TYPES_DURATION]); break; default: UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Read error! Unknown property."); } break; } case UA_NS0ID_PUBLISHEDDATAITEMSTYPE: { UA_PublishedDataSet *publishedDataSet = UA_PublishedDataSet_findPDSbyId(server, *myNodeId); if(!publishedDataSet) return; switch(nodeContext->elementClassiefier) { case UA_NS0ID_PUBLISHEDDATAITEMSTYPE_PUBLISHEDDATA: { UA_PublishedVariableDataType *pvd = (UA_PublishedVariableDataType *) UA_calloc(publishedDataSet->fieldSize, sizeof(UA_PublishedVariableDataType)); size_t counter = 0; UA_DataSetField *field; LIST_FOREACH(field, &publishedDataSet->fields, listEntry) { pvd[counter].attributeId = UA_ATTRIBUTEID_VALUE; pvd[counter].publishedVariable = field->config.field.variable.publishParameters.publishedVariable; //UA_NodeId_copy(&field->config.field.variable.publishParameters.publishedVariable, &pvd[counter].publishedVariable); counter++; } UA_Variant_setArray(&value, pvd, publishedDataSet->fieldSize, &UA_TYPES[UA_TYPES_PUBLISHEDVARIABLEDATATYPE]); break; } case UA_NS0ID_PUBLISHEDDATAITEMSTYPE_DATASETMETADATA: { UA_Variant_setScalarCopy(&value, &publishedDataSet->dataSetMetaData, &UA_TYPES[UA_TYPES_DATASETMETADATATYPE]); break; } case UA_NS0ID_PUBLISHEDDATAITEMSTYPE_CONFIGURATIONVERSION: { UA_Variant_setScalarCopy(&value, &publishedDataSet->dataSetMetaData.configurationVersion, &UA_TYPES[UA_TYPES_CONFIGURATIONVERSIONDATATYPE]); break; } default: UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Read error! Unknown property."); } break; } default: UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Read error! Unknown parent element."); } UA_Server_writeValue(server, *nodeid, value); } static void onWrite(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, void *nodeContext, const UA_NumericRange *range, const UA_DataValue *data){ UA_Variant value; UA_NodeId myNodeId; UA_WriterGroup *writerGroup = NULL; switch(((UA_NodePropertyContext *) nodeContext)->parentClassifier){ case UA_NS0ID_PUBSUBCONNECTIONTYPE: //no runtime writable attributes break; case UA_NS0ID_WRITERGROUPTYPE: myNodeId = ((UA_NodePropertyContext *) nodeContext)->parentNodeId; writerGroup = UA_WriterGroup_findWGbyId(server, myNodeId); UA_WriterGroupConfig writerGroupConfig; memset(&writerGroupConfig, 0, sizeof(writerGroupConfig)); if(!writerGroup) return; switch(((UA_NodePropertyContext *) nodeContext)->elementClassiefier){ case UA_NS0ID_WRITERGROUPTYPE_PUBLISHINGINTERVAL: UA_Server_getWriterGroupConfig(server, writerGroup->identifier, &writerGroupConfig); writerGroupConfig.publishingInterval = *((UA_Duration *) data->value.data); UA_Server_updateWriterGroupConfig(server, writerGroup->identifier, &writerGroupConfig); UA_Variant_setScalar(&value, data->value.data, &UA_TYPES[UA_TYPES_DURATION]); UA_WriterGroupConfig_deleteMembers(&writerGroupConfig); break; default: UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Write error! Unknown property element."); } break; default: UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Read error! Unknown parent element."); } } static UA_StatusCode addVariableValueSource(UA_Server *server, UA_ValueCallback valueCallback, UA_NodeId node, UA_NodePropertyContext *context){ UA_Server_setNodeContext(server, node, context); return UA_Server_setVariableNode_valueCallback(server, node, valueCallback); } /*************************************************/ /* PubSubConnection */ /*************************************************/ UA_StatusCode addPubSubConnectionRepresentation(UA_Server *server, UA_PubSubConnection *connection){ UA_StatusCode retVal = UA_STATUSCODE_GOOD; if(connection->config->name.length > 512) return UA_STATUSCODE_BADOUTOFMEMORY; UA_STACKARRAY(char, connectionName, sizeof(char) * connection->config->name.length +1); memcpy(connectionName, connection->config->name.data, connection->config->name.length); connectionName[connection->config->name.length] = '\0'; //This code block must use a lock UA_Nodestore_removeNode(server->nsCtx, &connection->identifier); UA_NodeId pubSubConnectionNodeId; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("de-DE", connectionName); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(0, connection->identifier.identifier.numeric), UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPUBSUBCONNECTION), UA_QUALIFIEDNAME(0, connectionName), UA_NODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES], NULL, &pubSubConnectionNodeId); addPubSubObjectNode(server, "Address", connection->identifier.identifier.numeric+1, pubSubConnectionNodeId.identifier.numeric, UA_NS0ID_HASCOMPONENT, UA_NS0ID_NETWORKADDRESSURLTYPE); UA_Server_addNode_finish(server, pubSubConnectionNodeId); //End lock zone UA_NodeId addressNode, urlNode, interfaceNode, publisherIdNode, connectionPropertieNode, transportProfileUri; addressNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "Address"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_NODEID_NUMERIC(0, connection->identifier.identifier.numeric)); urlNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "Url"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), addressNode); interfaceNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "NetworkInterface"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), addressNode); publisherIdNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublisherId"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), UA_NODEID_NUMERIC(0, connection->identifier.identifier.numeric)); connectionPropertieNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "ConnectionProperties"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), UA_NODEID_NUMERIC(0, connection->identifier.identifier.numeric)); transportProfileUri = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "TransportProfileUri"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_NODEID_NUMERIC(0, connection->identifier.identifier.numeric)); if(UA_NodeId_equal(&addressNode, &UA_NODEID_NULL) || UA_NodeId_equal(&urlNode, &UA_NODEID_NULL) || UA_NodeId_equal(&interfaceNode, &UA_NODEID_NULL) || UA_NodeId_equal(&publisherIdNode, &UA_NODEID_NULL) || UA_NodeId_equal(&connectionPropertieNode, &UA_NODEID_NULL) || UA_NodeId_equal(&transportProfileUri, &UA_NODEID_NULL)) { return UA_STATUSCODE_BADNOTFOUND; } retVal |= writePubSubNs0VariableArray(server, connectionPropertieNode.identifier.numeric, connection->config->connectionProperties, connection->config->connectionPropertiesSize, &UA_TYPES[UA_TYPES_KEYVALUEPAIR]); UA_NetworkAddressUrlDataType *networkAddressUrlDataType = ((UA_NetworkAddressUrlDataType *) connection->config->address.data); UA_Variant value; UA_Variant_init(&value); UA_Variant_setScalar(&value, &networkAddressUrlDataType->url, &UA_TYPES[UA_TYPES_STRING]); UA_Server_writeValue(server, urlNode, value); UA_Variant_setScalar(&value, &networkAddressUrlDataType->networkInterface, &UA_TYPES[UA_TYPES_STRING]); UA_Server_writeValue(server, interfaceNode, value); UA_Variant_setScalar(&value, &connection->config->transportProfileUri, &UA_TYPES[UA_TYPES_STRING]); UA_Server_writeValue(server, transportProfileUri, value); UA_NodePropertyContext *connectionPublisherIdContext = (UA_NodePropertyContext *) UA_malloc(sizeof(UA_NodePropertyContext)); connectionPublisherIdContext->parentNodeId = connection->identifier; connectionPublisherIdContext->parentClassifier = UA_NS0ID_PUBSUBCONNECTIONTYPE; connectionPublisherIdContext->elementClassiefier = UA_NS0ID_PUBSUBCONNECTIONTYPE_PUBLISHERID; UA_ValueCallback valueCallback; valueCallback.onRead = onRead; valueCallback.onWrite = NULL; retVal |= addVariableValueSource(server, valueCallback, publisherIdNode, connectionPublisherIdContext); #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS retVal |= UA_Server_addReference(server, connection->identifier, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE_ADDWRITERGROUP), true); retVal |= UA_Server_addReference(server, connection->identifier, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE_ADDREADERGROUP), true); retVal |= UA_Server_addReference(server, connection->identifier, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE_REMOVEGROUP), true); #endif return retVal; } #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS static UA_StatusCode addPubSubConnectionAction(UA_Server *server, const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId, void *objectContext, size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output){ UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_PubSubConnectionDataType pubSubConnectionDataType = *((UA_PubSubConnectionDataType *) input[0].data); UA_NetworkAddressUrlDataType networkAddressUrlDataType; memset(&networkAddressUrlDataType, 0, sizeof(networkAddressUrlDataType)); UA_ExtensionObject eo = pubSubConnectionDataType.address; if(eo.encoding == UA_EXTENSIONOBJECT_DECODED){ if(eo.content.decoded.type == &UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]){ if(UA_NetworkAddressUrlDataType_copy((UA_NetworkAddressUrlDataType *) eo.content.decoded.data, &networkAddressUrlDataType) != UA_STATUSCODE_GOOD){ return UA_STATUSCODE_BADOUTOFMEMORY; } } } UA_PubSubConnectionConfig connectionConfig; memset(&connectionConfig, 0, sizeof(UA_PubSubConnectionConfig)); connectionConfig.transportProfileUri = pubSubConnectionDataType.transportProfileUri; connectionConfig.name = pubSubConnectionDataType.name; //TODO set real connection state connectionConfig.enabled = pubSubConnectionDataType.enabled; //connectionConfig.enabled = pubSubConnectionDataType.enabled; UA_Variant_setScalar(&connectionConfig.address, &networkAddressUrlDataType, &UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]); if(pubSubConnectionDataType.publisherId.type == &UA_TYPES[UA_TYPES_UINT32]){ connectionConfig.publisherId.numeric = * ((UA_UInt32 *) pubSubConnectionDataType.publisherId.data); } else if(pubSubConnectionDataType.publisherId.type == &UA_TYPES[UA_TYPES_STRING]){ connectionConfig.publisherIdType = UA_PUBSUB_PUBLISHERID_STRING; UA_String_copy((UA_String *) pubSubConnectionDataType.publisherId.data, &connectionConfig.publisherId.string); } else { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Unsupported PublisherId Type used."); //TODO what's the best default behaviour here? connectionConfig.publisherId.numeric = 0; } //call API function and create the connection UA_NodeId connectionId; retVal |= UA_Server_addPubSubConnection(server, &connectionConfig, &connectionId); if(retVal != UA_STATUSCODE_GOOD){ return retVal; } for(size_t i = 0; i < pubSubConnectionDataType.writerGroupsSize; i++){ //UA_PubSubConnection_addWriterGroup(server, UA_NODEID_NULL, NULL, NULL); } for(size_t i = 0; i < pubSubConnectionDataType.readerGroupsSize; i++){ //UA_Server_addReaderGroup(server, NULL, NULL, NULL); } UA_NetworkAddressUrlDataType_deleteMembers(&networkAddressUrlDataType); //set ouput value UA_Variant_setScalarCopy(output, &connectionId, &UA_TYPES[UA_TYPES_NODEID]); return UA_STATUSCODE_GOOD; } #endif UA_StatusCode removePubSubConnectionRepresentation(UA_Server *server, UA_PubSubConnection *connection){ UA_StatusCode retVal = UA_STATUSCODE_GOOD; #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS retVal |= UA_Server_deleteReference(server, connection->identifier, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), true, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE_ADDWRITERGROUP), false); retVal |= UA_Server_deleteReference(server, connection->identifier, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), true, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE_ADDREADERGROUP), false); retVal |= UA_Server_deleteReference(server, connection->identifier, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), true, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE_REMOVEGROUP), false); #endif retVal |= UA_Server_deleteNode(server, connection->identifier, true); return retVal; } #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS static UA_StatusCode removeConnectionAction(UA_Server *server, const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId, void *objectContext, size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output){ UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_NodeId nodeToRemove = *((UA_NodeId *) input[0].data); retVal |= UA_Server_removePubSubConnection(server, nodeToRemove); if(retVal == UA_STATUSCODE_BADNOTFOUND) retVal = UA_STATUSCODE_BADNODEIDUNKNOWN; return retVal; } #endif /**********************************************/ /* DataSetReader */ /**********************************************/ UA_StatusCode addDataSetReaderRepresentation(UA_Server *server, UA_DataSetReader *dataSetReader){ //TODO implement reader part return UA_STATUSCODE_BADNOTIMPLEMENTED; } #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS static UA_StatusCode addDataSetReaderAction(UA_Server *server, const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId, void *objectContext, size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output){ UA_StatusCode retVal = UA_STATUSCODE_BADNOTIMPLEMENTED; //TODO implement reader part return retVal; } #endif UA_StatusCode removeDataSetReaderRepresentation(UA_Server *server, UA_DataSetReader* dataSetReader){ //TODO implement reader part return UA_STATUSCODE_BADNOTIMPLEMENTED; } #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS static UA_StatusCode removeDataSetReaderAction(UA_Server *server, const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId, void *objectContext, size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output){ UA_StatusCode retVal = UA_STATUSCODE_BADNOTIMPLEMENTED; //TODO implement reader part return retVal; } #endif /*************************************************/ /* PublishedDataSet */ /*************************************************/ #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS static UA_StatusCode addDataSetFolderAction(UA_Server *server, const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId, void *objectContext, size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output){ /* defined in R 1.04 9.1.4.5.7 */ UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_String newFolderName = *((UA_String *) input[0].data); UA_NodeId generatedId; UA_ObjectAttributes objectAttributes = UA_ObjectAttributes_default; UA_LocalizedText name = {UA_STRING("en-US"), newFolderName}; objectAttributes.displayName = name; retVal |= UA_Server_addObjectNode(server, UA_NODEID_NULL, *objectId, UA_NODEID_NUMERIC(0,UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(0, "DataSetFolder"), UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE), objectAttributes, NULL, &generatedId); UA_Variant_setScalarCopy(output, &generatedId, &UA_TYPES[UA_TYPES_NODEID]); #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS retVal |= UA_Server_addReference(server, generatedId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_ADDPUBLISHEDDATAITEMS), true); retVal |= UA_Server_addReference(server, generatedId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_REMOVEPUBLISHEDDATASET), true); retVal |= UA_Server_addReference(server, generatedId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_ADDDATASETFOLDER), true); retVal |= UA_Server_addReference(server, generatedId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_REMOVEDATASETFOLDER), true); #endif return retVal; } #endif #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS static UA_StatusCode removeDataSetFolderAction(UA_Server *server, const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId, void *objectContext, size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output){ UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_NodeId nodeToRemove = *((UA_NodeId *) input[0].data); #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS retVal |= UA_Server_deleteReference(server, nodeToRemove, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), true, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_ADDPUBLISHEDDATAITEMS), false); retVal |= UA_Server_deleteReference(server, nodeToRemove, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), true, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_REMOVEPUBLISHEDDATASET), false); retVal |= UA_Server_deleteReference(server, nodeToRemove, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), true, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_ADDDATASETFOLDER), false); retVal |= UA_Server_deleteReference(server, nodeToRemove, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), true, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_REMOVEDATASETFOLDER), false); #endif retVal |= UA_Server_deleteNode(server, nodeToRemove, false); return retVal; } #endif UA_StatusCode addPublishedDataItemsRepresentation(UA_Server *server, UA_PublishedDataSet *publishedDataSet) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; if(publishedDataSet->config.name.length > 512) return UA_STATUSCODE_BADOUTOFMEMORY; UA_STACKARRAY(char, pdsName, sizeof(char) * publishedDataSet->config.name.length +1); memcpy(pdsName, publishedDataSet->config.name.data, publishedDataSet->config.name.length); pdsName[publishedDataSet->config.name.length] = '\0'; //This code block must use a lock UA_Nodestore_removeNode(server->nsCtx, &publishedDataSet->identifier); retVal |= addPubSubObjectNode(server, pdsName, publishedDataSet->identifier.identifier.numeric, UA_NS0ID_PUBLISHSUBSCRIBE_PUBLISHEDDATASETS, UA_NS0ID_HASPROPERTY, UA_NS0ID_PUBLISHEDDATAITEMSTYPE); //End lock zone UA_ValueCallback valueCallback; valueCallback.onRead = onRead; valueCallback.onWrite = NULL; UA_NodeId configurationVersionNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "ConfigurationVersion"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), UA_NODEID_NUMERIC(0, publishedDataSet->identifier.identifier.numeric)); if(UA_NodeId_equal(&configurationVersionNode, &UA_NODEID_NULL)) return UA_STATUSCODE_BADNOTFOUND; UA_NodePropertyContext * configurationVersionContext = (UA_NodePropertyContext *) UA_malloc(sizeof(UA_NodePropertyContext)); configurationVersionContext->parentNodeId = publishedDataSet->identifier; configurationVersionContext->parentClassifier = UA_NS0ID_PUBLISHEDDATAITEMSTYPE; configurationVersionContext->elementClassiefier = UA_NS0ID_PUBLISHEDDATAITEMSTYPE_CONFIGURATIONVERSION; retVal |= addVariableValueSource(server, valueCallback, configurationVersionNode, configurationVersionContext); UA_NodeId publishedDataNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublishedData"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), UA_NODEID_NUMERIC(0, publishedDataSet->identifier.identifier.numeric)); if(UA_NodeId_equal(&publishedDataNode, &UA_NODEID_NULL)) return UA_STATUSCODE_BADNOTFOUND; UA_NodePropertyContext * publishingIntervalContext = (UA_NodePropertyContext *) UA_malloc(sizeof(UA_NodePropertyContext)); publishingIntervalContext->parentNodeId = publishedDataSet->identifier; publishingIntervalContext->parentClassifier = UA_NS0ID_PUBLISHEDDATAITEMSTYPE; publishingIntervalContext->elementClassiefier = UA_NS0ID_PUBLISHEDDATAITEMSTYPE_PUBLISHEDDATA; retVal |= addVariableValueSource(server, valueCallback, publishedDataNode, publishingIntervalContext); UA_NodeId dataSetMetaDataNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "DataSetMetaData"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), UA_NODEID_NUMERIC(0, publishedDataSet->identifier.identifier.numeric)); if(UA_NodeId_equal(&dataSetMetaDataNode, &UA_NODEID_NULL)) return UA_STATUSCODE_BADNOTFOUND; UA_NodePropertyContext *metaDataContext = (UA_NodePropertyContext *) UA_malloc(sizeof(UA_NodePropertyContext)); metaDataContext->parentNodeId = publishedDataSet->identifier; metaDataContext->parentClassifier = UA_NS0ID_PUBLISHEDDATAITEMSTYPE; metaDataContext->elementClassiefier = UA_NS0ID_PUBLISHEDDATAITEMSTYPE_DATASETMETADATA; retVal |= addVariableValueSource(server, valueCallback, dataSetMetaDataNode, metaDataContext); #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS retVal |= UA_Server_addReference(server, publishedDataSet->identifier, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PUBLISHEDDATAITEMSTYPE_ADDVARIABLES), true); retVal |= UA_Server_addReference(server, publishedDataSet->identifier, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PUBLISHEDDATAITEMSTYPE_REMOVEVARIABLES), true); #endif return retVal; } #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS static UA_StatusCode addPublishedDataItemsAction(UA_Server *server, const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId, void *objectContext, size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output){ UA_StatusCode retVal = UA_STATUSCODE_GOOD; size_t fieldNameAliasesSize = input[1].arrayLength; UA_String * fieldNameAliases = (UA_String *) input[1].data; size_t fieldFlagsSize = input[2].arrayLength; UA_DataSetFieldFlags * fieldFlags = (UA_DataSetFieldFlags *) input[2].data; size_t variablesToAddSize = input[3].arrayLength; UA_PublishedVariableDataType *variablesToAddField = (UA_PublishedVariableDataType *) input[3].data; if(!(fieldNameAliasesSize == fieldFlagsSize || fieldFlagsSize == variablesToAddSize)) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_PublishedDataSetConfig publishedDataSetConfig; memset(&publishedDataSetConfig, 0, sizeof(publishedDataSetConfig)); publishedDataSetConfig.name = *((UA_String *) input[0].data); publishedDataSetConfig.publishedDataSetType = UA_PUBSUB_DATASET_PUBLISHEDITEMS; UA_NodeId dataSetItemsNodeId; retVal |= UA_Server_addPublishedDataSet(server, &publishedDataSetConfig, &dataSetItemsNodeId).addResult; UA_DataSetFieldConfig dataSetFieldConfig; for(size_t j = 0; j < variablesToAddSize; ++j) { memset(&dataSetFieldConfig, 0, sizeof(dataSetFieldConfig)); dataSetFieldConfig.dataSetFieldType = UA_PUBSUB_DATASETFIELD_VARIABLE; dataSetFieldConfig.field.variable.fieldNameAlias = fieldNameAliases[j]; if(fieldFlags[j] == UA_DATASETFIELDFLAGS_PROMOTEDFIELD){ dataSetFieldConfig.field.variable.promotedField = UA_TRUE; } dataSetFieldConfig.field.variable.publishParameters = variablesToAddField[j]; UA_Server_addDataSetField(server, dataSetItemsNodeId, &dataSetFieldConfig, NULL); } UA_PublishedVariableDataType_clear(variablesToAddField); return retVal; } #endif #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS static UA_StatusCode addVariablesAction(UA_Server *server, const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId, void *objectContext, size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output){ UA_StatusCode retVal = UA_STATUSCODE_GOOD; return retVal; } static UA_StatusCode removeVariablesAction(UA_Server *server, const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId, void *objectContext, size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output){ UA_StatusCode retVal = UA_STATUSCODE_GOOD; return retVal; } #endif UA_StatusCode removePublishedDataSetRepresentation(UA_Server *server, UA_PublishedDataSet *publishedDataSet){ UA_StatusCode retVal = UA_STATUSCODE_GOOD; retVal |= UA_Server_deleteNode(server, publishedDataSet->identifier, false); return retVal; } #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS static UA_StatusCode removePublishedDataSetAction(UA_Server *server, const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId, void *objectContext, size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output){ UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_NodeId nodeToRemove = *((UA_NodeId *) input[0].data); retVal |= UA_Server_removePublishedDataSet(server, nodeToRemove); return retVal; } #endif /**********************************************/ /* WriterGroup */ /**********************************************/ static UA_StatusCode readContentMask(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, void *nodeContext, UA_Boolean includeSourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value) { UA_WriterGroup *writerGroup = (UA_WriterGroup*)nodeContext; if((writerGroup->config.messageSettings.encoding != UA_EXTENSIONOBJECT_DECODED && writerGroup->config.messageSettings.encoding != UA_EXTENSIONOBJECT_DECODED_NODELETE) || writerGroup->config.messageSettings.content.decoded.type != &UA_TYPES[UA_TYPES_UADPWRITERGROUPMESSAGEDATATYPE]) return UA_STATUSCODE_BADINTERNALERROR; UA_UadpWriterGroupMessageDataType *wgm = (UA_UadpWriterGroupMessageDataType*) writerGroup->config.messageSettings.content.decoded.data; UA_Variant_setScalarCopy(&value->value, &wgm->networkMessageContentMask, &UA_TYPES[UA_TYPES_UADPNETWORKMESSAGECONTENTMASK]); value->hasValue = true; return UA_STATUSCODE_GOOD; } static UA_StatusCode writeContentMask(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, void *nodeContext, const UA_NumericRange *range, const UA_DataValue *value) { UA_WriterGroup *writerGroup = (UA_WriterGroup*)nodeContext; if((writerGroup->config.messageSettings.encoding != UA_EXTENSIONOBJECT_DECODED && writerGroup->config.messageSettings.encoding != UA_EXTENSIONOBJECT_DECODED_NODELETE) || writerGroup->config.messageSettings.content.decoded.type != &UA_TYPES[UA_TYPES_UADPWRITERGROUPMESSAGEDATATYPE]) return UA_STATUSCODE_BADINTERNALERROR; UA_UadpWriterGroupMessageDataType *wgm = (UA_UadpWriterGroupMessageDataType*) writerGroup->config.messageSettings.content.decoded.data; if(!value->value.type) return UA_STATUSCODE_BADTYPEMISMATCH; if(value->value.type->typeKind != UA_DATATYPEKIND_ENUM && value->value.type->typeKind != UA_DATATYPEKIND_INT32) return UA_STATUSCODE_BADTYPEMISMATCH; wgm->networkMessageContentMask = *(UA_UadpNetworkMessageContentMask*)value->value.data; return UA_STATUSCODE_GOOD; } UA_StatusCode addWriterGroupRepresentation(UA_Server *server, UA_WriterGroup *writerGroup){ UA_StatusCode retVal = UA_STATUSCODE_GOOD; if(writerGroup->config.name.length > 512) return UA_STATUSCODE_BADOUTOFMEMORY; UA_STACKARRAY(char, wgName, sizeof(char) * writerGroup->config.name.length + 1); memcpy(wgName, writerGroup->config.name.data, writerGroup->config.name.length); wgName[writerGroup->config.name.length] = '\0'; //This code block must use a lock UA_Nodestore_removeNode(server->nsCtx, &writerGroup->identifier); retVal |= addPubSubObjectNode(server, wgName, writerGroup->identifier.identifier.numeric, writerGroup->linkedConnection.identifier.numeric, UA_NS0ID_HASCOMPONENT, UA_NS0ID_WRITERGROUPTYPE); //End lock zone UA_NodeId keepAliveNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "KeepAliveTime"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), UA_NODEID_NUMERIC(0, writerGroup->identifier.identifier.numeric)); UA_NodeId publishingIntervalNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublishingInterval"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), UA_NODEID_NUMERIC(0, writerGroup->identifier.identifier.numeric)); if(UA_NodeId_equal(&keepAliveNode, &UA_NODEID_NULL) || UA_NodeId_equal(&publishingIntervalNode, &UA_NODEID_NULL)) return UA_STATUSCODE_BADNOTFOUND; UA_NodePropertyContext * publishingIntervalContext = (UA_NodePropertyContext *) UA_malloc(sizeof(UA_NodePropertyContext)); publishingIntervalContext->parentNodeId = writerGroup->identifier; publishingIntervalContext->parentClassifier = UA_NS0ID_WRITERGROUPTYPE; publishingIntervalContext->elementClassiefier = UA_NS0ID_WRITERGROUPTYPE_PUBLISHINGINTERVAL; UA_ValueCallback valueCallback; valueCallback.onRead = onRead; valueCallback.onWrite = onWrite; retVal |= addVariableValueSource(server, valueCallback, publishingIntervalNode, publishingIntervalContext); UA_Server_writeAccessLevel(server, publishingIntervalNode, UA_ACCESSLEVELMASK_READ ^ UA_ACCESSLEVELMASK_WRITE); UA_NodeId priorityNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "Priority"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), UA_NODEID_NUMERIC(0, writerGroup->identifier.identifier.numeric)); UA_NodeId writerGroupIdNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "WriterGroupId"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), UA_NODEID_NUMERIC(0, writerGroup->identifier.identifier.numeric)); UA_Variant value; UA_Variant_init(&value); UA_Variant_setScalar(&value, &writerGroup->config.publishingInterval, &UA_TYPES[UA_TYPES_DURATION]); UA_Server_writeValue(server, publishingIntervalNode, value); UA_Variant_setScalar(&value, &writerGroup->config.keepAliveTime, &UA_TYPES[UA_TYPES_DURATION]); UA_Server_writeValue(server, keepAliveNode, value); UA_Variant_setScalar(&value, &writerGroup->config.priority, &UA_TYPES[UA_TYPES_BYTE]); UA_Server_writeValue(server, priorityNode, value); UA_Variant_setScalar(&value, &writerGroup->config.writerGroupId, &UA_TYPES[UA_TYPES_UINT16]); UA_Server_writeValue(server, writerGroupIdNode, value); retVal |= addPubSubObjectNode(server, "MessageSettings", 0, writerGroup->identifier.identifier.numeric, UA_NS0ID_HASCOMPONENT, UA_NS0ID_UADPWRITERGROUPMESSAGETYPE); /* Find the variable with the content mask */ UA_NodeId messageSettingsId = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "MessageSettings"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_NODEID_NUMERIC(0, writerGroup->identifier.identifier.numeric)); UA_NodeId contentMaskId = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "NetworkMessageContentMask"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), messageSettingsId); if(UA_NodeId_equal(&messageSettingsId, &UA_NODEID_NULL) || UA_NodeId_equal(&contentMaskId, &UA_NODEID_NULL)) { return UA_STATUSCODE_BADNOTFOUND; } /* Set the callback */ UA_DataSource ds; ds.read = readContentMask; ds.write = writeContentMask; UA_Server_setVariableNode_dataSource(server, contentMaskId, ds); UA_Server_setNodeContext(server, contentMaskId, writerGroup); /* Make writable */ UA_Server_writeAccessLevel(server, contentMaskId, UA_ACCESSLEVELMASK_WRITE | UA_ACCESSLEVELMASK_READ); return retVal; } #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS static UA_StatusCode addWriterGroupAction(UA_Server *server, const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId, void *objectContext, size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output){ UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_WriterGroupDataType *writerGroupDataType = ((UA_WriterGroupDataType *) input[0].data); UA_NodeId generatedId; UA_WriterGroupConfig writerGroupConfig; memset(&writerGroupConfig, 0, sizeof(UA_WriterGroupConfig)); writerGroupConfig.name = writerGroupDataType->name; writerGroupConfig.publishingInterval = writerGroupDataType->publishingInterval; writerGroupConfig.writerGroupId = writerGroupDataType->writerGroupId; writerGroupConfig.enabled = writerGroupDataType->enabled; writerGroupConfig.priority = writerGroupDataType->priority; //TODO remove hard coded UADP writerGroupConfig.encodingMimeType = UA_PUBSUB_ENCODING_UADP; //ToDo transfer all arguments to internal WGConfiguration retVal |= UA_Server_addWriterGroup(server, *objectId, &writerGroupConfig, &generatedId); UA_Variant_setScalarCopy(output, &generatedId, &UA_TYPES[UA_TYPES_NODEID]); return retVal; } #endif UA_StatusCode removeGroupRepresentation(UA_Server *server, UA_WriterGroup *writerGroup) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; retVal |= UA_Server_deleteNode(server, writerGroup->identifier, false); return retVal; } #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS static UA_StatusCode removeGroupAction(UA_Server *server, const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId, void *objectContext, size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output){ UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_NodeId nodeToRemove = *((UA_NodeId *) input[0].data); if(UA_WriterGroup_findWGbyId(server, nodeToRemove) != NULL) retVal |= UA_Server_removeWriterGroup(server, nodeToRemove); //else //retVal |= UA_Server_removeReaderGroup(server, nodeToRemve); return retVal; } #endif /**********************************************/ /* ReaderGroup */ /**********************************************/ UA_StatusCode addReaderGroupRepresentation(UA_Server *server, UA_ReaderGroup *readerGroup){ //TODO implement reader part return UA_STATUSCODE_BADNOTIMPLEMENTED; } #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS static UA_StatusCode addReaderGroupAction(UA_Server *server, const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId, void *objectContext, size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output){ UA_StatusCode retVal = UA_STATUSCODE_GOOD; //TODO implement reader part return retVal; } #endif /**********************************************/ /* DataSetWriter */ /**********************************************/ UA_StatusCode addDataSetWriterRepresentation(UA_Server *server, UA_DataSetWriter *dataSetWriter){ UA_StatusCode retVal = UA_STATUSCODE_GOOD; if(dataSetWriter->config.name.length > 512) return UA_STATUSCODE_BADOUTOFMEMORY; UA_STACKARRAY(char, dswName, sizeof(char) * dataSetWriter->config.name.length + 1); memcpy(dswName, dataSetWriter->config.name.data, dataSetWriter->config.name.length); dswName[dataSetWriter->config.name.length] = '\0'; //This code block must use a lock UA_Nodestore_removeNode(server->nsCtx, &dataSetWriter->identifier); retVal |= addPubSubObjectNode(server, dswName, dataSetWriter->identifier.identifier.numeric, dataSetWriter->linkedWriterGroup.identifier.numeric, UA_NS0ID_HASDATASETWRITER, UA_NS0ID_DATASETWRITERTYPE); //End lock zone retVal |= UA_Server_addReference(server, dataSetWriter->connectedDataSet, UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETTOWRITER), UA_EXPANDEDNODEID_NUMERIC(0, dataSetWriter->identifier.identifier.numeric), true); retVal |= addPubSubObjectNode(server, "MessageSettings", 0, dataSetWriter->identifier.identifier.numeric, UA_NS0ID_HASCOMPONENT, UA_NS0ID_UADPDATASETWRITERMESSAGETYPE); return retVal; } #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS static UA_StatusCode addDataSetWriterAction(UA_Server *server, const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId, void *objectContext, size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output){ UA_DataSetWriterDataType *dataSetWriterDataType = (UA_DataSetWriterDataType *) input[0].data; UA_NodeId targetPDS = UA_NODEID_NULL; for(size_t i = 0; i < server->pubSubManager.publishedDataSetsSize; ++i) { if(UA_String_equal(&dataSetWriterDataType->dataSetName, &server->pubSubManager.publishedDataSets[i].config.name)){ targetPDS = server->pubSubManager.publishedDataSets[i].identifier; } } if(UA_NodeId_isNull(&targetPDS)) return UA_STATUSCODE_BADPARENTNODEIDINVALID; UA_NodeId generatedId; UA_DataSetWriterConfig dataSetWriterConfig; memset(&dataSetWriterConfig, 0, sizeof(UA_DataSetWriterConfig)); dataSetWriterConfig.name = dataSetWriterDataType->name; dataSetWriterConfig.dataSetName = dataSetWriterDataType->dataSetName; dataSetWriterConfig.keyFrameCount = dataSetWriterDataType->keyFrameCount; dataSetWriterConfig.dataSetWriterId = dataSetWriterDataType->dataSetWriterId; UA_Server_addDataSetWriter(server, *objectId, targetPDS, &dataSetWriterConfig, &generatedId); UA_Variant_setScalarCopy(output, &generatedId, &UA_TYPES[UA_TYPES_NODEID]); return UA_STATUSCODE_GOOD; } #endif UA_StatusCode removeDataSetWriterRepresentation(UA_Server *server, UA_DataSetWriter *dataSetWriter) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; retVal |= UA_Server_deleteNode(server, dataSetWriter->identifier, false); return retVal; } #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS static UA_StatusCode removeDataSetWriterAction(UA_Server *server, const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId, void *objectContext, size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output){ UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_NodeId nodeToRemove = *((UA_NodeId *) input[0].data); retVal |= UA_Server_removeDataSetWriter(server, nodeToRemove); return retVal; } #endif /**********************************************/ /* Destructors */ /**********************************************/ static void connectionTypeDestructor(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *typeId, void *typeContext, const UA_NodeId *nodeId, void **nodeContext) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_USERLAND, "Connection destructor called!"); UA_NodeId publisherIdNode; publisherIdNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublisherId"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), *nodeId); UA_NodePropertyContext *internalConnectionContext; UA_Server_getNodeContext(server, publisherIdNode, (void **) &internalConnectionContext); if(!UA_NodeId_equal(&UA_NODEID_NULL , &publisherIdNode)){ UA_free(internalConnectionContext); } } static void writerGroupTypeDestructor(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *typeId, void *typeContext, const UA_NodeId *nodeId, void **nodeContext) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_USERLAND, "WriterGroup destructor called!"); UA_NodeId intervalNode; intervalNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublishingInterval"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), *nodeId); UA_NodePropertyContext *internalConnectionContext; UA_Server_getNodeContext(server, intervalNode, (void **) &internalConnectionContext); if(!UA_NodeId_equal(&UA_NODEID_NULL , &intervalNode)){ UA_free(internalConnectionContext); } } static void readerGroupTypeDestructor(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *typeId, void *typeContext, const UA_NodeId *nodeId, void **nodeContext) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_USERLAND, "ReaderGroup destructor called!"); } static void dataSetWriterTypeDestructor(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *typeId, void *typeContext, const UA_NodeId *nodeId, void **nodeContext) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_USERLAND, "DataSetWriter destructor called!"); } static void dataSetReaderTypeDestructor(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *typeId, void *typeContext, const UA_NodeId *nodeId, void **nodeContext) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_USERLAND, "DataSetReader destructor called!"); } static void publishedDataItemsTypeDestructor(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *typeId, void *typeContext, const UA_NodeId *nodeId, void **nodeContext) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_USERLAND, "PublishedDataItems destructor called!"); void *childContext; UA_NodeId node = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublishedData"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), *nodeId); UA_Server_getNodeContext(server, node, (void**)&childContext); if(!UA_NodeId_equal(&UA_NODEID_NULL , &node)) UA_free(childContext); node = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "ConfigurationVersion"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), *nodeId); UA_Server_getNodeContext(server, node, (void**)&childContext); if(!UA_NodeId_equal(&UA_NODEID_NULL , &node)) UA_free(childContext); node = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "DataSetMetaData"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), *nodeId); UA_Server_getNodeContext(server, node, (void**)&childContext); if(!UA_NodeId_equal(&node, &UA_NODEID_NULL)) UA_free(childContext); } UA_StatusCode UA_Server_initPubSubNS0(UA_Server *server) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_String profileArray[1]; profileArray[0] = UA_STRING("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp"); retVal |= writePubSubNs0VariableArray(server, UA_NS0ID_PUBLISHSUBSCRIBE_SUPPORTEDTRANSPORTPROFILES, profileArray, 1, &UA_TYPES[UA_TYPES_STRING]); #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS retVal |= UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE_ADDCONNECTION), addPubSubConnectionAction); retVal |= UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE_REMOVECONNECTION), removeConnectionAction); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE_PUBLISHEDDATASETS), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_ADDDATASETFOLDER), true); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE_PUBLISHEDDATASETS), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_ADDPUBLISHEDDATAITEMS), true); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE_PUBLISHEDDATASETS), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_REMOVEPUBLISHEDDATASET), true); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE_PUBLISHEDDATASETS), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_REMOVEDATASETFOLDER), true); retVal |= UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_ADDDATASETFOLDER), addDataSetFolderAction); retVal |= UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_REMOVEDATASETFOLDER), removeDataSetFolderAction); retVal |= UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_ADDPUBLISHEDDATAITEMS), addPublishedDataItemsAction); retVal |= UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_REMOVEPUBLISHEDDATASET), removePublishedDataSetAction); retVal |= UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHEDDATAITEMSTYPE_ADDVARIABLES), addVariablesAction); retVal |= UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHEDDATAITEMSTYPE_REMOVEVARIABLES), removeVariablesAction); retVal |= UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE_ADDWRITERGROUP), addWriterGroupAction); retVal |= UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE_ADDREADERGROUP), addReaderGroupAction); retVal |= UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE_REMOVEGROUP), removeGroupAction); retVal |= UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_WRITERGROUPTYPE_ADDDATASETWRITER), addDataSetWriterAction); retVal |= UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_WRITERGROUPTYPE_REMOVEDATASETWRITER), removeDataSetWriterAction); retVal |= UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_READERGROUPTYPE_ADDDATASETREADER), addDataSetReaderAction); retVal |= UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_READERGROUPTYPE_REMOVEDATASETREADER), removeDataSetReaderAction); #else retVal |= UA_Server_deleteReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), true, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE_ADDCONNECTION), false); retVal |= UA_Server_deleteReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), true, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE_REMOVECONNECTION), false); #endif UA_NodeTypeLifecycle liveCycle; liveCycle.constructor = NULL; liveCycle.destructor = connectionTypeDestructor; UA_Server_setNodeTypeLifecycle(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE), liveCycle); liveCycle.destructor = writerGroupTypeDestructor; UA_Server_setNodeTypeLifecycle(server, UA_NODEID_NUMERIC(0, UA_NS0ID_WRITERGROUPTYPE), liveCycle); liveCycle.destructor = readerGroupTypeDestructor; UA_Server_setNodeTypeLifecycle(server, UA_NODEID_NUMERIC(0, UA_NS0ID_READERGROUPTYPE), liveCycle); liveCycle.destructor = dataSetWriterTypeDestructor; UA_Server_setNodeTypeLifecycle(server, UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETWRITERDATATYPE), liveCycle); liveCycle.destructor = publishedDataItemsTypeDestructor; UA_Server_setNodeTypeLifecycle(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHEDDATAITEMSTYPE), liveCycle); liveCycle.destructor = dataSetReaderTypeDestructor; UA_Server_setNodeTypeLifecycle(server, UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETREADERDATATYPE), liveCycle); return retVal; } #endif /* UA_ENABLE_PUBSUB_INFORMATIONMODEL */ /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_services_view.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2019 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2014-2017 (c) Florian Palm * Copyright 2015-2016 (c) Sten Grüner * Copyright 2015 (c) LEvertz * Copyright 2015 (c) Chris Iatrou * Copyright 2015 (c) Ecosmos * Copyright 2015-2016 (c) Oleksiy Vasylyev * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2016 (c) Lorenz Haas * Copyright 2017 (c) pschoppe * Copyright 2017 (c) Julian Grothoff * Copyright 2017 (c) Henrik Norrman */ /********************/ /* Browse Recursive */ /********************/ /* A RefTree holds a single array for both the NodeIds encountered during * recursive browsing and the entries for a tree-structure to check for * duplicates. Once the (recursive) browse has finished, the tree-structure part * can be simply cut away. A single realloc operation (with some pointer * repairing) can be used to increase the capacity of the RefTree. * * If an ExpandedNodeId is encountered, it has to be processed right away. * Remote ExpandedNodeId are not put into the tree, since it is not possible to * recurse into them anyway. * * The layout of the results array is as follows: * * | Targets [ExpandedNodeId] | Tree [RefEntry] | */ #define UA_BROWSE_INITIAL_SIZE 16 typedef struct RefEntry { ZIP_ENTRY(RefEntry) zipfields; const UA_ExpandedNodeId *target; UA_UInt32 targetHash; /* Hash of the target nodeid */ } RefEntry; static enum ZIP_CMP cmpTarget(const void *a, const void *b) { const RefEntry *aa = (const RefEntry*)a; const RefEntry *bb = (const RefEntry*)b; if(aa->targetHash < bb->targetHash) return ZIP_CMP_LESS; if(aa->targetHash > bb->targetHash) return ZIP_CMP_MORE; return (enum ZIP_CMP)UA_ExpandedNodeId_order(aa->target, bb->target); } ZIP_HEAD(RefHead, RefEntry); typedef struct RefHead RefHead; ZIP_PROTTYPE(RefHead, RefEntry, RefEntry) ZIP_IMPL(RefHead, RefEntry, zipfields, RefEntry, zipfields, cmpTarget) typedef struct { UA_ExpandedNodeId *targets; RefHead head; size_t capacity; /* available space */ size_t size; /* used space */ } RefTree; static UA_StatusCode UA_FUNC_ATTR_WARN_UNUSED_RESULT RefTree_init(RefTree *rt) { size_t space = (sizeof(UA_ExpandedNodeId) + sizeof(RefEntry)) * UA_BROWSE_INITIAL_SIZE; rt->targets = (UA_ExpandedNodeId*)UA_malloc(space); if(!rt->targets) return UA_STATUSCODE_BADOUTOFMEMORY; rt->capacity = UA_BROWSE_INITIAL_SIZE; rt->size = 0; ZIP_INIT(&rt->head); return UA_STATUSCODE_GOOD; } static void RefTree_clear(RefTree *rt) { for(size_t i = 0; i < rt->size; i++) UA_ExpandedNodeId_deleteMembers(&rt->targets[i]); UA_free(rt->targets); } /* Double the capacity of the reftree */ static UA_StatusCode UA_FUNC_ATTR_WARN_UNUSED_RESULT RefTree_double(RefTree *rt) { size_t capacity = rt->capacity * 2; UA_assert(capacity > 0); size_t space = (sizeof(UA_ExpandedNodeId) + sizeof(RefEntry)) * capacity; UA_ExpandedNodeId *newTargets = (UA_ExpandedNodeId*)UA_realloc(rt->targets, space); if(!newTargets) return UA_STATUSCODE_BADOUTOFMEMORY; /* Repair the pointers for the realloced array+tree */ uintptr_t arraydiff = (uintptr_t)newTargets - (uintptr_t)rt->targets; RefEntry *reArray = (RefEntry*) ((uintptr_t)newTargets + (capacity * sizeof(UA_ExpandedNodeId))); uintptr_t entrydiff = (uintptr_t)reArray - ((uintptr_t)rt->targets + (rt->capacity * sizeof(UA_ExpandedNodeId))); RefEntry *oldReArray = (RefEntry*) ((uintptr_t)newTargets + (rt->capacity * sizeof(UA_ExpandedNodeId))); memmove(reArray, oldReArray, rt->size * sizeof(RefEntry)); for(size_t i = 0; i < rt->size; i++) { if(reArray[i].zipfields.zip_left) *(uintptr_t*)&reArray[i].zipfields.zip_left += entrydiff; if(reArray[i].zipfields.zip_right) *(uintptr_t*)&reArray[i].zipfields.zip_right += entrydiff; *(uintptr_t*)&reArray[i].target += arraydiff; } rt->head.zip_root = (RefEntry*)((uintptr_t)rt->head.zip_root + entrydiff); rt->capacity = capacity; rt->targets = newTargets; return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_FUNC_ATTR_WARN_UNUSED_RESULT RefTree_add(RefTree *rt, const UA_ExpandedNodeId *target) { UA_StatusCode s = UA_STATUSCODE_GOOD; if(rt->capacity <= rt->size) { s = RefTree_double(rt); if(s != UA_STATUSCODE_GOOD) return s; } s = UA_ExpandedNodeId_copy(target, &rt->targets[rt->size]); if(s != UA_STATUSCODE_GOOD) return s; RefEntry *re = (RefEntry*)((uintptr_t)rt->targets + (sizeof(UA_ExpandedNodeId) * rt->capacity) + (sizeof(RefEntry) * rt->size)); re->target = &rt->targets[rt->size]; re->targetHash = UA_ExpandedNodeId_hash(target); ZIP_INSERT(RefHead, &rt->head, re, ZIP_FFS32(UA_UInt32_random())); rt->size++; return UA_STATUSCODE_GOOD; } static UA_Boolean relevantReference(const UA_NodeId *refType, size_t relevantRefsSize, const UA_NodeId *relevantRefs) { if(!relevantRefs) return true; for(size_t i = 0; i < relevantRefsSize; i++) { if(UA_NodeId_equal(refType, &relevantRefs[i])) return true; } return false; } static UA_StatusCode addRelevantReferences(UA_Server *server, RefTree *rt, const UA_NodeId *nodeId, size_t refTypesSize, const UA_NodeId *refTypes, UA_BrowseDirection browseDirection) { const UA_Node *node = UA_Nodestore_getNode(server->nsCtx, nodeId); if(!node) return UA_STATUSCODE_BADNODEIDUNKNOWN; UA_StatusCode retval = UA_STATUSCODE_GOOD; for(size_t i = 0; i < node->referencesSize; i++) { UA_NodeReferenceKind *rk = &node->references[i]; /* Reference in the right direction? */ if(rk->isInverse && browseDirection == UA_BROWSEDIRECTION_FORWARD) continue; if(!rk->isInverse && browseDirection == UA_BROWSEDIRECTION_INVERSE) continue; /* Is the reference part of the hierarchy of references we look for? */ if(!relevantReference(&rk->referenceTypeId, refTypesSize, refTypes)) continue; for(size_t k = 0; k < rk->refTargetsSize; k++) { retval = RefTree_add(rt, &rk->refTargets[k].target); if(retval != UA_STATUSCODE_GOOD) goto cleanup; } } cleanup: UA_Nodestore_releaseNode(server->nsCtx, node); return retval; } UA_StatusCode browseRecursive(UA_Server *server, size_t startNodesSize, const UA_NodeId *startNodes, size_t refTypesSize, const UA_NodeId *refTypes, UA_BrowseDirection browseDirection, UA_Boolean includeStartNodes, size_t *resultsSize, UA_ExpandedNodeId **results) { RefTree rt; UA_StatusCode retval = RefTree_init(&rt); if(retval != UA_STATUSCODE_GOOD) return retval; /* Add the start nodes? */ UA_ExpandedNodeId en = UA_EXPANDEDNODEID_NULL; for(size_t i = 0; i < startNodesSize && retval == UA_STATUSCODE_GOOD; i++) { if(includeStartNodes) { en.nodeId = startNodes[i]; retval = RefTree_add(&rt, &en); } else { retval = addRelevantReferences(server, &rt, &startNodes[i], refTypesSize, refTypes, browseDirection); } } if(retval != UA_STATUSCODE_GOOD) { RefTree_clear(&rt); return retval; } /* Loop over the targets we have so far. This recurses, as new targets are * added to rt. */ for(size_t i = 0; i < rt.size; i++) { /* Dont recurse into remote nodes */ if(rt.targets[i].serverIndex > 0) continue; if(rt.targets[i].namespaceUri.data != NULL) continue; retval = addRelevantReferences(server, &rt, &rt.targets[i].nodeId, refTypesSize, refTypes, browseDirection); if(retval != UA_STATUSCODE_GOOD) { RefTree_clear(&rt); return retval; } } if(rt.size > 0) { *results = rt.targets; *resultsSize = rt.size; } else { RefTree_clear(&rt); } return UA_STATUSCODE_GOOD; } /* Only if IncludeSubtypes is selected */ UA_StatusCode referenceSubtypes(UA_Server *server, const UA_NodeId *refType, size_t *refTypesSize, UA_NodeId **refTypes) { /* Leave refTypes == NULL */ if(UA_NodeId_isNull(refType)) return UA_STATUSCODE_GOOD; /* Browse recursive for the hierarchy of sub-references */ UA_ExpandedNodeId *rt = NULL; size_t rtSize = 0; UA_NodeId hasSubtype = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE); UA_StatusCode retval = browseRecursive(server, 1, refType, 1, &hasSubtype, UA_BROWSEDIRECTION_FORWARD, true, &rtSize, &rt); if(retval != UA_STATUSCODE_GOOD) return retval; UA_assert(rtSize > 0); /* Allocate space (realloc if non-NULL) */ UA_NodeId *newRt = NULL; if(!*refTypes) { newRt = (UA_NodeId*)UA_malloc(rtSize * UA_TYPES[UA_TYPES_NODEID].memSize); } else { newRt = (UA_NodeId*)UA_realloc(*refTypes, (*refTypesSize + rtSize) * UA_TYPES[UA_TYPES_NODEID].memSize); } if(!newRt) { UA_Array_delete(rt, rtSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); return UA_STATUSCODE_BADOUTOFMEMORY; } *refTypes = newRt; /* Move NodeIds */ for(size_t i = 0; i < rtSize; i++) { (*refTypes)[*refTypesSize + i] = rt[i].nodeId; UA_NodeId_init(&rt[i].nodeId); } *refTypesSize += rtSize; UA_Array_delete(rt, rtSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Server_browseRecursive(UA_Server *server, const UA_BrowseDescription *bd, size_t *resultsSize, UA_ExpandedNodeId **results) { /* Set the list of relevant reference types */ UA_NodeId *refTypes = NULL; size_t refTypesSize = 0; UA_StatusCode retval = UA_STATUSCODE_GOOD; if(!UA_NodeId_isNull(&bd->referenceTypeId)) { if(!bd->includeSubtypes) { refTypes = (UA_NodeId*)(uintptr_t)&bd->referenceTypeId; refTypesSize = 1; } else { retval = referenceSubtypes(server, &bd->referenceTypeId, &refTypesSize, &refTypes); if(retval != UA_STATUSCODE_GOOD) return retval; } } /* Browse */ retval = browseRecursive(server, 1, &bd->nodeId, refTypesSize, refTypes, bd->browseDirection, false, resultsSize, results); /* Clean up */ if(refTypes && bd->includeSubtypes) UA_Array_delete(refTypes, refTypesSize, &UA_TYPES[UA_TYPES_NODEID]); return retval; } /**********/ /* Browse */ /**********/ typedef struct { size_t size; size_t capacity; UA_ReferenceDescription *descr; } RefResult; static UA_StatusCode UA_FUNC_ATTR_WARN_UNUSED_RESULT RefResult_init(RefResult *rr) { memset(rr, 0, sizeof(RefResult)); rr->descr = (UA_ReferenceDescription*) UA_Array_new(UA_BROWSE_INITIAL_SIZE, &UA_TYPES[UA_TYPES_REFERENCEDESCRIPTION]); if(!rr->descr) return UA_STATUSCODE_BADOUTOFMEMORY; rr->capacity = UA_BROWSE_INITIAL_SIZE; rr->size = 0; return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_FUNC_ATTR_WARN_UNUSED_RESULT RefResult_double(RefResult *rr) { size_t newSize = rr->capacity * 2; UA_ReferenceDescription *rd = (UA_ReferenceDescription*) UA_realloc(rr->descr, newSize * sizeof(UA_ReferenceDescription)); if(!rd) return UA_STATUSCODE_BADOUTOFMEMORY; memset(&rd[rr->size], 0, sizeof(UA_ReferenceDescription) * (newSize - rr->size)); rr->descr = rd; rr->capacity = newSize; return UA_STATUSCODE_GOOD; } static void RefResult_clear(RefResult *rr) { UA_assert(rr->descr != NULL); for(size_t i = 0; i < rr->size; i++) UA_ReferenceDescription_clear(&rr->descr[i]); UA_free(rr->descr); } struct ContinuationPoint { ContinuationPoint *next; UA_ByteString identifier; UA_BrowseDescription browseDescription; UA_UInt32 maxReferences; size_t relevantReferencesSize; UA_NodeId *relevantReferences; /* The last point in the node references? */ size_t referenceKindIndex; size_t targetIndex; }; ContinuationPoint * ContinuationPoint_clear(ContinuationPoint *cp) { UA_ByteString_deleteMembers(&cp->identifier); UA_BrowseDescription_deleteMembers(&cp->browseDescription); UA_Array_delete(cp->relevantReferences, cp->relevantReferencesSize, &UA_TYPES[UA_TYPES_NODEID]); return cp->next; } /* Target node on top of the stack */ static UA_StatusCode UA_FUNC_ATTR_WARN_UNUSED_RESULT addReferenceDescription(UA_Server *server, RefResult *rr, const UA_NodeReferenceKind *ref, UA_UInt32 mask, const UA_ExpandedNodeId *nodeId, const UA_Node *curr) { /* Ensure capacity is left */ UA_StatusCode retval = UA_STATUSCODE_GOOD; if(rr->size >= rr->capacity) { retval = RefResult_double(rr); if(retval != UA_STATUSCODE_GOOD) return retval; } UA_ReferenceDescription *descr = &rr->descr[rr->size]; /* Fields without access to the actual node */ retval = UA_ExpandedNodeId_copy(nodeId, &descr->nodeId); if(mask & UA_BROWSERESULTMASK_REFERENCETYPEID) retval |= UA_NodeId_copy(&ref->referenceTypeId, &descr->referenceTypeId); if(mask & UA_BROWSERESULTMASK_ISFORWARD) descr->isForward = !ref->isInverse; /* Remote references (ExpandedNodeId) are not further looked up here */ if(!curr) { UA_ReferenceDescription_deleteMembers(descr); return retval; } /* Fields that require the actual node */ if(mask & UA_BROWSERESULTMASK_NODECLASS) retval |= UA_NodeClass_copy(&curr->nodeClass, &descr->nodeClass); if(mask & UA_BROWSERESULTMASK_BROWSENAME) retval |= UA_QualifiedName_copy(&curr->browseName, &descr->browseName); if(mask & UA_BROWSERESULTMASK_DISPLAYNAME) retval |= UA_LocalizedText_copy(&curr->displayName, &descr->displayName); if(mask & UA_BROWSERESULTMASK_TYPEDEFINITION) { if(curr->nodeClass == UA_NODECLASS_OBJECT || curr->nodeClass == UA_NODECLASS_VARIABLE) { const UA_Node *type = getNodeType(server, curr); if(type) { retval |= UA_NodeId_copy(&type->nodeId, &descr->typeDefinition.nodeId); UA_Nodestore_releaseNode(server->nsCtx, type); } } } if(retval == UA_STATUSCODE_GOOD) rr->size++; /* Increase the counter */ else UA_ReferenceDescription_deleteMembers(descr); return retval; } static UA_Boolean matchClassMask(const UA_Node *node, UA_UInt32 nodeClassMask) { if(nodeClassMask != UA_NODECLASS_UNSPECIFIED && (node->nodeClass & nodeClassMask) == 0) return false; return true; } /* Returns whether the node / continuationpoint is done */ static UA_StatusCode browseReferences(UA_Server *server, const UA_Node *node, ContinuationPoint *cp, RefResult *rr, UA_Boolean *done) { UA_assert(cp != NULL); const UA_BrowseDescription *bd= &cp->browseDescription; size_t referenceKindIndex = cp->referenceKindIndex; size_t targetIndex = cp->targetIndex; /* Loop over the node's references */ const UA_Node *target = NULL; UA_StatusCode retval = UA_STATUSCODE_GOOD; for(; referenceKindIndex < node->referencesSize; ++referenceKindIndex) { UA_NodeReferenceKind *rk = &node->references[referenceKindIndex]; /* Reference in the right direction? */ if(rk->isInverse && bd->browseDirection == UA_BROWSEDIRECTION_FORWARD) continue; if(!rk->isInverse && bd->browseDirection == UA_BROWSEDIRECTION_INVERSE) continue; /* Is the reference part of the hierarchy of references we look for? */ if(!relevantReference(&rk->referenceTypeId, cp->relevantReferencesSize, cp->relevantReferences)) continue; /* Loop over the targets */ for(; targetIndex < rk->refTargetsSize; ++targetIndex) { target = NULL; /* Get the node if it is not a remote reference */ if(rk->refTargets[targetIndex].target.serverIndex == 0 && rk->refTargets[targetIndex].target.namespaceUri.data == NULL) { target = UA_Nodestore_getNode(server->nsCtx, &rk->refTargets[targetIndex].target.nodeId); /* Test if the node class matches */ if(target && !matchClassMask(target, bd->nodeClassMask)) { if(target) UA_Nodestore_releaseNode(server->nsCtx, target); continue; } } /* A match! Did we reach maxrefs? */ if(rr->size >= cp->maxReferences) { cp->referenceKindIndex = referenceKindIndex; cp->targetIndex = targetIndex; if(target) UA_Nodestore_releaseNode(server->nsCtx, target); return UA_STATUSCODE_GOOD; } /* Copy the node description. Target is on top of the stack */ retval = addReferenceDescription(server, rr, rk, bd->resultMask, &rk->refTargets[targetIndex].target, target); UA_Nodestore_releaseNode(server->nsCtx, target); if(retval != UA_STATUSCODE_GOOD) return retval; } targetIndex = 0; /* Start at index 0 for the next reference kind */ } /* The node is done */ *done = true; return UA_STATUSCODE_GOOD; } /* Results for a single browsedescription. This is the inner loop for both * Browse and BrowseNext. The ContinuationPoint contains all the data used. * Including the BrowseDescription. Returns whether there are remaining * references. */ static UA_Boolean browseWithContinuation(UA_Server *server, UA_Session *session, ContinuationPoint *cp, UA_BrowseResult *result) { const UA_BrowseDescription *descr = &cp->browseDescription; /* Is the browsedirection valid? */ if(descr->browseDirection != UA_BROWSEDIRECTION_BOTH && descr->browseDirection != UA_BROWSEDIRECTION_FORWARD && descr->browseDirection != UA_BROWSEDIRECTION_INVERSE) { result->statusCode = UA_STATUSCODE_BADBROWSEDIRECTIONINVALID; return true; } /* Is the reference type valid? */ if(!UA_NodeId_isNull(&descr->referenceTypeId)) { const UA_Node *reftype = UA_Nodestore_getNode(server->nsCtx, &descr->referenceTypeId); if(!reftype) { result->statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID; return true; } UA_Boolean isRef = (reftype->nodeClass == UA_NODECLASS_REFERENCETYPE); UA_Nodestore_releaseNode(server->nsCtx, reftype); if(!isRef) { result->statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID; return true; } } const UA_Node *node = UA_Nodestore_getNode(server->nsCtx, &descr->nodeId); if(!node) { result->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN; return true; } RefResult rr; result->statusCode = RefResult_init(&rr); if(result->statusCode != UA_STATUSCODE_GOOD) { UA_Nodestore_releaseNode(server->nsCtx, node); return true; } /* Browse the references */ UA_Boolean done = false; result->statusCode = browseReferences(server, node, cp, &rr, &done); UA_Nodestore_releaseNode(server->nsCtx, node); if(result->statusCode != UA_STATUSCODE_GOOD) { RefResult_clear(&rr); return true; } /* Move results */ if(rr.size > 0) { result->references = rr.descr; result->referencesSize = rr.size; } else { /* No relevant references, return array of length zero */ RefResult_clear(&rr); result->references = (UA_ReferenceDescription*)UA_EMPTY_ARRAY_SENTINEL; } return done; } /* Start to browse with no previous cp */ void Operation_Browse(UA_Server *server, UA_Session *session, const UA_UInt32 *maxrefs, const UA_BrowseDescription *descr, UA_BrowseResult *result) { /* Stack-allocate a temporary cp */ UA_STACKARRAY(ContinuationPoint, cp, 1); memset(cp, 0, sizeof(ContinuationPoint)); cp->maxReferences = *maxrefs; cp->browseDescription = *descr; /* Shallow copy. Deep-copy later if we persist the cp. */ /* How many references can we return at most? */ if(cp->maxReferences == 0) { if(server->config.maxReferencesPerNode != 0) { cp->maxReferences = server->config.maxReferencesPerNode; } else { cp->maxReferences = UA_INT32_MAX; } } else { if(server->config.maxReferencesPerNode != 0 && cp->maxReferences > server->config.maxReferencesPerNode) { cp->maxReferences= server->config.maxReferencesPerNode; } } /* Get the list of relevant reference types */ if(!UA_NodeId_isNull(&descr->referenceTypeId)) { if(!descr->includeSubtypes) { cp->relevantReferences = (UA_NodeId*)(uintptr_t)&descr->referenceTypeId; cp->relevantReferencesSize = 1; } else { result->statusCode = referenceSubtypes(server, &descr->referenceTypeId, &cp->relevantReferencesSize, &cp->relevantReferences); if(result->statusCode != UA_STATUSCODE_GOOD) return; } } UA_Boolean done = browseWithContinuation(server, session, cp, result); /* Exit early if done or an error occurred */ if(done || result->statusCode != UA_STATUSCODE_GOOD) { if(descr->includeSubtypes) UA_Array_delete(cp->relevantReferences, cp->relevantReferencesSize, &UA_TYPES[UA_TYPES_NODEID]); return; } /* Persist the new continuation point */ ContinuationPoint *cp2 = NULL; UA_Guid *ident = NULL; UA_StatusCode retval = UA_STATUSCODE_GOOD; /* Enough space for the continuation point? */ if(session->availableContinuationPoints <= 0) { retval = UA_STATUSCODE_BADNOCONTINUATIONPOINTS; goto cleanup; } /* Allocate and fill the data structure */ cp2 = (ContinuationPoint*)UA_malloc(sizeof(ContinuationPoint)); if(!cp2) { retval = UA_STATUSCODE_BADOUTOFMEMORY; goto cleanup; } memset(cp2, 0, sizeof(ContinuationPoint)); cp2->referenceKindIndex = cp->referenceKindIndex; cp2->targetIndex = cp->targetIndex; cp2->maxReferences = cp->maxReferences; if(descr->includeSubtypes) { cp2->relevantReferences = cp->relevantReferences; cp2->relevantReferencesSize = cp->relevantReferencesSize; } else { retval = UA_Array_copy(cp->relevantReferences, cp->relevantReferencesSize, (void**)&cp2->relevantReferences, &UA_TYPES[UA_TYPES_NODEID]); if(retval != UA_STATUSCODE_GOOD) goto cleanup; cp2->relevantReferencesSize = cp->relevantReferencesSize; } /* Copy the description */ retval = UA_BrowseDescription_copy(descr, &cp2->browseDescription); if(retval != UA_STATUSCODE_GOOD) goto cleanup; /* Create a random bytestring via a Guid */ ident = UA_Guid_new(); if(!ident) { retval = UA_STATUSCODE_BADOUTOFMEMORY; goto cleanup; } *ident = UA_Guid_random(); cp2->identifier.data = (UA_Byte*)ident; cp2->identifier.length = sizeof(UA_Guid); /* Return the cp identifier */ retval = UA_ByteString_copy(&cp2->identifier, &result->continuationPoint); if(retval != UA_STATUSCODE_GOOD) goto cleanup; /* Attach the cp to the session */ cp2->next = session->continuationPoints; session->continuationPoints = cp2; --session->availableContinuationPoints; return; cleanup: if(cp2) { ContinuationPoint_clear(cp2); UA_free(cp2); } UA_BrowseResult_deleteMembers(result); result->statusCode = retval; } void Service_Browse(UA_Server *server, UA_Session *session, const UA_BrowseRequest *request, UA_BrowseResponse *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing BrowseRequest"); /* Test the number of operations in the request */ if(server->config.maxNodesPerBrowse != 0 && request->nodesToBrowseSize > server->config.maxNodesPerBrowse) { response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS; return; } /* No views supported at the moment */ if(!UA_NodeId_isNull(&request->view.viewId)) { response->responseHeader.serviceResult = UA_STATUSCODE_BADVIEWIDUNKNOWN; return; } response->responseHeader.serviceResult = UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_Browse, &request->requestedMaxReferencesPerNode, &request->nodesToBrowseSize, &UA_TYPES[UA_TYPES_BROWSEDESCRIPTION], &response->resultsSize, &UA_TYPES[UA_TYPES_BROWSERESULT]); } UA_BrowseResult UA_Server_browse(UA_Server *server, UA_UInt32 maxReferences, const UA_BrowseDescription *bd) { UA_BrowseResult result; UA_BrowseResult_init(&result); Operation_Browse(server, &server->adminSession, &maxReferences, bd, &result); return result; } static void Operation_BrowseNext(UA_Server *server, UA_Session *session, const UA_Boolean *releaseContinuationPoints, const UA_ByteString *continuationPoint, UA_BrowseResult *result) { /* Find the continuation point */ ContinuationPoint **prev = &session->continuationPoints, *cp; while((cp = *prev)) { if(UA_ByteString_equal(&cp->identifier, continuationPoint)) break; prev = &cp->next; } if(!cp) { result->statusCode = UA_STATUSCODE_BADCONTINUATIONPOINTINVALID; return; } /* Remove the cp */ if(*releaseContinuationPoints) { *prev = ContinuationPoint_clear(cp); UA_free(cp); ++session->availableContinuationPoints; return; } /* Continue browsing */ UA_Boolean done = browseWithContinuation(server, session, cp, result); if(done) { /* Remove the cp if there are no references left */ *prev = ContinuationPoint_clear(cp); UA_free(cp); ++session->availableContinuationPoints; } else { /* Return the cp identifier */ UA_StatusCode retval = UA_ByteString_copy(&cp->identifier, &result->continuationPoint); if(retval != UA_STATUSCODE_GOOD) { UA_BrowseResult_deleteMembers(result); result->statusCode = retval; } } } void Service_BrowseNext(UA_Server *server, UA_Session *session, const UA_BrowseNextRequest *request, UA_BrowseNextResponse *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing BrowseNextRequest"); UA_Boolean releaseContinuationPoints = request->releaseContinuationPoints; /* request is const */ response->responseHeader.serviceResult = UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_BrowseNext, &releaseContinuationPoints, &request->continuationPointsSize, &UA_TYPES[UA_TYPES_BYTESTRING], &response->resultsSize, &UA_TYPES[UA_TYPES_BROWSERESULT]); } UA_BrowseResult UA_Server_browseNext(UA_Server *server, UA_Boolean releaseContinuationPoint, const UA_ByteString *continuationPoint) { UA_BrowseResult result; UA_BrowseResult_init(&result); Operation_BrowseNext(server, &server->adminSession, &releaseContinuationPoint, continuationPoint, &result); return result; } /***********************/ /* TranslateBrowsePath */ /***********************/ static void walkBrowsePathElementReferenceTargets(UA_BrowsePathResult *result, size_t *targetsSize, UA_NodeId **next, size_t *nextSize, size_t *nextCount, UA_UInt32 elemDepth, const UA_NodeReferenceKind *rk) { /* Loop over the targets */ for(size_t i = 0; i < rk->refTargetsSize; i++) { UA_ExpandedNodeId *targetId = &rk->refTargets[i].target; /* Does the reference point to an external server? Then add to the * targets with the right path depth. */ if(targetId->serverIndex != 0) { UA_BrowsePathTarget *tempTargets = (UA_BrowsePathTarget*)UA_realloc(result->targets, sizeof(UA_BrowsePathTarget) * (*targetsSize) * 2); if(!tempTargets) { result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY; return; } result->targets = tempTargets; (*targetsSize) *= 2; result->statusCode = UA_ExpandedNodeId_copy(targetId, &result->targets[result->targetsSize].targetId); result->targets[result->targetsSize].remainingPathIndex = elemDepth; continue; } /* Can we store the node in the array of candidates for deep-search? */ if(*nextSize <= *nextCount) { UA_NodeId *tempNext = (UA_NodeId*)UA_realloc(*next, sizeof(UA_NodeId) * (*nextSize) * 2); if(!tempNext) { result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY; return; } *next = tempNext; (*nextSize) *= 2; } /* Add the node to the next array for the following path element */ result->statusCode = UA_NodeId_copy(&targetId->nodeId, &(*next)[*nextCount]); if(result->statusCode != UA_STATUSCODE_GOOD) return; ++(*nextCount); } } static void walkBrowsePathElement(UA_Server *server, UA_Session *session, UA_UInt32 nodeClassMask, UA_BrowsePathResult *result, size_t *targetsSize, const UA_RelativePathElement *elem, UA_UInt32 elemDepth, const UA_QualifiedName *targetName, const UA_NodeId *current, const size_t currentCount, UA_NodeId **next, size_t *nextSize, size_t *nextCount) { /* Return all references? */ UA_Boolean all_refs = UA_NodeId_isNull(&elem->referenceTypeId); if(!all_refs) { const UA_Node *rootRef = UA_Nodestore_getNode(server->nsCtx, &elem->referenceTypeId); if(!rootRef) return; UA_Boolean match = (rootRef->nodeClass == UA_NODECLASS_REFERENCETYPE); UA_Nodestore_releaseNode(server->nsCtx, rootRef); if(!match) return; } /* Iterate over all nodes at the current depth-level */ for(size_t i = 0; i < currentCount; ++i) { /* Get the node */ const UA_Node *node = UA_Nodestore_getNode(server->nsCtx, ¤t[i]); if(!node) { /* If we cannot find the node at depth 0, the starting node does not exist */ if(elemDepth == 0) result->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN; continue; } /* Test whether the node fits the class mask */ if(!matchClassMask(node, nodeClassMask)) { UA_Nodestore_releaseNode(server->nsCtx, node); continue; } /* Test whether the node has the target name required in the previous * path element */ if(targetName && (targetName->namespaceIndex != node->browseName.namespaceIndex || !UA_String_equal(&targetName->name, &node->browseName.name))) { UA_Nodestore_releaseNode(server->nsCtx, node); continue; } /* Loop over the nodes references */ for(size_t r = 0; r < node->referencesSize && result->statusCode == UA_STATUSCODE_GOOD; ++r) { UA_NodeReferenceKind *rk = &node->references[r]; /* Does the direction of the reference match? */ if(rk->isInverse != elem->isInverse) continue; /* Is the node relevant? */ if(!all_refs) { if(!elem->includeSubtypes && !UA_NodeId_equal(&rk->referenceTypeId, &elem->referenceTypeId)) continue; if(!isNodeInTree(server->nsCtx, &rk->referenceTypeId, &elem->referenceTypeId, &subtypeId, 1)) continue; } /* Walk over the reference targets */ walkBrowsePathElementReferenceTargets(result, targetsSize, next, nextSize, nextCount, elemDepth, rk); } UA_Nodestore_releaseNode(server->nsCtx, node); } } /* This assumes that result->targets has enough room for all currentCount elements */ static void addBrowsePathTargets(UA_Server *server, UA_Session *session, UA_UInt32 nodeClassMask, UA_BrowsePathResult *result, const UA_QualifiedName *targetName, UA_NodeId *current, size_t currentCount) { for(size_t i = 0; i < currentCount; i++) { const UA_Node *node = UA_Nodestore_getNode(server->nsCtx, ¤t[i]); if(!node) { UA_NodeId_deleteMembers(¤t[i]); continue; } /* Test whether the node fits the class mask */ UA_Boolean skip = !matchClassMask(node, nodeClassMask); /* Test whether the node has the target name required in the * previous path element */ if(targetName->namespaceIndex != node->browseName.namespaceIndex || !UA_String_equal(&targetName->name, &node->browseName.name)) skip = true; UA_Nodestore_releaseNode(server->nsCtx, node); if(skip) { UA_NodeId_deleteMembers(¤t[i]); continue; } /* Move the nodeid to the target array */ UA_BrowsePathTarget_init(&result->targets[result->targetsSize]); result->targets[result->targetsSize].targetId.nodeId = current[i]; result->targets[result->targetsSize].remainingPathIndex = UA_UINT32_MAX; ++result->targetsSize; } } static void walkBrowsePath(UA_Server *server, UA_Session *session, const UA_BrowsePath *path, UA_UInt32 nodeClassMask, UA_BrowsePathResult *result, size_t targetsSize, UA_NodeId **current, size_t *currentSize, size_t *currentCount, UA_NodeId **next, size_t *nextSize, size_t *nextCount) { UA_assert(*currentCount == 1); UA_assert(*nextCount == 0); /* Points to the targetName of the _previous_ path element */ const UA_QualifiedName *targetName = NULL; /* Iterate over path elements */ UA_assert(path->relativePath.elementsSize > 0); for(UA_UInt32 i = 0; i < path->relativePath.elementsSize; ++i) { walkBrowsePathElement(server, session, nodeClassMask, result, &targetsSize, &path->relativePath.elements[i], i, targetName, *current, *currentCount, next, nextSize, nextCount); /* Clean members of current */ for(size_t j = 0; j < *currentCount; j++) UA_NodeId_deleteMembers(&(*current)[j]); *currentCount = 0; /* When no targets are left or an error occurred. None of next's * elements will be copied to result->targets */ if(*nextCount == 0 || result->statusCode != UA_STATUSCODE_GOOD) { UA_assert(*currentCount == 0); UA_assert(*nextCount == 0); return; } /* Exchange current and next for the next depth */ size_t tSize = *currentSize; size_t tCount = *currentCount; UA_NodeId *tT = *current; *currentSize = *nextSize; *currentCount = *nextCount; *current = *next; *nextSize = tSize; *nextCount = tCount; *next = tT; /* Store the target name of the previous path element */ targetName = &path->relativePath.elements[i].targetName; } UA_assert(targetName != NULL); UA_assert(*nextCount == 0); /* After the last BrowsePathElement, move members from current to the * result targets */ /* Realloc if more space is needed */ if(targetsSize < result->targetsSize + (*currentCount)) { UA_BrowsePathTarget *newTargets = (UA_BrowsePathTarget*)UA_realloc(result->targets, sizeof(UA_BrowsePathTarget) * (result->targetsSize + (*currentCount))); if(!newTargets) { result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY; for(size_t i = 0; i < *currentCount; ++i) UA_NodeId_deleteMembers(&(*current)[i]); *currentCount = 0; return; } result->targets = newTargets; } /* Move the elements of current to the targets */ addBrowsePathTargets(server, session, nodeClassMask, result, targetName, *current, *currentCount); *currentCount = 0; } static void Operation_TranslateBrowsePathToNodeIds(UA_Server *server, UA_Session *session, const UA_UInt32 *nodeClassMask, const UA_BrowsePath *path, UA_BrowsePathResult *result) { if(path->relativePath.elementsSize <= 0) { result->statusCode = UA_STATUSCODE_BADNOTHINGTODO; return; } /* RelativePath elements must not have an empty targetName */ for(size_t i = 0; i < path->relativePath.elementsSize; ++i) { if(UA_QualifiedName_isNull(&path->relativePath.elements[i].targetName)) { result->statusCode = UA_STATUSCODE_BADBROWSENAMEINVALID; return; } } /* Allocate memory for the targets */ size_t targetsSize = 10; /* When to realloc; the member count is stored in * result->targetsSize */ result->targets = (UA_BrowsePathTarget*)UA_malloc(sizeof(UA_BrowsePathTarget) * targetsSize); if(!result->targets) { result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY; return; } /* Allocate memory for two temporary arrays. One with the results for the * previous depth of the path. The other for the new results at the current * depth. The two arrays alternate as we descend down the tree. */ size_t currentSize = 10; /* When to realloc */ size_t currentCount = 0; /* Current elements */ UA_NodeId *current = (UA_NodeId*)UA_malloc(sizeof(UA_NodeId) * currentSize); if(!current) { result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY; UA_free(result->targets); return; } size_t nextSize = 10; /* When to realloc */ size_t nextCount = 0; /* Current elements */ UA_NodeId *next = (UA_NodeId*)UA_malloc(sizeof(UA_NodeId) * nextSize); if(!next) { result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY; UA_free(result->targets); UA_free(current); return; } /* Copy the starting node into current */ result->statusCode = UA_NodeId_copy(&path->startingNode, ¤t[0]); if(result->statusCode != UA_STATUSCODE_GOOD) { UA_free(result->targets); UA_free(current); UA_free(next); return; } currentCount = 1; /* Walk the path elements */ walkBrowsePath(server, session, path, *nodeClassMask, result, targetsSize, ¤t, ¤tSize, ¤tCount, &next, &nextSize, &nextCount); UA_assert(currentCount == 0); UA_assert(nextCount == 0); /* No results => BadNoMatch status code */ if(result->targetsSize == 0 && result->statusCode == UA_STATUSCODE_GOOD) result->statusCode = UA_STATUSCODE_BADNOMATCH; /* Clean up the temporary arrays and the targets */ UA_free(current); UA_free(next); if(result->statusCode != UA_STATUSCODE_GOOD) { for(size_t i = 0; i < result->targetsSize; ++i) UA_BrowsePathTarget_deleteMembers(&result->targets[i]); UA_free(result->targets); result->targets = NULL; result->targetsSize = 0; } } UA_BrowsePathResult UA_Server_translateBrowsePathToNodeIds(UA_Server *server, const UA_BrowsePath *browsePath) { UA_BrowsePathResult result; UA_BrowsePathResult_init(&result); UA_UInt32 nodeClassMask = 0; /* All node classes */ Operation_TranslateBrowsePathToNodeIds(server, &server->adminSession, &nodeClassMask, browsePath, &result); return result; } void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session, const UA_TranslateBrowsePathsToNodeIdsRequest *request, UA_TranslateBrowsePathsToNodeIdsResponse *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing TranslateBrowsePathsToNodeIdsRequest"); /* Test the number of operations in the request */ if(server->config.maxNodesPerTranslateBrowsePathsToNodeIds != 0 && request->browsePathsSize > server->config.maxNodesPerTranslateBrowsePathsToNodeIds) { response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS; return; } UA_UInt32 nodeClassMask = 0; /* All node classes */ response->responseHeader.serviceResult = UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_TranslateBrowsePathToNodeIds, &nodeClassMask, &request->browsePathsSize, &UA_TYPES[UA_TYPES_BROWSEPATH], &response->resultsSize, &UA_TYPES[UA_TYPES_BROWSEPATHRESULT]); } UA_BrowsePathResult UA_Server_browseSimplifiedBrowsePath(UA_Server *server, const UA_NodeId origin, size_t browsePathSize, const UA_QualifiedName *browsePath) { /* Construct the BrowsePath */ UA_BrowsePath bp; UA_BrowsePath_init(&bp); bp.startingNode = origin; UA_STACKARRAY(UA_RelativePathElement, rpe, browsePathSize); memset(rpe, 0, sizeof(UA_RelativePathElement) * browsePathSize); for(size_t j = 0; j < browsePathSize; j++) { rpe[j].referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES); rpe[j].includeSubtypes = true; rpe[j].targetName = browsePath[j]; } bp.relativePath.elements = rpe; bp.relativePath.elementsSize = browsePathSize; /* Browse */ UA_BrowsePathResult bpr; UA_BrowsePathResult_init(&bpr); UA_UInt32 nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE; Operation_TranslateBrowsePathToNodeIds(server, &server->adminSession, &nodeClassMask, &bp, &bpr); return bpr; } /************/ /* Register */ /************/ void Service_RegisterNodes(UA_Server *server, UA_Session *session, const UA_RegisterNodesRequest *request, UA_RegisterNodesResponse *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing RegisterNodesRequest"); //TODO: hang the nodeids to the session if really needed if(request->nodesToRegisterSize == 0) { response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO; return; } /* Test the number of operations in the request */ if(server->config.maxNodesPerRegisterNodes != 0 && request->nodesToRegisterSize > server->config.maxNodesPerRegisterNodes) { response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS; return; } response->responseHeader.serviceResult = UA_Array_copy(request->nodesToRegister, request->nodesToRegisterSize, (void**)&response->registeredNodeIds, &UA_TYPES[UA_TYPES_NODEID]); if(response->responseHeader.serviceResult == UA_STATUSCODE_GOOD) response->registeredNodeIdsSize = request->nodesToRegisterSize; } void Service_UnregisterNodes(UA_Server *server, UA_Session *session, const UA_UnregisterNodesRequest *request, UA_UnregisterNodesResponse *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing UnRegisterNodesRequest"); //TODO: remove the nodeids from the session if really needed if(request->nodesToUnregisterSize == 0) response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO; /* Test the number of operations in the request */ if(server->config.maxNodesPerRegisterNodes != 0 && request->nodesToUnregisterSize > server->config.maxNodesPerRegisterNodes) { response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS; return; } } /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_services_method.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2015 (c) Chris Iatrou * Copyright 2015-2017 (c) Florian Palm * Copyright 2015-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2015-2016 (c) Sten Grüner * Copyright 2015 (c) Oleksiy Vasylyev * Copyright 2016 (c) LEvertz * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2017 (c) Julian Grothoff */ #ifdef UA_ENABLE_METHODCALLS /* conditional compilation */ 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) { UA_NodeReferenceKind *rk = &ofMethod->references[i]; if(rk->isInverse != false) continue; if(!UA_NodeId_equal(&hasProperty, &rk->referenceTypeId)) continue; for(size_t j = 0; j < rk->refTargetsSize; ++j) { const UA_Node *refTarget = UA_Nodestore_getNode(server->nsCtx, &rk->refTargets[j].target.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; } UA_Nodestore_releaseNode(server->nsCtx, refTarget); } } return NULL; } /* inputArgumentResults has the length request->inputArgumentsSize */ static UA_StatusCode typeCheckArguments(UA_Server *server, UA_Session *session, const UA_VariableNode *argRequirements, size_t argsSize, UA_Variant *args, UA_StatusCode *inputArgumentResults) { /* Verify that we have a Variant containing UA_Argument (scalar or array) in * the "InputArguments" node */ if(argRequirements->valueSource != UA_VALUESOURCE_DATA) return UA_STATUSCODE_BADINTERNALERROR; if(!argRequirements->value.data.value.hasValue) return UA_STATUSCODE_BADINTERNALERROR; if(argRequirements->value.data.value.value.type != &UA_TYPES[UA_TYPES_ARGUMENT]) return UA_STATUSCODE_BADINTERNALERROR; /* Verify the number of arguments. A scalar argument value is interpreted as * an array of length 1. */ size_t argReqsSize = argRequirements->value.data.value.value.arrayLength; if(UA_Variant_isScalar(&argRequirements->value.data.value.value)) argReqsSize = 1; if(argReqsSize > argsSize) return UA_STATUSCODE_BADARGUMENTSMISSING; if(argReqsSize < argsSize) return UA_STATUSCODE_BADTOOMANYARGUMENTS; /* Type-check every argument against the definition */ UA_StatusCode retval = UA_STATUSCODE_GOOD; UA_Argument *argReqs = (UA_Argument*)argRequirements->value.data.value.value.data; for(size_t i = 0; i < argReqsSize; ++i) { if(!compatibleValue(server, session, &argReqs[i].dataType, argReqs[i].valueRank, argReqs[i].arrayDimensionsSize, argReqs[i].arrayDimensions, &args[i], NULL)) { inputArgumentResults[i] = UA_STATUSCODE_BADTYPEMISMATCH; retval = UA_STATUSCODE_BADINVALIDARGUMENT; } } return retval; } /* inputArgumentResults has the length request->inputArgumentsSize */ static UA_StatusCode validMethodArguments(UA_Server *server, UA_Session *session, const UA_MethodNode *method, const UA_CallMethodRequest *request, UA_StatusCode *inputArgumentResults) { /* Get the input arguments node */ const UA_VariableNode *inputArguments = getArgumentsVariableNode(server, method, UA_STRING("InputArguments")); if(!inputArguments) { if(request->inputArgumentsSize > 0) return UA_STATUSCODE_BADTOOMANYARGUMENTS; return UA_STATUSCODE_GOOD; } /* Verify the request */ UA_StatusCode retval = typeCheckArguments(server, session, inputArguments, request->inputArgumentsSize, request->inputArguments, inputArgumentResults); /* Release the input arguments node */ UA_Nodestore_releaseNode(server->nsCtx, (const UA_Node*)inputArguments); return retval; } static const UA_NodeId hasComponentNodeId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASCOMPONENT}}; static const UA_NodeId hasSubTypeNodeId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASSUBTYPE}}; static void callWithMethodAndObject(UA_Server *server, UA_Session *session, const UA_CallMethodRequest *request, UA_CallMethodResult *result, const UA_MethodNode *method, const UA_ObjectNode *object) { /* Verify the object's NodeClass */ if(object->nodeClass != UA_NODECLASS_OBJECT && object->nodeClass != UA_NODECLASS_OBJECTTYPE) { result->statusCode = UA_STATUSCODE_BADNODECLASSINVALID; return; } /* Verify the method's NodeClass */ if(method->nodeClass != UA_NODECLASS_METHOD) { result->statusCode = UA_STATUSCODE_BADNODECLASSINVALID; return; } /* Is there a method to execute? */ if(!method->method) { result->statusCode = UA_STATUSCODE_BADINTERNALERROR; return; } /* Verify method/object relations. Object must have a hasComponent or a * subtype of hasComponent reference to the method node. Therefore, check * every reference between the parent object and the method node if there is * a hasComponent (or subtype) reference */ UA_Boolean found = false; for(size_t i = 0; i < object->referencesSize && !found; ++i) { UA_NodeReferenceKind *rk = &object->references[i]; if(rk->isInverse) continue; if(!isNodeInTree(server->nsCtx, &rk->referenceTypeId, &hasComponentNodeId, &hasSubTypeNodeId, 1)) continue; for(size_t j = 0; j < rk->refTargetsSize; ++j) { if(UA_NodeId_equal(&rk->refTargets[j].target.nodeId, &request->methodId)) { found = true; break; } } } if(!found) { result->statusCode = UA_STATUSCODE_BADMETHODINVALID; return; } /* Verify access rights */ UA_Boolean executable = method->executable; if(session != &server->adminSession) executable = executable && server->config.accessControl.getUserExecutableOnObject(server, &server->config.accessControl, &session->sessionId, session->sessionHandle, &request->methodId, method->context, &request->objectId, object->context); if(!executable) { result->statusCode = UA_STATUSCODE_BADNOTEXECUTABLE; return; } /* Allocate the inputArgumentResults array */ result->inputArgumentResults = (UA_StatusCode*) UA_Array_new(request->inputArgumentsSize, &UA_TYPES[UA_TYPES_STATUSCODE]); if(!result->inputArgumentResults) { result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY; return; } result->inputArgumentResultsSize = request->inputArgumentsSize; /* Verify Input Arguments */ result->statusCode = validMethodArguments(server, session, method, request, result->inputArgumentResults); /* Return inputArgumentResults only for BADINVALIDARGUMENT */ if(result->statusCode != UA_STATUSCODE_BADINVALIDARGUMENT) { UA_Array_delete(result->inputArgumentResults, result->inputArgumentResultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]); result->inputArgumentResults = NULL; result->inputArgumentResultsSize = 0; } /* Error during type-checking? */ if(result->statusCode != UA_STATUSCODE_GOOD) return; /* Get the output arguments node */ const UA_VariableNode *outputArguments = getArgumentsVariableNode(server, method, UA_STRING("OutputArguments")); /* Allocate the output arguments array */ size_t outputArgsSize = 0; if(outputArguments) outputArgsSize = outputArguments->value.data.value.value.arrayLength; result->outputArguments = (UA_Variant*) UA_Array_new(outputArgsSize, &UA_TYPES[UA_TYPES_VARIANT]); if(!result->outputArguments) { result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY; return; } result->outputArgumentsSize = outputArgsSize; /* Release the output arguments node */ UA_Nodestore_releaseNode(server->nsCtx, (const UA_Node*)outputArguments); /* Call the method */ result->statusCode = method->method(server, &session->sessionId, session->sessionHandle, &method->nodeId, method->context, &object->nodeId, object->context, request->inputArgumentsSize, request->inputArguments, result->outputArgumentsSize, result->outputArguments); /* TODO: Verify Output matches the argument definition */ } static void Operation_CallMethod(UA_Server *server, UA_Session *session, void *context, const UA_CallMethodRequest *request, UA_CallMethodResult *result) { /* Get the method node */ const UA_MethodNode *method = (const UA_MethodNode*) UA_Nodestore_getNode(server->nsCtx, &request->methodId); if(!method) { result->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN; return; } /* Get the object node */ const UA_ObjectNode *object = (const UA_ObjectNode*) UA_Nodestore_getNode(server->nsCtx, &request->objectId); if(!object) { result->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN; UA_Nodestore_releaseNode(server->nsCtx, (const UA_Node*)method); return; } /* Continue with method and object as context */ callWithMethodAndObject(server, session, request, result, method, object); /* Release the method and object node */ UA_Nodestore_releaseNode(server->nsCtx, (const UA_Node*)method); UA_Nodestore_releaseNode(server->nsCtx, (const UA_Node*)object); } void Service_Call(UA_Server *server, UA_Session *session, const UA_CallRequest *request, UA_CallResponse *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing CallRequest"); if(server->config.maxNodesPerMethodCall != 0 && request->methodsToCallSize > server->config.maxNodesPerMethodCall) { response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS; return; } response->responseHeader.serviceResult = UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_CallMethod, NULL, &request->methodsToCallSize, &UA_TYPES[UA_TYPES_CALLMETHODREQUEST], &response->resultsSize, &UA_TYPES[UA_TYPES_CALLMETHODRESULT]); } UA_CallMethodResult UA_EXPORT UA_Server_call(UA_Server *server, const UA_CallMethodRequest *request) { UA_CallMethodResult result; UA_CallMethodResult_init(&result); Operation_CallMethod(server, &server->adminSession, NULL, request, &result); return result; } #endif /* UA_ENABLE_METHODCALLS */ /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_services_session.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2014-2017 (c) Florian Palm * Copyright 2014-2016 (c) Sten Grüner * Copyright 2015 (c) Chris Iatrou * Copyright 2015 (c) Oleksiy Vasylyev * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2017-2018 (c) Mark Giraud, Fraunhofer IOSB * Copyright 2019 (c) Kalycito Infotech Private Limited */ static UA_StatusCode signCreateSessionResponse(UA_Server *server, UA_SecureChannel *channel, const UA_CreateSessionRequest *request, UA_CreateSessionResponse *response) { if(channel->securityMode != UA_MESSAGESECURITYMODE_SIGN && channel->securityMode != UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) return UA_STATUSCODE_GOOD; const UA_SecurityPolicy *const securityPolicy = channel->securityPolicy; UA_SignatureData *signatureData = &response->serverSignature; /* Prepare the signature */ size_t signatureSize = securityPolicy->certificateSigningAlgorithm. getLocalSignatureSize(securityPolicy, channel->channelContext); UA_StatusCode retval = UA_String_copy(&securityPolicy->certificateSigningAlgorithm.uri, &signatureData->algorithm); retval |= UA_ByteString_allocBuffer(&signatureData->signature, signatureSize); if(retval != UA_STATUSCODE_GOOD) return retval; /* Allocate a temp buffer */ size_t dataToSignSize = request->clientCertificate.length + request->clientNonce.length; UA_ByteString dataToSign; retval = UA_ByteString_allocBuffer(&dataToSign, dataToSignSize); if(retval != UA_STATUSCODE_GOOD) return retval; /* signatureData->signature is cleaned up with the response */ /* Sign the signature */ memcpy(dataToSign.data, request->clientCertificate.data, request->clientCertificate.length); memcpy(dataToSign.data + request->clientCertificate.length, request->clientNonce.data, request->clientNonce.length); retval = securityPolicy->certificateSigningAlgorithm. sign(securityPolicy, channel->channelContext, &dataToSign, &signatureData->signature); /* Clean up */ UA_ByteString_deleteMembers(&dataToSign); return retval; } void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel, const UA_CreateSessionRequest *request, UA_CreateSessionResponse *response) { if(!channel) { response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR; return; } if(!channel->connection) { response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR; return; } UA_LOG_DEBUG_CHANNEL(&server->config.logger, channel, "Trying to create session"); if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGN || channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) { /* Compare the clientCertificate with the remoteCertificate of the channel. * Both the clientCertificate of this request and the remoteCertificate * of the channel may contain a partial or a complete certificate chain. * The compareCertificate function of the channelModule will compare the * first certificate of each chain. The end certificate shall be located * first in the chain according to the OPC UA specification Part 6 (1.04), * chapter 6.2.3.*/ UA_StatusCode retval = channel->securityPolicy->channelModule. compareCertificate(channel->channelContext, &request->clientCertificate); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING_CHANNEL(&server->config.logger, channel, "The client certificate did not validate"); response->responseHeader.serviceResult = UA_STATUSCODE_BADCERTIFICATEINVALID; return; } } if(channel->securityToken.channelId == 0) { response->responseHeader.serviceResult = UA_STATUSCODE_BADSECURECHANNELIDINVALID; return; } if(!UA_ByteString_equal(&channel->securityPolicy->policyUri, &UA_SECURITY_POLICY_NONE_URI) && request->clientNonce.length < 32) { response->responseHeader.serviceResult = UA_STATUSCODE_BADNONCEINVALID; return; } /* TODO: Compare application URI with certificate uri (decode certificate) */ UA_CertificateVerification *cv = channel->securityPolicy->certificateVerification; if(cv && cv->verifyApplicationURI) { response->responseHeader.serviceResult = cv->verifyApplicationURI(cv->context, &request->clientCertificate, &request->clientDescription.applicationUri); if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_LOG_WARNING_CHANNEL(&server->config.logger, channel, "The client's ApplicationURI did not match the certificate"); return; } } UA_Session *newSession = NULL; response->responseHeader.serviceResult = UA_SessionManager_createSession(&server->sessionManager, channel, request, &newSession); if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_LOG_WARNING_CHANNEL(&server->config.logger, channel, "Processing CreateSessionRequest failed"); return; } UA_assert(newSession != NULL); /* Allocate the response */ response->serverEndpoints = (UA_EndpointDescription *) UA_Array_new(server->config.endpointsSize, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]); if(!response->serverEndpoints) { response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY; UA_SessionManager_removeSession(&server->sessionManager, &newSession->header.authenticationToken); return; } response->serverEndpointsSize = server->config.endpointsSize; /* Copy the server's endpointdescriptions into the response */ for(size_t i = 0; i < server->config.endpointsSize; ++i) response->responseHeader.serviceResult |= UA_EndpointDescription_copy(&server->config.endpoints[i], &response->serverEndpoints[i]); if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_SessionManager_removeSession(&server->sessionManager, &newSession->header.authenticationToken); return; } /* Mirror back the endpointUrl */ for(size_t i = 0; i < response->serverEndpointsSize; ++i) { UA_String_deleteMembers(&response->serverEndpoints[i].endpointUrl); response->responseHeader.serviceResult |= UA_String_copy(&request->endpointUrl, &response->serverEndpoints[i].endpointUrl); } /* Attach the session to the channel. But don't activate for now. */ UA_Session_attachToSecureChannel(newSession, channel); /* Fill the session information */ newSession->maxResponseMessageSize = request->maxResponseMessageSize; newSession->maxRequestMessageSize = channel->connection->config.maxMessageSize; response->responseHeader.serviceResult |= UA_ApplicationDescription_copy(&request->clientDescription, &newSession->clientDescription); /* Prepare the response */ response->sessionId = newSession->sessionId; response->revisedSessionTimeout = (UA_Double)newSession->timeout; response->authenticationToken = newSession->header.authenticationToken; response->responseHeader.serviceResult |= UA_String_copy(&request->sessionName, &newSession->sessionName); UA_ByteString_init(&response->serverCertificate); if(server->config.endpointsSize > 0) for(size_t i = 0; i < response->serverEndpointsSize; ++i) { if(response->serverEndpoints[i].securityMode==channel->securityMode && UA_ByteString_equal(&response->serverEndpoints[i].securityPolicyUri, &channel->securityPolicy->policyUri) && UA_String_equal(&response->serverEndpoints[i].endpointUrl, &request->endpointUrl)) { response->responseHeader.serviceResult |= UA_ByteString_copy(&response->serverEndpoints[i].serverCertificate, &response->serverCertificate); } } /* Create a session nonce */ response->responseHeader.serviceResult |= UA_Session_generateNonce(newSession); response->responseHeader.serviceResult |= UA_ByteString_copy(&newSession->serverNonce, &response->serverNonce); /* Sign the signature */ response->responseHeader.serviceResult |= signCreateSessionResponse(server, channel, request, response); /* Failure -> remove the session */ if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_SessionManager_removeSession(&server->sessionManager, &newSession->header.authenticationToken); return; } UA_LOG_INFO_CHANNEL(&server->config.logger, channel, "Session " UA_PRINTF_GUID_FORMAT " created", UA_PRINTF_GUID_DATA(newSession->sessionId.identifier.guid)); } static UA_StatusCode checkSignature(const UA_Server *server, const UA_SecureChannel *channel, UA_Session *session, const UA_ActivateSessionRequest *request) { if(channel->securityMode != UA_MESSAGESECURITYMODE_SIGN && channel->securityMode != UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) return UA_STATUSCODE_GOOD; /* Check for zero signature length in client signature */ if(request->clientSignature.signature.length == 0) { return UA_STATUSCODE_BADAPPLICATIONSIGNATUREINVALID; } if(!channel->securityPolicy) return UA_STATUSCODE_BADINTERNALERROR; const UA_SecurityPolicy *securityPolicy = channel->securityPolicy; const UA_ByteString *localCertificate = &securityPolicy->localCertificate; size_t dataToVerifySize = localCertificate->length + session->serverNonce.length; UA_ByteString dataToVerify; UA_StatusCode retval = UA_ByteString_allocBuffer(&dataToVerify, dataToVerifySize); if(retval != UA_STATUSCODE_GOOD) return retval; memcpy(dataToVerify.data, localCertificate->data, localCertificate->length); memcpy(dataToVerify.data + localCertificate->length, session->serverNonce.data, session->serverNonce.length); retval = securityPolicy->certificateSigningAlgorithm.verify(securityPolicy, channel->channelContext, &dataToVerify, &request->clientSignature.signature); UA_ByteString_deleteMembers(&dataToVerify); return retval; } #ifdef UA_ENABLE_ENCRYPTION static UA_StatusCode decryptPassword(UA_SecurityPolicy *securityPolicy, void *tempChannelContext, const UA_ByteString *serverNonce, UA_UserNameIdentityToken *userToken) { UA_SecurityPolicyEncryptionAlgorithm *asymEnc = &securityPolicy->asymmetricModule.cryptoModule.encryptionAlgorithm; if(!UA_String_equal(&userToken->encryptionAlgorithm, &asymEnc->uri)) return UA_STATUSCODE_BADIDENTITYTOKENINVALID; UA_UInt32 tokenSecretLength; UA_ByteString decryptedTokenSecret, tokenServerNonce; if(UA_ByteString_copy(&userToken->password, &decryptedTokenSecret) != UA_STATUSCODE_GOOD) return UA_STATUSCODE_BADIDENTITYTOKENINVALID; UA_StatusCode retval = UA_STATUSCODE_BADIDENTITYTOKENINVALID; if(asymEnc->decrypt(securityPolicy, tempChannelContext, &decryptedTokenSecret) != UA_STATUSCODE_GOOD) goto cleanup; memcpy(&tokenSecretLength, decryptedTokenSecret.data, sizeof(UA_UInt32)); /* The decrypted data must be large enough to include the Encrypted Token * Secret Format and the length field must indicate enough data to include * the server nonce. */ if(decryptedTokenSecret.length < sizeof(UA_UInt32) + serverNonce->length || decryptedTokenSecret.length < sizeof(UA_UInt32) + tokenSecretLength || tokenSecretLength < serverNonce->length) goto cleanup; /* If the Encrypted Token Secret contains padding, the padding must be * zeroes according to the 1.04.1 specification errata, chapter 3. */ for(size_t i = sizeof(UA_UInt32) + tokenSecretLength; i < decryptedTokenSecret.length; i++) { if(decryptedTokenSecret.data[i] != 0) goto cleanup; } /* The server nonce must match according to the 1.04.1 specification errata, * chapter 3. */ tokenServerNonce.length = serverNonce->length; tokenServerNonce.data = &decryptedTokenSecret.data[sizeof(UA_UInt32) + tokenSecretLength - serverNonce->length]; if(!UA_ByteString_equal(serverNonce, &tokenServerNonce)) goto cleanup; /* The password was decrypted successfully. Replace usertoken with the * decrypted password. The encryptionAlgorithm and policyId fields are left * in the UserToken as an indication for the AccessControl plugin that * evaluates the decrypted content. */ memcpy(userToken->password.data, &decryptedTokenSecret.data[sizeof(UA_UInt32)], tokenSecretLength - serverNonce->length); userToken->password.length = tokenSecretLength - serverNonce->length; retval = UA_STATUSCODE_GOOD; cleanup: UA_ByteString_deleteMembers(&decryptedTokenSecret); return retval; } #endif /* TODO: Check all of the following: * * Part 4, §5.6.3: When the ActivateSession Service is called for the first time * then the Server shall reject the request if the SecureChannel is not same as * the one associated with the CreateSession request. Subsequent calls to * ActivateSession may be associated with different SecureChannels. If this is * the case then the Server shall verify that the Certificate the Client used to * create the new SecureChannel is the same as the Certificate used to create * the original SecureChannel. In addition, the Server shall verify that the * Client supplied a UserIdentityToken that is identical to the token currently * associated with the Session. Once the Server accepts the new SecureChannel it * shall reject requests sent via the old SecureChannel. */ void Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel, UA_Session *session, const UA_ActivateSessionRequest *request, UA_ActivateSessionResponse *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Execute ActivateSession"); if(session->validTill < UA_DateTime_nowMonotonic()) { UA_LOG_INFO_SESSION(&server->config.logger, session, "ActivateSession: SecureChannel %i wants " "to activate, but the session has timed out", channel->securityToken.channelId); response->responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID; return; } /* Check if the signature corresponds to the ServerNonce that was last sent * to the client */ response->responseHeader.serviceResult = checkSignature(server, channel, session, request); if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_LOG_INFO_SESSION(&server->config.logger, session, "Signature check failed with status code %s", UA_StatusCode_name(response->responseHeader.serviceResult)); return; } /* Find the matching endpoint */ const UA_EndpointDescription *ed = NULL; for(size_t i = 0; ed == NULL && i < server->config.endpointsSize; ++i) { const UA_EndpointDescription *e = &server->config.endpoints[i]; /* Match the Security Mode */ if(e->securityMode != channel->securityMode) continue; /* Match the SecurityPolicy */ if(!UA_String_equal(&e->securityPolicyUri, &channel->securityPolicy->policyUri)) continue; /* Match the UserTokenType */ for(size_t j = 0; j < e->userIdentityTokensSize; j++) { const UA_UserTokenPolicy *u = &e->userIdentityTokens[j]; if(u->tokenType == UA_USERTOKENTYPE_ANONYMOUS) { /* Part 4, Section 5.6.3.2, Table 17: A NULL or empty * UserIdentityToken should be treated as Anonymous */ if(request->userIdentityToken.content.decoded.type != &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN] && request->userIdentityToken.encoding != UA_EXTENSIONOBJECT_ENCODED_NOBODY) continue; } else if(u->tokenType == UA_USERTOKENTYPE_USERNAME) { if(request->userIdentityToken.content.decoded.type != &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]) continue; } else if(u->tokenType == UA_USERTOKENTYPE_CERTIFICATE) { if(request->userIdentityToken.content.decoded.type != &UA_TYPES[UA_TYPES_X509IDENTITYTOKEN]) continue; } else if(u->tokenType == UA_USERTOKENTYPE_ISSUEDTOKEN) { if(request->userIdentityToken.content.decoded.type != &UA_TYPES[UA_TYPES_ISSUEDIDENTITYTOKEN]) continue; } else { response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID; return; } /* Match found */ ed = e; break; } } /* No matching endpoint found */ if(!ed) { response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID; return; } #ifdef UA_ENABLE_ENCRYPTION /* If it is a UserNameIdentityToken, decrypt the password if encrypted */ if((request->userIdentityToken.encoding == UA_EXTENSIONOBJECT_DECODED) && (request->userIdentityToken.content.decoded.type == &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN])) { UA_UserNameIdentityToken *userToken = (UA_UserNameIdentityToken *) request->userIdentityToken.content.decoded.data; /* Find the UserTokenPolicy */ UA_Byte tokenIndex = 0; for(; tokenIndex < ed->userIdentityTokensSize; tokenIndex++) { if(ed->userIdentityTokens[tokenIndex].tokenType != UA_USERTOKENTYPE_USERNAME) continue; if(UA_String_equal(&userToken->policyId, &ed->userIdentityTokens[tokenIndex].policyId)) break; } if(tokenIndex == ed->userIdentityTokensSize) { response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID; return; } /* Get the SecurityPolicy. If the userTokenPolicy doesn't specify a * security policy the security policy of the secure channel is used. */ UA_SecurityPolicy* securityPolicy; if(ed->userIdentityTokens[tokenIndex].securityPolicyUri.data == NULL) securityPolicy = UA_SecurityPolicy_getSecurityPolicyByUri(server, &ed->securityPolicyUri); else securityPolicy = UA_SecurityPolicy_getSecurityPolicyByUri(server, &ed->userIdentityTokens[tokenIndex].securityPolicyUri); if(!securityPolicy) { response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR; return; } /* Encrypted password? */ if(!UA_String_equal(&securityPolicy->policyUri, &UA_SECURITY_POLICY_NONE_URI)) { /* Test if the encryption algorithm is correctly specified */ if(!UA_String_equal(&userToken->encryptionAlgorithm, &securityPolicy->asymmetricModule.cryptoModule. encryptionAlgorithm.uri)) { response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID; return; } /* Create a temporary channel context if a different SecurityPolicy is * used for the password from the SecureChannel */ void *tempChannelContext = channel->channelContext; if(securityPolicy != channel->securityPolicy) { /* TODO: This is a hack. We use our own certificate to create a * channel context. Because the client does not provide one in a * #None SecureChannel. We should not need a ChannelContext at all * for asymmetric decryption where the remote certificate is not * used. */ response->responseHeader.serviceResult = securityPolicy->channelModule.newContext(securityPolicy, &securityPolicy->localCertificate, &tempChannelContext); if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_LOG_WARNING_SESSION(&server->config.logger, session, "ActivateSession: " "Failed to create a context for the SecurityPolicy %.*s", (int)securityPolicy->policyUri.length, securityPolicy->policyUri.data); return; } } /* Decrypt */ response->responseHeader.serviceResult = decryptPassword(securityPolicy, tempChannelContext, &session->serverNonce, userToken); /* Remove the temporary channel context */ if(securityPolicy != channel->securityPolicy) securityPolicy->channelModule.deleteContext(tempChannelContext); } if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_LOG_INFO_SESSION(&server->config.logger, session, "ActivateSession: " "Failed to decrypt the password with the status code %s", UA_StatusCode_name(response->responseHeader.serviceResult)); } } #endif /* Callback into userland access control */ response->responseHeader.serviceResult = server->config.accessControl.activateSession(server, &server->config.accessControl, ed, &channel->remoteCertificate, &session->sessionId, &request->userIdentityToken, &session->sessionHandle); if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_LOG_INFO_SESSION(&server->config.logger, session, "ActivateSession: The AccessControl plugin " "denied the access with the status code %s", UA_StatusCode_name(response->responseHeader.serviceResult)); return; } if(session->header.channel && session->header.channel != channel) { UA_LOG_INFO_SESSION(&server->config.logger, session, "ActivateSession: Detach from old channel"); /* Detach the old SecureChannel and attach the new */ UA_Session_detachFromSecureChannel(session); UA_Session_attachToSecureChannel(session, channel); } /* Activate the session */ session->activated = true; UA_Session_updateLifetime(session); /* Generate a new session nonce for the next time ActivateSession is called */ response->responseHeader.serviceResult = UA_Session_generateNonce(session); response->responseHeader.serviceResult |= UA_ByteString_copy(&session->serverNonce, &response->serverNonce); if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_Session_detachFromSecureChannel(session); session->activated = false; UA_LOG_INFO_SESSION(&server->config.logger, session, "ActivateSession: Could not generate a server nonce"); return; } UA_LOG_INFO_SESSION(&server->config.logger, session, "ActivateSession: Session activated"); } void Service_CloseSession(UA_Server *server, UA_Session *session, const UA_CloseSessionRequest *request, UA_CloseSessionResponse *response) { UA_LOG_INFO_SESSION(&server->config.logger, session, "CloseSession"); response->responseHeader.serviceResult = UA_SessionManager_removeSession(&server->sessionManager, &session->header.authenticationToken); } /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_services_attribute.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2015-2016 (c) Sten Grüner * Copyright 2014-2017 (c) Florian Palm * Copyright 2015 (c) Christian Fimmers * Copyright 2015-2016 (c) Chris Iatrou * Copyright 2015-2016 (c) Oleksiy Vasylyev * Copyright 2015 (c) wuyangtang * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2016 (c) Lorenz Haas * Copyright 2017 (c) frax2222 * Copyright 2017 (c) Thomas Bender * Copyright 2017 (c) Julian Grothoff * Copyright 2017 (c) Jonas Green * Copyright 2017 (c) Henrik Norrman */ #ifdef UA_ENABLE_HISTORIZING #endif /******************/ /* Access Control */ /******************/ static UA_UInt32 getUserWriteMask(UA_Server *server, const UA_Session *session, const UA_Node *node) { if(session == &server->adminSession) return 0xFFFFFFFF; /* the local admin user has all rights */ return node->writeMask & server->config.accessControl.getUserRightsMask(server, &server->config.accessControl, &session->sessionId, session->sessionHandle, &node->nodeId, node->context); } static UA_Byte getAccessLevel(UA_Server *server, const UA_Session *session, const UA_VariableNode *node) { if(session == &server->adminSession) return 0xFF; /* the local admin user has all rights */ return node->accessLevel; } static UA_Byte getUserAccessLevel(UA_Server *server, const UA_Session *session, const UA_VariableNode *node) { if(session == &server->adminSession) return 0xFF; /* the local admin user has all rights */ return node->accessLevel & server->config.accessControl.getUserAccessLevel(server, &server->config.accessControl, &session->sessionId, session->sessionHandle, &node->nodeId, node->context); } static UA_Boolean getUserExecutable(UA_Server *server, const UA_Session *session, const UA_MethodNode *node) { if(session == &server->adminSession) return true; /* the local admin user has all rights */ return node->executable & server->config.accessControl.getUserExecutable(server, &server->config.accessControl, &session->sessionId, session->sessionHandle, &node->nodeId, node->context); } /****************/ /* Read Service */ /****************/ static UA_StatusCode readIsAbstractAttribute(const UA_Node *node, UA_Variant *v) { const UA_Boolean *isAbstract; switch(node->nodeClass) { case UA_NODECLASS_REFERENCETYPE: isAbstract = &((const UA_ReferenceTypeNode*)node)->isAbstract; break; case UA_NODECLASS_OBJECTTYPE: isAbstract = &((const UA_ObjectTypeNode*)node)->isAbstract; break; case UA_NODECLASS_VARIABLETYPE: isAbstract = &((const UA_VariableTypeNode*)node)->isAbstract; break; case UA_NODECLASS_DATATYPE: isAbstract = &((const UA_DataTypeNode*)node)->isAbstract; break; default: return UA_STATUSCODE_BADATTRIBUTEIDINVALID; } return UA_Variant_setScalarCopy(v, isAbstract, &UA_TYPES[UA_TYPES_BOOLEAN]); } static UA_StatusCode readValueAttributeFromNode(UA_Server *server, UA_Session *session, const UA_VariableNode *vn, UA_DataValue *v, UA_NumericRange *rangeptr) { /* Update the value by the user callback */ if(vn->value.data.callback.onRead) { vn->value.data.callback.onRead(server, &session->sessionId, session->sessionHandle, &vn->nodeId, vn->context, rangeptr, &vn->value.data.value); vn = (const UA_VariableNode*)UA_Nodestore_getNode(server->nsCtx, &vn->nodeId); if(!vn) return UA_STATUSCODE_BADNODEIDUNKNOWN; } /* Set the result */ if(rangeptr) return UA_Variant_copyRange(&vn->value.data.value.value, &v->value, *rangeptr); UA_StatusCode retval = UA_DataValue_copy(&vn->value.data.value, v); /* Clean up */ if(vn->value.data.callback.onRead) UA_Nodestore_releaseNode(server->nsCtx, (const UA_Node *)vn); return retval; } static UA_StatusCode readValueAttributeFromDataSource(UA_Server *server, UA_Session *session, const UA_VariableNode *vn, UA_DataValue *v, UA_TimestampsToReturn timestamps, UA_NumericRange *rangeptr) { if(!vn->value.dataSource.read) return UA_STATUSCODE_BADINTERNALERROR; UA_Boolean sourceTimeStamp = (timestamps == UA_TIMESTAMPSTORETURN_SOURCE || timestamps == UA_TIMESTAMPSTORETURN_BOTH); UA_DataValue v2; UA_DataValue_init(&v2); UA_StatusCode retval = vn->value.dataSource. read(server, &session->sessionId, session->sessionHandle, &vn->nodeId, vn->context, sourceTimeStamp, rangeptr, &v2); if(v2.hasValue && v2.value.storageType == UA_VARIANT_DATA_NODELETE) { retval = UA_DataValue_copy(&v2, v); UA_DataValue_deleteMembers(&v2); } else { *v = v2; } return retval; } static UA_StatusCode readValueAttributeComplete(UA_Server *server, UA_Session *session, const UA_VariableNode *vn, UA_TimestampsToReturn timestamps, const UA_String *indexRange, UA_DataValue *v) { /* Compute the index range */ UA_NumericRange range; UA_NumericRange *rangeptr = NULL; UA_StatusCode retval = UA_STATUSCODE_GOOD; if(indexRange && indexRange->length > 0) { retval = UA_NumericRange_parseFromString(&range, indexRange); if(retval != UA_STATUSCODE_GOOD) return retval; rangeptr = ⦥ } /* Read the value */ if(vn->valueSource == UA_VALUESOURCE_DATA) retval = readValueAttributeFromNode(server, session, vn, v, rangeptr); else retval = readValueAttributeFromDataSource(server, session, vn, v, timestamps, rangeptr); /* Clean up */ if(rangeptr) UA_free(range.dimensions); return retval; } UA_StatusCode readValueAttribute(UA_Server *server, UA_Session *session, const UA_VariableNode *vn, UA_DataValue *v) { return readValueAttributeComplete(server, session, vn, UA_TIMESTAMPSTORETURN_NEITHER, NULL, v); } static const UA_String binEncoding = {sizeof("Default Binary")-1, (UA_Byte*)"Default Binary"}; static const UA_String xmlEncoding = {sizeof("Default XML")-1, (UA_Byte*)"Default XML"}; static const UA_String jsonEncoding = {sizeof("Default JSON")-1, (UA_Byte*)"Default JSON"}; #define CHECK_NODECLASS(CLASS) \ if(!(node->nodeClass & (CLASS))) { \ retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID; \ break; \ } /* Returns a datavalue that may point into the node via the * UA_VARIANT_DATA_NODELETE tag. Don't access the returned DataValue once the * node has been released! */ void ReadWithNode(const UA_Node *node, UA_Server *server, UA_Session *session, UA_TimestampsToReturn timestampsToReturn, const UA_ReadValueId *id, UA_DataValue *v) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Read the attribute %i", id->attributeId); /* Only Binary Encoding is supported */ if(id->dataEncoding.name.length > 0 && !UA_String_equal(&binEncoding, &id->dataEncoding.name)) { if(UA_String_equal(&xmlEncoding, &id->dataEncoding.name) || UA_String_equal(&jsonEncoding, &id->dataEncoding.name)) v->status = UA_STATUSCODE_BADDATAENCODINGUNSUPPORTED; else v->status = UA_STATUSCODE_BADDATAENCODINGINVALID; v->hasStatus = true; return; } /* Index range for an attribute other than value */ if(id->indexRange.length > 0 && id->attributeId != UA_ATTRIBUTEID_VALUE) { v->hasStatus = true; v->status = UA_STATUSCODE_BADINDEXRANGENODATA; return; } /* Read the attribute */ UA_StatusCode retval = UA_STATUSCODE_GOOD; switch(id->attributeId) { case UA_ATTRIBUTEID_NODEID: retval = UA_Variant_setScalarCopy(&v->value, &node->nodeId, &UA_TYPES[UA_TYPES_NODEID]); break; case UA_ATTRIBUTEID_NODECLASS: retval = UA_Variant_setScalarCopy(&v->value, &node->nodeClass, &UA_TYPES[UA_TYPES_NODECLASS]); break; case UA_ATTRIBUTEID_BROWSENAME: retval = UA_Variant_setScalarCopy(&v->value, &node->browseName, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]); break; case UA_ATTRIBUTEID_DISPLAYNAME: retval = UA_Variant_setScalarCopy(&v->value, &node->displayName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]); break; case UA_ATTRIBUTEID_DESCRIPTION: retval = UA_Variant_setScalarCopy(&v->value, &node->description, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]); break; case UA_ATTRIBUTEID_WRITEMASK: retval = UA_Variant_setScalarCopy(&v->value, &node->writeMask, &UA_TYPES[UA_TYPES_UINT32]); break; case UA_ATTRIBUTEID_USERWRITEMASK: { UA_UInt32 userWriteMask = getUserWriteMask(server, session, node); retval = UA_Variant_setScalarCopy(&v->value, &userWriteMask, &UA_TYPES[UA_TYPES_UINT32]); break; } case UA_ATTRIBUTEID_ISABSTRACT: retval = readIsAbstractAttribute(node, &v->value); break; case UA_ATTRIBUTEID_SYMMETRIC: CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE); retval = UA_Variant_setScalarCopy(&v->value, &((const UA_ReferenceTypeNode*)node)->symmetric, &UA_TYPES[UA_TYPES_BOOLEAN]); break; case UA_ATTRIBUTEID_INVERSENAME: CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE); retval = UA_Variant_setScalarCopy(&v->value, &((const UA_ReferenceTypeNode*)node)->inverseName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]); break; case UA_ATTRIBUTEID_CONTAINSNOLOOPS: CHECK_NODECLASS(UA_NODECLASS_VIEW); retval = UA_Variant_setScalarCopy(&v->value, &((const UA_ViewNode*)node)->containsNoLoops, &UA_TYPES[UA_TYPES_BOOLEAN]); break; case UA_ATTRIBUTEID_EVENTNOTIFIER: CHECK_NODECLASS(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT); if(node->nodeClass == UA_NODECLASS_VIEW) { retval = UA_Variant_setScalarCopy(&v->value, &((const UA_ViewNode*)node)->eventNotifier, &UA_TYPES[UA_TYPES_BYTE]); } else{ retval = UA_Variant_setScalarCopy(&v->value, &((const UA_ObjectNode*)node)->eventNotifier, &UA_TYPES[UA_TYPES_BYTE]); } break; case UA_ATTRIBUTEID_VALUE: { CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); /* VariableTypes don't have the AccessLevel concept. Always allow reading the value. */ if(node->nodeClass == UA_NODECLASS_VARIABLE) { /* The access to a value variable is granted via the AccessLevel * and UserAccessLevel attributes */ UA_Byte accessLevel = getAccessLevel(server, session, (const UA_VariableNode*)node); if(!(accessLevel & (UA_ACCESSLEVELMASK_READ))) { retval = UA_STATUSCODE_BADNOTREADABLE; break; } accessLevel = getUserAccessLevel(server, session, (const UA_VariableNode*)node); if(!(accessLevel & (UA_ACCESSLEVELMASK_READ))) { retval = UA_STATUSCODE_BADUSERACCESSDENIED; break; } } retval = readValueAttributeComplete(server, session, (const UA_VariableNode*)node, timestampsToReturn, &id->indexRange, v); break; } case UA_ATTRIBUTEID_DATATYPE: CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); retval = UA_Variant_setScalarCopy(&v->value, &((const UA_VariableTypeNode*)node)->dataType, &UA_TYPES[UA_TYPES_NODEID]); break; case UA_ATTRIBUTEID_VALUERANK: CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); retval = UA_Variant_setScalarCopy(&v->value, &((const UA_VariableTypeNode*)node)->valueRank, &UA_TYPES[UA_TYPES_INT32]); break; case UA_ATTRIBUTEID_ARRAYDIMENSIONS: CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); retval = UA_Variant_setArrayCopy(&v->value, ((const UA_VariableTypeNode*)node)->arrayDimensions, ((const UA_VariableTypeNode*)node)->arrayDimensionsSize, &UA_TYPES[UA_TYPES_UINT32]); break; case UA_ATTRIBUTEID_ACCESSLEVEL: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); retval = UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode*)node)->accessLevel, &UA_TYPES[UA_TYPES_BYTE]); break; case UA_ATTRIBUTEID_USERACCESSLEVEL: { CHECK_NODECLASS(UA_NODECLASS_VARIABLE); UA_Byte userAccessLevel = getUserAccessLevel(server, session, (const UA_VariableNode*)node); retval = UA_Variant_setScalarCopy(&v->value, &userAccessLevel, &UA_TYPES[UA_TYPES_BYTE]); break; } case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); retval = UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode*)node)->minimumSamplingInterval, &UA_TYPES[UA_TYPES_DOUBLE]); break; case UA_ATTRIBUTEID_HISTORIZING: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); retval = UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode*)node)->historizing, &UA_TYPES[UA_TYPES_BOOLEAN]); break; case UA_ATTRIBUTEID_EXECUTABLE: CHECK_NODECLASS(UA_NODECLASS_METHOD); retval = UA_Variant_setScalarCopy(&v->value, &((const UA_MethodNode*)node)->executable, &UA_TYPES[UA_TYPES_BOOLEAN]); break; case UA_ATTRIBUTEID_USEREXECUTABLE: { CHECK_NODECLASS(UA_NODECLASS_METHOD); UA_Boolean userExecutable = getUserExecutable(server, session, (const UA_MethodNode*)node); retval = UA_Variant_setScalarCopy(&v->value, &userExecutable, &UA_TYPES[UA_TYPES_BOOLEAN]); break; } default: retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID; } /* Return error code when reading has failed */ if(retval != UA_STATUSCODE_GOOD) { v->hasStatus = true; v->status = retval; return; } v->hasValue = true; /* Create server timestamp */ if(timestampsToReturn == UA_TIMESTAMPSTORETURN_SERVER || timestampsToReturn == UA_TIMESTAMPSTORETURN_BOTH) { if (!v->hasServerTimestamp) { v->serverTimestamp = UA_DateTime_now(); v->hasServerTimestamp = true; } } else { /* In case the ServerTimestamp has been set manually */ v->hasServerTimestamp = false; } /* Handle source time stamp */ if(id->attributeId == UA_ATTRIBUTEID_VALUE) { if(timestampsToReturn == UA_TIMESTAMPSTORETURN_SERVER || timestampsToReturn == UA_TIMESTAMPSTORETURN_NEITHER) { v->hasSourceTimestamp = false; v->hasSourcePicoseconds = false; } else if(!v->hasSourceTimestamp) { v->sourceTimestamp = UA_DateTime_now(); v->hasSourceTimestamp = true; } } } static void Operation_Read(UA_Server *server, UA_Session *session, UA_ReadRequest *request, UA_ReadValueId *rvi, UA_DataValue *result) { /* Get the node */ const UA_Node *node = UA_Nodestore_getNode(server->nsCtx, &rvi->nodeId); /* Perform the read operation */ if(node) { ReadWithNode(node, server, session, request->timestampsToReturn, rvi, result); UA_Nodestore_releaseNode(server->nsCtx, node); } else { result->hasStatus = true; result->status = UA_STATUSCODE_BADNODEIDUNKNOWN; } } void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *request, UA_ReadResponse *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing ReadRequest"); /* Check if the timestampstoreturn is valid */ if(request->timestampsToReturn > UA_TIMESTAMPSTORETURN_NEITHER) { response->responseHeader.serviceResult = UA_STATUSCODE_BADTIMESTAMPSTORETURNINVALID; return; } /* Check if maxAge is valid */ if(request->maxAge < 0) { response->responseHeader.serviceResult = UA_STATUSCODE_BADMAXAGEINVALID; return; } /* Check if there are too many operations */ if(server->config.maxNodesPerRead != 0 && request->nodesToReadSize > server->config.maxNodesPerRead) { response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS; return; } response->responseHeader.serviceResult = UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_Read, request, &request->nodesToReadSize, &UA_TYPES[UA_TYPES_READVALUEID], &response->resultsSize, &UA_TYPES[UA_TYPES_DATAVALUE]); } UA_DataValue UA_Server_readWithSession(UA_Server *server, UA_Session *session, const UA_ReadValueId *item, UA_TimestampsToReturn timestampsToReturn) { UA_DataValue dv; UA_DataValue_init(&dv); /* Get the node */ const UA_Node *node = UA_Nodestore_getNode(server->nsCtx, &item->nodeId); if(!node) { dv.hasStatus = true; dv.status = UA_STATUSCODE_BADNODEIDUNKNOWN; return dv; } /* Perform the read operation */ ReadWithNode(node, server, session, timestampsToReturn, item, &dv); /* Release the node and return */ UA_Nodestore_releaseNode(server->nsCtx, node); return dv; } /* Exposes the Read service to local users */ UA_DataValue UA_Server_read(UA_Server *server, const UA_ReadValueId *item, UA_TimestampsToReturn timestamps) { return UA_Server_readWithSession(server, &server->adminSession, item, timestamps); } /* Used in inline functions exposing the Read service with more syntactic sugar * for individual attributes */ UA_StatusCode __UA_Server_read(UA_Server *server, const UA_NodeId *nodeId, const UA_AttributeId attributeId, void *v) { /* Call the read service */ UA_ReadValueId item; UA_ReadValueId_init(&item); item.nodeId = *nodeId; item.attributeId = attributeId; UA_DataValue dv = UA_Server_read(server, &item, UA_TIMESTAMPSTORETURN_NEITHER); /* Check the return value */ UA_StatusCode retval = UA_STATUSCODE_GOOD; if(dv.hasStatus) retval = dv.status; else if(!dv.hasValue) retval = UA_STATUSCODE_BADUNEXPECTEDERROR; if(retval != UA_STATUSCODE_GOOD) { UA_DataValue_deleteMembers(&dv); return retval; } if(attributeId == UA_ATTRIBUTEID_VALUE || attributeId == UA_ATTRIBUTEID_ARRAYDIMENSIONS) { /* Return the entire variant */ memcpy(v, &dv.value, sizeof(UA_Variant)); } else { /* Return the variant content only */ memcpy(v, dv.value.data, dv.value.type->memSize); UA_free(dv.value.data); } return retval; } UA_StatusCode UA_Server_readObjectProperty(UA_Server *server, const UA_NodeId objectId, const UA_QualifiedName propertyName, UA_Variant *value) { UA_RelativePathElement rpe; UA_RelativePathElement_init(&rpe); rpe.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY); rpe.isInverse = false; rpe.includeSubtypes = false; rpe.targetName = propertyName; UA_BrowsePath bp; UA_BrowsePath_init(&bp); bp.startingNode = objectId; bp.relativePath.elementsSize = 1; bp.relativePath.elements = &rpe; UA_StatusCode retval; UA_BrowsePathResult bpr = UA_Server_translateBrowsePathToNodeIds(server, &bp); if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) { retval = bpr.statusCode; UA_BrowsePathResult_deleteMembers(&bpr); return retval; } retval = UA_Server_readValue(server, bpr.targets[0].targetId.nodeId, value); UA_BrowsePathResult_deleteMembers(&bpr); return retval; } /*****************/ /* Type Checking */ /*****************/ static UA_DataTypeKind typeEquivalence(const UA_DataType *t) { UA_DataTypeKind k = (UA_DataTypeKind)t->typeKind; if(k == UA_DATATYPEKIND_ENUM) return UA_DATATYPEKIND_INT32; return k; } static const UA_NodeId enumNodeId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_ENUMERATION}}; UA_Boolean compatibleDataType(UA_Server *server, const UA_NodeId *dataType, const UA_NodeId *constraintDataType, UA_Boolean isValue) { /* 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; /* Same datatypes */ if (UA_NodeId_equal(dataType, 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->nsCtx, dataType, constraintDataType, &subtypeId, 1)) return true; /* Enum allows Int32 (only) */ if(UA_NodeId_equal(dataType, &UA_TYPES[UA_TYPES_INT32].typeId) && isNodeInTree(server->nsCtx, constraintDataType, &enumNodeId, &subtypeId, 1)) return true; /* More checks for the data type of real values (variants) */ if(isValue) { /* 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->nsCtx, constraintDataType, dataType, &subtypeId, 1)) return true; } return false; } /* Test whether a ValueRank and the given arraydimensions are compatible. * * 5.6.2 Variable NodeClass: If the maximum is unknown the value shall be 0. The * number of elements shall be equal to the value of the ValueRank Attribute. * This Attribute shall be null if ValueRank <= 0. */ UA_Boolean compatibleValueRankArrayDimensions(UA_Server *server, UA_Session *session, UA_Int32 valueRank, size_t arrayDimensionsSize) { /* ValueRank invalid */ if(valueRank < UA_VALUERANK_SCALAR_OR_ONE_DIMENSION) { UA_LOG_INFO_SESSION(&server->config.logger, session, "The ValueRank is invalid (< -3)"); return false; } /* case -3, UA_VALUERANK_SCALAR_OR_ONE_DIMENSION: the value can be a scalar or a one dimensional array */ /* case -2, UA_VALUERANK_ANY: the value can be a scalar or an array with any number of dimensions */ /* case -1, UA_VALUERANK_SCALAR: the value is a scalar */ /* case 0, UA_VALUERANK_ONE_OR_MORE_DIMENSIONS: the value is an array with one or more dimensions */ if(valueRank <= UA_VALUERANK_ONE_OR_MORE_DIMENSIONS) { if(arrayDimensionsSize > 0) { UA_LOG_INFO_SESSION(&server->config.logger, session, "No ArrayDimensions can be defined for a ValueRank <= 0"); return false; } return true; } /* case >= 1, UA_VALUERANK_ONE_DIMENSION: the value is an array with the specified number of dimensions */ if(arrayDimensionsSize != (size_t)valueRank) { UA_LOG_INFO_SESSION(&server->config.logger, session, "The number of ArrayDimensions is not equal to the (positive) ValueRank"); return false; } return true; } UA_Boolean compatibleValueRanks(UA_Int32 valueRank, UA_Int32 constraintValueRank) { /* Check if the valuerank of the variabletype allows the change. */ switch(constraintValueRank) { case UA_VALUERANK_SCALAR_OR_ONE_DIMENSION: /* the value can be a scalar or a one dimensional array */ if(valueRank != UA_VALUERANK_SCALAR && valueRank != UA_VALUERANK_ONE_DIMENSION) return false; break; case UA_VALUERANK_ANY: /* the value can be a scalar or an array with any number of dimensions */ break; case UA_VALUERANK_SCALAR: /* the value is a scalar */ if(valueRank != UA_VALUERANK_SCALAR) return false; break; case UA_VALUERANK_ONE_OR_MORE_DIMENSIONS: /* the value is an array with one or more dimensions */ if(valueRank < (UA_Int32) UA_VALUERANK_ONE_OR_MORE_DIMENSIONS) return false; break; default: /* >= 1: the value is an array with the specified number of dimensions */ if(valueRank != constraintValueRank) return false; break; } return true; } /* Check if the ValueRank allows for the value dimension. This is more * permissive than checking for the ArrayDimensions attribute. Because the value * can have dimensions if the ValueRank < 0 */ static UA_Boolean compatibleValueRankValue(UA_Int32 valueRank, const UA_Variant *value) { /* Invalid ValueRank */ if(valueRank < UA_VALUERANK_SCALAR_OR_ONE_DIMENSION) return false; /* Empty arrays (-1) always match */ if(!value->data) return true; size_t arrayDims = value->arrayDimensionsSize; if(arrayDims == 0 && !UA_Variant_isScalar(value)) arrayDims = 1; /* array but no arraydimensions -> implicit array dimension 1 */ /* We cannot simply use compatibleValueRankArrayDimensions since we can have * defined ArrayDimensions for the value if the ValueRank is -2 */ switch(valueRank) { case UA_VALUERANK_SCALAR_OR_ONE_DIMENSION: /* The value can be a scalar or a one dimensional array */ return (arrayDims <= 1); case UA_VALUERANK_ANY: /* The value can be a scalar or an array with any number of dimensions */ return true; case UA_VALUERANK_SCALAR: /* The value is a scalar */ return (arrayDims == 0); default: break; } UA_assert(valueRank >= UA_VALUERANK_ONE_OR_MORE_DIMENSIONS); /* case 0: the value is an array with one or more dimensions */ return (arrayDims == (UA_UInt32)valueRank); } UA_Boolean compatibleArrayDimensions(size_t constraintArrayDimensionsSize, const UA_UInt32 *constraintArrayDimensions, size_t testArrayDimensionsSize, const UA_UInt32 *testArrayDimensions) { /* No array dimensions defined -> everything is permitted if the value rank fits */ if(constraintArrayDimensionsSize == 0) return true; /* Dimension count must match */ if(testArrayDimensionsSize != constraintArrayDimensionsSize) return false; /* Dimension lengths must not be larger than the constraint. Zero in the * constraint indicates a wildcard. */ for(size_t i = 0; i < constraintArrayDimensionsSize; ++i) { if(constraintArrayDimensions[i] < testArrayDimensions[i] && constraintArrayDimensions[i] != 0) return false; } return true; } UA_Boolean compatibleValueArrayDimensions(const UA_Variant *value, size_t targetArrayDimensionsSize, const UA_UInt32 *targetArrayDimensions) { size_t valueArrayDimensionsSize = value->arrayDimensionsSize; UA_UInt32 *valueArrayDimensions = value->arrayDimensions; UA_UInt32 tempArrayDimensions; if(!valueArrayDimensions && !UA_Variant_isScalar(value)) { valueArrayDimensionsSize = 1; tempArrayDimensions = (UA_UInt32)value->arrayLength; valueArrayDimensions = &tempArrayDimensions; } UA_assert(valueArrayDimensionsSize == 0 || valueArrayDimensions != NULL); return compatibleArrayDimensions(targetArrayDimensionsSize, targetArrayDimensions, valueArrayDimensionsSize, valueArrayDimensions); } UA_Boolean compatibleValue(UA_Server *server, UA_Session *session, const UA_NodeId *targetDataTypeId, UA_Int32 targetValueRank, size_t targetArrayDimensionsSize, const UA_UInt32 *targetArrayDimensions, const UA_Variant *value, const UA_NumericRange *range) { /* Empty value */ if(!value->type) { /* Empty value is allowed for BaseDataType */ if(UA_NodeId_equal(targetDataTypeId, &UA_TYPES[UA_TYPES_VARIANT].typeId) || UA_NodeId_equal(targetDataTypeId, &UA_NODEID_NULL)) return true; /* Allow empty node values since existing information models may have * variables with no value, e.g. OldValues - ns=0;i=3024. See also * #1889, https://github.com/open62541/open62541/pull/1889#issuecomment-403506538 */ if(server->config.relaxEmptyValueConstraint) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Only Variables with data type BaseDataType can contain an " "empty value. Allow via explicit constraint relaxation."); return true; } UA_LOG_INFO_SESSION(&server->config.logger, session, "Only Variables with data type BaseDataType can contain an 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, true)) 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); } /*****************/ /* Write Service */ /*****************/ static void adjustValue(UA_Server *server, UA_Variant *value, const UA_NodeId *targetDataTypeId) { const UA_DataType *targetDataType = UA_findDataType(targetDataTypeId); if(!targetDataType) return; /* A string is written to a byte array. the valuerank and array dimensions * are checked later */ if(targetDataType == &UA_TYPES[UA_TYPES_BYTE] && value->type == &UA_TYPES[UA_TYPES_BYTESTRING] && UA_Variant_isScalar(value)) { UA_ByteString *str = (UA_ByteString*)value->data; value->type = &UA_TYPES[UA_TYPES_BYTE]; value->arrayLength = str->length; value->data = str->data; return; } /* An enum was sent as an int32, or an opaque type as a bytestring. This * is detected with the typeIndex indicating the "true" datatype. */ UA_DataTypeKind te1 = typeEquivalence(targetDataType); UA_DataTypeKind te2 = typeEquivalence(value->type); if(te1 == te2 && te1 <= UA_DATATYPEKIND_ENUM) { value->type = targetDataType; return; } /* No more possible equivalencies */ } static UA_StatusCode writeArrayDimensionsAttribute(UA_Server *server, UA_Session *session, UA_VariableNode *node, const UA_VariableTypeNode *type, size_t arrayDimensionsSize, UA_UInt32 *arrayDimensions) { UA_assert(node != NULL); UA_assert(type != NULL); /* If this is a variabletype, there must be no instances or subtypes of it * when we do the change */ if(node->nodeClass == UA_NODECLASS_VARIABLETYPE && UA_Node_hasSubTypeOrInstances((UA_Node*)node)) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "Cannot change a variable type with existing instances"); return UA_STATUSCODE_BADINTERNALERROR; } /* Check that the array dimensions match with the valuerank */ if(!compatibleValueRankArrayDimensions(server, session, node->valueRank, arrayDimensionsSize)) { UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "Cannot write the ArrayDimensions. The ValueRank does not match."); return UA_STATUSCODE_BADTYPEMISMATCH; } /* Check if the array dimensions match with the wildcards in the * variabletype (dimension length 0) */ if(type->arrayDimensions && !compatibleArrayDimensions(type->arrayDimensionsSize, type->arrayDimensions, arrayDimensionsSize, arrayDimensions)) { UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "Array dimensions in the variable type do not match"); return UA_STATUSCODE_BADTYPEMISMATCH; } /* Check if the current value is compatible with the array dimensions */ UA_DataValue value; UA_DataValue_init(&value); UA_StatusCode retval = readValueAttribute(server, session, node, &value); if(retval != UA_STATUSCODE_GOOD) return retval; if(value.hasValue) { if(!compatibleValueArrayDimensions(&value.value, arrayDimensionsSize, arrayDimensions)) retval = UA_STATUSCODE_BADTYPEMISMATCH; UA_DataValue_deleteMembers(&value); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "Array dimensions in the current value do not match"); return retval; } } /* Ok, apply */ UA_UInt32 *oldArrayDimensions = node->arrayDimensions; size_t oldArrayDimensionsSize = node->arrayDimensionsSize; retval = UA_Array_copy(arrayDimensions, arrayDimensionsSize, (void**)&node->arrayDimensions, &UA_TYPES[UA_TYPES_UINT32]); if(retval != UA_STATUSCODE_GOOD) return retval; UA_Array_delete(oldArrayDimensions, oldArrayDimensionsSize, &UA_TYPES[UA_TYPES_UINT32]); node->arrayDimensionsSize = arrayDimensionsSize; return UA_STATUSCODE_GOOD; } /* Stack layout: ... | node | type */ static UA_StatusCode writeValueRankAttribute(UA_Server *server, UA_Session *session, UA_VariableNode *node, const UA_VariableTypeNode *type, UA_Int32 valueRank) { UA_assert(node != NULL); UA_assert(type != NULL); UA_Int32 constraintValueRank = type->valueRank; /* If this is a variabletype, there must be no instances or subtypes of it * when we do the change */ if(node->nodeClass == UA_NODECLASS_VARIABLETYPE && UA_Node_hasSubTypeOrInstances((const UA_Node*)node)) return UA_STATUSCODE_BADINTERNALERROR; /* Check if the valuerank of the variabletype allows the change. */ if(!compatibleValueRanks(valueRank, constraintValueRank)) return UA_STATUSCODE_BADTYPEMISMATCH; /* Check if the new valuerank is compatible with the array dimensions. Use * the read service to handle data sources. */ size_t arrayDims = node->arrayDimensionsSize; if(arrayDims == 0) { /* the value could be an array with no arrayDimensions defined. dimensions zero indicate a scalar for compatibleValueRankArrayDimensions. */ UA_DataValue value; UA_DataValue_init(&value); UA_StatusCode retval = readValueAttribute(server, session, node, &value); if(retval != UA_STATUSCODE_GOOD) return retval; if(!value.hasValue || !value.value.type) { /* no value -> apply */ node->valueRank = valueRank; return UA_STATUSCODE_GOOD; } if(!UA_Variant_isScalar(&value.value)) arrayDims = 1; UA_DataValue_deleteMembers(&value); } if(!compatibleValueRankArrayDimensions(server, session, valueRank, arrayDims)) return UA_STATUSCODE_BADTYPEMISMATCH; /* All good, apply the change */ node->valueRank = valueRank; return UA_STATUSCODE_GOOD; } static UA_StatusCode writeDataTypeAttribute(UA_Server *server, UA_Session *session, UA_VariableNode *node, const UA_VariableTypeNode *type, const UA_NodeId *dataType) { UA_assert(node != NULL); UA_assert(type != NULL); /* If this is a variabletype, there must be no instances or subtypes of it when we do the change */ if(node->nodeClass == UA_NODECLASS_VARIABLETYPE && UA_Node_hasSubTypeOrInstances((const UA_Node*)node)) return UA_STATUSCODE_BADINTERNALERROR; /* Does the new type match the constraints of the variabletype? */ if(!compatibleDataType(server, dataType, &type->dataType, false)) return UA_STATUSCODE_BADTYPEMISMATCH; /* Check if the current value would match the new type */ UA_DataValue value; UA_DataValue_init(&value); UA_StatusCode retval = readValueAttribute(server, session, node, &value); if(retval != UA_STATUSCODE_GOOD) return retval; if(value.hasValue) { if(!compatibleValue(server, session, dataType, node->valueRank, node->arrayDimensionsSize, node->arrayDimensions, &value.value, NULL)) retval = UA_STATUSCODE_BADTYPEMISMATCH; UA_DataValue_deleteMembers(&value); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "The current value does not match the new data type"); return retval; } } /* Replace the datatype nodeid */ UA_NodeId dtCopy = node->dataType; retval = UA_NodeId_copy(dataType, &node->dataType); if(retval != UA_STATUSCODE_GOOD) { node->dataType = dtCopy; return retval; } UA_NodeId_deleteMembers(&dtCopy); return UA_STATUSCODE_GOOD; } static UA_StatusCode writeValueAttributeWithoutRange(UA_VariableNode *node, const UA_DataValue *value) { UA_DataValue new_value; UA_StatusCode retval = UA_DataValue_copy(value, &new_value); if(retval != UA_STATUSCODE_GOOD) return retval; UA_DataValue_deleteMembers(&node->value.data.value); node->value.data.value = new_value; return UA_STATUSCODE_GOOD; } static UA_StatusCode writeValueAttributeWithRange(UA_VariableNode *node, const UA_DataValue *value, const UA_NumericRange *rangeptr) { /* Value on both sides? */ if(value->status != node->value.data.value.status || !value->hasValue || !node->value.data.value.hasValue) return UA_STATUSCODE_BADINDEXRANGEINVALID; /* Make scalar a one-entry array for range matching */ UA_Variant editableValue; const UA_Variant *v = &value->value; if(UA_Variant_isScalar(&value->value)) { editableValue = value->value; editableValue.arrayLength = 1; v = &editableValue; } /* Check that the type is an exact match and not only "compatible" */ if(!node->value.data.value.value.type || !v->type || !UA_NodeId_equal(&node->value.data.value.value.type->typeId, &v->type->typeId)) return UA_STATUSCODE_BADTYPEMISMATCH; /* Write the value */ UA_StatusCode retval = UA_Variant_setRangeCopy(&node->value.data.value.value, v->data, v->arrayLength, *rangeptr); if(retval != UA_STATUSCODE_GOOD) return retval; /* Write the status and timestamps */ node->value.data.value.hasStatus = value->hasStatus; node->value.data.value.status = value->status; node->value.data.value.hasSourceTimestamp = value->hasSourceTimestamp; node->value.data.value.sourceTimestamp = value->sourceTimestamp; node->value.data.value.hasSourcePicoseconds = value->hasSourcePicoseconds; node->value.data.value.sourcePicoseconds = value->sourcePicoseconds; return UA_STATUSCODE_GOOD; } /* Stack layout: ... | node */ static UA_StatusCode writeValueAttribute(UA_Server *server, UA_Session *session, UA_VariableNode *node, const UA_DataValue *value, const UA_String *indexRange) { UA_assert(node != NULL); /* Parse the range */ UA_NumericRange range; UA_NumericRange *rangeptr = NULL; UA_StatusCode retval = UA_STATUSCODE_GOOD; if(indexRange && indexRange->length > 0) { retval = UA_NumericRange_parseFromString(&range, indexRange); if(retval != UA_STATUSCODE_GOOD) return retval; rangeptr = ⦥ } /* Created an editable version. The data is not touched. Only the variant * "container". */ UA_DataValue adjustedValue = *value; /* Type checking. May change the type of editableValue */ if(value->hasValue && value->value.type) { adjustValue(server, &adjustedValue.value, &node->dataType); /* The value may be an extension object, especially the nodeset compiler * uses extension objects to write variable values. If value is an * extension object we check if the current node value is also an * extension object. */ const UA_NodeId nodeDataType = UA_NODEID_NUMERIC(0, UA_NS0ID_STRUCTURE); const UA_NodeId *nodeDataTypePtr = &node->dataType; if(value->value.type->typeId.identifierType == UA_NODEIDTYPE_NUMERIC && value->value.type->typeId.identifier.numeric == UA_NS0ID_STRUCTURE) nodeDataTypePtr = &nodeDataType; if(!compatibleValue(server, session, nodeDataTypePtr, node->valueRank, node->arrayDimensionsSize, node->arrayDimensions, &adjustedValue.value, rangeptr)) { if(rangeptr) UA_free(range.dimensions); return UA_STATUSCODE_BADTYPEMISMATCH; } } /* Set the source timestamp if there is none */ UA_DateTime now = UA_DateTime_now(); if(!adjustedValue.hasSourceTimestamp) { adjustedValue.sourceTimestamp = now; adjustedValue.hasSourceTimestamp = true; } if(!adjustedValue.hasServerTimestamp) { adjustedValue.serverTimestamp = now; adjustedValue.hasServerTimestamp = true; } /* Ok, do it */ if(node->valueSource == UA_VALUESOURCE_DATA) { if(!rangeptr) retval = writeValueAttributeWithoutRange(node, &adjustedValue); else retval = writeValueAttributeWithRange(node, &adjustedValue, rangeptr); #ifdef UA_ENABLE_HISTORIZING /* node is a UA_VariableNode*, but it may also point to a UA_VariableTypeNode */ /* UA_VariableTypeNode doesn't have the historizing attribute */ if(retval == UA_STATUSCODE_GOOD && node->nodeClass == UA_NODECLASS_VARIABLE && server->config.historyDatabase.setValue) server->config.historyDatabase.setValue(server, server->config.historyDatabase.context, &session->sessionId, session->sessionHandle, &node->nodeId, node->historizing, &adjustedValue); #endif /* Callback after writing */ if(retval == UA_STATUSCODE_GOOD && node->value.data.callback.onWrite) node->value.data.callback.onWrite(server, &session->sessionId, session->sessionHandle, &node->nodeId, node->context, rangeptr, &adjustedValue); } else { if(node->value.dataSource.write) { retval = node->value.dataSource.write(server, &session->sessionId, session->sessionHandle, &node->nodeId, node->context, rangeptr, &adjustedValue); } else { retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; } } /* Clean up */ if(rangeptr) UA_free(range.dimensions); return retval; } static UA_StatusCode writeIsAbstractAttribute(UA_Node *node, UA_Boolean value) { switch(node->nodeClass) { case UA_NODECLASS_OBJECTTYPE: ((UA_ObjectTypeNode*)node)->isAbstract = value; break; case UA_NODECLASS_REFERENCETYPE: ((UA_ReferenceTypeNode*)node)->isAbstract = value; break; case UA_NODECLASS_VARIABLETYPE: ((UA_VariableTypeNode*)node)->isAbstract = value; break; case UA_NODECLASS_DATATYPE: ((UA_DataTypeNode*)node)->isAbstract = value; break; default: return UA_STATUSCODE_BADNODECLASSINVALID; } return UA_STATUSCODE_GOOD; } /*****************/ /* Write Service */ /*****************/ #define CHECK_DATATYPE_SCALAR(EXP_DT) \ if(!wvalue->value.hasValue || \ &UA_TYPES[UA_TYPES_##EXP_DT] != wvalue->value.value.type || \ !UA_Variant_isScalar(&wvalue->value.value)) { \ retval = UA_STATUSCODE_BADTYPEMISMATCH; \ break; \ } #define CHECK_DATATYPE_ARRAY(EXP_DT) \ if(!wvalue->value.hasValue || \ &UA_TYPES[UA_TYPES_##EXP_DT] != wvalue->value.value.type || \ UA_Variant_isScalar(&wvalue->value.value)) { \ retval = UA_STATUSCODE_BADTYPEMISMATCH; \ break; \ } #define CHECK_NODECLASS_WRITE(CLASS) \ if((node->nodeClass & (CLASS)) == 0) { \ retval = UA_STATUSCODE_BADNODECLASSINVALID; \ break; \ } #define CHECK_USERWRITEMASK(mask) \ if(!(userWriteMask & (mask))) { \ retval = UA_STATUSCODE_BADUSERACCESSDENIED; \ break; \ } #define GET_NODETYPE \ type = (const UA_VariableTypeNode*) \ getNodeType(server, node); \ if(!type) { \ retval = UA_STATUSCODE_BADTYPEMISMATCH; \ break; \ } /* This function implements the main part of the write service and operates on a copy of the node (not in single-threaded mode). */ static UA_StatusCode copyAttributeIntoNode(UA_Server *server, UA_Session *session, UA_Node *node, const UA_WriteValue *wvalue) { const void *value = wvalue->value.value.data; UA_UInt32 userWriteMask = getUserWriteMask(server, session, node); UA_StatusCode retval = UA_STATUSCODE_GOOD; const UA_VariableTypeNode *type; switch(wvalue->attributeId) { case UA_ATTRIBUTEID_NODEID: case UA_ATTRIBUTEID_NODECLASS: case UA_ATTRIBUTEID_USERWRITEMASK: case UA_ATTRIBUTEID_USERACCESSLEVEL: case UA_ATTRIBUTEID_USEREXECUTABLE: retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_BROWSENAME: CHECK_USERWRITEMASK(UA_WRITEMASK_BROWSENAME); CHECK_DATATYPE_SCALAR(QUALIFIEDNAME); UA_QualifiedName_deleteMembers(&node->browseName); UA_QualifiedName_copy((const UA_QualifiedName *)value, &node->browseName); break; case UA_ATTRIBUTEID_DISPLAYNAME: CHECK_USERWRITEMASK(UA_WRITEMASK_DISPLAYNAME); CHECK_DATATYPE_SCALAR(LOCALIZEDTEXT); UA_LocalizedText_deleteMembers(&node->displayName); UA_LocalizedText_copy((const UA_LocalizedText *)value, &node->displayName); break; case UA_ATTRIBUTEID_DESCRIPTION: CHECK_USERWRITEMASK(UA_WRITEMASK_DESCRIPTION); CHECK_DATATYPE_SCALAR(LOCALIZEDTEXT); UA_LocalizedText_deleteMembers(&node->description); UA_LocalizedText_copy((const UA_LocalizedText *)value, &node->description); break; case UA_ATTRIBUTEID_WRITEMASK: CHECK_USERWRITEMASK(UA_WRITEMASK_WRITEMASK); CHECK_DATATYPE_SCALAR(UINT32); node->writeMask = *(const UA_UInt32*)value; break; case UA_ATTRIBUTEID_ISABSTRACT: CHECK_USERWRITEMASK(UA_WRITEMASK_ISABSTRACT); CHECK_DATATYPE_SCALAR(BOOLEAN); retval = writeIsAbstractAttribute(node, *(const UA_Boolean*)value); break; case UA_ATTRIBUTEID_SYMMETRIC: CHECK_NODECLASS_WRITE(UA_NODECLASS_REFERENCETYPE); CHECK_USERWRITEMASK(UA_WRITEMASK_SYMMETRIC); CHECK_DATATYPE_SCALAR(BOOLEAN); ((UA_ReferenceTypeNode*)node)->symmetric = *(const UA_Boolean*)value; break; case UA_ATTRIBUTEID_INVERSENAME: CHECK_NODECLASS_WRITE(UA_NODECLASS_REFERENCETYPE); CHECK_USERWRITEMASK(UA_WRITEMASK_INVERSENAME); CHECK_DATATYPE_SCALAR(LOCALIZEDTEXT); UA_LocalizedText_deleteMembers(&((UA_ReferenceTypeNode*)node)->inverseName); UA_LocalizedText_copy((const UA_LocalizedText *)value, &((UA_ReferenceTypeNode*)node)->inverseName); break; case UA_ATTRIBUTEID_CONTAINSNOLOOPS: CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW); CHECK_USERWRITEMASK(UA_WRITEMASK_CONTAINSNOLOOPS); CHECK_DATATYPE_SCALAR(BOOLEAN); ((UA_ViewNode*)node)->containsNoLoops = *(const UA_Boolean*)value; break; case UA_ATTRIBUTEID_EVENTNOTIFIER: CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT); CHECK_USERWRITEMASK(UA_WRITEMASK_EVENTNOTIFIER); CHECK_DATATYPE_SCALAR(BYTE); if(node->nodeClass == UA_NODECLASS_VIEW) { ((UA_ViewNode*)node)->eventNotifier = *(const UA_Byte*)value; } else { ((UA_ObjectNode*)node)->eventNotifier = *(const UA_Byte*)value; } break; case UA_ATTRIBUTEID_VALUE: CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); if(node->nodeClass == UA_NODECLASS_VARIABLE) { /* The access to a value variable is granted via the AccessLevel * and UserAccessLevel attributes */ UA_Byte accessLevel = getAccessLevel(server, session, (const UA_VariableNode*)node); if(!(accessLevel & (UA_ACCESSLEVELMASK_WRITE))) { retval = UA_STATUSCODE_BADNOTWRITABLE; break; } accessLevel = getUserAccessLevel(server, session, (const UA_VariableNode*)node); if(!(accessLevel & (UA_ACCESSLEVELMASK_WRITE))) { retval = UA_STATUSCODE_BADUSERACCESSDENIED; break; } } else { /* UA_NODECLASS_VARIABLETYPE */ CHECK_USERWRITEMASK(UA_WRITEMASK_VALUEFORVARIABLETYPE); } retval = writeValueAttribute(server, session, (UA_VariableNode*)node, &wvalue->value, &wvalue->indexRange); break; case UA_ATTRIBUTEID_DATATYPE: CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); CHECK_USERWRITEMASK(UA_WRITEMASK_DATATYPE); CHECK_DATATYPE_SCALAR(NODEID); GET_NODETYPE retval = writeDataTypeAttribute(server, session, (UA_VariableNode*)node, type, (const UA_NodeId*)value); UA_Nodestore_releaseNode(server->nsCtx, (const UA_Node*)type); break; case UA_ATTRIBUTEID_VALUERANK: CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); CHECK_USERWRITEMASK(UA_WRITEMASK_VALUERANK); CHECK_DATATYPE_SCALAR(INT32); GET_NODETYPE retval = writeValueRankAttribute(server, session, (UA_VariableNode*)node, type, *(const UA_Int32*)value); UA_Nodestore_releaseNode(server->nsCtx, (const UA_Node*)type); break; case UA_ATTRIBUTEID_ARRAYDIMENSIONS: CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); CHECK_USERWRITEMASK(UA_WRITEMASK_ARRRAYDIMENSIONS); CHECK_DATATYPE_ARRAY(UINT32); GET_NODETYPE retval = writeArrayDimensionsAttribute(server, session, (UA_VariableNode*)node, type, wvalue->value.value.arrayLength, (UA_UInt32 *)wvalue->value.value.data); UA_Nodestore_releaseNode(server->nsCtx, (const UA_Node*)type); break; case UA_ATTRIBUTEID_ACCESSLEVEL: CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE); CHECK_USERWRITEMASK(UA_WRITEMASK_ACCESSLEVEL); CHECK_DATATYPE_SCALAR(BYTE); ((UA_VariableNode*)node)->accessLevel = *(const UA_Byte*)value; break; case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL: CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE); CHECK_USERWRITEMASK(UA_WRITEMASK_MINIMUMSAMPLINGINTERVAL); CHECK_DATATYPE_SCALAR(DOUBLE); ((UA_VariableNode*)node)->minimumSamplingInterval = *(const UA_Double*)value; break; case UA_ATTRIBUTEID_HISTORIZING: CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE); CHECK_USERWRITEMASK(UA_WRITEMASK_HISTORIZING); CHECK_DATATYPE_SCALAR(BOOLEAN); ((UA_VariableNode*)node)->historizing = *(const UA_Boolean*)value; break; case UA_ATTRIBUTEID_EXECUTABLE: CHECK_NODECLASS_WRITE(UA_NODECLASS_METHOD); CHECK_USERWRITEMASK(UA_WRITEMASK_EXECUTABLE); CHECK_DATATYPE_SCALAR(BOOLEAN); ((UA_MethodNode*)node)->executable = *(const UA_Boolean*)value; break; default: retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID; break; } if(retval != UA_STATUSCODE_GOOD) UA_LOG_INFO_SESSION(&server->config.logger, session, "WriteRequest returned status code %s", UA_StatusCode_name(retval)); return retval; } static void Operation_Write(UA_Server *server, UA_Session *session, void *context, UA_WriteValue *wv, UA_StatusCode *result) { *result = UA_Server_editNode(server, session, &wv->nodeId, (UA_EditNodeCallback)copyAttributeIntoNode, wv); } void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request, UA_WriteResponse *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing WriteRequest"); if(server->config.maxNodesPerWrite != 0 && request->nodesToWriteSize > server->config.maxNodesPerWrite) { response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS; return; } response->responseHeader.serviceResult = UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_Write, NULL, &request->nodesToWriteSize, &UA_TYPES[UA_TYPES_WRITEVALUE], &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]); } UA_StatusCode UA_Server_writeWithSession(UA_Server *server, UA_Session *session, const UA_WriteValue *value) { return UA_Server_editNode(server, session, &value->nodeId, (UA_EditNodeCallback)copyAttributeIntoNode, /* casting away const qualifier because callback uses const anyway */ (UA_WriteValue *)(uintptr_t)value); } UA_StatusCode UA_Server_write(UA_Server *server, const UA_WriteValue *value) { return UA_Server_editNode(server, &server->adminSession, &value->nodeId, (UA_EditNodeCallback)copyAttributeIntoNode, /* casting away const qualifier because callback uses const anyway */ (UA_WriteValue *)(uintptr_t)value); } /* Convenience function to be wrapped into inline functions */ UA_StatusCode __UA_Server_write(UA_Server *server, const UA_NodeId *nodeId, const UA_AttributeId attributeId, const UA_DataType *attr_type, const void *attr) { UA_WriteValue wvalue; UA_WriteValue_init(&wvalue); wvalue.nodeId = *nodeId; wvalue.attributeId = attributeId; wvalue.value.hasValue = true; if(attr_type != &UA_TYPES[UA_TYPES_VARIANT]) { /* hacked cast. the target WriteValue is used as const anyway */ UA_Variant_setScalar(&wvalue.value.value, (void*)(uintptr_t)attr, attr_type); } else { wvalue.value.value = *(const UA_Variant*)attr; } return UA_Server_write(server, &wvalue); } #ifdef UA_ENABLE_HISTORIZING void Service_HistoryRead(UA_Server *server, UA_Session *session, const UA_HistoryReadRequest *request, UA_HistoryReadResponse *response) { if(request->historyReadDetails.encoding != UA_EXTENSIONOBJECT_DECODED) { response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTSUPPORTED; return; } if(request->historyReadDetails.content.decoded.type != &UA_TYPES[UA_TYPES_READRAWMODIFIEDDETAILS]) { /* TODO handle more request->historyReadDetails.content.decoded.type types */ response->responseHeader.serviceResult = UA_STATUSCODE_BADHISTORYOPERATIONUNSUPPORTED; return; } /* History read with ReadRawModifiedDetails */ UA_ReadRawModifiedDetails * details = (UA_ReadRawModifiedDetails*) request->historyReadDetails.content.decoded.data; if(details->isReadModified) { // TODO add server->config.historyReadService.read_modified response->responseHeader.serviceResult = UA_STATUSCODE_BADHISTORYOPERATIONUNSUPPORTED; return; } /* Something to do? */ if(request->nodesToReadSize == 0) { response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO; return; } /* Check if there are too many operations */ if(server->config.maxNodesPerRead != 0 && request->nodesToReadSize > server->config.maxNodesPerRead) { response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS; return; } /* The history database is not configured */ if(!server->config.historyDatabase.readRaw) { response->responseHeader.serviceResult = UA_STATUSCODE_BADHISTORYOPERATIONUNSUPPORTED; return; } /* Allocate a temporary array to forward the result pointers to the * backend */ UA_HistoryData ** historyData = (UA_HistoryData **) UA_calloc(request->nodesToReadSize, sizeof(UA_HistoryData*)); if(!historyData) { response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY; return; } /* Allocate the results array */ response->results = (UA_HistoryReadResult*)UA_Array_new(request->nodesToReadSize, &UA_TYPES[UA_TYPES_HISTORYREADRESULT]); if(!response->results) { UA_free(historyData); response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY; return; } response->resultsSize = request->nodesToReadSize; for(size_t i = 0; i < response->resultsSize; ++i) { UA_HistoryData * data = UA_HistoryData_new(); response->results[i].historyData.encoding = UA_EXTENSIONOBJECT_DECODED; response->results[i].historyData.content.decoded.type = &UA_TYPES[UA_TYPES_HISTORYDATA]; response->results[i].historyData.content.decoded.data = data; historyData[i] = data; } server->config.historyDatabase.readRaw(server, server->config.historyDatabase.context, &session->sessionId, session->sessionHandle, &request->requestHeader, details, request->timestampsToReturn, request->releaseContinuationPoints, request->nodesToReadSize, request->nodesToRead, response, historyData); UA_free(historyData); } void Service_HistoryUpdate(UA_Server *server, UA_Session *session, const UA_HistoryUpdateRequest *request, UA_HistoryUpdateResponse *response) { response->resultsSize = request->historyUpdateDetailsSize; response->results = (UA_HistoryUpdateResult*)UA_Array_new(response->resultsSize, &UA_TYPES[UA_TYPES_HISTORYUPDATERESULT]); if (!response->results) { response->resultsSize = 0; response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY; return; } for (size_t i = 0; i < request->historyUpdateDetailsSize; ++i) { UA_HistoryUpdateResult_init(&response->results[i]); if(request->historyUpdateDetails[i].encoding != UA_EXTENSIONOBJECT_DECODED) { response->results[i].statusCode = UA_STATUSCODE_BADNOTSUPPORTED; continue; } if (request->historyUpdateDetails[i].content.decoded.type == &UA_TYPES[UA_TYPES_UPDATEDATADETAILS]) { if (server->config.historyDatabase.updateData) { server->config.historyDatabase.updateData(server, server->config.historyDatabase.context, &session->sessionId, session->sessionHandle, &request->requestHeader, (UA_UpdateDataDetails*)request->historyUpdateDetails[i].content.decoded.data, &response->results[i]); } else { response->results[i].statusCode = UA_STATUSCODE_BADNOTSUPPORTED; } continue; } else if (request->historyUpdateDetails[i].content.decoded.type == &UA_TYPES[UA_TYPES_DELETERAWMODIFIEDDETAILS]) { if (server->config.historyDatabase.deleteRawModified) { server->config.historyDatabase.deleteRawModified(server, server->config.historyDatabase.context, &session->sessionId, session->sessionHandle, &request->requestHeader, (UA_DeleteRawModifiedDetails*)request->historyUpdateDetails[i].content.decoded.data, &response->results[i]); } else { response->results[i].statusCode = UA_STATUSCODE_BADNOTSUPPORTED; } continue; } else { response->results[i].statusCode = UA_STATUSCODE_BADNOTSUPPORTED; continue; } } response->responseHeader.serviceResult = UA_STATUSCODE_GOOD; } #endif UA_StatusCode UA_EXPORT UA_Server_writeObjectProperty(UA_Server *server, const UA_NodeId objectId, const UA_QualifiedName propertyName, const UA_Variant value) { UA_RelativePathElement rpe; UA_RelativePathElement_init(&rpe); rpe.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY); rpe.isInverse = false; rpe.includeSubtypes = false; rpe.targetName = propertyName; UA_BrowsePath bp; UA_BrowsePath_init(&bp); bp.startingNode = objectId; bp.relativePath.elementsSize = 1; bp.relativePath.elements = &rpe; UA_StatusCode retval; UA_BrowsePathResult bpr = UA_Server_translateBrowsePathToNodeIds(server, &bp); if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) { retval = bpr.statusCode; UA_BrowsePathResult_deleteMembers(&bpr); return retval; } retval = UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value); UA_BrowsePathResult_deleteMembers(&bpr); return retval; } UA_StatusCode UA_EXPORT UA_Server_writeObjectProperty_scalar(UA_Server *server, const UA_NodeId objectId, const UA_QualifiedName propertyName, const void *value, const UA_DataType *type) { UA_Variant var; UA_Variant_init(&var); UA_Variant_setScalar(&var, (void*)(uintptr_t)value, type); return UA_Server_writeObjectProperty(server, objectId, propertyName, var); } /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_services_discovery.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2014-2016 (c) Sten Grüner * Copyright 2014, 2017 (c) Florian Palm * Copyright 2016 (c) Oleksiy Vasylyev * Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2017 (c) frax2222 * Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB */ #ifdef UA_ENABLE_DISCOVERY static UA_StatusCode setApplicationDescriptionFromRegisteredServer(const UA_FindServersRequest *request, UA_ApplicationDescription *target, const UA_RegisteredServer *registeredServer) { UA_ApplicationDescription_init(target); UA_StatusCode retval = UA_String_copy(®isteredServer->serverUri, &target->applicationUri); if(retval != UA_STATUSCODE_GOOD) return retval; retval = UA_String_copy(®isteredServer->productUri, &target->productUri); if(retval != UA_STATUSCODE_GOOD) return retval; // if the client requests a specific locale, select the corresponding server name if(request->localeIdsSize) { UA_Boolean appNameFound = false; for(size_t i =0; ilocaleIdsSize && !appNameFound; i++) { for(size_t j =0; jserverNamesSize; j++) { if(UA_String_equal(&request->localeIds[i], ®isteredServer->serverNames[j].locale)) { retval = UA_LocalizedText_copy(®isteredServer->serverNames[j], &target->applicationName); if(retval != UA_STATUSCODE_GOOD) return retval; appNameFound = true; break; } } } // server does not have the requested local, therefore we can select the // most suitable one if(!appNameFound && registeredServer->serverNamesSize) { retval = UA_LocalizedText_copy(®isteredServer->serverNames[0], &target->applicationName); if(retval != UA_STATUSCODE_GOOD) return retval; } } else if(registeredServer->serverNamesSize) { // just take the first name retval = UA_LocalizedText_copy(®isteredServer->serverNames[0], &target->applicationName); if(retval != UA_STATUSCODE_GOOD) return retval; } target->applicationType = registeredServer->serverType; retval = UA_String_copy(®isteredServer->gatewayServerUri, &target->gatewayServerUri); if(retval != UA_STATUSCODE_GOOD) return retval; // TODO where do we get the discoveryProfileUri for application data? target->discoveryUrlsSize = registeredServer->discoveryUrlsSize; if(registeredServer->discoveryUrlsSize) { size_t duSize = sizeof(UA_String) * registeredServer->discoveryUrlsSize; target->discoveryUrls = (UA_String *)UA_malloc(duSize); if(!target->discoveryUrls) return UA_STATUSCODE_BADOUTOFMEMORY; for(size_t i = 0; i < registeredServer->discoveryUrlsSize; i++) { retval = UA_String_copy(®isteredServer->discoveryUrls[i], &target->discoveryUrls[i]); if(retval != UA_STATUSCODE_GOOD) return retval; } } return retval; } #endif static UA_StatusCode setApplicationDescriptionFromServer(UA_ApplicationDescription *target, const UA_Server *server) { /* Copy ApplicationDescription from the config */ UA_StatusCode result = UA_ApplicationDescription_copy(&server->config.applicationDescription, target); if(result != UA_STATUSCODE_GOOD) return result; /* Add the discoveryUrls from the networklayers only if discoveryUrl * not already present and to avoid redundancy */ if(!target->discoveryUrlsSize) { size_t discSize = sizeof(UA_String) * (target->discoveryUrlsSize + server->config.networkLayersSize); UA_String* disc = (UA_String *)UA_realloc(target->discoveryUrls, discSize); if(!disc) return UA_STATUSCODE_BADOUTOFMEMORY; size_t existing = target->discoveryUrlsSize; target->discoveryUrls = disc; target->discoveryUrlsSize += server->config.networkLayersSize; for(size_t i = 0; i < server->config.networkLayersSize; i++) { UA_ServerNetworkLayer* nl = &server->config.networkLayers[i]; UA_String_copy(&nl->discoveryUrl, &target->discoveryUrls[existing + i]); } } return UA_STATUSCODE_GOOD; } void Service_FindServers(UA_Server *server, UA_Session *session, const UA_FindServersRequest *request, UA_FindServersResponse *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing FindServersRequest"); /* Return the server itself? */ UA_Boolean foundSelf = false; if(request->serverUrisSize) { for(size_t i = 0; i < request->serverUrisSize; i++) { if(UA_String_equal(&request->serverUris[i], &server->config.applicationDescription.applicationUri)) { foundSelf = true; break; } } } else { foundSelf = true; } #ifndef UA_ENABLE_DISCOVERY if(!foundSelf) return; UA_ApplicationDescription *ad = UA_ApplicationDescription_new(); if(!ad) { response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY; return; } UA_StatusCode retval = setApplicationDescriptionFromServer(ad, server); if(retval != UA_STATUSCODE_GOOD) { UA_ApplicationDescription_delete(ad); response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY; return; } response->servers = ad; response->serversSize = 1; return; #else /* Temporarily store all the pointers which we found to avoid reiterating * through the list */ size_t foundServersSize = 0; UA_STACKARRAY(UA_RegisteredServer*, foundServers, server->discoveryManager.registeredServersSize+1); registeredServer_list_entry* current; LIST_FOREACH(current, &server->discoveryManager.registeredServers, pointers) { if(request->serverUrisSize) { /* If client only requested a specific set of servers */ for(size_t i = 0; i < request->serverUrisSize; i++) { if(UA_String_equal(¤t->registeredServer.serverUri, &request->serverUris[i])) { foundServers[foundServersSize] = ¤t->registeredServer; foundServersSize++; break; } } } else { /* Return all registered servers */ foundServers[foundServersSize] = ¤t->registeredServer; foundServersSize++; } } size_t allocSize = foundServersSize; if(foundSelf) allocSize++; /* Nothing to do? */ if(allocSize == 0) return; /* Allocate memory */ response->servers = (UA_ApplicationDescription*)UA_Array_new(allocSize, &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]); if(!response->servers) { response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY; return; } response->serversSize = allocSize; /* Copy into the response. TODO: Evaluate return codes */ size_t pos = 0; if(foundSelf) { setApplicationDescriptionFromServer(&response->servers[pos++], server); } for(size_t i = 0; i < foundServersSize; i++) { setApplicationDescriptionFromRegisteredServer(request, &response->servers[pos++], foundServers[i]); } #endif } void Service_GetEndpoints(UA_Server *server, UA_Session *session, const UA_GetEndpointsRequest *request, UA_GetEndpointsResponse *response) { /* If the client expects to see a specific endpointurl, mirror it back. If not, clone the endpoints with the discovery url of all networklayers. */ const UA_String *endpointUrl = &request->endpointUrl; if(endpointUrl->length > 0) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing GetEndpointsRequest with endpointUrl " UA_PRINTF_STRING_FORMAT, UA_PRINTF_STRING_DATA(*endpointUrl)); } else { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing GetEndpointsRequest with an empty endpointUrl"); } /* test if the supported binary profile shall be returned */ size_t reSize = sizeof(UA_Boolean) * server->config.endpointsSize; UA_STACKARRAY(UA_Boolean, relevant_endpoints, reSize); memset(relevant_endpoints, 0, reSize); size_t relevant_count = 0; if(request->profileUrisSize == 0) { for(size_t j = 0; j < server->config.endpointsSize; ++j) relevant_endpoints[j] = true; relevant_count = server->config.endpointsSize; } else { for(size_t j = 0; j < server->config.endpointsSize; ++j) { for(size_t i = 0; i < request->profileUrisSize; ++i) { if(!UA_String_equal(&request->profileUris[i], &server->config.endpoints[j].transportProfileUri)) continue; relevant_endpoints[j] = true; ++relevant_count; break; } } } if(relevant_count == 0) { response->endpointsSize = 0; return; } /* Clone the endpoint for each networklayer? */ size_t clone_times = 1; UA_Boolean nl_endpointurl = false; if(endpointUrl->length == 0) { clone_times = server->config.networkLayersSize; nl_endpointurl = true; } response->endpoints = (UA_EndpointDescription*)UA_Array_new(relevant_count * clone_times, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]); if(!response->endpoints) { response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY; return; } response->endpointsSize = relevant_count * clone_times; size_t k = 0; UA_StatusCode retval; for(size_t i = 0; i < clone_times; ++i) { if(nl_endpointurl) endpointUrl = &server->config.networkLayers[i].discoveryUrl; for(size_t j = 0; j < server->config.endpointsSize; ++j) { if(!relevant_endpoints[j]) continue; retval = UA_EndpointDescription_copy(&server->config.endpoints[j], &response->endpoints[k]); if(retval != UA_STATUSCODE_GOOD) goto error; retval = UA_String_copy(endpointUrl, &response->endpoints[k].endpointUrl); if(retval != UA_STATUSCODE_GOOD) goto error; ++k; } } return; error: response->responseHeader.serviceResult = retval; UA_Array_delete(response->endpoints, response->endpointsSize, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]); response->endpoints = NULL; response->endpointsSize = 0; } #ifdef UA_ENABLE_DISCOVERY static void process_RegisterServer(UA_Server *server, UA_Session *session, const UA_RequestHeader* requestHeader, const UA_RegisteredServer *requestServer, const size_t requestDiscoveryConfigurationSize, const UA_ExtensionObject *requestDiscoveryConfiguration, UA_ResponseHeader* responseHeader, size_t *responseConfigurationResultsSize, UA_StatusCode **responseConfigurationResults, size_t *responseDiagnosticInfosSize, UA_DiagnosticInfo *responseDiagnosticInfos) { /* Find the server from the request in the registered list */ registeredServer_list_entry* current; registeredServer_list_entry *registeredServer_entry = NULL; LIST_FOREACH(current, &server->discoveryManager.registeredServers, pointers) { if(UA_String_equal(¤t->registeredServer.serverUri, &requestServer->serverUri)) { registeredServer_entry = current; break; } } UA_MdnsDiscoveryConfiguration *mdnsConfig = NULL; const UA_String* mdnsServerName = NULL; if(requestDiscoveryConfigurationSize) { *responseConfigurationResults = (UA_StatusCode *)UA_Array_new(requestDiscoveryConfigurationSize, &UA_TYPES[UA_TYPES_STATUSCODE]); if(!(*responseConfigurationResults)) { responseHeader->serviceResult = UA_STATUSCODE_BADOUTOFMEMORY; return; } *responseConfigurationResultsSize = requestDiscoveryConfigurationSize; for(size_t i = 0; i < requestDiscoveryConfigurationSize; i++) { const UA_ExtensionObject *object = &requestDiscoveryConfiguration[i]; if(!mdnsConfig && (object->encoding == UA_EXTENSIONOBJECT_DECODED || object->encoding == UA_EXTENSIONOBJECT_DECODED_NODELETE) && (object->content.decoded.type == &UA_TYPES[UA_TYPES_MDNSDISCOVERYCONFIGURATION])) { mdnsConfig = (UA_MdnsDiscoveryConfiguration *)object->content.decoded.data; mdnsServerName = &mdnsConfig->mdnsServerName; (*responseConfigurationResults)[i] = UA_STATUSCODE_GOOD; } else { (*responseConfigurationResults)[i] = UA_STATUSCODE_BADNOTSUPPORTED; } } } if(!mdnsServerName && requestServer->serverNamesSize) mdnsServerName = &requestServer->serverNames[0].text; if(!mdnsServerName) { responseHeader->serviceResult = UA_STATUSCODE_BADSERVERNAMEMISSING; return; } if(requestServer->discoveryUrlsSize == 0) { responseHeader->serviceResult = UA_STATUSCODE_BADDISCOVERYURLMISSING; return; } if(requestServer->semaphoreFilePath.length) { #ifdef UA_ENABLE_DISCOVERY_SEMAPHORE char* filePath = (char*) UA_malloc(sizeof(char)*requestServer->semaphoreFilePath.length+1); if(!filePath) { UA_LOG_ERROR_SESSION(&server->config.logger, session, "Cannot allocate memory for semaphore path. Out of memory."); responseHeader->serviceResult = UA_STATUSCODE_BADOUTOFMEMORY; return; } memcpy(filePath, requestServer->semaphoreFilePath.data, requestServer->semaphoreFilePath.length ); filePath[requestServer->semaphoreFilePath.length] = '\0'; if(!UA_fileExists( filePath )) { responseHeader->serviceResult = UA_STATUSCODE_BADSEMPAHOREFILEMISSING; UA_free(filePath); return; } UA_free(filePath); #else UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_CLIENT, "Ignoring semaphore file path. open62541 not compiled " "with UA_ENABLE_DISCOVERY_SEMAPHORE=ON"); #endif } #ifdef UA_ENABLE_DISCOVERY_MULTICAST if(server->config.discovery.mdnsEnable) { for(size_t i = 0; i < requestServer->discoveryUrlsSize; i++) { /* create TXT if is online and first index, delete TXT if is offline and last index */ UA_Boolean updateTxt = (requestServer->isOnline && i==0) || (!requestServer->isOnline && i==requestServer->discoveryUrlsSize); UA_Server_updateMdnsForDiscoveryUrl(server, mdnsServerName, mdnsConfig, &requestServer->discoveryUrls[i], requestServer->isOnline, updateTxt); } } #endif if(!requestServer->isOnline) { // server is shutting down. Remove it from the registered servers list if(!registeredServer_entry) { // server not found, show warning UA_LOG_WARNING_SESSION(&server->config.logger, session, "Could not unregister server %.*s. Not registered.", (int)requestServer->serverUri.length, requestServer->serverUri.data); responseHeader->serviceResult = UA_STATUSCODE_BADNOTHINGTODO; return; } if(server->discoveryManager.registerServerCallback) server->discoveryManager. registerServerCallback(requestServer, server->discoveryManager.registerServerCallbackData); // server found, remove from list LIST_REMOVE(registeredServer_entry, pointers); UA_RegisteredServer_deleteMembers(®isteredServer_entry->registeredServer); #ifndef UA_ENABLE_MULTITHREADING UA_free(registeredServer_entry); server->discoveryManager.registeredServersSize--; #else UA_atomic_subSize(&server->discoveryManager.registeredServersSize, 1); registeredServer_entry->delayedCleanup.callback = NULL; /* only free the structure */ UA_WorkQueue_enqueueDelayed(&server->workQueue, ®isteredServer_entry->delayedCleanup); #endif responseHeader->serviceResult = UA_STATUSCODE_GOOD; return; } UA_StatusCode retval = UA_STATUSCODE_GOOD; if(!registeredServer_entry) { // server not yet registered, register it by adding it to the list UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Registering new server: %.*s", (int)requestServer->serverUri.length, requestServer->serverUri.data); registeredServer_entry = (registeredServer_list_entry *)UA_malloc(sizeof(registeredServer_list_entry)); if(!registeredServer_entry) { responseHeader->serviceResult = UA_STATUSCODE_BADOUTOFMEMORY; return; } LIST_INSERT_HEAD(&server->discoveryManager.registeredServers, registeredServer_entry, pointers); #ifndef UA_ENABLE_MULTITHREADING server->discoveryManager.registeredServersSize++; #else UA_atomic_addSize(&server->discoveryManager.registeredServersSize, 1); #endif } else { UA_RegisteredServer_deleteMembers(®isteredServer_entry->registeredServer); } // Always call the callback, if it is set. // Previously we only called it if it was a new register call. It may be the case that this endpoint // registered before, then crashed, restarts and registeres again. In that case the entry is not deleted // and the callback would not be called. if(server->discoveryManager.registerServerCallback) server->discoveryManager. registerServerCallback(requestServer, server->discoveryManager.registerServerCallbackData); // copy the data from the request into the list UA_RegisteredServer_copy(requestServer, ®isteredServer_entry->registeredServer); registeredServer_entry->lastSeen = UA_DateTime_nowMonotonic(); responseHeader->serviceResult = retval; } void Service_RegisterServer(UA_Server *server, UA_Session *session, const UA_RegisterServerRequest *request, UA_RegisterServerResponse *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing RegisterServerRequest"); process_RegisterServer(server, session, &request->requestHeader, &request->server, 0, NULL, &response->responseHeader, 0, NULL, 0, NULL); } void Service_RegisterServer2(UA_Server *server, UA_Session *session, const UA_RegisterServer2Request *request, UA_RegisterServer2Response *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing RegisterServer2Request"); process_RegisterServer(server, session, &request->requestHeader, &request->server, request->discoveryConfigurationSize, request->discoveryConfiguration, &response->responseHeader, &response->configurationResultsSize, &response->configurationResults, &response->diagnosticInfosSize, response->diagnosticInfos); } /* Cleanup server registration: If the semaphore file path is set, then it just * checks the existence of the file. When it is deleted, the registration is * removed. If there is no semaphore file, then the registration will be removed * if it is older than 60 minutes. */ void UA_Discovery_cleanupTimedOut(UA_Server *server, UA_DateTime nowMonotonic) { UA_DateTime timedOut = nowMonotonic; // registration is timed out if lastSeen is older than 60 minutes (default // value, can be modified by user). if(server->config.discovery.cleanupTimeout) timedOut -= server->config.discovery.cleanupTimeout*UA_DATETIME_SEC; registeredServer_list_entry* current, *temp; LIST_FOREACH_SAFE(current, &server->discoveryManager.registeredServers, pointers, temp) { UA_Boolean semaphoreDeleted = false; #ifdef UA_ENABLE_DISCOVERY_SEMAPHORE if(current->registeredServer.semaphoreFilePath.length) { size_t fpSize = sizeof(char)*current->registeredServer.semaphoreFilePath.length+1; // todo: malloc may fail: return a statuscode char* filePath = (char *)UA_malloc(fpSize); if(filePath) { memcpy(filePath, current->registeredServer.semaphoreFilePath.data, current->registeredServer.semaphoreFilePath.length ); filePath[current->registeredServer.semaphoreFilePath.length] = '\0'; semaphoreDeleted = UA_fileExists(filePath) == false; UA_free(filePath); } else { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Cannot check registration semaphore. Out of memory"); } } #endif if(semaphoreDeleted || (server->config.discovery.cleanupTimeout && current->lastSeen < timedOut)) { if(semaphoreDeleted) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "Registration of server with URI %.*s is removed because " "the semaphore file '%.*s' was deleted.", (int)current->registeredServer.serverUri.length, current->registeredServer.serverUri.data, (int)current->registeredServer.semaphoreFilePath.length, current->registeredServer.semaphoreFilePath.data); } else { // cppcheck-suppress unreadVariable UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "Registration of server with URI %.*s has timed out and is removed.", (int)current->registeredServer.serverUri.length, current->registeredServer.serverUri.data); } LIST_REMOVE(current, pointers); UA_RegisteredServer_deleteMembers(¤t->registeredServer); #ifndef UA_ENABLE_MULTITHREADING UA_free(current); server->discoveryManager.registeredServersSize--; #else UA_atomic_subSize(&server->discoveryManager.registeredServersSize, 1); current->delayedCleanup.callback = NULL; /* Only free the structure */ UA_WorkQueue_enqueueDelayed(&server->workQueue, ¤t->delayedCleanup); #endif } } } /* Called by the UA_Server callback. The OPC UA specification says: * * > If an error occurs during registration (e.g. the Discovery Server is not running) then the Server * > must periodically re-attempt registration. The frequency of these attempts should start at 1 second * > but gradually increase until the registration frequency is the same as what it would be if not * > errors occurred. The recommended approach would double the period each attempt until reaching the maximum. * * We will do so by using the additional data parameter which holds information * if the next interval is default or if it is a repeated call. */ static void periodicServerRegister(UA_Server *server, void *data) { UA_assert(data != NULL); struct PeriodicServerRegisterCallback *cb = (struct PeriodicServerRegisterCallback *)data; UA_StatusCode retval = UA_Client_connect_noSession(cb->client, cb->discovery_server_url); if (retval == UA_STATUSCODE_GOOD) { /* Register You can also use a semaphore file. That file must exist. When the file is deleted, the server is automatically unregistered. The semaphore file has to be accessible by the discovery server UA_StatusCode retval = UA_Server_register_discovery(server, "opc.tcp://localhost:4840", "/path/to/some/file"); */ retval = UA_Server_register_discovery(server, cb->client, NULL); } if (cb->client->state == UA_CLIENTSTATE_CONNECTED) { UA_StatusCode retval1 = UA_Client_disconnect(cb->client); if(retval1 != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Could not disconnect client from register server. StatusCode %s", UA_StatusCode_name(retval)); } } /* Registering failed */ if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Could not register server with discovery server. " "Is the discovery server started? StatusCode %s", UA_StatusCode_name(retval)); /* If the server was previously registered, retry in one second, * else, double the previous interval */ UA_Double nextInterval = 1000.0; if(!cb->registered) nextInterval = cb->this_interval * 2; /* The interval should be smaller than the default interval */ if(nextInterval > cb->default_interval) nextInterval = cb->default_interval; cb->this_interval = nextInterval; UA_Server_changeRepeatedCallbackInterval(server, cb->id, nextInterval); return; } /* Registering succeeded */ UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "Server successfully registered. Next periodical register will be in %d seconds", (int)(cb->default_interval/1000)); if(!cb->registered) { retval = UA_Server_changeRepeatedCallbackInterval(server, cb->id, cb->default_interval); /* If changing the interval fails, try again after the next registering */ if(retval == UA_STATUSCODE_GOOD) cb->registered = true; } } UA_StatusCode UA_Server_addPeriodicServerRegisterCallback(UA_Server *server, struct UA_Client *client, const char* discoveryServerUrl, UA_Double intervalMs, UA_Double delayFirstRegisterMs, UA_UInt64 *periodicCallbackId) { /* No valid server URL */ if(!discoveryServerUrl) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "No discovery server URL provided"); return UA_STATUSCODE_BADINTERNALERROR; } if (client->connection.state != UA_CONNECTION_CLOSED) return UA_STATUSCODE_BADINVALIDSTATE; /* check if we are already registering with the given discovery url and remove the old periodic call */ { periodicServerRegisterCallback_entry *rs, *rs_tmp; LIST_FOREACH_SAFE(rs, &server->discoveryManager. periodicServerRegisterCallbacks, pointers, rs_tmp) { if(strcmp(rs->callback->discovery_server_url, discoveryServerUrl) == 0) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "There is already a register callback for '%s' in place. Removing the older one.", discoveryServerUrl); UA_Server_removeRepeatedCallback(server, rs->callback->id); LIST_REMOVE(rs, pointers); UA_free(rs->callback->discovery_server_url); UA_free(rs->callback); UA_free(rs); break; } } } /* Allocate and initialize */ struct PeriodicServerRegisterCallback* cb = (struct PeriodicServerRegisterCallback*) UA_malloc(sizeof(struct PeriodicServerRegisterCallback)); if(!cb) return UA_STATUSCODE_BADOUTOFMEMORY; /* Start repeating a failed register after 1s, then increase the delay. Set * to 500ms, as the delay is doubled before changing the callback * interval.*/ cb->this_interval = 500.0; cb->default_interval = intervalMs; cb->registered = false; cb->client = client; size_t len = strlen(discoveryServerUrl); cb->discovery_server_url = (char*)UA_malloc(len+1); if (!cb->discovery_server_url) { UA_free(cb); return UA_STATUSCODE_BADOUTOFMEMORY; } memcpy(cb->discovery_server_url, discoveryServerUrl, len+1); /* Add the callback */ UA_StatusCode retval = UA_Server_addRepeatedCallback(server, periodicServerRegister, cb, delayFirstRegisterMs, &cb->id); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Could not create periodic job for server register. " "StatusCode %s", UA_StatusCode_name(retval)); UA_free(cb); return retval; } #ifndef __clang_analyzer__ // the analyzer reports on LIST_INSERT_HEAD a use after free false positive periodicServerRegisterCallback_entry *newEntry = (periodicServerRegisterCallback_entry *)UA_malloc(sizeof(periodicServerRegisterCallback_entry)); if(!newEntry) { UA_Server_removeRepeatedCallback(server, cb->id); UA_free(cb); return UA_STATUSCODE_BADOUTOFMEMORY; } newEntry->callback = cb; LIST_INSERT_HEAD(&server->discoveryManager.periodicServerRegisterCallbacks, newEntry, pointers); #endif if(periodicCallbackId) *periodicCallbackId = cb->id; return UA_STATUSCODE_GOOD; } void UA_Server_setRegisterServerCallback(UA_Server *server, UA_Server_registerServerCallback cb, void* data) { server->discoveryManager.registerServerCallback = cb; server->discoveryManager.registerServerCallbackData = data; } #endif /* UA_ENABLE_DISCOVERY */ /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_services_subscription.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2016-2017 (c) Florian Palm * Copyright 2015 (c) Chris Iatrou * Copyright 2015-2016 (c) Sten Grüner * Copyright 2015-2016 (c) Oleksiy Vasylyev * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2018 (c) Ari Breitkreuz, fortiss GmbH * Copyright 2017 (c) Mattias Bornhager * Copyright 2017 (c) Henrik Norrman * Copyright 2017-2018 (c) Thomas Stalder, Blue Time Concept SA * Copyright 2018 (c) Fabian Arndt, Root-Core */ #ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */ static UA_StatusCode setSubscriptionSettings(UA_Server *server, UA_Subscription *subscription, UA_Double requestedPublishingInterval, UA_UInt32 requestedLifetimeCount, UA_UInt32 requestedMaxKeepAliveCount, UA_UInt32 maxNotificationsPerPublish, UA_Byte priority) { /* deregister the callback if required */ Subscription_unregisterPublishCallback(server, subscription); /* re-parameterize the subscription */ UA_BOUNDEDVALUE_SETWBOUNDS(server->config.publishingIntervalLimits, requestedPublishingInterval, subscription->publishingInterval); /* check for nan*/ if(requestedPublishingInterval != requestedPublishingInterval) subscription->publishingInterval = server->config.publishingIntervalLimits.min; UA_BOUNDEDVALUE_SETWBOUNDS(server->config.keepAliveCountLimits, requestedMaxKeepAliveCount, subscription->maxKeepAliveCount); UA_BOUNDEDVALUE_SETWBOUNDS(server->config.lifeTimeCountLimits, requestedLifetimeCount, subscription->lifeTimeCount); if(subscription->lifeTimeCount < 3 * subscription->maxKeepAliveCount) subscription->lifeTimeCount = 3 * subscription->maxKeepAliveCount; subscription->notificationsPerPublish = maxNotificationsPerPublish; if(maxNotificationsPerPublish == 0 || maxNotificationsPerPublish > server->config.maxNotificationsPerPublish) subscription->notificationsPerPublish = server->config.maxNotificationsPerPublish; subscription->priority = priority; UA_StatusCode retval = Subscription_registerPublishCallback(server, subscription); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_DEBUG_SESSION(&server->config.logger, subscription->session, "Subscription %u | Could not register publish callback with error code %s", subscription->subscriptionId, UA_StatusCode_name(retval)); } return retval; } void Service_CreateSubscription(UA_Server *server, UA_Session *session, const UA_CreateSubscriptionRequest *request, UA_CreateSubscriptionResponse *response) { /* Check limits for the number of subscriptions */ if(((server->config.maxSubscriptions != 0) && (server->numSubscriptions >= server->config.maxSubscriptions)) || ((server->config.maxSubscriptionsPerSession != 0) && (session->numSubscriptions >= server->config.maxSubscriptionsPerSession))) { response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYSUBSCRIPTIONS; return; } /* Create the subscription */ UA_Subscription *newSubscription = UA_Subscription_new(session, response->subscriptionId); if(!newSubscription) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing CreateSubscriptionRequest failed"); response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY; return; } UA_Session_addSubscription(server, session, newSubscription); /* Also assigns the subscription id */ /* Set the subscription parameters */ newSubscription->publishingEnabled = request->publishingEnabled; UA_StatusCode retval = setSubscriptionSettings(server, newSubscription, request->requestedPublishingInterval, request->requestedLifetimeCount, request->requestedMaxKeepAliveCount, request->maxNotificationsPerPublish, request->priority); if(retval != UA_STATUSCODE_GOOD) { response->responseHeader.serviceResult = retval; return; } newSubscription->currentKeepAliveCount = newSubscription->maxKeepAliveCount; /* set settings first */ /* Prepare the response */ response->subscriptionId = newSubscription->subscriptionId; response->revisedPublishingInterval = newSubscription->publishingInterval; response->revisedLifetimeCount = newSubscription->lifeTimeCount; response->revisedMaxKeepAliveCount = newSubscription->maxKeepAliveCount; UA_LOG_INFO_SESSION(&server->config.logger, session, "Subscription %u | " "Created the Subscription with a publishing interval of %.2f ms", response->subscriptionId, newSubscription->publishingInterval); } void Service_ModifySubscription(UA_Server *server, UA_Session *session, const UA_ModifySubscriptionRequest *request, UA_ModifySubscriptionResponse *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing ModifySubscriptionRequest"); UA_Subscription *sub = UA_Session_getSubscriptionById(session, request->subscriptionId); if(!sub) { response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; return; } UA_StatusCode retval = setSubscriptionSettings(server, sub, request->requestedPublishingInterval, request->requestedLifetimeCount, request->requestedMaxKeepAliveCount, request->maxNotificationsPerPublish, request->priority); if(retval != UA_STATUSCODE_GOOD) { response->responseHeader.serviceResult = retval; return; } sub->currentLifetimeCount = 0; /* Reset the subscription lifetime */ response->revisedPublishingInterval = sub->publishingInterval; response->revisedLifetimeCount = sub->lifeTimeCount; response->revisedMaxKeepAliveCount = sub->maxKeepAliveCount; } static void Operation_SetPublishingMode(UA_Server *Server, UA_Session *session, const UA_Boolean *publishingEnabled, const UA_UInt32 *subscriptionId, UA_StatusCode *result) { UA_Subscription *sub = UA_Session_getSubscriptionById(session, *subscriptionId); if(!sub) { *result = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; return; } sub->currentLifetimeCount = 0; /* Reset the subscription lifetime */ sub->publishingEnabled = *publishingEnabled; /* Set the publishing mode */ } void Service_SetPublishingMode(UA_Server *server, UA_Session *session, const UA_SetPublishingModeRequest *request, UA_SetPublishingModeResponse *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing SetPublishingModeRequest"); UA_Boolean publishingEnabled = request->publishingEnabled; /* request is const */ response->responseHeader.serviceResult = UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_SetPublishingMode, &publishingEnabled, &request->subscriptionIdsSize, &UA_TYPES[UA_TYPES_UINT32], &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]); } /* TODO: Unify with senderror in ua_server_binary.c */ static void subscriptionSendError(UA_SecureChannel *channel, UA_UInt32 requestHandle, UA_UInt32 requestId, UA_StatusCode error) { UA_PublishResponse err_response; UA_PublishResponse_init(&err_response); err_response.responseHeader.requestHandle = requestHandle; err_response.responseHeader.timestamp = UA_DateTime_now(); err_response.responseHeader.serviceResult = error; UA_SecureChannel_sendSymmetricMessage(channel, requestId, UA_MESSAGETYPE_MSG, &err_response, &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]); } void Service_Publish(UA_Server *server, UA_Session *session, const UA_PublishRequest *request, UA_UInt32 requestId) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing PublishRequest"); /* Return an error if the session has no subscription */ if(LIST_EMPTY(&session->serverSubscriptions)) { subscriptionSendError(session->header.channel, request->requestHeader.requestHandle, requestId, UA_STATUSCODE_BADNOSUBSCRIPTION); return; } /* Handle too many subscriptions to free resources before trying to allocate * resources for the new publish request. If the limit has been reached the * oldest publish request shall be responded */ if((server->config.maxPublishReqPerSession != 0) && (session->numPublishReq >= server->config.maxPublishReqPerSession)) { if(!UA_Subscription_reachedPublishReqLimit(server, session)) { subscriptionSendError(session->header.channel, requestId, request->requestHeader.requestHandle, UA_STATUSCODE_BADINTERNALERROR); return; } } /* Allocate the response to store it in the retransmission queue */ UA_PublishResponseEntry *entry = (UA_PublishResponseEntry *) UA_malloc(sizeof(UA_PublishResponseEntry)); if(!entry) { subscriptionSendError(session->header.channel, requestId, request->requestHeader.requestHandle, UA_STATUSCODE_BADOUTOFMEMORY); return; } /* Prepare the response */ entry->requestId = requestId; UA_PublishResponse *response = &entry->response; UA_PublishResponse_init(response); response->responseHeader.requestHandle = request->requestHeader.requestHandle; /* Allocate the results array to acknowledge the acknowledge */ if(request->subscriptionAcknowledgementsSize > 0) { response->results = (UA_StatusCode *) UA_Array_new(request->subscriptionAcknowledgementsSize, &UA_TYPES[UA_TYPES_STATUSCODE]); if(!response->results) { UA_free(entry); subscriptionSendError(session->header.channel, requestId, request->requestHeader.requestHandle, UA_STATUSCODE_BADOUTOFMEMORY); return; } response->resultsSize = request->subscriptionAcknowledgementsSize; } /* Delete Acknowledged Subscription Messages */ for(size_t i = 0; i < request->subscriptionAcknowledgementsSize; ++i) { UA_SubscriptionAcknowledgement *ack = &request->subscriptionAcknowledgements[i]; UA_Subscription *sub = UA_Session_getSubscriptionById(session, ack->subscriptionId); if(!sub) { response->results[i] = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Cannot process acknowledgements subscription %u", ack->subscriptionId); continue; } /* Remove the acked transmission from the retransmission queue */ response->results[i] = UA_Subscription_removeRetransmissionMessage(sub, ack->sequenceNumber); } /* Queue the publish response. It will be dequeued in a repeated publish * callback. This can also be triggered right now for a late * subscription. */ UA_Session_queuePublishReq(session, entry, false); UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Queued a publication message"); /* If there are late subscriptions, the new publish request is used to * answer them immediately. However, a single subscription that generates * many notifications must not "starve" other late subscriptions. Therefore * we keep track of the last subscription that got preferential treatment. * We start searching for late subscriptions **after** the last one. */ UA_Subscription *immediate = NULL; if(session->lastSeenSubscriptionId > 0) { LIST_FOREACH(immediate, &session->serverSubscriptions, listEntry) { if(immediate->subscriptionId == session->lastSeenSubscriptionId) { immediate = LIST_NEXT(immediate, listEntry); break; } } } /* If no entry was found, start at the beginning and don't restart */ UA_Boolean found = false; if(!immediate) immediate = LIST_FIRST(&session->serverSubscriptions); else found = true; repeat: while(immediate) { if(immediate->state == UA_SUBSCRIPTIONSTATE_LATE) { session->lastSeenSubscriptionId = immediate->subscriptionId; UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Subscription %u | Response on a late subscription", immediate->subscriptionId); UA_Subscription_publish(server, immediate); return; } immediate = LIST_NEXT(immediate, listEntry); } /* Restart at the beginning of the list */ if(found) { immediate = LIST_FIRST(&session->serverSubscriptions); found = false; goto repeat; } /* No late subscription this time */ session->lastSeenSubscriptionId = 0; } static void Operation_DeleteSubscription(UA_Server *server, UA_Session *session, void *_, const UA_UInt32 *subscriptionId, UA_StatusCode *result) { *result = UA_Session_deleteSubscription(server, session, *subscriptionId); if(*result == UA_STATUSCODE_GOOD) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Subscription %u | Subscription deleted", *subscriptionId); } else { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Deleting Subscription with Id %u failed with error code %s", *subscriptionId, UA_StatusCode_name(*result)); } } void Service_DeleteSubscriptions(UA_Server *server, UA_Session *session, const UA_DeleteSubscriptionsRequest *request, UA_DeleteSubscriptionsResponse *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing DeleteSubscriptionsRequest"); response->responseHeader.serviceResult = UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_DeleteSubscription, NULL, &request->subscriptionIdsSize, &UA_TYPES[UA_TYPES_UINT32], &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]); /* The session has at least one subscription */ if(LIST_FIRST(&session->serverSubscriptions)) return; /* Send remaining publish responses if the last subscription was removed */ UA_Subscription_answerPublishRequestsNoSubscription(server, session); } void Service_Republish(UA_Server *server, UA_Session *session, const UA_RepublishRequest *request, UA_RepublishResponse *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing RepublishRequest"); /* Get the subscription */ UA_Subscription *sub = UA_Session_getSubscriptionById(session, request->subscriptionId); if(!sub) { response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; return; } /* Reset the subscription lifetime */ sub->currentLifetimeCount = 0; /* Find the notification in the retransmission queue */ UA_NotificationMessageEntry *entry; TAILQ_FOREACH(entry, &sub->retransmissionQueue, listEntry) { if(entry->message.sequenceNumber == request->retransmitSequenceNumber) break; } if(!entry) { response->responseHeader.serviceResult = UA_STATUSCODE_BADMESSAGENOTAVAILABLE; return; } response->responseHeader.serviceResult = UA_NotificationMessage_copy(&entry->message, &response->notificationMessage); } #endif /* UA_ENABLE_SUBSCRIPTIONS */ /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_services_monitoreditem.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2016-2017 (c) Florian Palm * Copyright 2015 (c) Chris Iatrou * Copyright 2015-2016 (c) Sten Grüner * Copyright 2015-2016 (c) Oleksiy Vasylyev * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2018 (c) Ari Breitkreuz, fortiss GmbH * Copyright 2017 (c) Mattias Bornhager * Copyright 2017 (c) Henrik Norrman * Copyright 2017-2018 (c) Thomas Stalder, Blue Time Concept SA * Copyright 2018 (c) Fabian Arndt, Root-Core */ #ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */ static UA_StatusCode setMonitoredItemSettings(UA_Server *server, UA_MonitoredItem *mon, UA_MonitoringMode monitoringMode, const UA_MonitoringParameters *params, const UA_DataType* dataType) { UA_StatusCode retval = UA_STATUSCODE_GOOD; if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) { /* Event MonitoredItem */ #ifndef UA_ENABLE_SUBSCRIPTIONS_EVENTS return UA_STATUSCODE_BADNOTSUPPORTED; #else if(params->filter.encoding != UA_EXTENSIONOBJECT_DECODED && params->filter.encoding != UA_EXTENSIONOBJECT_DECODED_NODELETE) return UA_STATUSCODE_BADEVENTFILTERINVALID; if(params->filter.content.decoded.type != &UA_TYPES[UA_TYPES_EVENTFILTER]) return UA_STATUSCODE_BADEVENTFILTERINVALID; UA_EventFilter_clear(&mon->filter.eventFilter); retval = UA_EventFilter_copy((UA_EventFilter *)params->filter.content.decoded.data, &mon->filter.eventFilter); #endif } else { /* DataChange MonitoredItem */ if(params->filter.encoding != UA_EXTENSIONOBJECT_DECODED && params->filter.encoding != UA_EXTENSIONOBJECT_DECODED_NODELETE) { /* Default: Look for status and value */ UA_DataChangeFilter_clear(&mon->filter.dataChangeFilter); mon->filter.dataChangeFilter.trigger = UA_DATACHANGETRIGGER_STATUSVALUE; } else if(params->filter.content.decoded.type == &UA_TYPES[UA_TYPES_DATACHANGEFILTER]) { UA_DataChangeFilter *filter = (UA_DataChangeFilter *)params->filter.content.decoded.data; // TODO implement EURange to support UA_DEADBANDTYPE_PERCENT switch(filter->deadbandType) { case UA_DEADBANDTYPE_NONE: break; case UA_DEADBANDTYPE_ABSOLUTE: if(!dataType || !UA_DataType_isNumeric(dataType)) return UA_STATUSCODE_BADFILTERNOTALLOWED; break; case UA_DEADBANDTYPE_PERCENT: return UA_STATUSCODE_BADMONITOREDITEMFILTERUNSUPPORTED; default: return UA_STATUSCODE_BADMONITOREDITEMFILTERUNSUPPORTED; } retval = UA_DataChangeFilter_copy(filter, &mon->filter.dataChangeFilter); } else { return UA_STATUSCODE_BADMONITOREDITEMFILTERUNSUPPORTED; } } if(retval != UA_STATUSCODE_GOOD) return retval; /* <-- The point of no return --> */ /* Unregister the callback */ UA_MonitoredItem_unregisterSampleCallback(server, mon); /* Remove the old samples */ UA_ByteString_deleteMembers(&mon->lastSampledValue); UA_Variant_deleteMembers(&mon->lastValue); /* ClientHandle */ mon->clientHandle = params->clientHandle; /* SamplingInterval */ UA_Double samplingInterval = params->samplingInterval; if(mon->attributeId == UA_ATTRIBUTEID_VALUE) { const UA_VariableNode *vn = (const UA_VariableNode *) UA_Nodestore_getNode(server->nsCtx, &mon->monitoredNodeId); if(vn) { if(vn->nodeClass == UA_NODECLASS_VARIABLE && samplingInterval < vn->minimumSamplingInterval) samplingInterval = vn->minimumSamplingInterval; UA_Nodestore_releaseNode(server->nsCtx, (const UA_Node *)vn); } } UA_BOUNDEDVALUE_SETWBOUNDS(server->config.samplingIntervalLimits, samplingInterval, mon->samplingInterval); if(samplingInterval != samplingInterval) /* Check for nan */ mon->samplingInterval = server->config.samplingIntervalLimits.min; /* QueueSize */ UA_BOUNDEDVALUE_SETWBOUNDS(server->config.queueSizeLimits, params->queueSize, mon->maxQueueSize); /* DiscardOldest */ mon->discardOldest = params->discardOldest; /* Register sample callback if reporting is enabled */ mon->monitoringMode = monitoringMode; if(monitoringMode == UA_MONITORINGMODE_SAMPLING || monitoringMode == UA_MONITORINGMODE_REPORTING) return UA_MonitoredItem_registerSampleCallback(server, mon); return UA_STATUSCODE_GOOD; } static const UA_String binaryEncoding = {sizeof("Default Binary") - 1, (UA_Byte *)"Default Binary"}; #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS static UA_StatusCode UA_Server_addMonitoredItemToNodeEditNodeCallback(UA_Server *server, UA_Session *session, UA_Node *node, void *data) { /* data is the MonitoredItem */ /* SLIST_INSERT_HEAD */ ((UA_MonitoredItem *)data)->next = ((UA_ObjectNode *)node)->monitoredItemQueue; ((UA_ObjectNode *)node)->monitoredItemQueue = (UA_MonitoredItem *)data; return UA_STATUSCODE_GOOD; } #endif /* Thread-local variables to pass additional arguments into the operation */ struct createMonContext { UA_Subscription *sub; UA_TimestampsToReturn timestampsToReturn; /* If sub is NULL, use local callbacks */ UA_Server_DataChangeNotificationCallback dataChangeCallback; void *context; }; static void Operation_CreateMonitoredItem(UA_Server *server, UA_Session *session, struct createMonContext *cmc, const UA_MonitoredItemCreateRequest *request, UA_MonitoredItemCreateResult *result) { /* Check available capacity */ if(cmc->sub && (((server->config.maxMonitoredItems != 0) && (server->numMonitoredItems >= server->config.maxMonitoredItems)) || ((server->config.maxMonitoredItemsPerSubscription != 0) && (cmc->sub->monitoredItemsSize >= server->config.maxMonitoredItemsPerSubscription)))) { result->statusCode = UA_STATUSCODE_BADTOOMANYMONITOREDITEMS; return; } /* Make an example read to get errors in the itemToMonitor. Allow return * codes "good" and "uncertain", as well as a list of statuscodes that might * be repaired inside the data source. */ UA_DataValue v = UA_Server_readWithSession(server, session, &request->itemToMonitor, cmc->timestampsToReturn); if(v.hasStatus && (v.status >> 30) > 1 && v.status != UA_STATUSCODE_BADRESOURCEUNAVAILABLE && v.status != UA_STATUSCODE_BADCOMMUNICATIONERROR && v.status != UA_STATUSCODE_BADWAITINGFORINITIALDATA && v.status != UA_STATUSCODE_BADUSERACCESSDENIED && v.status != UA_STATUSCODE_BADNOTREADABLE && v.status != UA_STATUSCODE_BADINDEXRANGENODATA) { result->statusCode = v.status; UA_DataValue_deleteMembers(&v); return; } /* Check if the encoding is supported */ if(request->itemToMonitor.dataEncoding.name.length > 0 && (!UA_String_equal(&binaryEncoding, &request->itemToMonitor.dataEncoding.name) || request->itemToMonitor.dataEncoding.namespaceIndex != 0)) { result->statusCode = UA_STATUSCODE_BADDATAENCODINGUNSUPPORTED; UA_DataValue_deleteMembers(&v); return; } /* Check if the encoding is set for a value */ if(request->itemToMonitor.attributeId != UA_ATTRIBUTEID_VALUE && request->itemToMonitor.dataEncoding.name.length > 0) { result->statusCode = UA_STATUSCODE_BADDATAENCODINGINVALID; UA_DataValue_deleteMembers(&v); return; } /* Allocate the MonitoredItem */ size_t nmsize = sizeof(UA_MonitoredItem); if(!cmc->sub) nmsize = sizeof(UA_LocalMonitoredItem); UA_MonitoredItem *newMon = (UA_MonitoredItem*)UA_malloc(nmsize); if(!newMon) { result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY; UA_DataValue_deleteMembers(&v); return; } /* Initialize the MonitoredItem */ UA_MonitoredItem_init(newMon, cmc->sub); newMon->attributeId = request->itemToMonitor.attributeId; newMon->timestampsToReturn = cmc->timestampsToReturn; UA_StatusCode retval = UA_STATUSCODE_GOOD; retval |= UA_NodeId_copy(&request->itemToMonitor.nodeId, &newMon->monitoredNodeId); retval |= UA_String_copy(&request->itemToMonitor.indexRange, &newMon->indexRange); retval |= setMonitoredItemSettings(server, newMon, request->monitoringMode, &request->requestedParameters, v.value.type); UA_DataValue_deleteMembers(&v); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_INFO_SESSION(&server->config.logger, session, "Subscription %u | Could not create a MonitoredItem " "with StatusCode %s", cmc->sub ? cmc->sub->subscriptionId : 0, UA_StatusCode_name(retval)); result->statusCode = retval; UA_MonitoredItem_delete(server, newMon); return; } /* Add to the subscriptions or the local MonitoredItems */ if(cmc->sub) { newMon->monitoredItemId = ++cmc->sub->lastMonitoredItemId; UA_Subscription_addMonitoredItem(server, cmc->sub, newMon); #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS if(newMon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) { /* Insert the monitored item into the node's queue */ UA_Server_editNode(server, NULL, &newMon->monitoredNodeId, UA_Server_addMonitoredItemToNodeEditNodeCallback, newMon); } #endif } else { //TODO support events for local monitored items UA_LocalMonitoredItem *localMon = (UA_LocalMonitoredItem*)newMon; localMon->context = cmc->context; localMon->callback.dataChangeCallback = cmc->dataChangeCallback; newMon->monitoredItemId = ++server->lastLocalMonitoredItemId; LIST_INSERT_HEAD(&server->localMonitoredItems, newMon, listEntry); } /* Register MonitoredItem in userland */ if(server->config.monitoredItemRegisterCallback) { void *targetContext = NULL; UA_Server_getNodeContext(server, request->itemToMonitor.nodeId, &targetContext); server->config.monitoredItemRegisterCallback(server, &session->sessionId, session->sessionHandle, &request->itemToMonitor.nodeId, targetContext, newMon->attributeId, false); newMon->registered = true; } UA_LOG_INFO_SESSION(&server->config.logger, session, "Subscription %u | MonitoredItem %i | " "Created the MonitoredItem", cmc->sub ? cmc->sub->subscriptionId : 0, newMon->monitoredItemId); /* Create the first sample */ if(request->monitoringMode == UA_MONITORINGMODE_REPORTING && newMon->attributeId != UA_ATTRIBUTEID_EVENTNOTIFIER) UA_MonitoredItem_sampleCallback(server, newMon); /* Prepare the response */ result->revisedSamplingInterval = newMon->samplingInterval; result->revisedQueueSize = newMon->maxQueueSize; result->monitoredItemId = newMon->monitoredItemId; } void Service_CreateMonitoredItems(UA_Server *server, UA_Session *session, const UA_CreateMonitoredItemsRequest *request, UA_CreateMonitoredItemsResponse *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing CreateMonitoredItemsRequest"); if(server->config.maxMonitoredItemsPerCall != 0 && request->itemsToCreateSize > server->config.maxMonitoredItemsPerCall) { response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS; return; } /* Check if the timestampstoreturn is valid */ struct createMonContext cmc; cmc.timestampsToReturn = request->timestampsToReturn; if(cmc.timestampsToReturn > UA_TIMESTAMPSTORETURN_NEITHER) { response->responseHeader.serviceResult = UA_STATUSCODE_BADTIMESTAMPSTORETURNINVALID; return; } /* Find the subscription */ cmc.sub = UA_Session_getSubscriptionById(session, request->subscriptionId); if(!cmc.sub) { response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; return; } /* Reset the subscription lifetime */ cmc.sub->currentLifetimeCount = 0; response->responseHeader.serviceResult = UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_CreateMonitoredItem, &cmc, &request->itemsToCreateSize, &UA_TYPES[UA_TYPES_MONITOREDITEMCREATEREQUEST], &response->resultsSize, &UA_TYPES[UA_TYPES_MONITOREDITEMCREATERESULT]); } UA_MonitoredItemCreateResult UA_Server_createDataChangeMonitoredItem(UA_Server *server, UA_TimestampsToReturn timestampsToReturn, const UA_MonitoredItemCreateRequest item, void *monitoredItemContext, UA_Server_DataChangeNotificationCallback callback) { struct createMonContext cmc; cmc.sub = NULL; cmc.context = monitoredItemContext; cmc.dataChangeCallback = callback; cmc.timestampsToReturn = timestampsToReturn; UA_MonitoredItemCreateResult result; UA_MonitoredItemCreateResult_init(&result); Operation_CreateMonitoredItem(server, &server->adminSession, &cmc, &item, &result); return result; } static void Operation_ModifyMonitoredItem(UA_Server *server, UA_Session *session, UA_Subscription *sub, const UA_MonitoredItemModifyRequest *request, UA_MonitoredItemModifyResult *result) { /* Get the MonitoredItem */ UA_MonitoredItem *mon = UA_Subscription_getMonitoredItem(sub, request->monitoredItemId); if(!mon) { result->statusCode = UA_STATUSCODE_BADMONITOREDITEMIDINVALID; return; } /* Read the current value to test if filters are possible. * Can return an empty value (v.value.type == NULL). */ UA_ReadValueId rvid; UA_ReadValueId_init(&rvid); rvid.nodeId = mon->monitoredNodeId; rvid.attributeId = mon->attributeId; rvid.indexRange = mon->indexRange; UA_DataValue v = UA_Server_readWithSession(server, session, &rvid, mon->timestampsToReturn); UA_StatusCode retval = setMonitoredItemSettings(server, mon, mon->monitoringMode, &request->requestedParameters, v.value.type); UA_DataValue_deleteMembers(&v); if(retval != UA_STATUSCODE_GOOD) { result->statusCode = retval; return; } result->revisedSamplingInterval = mon->samplingInterval; result->revisedQueueSize = mon->maxQueueSize; /* Remove some notifications if the queue is now too small */ UA_MonitoredItem_ensureQueueSpace(server, mon); } void Service_ModifyMonitoredItems(UA_Server *server, UA_Session *session, const UA_ModifyMonitoredItemsRequest *request, UA_ModifyMonitoredItemsResponse *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing ModifyMonitoredItemsRequest"); if(server->config.maxMonitoredItemsPerCall != 0 && request->itemsToModifySize > server->config.maxMonitoredItemsPerCall) { response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS; return; } /* Check if the timestampstoreturn is valid */ if(request->timestampsToReturn > UA_TIMESTAMPSTORETURN_NEITHER) { response->responseHeader.serviceResult = UA_STATUSCODE_BADTIMESTAMPSTORETURNINVALID; return; } /* Get the subscription */ UA_Subscription *sub = UA_Session_getSubscriptionById(session, request->subscriptionId); if(!sub) { response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; return; } sub->currentLifetimeCount = 0; /* Reset the subscription lifetime */ response->responseHeader.serviceResult = UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_ModifyMonitoredItem, sub, &request->itemsToModifySize, &UA_TYPES[UA_TYPES_MONITOREDITEMMODIFYREQUEST], &response->resultsSize, &UA_TYPES[UA_TYPES_MONITOREDITEMMODIFYRESULT]); } struct setMonitoringContext { UA_Subscription *sub; UA_MonitoringMode monitoringMode; }; static void Operation_SetMonitoringMode(UA_Server *server, UA_Session *session, struct setMonitoringContext *smc, const UA_UInt32 *monitoredItemId, UA_StatusCode *result) { UA_MonitoredItem *mon = UA_Subscription_getMonitoredItem(smc->sub, *monitoredItemId); if(!mon) { *result = UA_STATUSCODE_BADMONITOREDITEMIDINVALID; return; } UA_Subscription *sub = mon->subscription; /* Check if the MonitoringMode is valid or not */ if(smc->monitoringMode > UA_MONITORINGMODE_REPORTING) { *result = UA_STATUSCODE_BADMONITORINGMODEINVALID; return; } /* Nothing has changed */ if(mon->monitoringMode == smc->monitoringMode) return; mon->monitoringMode = smc->monitoringMode; /* When reporting is enabled, put all notifications that were already * sampled into the global queue of the subscription. When sampling is * enabled, remove all notifications from the global queue. !!! This needs * to be the same operation as in UA_Notification_enqueue !!! */ if(mon->monitoringMode == UA_MONITORINGMODE_REPORTING) { UA_Notification *notification; TAILQ_FOREACH(notification, &mon->queue, listEntry) { TAILQ_INSERT_TAIL(&sub->notificationQueue, notification, globalEntry); ++sub->notificationQueueSize; #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) { ++sub->eventNotifications; } else #endif { ++sub->dataChangeNotifications; } } /* Register the sampling callback with an interval */ *result = UA_MonitoredItem_registerSampleCallback(server, mon); } else if(mon->monitoringMode == UA_MONITORINGMODE_SAMPLING) { UA_Notification *notification; TAILQ_FOREACH(notification, &mon->queue, listEntry) { TAILQ_REMOVE(&sub->notificationQueue, notification, globalEntry); TAILQ_NEXT(notification, globalEntry) = UA_SUBSCRIPTION_QUEUE_SENTINEL; --sub->notificationQueueSize; #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) { --sub->eventNotifications; } else #endif { --sub->dataChangeNotifications; } } /* Register the sampling callback with an interval */ *result = UA_MonitoredItem_registerSampleCallback(server, mon); } else { /* UA_MONITORINGMODE_DISABLED */ UA_MonitoredItem_unregisterSampleCallback(server, mon); /* Setting the mode to DISABLED or SAMPLING causes all queued Notifications to be deleted */ UA_Notification *notification, *notification_tmp; TAILQ_FOREACH_SAFE(notification, &mon->queue, listEntry, notification_tmp) { UA_Notification_dequeue(server, notification); UA_Notification_delete(notification); } /* Initialize lastSampledValue */ UA_ByteString_deleteMembers(&mon->lastSampledValue); UA_Variant_deleteMembers(&mon->lastValue); } } void Service_SetMonitoringMode(UA_Server *server, UA_Session *session, const UA_SetMonitoringModeRequest *request, UA_SetMonitoringModeResponse *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing SetMonitoringMode"); if(server->config.maxMonitoredItemsPerCall != 0 && request->monitoredItemIdsSize > server->config.maxMonitoredItemsPerCall) { response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS; return; } /* Get the subscription */ struct setMonitoringContext smc; smc.sub = UA_Session_getSubscriptionById(session, request->subscriptionId); if(!smc.sub) { response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; return; } smc.sub->currentLifetimeCount = 0; /* Reset the subscription lifetime */ smc.monitoringMode = request->monitoringMode; response->responseHeader.serviceResult = UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_SetMonitoringMode, &smc, &request->monitoredItemIdsSize, &UA_TYPES[UA_TYPES_UINT32], &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]); } static void Operation_DeleteMonitoredItem(UA_Server *server, UA_Session *session, UA_Subscription *sub, const UA_UInt32 *monitoredItemId, UA_StatusCode *result) { *result = UA_Subscription_deleteMonitoredItem(server, sub, *monitoredItemId); } void Service_DeleteMonitoredItems(UA_Server *server, UA_Session *session, const UA_DeleteMonitoredItemsRequest *request, UA_DeleteMonitoredItemsResponse *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing DeleteMonitoredItemsRequest"); if(server->config.maxMonitoredItemsPerCall != 0 && request->monitoredItemIdsSize > server->config.maxMonitoredItemsPerCall) { response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS; return; } /* Get the subscription */ UA_Subscription *sub = UA_Session_getSubscriptionById(session, request->subscriptionId); if(!sub) { response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; return; } /* Reset the subscription lifetime */ sub->currentLifetimeCount = 0; response->responseHeader.serviceResult = UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_DeleteMonitoredItem, sub, &request->monitoredItemIdsSize, &UA_TYPES[UA_TYPES_UINT32], &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]); } UA_StatusCode UA_Server_deleteMonitoredItem(UA_Server *server, UA_UInt32 monitoredItemId) { UA_MonitoredItem *mon; LIST_FOREACH(mon, &server->localMonitoredItems, listEntry) { if(mon->monitoredItemId != monitoredItemId) continue; LIST_REMOVE(mon, listEntry); UA_MonitoredItem_delete(server, mon); return UA_STATUSCODE_GOOD; } return UA_STATUSCODE_BADMONITOREDITEMIDINVALID; } #endif /* UA_ENABLE_SUBSCRIPTIONS */ /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_services_securechannel.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2014, 2017 (c) Florian Palm * Copyright 2015 (c) Oleksiy Vasylyev * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB */ void Service_OpenSecureChannel(UA_Server *server, UA_SecureChannel *channel, const UA_OpenSecureChannelRequest *request, UA_OpenSecureChannelResponse *response) { if(request->requestType == UA_SECURITYTOKENREQUESTTYPE_RENEW) { /* Renew the channel */ response->responseHeader.serviceResult = UA_SecureChannelManager_renew(&server->secureChannelManager, channel, request, response); /* Logging */ if(response->responseHeader.serviceResult == UA_STATUSCODE_GOOD) { UA_LOG_DEBUG_CHANNEL(&server->config.logger, channel, "SecureChannel renewed"); } else { UA_LOG_DEBUG_CHANNEL(&server->config.logger, channel, "Renewing SecureChannel failed"); } return; } /* Must be ISSUE or RENEW */ if(request->requestType != UA_SECURITYTOKENREQUESTTYPE_ISSUE) { response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR; return; } /* Open the channel */ response->responseHeader.serviceResult = UA_SecureChannelManager_open(&server->secureChannelManager, channel, request, response); /* Logging */ if(response->responseHeader.serviceResult == UA_STATUSCODE_GOOD) { UA_LOG_INFO_CHANNEL(&server->config.logger, channel, "Opened SecureChannel"); } else { UA_LOG_INFO_CHANNEL(&server->config.logger, channel, "Opening a SecureChannel failed"); } } /* The server does not send a CloseSecureChannel response */ void Service_CloseSecureChannel(UA_Server *server, UA_SecureChannel *channel) { UA_LOG_INFO_CHANNEL(&server->config.logger, channel, "CloseSecureChannel"); UA_SecureChannelManager_close(&server->secureChannelManager, channel->securityToken.channelId); } /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_services_nodemanagement.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2014-2017 (c) Florian Palm * Copyright 2015-2016 (c) Sten Grüner * Copyright 2015-2016 (c) Chris Iatrou * Copyright 2015-2016 (c) Oleksiy Vasylyev * Copyright 2017 (c) Julian Grothoff * Copyright 2016 (c) LEvertz * Copyright 2016 (c) Lorenz Haas * Copyright 2017 (c) frax2222 * Copyright 2017-2018 (c) Stefan Profanter, fortiss GmbH * Copyright 2017 (c) Christian von Arnim * Copyright 2017 (c) Henrik Norrman */ #define UA_LOG_NODEID_WRAP(NODEID, LOG) { \ UA_String nodeIdStr = UA_STRING_NULL; \ UA_NodeId_toString(NODEID, &nodeIdStr); \ LOG; \ UA_String_deleteMembers(&nodeIdStr); \ } /*********************/ /* Edit Node Context */ /*********************/ UA_StatusCode UA_Server_getNodeContext(UA_Server *server, UA_NodeId nodeId, void **nodeContext) { const UA_Node *node = UA_Nodestore_getNode(server->nsCtx, &nodeId); if(!node) return UA_STATUSCODE_BADNODEIDUNKNOWN; *nodeContext = node->context; UA_Nodestore_releaseNode(server->nsCtx, node); return UA_STATUSCODE_GOOD; } static UA_StatusCode setDeconstructedNode(UA_Server *server, UA_Session *session, UA_Node *node, void *context) { node->constructed = false; return UA_STATUSCODE_GOOD; } static UA_StatusCode setConstructedNodeContext(UA_Server *server, UA_Session *session, UA_Node *node, void *context) { node->context = context; node->constructed = true; return UA_STATUSCODE_GOOD; } static UA_StatusCode editNodeContext(UA_Server *server, UA_Session* session, UA_Node* node, void *context) { node->context = context; return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Server_setNodeContext(UA_Server *server, UA_NodeId nodeId, void *nodeContext) { return UA_Server_editNode(server, &server->adminSession, &nodeId, (UA_EditNodeCallback)editNodeContext, nodeContext); } /**********************/ /* Consistency Checks */ /**********************/ #define UA_PARENT_REFERENCES_COUNT 2 const UA_NodeId parentReferences[UA_PARENT_REFERENCES_COUNT] = { {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASSUBTYPE}}, {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASCOMPONENT}} }; /* Check if the requested parent node exists, has the right node class and is * referenced with an allowed (hierarchical) reference type. For "type" nodes, * only hasSubType references are allowed. */ static UA_StatusCode checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeClass, const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId) { /* Objects do not need a parent (e.g. mandatory/optional modellingrules) */ /* Also, there are some variables which do not have parents, e.g. EnumStrings, EnumValues */ if((nodeClass == UA_NODECLASS_OBJECT || nodeClass == UA_NODECLASS_VARIABLE) && UA_NodeId_isNull(parentNodeId) && UA_NodeId_isNull(referenceTypeId)) return UA_STATUSCODE_GOOD; /* See if the parent exists */ const UA_Node *parent = UA_Nodestore_getNode(server->nsCtx, parentNodeId); if(!parent) { UA_LOG_NODEID_WRAP(parentNodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: Parent node %.*s not found", (int)nodeIdStr.length, nodeIdStr.data)); return UA_STATUSCODE_BADPARENTNODEIDINVALID; } UA_NodeClass parentNodeClass = parent->nodeClass; UA_Nodestore_releaseNode(server->nsCtx, parent); /* Check the referencetype exists */ const UA_ReferenceTypeNode *referenceType = (const UA_ReferenceTypeNode*) UA_Nodestore_getNode(server->nsCtx, referenceTypeId); if(!referenceType) { UA_LOG_NODEID_WRAP(referenceTypeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: Reference type %.*s to the parent not found", (int)nodeIdStr.length, nodeIdStr.data)); return UA_STATUSCODE_BADREFERENCETYPEIDINVALID; } /* Check if the referencetype is a reference type node */ if(referenceType->nodeClass != UA_NODECLASS_REFERENCETYPE) { UA_LOG_NODEID_WRAP(referenceTypeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: Reference type %.*s to the parent is not a ReferenceTypeNode", (int)nodeIdStr.length, nodeIdStr.data)); UA_Nodestore_releaseNode(server->nsCtx, (const UA_Node*)referenceType); return UA_STATUSCODE_BADREFERENCETYPEIDINVALID; } UA_Boolean referenceTypeIsAbstract = referenceType->isAbstract; UA_Nodestore_releaseNode(server->nsCtx, (const UA_Node*)referenceType); /* Check that the reference type is not abstract */ if(referenceTypeIsAbstract == true) { UA_LOG_NODEID_WRAP(referenceTypeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: Abstract reference type %.*s to the parent not allowed", (int)nodeIdStr.length, nodeIdStr.data)); return UA_STATUSCODE_BADREFERENCENOTALLOWED; } /* Check hassubtype relation for type nodes */ if(nodeClass == UA_NODECLASS_DATATYPE || nodeClass == UA_NODECLASS_VARIABLETYPE || nodeClass == UA_NODECLASS_OBJECTTYPE || nodeClass == UA_NODECLASS_REFERENCETYPE) { /* type needs hassubtype reference to the supertype */ if(!UA_NodeId_equal(referenceTypeId, &subtypeId)) { UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: Type nodes need to have a HasSubType " "reference to the parent"); return UA_STATUSCODE_BADREFERENCENOTALLOWED; } /* supertype needs to be of the same node type */ if(parentNodeClass != nodeClass) { UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: Type nodes needs to be of the same node " "type as their parent"); return UA_STATUSCODE_BADPARENTNODEIDINVALID; } return UA_STATUSCODE_GOOD; } /* Test if the referencetype is hierarchical */ if(!isNodeInTree(server->nsCtx, referenceTypeId, &hierarchicalReferences, &subtypeId, 1)) { UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: Reference type to the parent is not hierarchical"); return UA_STATUSCODE_BADREFERENCETYPEIDINVALID; } return UA_STATUSCODE_GOOD; } static UA_StatusCode typeCheckVariableNode(UA_Server *server, UA_Session *session, const UA_VariableNode *node, const UA_VariableTypeNode *vt) { /* The value might come from a datasource, so we perform a * regular read. */ UA_DataValue value; UA_DataValue_init(&value); UA_StatusCode retval = readValueAttribute(server, session, node, &value); if(retval != UA_STATUSCODE_GOOD) return retval; UA_NodeId baseDataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE); /* Check the datatype against the vt */ /* If the node does not have any value and the dataType is BaseDataType, * then it's also fine. This is the default for empty nodes. */ if(!compatibleDataType(server, &node->dataType, &vt->dataType, false) && (value.hasValue || !UA_NodeId_equal(&node->dataType, &baseDataType))) { UA_LOG_NODEID_WRAP(&node->nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: The value of %.*s is incompatible with " "the datatype of the VariableType", (int)nodeIdStr.length, nodeIdStr.data)); UA_DataValue_deleteMembers(&value); return UA_STATUSCODE_BADTYPEMISMATCH; } /* Check valueRank against array dimensions */ if(!compatibleValueRankArrayDimensions(server, session, node->valueRank, node->arrayDimensionsSize)) { UA_LOG_NODEID_WRAP(&node->nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: The value rank of %.*s is incomatible " "with its array dimensions", (int)nodeIdStr.length, nodeIdStr.data)); UA_DataValue_deleteMembers(&value); return UA_STATUSCODE_BADTYPEMISMATCH; } /* Check valueRank against the vt */ if(!compatibleValueRanks(node->valueRank, vt->valueRank)) { UA_LOG_NODEID_WRAP(&node->nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: The value rank of %.*s is incomatible " "with the value rank of the VariableType", (int)nodeIdStr.length, nodeIdStr.data)); UA_DataValue_deleteMembers(&value); return UA_STATUSCODE_BADTYPEMISMATCH; } /* Check array dimensions against the vt */ if(!compatibleArrayDimensions(vt->arrayDimensionsSize, vt->arrayDimensions, node->arrayDimensionsSize, node->arrayDimensions)) { UA_LOG_NODEID_WRAP(&node->nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: The array dimensions of %.*s are " "incomatible with the array dimensions of the VariableType", (int)nodeIdStr.length, nodeIdStr.data)); UA_DataValue_deleteMembers(&value); return UA_STATUSCODE_BADTYPEMISMATCH; } /* Typecheck the value */ if(value.hasValue && value.value.data) { /* If the type-check failed write the same value again. The * write-service tries to convert to the correct type... */ if(!compatibleValue(server, session, &node->dataType, node->valueRank, node->arrayDimensionsSize, node->arrayDimensions, &value.value, NULL)) retval = UA_Server_writeValue(server, node->nodeId, value.value); UA_DataValue_deleteMembers(&value); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_NODEID_WRAP(&node->nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: The value of of %.*s is incomatible with the " "variable definition", (int)nodeIdStr.length, nodeIdStr.data)); } } return retval; } /********************/ /* Instantiate Node */ /********************/ static const UA_NodeId baseDataVariableType = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_BASEDATAVARIABLETYPE}}; static const UA_NodeId baseObjectType = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_BASEOBJECTTYPE}}; static const UA_NodeId hasTypeDefinition = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASTYPEDEFINITION}}; /* Use attributes from the variable type wherever required. Reload the node if * changes were made. */ static UA_StatusCode useVariableTypeAttributes(UA_Server *server, UA_Session *session, const UA_VariableNode **node_ptr, const UA_VariableTypeNode *vt) { const UA_VariableNode *node = *node_ptr; UA_Boolean modified = false; /* If no value is set, see if the vt provides one and copy it. This needs to * be done before copying the datatype from the vt, as setting the datatype * triggers a typecheck. */ UA_DataValue orig; UA_DataValue_init(&orig); UA_StatusCode retval = readValueAttribute(server, session, node, &orig); if(retval != UA_STATUSCODE_GOOD) return retval; if(orig.value.type) { /* A value is present */ UA_DataValue_deleteMembers(&orig); } else { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "AddNodes: No value given; Copy the value " "from the TypeDefinition"); UA_WriteValue v; UA_WriteValue_init(&v); retval = readValueAttribute(server, session, (const UA_VariableNode*)vt, &v.value); if(retval == UA_STATUSCODE_GOOD && v.value.hasValue) { v.nodeId = node->nodeId; v.attributeId = UA_ATTRIBUTEID_VALUE; retval = UA_Server_writeWithSession(server, session, &v); modified = true; } UA_DataValue_deleteMembers(&v.value); if(retval != UA_STATUSCODE_GOOD) return retval; } /* If no datatype is given, use the datatype of the vt */ if(UA_NodeId_isNull(&node->dataType)) { UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: " "No datatype given; Copy the datatype attribute " "from the TypeDefinition"); UA_WriteValue v; UA_WriteValue_init(&v); v.nodeId = node->nodeId; v.attributeId = UA_ATTRIBUTEID_DATATYPE; v.value.hasValue = true; UA_Variant_setScalar(&v.value.value, (void*)(uintptr_t)&vt->dataType, &UA_TYPES[UA_TYPES_NODEID]); retval = UA_Server_writeWithSession(server, session, &v); modified = true; if(retval != UA_STATUSCODE_GOOD) return retval; } /* Use the ArrayDimensions of the vt */ if(node->arrayDimensionsSize == 0 && vt->arrayDimensionsSize > 0) { UA_WriteValue v; UA_WriteValue_init(&v); v.nodeId = node->nodeId; v.attributeId = UA_ATTRIBUTEID_ARRAYDIMENSIONS; v.value.hasValue = true; UA_Variant_setArray(&v.value.value, vt->arrayDimensions, vt->arrayDimensionsSize, &UA_TYPES[UA_TYPES_UINT32]); retval = UA_Server_writeWithSession(server, session, &v); modified = true; if(retval != UA_STATUSCODE_GOOD) return retval; } /* If the node was modified, update the pointer to the new version */ if(modified) { const UA_VariableNode *updated = (const UA_VariableNode*) UA_Nodestore_getNode(server->nsCtx, &node->nodeId); if(!updated) return UA_STATUSCODE_BADINTERNALERROR; UA_Nodestore_releaseNode(server->nsCtx, (const UA_Node*)node); *node_ptr = updated; } return UA_STATUSCODE_GOOD; } /* Search for an instance of "browseName" in node searchInstance. Used during * copyChildNodes to find overwritable/mergable nodes. Does not touch * outInstanceNodeId if no child is found. */ static UA_StatusCode findChildByBrowsename(UA_Server *server, UA_Session *session, const UA_NodeId *searchInstance, const UA_QualifiedName *browseName, UA_NodeId *outInstanceNodeId) { UA_BrowseDescription bd; UA_BrowseDescription_init(&bd); bd.nodeId = *searchInstance; bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES); bd.includeSubtypes = true; bd.browseDirection = UA_BROWSEDIRECTION_FORWARD; bd.nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE | UA_NODECLASS_METHOD; bd.resultMask = UA_BROWSERESULTMASK_BROWSENAME; UA_BrowseResult br; UA_BrowseResult_init(&br); UA_UInt32 maxrefs = 0; Operation_Browse(server, session, &maxrefs, &bd, &br); if(br.statusCode != UA_STATUSCODE_GOOD) return br.statusCode; UA_StatusCode retval = UA_STATUSCODE_GOOD; for(size_t i = 0; i < br.referencesSize; ++i) { UA_ReferenceDescription *rd = &br.references[i]; if(rd->browseName.namespaceIndex == browseName->namespaceIndex && UA_String_equal(&rd->browseName.name, &browseName->name)) { retval = UA_NodeId_copy(&rd->nodeId.nodeId, outInstanceNodeId); break; } } UA_BrowseResult_deleteMembers(&br); return retval; } static const UA_NodeId mandatoryId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_MODELLINGRULE_MANDATORY}}; static const UA_NodeId hasModellingRuleId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASMODELLINGRULE}}; static UA_Boolean isMandatoryChild(UA_Server *server, UA_Session *session, const UA_NodeId *childNodeId) { /* Get the child */ const UA_Node *child = UA_Nodestore_getNode(server->nsCtx, childNodeId); if(!child) return false; /* Look for the reference making the child mandatory */ for(size_t i = 0; i < child->referencesSize; ++i) { UA_NodeReferenceKind *refs = &child->references[i]; if(!UA_NodeId_equal(&hasModellingRuleId, &refs->referenceTypeId)) continue; if(refs->isInverse) continue; for(size_t j = 0; j < refs->refTargetsSize; ++j) { if(UA_NodeId_equal(&mandatoryId, &refs->refTargets[j].target.nodeId)) { UA_Nodestore_releaseNode(server->nsCtx, child); return true; } } } UA_Nodestore_releaseNode(server->nsCtx, child); return false; } static UA_StatusCode copyAllChildren(UA_Server *server, UA_Session *session, const UA_NodeId *source, const UA_NodeId *destination); static UA_StatusCode recursiveTypeCheckAddChildren(UA_Server *server, UA_Session *session, const UA_Node **node, const UA_Node *type); static void Operation_addReference(UA_Server *server, UA_Session *session, void *context, const UA_AddReferencesItem *item, UA_StatusCode *retval); static UA_StatusCode copyChild(UA_Server *server, UA_Session *session, const UA_NodeId *destinationNodeId, const UA_ReferenceDescription *rd) { /* Is there an existing child with the browsename? */ UA_NodeId existingChild = UA_NODEID_NULL; UA_StatusCode retval = findChildByBrowsename(server, session, destinationNodeId, &rd->browseName, &existingChild); if(retval != UA_STATUSCODE_GOOD) return retval; /* Have a child with that browseName. Deep-copy missing members. */ if(!UA_NodeId_isNull(&existingChild)) { if(rd->nodeClass == UA_NODECLASS_VARIABLE || rd->nodeClass == UA_NODECLASS_OBJECT) retval = copyAllChildren(server, session, &rd->nodeId.nodeId, &existingChild); UA_NodeId_deleteMembers(&existingChild); return retval; } /* Is the child mandatory? If not, ask callback whether child should be instantiated. * If not, skip. */ if(!isMandatoryChild(server, session, &rd->nodeId.nodeId)) { if(!server->config.nodeLifecycle.createOptionalChild) return UA_STATUSCODE_GOOD; if(server->config.nodeLifecycle.createOptionalChild(server, &session->sessionId, session->sessionHandle, &rd->nodeId.nodeId, destinationNodeId, &rd->referenceTypeId) == UA_FALSE) { return UA_STATUSCODE_GOOD; } } /* Child is a method -> create a reference */ if(rd->nodeClass == UA_NODECLASS_METHOD) { UA_AddReferencesItem newItem; UA_AddReferencesItem_init(&newItem); newItem.sourceNodeId = *destinationNodeId; newItem.referenceTypeId = rd->referenceTypeId; newItem.isForward = true; newItem.targetNodeId = rd->nodeId; newItem.targetNodeClass = UA_NODECLASS_METHOD; Operation_addReference(server, session, NULL, &newItem, &retval); return retval; } /* Child is a variable or object */ if(rd->nodeClass == UA_NODECLASS_VARIABLE || rd->nodeClass == UA_NODECLASS_OBJECT) { /* Make a copy of the node */ UA_Node *node; retval = UA_Nodestore_getNodeCopy(server->nsCtx, &rd->nodeId.nodeId, &node); if(retval != UA_STATUSCODE_GOOD) return retval; /* Remove the context of the copied node */ node->context = NULL; node->constructed = false; /* Reset the NodeId (random numeric id will be assigned in the nodestore) */ UA_NodeId_deleteMembers(&node->nodeId); node->nodeId.namespaceIndex = destinationNodeId->namespaceIndex; if (server->config.nodeLifecycle.generateChildNodeId) { retval = server->config.nodeLifecycle.generateChildNodeId(server, &session->sessionId, session->sessionHandle, &rd->nodeId.nodeId, destinationNodeId, &rd->referenceTypeId, &node->nodeId); if(retval != UA_STATUSCODE_GOOD) { UA_Nodestore_deleteNode(server->nsCtx, node); return retval; } } /* Remove references, they are re-created from scratch in addnode_finish */ /* TODO: Be more clever in removing references that are re-added during * addnode_finish. That way, we can call addnode_finish also on children that were * manually added by the user during addnode_begin and addnode_finish. */ /* For now we keep all the modelling rule references and delete all others */ UA_NodeId modellingRuleReferenceId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE); UA_Node_deleteReferencesSubset(node, 1, &modellingRuleReferenceId); /* Add the node to the nodestore */ UA_NodeId newNodeId; retval = UA_Nodestore_insertNode(server->nsCtx, node, &newNodeId); if(retval != UA_STATUSCODE_GOOD) return retval; /* Add the node references */ retval = AddNode_addRefs(server, session, &newNodeId, destinationNodeId, &rd->referenceTypeId, &rd->typeDefinition.nodeId); if(retval != UA_STATUSCODE_GOOD) { UA_Nodestore_removeNode(server->nsCtx, &newNodeId); return retval; } /* For the new child, recursively copy the members of the original. No * typechecking is performed here. Assuming that the original is * consistent. */ retval = copyAllChildren(server, session, &rd->nodeId.nodeId, &newNodeId); } return retval; } /* Copy any children of Node sourceNodeId to another node destinationNodeId. */ static UA_StatusCode copyAllChildren(UA_Server *server, UA_Session *session, const UA_NodeId *source, const UA_NodeId *destination) { /* Browse to get all children of the source */ UA_BrowseDescription bd; UA_BrowseDescription_init(&bd); bd.nodeId = *source; bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES); bd.includeSubtypes = true; bd.browseDirection = UA_BROWSEDIRECTION_FORWARD; bd.nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE | UA_NODECLASS_METHOD; bd.resultMask = UA_BROWSERESULTMASK_REFERENCETYPEID | UA_BROWSERESULTMASK_NODECLASS | UA_BROWSERESULTMASK_BROWSENAME | UA_BROWSERESULTMASK_TYPEDEFINITION; UA_BrowseResult br; UA_BrowseResult_init(&br); UA_UInt32 maxrefs = 0; Operation_Browse(server, session, &maxrefs, &bd, &br); if(br.statusCode != UA_STATUSCODE_GOOD) return br.statusCode; UA_StatusCode retval = UA_STATUSCODE_GOOD; for(size_t i = 0; i < br.referencesSize; ++i) { UA_ReferenceDescription *rd = &br.references[i]; retval = copyChild(server, session, destination, rd); if(retval != UA_STATUSCODE_GOOD) return retval; } UA_BrowseResult_deleteMembers(&br); return retval; } static UA_StatusCode addTypeChildren(UA_Server *server, UA_Session *session, const UA_Node *node, const UA_Node *type) { /* Get the hierarchy of the type and all its supertypes */ UA_NodeId *hierarchy = NULL; size_t hierarchySize = 0; UA_StatusCode retval = getParentTypeAndInterfaceHierarchy(server, &type->nodeId, &hierarchy, &hierarchySize); if(retval != UA_STATUSCODE_GOOD) return retval; UA_assert(hierarchySize < 1000); /* Copy members of the type and supertypes (and instantiate them) */ for(size_t i = 0; i < hierarchySize; ++i) { retval = copyAllChildren(server, session, &hierarchy[i], &node->nodeId); if(retval != UA_STATUSCODE_GOOD) break; } UA_Array_delete(hierarchy, hierarchySize, &UA_TYPES[UA_TYPES_NODEID]); return retval; } static UA_StatusCode addRef(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId, const UA_NodeId *referenceTypeId, const UA_NodeId *parentNodeId, UA_Boolean forward) { UA_AddReferencesItem ref_item; UA_AddReferencesItem_init(&ref_item); ref_item.sourceNodeId = *nodeId; ref_item.referenceTypeId = *referenceTypeId; ref_item.isForward = forward; ref_item.targetNodeId.nodeId = *parentNodeId; UA_StatusCode retval = UA_STATUSCODE_GOOD; Operation_addReference(server, session, NULL, &ref_item, &retval); return retval; } /************/ /* Add Node */ /************/ static const UA_NodeId hasSubtype = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASSUBTYPE}}; UA_StatusCode AddNode_addRefs(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId, const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId, const UA_NodeId *typeDefinitionId) { /* Get the node */ const UA_Node *type = NULL; const UA_Node *node = UA_Nodestore_getNode(server->nsCtx, nodeId); if(!node) return UA_STATUSCODE_BADNODEIDUNKNOWN; /* Use the typeDefinition as parent for type-nodes */ if(node->nodeClass == UA_NODECLASS_VARIABLETYPE || node->nodeClass == UA_NODECLASS_OBJECTTYPE || node->nodeClass == UA_NODECLASS_REFERENCETYPE || node->nodeClass == UA_NODECLASS_DATATYPE) { if(UA_NodeId_equal(referenceTypeId, &UA_NODEID_NULL)) referenceTypeId = &hasSubtype; const UA_Node *parentNode = UA_Nodestore_getNode(server->nsCtx, parentNodeId); if(parentNode) { if(parentNode->nodeClass == node->nodeClass) typeDefinitionId = parentNodeId; UA_Nodestore_releaseNode(server->nsCtx, parentNode); } } UA_StatusCode retval; /* Make sure newly created node does not have itself as parent */ if (UA_NodeId_equal(nodeId, parentNodeId)) { UA_LOG_NODEID_WRAP(nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: The node %.*s can not have " "itself as parent", (int)nodeIdStr.length, nodeIdStr.data)); retval = UA_STATUSCODE_BADINVALIDARGUMENT; goto cleanup; } /* Check parent reference. Objects may have no parent. */ retval = checkParentReference(server, session, node->nodeClass, parentNodeId, referenceTypeId); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_NODEID_WRAP(nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: The parent reference for %.*s is invalid " "with status code %s", (int)nodeIdStr.length, nodeIdStr.data, UA_StatusCode_name(retval))); goto cleanup; } /* Replace empty typeDefinition with the most permissive default */ if((node->nodeClass == UA_NODECLASS_VARIABLE || node->nodeClass == UA_NODECLASS_OBJECT) && UA_NodeId_isNull(typeDefinitionId)) { UA_LOG_NODEID_WRAP(nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: No TypeDefinition for %.*s; Use the default " "TypeDefinition for the Variable/Object", (int)nodeIdStr.length, nodeIdStr.data)); if(node->nodeClass == UA_NODECLASS_VARIABLE) typeDefinitionId = &baseDataVariableType; else typeDefinitionId = &baseObjectType; } /* Get the node type. There must be a typedefinition for variables, objects * and type-nodes. See the above checks. */ if(!UA_NodeId_isNull(typeDefinitionId)) { /* Get the type node */ type = UA_Nodestore_getNode(server->nsCtx, typeDefinitionId); if(!type) { UA_LOG_NODEID_WRAP(typeDefinitionId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: Node type %.*s not found", (int)nodeIdStr.length, nodeIdStr.data)); retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID; goto cleanup; } UA_Boolean typeOk = false; switch(node->nodeClass) { case UA_NODECLASS_DATATYPE: typeOk = type->nodeClass == UA_NODECLASS_DATATYPE; break; case UA_NODECLASS_METHOD: typeOk = type->nodeClass == UA_NODECLASS_METHOD; break; case UA_NODECLASS_OBJECT: typeOk = type->nodeClass == UA_NODECLASS_OBJECTTYPE; break; case UA_NODECLASS_OBJECTTYPE: typeOk = type->nodeClass == UA_NODECLASS_OBJECTTYPE; break; case UA_NODECLASS_REFERENCETYPE: typeOk = type->nodeClass == UA_NODECLASS_REFERENCETYPE; break; case UA_NODECLASS_VARIABLE: typeOk = type->nodeClass == UA_NODECLASS_VARIABLETYPE; break; case UA_NODECLASS_VARIABLETYPE: typeOk = type->nodeClass == UA_NODECLASS_VARIABLETYPE; break; case UA_NODECLASS_VIEW: typeOk = type->nodeClass == UA_NODECLASS_VIEW; break; default: typeOk = false; } if(!typeOk) { UA_LOG_NODEID_WRAP(nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: Type for %.*s does not match node class", (int)nodeIdStr.length, nodeIdStr.data)); retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID; goto cleanup; } /* See if the type has the correct node class. For type-nodes, we know * that type has the same nodeClass from checkParentReference. */ if(node->nodeClass == UA_NODECLASS_VARIABLE) { if(((const UA_VariableTypeNode*)type)->isAbstract) { /* Get subtypes of the parent reference types */ UA_NodeId *parentTypeHierarchy = NULL; size_t parentTypeHierarchySize = 0; retval |= referenceSubtypes(server, &parentReferences[0], &parentTypeHierarchySize, &parentTypeHierarchy); retval |= referenceSubtypes(server, &parentReferences[1], &parentTypeHierarchySize, &parentTypeHierarchy); if(retval != UA_STATUSCODE_GOOD) { UA_Array_delete(parentTypeHierarchy, parentTypeHierarchySize, &UA_TYPES[UA_TYPES_NODEID]); goto cleanup; } /* Abstract variable is allowed if parent is a children of a * base data variable. An abstract variable may be part of an * object type which again is below BaseObjectType */ const UA_NodeId variableTypes = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE); const UA_NodeId objectTypes = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE); if(!isNodeInTree(server->nsCtx, parentNodeId, &variableTypes, parentTypeHierarchy, parentTypeHierarchySize) && !isNodeInTree(server->nsCtx, parentNodeId, &objectTypes, parentTypeHierarchy, parentTypeHierarchySize)) { UA_LOG_NODEID_WRAP(nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: Type of variable node %.*s must " "be VariableType and not cannot be abstract", (int)nodeIdStr.length, nodeIdStr.data)); retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID; } UA_Array_delete(parentTypeHierarchy, parentTypeHierarchySize, &UA_TYPES[UA_TYPES_NODEID]); if(retval != UA_STATUSCODE_GOOD) goto cleanup; } } if(node->nodeClass == UA_NODECLASS_OBJECT) { if(((const UA_ObjectTypeNode*)type)->isAbstract) { /* Get subtypes of the parent reference types */ UA_NodeId *parentTypeHierarchy = NULL; size_t parentTypeHierarchySize = 0; retval |= referenceSubtypes(server, &parentReferences[0], &parentTypeHierarchySize, &parentTypeHierarchy); retval |= referenceSubtypes(server, &parentReferences[1], &parentTypeHierarchySize, &parentTypeHierarchy); if(retval != UA_STATUSCODE_GOOD) { UA_Array_delete(parentTypeHierarchy, parentTypeHierarchySize, &UA_TYPES[UA_TYPES_NODEID]); goto cleanup; } /* Object node created of an abstract ObjectType. Only allowed * if within BaseObjectType folder or if it's an event (subType of BaseEventType) */ const UA_NodeId objectTypes = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE); UA_Boolean isInBaseObjectType = isNodeInTree(server->nsCtx, parentNodeId, &objectTypes, parentTypeHierarchy, parentTypeHierarchySize); const UA_NodeId eventTypes = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE); UA_Boolean isInBaseEventType = isNodeInTree(server->nsCtx, &type->nodeId, &eventTypes, &hasSubtype, 1); if(!isInBaseObjectType && !(isInBaseEventType && UA_NodeId_isNull(parentNodeId))) { UA_LOG_NODEID_WRAP(nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: Type of object node %.*s must " "be ObjectType and not be abstract", (int)nodeIdStr.length, nodeIdStr.data)); retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID; } UA_Array_delete(parentTypeHierarchy, parentTypeHierarchySize, &UA_TYPES[UA_TYPES_NODEID]); if(retval != UA_STATUSCODE_GOOD) goto cleanup; } } } /* Add reference to the parent */ if(!UA_NodeId_isNull(parentNodeId)) { if(UA_NodeId_isNull(referenceTypeId)) { UA_LOG_NODEID_WRAP(nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: Reference to parent of %.*s cannot be null", (int)nodeIdStr.length, nodeIdStr.data)); retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID; goto cleanup; } retval = addRef(server, session, &node->nodeId, referenceTypeId, parentNodeId, false); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_NODEID_WRAP(nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: Adding reference to parent of %.*s failed", (int)nodeIdStr.length, nodeIdStr.data)); goto cleanup; } } /* Add a hasTypeDefinition reference */ if(node->nodeClass == UA_NODECLASS_VARIABLE || node->nodeClass == UA_NODECLASS_OBJECT) { UA_assert(type != NULL); /* see above */ retval = addRef(server, session, &node->nodeId, &hasTypeDefinition, &type->nodeId, true); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_NODEID_WRAP(nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: Adding a reference to the type " "definition of %.*s failed with error code %s", (int)nodeIdStr.length, nodeIdStr.data, UA_StatusCode_name(retval))); } } cleanup: UA_Nodestore_releaseNode(server->nsCtx, node); if(type) UA_Nodestore_releaseNode(server->nsCtx, type); return retval; } /* Create the node and add it to the nodestore. But don't typecheck and add * references so far */ UA_StatusCode AddNode_raw(UA_Server *server, UA_Session *session, void *nodeContext, const UA_AddNodesItem *item, UA_NodeId *outNewNodeId) { /* Do not check access for server */ if(session != &server->adminSession && server->config.accessControl.allowAddNode && !server->config.accessControl.allowAddNode(server, &server->config.accessControl, &session->sessionId, session->sessionHandle, item)) { return UA_STATUSCODE_BADUSERACCESSDENIED; } /* Check the namespaceindex */ if(item->requestedNewNodeId.nodeId.namespaceIndex >= server->namespacesSize) { UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: Namespace invalid"); return UA_STATUSCODE_BADNODEIDINVALID; } if(item->nodeAttributes.encoding != UA_EXTENSIONOBJECT_DECODED && item->nodeAttributes.encoding != UA_EXTENSIONOBJECT_DECODED_NODELETE) { UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: Node attributes invalid"); return UA_STATUSCODE_BADINTERNALERROR; } /* Create a node */ UA_Node *node = UA_Nodestore_newNode(server->nsCtx, item->nodeClass); if(!node) { UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: Node could not create a node " "in the nodestore"); return UA_STATUSCODE_BADOUTOFMEMORY; } /* Fill the node attributes */ node->context = nodeContext; UA_StatusCode retval = UA_NodeId_copy(&item->requestedNewNodeId.nodeId, &node->nodeId); if(retval != UA_STATUSCODE_GOOD) goto create_error; retval = UA_QualifiedName_copy(&item->browseName, &node->browseName); if(retval != UA_STATUSCODE_GOOD) goto create_error; retval = UA_Node_setAttributes(node, item->nodeAttributes.content.decoded.data, item->nodeAttributes.content.decoded.type); if(retval != UA_STATUSCODE_GOOD) goto create_error; /* Add the node to the nodestore */ retval = UA_Nodestore_insertNode(server->nsCtx, node, outNewNodeId); if(retval != UA_STATUSCODE_GOOD) UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: Node could not add the new node " "to the nodestore with error code %s", UA_StatusCode_name(retval)); return retval; create_error: UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: Node could not create a node " "with error code %s", UA_StatusCode_name(retval)); UA_Nodestore_deleteNode(server->nsCtx, node); return retval; } /* Prepare the node, then add it to the nodestore */ static UA_StatusCode Operation_addNode_begin(UA_Server *server, UA_Session *session, void *nodeContext, const UA_AddNodesItem *item, const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId, UA_NodeId *outNewNodeId) { /* Create a temporary NodeId if none is returned */ UA_NodeId newId; if(!outNewNodeId) { UA_NodeId_init(&newId); outNewNodeId = &newId; } /* Create the node and add it to the nodestore */ UA_StatusCode retval = AddNode_raw(server, session, nodeContext, item, outNewNodeId); if(retval != UA_STATUSCODE_GOOD) return retval; /* Typecheck and add references to parent and type definition */ retval = AddNode_addRefs(server, session, outNewNodeId, parentNodeId, referenceTypeId, &item->typeDefinition.nodeId); if(retval != UA_STATUSCODE_GOOD) UA_Server_deleteNode(server, *outNewNodeId, true); if(outNewNodeId == &newId) UA_NodeId_deleteMembers(&newId); return retval; } static UA_StatusCode recursiveTypeCheckAddChildren(UA_Server *server, UA_Session *session, const UA_Node **nodeptr, const UA_Node *type) { UA_assert(type != NULL); UA_StatusCode retval = UA_STATUSCODE_GOOD; const UA_Node *node = *nodeptr; /* Use attributes from the type. The value and value constraints are the * same for the variable and variabletype attribute structs. */ if(node->nodeClass == UA_NODECLASS_VARIABLE || node->nodeClass == UA_NODECLASS_VARIABLETYPE) { retval = useVariableTypeAttributes(server, session, (const UA_VariableNode**)nodeptr, (const UA_VariableTypeNode*)type); node = *nodeptr; /* If the node was replaced */ if(retval != UA_STATUSCODE_GOOD) { UA_LOG_NODEID_WRAP(&node->nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: Using attributes for %.*s from the variable type " "failed with error code %s", (int)nodeIdStr.length, nodeIdStr.data, UA_StatusCode_name(retval))); return retval; } /* Check NodeClass for 'hasSubtype'. UA_NODECLASS_VARIABLE not allowed to have subtype */ if((node->nodeClass == UA_NODECLASS_VARIABLE) && (UA_NodeId_equal( &node->references->referenceTypeId, &hasSubtype))) { UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: VariableType not allowed to have HasSubType"); return UA_STATUSCODE_BADREFERENCENOTALLOWED; } /* Check if all attributes hold the constraints of the type now. The initial * attributes must type-check. The constructor might change the attributes * again. Then, the changes are type-checked by the normal write service. */ retval = typeCheckVariableNode(server, session, (const UA_VariableNode*)node, (const UA_VariableTypeNode*)type); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_NODEID_WRAP(&node->nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: Type-checking the variable node %.*s " "failed with error code %s", (int)nodeIdStr.length, nodeIdStr.data, UA_StatusCode_name(retval))); return retval; } } /* Add (mandatory) child nodes from the type definition */ if(node->nodeClass == UA_NODECLASS_VARIABLE || node->nodeClass == UA_NODECLASS_OBJECT) { retval = addTypeChildren(server, session, node, type); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_NODEID_WRAP(&node->nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: Adding child nodes of %.*s failed with error code %s", (int)nodeIdStr.length, nodeIdStr.data, UA_StatusCode_name(retval))); } } return UA_STATUSCODE_GOOD; } static UA_StatusCode findDefaultInstanceBrowseNameNode(UA_Server *server, UA_NodeId startingNode, UA_NodeId *foundId){ UA_NodeId_init(foundId); UA_RelativePathElement rpe; UA_RelativePathElement_init(&rpe); rpe.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY); rpe.isInverse = false; rpe.includeSubtypes = false; rpe.targetName = UA_QUALIFIEDNAME(0, "DefaultInstanceBrowseName"); UA_BrowsePath bp; UA_BrowsePath_init(&bp); bp.startingNode = startingNode; bp.relativePath.elementsSize = 1; bp.relativePath.elements = &rpe; UA_BrowsePathResult bpr = UA_Server_translateBrowsePathToNodeIds(server, &bp); UA_StatusCode retval = bpr.statusCode; if (retval == UA_STATUSCODE_GOOD && bpr.targetsSize > 0) { retval = UA_NodeId_copy(&bpr.targets[0].targetId.nodeId, foundId); } UA_BrowsePathResult_deleteMembers(&bpr); return retval; } /* Check if we got a valid browse name for the new node. * For object nodes the BrowseName may only be null if the parent type has a * 'DefaultInstanceBrowseName' property. * */ static UA_StatusCode checkValidBrowseName(UA_Server *server, UA_Session *session, const UA_Node *node, const UA_Node *type) { UA_assert(type != NULL); UA_StatusCode retval = UA_STATUSCODE_GOOD; if(node->nodeClass != UA_NODECLASS_OBJECT) { /* nodes other than Objects must have a browseName */ if (UA_QualifiedName_isNull(&node->browseName)) return UA_STATUSCODE_BADBROWSENAMEINVALID; return UA_STATUSCODE_GOOD; } /* If the object node already has a browse name we are done here. */ if(!UA_QualifiedName_isNull(&node->browseName)) return UA_STATUSCODE_GOOD; /* at this point we have an object with an empty browse name. * Check the type node if it has a DefaultInstanceBrowseName property */ UA_NodeId defaultBrowseNameNode; retval = findDefaultInstanceBrowseNameNode(server, type->nodeId, &defaultBrowseNameNode); if (retval != UA_STATUSCODE_GOOD) { if (retval == UA_STATUSCODE_BADNOMATCH) /* the DefaultBrowseName property is not found, return the corresponding status code */ return UA_STATUSCODE_BADBROWSENAMEINVALID; return retval; } UA_Variant defaultBrowseName; retval = UA_Server_readValue(server, defaultBrowseNameNode, &defaultBrowseName); if (retval != UA_STATUSCODE_GOOD) return retval; UA_QualifiedName *defaultValue = (UA_QualifiedName *) defaultBrowseName.data; retval = UA_Server_writeBrowseName(server, node->nodeId, *defaultValue); UA_Variant_clear(&defaultBrowseName); return retval; } /* Construct children first */ static UA_StatusCode recursiveCallConstructors(UA_Server *server, UA_Session *session, const UA_Node *node, const UA_Node *type) { if(node->constructed) return UA_STATUSCODE_GOOD; /* Construct the children */ UA_BrowseDescription bd; UA_BrowseDescription_init(&bd); bd.nodeId = node->nodeId; bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES); bd.includeSubtypes = true; bd.browseDirection = UA_BROWSEDIRECTION_FORWARD; UA_BrowseResult br; UA_BrowseResult_init(&br); UA_UInt32 maxrefs = 0; Operation_Browse(server, session, &maxrefs, &bd, &br); if(br.statusCode != UA_STATUSCODE_GOOD) return br.statusCode; /* Call the constructor for every unconstructed node */ UA_StatusCode retval = UA_STATUSCODE_GOOD; for(size_t i = 0; i < br.referencesSize; ++i) { UA_ReferenceDescription *rd = &br.references[i]; const UA_Node *target = UA_Nodestore_getNode(server->nsCtx, &rd->nodeId.nodeId); if(!target) continue; if(target->constructed) { UA_Nodestore_releaseNode(server->nsCtx, target); continue; } const UA_Node *targetType = NULL; if(node->nodeClass == UA_NODECLASS_VARIABLE || node->nodeClass == UA_NODECLASS_OBJECT) { targetType = getNodeType(server, target); if(!targetType) { UA_Nodestore_releaseNode(server->nsCtx, target); retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID; break; } } retval = recursiveCallConstructors(server, session, target, targetType); UA_Nodestore_releaseNode(server->nsCtx, target); if(targetType) UA_Nodestore_releaseNode(server->nsCtx, targetType); if(retval != UA_STATUSCODE_GOOD) break; } UA_BrowseResult_deleteMembers(&br); /* If a child could not be constructed or the node is already constructed */ if(retval != UA_STATUSCODE_GOOD) return retval; /* Get the node type constructor */ const UA_NodeTypeLifecycle *lifecycle = NULL; if(type && node->nodeClass == UA_NODECLASS_OBJECT) { const UA_ObjectTypeNode *ot = (const UA_ObjectTypeNode*)type; lifecycle = &ot->lifecycle; } else if(type && node->nodeClass == UA_NODECLASS_VARIABLE) { const UA_VariableTypeNode *vt = (const UA_VariableTypeNode*)type; lifecycle = &vt->lifecycle; } /* Call the global constructor */ void *context = node->context; if(server->config.nodeLifecycle.constructor) retval = server->config.nodeLifecycle.constructor(server, &session->sessionId, session->sessionHandle, &node->nodeId, &context); /* Call the type constructor */ if(retval == UA_STATUSCODE_GOOD && lifecycle && lifecycle->constructor) retval = lifecycle->constructor(server, &session->sessionId, session->sessionHandle, &type->nodeId, type->context, &node->nodeId, &context); if(retval != UA_STATUSCODE_GOOD) goto fail1; /* Set the context *and* mark the node as constructed */ if(retval == UA_STATUSCODE_GOOD) retval = UA_Server_editNode(server, &server->adminSession, &node->nodeId, (UA_EditNodeCallback)setConstructedNodeContext, context); /* All good, return */ if(retval == UA_STATUSCODE_GOOD) return retval; /* Fail. Call the destructors. */ if(lifecycle && lifecycle->destructor) lifecycle->destructor(server, &session->sessionId, session->sessionHandle, &type->nodeId, type->context, &node->nodeId, &context); fail1: if(server->config.nodeLifecycle.destructor) server->config.nodeLifecycle.destructor(server, &session->sessionId, session->sessionHandle, &node->nodeId, context); return retval; } static void recursiveDeconstructNode(UA_Server *server, UA_Session *session, size_t hierarchicalReferencesSize, UA_ExpandedNodeId *hierarchicalReferences, const UA_Node *node); static void recursiveDeleteNode(UA_Server *server, UA_Session *session, size_t hierarchicalReferencesSize, UA_ExpandedNodeId *hierarchicalReferences, const UA_Node *node, UA_Boolean removeTargetRefs); /* Children, references, type-checking, constructors. */ UA_StatusCode AddNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId) { UA_StatusCode retval = UA_STATUSCODE_GOOD; /* Get the node */ const UA_Node *node = UA_Nodestore_getNode(server->nsCtx, nodeId); if(!node) return UA_STATUSCODE_BADNODEIDUNKNOWN; const UA_Node *type = NULL; /* Instantiate variables and objects */ if(node->nodeClass == UA_NODECLASS_VARIABLE || node->nodeClass == UA_NODECLASS_VARIABLETYPE || node->nodeClass == UA_NODECLASS_OBJECT) { /* Get the type node */ type = getNodeType(server, node); if(!type) { if(server->bootstrapNS0) goto constructor; UA_LOG_NODEID_WRAP(&node->nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: Node type for %.*s not found", (int)nodeIdStr.length, nodeIdStr.data)); retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID; goto cleanup; } retval = checkValidBrowseName(server, session, node, type); if(retval != UA_STATUSCODE_GOOD) goto cleanup; retval = recursiveTypeCheckAddChildren(server, session, &node, type); if(retval != UA_STATUSCODE_GOOD) goto cleanup; } /* Call the constructor(s) */ constructor: retval = recursiveCallConstructors(server, session, node, type); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_NODEID_WRAP(&node->nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNodes: Calling the node constructor(s) of %.*s failed " "with status code %s", (int)nodeIdStr.length, nodeIdStr.data, UA_StatusCode_name(retval))); } cleanup: if(type) UA_Nodestore_releaseNode(server->nsCtx, type); if(retval != UA_STATUSCODE_GOOD) { recursiveDeconstructNode(server, session, 0, NULL, node); recursiveDeleteNode(server, session, 0, NULL, node, true); } UA_Nodestore_releaseNode(server->nsCtx, node); return retval; } static void Operation_addNode(UA_Server *server, UA_Session *session, void *nodeContext, const UA_AddNodesItem *item, UA_AddNodesResult *result) { result->statusCode = Operation_addNode_begin(server, session, nodeContext, item, &item->parentNodeId.nodeId, &item->referenceTypeId, &result->addedNodeId); if(result->statusCode != UA_STATUSCODE_GOOD) return; /* AddNodes_finish */ result->statusCode = AddNode_finish(server, session, &result->addedNodeId); /* If finishing failed, the node was deleted */ if(result->statusCode != UA_STATUSCODE_GOOD) UA_NodeId_deleteMembers(&result->addedNodeId); } void Service_AddNodes(UA_Server *server, UA_Session *session, const UA_AddNodesRequest *request, UA_AddNodesResponse *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing AddNodesRequest"); if(server->config.maxNodesPerNodeManagement != 0 && request->nodesToAddSize > server->config.maxNodesPerNodeManagement) { response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS; return; } response->responseHeader.serviceResult = UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_addNode, NULL, &request->nodesToAddSize, &UA_TYPES[UA_TYPES_ADDNODESITEM], &response->resultsSize, &UA_TYPES[UA_TYPES_ADDNODESRESULT]); } UA_StatusCode __UA_Server_addNode(UA_Server *server, const UA_NodeClass nodeClass, const UA_NodeId *requestedNewNodeId, const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId, const UA_QualifiedName browseName, const UA_NodeId *typeDefinition, const UA_NodeAttributes *attr, const UA_DataType *attributeType, void *nodeContext, UA_NodeId *outNewNodeId) { /* Create the AddNodesItem */ UA_AddNodesItem item; UA_AddNodesItem_init(&item); item.nodeClass = nodeClass; item.requestedNewNodeId.nodeId = *requestedNewNodeId; item.browseName = browseName; item.parentNodeId.nodeId = *parentNodeId; item.referenceTypeId = *referenceTypeId; item.typeDefinition.nodeId = *typeDefinition; item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE; item.nodeAttributes.content.decoded.type = attributeType; item.nodeAttributes.content.decoded.data = (void*)(uintptr_t)attr; /* Call the normal addnodes service */ UA_AddNodesResult result; UA_AddNodesResult_init(&result); Operation_addNode(server, &server->adminSession, nodeContext, &item, &result); if(outNewNodeId) *outNewNodeId = result.addedNodeId; else UA_NodeId_deleteMembers(&result.addedNodeId); return result.statusCode; } UA_StatusCode UA_Server_addNode_begin(UA_Server *server, const UA_NodeClass nodeClass, const UA_NodeId requestedNewNodeId, const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId, const UA_QualifiedName browseName, const UA_NodeId typeDefinition, const void *attr, const UA_DataType *attributeType, void *nodeContext, UA_NodeId *outNewNodeId) { UA_AddNodesItem item; UA_AddNodesItem_init(&item); item.nodeClass = nodeClass; item.requestedNewNodeId.nodeId = requestedNewNodeId; item.browseName = browseName; item.typeDefinition.nodeId = typeDefinition; item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE; item.nodeAttributes.content.decoded.type = attributeType; item.nodeAttributes.content.decoded.data = (void*)(uintptr_t)attr; return Operation_addNode_begin(server, &server->adminSession, nodeContext, &item, &parentNodeId, &referenceTypeId, outNewNodeId); } UA_StatusCode UA_Server_addNode_finish(UA_Server *server, const UA_NodeId nodeId) { return AddNode_finish(server, &server->adminSession, &nodeId); } /****************/ /* Delete Nodes */ /****************/ static void Operation_deleteReference(UA_Server *server, UA_Session *session, void *context, const UA_DeleteReferencesItem *item, UA_StatusCode *retval); /* Remove references to this node (in the other nodes) */ static void removeIncomingReferences(UA_Server *server, UA_Session *session, const UA_Node *node) { UA_DeleteReferencesItem item; UA_DeleteReferencesItem_init(&item); item.targetNodeId.nodeId = node->nodeId; item.deleteBidirectional = false; UA_StatusCode dummy; for(size_t i = 0; i < node->referencesSize; ++i) { UA_NodeReferenceKind *refs = &node->references[i]; item.isForward = refs->isInverse; item.referenceTypeId = refs->referenceTypeId; for(size_t j = 0; j < refs->refTargetsSize; ++j) { item.sourceNodeId = refs->refTargets[j].target.nodeId; Operation_deleteReference(server, session, NULL, &item, &dummy); } } } /* A node can only be deleted if it has at most one incoming hierarchical * reference. If hierarchicalReferences is NULL, always remove. */ static UA_Boolean multipleHierarchies(size_t hierarchicalRefsSize, UA_ExpandedNodeId *hierarchicalRefs, const UA_Node *node) { if(!hierarchicalRefs) return false; size_t incomingRefs = 0; for(size_t i = 0; i < node->referencesSize; i++) { const UA_NodeReferenceKind *k = &node->references[i]; if(!k->isInverse) continue; UA_Boolean hierarchical = false; for(size_t j = 0; j < hierarchicalRefsSize; j++) { if(UA_NodeId_equal(&hierarchicalRefs[j].nodeId, &k->referenceTypeId)) { hierarchical = true; break; } } if(!hierarchical) continue; incomingRefs += k->refTargetsSize; if(incomingRefs > 1) return true; } return false; } /* Recursively call the destructors of this node and all child nodes. * Deconstructs the parent before its children. */ static void recursiveDeconstructNode(UA_Server *server, UA_Session *session, size_t hierarchicalRefsSize, UA_ExpandedNodeId *hierarchicalRefs, const UA_Node *node) { /* Was the constructor called for the node? */ if(!node->constructed) return; /* Call the type-level destructor */ void *context = node->context; /* No longer needed after this function */ if(node->nodeClass == UA_NODECLASS_OBJECT || node->nodeClass == UA_NODECLASS_VARIABLE) { const UA_Node *type = getNodeType(server, node); if(type) { const UA_NodeTypeLifecycle *lifecycle; if(node->nodeClass == UA_NODECLASS_OBJECT) lifecycle = &((const UA_ObjectTypeNode*)type)->lifecycle; else lifecycle = &((const UA_VariableTypeNode*)type)->lifecycle; if(lifecycle->destructor) lifecycle->destructor(server, &session->sessionId, session->sessionHandle, &type->nodeId, type->context, &node->nodeId, &context); UA_Nodestore_releaseNode(server->nsCtx, type); } } /* Call the global destructor */ if(server->config.nodeLifecycle.destructor) server->config.nodeLifecycle.destructor(server, &session->sessionId, session->sessionHandle, &node->nodeId, context); /* Set the constructed flag to false */ UA_Server_editNode(server, &server->adminSession, &node->nodeId, (UA_EditNodeCallback)setDeconstructedNode, context); /* Browse to get all children of the node */ UA_BrowseDescription bd; UA_BrowseDescription_init(&bd); bd.nodeId = node->nodeId; bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES); bd.includeSubtypes = true; bd.browseDirection = UA_BROWSEDIRECTION_FORWARD; UA_BrowseResult br; UA_BrowseResult_init(&br); UA_UInt32 maxrefs = 0; Operation_Browse(server, session, &maxrefs, &bd, &br); if(br.statusCode != UA_STATUSCODE_GOOD) return; /* Deconstruct every child node */ for(size_t i = 0; i < br.referencesSize; ++i) { UA_ReferenceDescription *rd = &br.references[i]; const UA_Node *child = UA_Nodestore_getNode(server->nsCtx, &rd->nodeId.nodeId); if(!child) continue; /* Only delete child nodes that have no other parent */ if(!multipleHierarchies(hierarchicalRefsSize, hierarchicalRefs, child)) recursiveDeconstructNode(server, session, hierarchicalRefsSize, hierarchicalRefs, child); UA_Nodestore_releaseNode(server->nsCtx, child); } UA_BrowseResult_deleteMembers(&br); } static void recursiveDeleteNode(UA_Server *server, UA_Session *session, size_t hierarchicalRefsSize, UA_ExpandedNodeId *hierarchicalRefs, const UA_Node *node, UA_Boolean removeTargetRefs) { /* Browse to get all children of the node */ UA_BrowseDescription bd; UA_BrowseDescription_init(&bd); bd.nodeId = node->nodeId; bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES); bd.includeSubtypes = true; bd.browseDirection = UA_BROWSEDIRECTION_FORWARD; UA_BrowseResult br; UA_BrowseResult_init(&br); UA_UInt32 maxrefs = 0; Operation_Browse(server, session, &maxrefs, &bd, &br); if(br.statusCode != UA_STATUSCODE_GOOD) return; /* Remove every child */ for(size_t i = 0; i < br.referencesSize; ++i) { UA_ReferenceDescription *rd = &br.references[i]; /* Check for self-reference to avoid endless loop */ if(UA_NodeId_equal(&node->nodeId, &rd->nodeId.nodeId)) continue; const UA_Node *child = UA_Nodestore_getNode(server->nsCtx, &rd->nodeId.nodeId); if(!child) continue; /* Only delete child nodes that have no other parent */ if(!multipleHierarchies(hierarchicalRefsSize, hierarchicalRefs, child)) recursiveDeleteNode(server, session, hierarchicalRefsSize, hierarchicalRefs, child, true); UA_Nodestore_releaseNode(server->nsCtx, child); } UA_BrowseResult_deleteMembers(&br); if(removeTargetRefs) removeIncomingReferences(server, session, node); UA_Nodestore_removeNode(server->nsCtx, &node->nodeId); } static void deleteNodeOperation(UA_Server *server, UA_Session *session, void *context, const UA_DeleteNodesItem *item, UA_StatusCode *result) { /* Do not check access for server */ if(session != &server->adminSession && server->config.accessControl.allowDeleteNode && !server->config.accessControl.allowDeleteNode(server, &server->config.accessControl, &session->sessionId, session->sessionHandle, item)) { *result = UA_STATUSCODE_BADUSERACCESSDENIED; return; } const UA_Node *node = UA_Nodestore_getNode(server->nsCtx, &item->nodeId); if(!node) { *result = UA_STATUSCODE_BADNODEIDUNKNOWN; return; } if(UA_Node_hasSubTypeOrInstances(node)) { UA_LOG_INFO_SESSION(&server->config.logger, session, "Delete Nodes: Cannot delete a type node " "with active instances or subtypes"); UA_Nodestore_releaseNode(server->nsCtx, node); *result = UA_STATUSCODE_BADINTERNALERROR; return; } /* TODO: Check if the information model consistency is violated */ /* TODO: Check if the node is a mandatory child of a parent */ /* A node can be referenced with hierarchical references from several * parents in the information model. (But not in a circular way.) The * hierarchical references are checked to see if a node can be deleted. * Getting the type hierarchy can fail in case of low RAM. In that case the * nodes are always deleted. */ UA_ExpandedNodeId *hierarchicalRefs = NULL; size_t hierarchicalRefsSize = 0; UA_NodeId hr = UA_NODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES); browseRecursive(server, 1, &hr, 1, &subtypeId, UA_BROWSEDIRECTION_FORWARD, true, &hierarchicalRefsSize, &hierarchicalRefs); if(!hierarchicalRefs) { UA_LOG_WARNING_SESSION(&server->config.logger, session, "Delete Nodes: Cannot test for hierarchical " "references. Deleting the node and all child nodes."); } recursiveDeconstructNode(server, session, hierarchicalRefsSize, hierarchicalRefs, node); recursiveDeleteNode(server, session, hierarchicalRefsSize, hierarchicalRefs, node, item->deleteTargetReferences); UA_Array_delete(hierarchicalRefs, hierarchicalRefsSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); UA_Nodestore_releaseNode(server->nsCtx, node); } void Service_DeleteNodes(UA_Server *server, UA_Session *session, const UA_DeleteNodesRequest *request, UA_DeleteNodesResponse *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing DeleteNodesRequest"); if(server->config.maxNodesPerNodeManagement != 0 && request->nodesToDeleteSize > server->config.maxNodesPerNodeManagement) { response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS; return; } response->responseHeader.serviceResult = UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)deleteNodeOperation, NULL, &request->nodesToDeleteSize, &UA_TYPES[UA_TYPES_DELETENODESITEM], &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]); } UA_StatusCode UA_Server_deleteNode(UA_Server *server, const UA_NodeId nodeId, UA_Boolean deleteReferences) { UA_DeleteNodesItem item; item.deleteTargetReferences = deleteReferences; item.nodeId = nodeId; UA_StatusCode retval = UA_STATUSCODE_GOOD; deleteNodeOperation(server, &server->adminSession, NULL, &item, &retval); return retval; } /******************/ /* Add References */ /******************/ static UA_StatusCode addOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node, const UA_AddReferencesItem *item) { return UA_Node_addReference(node, item); } static UA_StatusCode deleteOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node, const UA_DeleteReferencesItem *item) { return UA_Node_deleteReference(node, item); } static void Operation_addReference(UA_Server *server, UA_Session *session, void *context, const UA_AddReferencesItem *item, UA_StatusCode *retval) { /* Do not check access for server */ if(session != &server->adminSession && server->config.accessControl.allowAddReference && !server->config.accessControl. allowAddReference(server, &server->config.accessControl, &session->sessionId, session->sessionHandle, item)) { *retval = UA_STATUSCODE_BADUSERACCESSDENIED; return; } /* Currently no expandednodeids are allowed */ if(item->targetServerUri.length > 0) { *retval = UA_STATUSCODE_BADNOTIMPLEMENTED; return; } /* Add the first direction */ *retval = UA_Server_editNode(server, session, &item->sourceNodeId, (UA_EditNodeCallback)addOneWayReference, /* cast away const because callback uses const anyway */ (UA_AddReferencesItem *)(uintptr_t)item); UA_Boolean firstExisted = false; if(*retval == UA_STATUSCODE_BADDUPLICATEREFERENCENOTALLOWED) { *retval = UA_STATUSCODE_GOOD; firstExisted = true; } else if(*retval != UA_STATUSCODE_GOOD) return; /* Add the second direction */ UA_AddReferencesItem secondItem; UA_AddReferencesItem_init(&secondItem); secondItem.sourceNodeId = item->targetNodeId.nodeId; secondItem.referenceTypeId = item->referenceTypeId; secondItem.isForward = !item->isForward; secondItem.targetNodeId.nodeId = item->sourceNodeId; /* keep default secondItem.targetNodeClass = UA_NODECLASS_UNSPECIFIED */ *retval = UA_Server_editNode(server, session, &secondItem.sourceNodeId, (UA_EditNodeCallback)addOneWayReference, &secondItem); /* remove reference if the second direction failed */ UA_Boolean secondExisted = false; if(*retval == UA_STATUSCODE_BADDUPLICATEREFERENCENOTALLOWED) { *retval = UA_STATUSCODE_GOOD; secondExisted = true; } else if(*retval != UA_STATUSCODE_GOOD && !firstExisted) { UA_DeleteReferencesItem deleteItem; deleteItem.sourceNodeId = item->sourceNodeId; deleteItem.referenceTypeId = item->referenceTypeId; deleteItem.isForward = item->isForward; deleteItem.targetNodeId = item->targetNodeId; deleteItem.deleteBidirectional = false; /* ignore returned status code */ UA_Server_editNode(server, session, &item->sourceNodeId, (UA_EditNodeCallback)deleteOneWayReference, &deleteItem); } /* Calculate common duplicate reference not allowed result and set bad result * if BOTH directions already existed */ if(firstExisted && secondExisted) *retval = UA_STATUSCODE_BADDUPLICATEREFERENCENOTALLOWED; } void Service_AddReferences(UA_Server *server, UA_Session *session, const UA_AddReferencesRequest *request, UA_AddReferencesResponse *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing AddReferencesRequest"); if(server->config.maxNodesPerNodeManagement != 0 && request->referencesToAddSize > server->config.maxNodesPerNodeManagement) { response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS; return; } response->responseHeader.serviceResult = UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_addReference, NULL, &request->referencesToAddSize, &UA_TYPES[UA_TYPES_ADDREFERENCESITEM], &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]); } UA_StatusCode UA_Server_addReference(UA_Server *server, const UA_NodeId sourceId, const UA_NodeId refTypeId, const UA_ExpandedNodeId targetId, UA_Boolean isForward) { UA_AddReferencesItem item; UA_AddReferencesItem_init(&item); item.sourceNodeId = sourceId; item.referenceTypeId = refTypeId; item.isForward = isForward; item.targetNodeId = targetId; UA_StatusCode retval = UA_STATUSCODE_GOOD; Operation_addReference(server, &server->adminSession, NULL, &item, &retval); return retval; } /*********************/ /* Delete References */ /*********************/ static void Operation_deleteReference(UA_Server *server, UA_Session *session, void *context, const UA_DeleteReferencesItem *item, UA_StatusCode *retval) { /* Do not check access for server */ if(session != &server->adminSession && server->config.accessControl.allowDeleteReference && !server->config.accessControl.allowDeleteReference(server, &server->config.accessControl, &session->sessionId, session->sessionHandle, item)) { *retval = UA_STATUSCODE_BADUSERACCESSDENIED; return; } // TODO: Check consistency constraints, remove the references. *retval = UA_Server_editNode(server, session, &item->sourceNodeId, (UA_EditNodeCallback)deleteOneWayReference, /* cast away const qualifier because callback uses it anyway */ (UA_DeleteReferencesItem *)(uintptr_t)item); if(*retval != UA_STATUSCODE_GOOD) return; if(!item->deleteBidirectional || item->targetNodeId.serverIndex != 0) return; UA_DeleteReferencesItem secondItem; UA_DeleteReferencesItem_init(&secondItem); secondItem.isForward = !item->isForward; secondItem.sourceNodeId = item->targetNodeId.nodeId; secondItem.targetNodeId.nodeId = item->sourceNodeId; secondItem.referenceTypeId = item->referenceTypeId; *retval = UA_Server_editNode(server, session, &secondItem.sourceNodeId, (UA_EditNodeCallback)deleteOneWayReference, &secondItem); } void Service_DeleteReferences(UA_Server *server, UA_Session *session, const UA_DeleteReferencesRequest *request, UA_DeleteReferencesResponse *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing DeleteReferencesRequest"); if(server->config.maxNodesPerNodeManagement != 0 && request->referencesToDeleteSize > server->config.maxNodesPerNodeManagement) { response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS; return; } response->responseHeader.serviceResult = UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_deleteReference, NULL, &request->referencesToDeleteSize, &UA_TYPES[UA_TYPES_DELETEREFERENCESITEM], &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]); } UA_StatusCode UA_Server_deleteReference(UA_Server *server, const UA_NodeId sourceNodeId, const UA_NodeId referenceTypeId, UA_Boolean isForward, const UA_ExpandedNodeId targetNodeId, UA_Boolean deleteBidirectional) { UA_DeleteReferencesItem item; item.sourceNodeId = sourceNodeId; item.referenceTypeId = referenceTypeId; item.isForward = isForward; item.targetNodeId = targetNodeId; item.deleteBidirectional = deleteBidirectional; UA_StatusCode retval = UA_STATUSCODE_GOOD; Operation_deleteReference(server, &server->adminSession, NULL, &item, &retval); return retval; } /**********************/ /* Set Value Callback */ /**********************/ static UA_StatusCode setValueCallback(UA_Server *server, UA_Session *session, UA_VariableNode *node, const UA_ValueCallback *callback) { if(node->nodeClass != UA_NODECLASS_VARIABLE) return UA_STATUSCODE_BADNODECLASSINVALID; node->value.data.callback = *callback; return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Server_setVariableNode_valueCallback(UA_Server *server, const UA_NodeId nodeId, const UA_ValueCallback callback) { return UA_Server_editNode(server, &server->adminSession, &nodeId, (UA_EditNodeCallback)setValueCallback, /* cast away const because callback uses const anyway */ (UA_ValueCallback *)(uintptr_t) &callback); } /***************************************************/ /* Special Handling of Variables with Data Sources */ /***************************************************/ UA_StatusCode UA_Server_addDataSourceVariableNode(UA_Server *server, const UA_NodeId requestedNewNodeId, const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId, const UA_QualifiedName browseName, const UA_NodeId typeDefinition, const UA_VariableAttributes attr, const UA_DataSource dataSource, void *nodeContext, UA_NodeId *outNewNodeId) { UA_AddNodesItem item; UA_AddNodesItem_init(&item); item.nodeClass = UA_NODECLASS_VARIABLE; item.requestedNewNodeId.nodeId = requestedNewNodeId; item.browseName = browseName; UA_ExpandedNodeId typeDefinitionId; UA_ExpandedNodeId_init(&typeDefinitionId); typeDefinitionId.nodeId = typeDefinition; item.typeDefinition = typeDefinitionId; item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE; item.nodeAttributes.content.decoded.data = (void*)(uintptr_t)&attr; item.nodeAttributes.content.decoded.type = &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES]; UA_NodeId newNodeId; if(!outNewNodeId) { newNodeId = UA_NODEID_NULL; outNewNodeId = &newNodeId; } /* Create the node and add it to the nodestore */ UA_StatusCode retval = AddNode_raw(server, &server->adminSession, nodeContext, &item, outNewNodeId); if(retval != UA_STATUSCODE_GOOD) goto cleanup; /* Set the data source */ retval = UA_Server_setVariableNode_dataSource(server, *outNewNodeId, dataSource); if(retval != UA_STATUSCODE_GOOD) goto cleanup; /* Typecheck and add references to parent and type definition */ retval = AddNode_addRefs(server, &server->adminSession, outNewNodeId, &parentNodeId, &referenceTypeId, &typeDefinition); if(retval != UA_STATUSCODE_GOOD) goto cleanup; /* Call the constructors */ retval = AddNode_finish(server, &server->adminSession, outNewNodeId); cleanup: if(outNewNodeId == &newNodeId) UA_NodeId_deleteMembers(&newNodeId); return retval; } static UA_StatusCode setDataSource(UA_Server *server, UA_Session *session, UA_VariableNode* node, const UA_DataSource *dataSource) { if(node->nodeClass != UA_NODECLASS_VARIABLE) return UA_STATUSCODE_BADNODECLASSINVALID; if(node->valueSource == UA_VALUESOURCE_DATA) UA_DataValue_deleteMembers(&node->value.data.value); node->value.dataSource = *dataSource; node->valueSource = UA_VALUESOURCE_DATASOURCE; return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Server_setVariableNode_dataSource(UA_Server *server, const UA_NodeId nodeId, const UA_DataSource dataSource) { return UA_Server_editNode(server, &server->adminSession, &nodeId, (UA_EditNodeCallback)setDataSource, /* casting away const because callback casts it back anyway */ (UA_DataSource *) (uintptr_t)&dataSource); } /************************************/ /* Special Handling of Method Nodes */ /************************************/ #ifdef UA_ENABLE_METHODCALLS static const UA_NodeId hasproperty = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASPROPERTY}}; static const UA_NodeId propertytype = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_PROPERTYTYPE}}; static UA_StatusCode UA_Server_addMethodNodeEx_finish(UA_Server *server, const UA_NodeId nodeId, UA_MethodCallback method, const size_t inputArgumentsSize, const UA_Argument *inputArguments, const UA_NodeId inputArgumentsRequestedNewNodeId, UA_NodeId *inputArgumentsOutNewNodeId, const size_t outputArgumentsSize, const UA_Argument *outputArguments, const UA_NodeId outputArgumentsRequestedNewNodeId, UA_NodeId *outputArgumentsOutNewNodeId) { /* Browse to see which argument nodes exist */ UA_BrowseDescription bd; UA_BrowseDescription_init(&bd); bd.nodeId = nodeId; bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY); bd.includeSubtypes = false; bd.browseDirection = UA_BROWSEDIRECTION_FORWARD; bd.nodeClassMask = UA_NODECLASS_VARIABLE; bd.resultMask = UA_BROWSERESULTMASK_BROWSENAME; UA_BrowseResult br; UA_BrowseResult_init(&br); UA_UInt32 maxrefs = 0; Operation_Browse(server, &server->adminSession, &maxrefs, &bd, &br); UA_StatusCode retval = br.statusCode; if(retval != UA_STATUSCODE_GOOD) { UA_Server_deleteNode(server, nodeId, true); UA_BrowseResult_deleteMembers(&br); return retval; } /* Filter out the argument nodes */ UA_NodeId inputArgsId = UA_NODEID_NULL; UA_NodeId outputArgsId = UA_NODEID_NULL; const UA_QualifiedName inputArgsName = UA_QUALIFIEDNAME(0, "InputArguments"); const UA_QualifiedName outputArgsName = UA_QUALIFIEDNAME(0, "OutputArguments"); for(size_t i = 0; i < br.referencesSize; i++) { UA_ReferenceDescription *rd = &br.references[i]; if(rd->browseName.namespaceIndex == 0 && UA_String_equal(&rd->browseName.name, &inputArgsName.name)) inputArgsId = rd->nodeId.nodeId; else if(rd->browseName.namespaceIndex == 0 && UA_String_equal(&rd->browseName.name, &outputArgsName.name)) outputArgsId = rd->nodeId.nodeId; } /* Add the Input Arguments VariableNode */ if(inputArgumentsSize > 0 && UA_NodeId_isNull(&inputArgsId)) { UA_VariableAttributes attr = UA_VariableAttributes_default; char *name = "InputArguments"; attr.displayName = UA_LOCALIZEDTEXT("", name); attr.dataType = UA_TYPES[UA_TYPES_ARGUMENT].typeId; attr.valueRank = UA_VALUERANK_ONE_DIMENSION; UA_UInt32 inputArgsSize32 = (UA_UInt32)inputArgumentsSize; attr.arrayDimensions = &inputArgsSize32; attr.arrayDimensionsSize = 1; UA_Variant_setArray(&attr.value, (void *)(uintptr_t)inputArguments, inputArgumentsSize, &UA_TYPES[UA_TYPES_ARGUMENT]); retval = UA_Server_addVariableNode(server, inputArgumentsRequestedNewNodeId, nodeId, hasproperty, UA_QUALIFIEDNAME(0, name), propertytype, attr, NULL, &inputArgsId); if(retval != UA_STATUSCODE_GOOD) goto error; } /* Add the Output Arguments VariableNode */ if(outputArgumentsSize > 0 && UA_NodeId_isNull(&outputArgsId)) { UA_VariableAttributes attr = UA_VariableAttributes_default; char *name = "OutputArguments"; attr.displayName = UA_LOCALIZEDTEXT("", name); attr.dataType = UA_TYPES[UA_TYPES_ARGUMENT].typeId; attr.valueRank = UA_VALUERANK_ONE_DIMENSION; UA_UInt32 outputArgsSize32 = (UA_UInt32)outputArgumentsSize; attr.arrayDimensions = &outputArgsSize32; attr.arrayDimensionsSize = 1; UA_Variant_setArray(&attr.value, (void *)(uintptr_t)outputArguments, outputArgumentsSize, &UA_TYPES[UA_TYPES_ARGUMENT]); retval = UA_Server_addVariableNode(server, outputArgumentsRequestedNewNodeId, nodeId, hasproperty, UA_QUALIFIEDNAME(0, name), propertytype, attr, NULL, &outputArgsId); if(retval != UA_STATUSCODE_GOOD) goto error; } retval = UA_Server_setMethodNode_callback(server, nodeId, method); if(retval != UA_STATUSCODE_GOOD) goto error; /* Call finish to add the parent reference */ retval = AddNode_finish(server, &server->adminSession, &nodeId); if(retval != UA_STATUSCODE_GOOD) goto error; if(inputArgumentsOutNewNodeId != NULL) { UA_NodeId_copy(&inputArgsId, inputArgumentsOutNewNodeId); } if(outputArgumentsOutNewNodeId != NULL) { UA_NodeId_copy(&outputArgsId, outputArgumentsOutNewNodeId); } UA_BrowseResult_deleteMembers(&br); return retval; error: UA_Server_deleteNode(server, nodeId, true); UA_Server_deleteNode(server, inputArgsId, true); UA_Server_deleteNode(server, outputArgsId, true); UA_BrowseResult_deleteMembers(&br); return retval; } UA_StatusCode UA_Server_addMethodNode_finish(UA_Server *server, const UA_NodeId nodeId, UA_MethodCallback method, size_t inputArgumentsSize, const UA_Argument* inputArguments, size_t outputArgumentsSize, const UA_Argument* outputArguments) { return UA_Server_addMethodNodeEx_finish(server, nodeId, method, inputArgumentsSize, inputArguments, UA_NODEID_NULL, NULL, outputArgumentsSize, outputArguments, UA_NODEID_NULL, NULL); } UA_StatusCode UA_Server_addMethodNodeEx(UA_Server *server, const UA_NodeId requestedNewNodeId, const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId, const UA_QualifiedName browseName, const UA_MethodAttributes attr, UA_MethodCallback method, size_t inputArgumentsSize, const UA_Argument *inputArguments, const UA_NodeId inputArgumentsRequestedNewNodeId, UA_NodeId *inputArgumentsOutNewNodeId, size_t outputArgumentsSize, const UA_Argument *outputArguments, const UA_NodeId outputArgumentsRequestedNewNodeId, UA_NodeId *outputArgumentsOutNewNodeId, void *nodeContext, UA_NodeId *outNewNodeId) { UA_AddNodesItem item; UA_AddNodesItem_init(&item); item.nodeClass = UA_NODECLASS_METHOD; item.requestedNewNodeId.nodeId = requestedNewNodeId; item.browseName = browseName; item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE; item.nodeAttributes.content.decoded.data = (void*)(uintptr_t)&attr; item.nodeAttributes.content.decoded.type = &UA_TYPES[UA_TYPES_METHODATTRIBUTES]; UA_NodeId newId; if(!outNewNodeId) { UA_NodeId_init(&newId); outNewNodeId = &newId; } UA_StatusCode retval = Operation_addNode_begin(server, &server->adminSession, nodeContext, &item, &parentNodeId, &referenceTypeId, outNewNodeId); if(retval != UA_STATUSCODE_GOOD) return retval; retval = UA_Server_addMethodNodeEx_finish(server, *outNewNodeId, method, inputArgumentsSize, inputArguments, inputArgumentsRequestedNewNodeId, inputArgumentsOutNewNodeId, outputArgumentsSize, outputArguments, outputArgumentsRequestedNewNodeId, outputArgumentsOutNewNodeId); if(outNewNodeId == &newId) UA_NodeId_deleteMembers(&newId); return retval; } static UA_StatusCode editMethodCallback(UA_Server *server, UA_Session* session, UA_Node* node, void* handle) { if(node->nodeClass != UA_NODECLASS_METHOD) return UA_STATUSCODE_BADNODECLASSINVALID; UA_MethodNode *mnode = (UA_MethodNode*) node; mnode->method = (UA_MethodCallback)(uintptr_t)handle; return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Server_setMethodNode_callback(UA_Server *server, const UA_NodeId methodNodeId, UA_MethodCallback methodCallback) { return UA_Server_editNode(server, &server->adminSession, &methodNodeId, (UA_EditNodeCallback)editMethodCallback, (void*)(uintptr_t)methodCallback); } #endif /************************/ /* Lifecycle Management */ /************************/ static UA_StatusCode setNodeTypeLifecycle(UA_Server *server, UA_Session *session, UA_Node* node, UA_NodeTypeLifecycle *lifecycle) { if(node->nodeClass == UA_NODECLASS_OBJECTTYPE) { UA_ObjectTypeNode *ot = (UA_ObjectTypeNode*)node; ot->lifecycle = *lifecycle; return UA_STATUSCODE_GOOD; } if(node->nodeClass == UA_NODECLASS_VARIABLETYPE) { UA_VariableTypeNode *vt = (UA_VariableTypeNode*)node; vt->lifecycle = *lifecycle; return UA_STATUSCODE_GOOD; } return UA_STATUSCODE_BADNODECLASSINVALID; } UA_StatusCode UA_Server_setNodeTypeLifecycle(UA_Server *server, UA_NodeId nodeId, UA_NodeTypeLifecycle lifecycle) { return UA_Server_editNode(server, &server->adminSession, &nodeId, (UA_EditNodeCallback)setNodeTypeLifecycle, &lifecycle); } /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_services_discovery_multicast.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2017 (c) Thomas Stalder, Blue Time Concept SA */ #if defined(UA_ENABLE_DISCOVERY) && defined(UA_ENABLE_DISCOVERY_MULTICAST) #ifdef UA_ENABLE_MULTITHREADING static void * multicastWorkerLoop(UA_Server *server) { struct timeval next_sleep = {.tv_sec = 0, .tv_usec = 0}; volatile UA_Boolean *running = &server->discoveryManager.mdnsRunning; fd_set fds; while(*running) { FD_ZERO(&fds); UA_fd_set(server->discoveryManager.mdnsSocket, &fds); select(server->discoveryManager.mdnsSocket + 1, &fds, 0, 0, &next_sleep); if(!*running) break; unsigned short retVal = mdnsd_step(server->discoveryManager.mdnsDaemon, server->discoveryManager.mdnsSocket, FD_ISSET(server->discoveryManager.mdnsSocket, &fds), true, &next_sleep); if(retVal == 1) { UA_LOG_SOCKET_ERRNO_WRAP( UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Multicast error: Can not read from socket. %s", errno_str)); break; } else if (retVal == 2) { UA_LOG_SOCKET_ERRNO_WRAP( UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "Multicast error: Can not write to socket. %s", errno_str)); break; } } return NULL; } static UA_StatusCode multicastListenStart(UA_Server* server) { int err = pthread_create(&server->discoveryManager.mdnsThread, NULL, (void* (*)(void*))multicastWorkerLoop, server); if(err != 0) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Multicast error: Can not create multicast thread."); return UA_STATUSCODE_BADUNEXPECTEDERROR; } return UA_STATUSCODE_GOOD; } static UA_StatusCode multicastListenStop(UA_Server* server) { mdnsd_shutdown(server->discoveryManager.mdnsDaemon); // wake up select if (write(server->discoveryManager.mdnsSocket, "\0", 1)) { // TODO: if makes no sense here? } // TODO: move to arch? if (pthread_join(server->discoveryManager.mdnsThread, NULL)) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Multicast error: Can not stop thread."); return UA_STATUSCODE_BADUNEXPECTEDERROR; } return UA_STATUSCODE_BADNOTIMPLEMENTED; } # endif /* UA_ENABLE_MULTITHREADING */ static UA_StatusCode addMdnsRecordForNetworkLayer(UA_Server *server, const UA_String *appName, const UA_ServerNetworkLayer* nl) { UA_String hostname = UA_STRING_NULL; UA_UInt16 port = 4840; UA_String path = UA_STRING_NULL; UA_StatusCode retval = UA_parseEndpointUrl(&nl->discoveryUrl, &hostname, &port, &path); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_NETWORK, "Server url is invalid: %.*s", (int)nl->discoveryUrl.length, nl->discoveryUrl.data); return retval; } retval = UA_Discovery_addRecord(server, appName, &hostname, port, &path, UA_DISCOVERY_TCP, true, server->config.discovery.mdns.serverCapabilities, server->config.discovery.mdns.serverCapabilitiesSize); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_NETWORK, "Cannot add mDNS Record: %s", UA_StatusCode_name(retval)); return retval; } return UA_STATUSCODE_GOOD; } void startMulticastDiscoveryServer(UA_Server *server) { UA_String *appName = &server->config.discovery.mdns.mdnsServerName; for(size_t i = 0; i < server->config.networkLayersSize; i++) addMdnsRecordForNetworkLayer(server, appName, &server->config.networkLayers[i]); /* find any other server on the net */ UA_Discovery_multicastQuery(server); # ifdef UA_ENABLE_MULTITHREADING multicastListenStart(server); # endif } void stopMulticastDiscoveryServer(UA_Server *server) { if (!server->discoveryManager.mdnsDaemon) return; char hostname[256]; if(UA_gethostname(hostname, 255) == 0) { UA_String hnString = UA_STRING(hostname); UA_Discovery_removeRecord(server, &server->config.discovery.mdns.mdnsServerName, &hnString, 4840, true); } else { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Could not get hostname for multicast discovery."); } # ifdef UA_ENABLE_MULTITHREADING multicastListenStop(server); # else // send out last package with TTL = 0 iterateMulticastDiscoveryServer(server, NULL, false); # endif } /* All filter criteria must be fulfilled */ static UA_Boolean filterServerRecord(size_t serverCapabilityFilterSize, UA_String *serverCapabilityFilter, serverOnNetwork_list_entry* current) { for(size_t i = 0; i < serverCapabilityFilterSize; i++) { for(size_t j = 0; j < current->serverOnNetwork.serverCapabilitiesSize; j++) if(!UA_String_equal(&serverCapabilityFilter[i], ¤t->serverOnNetwork.serverCapabilities[j])) return false; } return true; } void Service_FindServersOnNetwork(UA_Server *server, UA_Session *session, const UA_FindServersOnNetworkRequest *request, UA_FindServersOnNetworkResponse *response) { if (!server->config.discovery.mdnsEnable) { response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTIMPLEMENTED; return; } /* Set LastCounterResetTime */ UA_DateTime_copy(&server->discoveryManager.serverOnNetworkRecordIdLastReset, &response->lastCounterResetTime); /* Compute the max number of records to return */ UA_UInt32 recordCount = 0; if(request->startingRecordId < server->discoveryManager.serverOnNetworkRecordIdCounter) recordCount = server->discoveryManager.serverOnNetworkRecordIdCounter - request->startingRecordId; if(request->maxRecordsToReturn && recordCount > request->maxRecordsToReturn) recordCount = UA_MIN(recordCount, request->maxRecordsToReturn); if(recordCount == 0) { response->serversSize = 0; return; } /* Iterate over all records and add to filtered list */ UA_UInt32 filteredCount = 0; UA_STACKARRAY(UA_ServerOnNetwork*, filtered, recordCount); serverOnNetwork_list_entry* current; LIST_FOREACH(current, &server->discoveryManager.serverOnNetwork, pointers) { if(filteredCount >= recordCount) break; if(current->serverOnNetwork.recordId < request->startingRecordId) continue; if(!filterServerRecord(request->serverCapabilityFilterSize, request->serverCapabilityFilter, current)) continue; filtered[filteredCount++] = ¤t->serverOnNetwork; } if(filteredCount == 0) return; /* Allocate the array for the response */ response->servers = (UA_ServerOnNetwork*)UA_malloc(sizeof(UA_ServerOnNetwork)*filteredCount); if(!response->servers) { response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY; return; } response->serversSize = filteredCount; /* Copy the server names */ for(size_t i = 0; i < filteredCount; i++) UA_ServerOnNetwork_copy(filtered[i], &response->servers[filteredCount-i-1]); } void UA_Server_updateMdnsForDiscoveryUrl(UA_Server *server, const UA_String *serverName, const UA_MdnsDiscoveryConfiguration *mdnsConfig, const UA_String *discoveryUrl, UA_Boolean isOnline, UA_Boolean updateTxt) { UA_String hostname = UA_STRING_NULL; UA_UInt16 port = 4840; UA_String path = UA_STRING_NULL; UA_StatusCode retval = UA_parseEndpointUrl(discoveryUrl, &hostname, &port, &path); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_NETWORK, "Server url invalid: %.*s", (int)discoveryUrl->length, discoveryUrl->data); return; } if(!isOnline) { UA_StatusCode removeRetval = UA_Discovery_removeRecord(server, serverName, &hostname, port, updateTxt); if(removeRetval != UA_STATUSCODE_GOOD) UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Could not remove mDNS record for hostname %.*s.", (int)serverName->length, serverName->data); return; } UA_String *capabilities = NULL; size_t capabilitiesSize = 0; if(mdnsConfig) { capabilities = mdnsConfig->serverCapabilities; capabilitiesSize = mdnsConfig->serverCapabilitiesSize; } UA_StatusCode addRetval = UA_Discovery_addRecord(server, serverName, &hostname, port, &path, UA_DISCOVERY_TCP, updateTxt, capabilities, capabilitiesSize); if(addRetval != UA_STATUSCODE_GOOD) UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Could not add mDNS record for hostname %.*s.", (int)serverName->length, serverName->data); } void UA_Server_setServerOnNetworkCallback(UA_Server *server, UA_Server_serverOnNetworkCallback cb, void* data) { server->discoveryManager.serverOnNetworkCallback = cb; server->discoveryManager.serverOnNetworkCallbackData = data; } static void UA_Discovery_multicastConflict(char *name, int type, void *arg) { // cppcheck-suppress unreadVariable UA_Server *server = (UA_Server*) arg; UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Multicast DNS name conflict detected: " "'%s' for type %d", name, type); } /* Create a service domain with the format [servername]-[hostname]._opcua-tcp._tcp.local. */ static void createFullServiceDomain(char *outServiceDomain, size_t maxLen, const UA_String *servername, const UA_String *hostname) { size_t hostnameLen = hostname->length; size_t servernameLen = servername->length; maxLen -= 24; /* the length we have remaining before the opc ua postfix and * the trailing zero */ /* Can we use hostname and servername with full length? */ if(hostnameLen + servernameLen + 1 > maxLen) { if(servernameLen + 2 > maxLen) { servernameLen = maxLen; hostnameLen = 0; } else { hostnameLen = maxLen - servernameLen - 1; } } size_t offset = 0; if (hostnameLen > 0) { UA_snprintf(outServiceDomain, maxLen + 1, "%.*s-%.*s", (int) servernameLen, (char *) servername->data, (int) hostnameLen, (char *) hostname->data); offset = servernameLen + hostnameLen + 1; } else { UA_snprintf(outServiceDomain, maxLen + 1, "%.*s", (int) servernameLen, (char *) servername->data); offset = servernameLen; } UA_snprintf(&outServiceDomain[offset], 24, "._opcua-tcp._tcp.local."); } /* Check if mDNS already has an entry for given hostname and port combination */ static UA_Boolean UA_Discovery_recordExists(UA_Server* server, const char* fullServiceDomain, unsigned short port, const UA_DiscoveryProtocol protocol) { // [servername]-[hostname]._opcua-tcp._tcp.local. 86400 IN SRV 0 5 port [hostname]. mdns_record_t *r = mdnsd_get_published(server->discoveryManager.mdnsDaemon, fullServiceDomain); while(r) { const mdns_answer_t *data = mdnsd_record_data(r); if(data->type == QTYPE_SRV && (port == 0 || data->srv.port == port)) return true; r = mdnsd_record_next(r); } return false; } static int discovery_multicastQueryAnswer(mdns_answer_t *a, void *arg) { UA_Server *server = (UA_Server*) arg; if(a->type != QTYPE_PTR) return 0; if(a->rdname == NULL) return 0; /* Skip, if we already know about this server */ UA_Boolean exists = UA_Discovery_recordExists(server, a->rdname, 0, UA_DISCOVERY_TCP); if(exists == true) return 0; if(mdnsd_has_query(server->discoveryManager.mdnsDaemon, a->rdname)) return 0; UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "mDNS send query for: %s SRV&TXT %s", a->name, a->rdname); mdnsd_query(server->discoveryManager.mdnsDaemon, a->rdname, QTYPE_SRV, discovery_multicastQueryAnswer, server); mdnsd_query(server->discoveryManager.mdnsDaemon, a->rdname, QTYPE_TXT, discovery_multicastQueryAnswer, server); return 0; } UA_StatusCode UA_Discovery_multicastQuery(UA_Server* server) { mdnsd_query(server->discoveryManager.mdnsDaemon, "_opcua-tcp._tcp.local.", QTYPE_PTR,discovery_multicastQueryAnswer, server); return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Discovery_addRecord(UA_Server *server, const UA_String *servername, const UA_String *hostname, UA_UInt16 port, const UA_String *path, const UA_DiscoveryProtocol protocol, UA_Boolean createTxt, const UA_String* capabilites, const size_t capabilitiesSize) { // we assume that the hostname is not an IP address, but a valid domain name // It is required by the OPC UA spec (see Part 12, DiscoveryURL to DNS SRV mapping) // to always use the hostname instead of the IP address if(capabilitiesSize > 0 && !capabilites) return UA_STATUSCODE_BADINVALIDARGUMENT; size_t hostnameLen = hostname->length; size_t servernameLen = servername->length; if(hostnameLen == 0 || servernameLen == 0) return UA_STATUSCODE_BADOUTOFRANGE; // use a limit for the hostname length to make sure full string fits into 63 // chars (limited by DNS spec) if(hostnameLen+servernameLen + 1 > 63) { // include dash between servername-hostname UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Multicast DNS: Combination of hostname+servername exceeds " "maximum of 62 chars. It will be truncated."); } else if(hostnameLen > 63) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Multicast DNS: Hostname length exceeds maximum of 63 chars. " "It will be truncated."); } if(!server->discoveryManager.mdnsMainSrvAdded) { mdns_record_t *r = mdnsd_shared(server->discoveryManager.mdnsDaemon, "_services._dns-sd._udp.local.", QTYPE_PTR, 600); mdnsd_set_host(server->discoveryManager.mdnsDaemon, r, "_opcua-tcp._tcp.local."); server->discoveryManager.mdnsMainSrvAdded = true; } // [servername]-[hostname]._opcua-tcp._tcp.local. char fullServiceDomain[63+24]; createFullServiceDomain(fullServiceDomain, 63+24, servername, hostname); UA_Boolean exists = UA_Discovery_recordExists(server, fullServiceDomain, port, protocol); if(exists == true) return UA_STATUSCODE_GOOD; UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "Multicast DNS: add record for domain: %s", fullServiceDomain); // _services._dns-sd._udp.local. PTR _opcua-tcp._tcp.local // check if there is already a PTR entry for the given service. // _opcua-tcp._tcp.local. PTR [servername]-[hostname]._opcua-tcp._tcp.local. mdns_record_t *r = mdns_find_record(server->discoveryManager.mdnsDaemon, QTYPE_PTR, "_opcua-tcp._tcp.local.", fullServiceDomain); if(!r) { r = mdnsd_shared(server->discoveryManager.mdnsDaemon, "_opcua-tcp._tcp.local.", QTYPE_PTR, 600); mdnsd_set_host(server->discoveryManager.mdnsDaemon, r, fullServiceDomain); } /* The first 63 characters of the hostname (or less) */ size_t maxHostnameLen = UA_MIN(hostnameLen, 63); char localDomain[65]; memcpy(localDomain, hostname->data, maxHostnameLen); localDomain[maxHostnameLen] = '.'; localDomain[maxHostnameLen+1] = '\0'; // [servername]-[hostname]._opcua-tcp._tcp.local. 86400 IN SRV 0 5 port [hostname]. r = mdnsd_unique(server->discoveryManager.mdnsDaemon, fullServiceDomain, QTYPE_SRV, 600, UA_Discovery_multicastConflict, server); mdnsd_set_srv(server->discoveryManager.mdnsDaemon, r, 0, 0, port, localDomain); // A/AAAA record for all ip addresses. // [servername]-[hostname]._opcua-tcp._tcp.local. A [ip]. // [hostname]. A [ip]. mdns_set_address_record(server, fullServiceDomain, localDomain); // TXT record: [servername]-[hostname]._opcua-tcp._tcp.local. TXT path=/ caps=NA,DA,... UA_STACKARRAY(char, pathChars, path->length + 1); if(createTxt) { if(path->length > 0) memcpy(pathChars, path->data, path->length); pathChars[path->length] = 0; mdns_create_txt(server, fullServiceDomain, pathChars, capabilites, capabilitiesSize, UA_Discovery_multicastConflict); } return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Discovery_removeRecord(UA_Server *server, const UA_String *servername, const UA_String *hostname, UA_UInt16 port, UA_Boolean removeTxt) { // use a limit for the hostname length to make sure full string fits into 63 // chars (limited by DNS spec) size_t hostnameLen = hostname->length; size_t servernameLen = servername->length; if(hostnameLen == 0 || servernameLen == 0) return UA_STATUSCODE_BADOUTOFRANGE; if(hostnameLen+servernameLen+1 > 63) { // include dash between servername-hostname UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Multicast DNS: Combination of hostname+servername exceeds " "maximum of 62 chars. It will be truncated."); } // [servername]-[hostname]._opcua-tcp._tcp.local. char fullServiceDomain[63 + 24]; createFullServiceDomain(fullServiceDomain, 63+24, servername, hostname); UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "Multicast DNS: remove record for domain: %s", fullServiceDomain); // _opcua-tcp._tcp.local. PTR [servername]-[hostname]._opcua-tcp._tcp.local. mdns_record_t *r = mdns_find_record(server->discoveryManager.mdnsDaemon, QTYPE_PTR, "_opcua-tcp._tcp.local.", fullServiceDomain); if(!r) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Multicast DNS: could not remove record. " "PTR Record not found for domain: %s", fullServiceDomain); return UA_STATUSCODE_BADNOTHINGTODO; } mdnsd_done(server->discoveryManager.mdnsDaemon, r); // looks for [servername]-[hostname]._opcua-tcp._tcp.local. 86400 IN SRV 0 5 port hostname.local. // and TXT record: [servername]-[hostname]._opcua-tcp._tcp.local. TXT path=/ caps=NA,DA,... // and A record: [servername]-[hostname]._opcua-tcp._tcp.local. A [ip] mdns_record_t *r2 = mdnsd_get_published(server->discoveryManager.mdnsDaemon, fullServiceDomain); if(!r2) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Multicast DNS: could not remove record. Record not " "found for domain: %s", fullServiceDomain); return UA_STATUSCODE_BADNOTHINGTODO; } while(r2) { const mdns_answer_t *data = mdnsd_record_data(r2); mdns_record_t *next = mdnsd_record_next(r2); if((removeTxt && data->type == QTYPE_TXT) || (removeTxt && data->type == QTYPE_A) || data->srv.port == port) { mdnsd_done(server->discoveryManager.mdnsDaemon, r2); } r2 = next; } return UA_STATUSCODE_GOOD; } UA_StatusCode iterateMulticastDiscoveryServer(UA_Server* server, UA_DateTime *nextRepeat, UA_Boolean processIn) { struct timeval next_sleep = { 0, 0 }; unsigned short retval = mdnsd_step(server->discoveryManager.mdnsDaemon, (int)server->discoveryManager.mdnsSocket, processIn, true, &next_sleep); if(retval == 1) { UA_LOG_SOCKET_ERRNO_WRAP( UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "Multicast error: Can not read from socket. %s", errno_str)); return UA_STATUSCODE_BADNOCOMMUNICATION; } else if(retval == 2) { UA_LOG_SOCKET_ERRNO_WRAP( UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "Multicast error: Can not write to socket. %s", errno_str)); return UA_STATUSCODE_BADNOCOMMUNICATION; } if(nextRepeat) *nextRepeat = UA_DateTime_now() + (UA_DateTime)((next_sleep.tv_sec * UA_DATETIME_SEC) + (next_sleep.tv_usec * UA_DATETIME_USEC)); return UA_STATUSCODE_GOOD; } #endif /* defined(UA_ENABLE_DISCOVERY) && defined(UA_ENABLE_DISCOVERY_MULTICAST) */ /*********************************** amalgamated original file "/home/jvoe/open62541/src/client/ua_client.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2015-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2015-2016 (c) Sten Grüner * Copyright 2015-2016 (c) Chris Iatrou * Copyright 2015 (c) hfaham * Copyright 2015-2017 (c) Florian Palm * Copyright 2017-2018 (c) Thomas Stalder, Blue Time Concept SA * Copyright 2015 (c) Holger Jeromin * Copyright 2015 (c) Oleksiy Vasylyev * Copyright 2016 (c) TorbenD * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2016 (c) Lykurg * Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB * Copyright 2018 (c) Kalycito Infotech Private Limited */ #define STATUS_CODE_BAD_POINTER 0x01 /********************/ /* Client Lifecycle */ /********************/ static void UA_Client_init(UA_Client* client) { memset(client, 0, sizeof(UA_Client)); UA_SecureChannel_init(&client->channel); if(client->config.stateCallback) client->config.stateCallback(client, client->state); /* Catch error during async connection */ client->connectStatus = UA_STATUSCODE_GOOD; UA_Timer_init(&client->timer); UA_WorkQueue_init(&client->workQueue); } UA_Client * UA_Client_new() { UA_Client *client = (UA_Client*)UA_malloc(sizeof(UA_Client)); if(!client) return NULL; UA_Client_init(client); return client; } static void UA_ClientConfig_deleteMembers(UA_ClientConfig *config) { UA_ApplicationDescription_deleteMembers(&config->clientDescription); UA_ExtensionObject_deleteMembers(&config->userIdentityToken); UA_String_deleteMembers(&config->securityPolicyUri); UA_EndpointDescription_deleteMembers(&config->endpoint); UA_UserTokenPolicy_deleteMembers(&config->userTokenPolicy); if(config->certificateVerification.deleteMembers) config->certificateVerification.deleteMembers(&config->certificateVerification); /* Delete the SecurityPolicies */ if(config->securityPolicies == 0) return; for(size_t i = 0; i < config->securityPoliciesSize; i++) config->securityPolicies[i].deleteMembers(&config->securityPolicies[i]); UA_free(config->securityPolicies); config->securityPolicies = 0; } static void UA_Client_deleteMembers(UA_Client *client) { UA_Client_disconnect(client); /* Commented as UA_SecureChannel_deleteMembers already done * in UA_Client_disconnect function */ //UA_SecureChannel_deleteMembersCleanup(&client->channel); if (client->connection.free) client->connection.free(&client->connection); UA_Connection_deleteMembers(&client->connection); UA_NodeId_deleteMembers(&client->authenticationToken); UA_String_deleteMembers(&client->endpointUrl); /* Delete the async service calls */ UA_Client_AsyncService_removeAll(client, UA_STATUSCODE_BADSHUTDOWN); /* Delete the subscriptions */ #ifdef UA_ENABLE_SUBSCRIPTIONS UA_Client_Subscriptions_clean(client); #endif /* Delete the timed work */ UA_Timer_deleteMembers(&client->timer); /* Clean up the work queue */ UA_WorkQueue_cleanup(&client->workQueue); UA_ClientConfig_deleteMembers(&client->config); } void UA_Client_reset(UA_Client* client) { UA_Client_deleteMembers(client); UA_Client_init(client); } void UA_Client_delete(UA_Client* client) { UA_Client_deleteMembers(client); UA_free(client); } UA_ClientState UA_Client_getState(UA_Client *client) { return client->state; } UA_ClientConfig * UA_Client_getConfig(UA_Client *client) { if(!client) return NULL; return &client->config; } /****************/ /* Raw Services */ /****************/ /* For synchronous service calls. Execute async responses with a callback. When * the response with the correct requestId turns up, return it via the * SyncResponseDescription pointer. */ typedef struct { UA_Client *client; UA_Boolean received; UA_UInt32 requestId; void *response; const UA_DataType *responseType; } SyncResponseDescription; /* For both synchronous and asynchronous service calls */ static UA_StatusCode sendSymmetricServiceRequest(UA_Client *client, const void *request, const UA_DataType *requestType, UA_UInt32 *requestId) { /* Make sure we have a valid session */ UA_StatusCode retval = UA_STATUSCODE_GOOD; /* FIXME: this is just a dirty workaround. We need to rework some of the sync and async processing * FIXME: in the client. Currently a lot of stuff is semi broken and in dire need of cleaning up.*/ /*UA_StatusCode retval = openSecureChannel(client, true); if(retval != UA_STATUSCODE_GOOD) return retval;*/ /* Adjusting the request header. The const attribute is violated, but we * only touch the following members: */ UA_RequestHeader *rr = (UA_RequestHeader*)(uintptr_t)request; rr->authenticationToken = client->authenticationToken; /* cleaned up at the end */ rr->timestamp = UA_DateTime_now(); rr->requestHandle = ++client->requestHandle; /* Send the request */ UA_UInt32 rqId = ++client->requestId; UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Sending a request of type %i", requestType->typeId.identifier.numeric); if (client->channel.nextSecurityToken.tokenId != 0) // Change to the new security token if the secure channel has been renewed. UA_SecureChannel_revolveTokens(&client->channel); retval = UA_SecureChannel_sendSymmetricMessage(&client->channel, rqId, UA_MESSAGETYPE_MSG, rr, requestType); UA_NodeId_init(&rr->authenticationToken); /* Do not return the token to the user */ if(retval != UA_STATUSCODE_GOOD) return retval; *requestId = rqId; return UA_STATUSCODE_GOOD; } static const UA_NodeId serviceFaultId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_SERVICEFAULT_ENCODING_DEFAULTBINARY}}; /* Look for the async callback in the linked list, execute and delete it */ static UA_StatusCode processAsyncResponse(UA_Client *client, UA_UInt32 requestId, const UA_NodeId *responseTypeId, const UA_ByteString *responseMessage, size_t *offset) { /* Find the callback */ AsyncServiceCall *ac; LIST_FOREACH(ac, &client->asyncServiceCalls, pointers) { if(ac->requestId == requestId) break; } if(!ac) return UA_STATUSCODE_BADREQUESTHEADERINVALID; /* Allocate the response */ UA_STACKARRAY(UA_Byte, responseBuf, ac->responseType->memSize); void *response = (void*)(uintptr_t)&responseBuf[0]; /* workaround aliasing rules */ /* Verify the type of the response */ const UA_DataType *responseType = ac->responseType; const UA_NodeId expectedNodeId = UA_NODEID_NUMERIC(0, ac->responseType->binaryEncodingId); UA_StatusCode retval = UA_STATUSCODE_GOOD; if(!UA_NodeId_equal(responseTypeId, &expectedNodeId)) { UA_init(response, ac->responseType); if(UA_NodeId_equal(responseTypeId, &serviceFaultId)) { /* Decode as a ServiceFault, i.e. only the response header */ UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Received a ServiceFault response"); responseType = &UA_TYPES[UA_TYPES_SERVICEFAULT]; } else { /* Close the connection */ UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Reply contains the wrong service response"); retval = UA_STATUSCODE_BADCOMMUNICATIONERROR; goto process; } } /* Decode the response */ retval = UA_decodeBinary(responseMessage, offset, response, responseType, client->config.customDataTypes); process: if(retval != UA_STATUSCODE_GOOD) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Could not decode the response with id %u due to %s", requestId, UA_StatusCode_name(retval)); ((UA_ResponseHeader*)response)->serviceResult = retval; } else if(((UA_ResponseHeader*)response)->serviceResult != UA_STATUSCODE_GOOD) { /* Decode as a ServiceFault, i.e. only the response header */ UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "The ServiceResult has the StatusCode %s", UA_StatusCode_name(((UA_ResponseHeader*)response)->serviceResult)); } /* Call the callback */ if(ac->callback) ac->callback(client, ac->userdata, requestId, response); UA_deleteMembers(response, ac->responseType); /* Remove the callback */ LIST_REMOVE(ac, pointers); UA_free(ac); return retval; } /* Processes the received service response. Either with an async callback or by * decoding the message and returning it "upwards" in the * SyncResponseDescription. */ static void processServiceResponse(void *application, UA_SecureChannel *channel, UA_MessageType messageType, UA_UInt32 requestId, const UA_ByteString *message) { SyncResponseDescription *rd = (SyncResponseDescription*)application; /* Must be OPN or MSG */ if(messageType != UA_MESSAGETYPE_OPN && messageType != UA_MESSAGETYPE_MSG) { UA_LOG_TRACE_CHANNEL(&rd->client->config.logger, channel, "Invalid message type"); return; } /* Forward declaration for the goto */ UA_NodeId expectedNodeId = UA_NODEID_NULL; /* Decode the data type identifier of the response */ size_t offset = 0; UA_NodeId responseId; UA_StatusCode retval = UA_NodeId_decodeBinary(message, &offset, &responseId); if(retval != UA_STATUSCODE_GOOD) goto finish; /* Got an asynchronous response. Don't expected a synchronous response * (responseType NULL) or the id does not match. */ if(!rd->responseType || requestId != rd->requestId) { retval = processAsyncResponse(rd->client, requestId, &responseId, message, &offset); goto finish; } /* Got the synchronous response */ rd->received = true; /* Check that the response type matches */ expectedNodeId = UA_NODEID_NUMERIC(0, rd->responseType->binaryEncodingId); if(!UA_NodeId_equal(&responseId, &expectedNodeId)) { if(UA_NodeId_equal(&responseId, &serviceFaultId)) { UA_init(rd->response, rd->responseType); retval = UA_decodeBinary(message, &offset, rd->response, &UA_TYPES[UA_TYPES_SERVICEFAULT], rd->client->config.customDataTypes); if(retval != UA_STATUSCODE_GOOD) ((UA_ResponseHeader*)rd->response)->serviceResult = retval; UA_LOG_INFO(&rd->client->config.logger, UA_LOGCATEGORY_CLIENT, "Received a ServiceFault response with StatusCode %s", UA_StatusCode_name(((UA_ResponseHeader*)rd->response)->serviceResult)); } else { /* Close the connection */ UA_LOG_ERROR(&rd->client->config.logger, UA_LOGCATEGORY_CLIENT, "Reply contains the wrong service response"); retval = UA_STATUSCODE_BADCOMMUNICATIONERROR; } goto finish; } #ifdef UA_ENABLE_TYPENAMES UA_LOG_DEBUG(&rd->client->config.logger, UA_LOGCATEGORY_CLIENT, "Decode a message of type %s", rd->responseType->typeName); #else UA_LOG_DEBUG(&rd->client->config.logger, UA_LOGCATEGORY_CLIENT, "Decode a message of type %u", responseId.identifier.numeric); #endif /* Decode the response */ retval = UA_decodeBinary(message, &offset, rd->response, rd->responseType, rd->client->config.customDataTypes); finish: UA_NodeId_deleteMembers(&responseId); if(retval != UA_STATUSCODE_GOOD) { if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED) retval = UA_STATUSCODE_BADRESPONSETOOLARGE; UA_LOG_INFO(&rd->client->config.logger, UA_LOGCATEGORY_CLIENT, "Error receiving the response with status code %s", UA_StatusCode_name(retval)); if(rd->response) { UA_ResponseHeader *respHeader = (UA_ResponseHeader*)rd->response; respHeader->serviceResult = retval; } } } /* Forward complete chunks directly to the securechannel */ static UA_StatusCode client_processChunk(void *application, UA_Connection *connection, UA_ByteString *chunk) { SyncResponseDescription *rd = (SyncResponseDescription*)application; UA_StatusCode retval = UA_SecureChannel_decryptAddChunk(&rd->client->channel, chunk, true); if(retval != UA_STATUSCODE_GOOD) return retval; return UA_SecureChannel_persistIncompleteMessages(&rd->client->channel); } /* Receive and process messages until a synchronous message arrives or the * timout finishes */ UA_StatusCode receiveServiceResponse(UA_Client *client, void *response, const UA_DataType *responseType, UA_DateTime maxDate, const UA_UInt32 *synchronousRequestId) { /* Prepare the response and the structure we give into processServiceResponse */ SyncResponseDescription rd = { client, false, 0, response, responseType }; /* Return upon receiving the synchronized response. All other responses are * processed with a callback "in the background". */ if(synchronousRequestId) rd.requestId = *synchronousRequestId; UA_StatusCode retval; do { UA_DateTime now = UA_DateTime_nowMonotonic(); /* >= avoid timeout to be set to 0 */ if(now >= maxDate) return UA_STATUSCODE_GOODNONCRITICALTIMEOUT; /* round always to upper value to avoid timeout to be set to 0 * if(maxDate - now) < (UA_DATETIME_MSEC/2) */ UA_UInt32 timeout = (UA_UInt32)(((maxDate - now) + (UA_DATETIME_MSEC - 1)) / UA_DATETIME_MSEC); retval = UA_Connection_receiveChunksBlocking(&client->connection, &rd, client_processChunk, timeout); UA_SecureChannel_processCompleteMessages(&client->channel, &rd, processServiceResponse); if(retval != UA_STATUSCODE_GOOD && retval != UA_STATUSCODE_GOODNONCRITICALTIMEOUT) { if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED) setClientState(client, UA_CLIENTSTATE_DISCONNECTED); UA_Client_disconnect(client); break; } } while(!rd.received); return retval; } void __UA_Client_Service(UA_Client *client, const void *request, const UA_DataType *requestType, void *response, const UA_DataType *responseType) { UA_init(response, responseType); UA_ResponseHeader *respHeader = (UA_ResponseHeader*)response; /* Send the request */ UA_UInt32 requestId; UA_StatusCode retval = sendSymmetricServiceRequest(client, request, requestType, &requestId); if(retval != UA_STATUSCODE_GOOD) { if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED) respHeader->serviceResult = UA_STATUSCODE_BADREQUESTTOOLARGE; else respHeader->serviceResult = retval; UA_Client_disconnect(client); return; } /* Retrieve the response */ UA_DateTime maxDate = UA_DateTime_nowMonotonic() + (client->config.timeout * UA_DATETIME_MSEC); retval = receiveServiceResponse(client, response, responseType, maxDate, &requestId); if(retval == UA_STATUSCODE_GOODNONCRITICALTIMEOUT) { /* In synchronous service, if we have don't have a reply we need to close the connection */ UA_Client_disconnect(client); retval = UA_STATUSCODE_BADCONNECTIONCLOSED; } if(retval != UA_STATUSCODE_GOOD) respHeader->serviceResult = retval; } UA_StatusCode receiveServiceResponseAsync(UA_Client *client, void *response, const UA_DataType *responseType) { SyncResponseDescription rd = { client, false, 0, response, responseType }; UA_StatusCode retval = UA_Connection_receiveChunksNonBlocking( &client->connection, &rd, client_processChunk); UA_SecureChannel_processCompleteMessages(&client->channel, &rd, processServiceResponse); /*let client run when non critical timeout*/ if(retval != UA_STATUSCODE_GOOD && retval != UA_STATUSCODE_GOODNONCRITICALTIMEOUT) { if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED) { setClientState(client, UA_CLIENTSTATE_DISCONNECTED); } UA_Client_disconnect(client); } return retval; } UA_StatusCode receivePacketAsync(UA_Client *client) { UA_StatusCode retval = UA_STATUSCODE_GOOD; if (UA_Client_getState(client) == UA_CLIENTSTATE_DISCONNECTED || UA_Client_getState(client) == UA_CLIENTSTATE_WAITING_FOR_ACK) { retval = UA_Connection_receiveChunksNonBlocking(&client->connection, client, processACKResponseAsync); } else if(UA_Client_getState(client) == UA_CLIENTSTATE_CONNECTED) { retval = UA_Connection_receiveChunksNonBlocking(&client->connection, client, processOPNResponseAsync); } if(retval != UA_STATUSCODE_GOOD && retval != UA_STATUSCODE_GOODNONCRITICALTIMEOUT) { if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED) setClientState(client, UA_CLIENTSTATE_DISCONNECTED); UA_Client_disconnect(client); } return retval; } void UA_Client_AsyncService_cancel(UA_Client *client, AsyncServiceCall *ac, UA_StatusCode statusCode) { /* Create an empty response with the statuscode */ UA_STACKARRAY(UA_Byte, responseBuf, ac->responseType->memSize); void *resp = (void*)(uintptr_t)&responseBuf[0]; /* workaround aliasing rules */ UA_init(resp, ac->responseType); ((UA_ResponseHeader*)resp)->serviceResult = statusCode; if(ac->callback) ac->callback(client, ac->userdata, ac->requestId, resp); /* Clean up the response. Users might move data into it. For whatever reasons. */ UA_deleteMembers(resp, ac->responseType); } void UA_Client_AsyncService_removeAll(UA_Client *client, UA_StatusCode statusCode) { AsyncServiceCall *ac, *ac_tmp; LIST_FOREACH_SAFE(ac, &client->asyncServiceCalls, pointers, ac_tmp) { LIST_REMOVE(ac, pointers); UA_Client_AsyncService_cancel(client, ac, statusCode); UA_free(ac); } } UA_StatusCode __UA_Client_AsyncServiceEx(UA_Client *client, const void *request, const UA_DataType *requestType, UA_ClientAsyncServiceCallback callback, const UA_DataType *responseType, void *userdata, UA_UInt32 *requestId, UA_UInt32 timeout) { /* Prepare the entry for the linked list */ AsyncServiceCall *ac = (AsyncServiceCall*)UA_malloc(sizeof(AsyncServiceCall)); if(!ac) return UA_STATUSCODE_BADOUTOFMEMORY; ac->callback = callback; ac->responseType = responseType; ac->userdata = userdata; ac->timeout = timeout; /* Call the service and set the requestId */ UA_StatusCode retval = sendSymmetricServiceRequest(client, request, requestType, &ac->requestId); if(retval != UA_STATUSCODE_GOOD) { UA_free(ac); return retval; } ac->start = UA_DateTime_nowMonotonic(); /* Store the entry for async processing */ LIST_INSERT_HEAD(&client->asyncServiceCalls, ac, pointers); if(requestId) *requestId = ac->requestId; return UA_STATUSCODE_GOOD; } UA_StatusCode __UA_Client_AsyncService(UA_Client *client, const void *request, const UA_DataType *requestType, UA_ClientAsyncServiceCallback callback, const UA_DataType *responseType, void *userdata, UA_UInt32 *requestId) { return __UA_Client_AsyncServiceEx(client, request, requestType, callback, responseType, userdata, requestId, client->config.timeout); } UA_StatusCode UA_Client_sendAsyncRequest(UA_Client *client, const void *request, const UA_DataType *requestType, UA_ClientAsyncServiceCallback callback, const UA_DataType *responseType, void *userdata, UA_UInt32 *requestId) { if (UA_Client_getState(client) < UA_CLIENTSTATE_SECURECHANNEL) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Client must be connected to send high-level requests"); return UA_STATUSCODE_BADSERVERNOTCONNECTED; } return __UA_Client_AsyncService(client, request, requestType, callback, responseType, userdata, requestId); } UA_StatusCode UA_EXPORT UA_Client_addTimedCallback(UA_Client *client, UA_ClientCallback callback, void *data, UA_DateTime date, UA_UInt64 *callbackId) { return UA_Timer_addTimedCallback(&client->timer, (UA_ApplicationCallback) callback, client, data, date, callbackId); } UA_StatusCode UA_Client_addRepeatedCallback(UA_Client *client, UA_ClientCallback callback, void *data, UA_Double interval_ms, UA_UInt64 *callbackId) { return UA_Timer_addRepeatedCallback(&client->timer, (UA_ApplicationCallback) callback, client, data, interval_ms, callbackId); } UA_StatusCode UA_Client_changeRepeatedCallbackInterval(UA_Client *client, UA_UInt64 callbackId, UA_Double interval_ms) { return UA_Timer_changeRepeatedCallbackInterval(&client->timer, callbackId, interval_ms); } void UA_Client_removeCallback(UA_Client *client, UA_UInt64 callbackId) { UA_Timer_removeCallback(&client->timer, callbackId); } /*********************************** amalgamated original file "/home/jvoe/open62541/src/client/ua_client_connect.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB * Copyright 2017-2018 (c) Thomas Stalder, Blue Time Concept SA * Copyright 2017-2019 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2018 (c) Kalycito Infotech Private Limited */ /* Size are refered in bytes */ #define UA_MINMESSAGESIZE 8192 #define UA_SESSION_LOCALNONCELENGTH 32 #define MAX_DATA_SIZE 4096 /********************/ /* Set client state */ /********************/ void setClientState(UA_Client *client, UA_ClientState state) { if(client->state != state) { client->state = state; if(client->config.stateCallback) client->config.stateCallback(client, client->state); } } /***********************/ /* Open the Connection */ /***********************/ #define UA_BITMASK_MESSAGETYPE 0x00ffffffu #define UA_BITMASK_CHUNKTYPE 0xff000000u static UA_StatusCode processACKResponse(void *application, UA_Connection *connection, UA_ByteString *chunk) { UA_Client *client = (UA_Client*)application; /* Decode the message */ size_t offset = 0; UA_StatusCode retval; UA_TcpMessageHeader messageHeader; UA_TcpAcknowledgeMessage ackMessage; retval = UA_TcpMessageHeader_decodeBinary(chunk, &offset, &messageHeader); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_NETWORK, "Decoding ACK message failed"); return retval; } // check if we got an error response from the server UA_MessageType messageType = (UA_MessageType) (messageHeader.messageTypeAndChunkType & UA_BITMASK_MESSAGETYPE); UA_ChunkType chunkType = (UA_ChunkType) (messageHeader.messageTypeAndChunkType & UA_BITMASK_CHUNKTYPE); if (messageType == UA_MESSAGETYPE_ERR) { // Header + ErrorMessage (error + reasonLength_field + length) UA_StatusCode error = *(UA_StatusCode*)(&chunk->data[offset]); UA_UInt32 len = *((UA_UInt32*)&chunk->data[offset + 4]); UA_Byte *data = (UA_Byte*)&chunk->data[offset + 4+4]; UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_NETWORK, "Received ERR response. %s - %.*s", UA_StatusCode_name(error), len, data); return error; } if (chunkType != UA_CHUNKTYPE_FINAL) { return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; } /* Decode the ACK message */ retval = UA_TcpAcknowledgeMessage_decodeBinary(chunk, &offset, &ackMessage); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_NETWORK, "Decoding ACK message failed"); return retval; } UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_NETWORK, "Received ACK message"); /* Process the ACK message */ return UA_Connection_processHELACK(connection, &client->config.localConnectionConfig, (const UA_ConnectionConfig*)&ackMessage); } static UA_StatusCode HelAckHandshake(UA_Client *client, const UA_String endpointUrl) { /* Get a buffer */ UA_ByteString message; UA_Connection *conn = &client->connection; UA_StatusCode retval = conn->getSendBuffer(conn, UA_MINMESSAGESIZE, &message); if(retval != UA_STATUSCODE_GOOD) return retval; /* Prepare the HEL message and encode at offset 8 */ UA_TcpHelloMessage hello; /* just reference to avoid copy */ hello.endpointUrl = endpointUrl; memcpy(&hello, &client->config.localConnectionConfig, sizeof(UA_ConnectionConfig)); /* same struct layout */ UA_Byte *bufPos = &message.data[8]; /* skip the header */ const UA_Byte *bufEnd = &message.data[message.length]; retval = UA_TcpHelloMessage_encodeBinary(&hello, &bufPos, bufEnd); /* avoid deleting reference */ hello.endpointUrl = UA_STRING_NULL; UA_TcpHelloMessage_deleteMembers(&hello); if(retval != UA_STATUSCODE_GOOD) { conn->releaseSendBuffer(conn, &message); return retval; } /* Encode the message header at offset 0 */ UA_TcpMessageHeader messageHeader; messageHeader.messageTypeAndChunkType = UA_CHUNKTYPE_FINAL + UA_MESSAGETYPE_HEL; messageHeader.messageSize = (UA_UInt32)((uintptr_t)bufPos - (uintptr_t)message.data); bufPos = message.data; retval = UA_TcpMessageHeader_encodeBinary(&messageHeader, &bufPos, bufEnd); if(retval != UA_STATUSCODE_GOOD) { conn->releaseSendBuffer(conn, &message); return retval; } /* Send the HEL message */ message.length = messageHeader.messageSize; retval = conn->send(conn, &message); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_NETWORK, "Sending HEL failed"); return retval; } UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_NETWORK, "Sent HEL message"); /* Loop until we have a complete chunk */ retval = UA_Connection_receiveChunksBlocking(conn, client, processACKResponse, client->config.timeout); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_NETWORK, "Receiving ACK message failed with %s", UA_StatusCode_name(retval)); if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED) client->state = UA_CLIENTSTATE_DISCONNECTED; UA_Client_disconnect(client); } return retval; } UA_SecurityPolicy * getSecurityPolicy(UA_Client *client, UA_String policyUri) { for(size_t i = 0; i < client->config.securityPoliciesSize; i++) { if(UA_String_equal(&policyUri, &client->config.securityPolicies[i].policyUri)) return &client->config.securityPolicies[i]; } return NULL; } static void processDecodedOPNResponse(UA_Client *client, UA_OpenSecureChannelResponse *response, UA_Boolean renew) { /* Replace the token */ if(renew) client->channel.nextSecurityToken = response->securityToken; else client->channel.securityToken = response->securityToken; /* Replace the nonce */ UA_ByteString_deleteMembers(&client->channel.remoteNonce); client->channel.remoteNonce = response->serverNonce; UA_ByteString_init(&response->serverNonce); if(client->channel.state == UA_SECURECHANNELSTATE_OPEN) UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "SecureChannel renewed"); else UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Opened SecureChannel with SecurityPolicy %.*s", (int)client->channel.securityPolicy->policyUri.length, client->channel.securityPolicy->policyUri.data); /* 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->channel.state = UA_SECURECHANNELSTATE_OPEN; client->nextChannelRenewal = UA_DateTime_nowMonotonic() + (UA_DateTime) (client->channel.securityToken.revisedLifetime * (UA_Double)UA_DATETIME_MSEC * 0.75); } UA_StatusCode openSecureChannel(UA_Client *client, UA_Boolean renew) { /* Check if sc is still valid */ if(renew && client->nextChannelRenewal > UA_DateTime_nowMonotonic()) return UA_STATUSCODE_GOOD; UA_Connection *conn = &client->connection; if(conn->state != UA_CONNECTION_ESTABLISHED) return UA_STATUSCODE_BADSERVERNOTCONNECTED; /* Generate clientNonce. */ UA_StatusCode retval = UA_SecureChannel_generateLocalNonce(&client->channel); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Generating a local nonce failed"); return retval; } /* Prepare the OpenSecureChannelRequest */ UA_OpenSecureChannelRequest opnSecRq; UA_OpenSecureChannelRequest_init(&opnSecRq); opnSecRq.requestHeader.timestamp = UA_DateTime_now(); opnSecRq.requestHeader.authenticationToken = client->authenticationToken; if(renew) { opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_RENEW; UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "Requesting to renew the SecureChannel"); } else { opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_ISSUE; UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "Requesting to open a SecureChannel"); } /* Set the securityMode to input securityMode from client data */ opnSecRq.securityMode = client->channel.securityMode; opnSecRq.clientNonce = client->channel.localNonce; opnSecRq.requestedLifetime = client->config.secureChannelLifeTime; /* Send the OPN message */ UA_UInt32 requestId = ++client->requestId; retval = UA_SecureChannel_sendAsymmetricOPNMessage(&client->channel, requestId, &opnSecRq, &UA_TYPES[UA_TYPES_OPENSECURECHANNELREQUEST]); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "Sending OPN message failed with error %s", UA_StatusCode_name(retval)); UA_Client_disconnect(client); return retval; } UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "OPN message sent"); /* Increase nextChannelRenewal to avoid that we re-start renewal when * publish responses are received before the OPN response arrives. */ client->nextChannelRenewal = UA_DateTime_nowMonotonic() + (2 * ((UA_DateTime)client->config.timeout * UA_DATETIME_MSEC)); /* Receive / decrypt / decode the OPN response. Process async services in * the background until the OPN response arrives. */ UA_OpenSecureChannelResponse response; retval = receiveServiceResponse(client, &response, &UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE], UA_DateTime_nowMonotonic() + ((UA_DateTime)client->config.timeout * UA_DATETIME_MSEC), &requestId); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "Receiving service response failed with error %s", UA_StatusCode_name(retval)); UA_Client_disconnect(client); return retval; } processDecodedOPNResponse(client, &response, renew); UA_OpenSecureChannelResponse_deleteMembers(&response); return retval; } /* Function to verify the signature corresponds to ClientNonce * using the local certificate */ static UA_StatusCode checkClientSignature(const UA_SecureChannel *channel, const UA_CreateSessionResponse *response) { if(channel->securityMode != UA_MESSAGESECURITYMODE_SIGN && channel->securityMode != UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) return UA_STATUSCODE_GOOD; if(!channel->securityPolicy) return UA_STATUSCODE_BADINTERNALERROR; const UA_SecurityPolicy *sp = channel->securityPolicy; const UA_ByteString *lc = &sp->localCertificate; size_t dataToVerifySize = lc->length + channel->localNonce.length; UA_ByteString dataToVerify = UA_BYTESTRING_NULL; UA_StatusCode retval = UA_ByteString_allocBuffer(&dataToVerify, dataToVerifySize); if(retval != UA_STATUSCODE_GOOD) return retval; memcpy(dataToVerify.data, lc->data, lc->length); memcpy(dataToVerify.data + lc->length, channel->localNonce.data, channel->localNonce.length); retval = sp->certificateSigningAlgorithm. verify(sp, channel->channelContext, &dataToVerify, &response->serverSignature.signature); UA_ByteString_deleteMembers(&dataToVerify); return retval; } /* Function to create a signature using remote certificate and nonce */ #ifdef UA_ENABLE_ENCRYPTION UA_StatusCode signActivateSessionRequest(UA_SecureChannel *channel, UA_ActivateSessionRequest *request) { if(channel->securityMode != UA_MESSAGESECURITYMODE_SIGN && channel->securityMode != UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) return UA_STATUSCODE_GOOD; const UA_SecurityPolicy *sp = channel->securityPolicy; UA_SignatureData *sd = &request->clientSignature; /* Prepare the signature */ size_t signatureSize = sp->certificateSigningAlgorithm. getLocalSignatureSize(sp, channel->channelContext); UA_StatusCode retval = UA_String_copy(&sp->certificateSigningAlgorithm.uri, &sd->algorithm); if(retval != UA_STATUSCODE_GOOD) return retval; retval = UA_ByteString_allocBuffer(&sd->signature, signatureSize); if(retval != UA_STATUSCODE_GOOD) return retval; /* Allocate a temporary buffer */ size_t dataToSignSize = channel->remoteCertificate.length + channel->remoteNonce.length; if(dataToSignSize > MAX_DATA_SIZE) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString dataToSign; retval = UA_ByteString_allocBuffer(&dataToSign, dataToSignSize); if(retval != UA_STATUSCODE_GOOD) return retval; /* sd->signature is cleaned up with the response */ /* Sign the signature */ memcpy(dataToSign.data, channel->remoteCertificate.data, channel->remoteCertificate.length); memcpy(dataToSign.data + channel->remoteCertificate.length, channel->remoteNonce.data, channel->remoteNonce.length); retval = sp->certificateSigningAlgorithm.sign(sp, channel->channelContext, &dataToSign, &sd->signature); /* Clean up */ UA_ByteString_deleteMembers(&dataToSign); return retval; } UA_StatusCode encryptUserIdentityToken(UA_Client *client, const UA_String *userTokenSecurityPolicy, UA_ExtensionObject *userIdentityToken) { UA_IssuedIdentityToken *iit = NULL; UA_UserNameIdentityToken *unit = NULL; UA_ByteString *tokenData; if(userIdentityToken->content.decoded.type == &UA_TYPES[UA_TYPES_ISSUEDIDENTITYTOKEN]) { iit = (UA_IssuedIdentityToken*)userIdentityToken->content.decoded.data; tokenData = &iit->tokenData; } else if(userIdentityToken->content.decoded.type == &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]) { unit = (UA_UserNameIdentityToken*)userIdentityToken->content.decoded.data; tokenData = &unit->password; } else { return UA_STATUSCODE_GOOD; } /* No encryption */ const UA_String none = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None"); if(userTokenSecurityPolicy->length == 0 || UA_String_equal(userTokenSecurityPolicy, &none)) { return UA_STATUSCODE_GOOD; } UA_SecurityPolicy *sp = getSecurityPolicy(client, *userTokenSecurityPolicy); if(!sp) { UA_LOG_WARNING(&client->config.logger, UA_LOGCATEGORY_NETWORK, "Could not find the required SecurityPolicy for the UserToken"); return UA_STATUSCODE_BADSECURITYPOLICYREJECTED; } /* Create a temp channel context */ void *channelContext; UA_StatusCode retval = sp->channelModule. newContext(sp, &client->config.endpoint.serverCertificate, &channelContext); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&client->config.logger, UA_LOGCATEGORY_NETWORK, "Could not instantiate the SecurityPolicy for the UserToken"); return UA_STATUSCODE_BADINTERNALERROR; } /* Compute the encrypted length (at least one byte padding) */ size_t plainTextBlockSize = sp->asymmetricModule.cryptoModule. encryptionAlgorithm.getRemotePlainTextBlockSize(sp, channelContext); UA_UInt32 length = (UA_UInt32)(tokenData->length + client->channel.remoteNonce.length); UA_UInt32 totalLength = length + 4; /* Including the length field */ size_t blocks = totalLength / plainTextBlockSize; if(totalLength % plainTextBlockSize != 0) blocks++; size_t overHead = UA_SecurityPolicy_getRemoteAsymEncryptionBufferLengthOverhead(sp, channelContext, blocks * plainTextBlockSize); /* Allocate memory for encryption overhead */ UA_ByteString encrypted; retval = UA_ByteString_allocBuffer(&encrypted, (blocks * plainTextBlockSize) + overHead); if(retval != UA_STATUSCODE_GOOD) { sp->channelModule.deleteContext(channelContext); return UA_STATUSCODE_BADOUTOFMEMORY; } UA_Byte *pos = encrypted.data; const UA_Byte *end = &encrypted.data[encrypted.length]; UA_UInt32_encodeBinary(&length, &pos, end); memcpy(pos, tokenData->data, tokenData->length); memcpy(&pos[tokenData->length], client->channel.remoteNonce.data, client->channel.remoteNonce.length); /* Add padding * * 7.36.2.2 Legacy Encrypted Token Secret Format: A Client should not add any * padding after the secret. If a Client adds padding then all bytes shall * be zero. A Server shall check for padding added by Clients and ensure * that all padding bytes are zeros. */ size_t paddedLength = plainTextBlockSize * blocks; for(size_t i = totalLength; i < paddedLength; i++) encrypted.data[i] = 0; encrypted.length = paddedLength; retval = sp->asymmetricModule.cryptoModule.encryptionAlgorithm.encrypt(sp, channelContext, &encrypted); encrypted.length = (blocks * plainTextBlockSize) + overHead; if(iit) { retval |= UA_String_copy(&sp->asymmetricModule.cryptoModule.encryptionAlgorithm.uri, &iit->encryptionAlgorithm); } else { retval |= UA_String_copy(&sp->asymmetricModule.cryptoModule.encryptionAlgorithm.uri, &unit->encryptionAlgorithm); } UA_ByteString_deleteMembers(tokenData); *tokenData = encrypted; /* Delete the temp channel context */ sp->channelModule.deleteContext(channelContext); return retval; } #endif static UA_StatusCode activateSession(UA_Client *client) { UA_ActivateSessionRequest request; UA_ActivateSessionRequest_init(&request); request.requestHeader.requestHandle = ++client->requestHandle; request.requestHeader.timestamp = UA_DateTime_now(); request.requestHeader.timeoutHint = 600000; UA_StatusCode retval = UA_ExtensionObject_copy(&client->config.userIdentityToken, &request.userIdentityToken); if(retval != UA_STATUSCODE_GOOD) return retval; /* If not token is set, use anonymous */ if(request.userIdentityToken.encoding == UA_EXTENSIONOBJECT_ENCODED_NOBODY) { UA_AnonymousIdentityToken *t = UA_AnonymousIdentityToken_new(); if(!t) { UA_ActivateSessionRequest_deleteMembers(&request); return UA_STATUSCODE_BADOUTOFMEMORY; } request.userIdentityToken.content.decoded.data = t; request.userIdentityToken.content.decoded.type = &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN]; request.userIdentityToken.encoding = UA_EXTENSIONOBJECT_DECODED; } /* Set the policy-Id from the endpoint. Every IdentityToken starts with a * string. */ retval = UA_String_copy(&client->config.userTokenPolicy.policyId, (UA_String*)request.userIdentityToken.content.decoded.data); #ifdef UA_ENABLE_ENCRYPTION /* Encrypt the UserIdentityToken */ const UA_String *userTokenPolicy = &client->channel.securityPolicy->policyUri; if(client->config.userTokenPolicy.securityPolicyUri.length > 0) userTokenPolicy = &client->config.userTokenPolicy.securityPolicyUri; retval |= encryptUserIdentityToken(client, userTokenPolicy, &request.userIdentityToken); /* This function call is to prepare a client signature */ retval |= signActivateSessionRequest(&client->channel, &request); #endif if(retval != UA_STATUSCODE_GOOD) { UA_ActivateSessionRequest_deleteMembers(&request); return retval; } UA_ActivateSessionResponse response; __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST], &response, &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE]); if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "ActivateSession failed with error code %s", UA_StatusCode_name(response.responseHeader.serviceResult)); } retval = response.responseHeader.serviceResult; UA_ActivateSessionRequest_deleteMembers(&request); UA_ActivateSessionResponse_deleteMembers(&response); return retval; } /* Gets a list of endpoints. Memory is allocated for endpointDescription array */ UA_StatusCode UA_Client_getEndpointsInternal(UA_Client *client, const UA_String endpointUrl, size_t *endpointDescriptionsSize, UA_EndpointDescription **endpointDescriptions) { UA_GetEndpointsRequest request; UA_GetEndpointsRequest_init(&request); request.requestHeader.timestamp = UA_DateTime_now(); request.requestHeader.timeoutHint = 10000; // assume the endpointurl outlives the service call request.endpointUrl = endpointUrl; UA_GetEndpointsResponse response; __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST], &response, &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE]); if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_StatusCode retval = response.responseHeader.serviceResult; UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "GetEndpointRequest failed with error code %s", UA_StatusCode_name(retval)); UA_GetEndpointsResponse_deleteMembers(&response); return retval; } *endpointDescriptions = response.endpoints; *endpointDescriptionsSize = response.endpointsSize; response.endpoints = NULL; response.endpointsSize = 0; UA_GetEndpointsResponse_deleteMembers(&response); return UA_STATUSCODE_GOOD; } static UA_StatusCode selectEndpoint(UA_Client *client, const UA_String endpointUrl) { UA_EndpointDescription* endpointArray = NULL; size_t endpointArraySize = 0; UA_StatusCode retval = UA_Client_getEndpointsInternal(client, endpointUrl, &endpointArraySize, &endpointArray); if(retval != UA_STATUSCODE_GOOD) return retval; UA_Boolean endpointFound = false; UA_Boolean tokenFound = false; UA_String binaryTransport = UA_STRING("http://opcfoundation.org/UA-Profile/" "Transport/uatcp-uasc-uabinary"); UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Found %lu endpoints", (long unsigned)endpointArraySize); for(size_t i = 0; i < endpointArraySize; ++i) { UA_EndpointDescription* endpoint = &endpointArray[i]; /* Match Binary TransportProfile? * Note: Siemens returns empty ProfileUrl, we will accept it as binary */ if(endpoint->transportProfileUri.length != 0 && !UA_String_equal(&endpoint->transportProfileUri, &binaryTransport)) continue; /* Valid SecurityMode? */ if(endpoint->securityMode < 1 || endpoint->securityMode > 3) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Rejecting endpoint %lu: invalid security mode", (long unsigned)i); continue; } /* Selected SecurityMode? */ if(client->config.securityMode > 0 && client->config.securityMode != endpoint->securityMode) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Rejecting endpoint %lu: security mode doesn't match", (long unsigned)i); continue; } /* Matching SecurityPolicy? */ if(client->config.securityPolicyUri.length > 0 && !UA_String_equal(&client->config.securityPolicyUri, &endpoint->securityPolicyUri)) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Rejecting endpoint %lu: security policy doesn't match", (long unsigned)i); continue; } /* SecurityPolicy available? */ if(!getSecurityPolicy(client, endpoint->securityPolicyUri)) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Rejecting endpoint %lu: security policy not available", (long unsigned)i); continue; } endpointFound = true; /* Select a matching UserTokenPolicy inside the endpoint */ UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Endpoint %lu has %lu user token policies", (long unsigned)i, (long unsigned)endpoint->userIdentityTokensSize); for(size_t j = 0; j < endpoint->userIdentityTokensSize; ++j) { UA_UserTokenPolicy* userToken = &endpoint->userIdentityTokens[j]; /* Usertokens also have a security policy... */ if (userToken->securityPolicyUri.length > 0 && !getSecurityPolicy(client, userToken->securityPolicyUri)) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Rejecting UserTokenPolicy %lu in endpoint %lu: security policy '%.*s' not available", (long unsigned)j, (long unsigned)i, (int)userToken->securityPolicyUri.length, userToken->securityPolicyUri.data); continue; } if(userToken->tokenType > 3) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Rejecting UserTokenPolicy %lu in endpoint %lu: invalid token type", (long unsigned)j, (long unsigned)i); continue; } /* Does the token type match the client configuration? */ if (userToken->tokenType == UA_USERTOKENTYPE_ANONYMOUS && client->config.userIdentityToken.content.decoded.type != &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN] && client->config.userIdentityToken.content.decoded.type != NULL) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Rejecting UserTokenPolicy %lu (anonymous) in endpoint %lu: configuration doesn't match", (long unsigned)j, (long unsigned)i); continue; } if (userToken->tokenType == UA_USERTOKENTYPE_USERNAME && client->config.userIdentityToken.content.decoded.type != &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Rejecting UserTokenPolicy %lu (username) in endpoint %lu: configuration doesn't match", (long unsigned)j, (long unsigned)i); continue; } if (userToken->tokenType == UA_USERTOKENTYPE_CERTIFICATE && client->config.userIdentityToken.content.decoded.type != &UA_TYPES[UA_TYPES_X509IDENTITYTOKEN]) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Rejecting UserTokenPolicy %lu (certificate) in endpoint %lu: configuration doesn't match", (long unsigned)j, (long unsigned)i); continue; } if (userToken->tokenType == UA_USERTOKENTYPE_ISSUEDTOKEN && client->config.userIdentityToken.content.decoded.type != &UA_TYPES[UA_TYPES_ISSUEDIDENTITYTOKEN]) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Rejecting UserTokenPolicy %lu (token) in endpoint %lu: configuration doesn't match", (long unsigned)j, (long unsigned)i); continue; } /* Endpoint with matching UserTokenPolicy found. Copy to the configuration. */ tokenFound = true; UA_EndpointDescription_deleteMembers(&client->config.endpoint); UA_EndpointDescription temp = *endpoint; temp.userIdentityTokensSize = 0; temp.userIdentityTokens = NULL; UA_UserTokenPolicy_deleteMembers(&client->config.userTokenPolicy); retval = UA_EndpointDescription_copy(&temp, &client->config.endpoint); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Copying endpoint description failed with error code %s", UA_StatusCode_name(retval)); break; } retval = UA_UserTokenPolicy_copy(userToken, &client->config.userTokenPolicy); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Copying user token policy failed with error code %s", UA_StatusCode_name(retval)); break; } #if UA_LOGLEVEL <= 300 const char *securityModeNames[3] = {"None", "Sign", "SignAndEncrypt"}; const char *userTokenTypeNames[4] = {"Anonymous", "UserName", "Certificate", "IssuedToken"}; UA_String *securityPolicyUri = &userToken->securityPolicyUri; if(securityPolicyUri->length == 0) securityPolicyUri = &endpoint->securityPolicyUri; /* Log the selected endpoint */ UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Selected Endpoint %.*s with SecurityMode %s and SecurityPolicy %.*s", (int)endpoint->endpointUrl.length, endpoint->endpointUrl.data, securityModeNames[endpoint->securityMode - 1], (int)endpoint->securityPolicyUri.length, endpoint->securityPolicyUri.data); /* Log the selected UserTokenPolicy */ UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Selected UserTokenPolicy %.*s with UserTokenType %s and SecurityPolicy %.*s", (int)userToken->policyId.length, userToken->policyId.data, userTokenTypeNames[userToken->tokenType], (int)securityPolicyUri->length, securityPolicyUri->data); #endif break; } if(tokenFound) break; } UA_Array_delete(endpointArray, endpointArraySize, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]); if(retval != UA_STATUSCODE_GOOD) return retval; if(!endpointFound) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "No suitable endpoint found"); retval = UA_STATUSCODE_BADINTERNALERROR; } else if(!tokenFound) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "No suitable UserTokenPolicy found for the possible endpoints"); retval = UA_STATUSCODE_BADINTERNALERROR; } return retval; } static UA_StatusCode createSession(UA_Client *client) { UA_CreateSessionRequest request; UA_CreateSessionRequest_init(&request); UA_StatusCode retval = UA_STATUSCODE_GOOD; if(client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGN || client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) { if(client->channel.localNonce.length != UA_SESSION_LOCALNONCELENGTH) { UA_ByteString_deleteMembers(&client->channel.localNonce); retval = UA_ByteString_allocBuffer(&client->channel.localNonce, UA_SESSION_LOCALNONCELENGTH); if(retval != UA_STATUSCODE_GOOD) return retval; } retval = client->channel.securityPolicy->symmetricModule. generateNonce(client->channel.securityPolicy, &client->channel.localNonce); if(retval != UA_STATUSCODE_GOOD) return retval; } request.requestHeader.timestamp = UA_DateTime_now(); request.requestHeader.timeoutHint = 10000; UA_ByteString_copy(&client->channel.localNonce, &request.clientNonce); request.requestedSessionTimeout = client->config.requestedSessionTimeout; request.maxResponseMessageSize = UA_INT32_MAX; UA_String_copy(&client->config.endpoint.endpointUrl, &request.endpointUrl); UA_ApplicationDescription_copy(&client->config.clientDescription, &request.clientDescription); if(client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGN || client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) { UA_ByteString_copy(&client->channel.securityPolicy->localCertificate, &request.clientCertificate); } UA_CreateSessionResponse response; __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST], &response, &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE]); if(response.responseHeader.serviceResult == UA_STATUSCODE_GOOD) { /* Verify the encrypted response */ if(client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGN || client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) { if(!UA_ByteString_equal(&response.serverCertificate, &client->channel.remoteCertificate)) { retval = UA_STATUSCODE_BADCERTIFICATEINVALID; goto cleanup; } /* Verify the client signature */ retval = checkClientSignature(&client->channel, &response); if(retval != UA_STATUSCODE_GOOD) goto cleanup; } /* Copy nonce and and authenticationtoken */ UA_ByteString_deleteMembers(&client->channel.remoteNonce); retval |= UA_ByteString_copy(&response.serverNonce, &client->channel.remoteNonce); UA_NodeId_deleteMembers(&client->authenticationToken); retval |= UA_NodeId_copy(&response.authenticationToken, &client->authenticationToken); } retval |= response.responseHeader.serviceResult; cleanup: UA_CreateSessionRequest_deleteMembers(&request); UA_CreateSessionResponse_deleteMembers(&response); return retval; } UA_StatusCode UA_Client_connectTCPSecureChannel(UA_Client *client, const UA_String endpointUrl) { if(client->state >= UA_CLIENTSTATE_CONNECTED) return UA_STATUSCODE_GOOD; UA_ChannelSecurityToken_init(&client->channel.securityToken); client->channel.state = UA_SECURECHANNELSTATE_FRESH; client->channel.sendSequenceNumber = 0; client->requestId = 0; /* Set the channel SecurityMode */ client->channel.securityMode = client->config.endpoint.securityMode; if(client->channel.securityMode == UA_MESSAGESECURITYMODE_INVALID) client->channel.securityMode = UA_MESSAGESECURITYMODE_NONE; /* Initialized the SecureChannel */ UA_StatusCode retval = UA_STATUSCODE_GOOD; UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Initialize the SecurityPolicy context"); if(!client->channel.securityPolicy) { /* Set the channel SecurityPolicy to #None if no endpoint is selected */ UA_String sps = client->config.endpoint.securityPolicyUri; if(sps.length == 0) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "SecurityPolicy not specified -> use default #None"); sps = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None"); } UA_SecurityPolicy *sp = getSecurityPolicy(client, sps); if(!sp) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Failed to find the required security policy"); retval = UA_STATUSCODE_BADINTERNALERROR; goto cleanup; } retval = UA_SecureChannel_setSecurityPolicy(&client->channel, sp, &client->config.endpoint.serverCertificate); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Failed to set the security policy"); goto cleanup; } } /* Open a TCP connection */ client->connection = client->config.connectionFunc(client->config.localConnectionConfig, endpointUrl, client->config.timeout, &client->config.logger); if(client->connection.state != UA_CONNECTION_OPENING) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Opening the TCP socket failed"); retval = UA_STATUSCODE_BADCONNECTIONCLOSED; goto cleanup; } UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "TCP connection established"); /* Perform the HEL/ACK handshake */ client->connection.config = client->config.localConnectionConfig; retval = HelAckHandshake(client, endpointUrl); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "HEL/ACK handshake failed"); goto cleanup; } setClientState(client, UA_CLIENTSTATE_CONNECTED); /* Open a SecureChannel. */ client->channel.connection = &client->connection; retval = openSecureChannel(client, false); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Opening a secure channel failed"); goto cleanup; } retval = UA_SecureChannel_generateNewKeys(&client->channel); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Generating new keys failed"); return retval; } setClientState(client, UA_CLIENTSTATE_SECURECHANNEL); return retval; cleanup: UA_Client_disconnect(client); return retval; } UA_StatusCode UA_Client_connectSession(UA_Client *client) { if(client->state < UA_CLIENTSTATE_SECURECHANNEL) return UA_STATUSCODE_BADINTERNALERROR; /* Delete async service. TODO: Move this from connect to the disconnect/cleanup phase */ UA_Client_AsyncService_removeAll(client, UA_STATUSCODE_BADSHUTDOWN); // TODO: actually, reactivate an existing session is working, but currently // republish is not implemented This option is disabled until we have a good // implementation of the subscription recovery. #ifdef UA_SESSION_RECOVERY /* Try to activate an existing Session for this SecureChannel */ if((!UA_NodeId_equal(&client->authenticationToken, &UA_NODEID_NULL)) && (createNewSession)) { UA_StatusCode res = activateSession(client); if(res != UA_STATUSCODE_BADSESSIONIDINVALID) { if(res == UA_STATUSCODE_GOOD) { setClientState(client, UA_CLIENTSTATE_SESSION_RENEWED); } else { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Could not activate the Session with StatusCode %s", UA_StatusCode_name(retval)); UA_Client_disconnect(client); } return res; } } #endif /* UA_SESSION_RECOVERY */ /* Could not recover an old session. Remove authenticationToken */ UA_NodeId_deleteMembers(&client->authenticationToken); /* Create a session */ UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Create a new session"); UA_StatusCode retval = createSession(client); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Could not open a Session with StatusCode %s", UA_StatusCode_name(retval)); UA_Client_disconnect(client); return retval; } /* A new session has been created. We need to clean up the subscriptions */ #ifdef UA_ENABLE_SUBSCRIPTIONS UA_Client_Subscriptions_clean(client); client->currentlyOutStandingPublishRequests = 0; #endif /* Activate the session */ retval = activateSession(client); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Could not activate the Session with StatusCode %s", UA_StatusCode_name(retval)); UA_Client_disconnect(client); return retval; } setClientState(client, UA_CLIENTSTATE_SESSION); return retval; } #ifdef UA_ENABLE_ENCRYPTION /* The local ApplicationURI has to match the certificates of the * SecurityPolicies */ static void verifyClientApplicationURI(const UA_Client *client) { #if UA_LOGLEVEL <= 400 for(size_t i = 0; i < client->config.securityPoliciesSize; i++) { UA_SecurityPolicy *sp = &client->config.securityPolicies[i]; if(!sp->certificateVerification) continue; UA_StatusCode retval = sp->certificateVerification-> verifyApplicationURI(sp->certificateVerification->context, &sp->localCertificate, &client->config.clientDescription.applicationUri); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&client->config.logger, UA_LOGCATEGORY_CLIENT, "The configured ApplicationURI does not match the URI " "specified in the certificate for the SecurityPolicy %.*s", (int)sp->policyUri.length, sp->policyUri.data); } } #endif } #endif UA_StatusCode UA_Client_connectInternal(UA_Client *client, const UA_String endpointUrl) { if(client->state >= UA_CLIENTSTATE_CONNECTED) return UA_STATUSCODE_GOOD; UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Connecting to endpoint %.*s", (int)endpointUrl.length, endpointUrl.data); #ifdef UA_ENABLE_ENCRYPTION verifyClientApplicationURI(client); #endif /* Get endpoints only if the description has not been touched (memset to zero) */ UA_Byte test = 0; UA_Byte *pos = (UA_Byte*)&client->config.endpoint; for(size_t i = 0; i < sizeof(UA_EndpointDescription); i++) test = test | pos[i]; pos = (UA_Byte*)&client->config.userTokenPolicy; for(size_t i = 0; i < sizeof(UA_UserTokenPolicy); i++) test = test | pos[i]; UA_Boolean getEndpoints = (test == 0); /* Connect up to the SecureChannel */ UA_StatusCode retval = UA_Client_connectTCPSecureChannel(client, endpointUrl); if (retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Couldn't connect the client to a TCP secure channel"); goto cleanup; } /* Get and select endpoints if required */ if(getEndpoints) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Endpoint and UserTokenPolicy unconfigured, perform GetEndpoints"); retval = selectEndpoint(client, endpointUrl); if(retval != UA_STATUSCODE_GOOD) goto cleanup; /* Reconnect with a new SecureChannel if the current one does not match * the selected endpoint */ if(!UA_String_equal(&client->config.endpoint.securityPolicyUri, &client->channel.securityPolicy->policyUri)) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Disconnect to switch to a different SecurityPolicy"); UA_Client_disconnect(client); return UA_Client_connectInternal(client, endpointUrl); } } retval = UA_Client_connectSession(client); if(retval != UA_STATUSCODE_GOOD) goto cleanup; return retval; cleanup: UA_Client_disconnect(client); return retval; } UA_StatusCode UA_Client_connect(UA_Client *client, const char *endpointUrl) { return UA_Client_connectInternal(client, UA_STRING((char*)(uintptr_t)endpointUrl)); } UA_StatusCode UA_Client_connect_noSession(UA_Client *client, const char *endpointUrl) { return UA_Client_connectTCPSecureChannel(client, UA_STRING((char*)(uintptr_t)endpointUrl)); } UA_StatusCode UA_Client_connect_username(UA_Client *client, const char *endpointUrl, const char *username, const char *password) { UA_UserNameIdentityToken* identityToken = UA_UserNameIdentityToken_new(); if(!identityToken) return UA_STATUSCODE_BADOUTOFMEMORY; identityToken->userName = UA_STRING_ALLOC(username); identityToken->password = UA_STRING_ALLOC(password); UA_ExtensionObject_deleteMembers(&client->config.userIdentityToken); client->config.userIdentityToken.encoding = UA_EXTENSIONOBJECT_DECODED; client->config.userIdentityToken.content.decoded.type = &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]; client->config.userIdentityToken.content.decoded.data = identityToken; return UA_Client_connect(client, endpointUrl); } /************************/ /* Close the Connection */ /************************/ static void sendCloseSession(UA_Client *client) { UA_CloseSessionRequest request; UA_CloseSessionRequest_init(&request); request.requestHeader.timestamp = UA_DateTime_now(); request.requestHeader.timeoutHint = 10000; request.deleteSubscriptions = true; UA_CloseSessionResponse response; __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST], &response, &UA_TYPES[UA_TYPES_CLOSESESSIONRESPONSE]); UA_CloseSessionRequest_deleteMembers(&request); UA_CloseSessionResponse_deleteMembers(&response); } static void sendCloseSecureChannel(UA_Client *client) { UA_SecureChannel *channel = &client->channel; UA_CloseSecureChannelRequest request; UA_CloseSecureChannelRequest_init(&request); request.requestHeader.requestHandle = ++client->requestHandle; request.requestHeader.timestamp = UA_DateTime_now(); request.requestHeader.timeoutHint = 10000; request.requestHeader.authenticationToken = client->authenticationToken; UA_SecureChannel_sendSymmetricMessage(channel, ++client->requestId, UA_MESSAGETYPE_CLO, &request, &UA_TYPES[UA_TYPES_CLOSESECURECHANNELREQUEST]); UA_CloseSecureChannelRequest_deleteMembers(&request); UA_SecureChannel_close(&client->channel); UA_SecureChannel_deleteMembers(&client->channel); } UA_StatusCode UA_Client_disconnect(UA_Client *client) { /* Is a session established? */ if(client->state >= UA_CLIENTSTATE_SESSION) { client->state = UA_CLIENTSTATE_SECURECHANNEL; sendCloseSession(client); } UA_NodeId_deleteMembers(&client->authenticationToken); client->requestHandle = 0; /* Is a secure channel established? */ if(client->state >= UA_CLIENTSTATE_SECURECHANNEL) { client->state = UA_CLIENTSTATE_CONNECTED; sendCloseSecureChannel(client); } /* Close the TCP connection */ if(client->connection.state != UA_CONNECTION_CLOSED && client->connection.state != UA_CONNECTION_OPENING) /* UA_ClientConnectionTCP_init sets initial state to opening */ if(client->connection.close != NULL) client->connection.close(&client->connection); #ifdef UA_ENABLE_SUBSCRIPTIONS // TODO REMOVE WHEN UA_SESSION_RECOVERY IS READY /* We need to clean up the subscriptions */ UA_Client_Subscriptions_clean(client); #endif UA_SecureChannel_deleteMembers(&client->channel); setClientState(client, UA_CLIENTSTATE_DISCONNECTED); return UA_STATUSCODE_GOOD; } /*********************************** amalgamated original file "/home/jvoe/open62541/src/client/ua_client_connect_async.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #define UA_MINMESSAGESIZE 8192 #define UA_SESSION_LOCALNONCELENGTH 32 #define MAX_DATA_SIZE 4096 /* Asynchronous client connection * To prepare an async connection, UA_Client_connectAsync() is called, which does not connect the * client directly. UA_Client_run_iterate() takes care of actually connecting the client: * if client is disconnected: * send hello msg and set the client state to be WAITING_FOR_ACK * (see UA_Client_connect_iterate()) * if client is waiting for the ACK: * call the non-blocking receiving function and register processACKResponseAsync() as its callback * (see receivePacketAsync()) * if ACK is processed (callback called): * processACKResponseAsync() calls openSecureChannelAsync() at the end, which prepares the request * to open secure channel and the client is connected * if client is connected: * call the non-blocking receiving function and register processOPNResponse() as its callback * (see receivePacketAsync()) * if OPN-request processed (callback called) * send session request, where the session response is put into a normal AsyncServiceCall, and when * called, request to activate session is sent, where its response is again put into an AsyncServiceCall * in the very last step responseActivateSession(): * the user defined callback that is passed into UA_Client_connectAsync() is called and the * async connection finalized. * */ /***********************/ /* Open the Connection */ /***********************/ static UA_StatusCode openSecureChannelAsync(UA_Client *client/*, UA_Boolean renew*/); static UA_StatusCode requestSession(UA_Client *client, UA_UInt32 *requestId); static UA_StatusCode requestGetEndpoints(UA_Client *client, UA_UInt32 *requestId); /*receives hello ack, opens secure channel*/ UA_StatusCode processACKResponseAsync(void *application, UA_Connection *connection, UA_ByteString *chunk) { UA_Client *client = (UA_Client*)application; /* Decode the message */ size_t offset = 0; UA_TcpMessageHeader messageHeader; UA_TcpAcknowledgeMessage ackMessage; client->connectStatus = UA_TcpMessageHeader_decodeBinary (chunk, &offset, &messageHeader); client->connectStatus |= UA_TcpAcknowledgeMessage_decodeBinary( chunk, &offset, &ackMessage); if (client->connectStatus != UA_STATUSCODE_GOOD) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_NETWORK, "Decoding ACK message failed"); return client->connectStatus; } UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_NETWORK, "Received ACK message"); client->connectStatus = UA_Connection_processHELACK(connection, &client->config.localConnectionConfig, (const UA_ConnectionConfig*)&ackMessage); if(client->connectStatus != UA_STATUSCODE_GOOD) return client->connectStatus; client->state = UA_CLIENTSTATE_CONNECTED; /* Open a SecureChannel. TODO: Select with endpoint */ client->channel.connection = &client->connection; client->connectStatus = openSecureChannelAsync(client/*, false*/); return client->connectStatus; } static UA_StatusCode sendHELMessage(UA_Client *client) { /* Get a buffer */ UA_ByteString message; UA_Connection *conn = &client->connection; UA_StatusCode retval = conn->getSendBuffer(conn, UA_MINMESSAGESIZE, &message); if(retval != UA_STATUSCODE_GOOD) return retval; /* Prepare the HEL message and encode at offset 8 */ UA_TcpHelloMessage hello; UA_String_copy(&client->endpointUrl, &hello.endpointUrl); /* must be less than 4096 bytes */ memcpy(&hello, &client->config.localConnectionConfig, sizeof(UA_ConnectionConfig)); /* same struct layout */ UA_Byte *bufPos = &message.data[8]; /* skip the header */ const UA_Byte *bufEnd = &message.data[message.length]; client->connectStatus = UA_TcpHelloMessage_encodeBinary(&hello, &bufPos, bufEnd); UA_TcpHelloMessage_deleteMembers (&hello); /* Encode the message header at offset 0 */ UA_TcpMessageHeader messageHeader; messageHeader.messageTypeAndChunkType = UA_CHUNKTYPE_FINAL + UA_MESSAGETYPE_HEL; messageHeader.messageSize = (UA_UInt32) ((uintptr_t)bufPos - (uintptr_t)message.data); bufPos = message.data; retval = UA_TcpMessageHeader_encodeBinary(&messageHeader, &bufPos, bufEnd); if(retval != UA_STATUSCODE_GOOD) { conn->releaseSendBuffer(conn, &message); return retval; } /* Send the HEL message */ message.length = messageHeader.messageSize; retval = conn->send (conn, &message); if(retval == UA_STATUSCODE_GOOD) { UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_NETWORK, "Sent HEL message"); } else { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_NETWORK, "Sending HEL failed"); } return retval; } 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); } UA_StatusCode processOPNResponseAsync(void *application, UA_Connection *connection, UA_ByteString *chunk) { UA_Client *client = (UA_Client*) application; UA_StatusCode retval = UA_SecureChannel_decryptAddChunk(&client->channel, chunk, true); client->connectStatus = retval; if(retval != UA_STATUSCODE_GOOD) goto error; UA_SecureChannel_processCompleteMessages(&client->channel, client, processDecodedOPNResponseAsync); if(client->state < UA_CLIENTSTATE_SECURECHANNEL) { retval = UA_STATUSCODE_BADSECURECHANNELCLOSED; goto error; } retval = UA_SecureChannel_persistIncompleteMessages(&client->channel); if(retval != UA_STATUSCODE_GOOD) goto error; retval = UA_SecureChannel_generateNewKeys(&client->channel); if(retval != UA_STATUSCODE_GOOD) goto error; /* Following requests and responses */ UA_UInt32 reqId; if(client->endpointsHandshake) retval = requestGetEndpoints (client, &reqId); else retval = requestSession (client, &reqId); if(retval != UA_STATUSCODE_GOOD) goto error; return retval; error: UA_Client_disconnect(client); return retval; } /* OPN messges to renew the channel are sent asynchronous */ static UA_StatusCode openSecureChannelAsync(UA_Client *client/*, UA_Boolean renew*/) { /* Check if sc is still valid */ /*if(renew && client->nextChannelRenewal - UA_DateTime_nowMonotonic () > 0) return UA_STATUSCODE_GOOD;*/ UA_Connection *conn = &client->connection; if(conn->state != UA_CONNECTION_ESTABLISHED) return UA_STATUSCODE_BADSERVERNOTCONNECTED; /* Prepare the OpenSecureChannelRequest */ UA_OpenSecureChannelRequest opnSecRq; UA_OpenSecureChannelRequest_init(&opnSecRq); opnSecRq.requestHeader.timestamp = UA_DateTime_now(); opnSecRq.requestHeader.authenticationToken = client->authenticationToken; /*if(renew) { opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_RENEW; UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "Requesting to renew the SecureChannel"); } else {*/ opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_ISSUE; UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "Requesting to open a SecureChannel"); //} opnSecRq.securityMode = client->channel.securityMode; opnSecRq.clientNonce = client->channel.localNonce; opnSecRq.requestedLifetime = client->config.secureChannelLifeTime; /* Prepare the entry for the linked list */ UA_UInt32 requestId = ++client->requestId; /*AsyncServiceCall *ac = NULL; if(renew) { ac = (AsyncServiceCall*)UA_malloc(sizeof(AsyncServiceCall)); if (!ac) return UA_STATUSCODE_BADOUTOFMEMORY; ac->callback = (UA_ClientAsyncServiceCallback) processDecodedOPNResponseAsync; ac->responseType = &UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE]; ac->requestId = requestId; ac->userdata = NULL; }*/ /* Send the OPN message */ UA_StatusCode retval = UA_SecureChannel_sendAsymmetricOPNMessage ( &client->channel, requestId, &opnSecRq, &UA_TYPES[UA_TYPES_OPENSECURECHANNELREQUEST]); client->connectStatus = retval; if(retval != UA_STATUSCODE_GOOD) { client->connectStatus = retval; UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "Sending OPN message failed with error %s", UA_StatusCode_name(retval)); UA_Client_disconnect(client); //if(renew) // UA_free(ac); return retval; } UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "OPN message sent"); /* Store the entry for async processing and return */ /*if(renew) { LIST_INSERT_HEAD(&client->asyncServiceCalls, ac, pointers); return retval; }*/ return retval; } static void responseActivateSession(UA_Client *client, void *userdata, UA_UInt32 requestId, void *response) { UA_ActivateSessionResponse *activateResponse = (UA_ActivateSessionResponse *) response; if(activateResponse->responseHeader.serviceResult) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "ActivateSession failed with error code %s", UA_StatusCode_name(activateResponse->responseHeader.serviceResult)); } client->connection.state = UA_CONNECTION_ESTABLISHED; setClientState(client, UA_CLIENTSTATE_SESSION); #ifdef UA_ENABLE_SUBSCRIPTIONS /* A new session has been created. We need to clean up the subscriptions */ UA_Client_Subscriptions_clean(client); #endif /* Call onConnect (client_async.c) callback */ if(client->asyncConnectCall.callback) client->asyncConnectCall.callback(client, client->asyncConnectCall.userdata, requestId + 1, &activateResponse->responseHeader.serviceResult); } static UA_StatusCode requestActivateSession (UA_Client *client, UA_UInt32 *requestId) { UA_ActivateSessionRequest request; UA_ActivateSessionRequest_init(&request); request.requestHeader.requestHandle = ++client->requestHandle; request.requestHeader.timestamp = UA_DateTime_now (); request.requestHeader.timeoutHint = 600000; UA_StatusCode retval = UA_ExtensionObject_copy(&client->config.userIdentityToken, &request.userIdentityToken); if(retval != UA_STATUSCODE_GOOD) return retval; /* If not token is set, use anonymous */ if(request.userIdentityToken.encoding == UA_EXTENSIONOBJECT_ENCODED_NOBODY) { UA_AnonymousIdentityToken *t = UA_AnonymousIdentityToken_new(); if(!t) { UA_ActivateSessionRequest_deleteMembers(&request); return UA_STATUSCODE_BADOUTOFMEMORY; } request.userIdentityToken.content.decoded.data = t; request.userIdentityToken.content.decoded.type = &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN]; request.userIdentityToken.encoding = UA_EXTENSIONOBJECT_DECODED; } /* Set the policy-Id from the endpoint. Every IdentityToken starts with a * string. */ retval = UA_String_copy(&client->config.userTokenPolicy.policyId, (UA_String*)request.userIdentityToken.content.decoded.data); #ifdef UA_ENABLE_ENCRYPTION /* Encrypt the UserIdentityToken */ const UA_String *userTokenPolicy = &client->channel.securityPolicy->policyUri; if(client->config.userTokenPolicy.securityPolicyUri.length > 0) userTokenPolicy = &client->config.userTokenPolicy.securityPolicyUri; retval |= encryptUserIdentityToken(client, userTokenPolicy, &request.userIdentityToken); /* This function call is to prepare a client signature */ retval |= signActivateSessionRequest(&client->channel, &request); #endif if(retval != UA_STATUSCODE_GOOD) { UA_ActivateSessionRequest_deleteMembers(&request); client->connectStatus = retval; return retval; } retval = UA_Client_sendAsyncRequest ( client, &request, &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST], (UA_ClientAsyncServiceCallback) responseActivateSession, &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE], NULL, requestId); UA_ActivateSessionRequest_deleteMembers(&request); client->connectStatus = retval; return retval; } /* Combination of UA_Client_getEndpointsInternal and getEndpoints */ static void responseGetEndpoints(UA_Client *client, void *userdata, UA_UInt32 requestId, void *response) { UA_EndpointDescription* endpointArray = NULL; size_t endpointArraySize = 0; UA_GetEndpointsResponse* resp; resp = (UA_GetEndpointsResponse*)response; if (resp->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { client->connectStatus = resp->responseHeader.serviceResult; UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "GetEndpointRequest failed with error code %s", UA_StatusCode_name (client->connectStatus)); UA_GetEndpointsResponse_deleteMembers(resp); return; } endpointArray = resp->endpoints; endpointArraySize = resp->endpointsSize; resp->endpoints = NULL; resp->endpointsSize = 0; UA_Boolean endpointFound = false; UA_Boolean tokenFound = false; UA_String securityNone = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None"); UA_String binaryTransport = UA_STRING("http://opcfoundation.org/UA-Profile/" "Transport/uatcp-uasc-uabinary"); // TODO: compare endpoint information with client->endpointUri for(size_t i = 0; i < endpointArraySize; ++i) { UA_EndpointDescription* endpoint = &endpointArray[i]; /* look out for binary transport endpoints */ /* Note: Siemens returns empty ProfileUrl, we will accept it as binary */ if(endpoint->transportProfileUri.length != 0 && !UA_String_equal (&endpoint->transportProfileUri, &binaryTransport)) continue; /* Look for an endpoint corresponding to the client security policy */ if(!UA_String_equal(&endpoint->securityPolicyUri, &client->channel.securityPolicy->policyUri)) continue; endpointFound = true; /* Look for a user token policy with an anonymous token */ for(size_t j = 0; j < endpoint->userIdentityTokensSize; ++j) { UA_UserTokenPolicy* userToken = &endpoint->userIdentityTokens[j]; /* Usertokens also have a security policy... */ if(userToken->securityPolicyUri.length > 0 && !UA_String_equal(&userToken->securityPolicyUri, &securityNone)) continue; /* Does the token type match the client configuration? */ if((userToken->tokenType == UA_USERTOKENTYPE_ANONYMOUS && client->config.userIdentityToken.content.decoded.type != &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN] && client->config.userIdentityToken.content.decoded.type != NULL) || (userToken->tokenType == UA_USERTOKENTYPE_USERNAME && client->config.userIdentityToken.content.decoded.type != &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]) || (userToken->tokenType == UA_USERTOKENTYPE_CERTIFICATE && client->config.userIdentityToken.content.decoded.type != &UA_TYPES[UA_TYPES_X509IDENTITYTOKEN]) || (userToken->tokenType == UA_USERTOKENTYPE_ISSUEDTOKEN && client->config.userIdentityToken.content.decoded.type != &UA_TYPES[UA_TYPES_ISSUEDIDENTITYTOKEN])) continue; /* Endpoint with matching usertokenpolicy found */ tokenFound = true; UA_EndpointDescription_deleteMembers(&client->config.endpoint); UA_EndpointDescription_copy(endpoint, &client->config.endpoint); UA_UserTokenPolicy_deleteMembers(&client->config.userTokenPolicy); UA_UserTokenPolicy_copy(userToken, &client->config.userTokenPolicy); break; } } UA_Array_delete(endpointArray, endpointArraySize, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]); if(!endpointFound) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "No suitable endpoint found"); client->connectStatus = UA_STATUSCODE_BADINTERNALERROR; } else if(!tokenFound) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "No suitable UserTokenPolicy found for the possible endpoints"); client->connectStatus = UA_STATUSCODE_BADINTERNALERROR; } requestSession(client, &requestId); } static UA_StatusCode requestGetEndpoints(UA_Client *client, UA_UInt32 *requestId) { UA_GetEndpointsRequest request; UA_GetEndpointsRequest_init(&request); request.requestHeader.timestamp = UA_DateTime_now(); request.requestHeader.timeoutHint = 10000; /* assume the endpointurl outlives the service call */ UA_String_copy(&client->endpointUrl, &request.endpointUrl); client->connectStatus = UA_Client_sendAsyncRequest( client, &request, &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST], (UA_ClientAsyncServiceCallback) responseGetEndpoints, &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE], NULL, requestId); UA_GetEndpointsRequest_deleteMembers(&request); return client->connectStatus; } static void responseSessionCallback(UA_Client *client, void *userdata, UA_UInt32 requestId, void *response) { UA_CreateSessionResponse *sessionResponse = (UA_CreateSessionResponse *)response; UA_NodeId_copy(&sessionResponse->authenticationToken, &client->authenticationToken); requestActivateSession(client, &requestId); } static UA_StatusCode requestSession(UA_Client *client, UA_UInt32 *requestId) { UA_CreateSessionRequest request; UA_CreateSessionRequest_init(&request); UA_StatusCode retval = UA_STATUSCODE_GOOD; if(client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGN || client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) { if(client->channel.localNonce.length != UA_SESSION_LOCALNONCELENGTH) { UA_ByteString_deleteMembers(&client->channel.localNonce); retval = UA_ByteString_allocBuffer(&client->channel.localNonce, UA_SESSION_LOCALNONCELENGTH); if(retval != UA_STATUSCODE_GOOD) return retval; } retval = client->channel.securityPolicy->symmetricModule. generateNonce(client->channel.securityPolicy, &client->channel.localNonce); if(retval != UA_STATUSCODE_GOOD) return retval; } request.requestHeader.requestHandle = ++client->requestHandle; request.requestHeader.timestamp = UA_DateTime_now(); request.requestHeader.timeoutHint = 10000; UA_ByteString_copy(&client->channel.localNonce, &request.clientNonce); request.requestedSessionTimeout = client->config.requestedSessionTimeout; request.maxResponseMessageSize = UA_INT32_MAX; UA_String_copy(&client->config.endpoint.endpointUrl, &request.endpointUrl); UA_ApplicationDescription_copy(&client->config.clientDescription, &request.clientDescription); retval = UA_Client_sendAsyncRequest ( client, &request, &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST], (UA_ClientAsyncServiceCallback) responseSessionCallback, &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE], NULL, requestId); UA_CreateSessionRequest_deleteMembers(&request); client->connectStatus = retval; return client->connectStatus; } UA_StatusCode UA_Client_connect_iterate(UA_Client *client) { UA_LOG_TRACE(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Client connect iterate"); if (client->connection.state == UA_CONNECTION_ESTABLISHED){ if(client->state < UA_CLIENTSTATE_WAITING_FOR_ACK) { client->connectStatus = sendHELMessage(client); if(client->connectStatus == UA_STATUSCODE_GOOD) { setClientState(client, UA_CLIENTSTATE_WAITING_FOR_ACK); } else { client->connection.close(&client->connection); client->connection.free(&client->connection); } return client->connectStatus; } } /* If server is not connected */ if(client->connection.state == UA_CONNECTION_CLOSED) { client->connectStatus = UA_STATUSCODE_BADCONNECTIONCLOSED; UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_NETWORK, "No connection to server."); } if(client->connectStatus != UA_STATUSCODE_GOOD) { client->connection.close(&client->connection); client->connection.free(&client->connection); } return client->connectStatus; } UA_StatusCode UA_Client_connect_async(UA_Client *client, const char *endpointUrl, UA_ClientAsyncServiceCallback callback, void *userdata) { UA_LOG_TRACE(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Client internal async"); if(client->state >= UA_CLIENTSTATE_WAITING_FOR_ACK) return UA_STATUSCODE_GOOD; UA_ChannelSecurityToken_init(&client->channel.securityToken); client->channel.state = UA_SECURECHANNELSTATE_FRESH; client->endpointsHandshake = true; client->channel.sendSequenceNumber = 0; client->requestId = 0; UA_String_deleteMembers(&client->endpointUrl); client->endpointUrl = UA_STRING_ALLOC(endpointUrl); UA_StatusCode retval = UA_STATUSCODE_GOOD; client->connection = client->config.initConnectionFunc(client->config.localConnectionConfig, client->endpointUrl, client->config.timeout, &client->config.logger); if(client->connection.state != UA_CONNECTION_OPENING) { UA_LOG_TRACE(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Could not init async connection"); retval = UA_STATUSCODE_BADCONNECTIONCLOSED; goto cleanup; } /* Set the channel SecurityMode if not done so far */ if(client->channel.securityMode == UA_MESSAGESECURITYMODE_INVALID) client->channel.securityMode = UA_MESSAGESECURITYMODE_NONE; /* Set the channel SecurityPolicy if not done so far */ if(!client->channel.securityPolicy) { UA_SecurityPolicy *sp = getSecurityPolicy(client, UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None")); if(!sp) { retval = UA_STATUSCODE_BADINTERNALERROR; goto cleanup; } UA_ByteString remoteCertificate = UA_BYTESTRING_NULL; retval = UA_SecureChannel_setSecurityPolicy(&client->channel, sp, &remoteCertificate); if(retval != UA_STATUSCODE_GOOD) goto cleanup; } client->asyncConnectCall.callback = callback; client->asyncConnectCall.userdata = userdata; if(!client->connection.connectCallbackID) { UA_LOG_TRACE(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Adding async connection callback"); retval = UA_Client_addRepeatedCallback( client, client->config.pollConnectionFunc, &client->connection, 100.0, &client->connection.connectCallbackID); if(retval != UA_STATUSCODE_GOOD) goto cleanup; } retval = UA_SecureChannel_generateLocalNonce(&client->channel); if(retval != UA_STATUSCODE_GOOD) goto cleanup; /* Delete async service. TODO: Move this from connect to the disconnect/cleanup phase */ UA_Client_AsyncService_removeAll(client, UA_STATUSCODE_BADSHUTDOWN); #ifdef UA_ENABLE_SUBSCRIPTIONS client->currentlyOutStandingPublishRequests = 0; #endif UA_NodeId_deleteMembers(&client->authenticationToken); /* Generate new local and remote key */ retval = UA_SecureChannel_generateNewKeys(&client->channel); if(retval != UA_STATUSCODE_GOOD) goto cleanup; return retval; cleanup: UA_LOG_TRACE(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Failure during async connect"); UA_Client_disconnect(client); return retval; } /* Async disconnection */ static void sendCloseSecureChannelAsync(UA_Client *client, void *userdata, UA_UInt32 requestId, void *response) { UA_NodeId_deleteMembers (&client->authenticationToken); client->requestHandle = 0; UA_SecureChannel *channel = &client->channel; UA_CloseSecureChannelRequest request; UA_CloseSecureChannelRequest_init(&request); request.requestHeader.requestHandle = ++client->requestHandle; request.requestHeader.timestamp = UA_DateTime_now(); request.requestHeader.timeoutHint = 10000; request.requestHeader.authenticationToken = client->authenticationToken; UA_SecureChannel_sendSymmetricMessage( channel, ++client->requestId, UA_MESSAGETYPE_CLO, &request, &UA_TYPES[UA_TYPES_CLOSESECURECHANNELREQUEST]); UA_SecureChannel_close(&client->channel); UA_SecureChannel_deleteMembers(&client->channel); } static void sendCloseSessionAsync(UA_Client *client, UA_UInt32 *requestId) { UA_CloseSessionRequest request; UA_CloseSessionRequest_init(&request); request.requestHeader.timestamp = UA_DateTime_now(); request.requestHeader.timeoutHint = 10000; request.deleteSubscriptions = true; UA_Client_sendAsyncRequest( client, &request, &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST], (UA_ClientAsyncServiceCallback) sendCloseSecureChannelAsync, &UA_TYPES[UA_TYPES_CLOSESESSIONRESPONSE], NULL, requestId); } UA_StatusCode UA_Client_disconnect_async(UA_Client *client, UA_UInt32 *requestId) { /* Is a session established? */ if (client->state == UA_CLIENTSTATE_SESSION) { client->state = UA_CLIENTSTATE_SESSION_DISCONNECTED; sendCloseSessionAsync(client, requestId); } /* Close the TCP connection * shutdown and close (in tcp.c) are already async*/ if (client->state >= UA_CLIENTSTATE_CONNECTED) client->connection.close(&client->connection); else UA_Client_removeRepeatedCallback(client, client->connection.connectCallbackID); #ifdef UA_ENABLE_SUBSCRIPTIONS // TODO REMOVE WHEN UA_SESSION_RECOVERY IS READY /* We need to clean up the subscriptions */ UA_Client_Subscriptions_clean(client); #endif setClientState(client, UA_CLIENTSTATE_DISCONNECTED); return UA_STATUSCODE_GOOD; } /*********************************** amalgamated original file "/home/jvoe/open62541/src/client/ua_client_discovery.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB * Copyright 2017 (c) Stefan Profanter, fortiss GmbH */ UA_StatusCode UA_Client_getEndpoints(UA_Client *client, const char *serverUrl, size_t* endpointDescriptionsSize, UA_EndpointDescription** endpointDescriptions) { UA_Boolean connected = (client->state > UA_CLIENTSTATE_DISCONNECTED); /* Client is already connected to a different server */ if(connected && strncmp((const char*)client->config.endpoint.endpointUrl.data, serverUrl, client->config.endpoint.endpointUrl.length) != 0) { return UA_STATUSCODE_BADINVALIDARGUMENT; } UA_StatusCode retval; const UA_String url = UA_STRING((char*)(uintptr_t)serverUrl); if(!connected) { retval = UA_Client_connectTCPSecureChannel(client, url); if(retval != UA_STATUSCODE_GOOD) return retval; } retval = UA_Client_getEndpointsInternal(client, url, endpointDescriptionsSize, endpointDescriptions); if(!connected) UA_Client_disconnect(client); return retval; } UA_StatusCode UA_Client_findServers(UA_Client *client, const char *serverUrl, size_t serverUrisSize, UA_String *serverUris, size_t localeIdsSize, UA_String *localeIds, size_t *registeredServersSize, UA_ApplicationDescription **registeredServers) { UA_Boolean connected = (client->state > UA_CLIENTSTATE_DISCONNECTED); /* Client is already connected to a different server */ if(connected && strncmp((const char*)client->config.endpoint.endpointUrl.data, serverUrl, client->config.endpoint.endpointUrl.length) != 0) { return UA_STATUSCODE_BADINVALIDARGUMENT; } UA_StatusCode retval; const UA_String url = UA_STRING((char*)(uintptr_t)serverUrl); if(!connected) { retval = UA_Client_connectTCPSecureChannel(client, url); if(retval != UA_STATUSCODE_GOOD) return retval; } /* Prepare the request */ UA_FindServersRequest request; UA_FindServersRequest_init(&request); request.serverUrisSize = serverUrisSize; request.serverUris = serverUris; request.localeIdsSize = localeIdsSize; request.localeIds = localeIds; /* Send the request */ UA_FindServersResponse response; __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_FINDSERVERSREQUEST], &response, &UA_TYPES[UA_TYPES_FINDSERVERSRESPONSE]); /* Process the response */ retval = response.responseHeader.serviceResult; if(retval == UA_STATUSCODE_GOOD) { *registeredServersSize = response.serversSize; *registeredServers = response.servers; response.serversSize = 0; response.servers = NULL; } else { *registeredServersSize = 0; *registeredServers = NULL; } /* Clean up */ UA_FindServersResponse_deleteMembers(&response); if(!connected) UA_Client_disconnect(client); return retval; } #ifdef UA_ENABLE_DISCOVERY UA_StatusCode UA_Client_findServersOnNetwork(UA_Client *client, const char *serverUrl, UA_UInt32 startingRecordId, UA_UInt32 maxRecordsToReturn, size_t serverCapabilityFilterSize, UA_String *serverCapabilityFilter, size_t *serverOnNetworkSize, UA_ServerOnNetwork **serverOnNetwork) { UA_Boolean connected = (client->state > UA_CLIENTSTATE_DISCONNECTED); /* Client is already connected to a different server */ if(connected && strncmp((const char*)client->config.endpoint.endpointUrl.data, serverUrl, client->config.endpoint.endpointUrl.length) != 0) { return UA_STATUSCODE_BADINVALIDARGUMENT; } UA_StatusCode retval; const UA_String url = UA_STRING((char*)(uintptr_t)serverUrl); if(!connected) { retval = UA_Client_connectTCPSecureChannel(client, url); if(retval != UA_STATUSCODE_GOOD) return retval; } /* Prepare the request */ UA_FindServersOnNetworkRequest request; UA_FindServersOnNetworkRequest_init(&request); request.startingRecordId = startingRecordId; request.maxRecordsToReturn = maxRecordsToReturn; request.serverCapabilityFilterSize = serverCapabilityFilterSize; request.serverCapabilityFilter = serverCapabilityFilter; /* Send the request */ UA_FindServersOnNetworkResponse response; __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKREQUEST], &response, &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKRESPONSE]); /* Process the response */ retval = response.responseHeader.serviceResult; if(retval == UA_STATUSCODE_GOOD) { *serverOnNetworkSize = response.serversSize; *serverOnNetwork = response.servers; response.serversSize = 0; response.servers = NULL; } else { *serverOnNetworkSize = 0; *serverOnNetwork = NULL; } /* Clean up */ UA_FindServersOnNetworkResponse_deleteMembers(&response); if(!connected) UA_Client_disconnect(client); return retval; } #endif /*********************************** amalgamated original file "/home/jvoe/open62541/src/client/ua_client_highlevel.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2015-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2015 (c) Oleksiy Vasylyev * Copyright 2017 (c) Florian Palm * Copyright 2016 (c) Chris Iatrou * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2018 (c) Fabian Arndt * Copyright 2018 (c) Peter Rustler, basyskom GmbH */ UA_StatusCode UA_Client_NamespaceGetIndex(UA_Client *client, UA_String *namespaceUri, UA_UInt16 *namespaceIndex) { UA_ReadRequest request; UA_ReadRequest_init(&request); UA_ReadValueId id; UA_ReadValueId_init(&id); id.attributeId = UA_ATTRIBUTEID_VALUE; id.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY); request.nodesToRead = &id; request.nodesToReadSize = 1; UA_ReadResponse response = UA_Client_Service_read(client, request); UA_StatusCode retval = UA_STATUSCODE_GOOD; if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) retval = response.responseHeader.serviceResult; else if(response.resultsSize != 1 || !response.results[0].hasValue) retval = UA_STATUSCODE_BADNODEATTRIBUTESINVALID; else if(response.results[0].value.type != &UA_TYPES[UA_TYPES_STRING]) retval = UA_STATUSCODE_BADTYPEMISMATCH; if(retval != UA_STATUSCODE_GOOD) { UA_ReadResponse_deleteMembers(&response); return retval; } retval = UA_STATUSCODE_BADNOTFOUND; UA_String *ns = (UA_String *)response.results[0].value.data; for(size_t i = 0; i < response.results[0].value.arrayLength; ++i) { if(UA_String_equal(namespaceUri, &ns[i])) { *namespaceIndex = (UA_UInt16)i; retval = UA_STATUSCODE_GOOD; break; } } UA_ReadResponse_deleteMembers(&response); return retval; } UA_StatusCode UA_Client_forEachChildNodeCall(UA_Client *client, UA_NodeId parentNodeId, UA_NodeIteratorCallback callback, void *handle) { UA_BrowseRequest bReq; UA_BrowseRequest_init(&bReq); bReq.requestedMaxReferencesPerNode = 0; bReq.nodesToBrowse = UA_BrowseDescription_new(); bReq.nodesToBrowseSize = 1; UA_NodeId_copy(&parentNodeId, &bReq.nodesToBrowse[0].nodeId); bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL; //return everything bReq.nodesToBrowse[0].browseDirection = UA_BROWSEDIRECTION_BOTH; UA_BrowseResponse bResp = UA_Client_Service_browse(client, bReq); UA_StatusCode retval = bResp.responseHeader.serviceResult; if(retval == UA_STATUSCODE_GOOD) { for(size_t i = 0; i < bResp.resultsSize; ++i) { for(size_t j = 0; j < bResp.results[i].referencesSize; ++j) { UA_ReferenceDescription *ref = &bResp.results[i].references[j]; retval |= callback(ref->nodeId.nodeId, !ref->isForward, ref->referenceTypeId, handle); } } } UA_BrowseRequest_deleteMembers(&bReq); UA_BrowseResponse_deleteMembers(&bResp); return retval; } /*******************/ /* Node Management */ /*******************/ UA_StatusCode UA_Client_addReference(UA_Client *client, const UA_NodeId sourceNodeId, const UA_NodeId referenceTypeId, UA_Boolean isForward, const UA_String targetServerUri, const UA_ExpandedNodeId targetNodeId, UA_NodeClass targetNodeClass) { UA_AddReferencesItem item; UA_AddReferencesItem_init(&item); item.sourceNodeId = sourceNodeId; item.referenceTypeId = referenceTypeId; item.isForward = isForward; item.targetServerUri = targetServerUri; item.targetNodeId = targetNodeId; item.targetNodeClass = targetNodeClass; UA_AddReferencesRequest request; UA_AddReferencesRequest_init(&request); request.referencesToAdd = &item; request.referencesToAddSize = 1; UA_AddReferencesResponse response = UA_Client_Service_addReferences(client, request); UA_StatusCode retval = response.responseHeader.serviceResult; if(retval != UA_STATUSCODE_GOOD) { UA_AddReferencesResponse_deleteMembers(&response); return retval; } if(response.resultsSize != 1) { UA_AddReferencesResponse_deleteMembers(&response); return UA_STATUSCODE_BADUNEXPECTEDERROR; } retval = response.results[0]; UA_AddReferencesResponse_deleteMembers(&response); return retval; } UA_StatusCode UA_Client_deleteReference(UA_Client *client, const UA_NodeId sourceNodeId, const UA_NodeId referenceTypeId, UA_Boolean isForward, const UA_ExpandedNodeId targetNodeId, UA_Boolean deleteBidirectional) { UA_DeleteReferencesItem item; UA_DeleteReferencesItem_init(&item); item.sourceNodeId = sourceNodeId; item.referenceTypeId = referenceTypeId; item.isForward = isForward; item.targetNodeId = targetNodeId; item.deleteBidirectional = deleteBidirectional; UA_DeleteReferencesRequest request; UA_DeleteReferencesRequest_init(&request); request.referencesToDelete = &item; request.referencesToDeleteSize = 1; UA_DeleteReferencesResponse response = UA_Client_Service_deleteReferences(client, request); UA_StatusCode retval = response.responseHeader.serviceResult; if(retval != UA_STATUSCODE_GOOD) { UA_DeleteReferencesResponse_deleteMembers(&response); return retval; } if(response.resultsSize != 1) { UA_DeleteReferencesResponse_deleteMembers(&response); return UA_STATUSCODE_BADUNEXPECTEDERROR; } retval = response.results[0]; UA_DeleteReferencesResponse_deleteMembers(&response); return retval; } UA_StatusCode UA_Client_deleteNode(UA_Client *client, const UA_NodeId nodeId, UA_Boolean deleteTargetReferences) { UA_DeleteNodesItem item; UA_DeleteNodesItem_init(&item); item.nodeId = nodeId; item.deleteTargetReferences = deleteTargetReferences; UA_DeleteNodesRequest request; UA_DeleteNodesRequest_init(&request); request.nodesToDelete = &item; request.nodesToDeleteSize = 1; UA_DeleteNodesResponse response = UA_Client_Service_deleteNodes(client, request); UA_StatusCode retval = response.responseHeader.serviceResult; if(retval != UA_STATUSCODE_GOOD) { UA_DeleteNodesResponse_deleteMembers(&response); return retval; } if(response.resultsSize != 1) { UA_DeleteNodesResponse_deleteMembers(&response); return UA_STATUSCODE_BADUNEXPECTEDERROR; } retval = response.results[0]; UA_DeleteNodesResponse_deleteMembers(&response); return retval; } UA_StatusCode __UA_Client_addNode(UA_Client *client, const UA_NodeClass nodeClass, const UA_NodeId requestedNewNodeId, const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId, const UA_QualifiedName browseName, const UA_NodeId typeDefinition, const UA_NodeAttributes *attr, const UA_DataType *attributeType, UA_NodeId *outNewNodeId) { UA_AddNodesRequest request; UA_AddNodesRequest_init(&request); UA_AddNodesItem item; UA_AddNodesItem_init(&item); item.parentNodeId.nodeId = parentNodeId; item.referenceTypeId = referenceTypeId; item.requestedNewNodeId.nodeId = requestedNewNodeId; item.browseName = browseName; item.nodeClass = nodeClass; item.typeDefinition.nodeId = typeDefinition; item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE; item.nodeAttributes.content.decoded.type = attributeType; item.nodeAttributes.content.decoded.data = (void*)(uintptr_t)attr; // hack. is not written into. request.nodesToAdd = &item; request.nodesToAddSize = 1; UA_AddNodesResponse response = UA_Client_Service_addNodes(client, request); UA_StatusCode retval = response.responseHeader.serviceResult; if(retval != UA_STATUSCODE_GOOD) { UA_AddNodesResponse_deleteMembers(&response); return retval; } if(response.resultsSize != 1) { UA_AddNodesResponse_deleteMembers(&response); return UA_STATUSCODE_BADUNEXPECTEDERROR; } /* Move the id of the created node */ retval = response.results[0].statusCode; if(retval == UA_STATUSCODE_GOOD && outNewNodeId) { *outNewNodeId = response.results[0].addedNodeId; UA_NodeId_init(&response.results[0].addedNodeId); } UA_AddNodesResponse_deleteMembers(&response); return retval; } /********/ /* Call */ /********/ #ifdef UA_ENABLE_METHODCALLS UA_StatusCode UA_Client_call(UA_Client *client, const UA_NodeId objectId, const UA_NodeId methodId, size_t inputSize, const UA_Variant *input, size_t *outputSize, UA_Variant **output) { /* Set up the request */ UA_CallRequest request; UA_CallRequest_init(&request); UA_CallMethodRequest item; UA_CallMethodRequest_init(&item); item.methodId = methodId; item.objectId = objectId; item.inputArguments = (UA_Variant *)(void*)(uintptr_t)input; // cast const... item.inputArgumentsSize = inputSize; request.methodsToCall = &item; request.methodsToCallSize = 1; /* Call the service */ UA_CallResponse response = UA_Client_Service_call(client, request); UA_StatusCode retval = response.responseHeader.serviceResult; if(retval == UA_STATUSCODE_GOOD) { if(response.resultsSize == 1) retval = response.results[0].statusCode; else retval = UA_STATUSCODE_BADUNEXPECTEDERROR; } if(retval != UA_STATUSCODE_GOOD) { UA_CallResponse_deleteMembers(&response); return retval; } /* Move the output arguments */ if(output != NULL && outputSize != NULL) { *output = response.results[0].outputArguments; *outputSize = response.results[0].outputArgumentsSize; response.results[0].outputArguments = NULL; response.results[0].outputArgumentsSize = 0; } UA_CallResponse_deleteMembers(&response); return retval; } #endif /********************/ /* Write Attributes */ /********************/ UA_StatusCode __UA_Client_writeAttribute(UA_Client *client, const UA_NodeId *nodeId, UA_AttributeId attributeId, const void *in, const UA_DataType *inDataType) { if(!in) return UA_STATUSCODE_BADTYPEMISMATCH; UA_WriteValue wValue; UA_WriteValue_init(&wValue); wValue.nodeId = *nodeId; wValue.attributeId = attributeId; if(attributeId == UA_ATTRIBUTEID_VALUE) wValue.value.value = *(const UA_Variant*)in; else /* hack. is never written into. */ UA_Variant_setScalar(&wValue.value.value, (void*)(uintptr_t)in, inDataType); wValue.value.hasValue = true; UA_WriteRequest wReq; UA_WriteRequest_init(&wReq); wReq.nodesToWrite = &wValue; wReq.nodesToWriteSize = 1; UA_WriteResponse wResp = UA_Client_Service_write(client, wReq); UA_StatusCode retval = wResp.responseHeader.serviceResult; if(retval == UA_STATUSCODE_GOOD) { if(wResp.resultsSize == 1) retval = wResp.results[0]; else retval = UA_STATUSCODE_BADUNEXPECTEDERROR; } UA_WriteResponse_deleteMembers(&wResp); return retval; } UA_StatusCode UA_Client_writeArrayDimensionsAttribute(UA_Client *client, const UA_NodeId nodeId, size_t newArrayDimensionsSize, const UA_UInt32 *newArrayDimensions) { if(!newArrayDimensions) return UA_STATUSCODE_BADTYPEMISMATCH; UA_WriteValue wValue; UA_WriteValue_init(&wValue); wValue.nodeId = nodeId; wValue.attributeId = UA_ATTRIBUTEID_ARRAYDIMENSIONS; UA_Variant_setArray(&wValue.value.value, (void*)(uintptr_t)newArrayDimensions, newArrayDimensionsSize, &UA_TYPES[UA_TYPES_UINT32]); wValue.value.hasValue = true; UA_WriteRequest wReq; UA_WriteRequest_init(&wReq); wReq.nodesToWrite = &wValue; wReq.nodesToWriteSize = 1; UA_WriteResponse wResp = UA_Client_Service_write(client, wReq); UA_StatusCode retval = wResp.responseHeader.serviceResult; if(retval == UA_STATUSCODE_GOOD) { if(wResp.resultsSize == 1) retval = wResp.results[0]; else retval = UA_STATUSCODE_BADUNEXPECTEDERROR; } UA_WriteResponse_deleteMembers(&wResp); return retval; } /*******************/ /* Read Attributes */ /*******************/ UA_StatusCode __UA_Client_readAttribute(UA_Client *client, const UA_NodeId *nodeId, UA_AttributeId attributeId, void *out, const UA_DataType *outDataType) { UA_ReadValueId item; UA_ReadValueId_init(&item); item.nodeId = *nodeId; item.attributeId = attributeId; UA_ReadRequest request; UA_ReadRequest_init(&request); request.nodesToRead = &item; request.nodesToReadSize = 1; UA_ReadResponse response = UA_Client_Service_read(client, request); UA_StatusCode retval = response.responseHeader.serviceResult; if(retval == UA_STATUSCODE_GOOD) { if(response.resultsSize == 1) retval = response.results[0].status; else retval = UA_STATUSCODE_BADUNEXPECTEDERROR; } if(retval != UA_STATUSCODE_GOOD) { UA_ReadResponse_deleteMembers(&response); return retval; } /* Set the StatusCode */ UA_DataValue *res = response.results; if(res->hasStatus) retval = res->status; /* Return early of no value is given */ if(!res->hasValue) { if(retval == UA_STATUSCODE_GOOD) retval = UA_STATUSCODE_BADUNEXPECTEDERROR; UA_ReadResponse_deleteMembers(&response); return retval; } /* Copy value into out */ if(attributeId == UA_ATTRIBUTEID_VALUE) { memcpy(out, &res->value, sizeof(UA_Variant)); UA_Variant_init(&res->value); } else if(attributeId == UA_ATTRIBUTEID_NODECLASS) { memcpy(out, (UA_NodeClass*)res->value.data, sizeof(UA_NodeClass)); } else if(UA_Variant_isScalar(&res->value) && res->value.type == outDataType) { memcpy(out, res->value.data, res->value.type->memSize); UA_free(res->value.data); res->value.data = NULL; } else { retval = UA_STATUSCODE_BADUNEXPECTEDERROR; } UA_ReadResponse_deleteMembers(&response); return retval; } static UA_StatusCode processReadArrayDimensionsResult(UA_ReadResponse *response, UA_UInt32 **outArrayDimensions, size_t *outArrayDimensionsSize) { UA_StatusCode retval = response->responseHeader.serviceResult; if(retval != UA_STATUSCODE_GOOD) return retval; if(response->resultsSize != 1) return UA_STATUSCODE_BADUNEXPECTEDERROR; retval = response->results[0].status; if(retval != UA_STATUSCODE_GOOD) return retval; UA_DataValue *res = &response->results[0]; if(!res->hasValue || UA_Variant_isScalar(&res->value) || res->value.type != &UA_TYPES[UA_TYPES_UINT32]) return UA_STATUSCODE_BADUNEXPECTEDERROR; /* Move results */ *outArrayDimensions = (UA_UInt32*)res->value.data; *outArrayDimensionsSize = res->value.arrayLength; res->value.data = NULL; res->value.arrayLength = 0; return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Client_readArrayDimensionsAttribute(UA_Client *client, const UA_NodeId nodeId, size_t *outArrayDimensionsSize, UA_UInt32 **outArrayDimensions) { UA_ReadValueId item; UA_ReadValueId_init(&item); item.nodeId = nodeId; item.attributeId = UA_ATTRIBUTEID_ARRAYDIMENSIONS; UA_ReadRequest request; UA_ReadRequest_init(&request); request.nodesToRead = &item; request.nodesToReadSize = 1; UA_ReadResponse response = UA_Client_Service_read(client, request); UA_StatusCode retval = processReadArrayDimensionsResult(&response, outArrayDimensions, outArrayDimensionsSize); UA_ReadResponse_deleteMembers(&response); return retval; } /*********************/ /* Historical Access */ /*********************/ #ifdef UA_ENABLE_HISTORIZING static UA_HistoryReadResponse __UA_Client_HistoryRead(UA_Client *client, const UA_NodeId *nodeId, UA_ExtensionObject* details, UA_String indexRange, UA_TimestampsToReturn timestampsToReturn, UA_ByteString continuationPoint, UA_Boolean releaseConti) { UA_HistoryReadValueId item; UA_HistoryReadValueId_init(&item); item.nodeId = *nodeId; item.indexRange = indexRange; item.continuationPoint = continuationPoint; item.dataEncoding = UA_QUALIFIEDNAME(0, "Default Binary"); UA_HistoryReadRequest request; UA_HistoryReadRequest_init(&request); request.nodesToRead = &item; request.nodesToReadSize = 1; request.timestampsToReturn = timestampsToReturn; // Defaults to Source request.releaseContinuationPoints = releaseConti; // No values are returned, if true /* Build ReadDetails */ request.historyReadDetails = *details; return UA_Client_Service_historyRead(client, request); } static UA_StatusCode __UA_Client_HistoryRead_service(UA_Client *client, const UA_NodeId *nodeId, const UA_HistoricalIteratorCallback callback, UA_ExtensionObject *details, UA_String indexRange, UA_TimestampsToReturn timestampsToReturn, void *callbackContext) { UA_ByteString continuationPoint = UA_BYTESTRING_NULL; UA_Boolean continuationAvail = false; UA_Boolean fetchMore = false; UA_StatusCode retval = UA_STATUSCODE_GOOD; do { /* We release the continuation point, if no more data is requested by the user */ UA_Boolean cleanup = !fetchMore && continuationAvail; UA_HistoryReadResponse response = __UA_Client_HistoryRead(client, nodeId, details, indexRange, timestampsToReturn, continuationPoint, cleanup); if (cleanup) { retval = response.responseHeader.serviceResult; cleanup: UA_HistoryReadResponse_deleteMembers(&response); UA_ByteString_deleteMembers(&continuationPoint); return retval; } retval = response.responseHeader.serviceResult; if (retval == UA_STATUSCODE_GOOD) { if (response.resultsSize == 1) retval = response.results[0].statusCode; else retval = UA_STATUSCODE_BADUNEXPECTEDERROR; } if (retval != UA_STATUSCODE_GOOD) goto cleanup; UA_HistoryReadResult *res = response.results; /* Clear old and check / store new continuation point */ UA_ByteString_deleteMembers(&continuationPoint); UA_ByteString_copy(&res->continuationPoint, &continuationPoint); continuationAvail = !UA_ByteString_equal(&continuationPoint, &UA_BYTESTRING_NULL); /* Client callback with possibility to request further values */ fetchMore = callback(client, nodeId, continuationAvail, &res->historyData, callbackContext); /* Regular cleanup */ UA_HistoryReadResponse_deleteMembers(&response); } while (continuationAvail); return retval; } #ifdef UA_ENABLE_EXPERIMENTAL_HISTORIZING UA_StatusCode UA_Client_HistoryRead_events(UA_Client *client, const UA_NodeId *nodeId, const UA_HistoricalIteratorCallback callback, UA_DateTime startTime, UA_DateTime endTime, UA_String indexRange, const UA_EventFilter filter, UA_UInt32 numValuesPerNode, UA_TimestampsToReturn timestampsToReturn, void *callbackContext) { UA_ReadEventDetails details; UA_ReadEventDetails_init(&details); details.filter = filter; // At least two of the following parameters must be set details.numValuesPerNode = numValuesPerNode; // 0 = return all / max server is capable of details.startTime = startTime; details.endTime = endTime; UA_ExtensionObject detailsExtensionObject; UA_ExtensionObject_init(&detailsExtensionObject); detailsExtensionObject.content.decoded.type = &UA_TYPES[UA_TYPES_READEVENTDETAILS]; detailsExtensionObject.content.decoded.data = &details; detailsExtensionObject.encoding = UA_EXTENSIONOBJECT_DECODED; return __UA_Client_HistoryRead_service(client, nodeId, callback, &detailsExtensionObject, indexRange, timestampsToReturn, callbackContext); } #endif // UA_ENABLE_EXPERIMENTAL_HISTORIZING static UA_StatusCode __UA_Client_HistoryRead_service_rawMod(UA_Client *client, const UA_NodeId *nodeId, const UA_HistoricalIteratorCallback callback, UA_DateTime startTime,UA_DateTime endTime, UA_String indexRange, UA_Boolean returnBounds, UA_UInt32 numValuesPerNode, UA_Boolean readModified, UA_TimestampsToReturn timestampsToReturn, void *callbackContext) { UA_ReadRawModifiedDetails details; UA_ReadRawModifiedDetails_init(&details); details.isReadModified = readModified; // Return only modified values details.returnBounds = returnBounds; // Return values pre / post given range // At least two of the following parameters must be set details.numValuesPerNode = numValuesPerNode; // 0 = return all / max server is capable of details.startTime = startTime; details.endTime = endTime; UA_ExtensionObject detailsExtensionObject; UA_ExtensionObject_init(&detailsExtensionObject); detailsExtensionObject.content.decoded.type = &UA_TYPES[UA_TYPES_READRAWMODIFIEDDETAILS]; detailsExtensionObject.content.decoded.data = &details; detailsExtensionObject.encoding = UA_EXTENSIONOBJECT_DECODED; return __UA_Client_HistoryRead_service(client, nodeId, callback, &detailsExtensionObject, indexRange, timestampsToReturn, callbackContext); } UA_StatusCode UA_Client_HistoryRead_raw(UA_Client *client, const UA_NodeId *nodeId, const UA_HistoricalIteratorCallback callback, UA_DateTime startTime, UA_DateTime endTime, UA_String indexRange, UA_Boolean returnBounds, UA_UInt32 numValuesPerNode, UA_TimestampsToReturn timestampsToReturn, void *callbackContext) { return __UA_Client_HistoryRead_service_rawMod(client, nodeId, callback, startTime, endTime, indexRange, returnBounds, numValuesPerNode, false, timestampsToReturn, callbackContext); } #ifdef UA_ENABLE_EXPERIMENTAL_HISTORIZING UA_StatusCode UA_Client_HistoryRead_modified(UA_Client *client, const UA_NodeId *nodeId, const UA_HistoricalIteratorCallback callback, UA_DateTime startTime, UA_DateTime endTime, UA_String indexRange, UA_Boolean returnBounds, UA_UInt32 maxItems, UA_TimestampsToReturn timestampsToReturn, void *callbackContext) { return __UA_Client_HistoryRead_service_rawMod(client, nodeId, callback, startTime, endTime, indexRange, returnBounds, maxItems, true, timestampsToReturn, callbackContext); } #endif // UA_ENABLE_EXPERIMENTAL_HISTORIZING static UA_HistoryUpdateResponse __UA_Client_HistoryUpdate(UA_Client *client, void *details, size_t typeId) { UA_HistoryUpdateRequest request; UA_HistoryUpdateRequest_init(&request); UA_ExtensionObject extension; UA_ExtensionObject_init(&extension); request.historyUpdateDetailsSize = 1; request.historyUpdateDetails = &extension; extension.encoding = UA_EXTENSIONOBJECT_DECODED; extension.content.decoded.type = &UA_TYPES[typeId]; extension.content.decoded.data = details; UA_HistoryUpdateResponse response; response = UA_Client_Service_historyUpdate(client, request); //UA_HistoryUpdateRequest_deleteMembers(&request); return response; } static UA_StatusCode __UA_Client_HistoryUpdate_updateData(UA_Client *client, const UA_NodeId *nodeId, UA_PerformUpdateType type, UA_DataValue *value) { UA_StatusCode ret = UA_STATUSCODE_GOOD; UA_UpdateDataDetails details; UA_UpdateDataDetails_init(&details); details.performInsertReplace = type; details.updateValuesSize = 1; details.updateValues = value; UA_NodeId_copy(nodeId, &details.nodeId); UA_HistoryUpdateResponse response; response = __UA_Client_HistoryUpdate(client, &details, UA_TYPES_UPDATEDATADETAILS); if (response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) { ret = response.responseHeader.serviceResult; goto cleanup; } if (response.resultsSize != 1 || response.results[0].operationResultsSize != 1) { ret = UA_STATUSCODE_BADUNEXPECTEDERROR; goto cleanup; } if (response.results[0].statusCode != UA_STATUSCODE_GOOD) { ret = response.results[0].statusCode; goto cleanup; } ret = response.results[0].operationResults[0]; cleanup: UA_HistoryUpdateResponse_deleteMembers(&response); UA_NodeId_deleteMembers(&details.nodeId); return ret; } UA_StatusCode UA_Client_HistoryUpdate_insert(UA_Client *client, const UA_NodeId *nodeId, UA_DataValue *value) { return __UA_Client_HistoryUpdate_updateData(client, nodeId, UA_PERFORMUPDATETYPE_INSERT, value); } UA_StatusCode UA_Client_HistoryUpdate_replace(UA_Client *client, const UA_NodeId *nodeId, UA_DataValue *value) { return __UA_Client_HistoryUpdate_updateData(client, nodeId, UA_PERFORMUPDATETYPE_REPLACE, value); } UA_StatusCode UA_Client_HistoryUpdate_update(UA_Client *client, const UA_NodeId *nodeId, UA_DataValue *value) { return __UA_Client_HistoryUpdate_updateData(client, nodeId, UA_PERFORMUPDATETYPE_UPDATE, value); } UA_StatusCode UA_Client_HistoryUpdate_deleteRaw(UA_Client *client, const UA_NodeId *nodeId, UA_DateTime startTimestamp, UA_DateTime endTimestamp) { UA_StatusCode ret = UA_STATUSCODE_GOOD; UA_DeleteRawModifiedDetails details; UA_DeleteRawModifiedDetails_init(&details); details.isDeleteModified = false; details.startTime = startTimestamp; details.endTime = endTimestamp; UA_NodeId_copy(nodeId, &details.nodeId); UA_HistoryUpdateResponse response; response = __UA_Client_HistoryUpdate(client, &details, UA_TYPES_DELETERAWMODIFIEDDETAILS); if (response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) { ret = response.responseHeader.serviceResult; goto cleanup; } if (response.resultsSize != 1) { ret = UA_STATUSCODE_BADUNEXPECTEDERROR; goto cleanup; } ret = response.results[0].statusCode; cleanup: UA_HistoryUpdateResponse_deleteMembers(&response); UA_NodeId_deleteMembers(&details.nodeId); return ret; } #endif // UA_ENABLE_HISTORIZING /* Async Functions */ static void ValueAttributeRead(UA_Client *client, void *userdata, UA_UInt32 requestId, void *response) { if(!response) return; /* Find the callback for the response */ CustomCallback *cc; LIST_FOREACH(cc, &client->customCallbacks, pointers) { if(cc->callbackId == requestId) break; } if(!cc) return; UA_ReadResponse *rr = (UA_ReadResponse *) response; UA_DataValue *res = rr->results; UA_Boolean done = false; if(rr->resultsSize == 1 && res != NULL && res->hasValue) { if(cc->attributeId == UA_ATTRIBUTEID_VALUE) { /* Call directly with the variant */ cc->callback(client, userdata, requestId, &res->value); done = true; } else if(UA_Variant_isScalar(&res->value) && res->value.type == cc->outDataType) { /* Unpack the value */ UA_STACKARRAY(UA_Byte, value, cc->outDataType->memSize); memcpy(&value, res->value.data, cc->outDataType->memSize); cc->callback(client, userdata, requestId, &value); done = true; } } /* Could not process, delete the callback anyway */ if(!done) UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Cannot process the response to the async read " "request %u", requestId); LIST_REMOVE(cc, pointers); UA_free(cc); } /*Read Attributes*/ UA_StatusCode __UA_Client_readAttribute_async(UA_Client *client, const UA_NodeId *nodeId, UA_AttributeId attributeId, const UA_DataType *outDataType, UA_ClientAsyncServiceCallback callback, void *userdata, UA_UInt32 *reqId) { UA_ReadValueId item; UA_ReadValueId_init(&item); item.nodeId = *nodeId; item.attributeId = attributeId; UA_ReadRequest request; UA_ReadRequest_init(&request); request.nodesToRead = &item; request.nodesToReadSize = 1; __UA_Client_AsyncService(client, &request, &UA_TYPES[UA_TYPES_READREQUEST], ValueAttributeRead, &UA_TYPES[UA_TYPES_READRESPONSE], userdata, reqId); CustomCallback *cc = (CustomCallback*) UA_malloc(sizeof(CustomCallback)); if (!cc) return UA_STATUSCODE_BADOUTOFMEMORY; cc->callback = callback; cc->callbackId = *reqId; cc->attributeId = attributeId; cc->outDataType = outDataType; LIST_INSERT_HEAD(&client->customCallbacks, cc, pointers); return UA_STATUSCODE_GOOD; } /*Write Attributes*/ UA_StatusCode __UA_Client_writeAttribute_async(UA_Client *client, const UA_NodeId *nodeId, UA_AttributeId attributeId, const void *in, const UA_DataType *inDataType, UA_ClientAsyncServiceCallback callback, void *userdata, UA_UInt32 *reqId) { if (!in) return UA_STATUSCODE_BADTYPEMISMATCH; UA_WriteValue wValue; UA_WriteValue_init(&wValue); wValue.nodeId = *nodeId; wValue.attributeId = attributeId; if (attributeId == UA_ATTRIBUTEID_VALUE) wValue.value.value = *(const UA_Variant*) in; else /* hack. is never written into. */ UA_Variant_setScalar(&wValue.value.value, (void*) (uintptr_t) in, inDataType); wValue.value.hasValue = true; UA_WriteRequest wReq; UA_WriteRequest_init(&wReq); wReq.nodesToWrite = &wValue; wReq.nodesToWriteSize = 1; return __UA_Client_AsyncService(client, &wReq, &UA_TYPES[UA_TYPES_WRITEREQUEST], callback, &UA_TYPES[UA_TYPES_WRITERESPONSE], userdata, reqId); } /*Node Management*/ UA_StatusCode UA_EXPORT __UA_Client_addNode_async(UA_Client *client, const UA_NodeClass nodeClass, const UA_NodeId requestedNewNodeId, const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId, const UA_QualifiedName browseName, const UA_NodeId typeDefinition, const UA_NodeAttributes *attr, const UA_DataType *attributeType, UA_NodeId *outNewNodeId, UA_ClientAsyncServiceCallback callback, void *userdata, UA_UInt32 *reqId) { UA_AddNodesRequest request; UA_AddNodesRequest_init(&request); UA_AddNodesItem item; UA_AddNodesItem_init(&item); item.parentNodeId.nodeId = parentNodeId; item.referenceTypeId = referenceTypeId; item.requestedNewNodeId.nodeId = requestedNewNodeId; item.browseName = browseName; item.nodeClass = nodeClass; item.typeDefinition.nodeId = typeDefinition; item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE; item.nodeAttributes.content.decoded.type = attributeType; item.nodeAttributes.content.decoded.data = (void*) (uintptr_t) attr; // hack. is not written into. request.nodesToAdd = &item; request.nodesToAddSize = 1; return __UA_Client_AsyncService(client, &request, &UA_TYPES[UA_TYPES_ADDNODESREQUEST], callback, &UA_TYPES[UA_TYPES_ADDNODESRESPONSE], userdata, reqId); } /* Misc Highlevel Functions */ #ifdef UA_ENABLE_METHODCALLS UA_StatusCode __UA_Client_call_async(UA_Client *client, const UA_NodeId objectId, const UA_NodeId methodId, size_t inputSize, const UA_Variant *input, UA_ClientAsyncServiceCallback callback, void *userdata, UA_UInt32 *reqId) { UA_CallRequest request; UA_CallRequest_init(&request); UA_CallMethodRequest item; UA_CallMethodRequest_init(&item); item.methodId = methodId; item.objectId = objectId; item.inputArguments = (UA_Variant *) (void*) (uintptr_t) input; // cast const... item.inputArgumentsSize = inputSize; request.methodsToCall = &item; request.methodsToCallSize = 1; return __UA_Client_AsyncService(client, &request, &UA_TYPES[UA_TYPES_CALLREQUEST], callback, &UA_TYPES[UA_TYPES_CALLRESPONSE], userdata, reqId); } #endif UA_StatusCode __UA_Client_translateBrowsePathsToNodeIds_async(UA_Client *client, char *paths[], UA_UInt32 ids[], size_t pathSize, UA_ClientAsyncServiceCallback callback, void *userdata, UA_UInt32 *reqId) { UA_BrowsePath browsePath; UA_BrowsePath_init(&browsePath); browsePath.startingNode = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER); browsePath.relativePath.elements = (UA_RelativePathElement*) UA_Array_new( pathSize, &UA_TYPES[UA_TYPES_RELATIVEPATHELEMENT]); if (!browsePath.relativePath.elements) return UA_STATUSCODE_BADOUTOFMEMORY; browsePath.relativePath.elementsSize = pathSize; UA_TranslateBrowsePathsToNodeIdsRequest request; UA_TranslateBrowsePathsToNodeIdsRequest_init(&request); request.browsePaths = &browsePath; request.browsePathsSize = 1; UA_StatusCode retval = __UA_Client_AsyncService(client, &request, &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSREQUEST], callback, &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE], userdata, reqId); if (retval != UA_STATUSCODE_GOOD) { UA_Array_delete(browsePath.relativePath.elements, browsePath.relativePath.elementsSize, &UA_TYPES[UA_TYPES_RELATIVEPATHELEMENT]); return retval; } UA_BrowsePath_deleteMembers(&browsePath); return retval; } /*********************************** amalgamated original file "/home/jvoe/open62541/src/client/ua_client_subscriptions.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2015-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2015 (c) Oleksiy Vasylyev * Copyright 2016 (c) Sten Grüner * Copyright 2017-2018 (c) Thomas Stalder, Blue Time Concept SA * Copyright 2016-2017 (c) Florian Palm * Copyright 2017 (c) Frank Meerkötter * Copyright 2017 (c) Stefan Profanter, fortiss GmbH */ #ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */ /*****************/ /* Subscriptions */ /*****************/ UA_CreateSubscriptionResponse UA_EXPORT UA_Client_Subscriptions_create(UA_Client *client, const UA_CreateSubscriptionRequest request, void *subscriptionContext, UA_Client_StatusChangeNotificationCallback statusChangeCallback, UA_Client_DeleteSubscriptionCallback deleteCallback) { UA_CreateSubscriptionResponse response; UA_CreateSubscriptionResponse_init(&response); /* Allocate the internal representation */ UA_Client_Subscription *newSub = (UA_Client_Subscription*) UA_malloc(sizeof(UA_Client_Subscription)); if(!newSub) { response.responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY; return response; } /* Send the request as a synchronous service call */ __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONREQUEST], &response, &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONRESPONSE]); if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_free(newSub); return response; } /* Prepare the internal representation */ newSub->context = subscriptionContext; newSub->subscriptionId = response.subscriptionId; newSub->sequenceNumber = 0; newSub->lastActivity = UA_DateTime_nowMonotonic(); newSub->statusChangeCallback = statusChangeCallback; newSub->deleteCallback = deleteCallback; newSub->publishingInterval = response.revisedPublishingInterval; newSub->maxKeepAliveCount = response.revisedMaxKeepAliveCount; LIST_INIT(&newSub->monitoredItems); LIST_INSERT_HEAD(&client->subscriptions, newSub, listEntry); return response; } static UA_Client_Subscription * findSubscription(const UA_Client *client, UA_UInt32 subscriptionId) { UA_Client_Subscription *sub = NULL; LIST_FOREACH(sub, &client->subscriptions, listEntry) { if(sub->subscriptionId == subscriptionId) break; } return sub; } UA_ModifySubscriptionResponse UA_EXPORT UA_Client_Subscriptions_modify(UA_Client *client, const UA_ModifySubscriptionRequest request) { UA_ModifySubscriptionResponse response; UA_ModifySubscriptionResponse_init(&response); /* Find the internal representation */ UA_Client_Subscription *sub = findSubscription(client, request.subscriptionId); if(!sub) { response.responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; return response; } /* Call the service */ __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONREQUEST], &response, &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONRESPONSE]); /* Adjust the internal representation */ sub->publishingInterval = response.revisedPublishingInterval; sub->maxKeepAliveCount = response.revisedMaxKeepAliveCount; return response; } static void UA_Client_Subscription_deleteInternal(UA_Client *client, UA_Client_Subscription *sub) { /* Remove the MonitoredItems */ UA_Client_MonitoredItem *mon, *mon_tmp; LIST_FOREACH_SAFE(mon, &sub->monitoredItems, listEntry, mon_tmp) UA_Client_MonitoredItem_remove(client, sub, mon); /* Call the delete callback */ if(sub->deleteCallback) sub->deleteCallback(client, sub->subscriptionId, sub->context); /* Remove */ LIST_REMOVE(sub, listEntry); UA_free(sub); } UA_DeleteSubscriptionsResponse UA_EXPORT UA_Client_Subscriptions_delete(UA_Client *client, const UA_DeleteSubscriptionsRequest request) { UA_STACKARRAY(UA_Client_Subscription*, subs, request.subscriptionIdsSize); memset(subs, 0, sizeof(void*) * request.subscriptionIdsSize); /* temporary remove the subscriptions from the list */ for(size_t i = 0; i < request.subscriptionIdsSize; i++) { subs[i] = findSubscription(client, request.subscriptionIds[i]); if (subs[i]) LIST_REMOVE(subs[i], listEntry); } /* Send the request */ UA_DeleteSubscriptionsResponse response; __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSREQUEST], &response, &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSRESPONSE]); if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) goto cleanup; if(request.subscriptionIdsSize != response.resultsSize) { response.responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR; goto cleanup; } /* Loop over the removed subscriptions and remove internally */ for(size_t i = 0; i < request.subscriptionIdsSize; i++) { if(response.results[i] != UA_STATUSCODE_GOOD && response.results[i] != UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID) { /* Something was wrong, reinsert the subscription in the list */ if (subs[i]) LIST_INSERT_HEAD(&client->subscriptions, subs[i], listEntry); continue; } if(!subs[i]) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "No internal representation of subscription %u", request.subscriptionIds[i]); continue; } LIST_INSERT_HEAD(&client->subscriptions, subs[i], listEntry); UA_Client_Subscription_deleteInternal(client, subs[i]); } return response; cleanup: for(size_t i = 0; i < request.subscriptionIdsSize; i++) { if (subs[i]) { LIST_INSERT_HEAD(&client->subscriptions, subs[i], listEntry); } } return response; } UA_StatusCode UA_EXPORT UA_Client_Subscriptions_deleteSingle(UA_Client *client, UA_UInt32 subscriptionId) { UA_DeleteSubscriptionsRequest request; UA_DeleteSubscriptionsRequest_init(&request); request.subscriptionIds = &subscriptionId; request.subscriptionIdsSize = 1; UA_DeleteSubscriptionsResponse response = UA_Client_Subscriptions_delete(client, request); UA_StatusCode retval = response.responseHeader.serviceResult; if(retval != UA_STATUSCODE_GOOD) { UA_DeleteSubscriptionsResponse_deleteMembers(&response); return retval; } if(response.resultsSize != 1) { UA_DeleteSubscriptionsResponse_deleteMembers(&response); return UA_STATUSCODE_BADINTERNALERROR; } retval = response.results[0]; UA_DeleteSubscriptionsResponse_deleteMembers(&response); return retval; } /******************/ /* MonitoredItems */ /******************/ void UA_Client_MonitoredItem_remove(UA_Client *client, UA_Client_Subscription *sub, UA_Client_MonitoredItem *mon) { // NOLINTNEXTLINE LIST_REMOVE(mon, listEntry); if(mon->deleteCallback) mon->deleteCallback(client, sub->subscriptionId, sub->context, mon->monitoredItemId, mon->context); UA_free(mon); } static void __UA_Client_MonitoredItems_create(UA_Client *client, const UA_CreateMonitoredItemsRequest *request, void **contexts, void **handlingCallbacks, UA_Client_DeleteMonitoredItemCallback *deleteCallbacks, UA_CreateMonitoredItemsResponse *response) { UA_CreateMonitoredItemsResponse_init(response); if (!request->itemsToCreateSize) { response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR; return; } /* Fix clang warning */ size_t itemsToCreateSize = request->itemsToCreateSize; UA_Client_Subscription *sub = NULL; /* Allocate the memory for internal representations */ UA_STACKARRAY(UA_Client_MonitoredItem*, mis, itemsToCreateSize); memset(mis, 0, sizeof(void*) * itemsToCreateSize); for(size_t i = 0; i < itemsToCreateSize; i++) { mis[i] = (UA_Client_MonitoredItem*)UA_malloc(sizeof(UA_Client_MonitoredItem)); if(!mis[i]) { response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY; goto cleanup; } } /* Get the subscription */ sub = findSubscription(client, request->subscriptionId); if(!sub) { response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; goto cleanup; } /* Set the clientHandle */ for(size_t i = 0; i < itemsToCreateSize; i++) request->itemsToCreate[i].requestedParameters.clientHandle = ++(client->monitoredItemHandles); /* Call the service */ __UA_Client_Service(client, request, &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSREQUEST], response, &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSRESPONSE]); if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) goto cleanup; if(response->resultsSize != itemsToCreateSize) { response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR; goto cleanup; } /* Add internally */ for(size_t i = 0; i < itemsToCreateSize; i++) { if(response->results[i].statusCode != UA_STATUSCODE_GOOD) { if (deleteCallbacks[i]) deleteCallbacks[i](client, sub->subscriptionId, sub->context, 0, contexts[i]); UA_free(mis[i]); mis[i] = NULL; continue; } UA_Client_MonitoredItem *newMon = mis[i]; newMon->clientHandle = request->itemsToCreate[i].requestedParameters.clientHandle; newMon->monitoredItemId = response->results[i].monitoredItemId; newMon->context = contexts[i]; newMon->deleteCallback = deleteCallbacks[i]; newMon->handler.dataChangeCallback = (UA_Client_DataChangeNotificationCallback)(uintptr_t)handlingCallbacks[i]; newMon->isEventMonitoredItem = (request->itemsToCreate[i].itemToMonitor.attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER); LIST_INSERT_HEAD(&sub->monitoredItems, newMon, listEntry); UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Subscription %u | Added a MonitoredItem with handle %u", sub->subscriptionId, newMon->clientHandle); } return; cleanup: for(size_t i = 0; i < itemsToCreateSize; i++) { if (deleteCallbacks[i]) { if (sub) deleteCallbacks[i](client, sub->subscriptionId, sub->context, 0, contexts[i]); else deleteCallbacks[i](client, 0, NULL, 0, contexts[i]); } if(mis[i]) UA_free(mis[i]); } } UA_CreateMonitoredItemsResponse UA_EXPORT UA_Client_MonitoredItems_createDataChanges(UA_Client *client, const UA_CreateMonitoredItemsRequest request, void **contexts, UA_Client_DataChangeNotificationCallback *callbacks, UA_Client_DeleteMonitoredItemCallback *deleteCallbacks) { UA_CreateMonitoredItemsResponse response; __UA_Client_MonitoredItems_create(client, &request, contexts, (void**)(uintptr_t)callbacks, deleteCallbacks, &response); return response; } UA_MonitoredItemCreateResult UA_EXPORT UA_Client_MonitoredItems_createDataChange(UA_Client *client, UA_UInt32 subscriptionId, UA_TimestampsToReturn timestampsToReturn, const UA_MonitoredItemCreateRequest item, void *context, UA_Client_DataChangeNotificationCallback callback, UA_Client_DeleteMonitoredItemCallback deleteCallback) { UA_CreateMonitoredItemsRequest request; UA_CreateMonitoredItemsRequest_init(&request); request.subscriptionId = subscriptionId; request.timestampsToReturn = timestampsToReturn; request.itemsToCreate = (UA_MonitoredItemCreateRequest*)(uintptr_t)&item; request.itemsToCreateSize = 1; UA_CreateMonitoredItemsResponse response = UA_Client_MonitoredItems_createDataChanges(client, request, &context, &callback, &deleteCallback); UA_MonitoredItemCreateResult result; UA_MonitoredItemCreateResult_init(&result); if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) result.statusCode = response.responseHeader.serviceResult; if(result.statusCode == UA_STATUSCODE_GOOD && response.resultsSize != 1) result.statusCode = UA_STATUSCODE_BADINTERNALERROR; if(result.statusCode == UA_STATUSCODE_GOOD) UA_MonitoredItemCreateResult_copy(&response.results[0] , &result); UA_CreateMonitoredItemsResponse_deleteMembers(&response); return result; } UA_CreateMonitoredItemsResponse UA_EXPORT UA_Client_MonitoredItems_createEvents(UA_Client *client, const UA_CreateMonitoredItemsRequest request, void **contexts, UA_Client_EventNotificationCallback *callback, UA_Client_DeleteMonitoredItemCallback *deleteCallback) { UA_CreateMonitoredItemsResponse response; __UA_Client_MonitoredItems_create(client, &request, contexts, (void**)(uintptr_t)callback, deleteCallback, &response); return response; } UA_MonitoredItemCreateResult UA_EXPORT UA_Client_MonitoredItems_createEvent(UA_Client *client, UA_UInt32 subscriptionId, UA_TimestampsToReturn timestampsToReturn, const UA_MonitoredItemCreateRequest item, void *context, UA_Client_EventNotificationCallback callback, UA_Client_DeleteMonitoredItemCallback deleteCallback) { UA_CreateMonitoredItemsRequest request; UA_CreateMonitoredItemsRequest_init(&request); request.subscriptionId = subscriptionId; request.timestampsToReturn = timestampsToReturn; request.itemsToCreate = (UA_MonitoredItemCreateRequest*)(uintptr_t)&item; request.itemsToCreateSize = 1; UA_CreateMonitoredItemsResponse response = UA_Client_MonitoredItems_createEvents(client, request, &context, &callback, &deleteCallback); UA_StatusCode retval = response.responseHeader.serviceResult; UA_MonitoredItemCreateResult result; UA_MonitoredItemCreateResult_init(&result); if(retval != UA_STATUSCODE_GOOD) { UA_CreateMonitoredItemsResponse_deleteMembers(&response); result.statusCode = retval; return result; } UA_MonitoredItemCreateResult_copy(response.results , &result); UA_CreateMonitoredItemsResponse_deleteMembers(&response); return result; } UA_DeleteMonitoredItemsResponse UA_EXPORT UA_Client_MonitoredItems_delete(UA_Client *client, const UA_DeleteMonitoredItemsRequest request) { /* Send the request */ UA_DeleteMonitoredItemsResponse response; __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSREQUEST], &response, &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSRESPONSE]); if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) return response; UA_Client_Subscription *sub = findSubscription(client, request.subscriptionId); if(!sub) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "No internal representation of subscription %u", request.subscriptionId); return response; } /* Loop over deleted MonitoredItems */ for(size_t i = 0; i < response.resultsSize; i++) { if(response.results[i] != UA_STATUSCODE_GOOD && response.results[i] != UA_STATUSCODE_BADMONITOREDITEMIDINVALID) { continue; } #ifndef __clang_analyzer__ /* Delete the internal representation */ UA_Client_MonitoredItem *mon; LIST_FOREACH(mon, &sub->monitoredItems, listEntry) { // NOLINTNEXTLINE if (mon->monitoredItemId == request.monitoredItemIds[i]) { UA_Client_MonitoredItem_remove(client, sub, mon); break; } } #endif } return response; } UA_StatusCode UA_EXPORT UA_Client_MonitoredItems_deleteSingle(UA_Client *client, UA_UInt32 subscriptionId, UA_UInt32 monitoredItemId) { UA_DeleteMonitoredItemsRequest request; UA_DeleteMonitoredItemsRequest_init(&request); request.subscriptionId = subscriptionId; request.monitoredItemIds = &monitoredItemId; request.monitoredItemIdsSize = 1; UA_DeleteMonitoredItemsResponse response = UA_Client_MonitoredItems_delete(client, request); UA_StatusCode retval = response.responseHeader.serviceResult; if(retval != UA_STATUSCODE_GOOD) { UA_DeleteMonitoredItemsResponse_deleteMembers(&response); return retval; } if(response.resultsSize != 1) { UA_DeleteMonitoredItemsResponse_deleteMembers(&response); return UA_STATUSCODE_BADINTERNALERROR; } retval = response.results[0]; UA_DeleteMonitoredItemsResponse_deleteMembers(&response); return retval; } UA_ModifyMonitoredItemsResponse UA_EXPORT UA_Client_MonitoredItems_modify(UA_Client *client, const UA_ModifyMonitoredItemsRequest request) { UA_ModifyMonitoredItemsResponse response; UA_Client_Subscription *sub = 0; LIST_FOREACH(sub, &client->subscriptions, listEntry) { if (sub->subscriptionId == request.subscriptionId) break; } if (!sub) { UA_ModifyMonitoredItemsResponse_init(&response); response.responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; return response; } UA_ModifyMonitoredItemsRequest modifiedRequest; UA_ModifyMonitoredItemsRequest_copy(&request, &modifiedRequest); for (size_t i = 0; i < modifiedRequest.itemsToModifySize; ++i) { UA_Client_MonitoredItem *mon = 0; LIST_FOREACH(mon, &sub->monitoredItems, listEntry) { if(mon->monitoredItemId == modifiedRequest.itemsToModify[i].monitoredItemId) { modifiedRequest.itemsToModify[i].requestedParameters.clientHandle = mon->clientHandle; break; } } } __UA_Client_Service(client, &modifiedRequest, &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSREQUEST], &response, &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSRESPONSE]); UA_ModifyMonitoredItemsRequest_deleteMembers(&modifiedRequest); return response; } /*************************************/ /* Async Processing of Notifications */ /*************************************/ /* Assume the request is already initialized */ UA_StatusCode UA_Client_preparePublishRequest(UA_Client *client, UA_PublishRequest *request) { /* Count acks */ UA_Client_NotificationsAckNumber *ack; LIST_FOREACH(ack, &client->pendingNotificationsAcks, listEntry) ++request->subscriptionAcknowledgementsSize; /* Create the array. Returns a sentinel pointer if the length is zero. */ request->subscriptionAcknowledgements = (UA_SubscriptionAcknowledgement*) UA_Array_new(request->subscriptionAcknowledgementsSize, &UA_TYPES[UA_TYPES_SUBSCRIPTIONACKNOWLEDGEMENT]); if(!request->subscriptionAcknowledgements) { request->subscriptionAcknowledgementsSize = 0; return UA_STATUSCODE_BADOUTOFMEMORY; } size_t i = 0; UA_Client_NotificationsAckNumber *ack_tmp; LIST_FOREACH_SAFE(ack, &client->pendingNotificationsAcks, listEntry, ack_tmp) { request->subscriptionAcknowledgements[i].sequenceNumber = ack->subAck.sequenceNumber; request->subscriptionAcknowledgements[i].subscriptionId = ack->subAck.subscriptionId; ++i; LIST_REMOVE(ack, listEntry); UA_free(ack); } return UA_STATUSCODE_GOOD; } /* According to OPC Unified Architecture, Part 4 5.13.1.1 i) */ /* The value 0 is never used for the sequence number */ static UA_UInt32 UA_Client_Subscriptions_nextSequenceNumber(UA_UInt32 sequenceNumber) { UA_UInt32 nextSequenceNumber = sequenceNumber + 1; if(nextSequenceNumber == 0) nextSequenceNumber = 1; return nextSequenceNumber; } static void processDataChangeNotification(UA_Client *client, UA_Client_Subscription *sub, UA_DataChangeNotification *dataChangeNotification) { for(size_t j = 0; j < dataChangeNotification->monitoredItemsSize; ++j) { UA_MonitoredItemNotification *min = &dataChangeNotification->monitoredItems[j]; /* Find the MonitoredItem */ UA_Client_MonitoredItem *mon; LIST_FOREACH(mon, &sub->monitoredItems, listEntry) { if(mon->clientHandle == min->clientHandle) break; } if(!mon) { UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Could not process a notification with clienthandle %u on subscription %u", min->clientHandle, sub->subscriptionId); continue; } if(mon->isEventMonitoredItem) { UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_CLIENT, "MonitoredItem is configured for Events. But received a " "DataChangeNotification."); continue; } mon->handler.dataChangeCallback(client, sub->subscriptionId, sub->context, mon->monitoredItemId, mon->context, &min->value); } } static void processEventNotification(UA_Client *client, UA_Client_Subscription *sub, UA_EventNotificationList *eventNotificationList) { for(size_t j = 0; j < eventNotificationList->eventsSize; ++j) { UA_EventFieldList *eventFieldList = &eventNotificationList->events[j]; /* Find the MonitoredItem */ UA_Client_MonitoredItem *mon; LIST_FOREACH(mon, &sub->monitoredItems, listEntry) { if(mon->clientHandle == eventFieldList->clientHandle) break; } if(!mon) { UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Could not process a notification with clienthandle %u on subscription %u", eventFieldList->clientHandle, sub->subscriptionId); continue; } if(!mon->isEventMonitoredItem) { UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_CLIENT, "MonitoredItem is configured for DataChanges. But received a " "EventNotification."); continue; } mon->handler.eventCallback(client, sub->subscriptionId, sub->context, mon->monitoredItemId, mon->context, eventFieldList->eventFieldsSize, eventFieldList->eventFields); } } static void processNotificationMessage(UA_Client *client, UA_Client_Subscription *sub, UA_ExtensionObject *msg) { if(msg->encoding != UA_EXTENSIONOBJECT_DECODED) return; /* Handle DataChangeNotification */ if(msg->content.decoded.type == &UA_TYPES[UA_TYPES_DATACHANGENOTIFICATION]) { UA_DataChangeNotification *dataChangeNotification = (UA_DataChangeNotification *)msg->content.decoded.data; processDataChangeNotification(client, sub, dataChangeNotification); return; } /* Handle EventNotification */ if(msg->content.decoded.type == &UA_TYPES[UA_TYPES_EVENTNOTIFICATIONLIST]) { UA_EventNotificationList *eventNotificationList = (UA_EventNotificationList *)msg->content.decoded.data; processEventNotification(client, sub, eventNotificationList); return; } /* Handle StatusChangeNotification */ if(msg->content.decoded.type == &UA_TYPES[UA_TYPES_STATUSCHANGENOTIFICATION]) { if(sub->statusChangeCallback) { sub->statusChangeCallback(client, sub->subscriptionId, sub->context, (UA_StatusChangeNotification*)msg->content.decoded.data); } else { UA_LOG_WARNING(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Dropped a StatusChangeNotification since no callback is registered"); } return; } UA_LOG_WARNING(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Unknown notification message type"); } void UA_Client_Subscriptions_processPublishResponse(UA_Client *client, UA_PublishRequest *request, UA_PublishResponse *response) { UA_NotificationMessage *msg = &response->notificationMessage; client->currentlyOutStandingPublishRequests--; if(response->responseHeader.serviceResult == UA_STATUSCODE_BADTOOMANYPUBLISHREQUESTS) { if(client->config.outStandingPublishRequests > 1) { client->config.outStandingPublishRequests--; UA_LOG_WARNING(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Too many publishrequest, reduce outStandingPublishRequests to %d", client->config.outStandingPublishRequests); } else { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Too many publishrequest when outStandingPublishRequests = 1"); UA_Client_Subscriptions_deleteSingle(client, response->subscriptionId); } return; } if(response->responseHeader.serviceResult == UA_STATUSCODE_BADSHUTDOWN) return; if(!LIST_FIRST(&client->subscriptions)) { response->responseHeader.serviceResult = UA_STATUSCODE_BADNOSUBSCRIPTION; return; } if(response->responseHeader.serviceResult == UA_STATUSCODE_BADSESSIONCLOSED) { if(client->state >= UA_CLIENTSTATE_SESSION) { UA_LOG_WARNING(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Received Publish Response with code %s", UA_StatusCode_name(response->responseHeader.serviceResult)); UA_Client_Subscription* sub = findSubscription(client, response->subscriptionId); if (sub != NULL) UA_Client_Subscription_deleteInternal(client, sub); } return; } if(response->responseHeader.serviceResult == UA_STATUSCODE_BADSESSIONIDINVALID) { UA_Client_disconnect(client); /* TODO: This should be handled before the process callback */ UA_LOG_WARNING(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Received BadSessionIdInvalid"); return; } if(response->responseHeader.serviceResult == UA_STATUSCODE_BADTIMEOUT) { if (client->config.inactivityCallback) client->config.inactivityCallback(client); UA_LOG_WARNING(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Received Timeout for Publish Response"); return; } if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Received Publish Response with code %s", UA_StatusCode_name(response->responseHeader.serviceResult)); return; } UA_Client_Subscription *sub = findSubscription(client, response->subscriptionId); if(!sub) { response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR; UA_LOG_WARNING(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Received Publish Response for a non-existant subscription"); return; } sub->lastActivity = UA_DateTime_nowMonotonic(); /* Detect missing message - OPC Unified Architecture, Part 4 5.13.1.1 e) */ if(UA_Client_Subscriptions_nextSequenceNumber(sub->sequenceNumber) != msg->sequenceNumber) { UA_LOG_WARNING(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Invalid subscription sequence number: expected %u but got %u", UA_Client_Subscriptions_nextSequenceNumber(sub->sequenceNumber), msg->sequenceNumber); /* This is an error. But we do not abort the connection. Some server * SDKs misbehave from time to time and send out-of-order sequence * numbers. (Probably some multi-threading synchronization issue.) */ /* UA_Client_disconnect(client); return; */ } /* According to f), a keep-alive message contains no notifications and has the sequence number * of the next NotificationMessage that is to be sent => More than one consecutive keep-alive * message or a NotificationMessage following a keep-alive message will share the same sequence * number. */ if (msg->notificationDataSize) sub->sequenceNumber = msg->sequenceNumber; /* Process the notification messages */ for(size_t k = 0; k < msg->notificationDataSize; ++k) processNotificationMessage(client, sub, &msg->notificationData[k]); /* Add to the list of pending acks */ for(size_t i = 0; i < response->availableSequenceNumbersSize; i++) { if(response->availableSequenceNumbers[i] != msg->sequenceNumber) continue; UA_Client_NotificationsAckNumber *tmpAck = (UA_Client_NotificationsAckNumber*) UA_malloc(sizeof(UA_Client_NotificationsAckNumber)); if(!tmpAck) { UA_LOG_WARNING(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Not enough memory to store the acknowledgement for a publish " "message on subscription %u", sub->subscriptionId); break; } tmpAck->subAck.sequenceNumber = msg->sequenceNumber; tmpAck->subAck.subscriptionId = sub->subscriptionId; LIST_INSERT_HEAD(&client->pendingNotificationsAcks, tmpAck, listEntry); break; } } static void processPublishResponseAsync(UA_Client *client, void *userdata, UA_UInt32 requestId, void *response) { UA_PublishRequest *req = (UA_PublishRequest*)userdata; UA_PublishResponse *res = (UA_PublishResponse*)response; /* Process the response */ UA_Client_Subscriptions_processPublishResponse(client, req, res); /* Delete the cached request */ UA_PublishRequest_delete(req); /* Fill up the outstanding publish requests */ UA_Client_Subscriptions_backgroundPublish(client); } void UA_Client_Subscriptions_clean(UA_Client *client) { UA_Client_NotificationsAckNumber *n, *tmp; LIST_FOREACH_SAFE(n, &client->pendingNotificationsAcks, listEntry, tmp) { LIST_REMOVE(n, listEntry); UA_free(n); } UA_Client_Subscription *sub, *tmps; LIST_FOREACH_SAFE(sub, &client->subscriptions, listEntry, tmps) UA_Client_Subscription_deleteInternal(client, sub); /* force local removal */ client->monitoredItemHandles = 0; } void UA_Client_Subscriptions_backgroundPublishInactivityCheck(UA_Client *client) { if(client->state < UA_CLIENTSTATE_SESSION) return; /* Is the lack of responses the client's fault? */ if(client->currentlyOutStandingPublishRequests == 0) return; UA_Client_Subscription *sub; LIST_FOREACH(sub, &client->subscriptions, listEntry) { UA_DateTime maxSilence = (UA_DateTime) ((sub->publishingInterval * sub->maxKeepAliveCount) + client->config.timeout) * UA_DATETIME_MSEC; if(maxSilence + sub->lastActivity < UA_DateTime_nowMonotonic()) { /* Reset activity */ sub->lastActivity = UA_DateTime_nowMonotonic(); if(client->config.subscriptionInactivityCallback) client->config.subscriptionInactivityCallback(client, sub->subscriptionId, sub->context); UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Inactivity for Subscription %u.", sub->subscriptionId); } } } UA_StatusCode UA_Client_Subscriptions_backgroundPublish(UA_Client *client) { if(client->state < UA_CLIENTSTATE_SESSION) return UA_STATUSCODE_BADSERVERNOTCONNECTED; /* The session must have at least one subscription */ if(!LIST_FIRST(&client->subscriptions)) return UA_STATUSCODE_GOOD; while(client->currentlyOutStandingPublishRequests < client->config.outStandingPublishRequests) { UA_PublishRequest *request = UA_PublishRequest_new(); if (!request) return UA_STATUSCODE_BADOUTOFMEMORY; request->requestHeader.timeoutHint=60000; UA_StatusCode retval = UA_Client_preparePublishRequest(client, request); if(retval != UA_STATUSCODE_GOOD) { UA_PublishRequest_delete(request); return retval; } UA_UInt32 requestId; client->currentlyOutStandingPublishRequests++; /* Disable the timeout, it is treat in UA_Client_Subscriptions_backgroundPublishInactivityCheck */ retval = __UA_Client_AsyncServiceEx(client, request, &UA_TYPES[UA_TYPES_PUBLISHREQUEST], processPublishResponseAsync, &UA_TYPES[UA_TYPES_PUBLISHRESPONSE], (void*)request, &requestId, 0); if(retval != UA_STATUSCODE_GOOD) { UA_PublishRequest_delete(request); return retval; } } return UA_STATUSCODE_GOOD; } #endif /* UA_ENABLE_SUBSCRIPTIONS */ /*********************************** amalgamated original file "/home/jvoe/open62541/src/client/ua_client_worker.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ static void asyncServiceTimeoutCheck(UA_Client *client) { UA_DateTime now = UA_DateTime_nowMonotonic(); /* Timeout occurs, remove the callback */ AsyncServiceCall *ac, *ac_tmp; LIST_FOREACH_SAFE(ac, &client->asyncServiceCalls, pointers, ac_tmp) { if(!ac->timeout) continue; if(ac->start + (UA_DateTime)(ac->timeout * UA_DATETIME_MSEC) <= now) { LIST_REMOVE(ac, pointers); UA_Client_AsyncService_cancel(client, ac, UA_STATUSCODE_BADTIMEOUT); UA_free(ac); } } } static void backgroundConnectivityCallback(UA_Client *client, void *userdata, UA_UInt32 requestId, const UA_ReadResponse *response) { if(response->responseHeader.serviceResult == UA_STATUSCODE_BADTIMEOUT) { if (client->config.inactivityCallback) client->config.inactivityCallback(client); } client->pendingConnectivityCheck = false; client->lastConnectivityCheck = UA_DateTime_nowMonotonic(); } static UA_StatusCode UA_Client_backgroundConnectivity(UA_Client *client) { if(!client->config.connectivityCheckInterval) return UA_STATUSCODE_GOOD; if (client->pendingConnectivityCheck) return UA_STATUSCODE_GOOD; UA_DateTime now = UA_DateTime_nowMonotonic(); UA_DateTime nextDate = client->lastConnectivityCheck + (UA_DateTime)(client->config.connectivityCheckInterval * UA_DATETIME_MSEC); if(now <= nextDate) return UA_STATUSCODE_GOOD; UA_ReadRequest request; UA_ReadRequest_init(&request); UA_ReadValueId rvid; UA_ReadValueId_init(&rvid); rvid.attributeId = UA_ATTRIBUTEID_VALUE; rvid.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STATE); request.nodesToRead = &rvid; request.nodesToReadSize = 1; UA_StatusCode retval = __UA_Client_AsyncService(client, &request, &UA_TYPES[UA_TYPES_READREQUEST], (UA_ClientAsyncServiceCallback)backgroundConnectivityCallback, &UA_TYPES[UA_TYPES_READRESPONSE], NULL, NULL); client->pendingConnectivityCheck = true; return retval; } /** * Main Client Loop * ---------------- * Start: Spin up the workers and the network layer * Iterate: Process repeated callbacks and events in the network layer. * This part can be driven from an external main-loop in an * event-driven single-threaded architecture. * Stop: Stop workers, finish all callbacks, stop the network layer, * clean up */ static void clientExecuteRepeatedCallback(UA_Client *client, UA_ApplicationCallback cb, void *callbackApplication, void *data) { cb(callbackApplication, data); /* TODO: Use workers in the client * UA_WorkQueue_enqueue(&client->workQueue, cb, callbackApplication, data); */ } UA_StatusCode UA_Client_run_iterate(UA_Client *client, UA_UInt16 timeout) { // TODO connectivity check & timeout features for the async implementation (timeout == 0) UA_StatusCode retval = UA_STATUSCODE_GOOD; #ifdef UA_ENABLE_SUBSCRIPTIONS UA_StatusCode retvalPublish = UA_Client_Subscriptions_backgroundPublish(client); if(client->state >= UA_CLIENTSTATE_SESSION && retvalPublish != UA_STATUSCODE_GOOD) return retvalPublish; #endif /* Make sure we have an open channel */ /************************************************************/ /* FIXME: This is a dirty workaround */ if(client->state >= UA_CLIENTSTATE_SECURECHANNEL) retval = openSecureChannel(client, true); /* FIXME: Will most likely break somewhere in the future */ /************************************************************/ if(timeout) { if(retval != UA_STATUSCODE_GOOD) return retval; retval = UA_Client_backgroundConnectivity(client); if(retval != UA_STATUSCODE_GOOD) return retval; UA_DateTime maxDate = UA_DateTime_nowMonotonic() + (timeout * UA_DATETIME_MSEC); retval = receiveServiceResponse(client, NULL, NULL, maxDate, NULL); if(retval == UA_STATUSCODE_GOODNONCRITICALTIMEOUT) retval = UA_STATUSCODE_GOOD; } else { UA_DateTime now = UA_DateTime_nowMonotonic(); UA_Timer_process(&client->timer, now, (UA_TimerExecutionCallback)clientExecuteRepeatedCallback, client); UA_ClientState cs = UA_Client_getState(client); retval = UA_Client_connect_iterate(client); /* Connection failed, drop the rest */ if(retval != UA_STATUSCODE_GOOD) return retval; if((cs == UA_CLIENTSTATE_SECURECHANNEL) || (cs == UA_CLIENTSTATE_SESSION)) { /* Check for new data */ retval = receiveServiceResponseAsync(client, NULL, NULL); } else { retval = receivePacketAsync(client); } } #ifdef UA_ENABLE_SUBSCRIPTIONS /* The inactivity check must be done after receiveServiceResponse*/ UA_Client_Subscriptions_backgroundPublishInactivityCheck(client); #endif asyncServiceTimeoutCheck(client); #ifndef UA_ENABLE_MULTITHREADING /* Process delayed callbacks when all callbacks and network events are * done */ UA_WorkQueue_manuallyProcessDelayed(&client->workQueue); #endif return retval; } /*********************************** amalgamated original file "/home/jvoe/open62541/deps/libc_time.c" ***********************************/ /* Originally released by the musl project (http://www.musl-libc.org/) under the * MIT license. Taken from the file /src/time/__secs_to_tm.c */ #include /* 2000-03-01 (mod 400 year, immediately after feb29 */ #define LEAPOCH (946684800LL + 86400*(31+29)) #define DAYS_PER_400Y (365*400 + 97) #define DAYS_PER_100Y (365*100 + 24) #define DAYS_PER_4Y (365*4 + 1) int __secs_to_tm(long long t, struct mytm *tm) { long long days, secs, years; int remdays, remsecs, remyears; int qc_cycles, c_cycles, q_cycles; int months; static const char days_in_month[] = {31,30,31,30,31,31,30,31,30,31,31,29}; /* Reject time_t values whose year would overflow int */ if (t < INT_MIN * 31622400LL || t > INT_MAX * 31622400LL) return -1; secs = t - LEAPOCH; days = secs / 86400LL; remsecs = (int)(secs % 86400); if (remsecs < 0) { remsecs += 86400; --days; } qc_cycles = (int)(days / DAYS_PER_400Y); remdays = (int)(days % DAYS_PER_400Y); if (remdays < 0) { remdays += DAYS_PER_400Y; --qc_cycles; } c_cycles = remdays / DAYS_PER_100Y; if (c_cycles == 4) --c_cycles; remdays -= c_cycles * DAYS_PER_100Y; q_cycles = remdays / DAYS_PER_4Y; if (q_cycles == 25) --q_cycles; remdays -= q_cycles * DAYS_PER_4Y; remyears = remdays / 365; if (remyears == 4) --remyears; remdays -= remyears * 365; years = remyears + 4*q_cycles + 100*c_cycles + 400LL*qc_cycles; for (months=0; days_in_month[months] <= remdays; ++months) remdays -= days_in_month[months]; if (years+100 > INT_MAX || years+100 < INT_MIN) return -1; tm->tm_year = (int)(years + 100); tm->tm_mon = months + 2; if (tm->tm_mon >= 12) { tm->tm_mon -=12; ++tm->tm_year; } tm->tm_mday = remdays + 1; tm->tm_hour = remsecs / 3600; tm->tm_min = remsecs / 60 % 60; tm->tm_sec = remsecs % 60; return 0; } static const int secs_through_month[] = {0, 31*86400, 59*86400, 90*86400, 120*86400, 151*86400, 181*86400, 212*86400, 243*86400, 273*86400, 304*86400, 334*86400 }; static int __month_to_secs(int month, int is_leap) { int t = secs_through_month[month]; if (is_leap && month >= 2) t+=86400; return t; } static long long __year_to_secs(const long long year, int *is_leap) { int cycles, centuries, leaps, rem; int is_leap_val = 0; if (!is_leap) { is_leap = &is_leap_val; } cycles = (int)((year-100) / 400); rem = (int)((year-100) % 400); if (rem < 0) { cycles--; rem += 400; } if (!rem) { *is_leap = 1; centuries = 0; leaps = 0; } else { if (rem >= 200) { if (rem >= 300) centuries = 3, rem -= 300; else centuries = 2, rem -= 200; } else { if (rem >= 100) centuries = 1, rem -= 100; else centuries = 0; } if (!rem) { *is_leap = 0; leaps = 0; } else { leaps = (rem / (int)4U); rem %= (int)4U; *is_leap = !rem; } } leaps += 97*cycles + 24*centuries - *is_leap; return (year-100) * 31536000LL + leaps * 86400LL + 946684800 + 86400; } long long __tm_to_secs(const struct mytm *tm) { int is_leap; long long year = tm->tm_year; int month = tm->tm_mon; if (month >= 12 || month < 0) { int adj = month / 12; month %= 12; if (month < 0) { adj--; month += 12; } year += adj; } long long t = __year_to_secs(year, &is_leap); t += __month_to_secs(month, is_leap); t += 86400LL * (tm->tm_mday-1); t += 3600LL * tm->tm_hour; t += 60LL * tm->tm_min; t += tm->tm_sec; return t; } /*********************************** amalgamated original file "/home/jvoe/open62541/deps/pcg_basic.c" ***********************************/ /* * PCG Random Number Generation for C. * * Copyright 2014 Melissa O'Neill * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * For additional information about the PCG random number generation scheme, * including its license and other licensing options, visit * * http://www.pcg-random.org */ void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initial_state, uint64_t initseq) { rng->state = 0U; rng->inc = (initseq << 1u) | 1u; pcg32_random_r(rng); rng->state += initial_state; pcg32_random_r(rng); } uint32_t pcg32_random_r(pcg32_random_t* rng) { uint64_t oldstate = rng->state; rng->state = oldstate * 6364136223846793005ULL + rng->inc; uint32_t xorshifted = (uint32_t)(((oldstate >> 18u) ^ oldstate) >> 27u); uint32_t rot = (uint32_t)(oldstate >> 59u); return (xorshifted >> rot) | (xorshifted << ((~rot + 1u) & 31)); /* was (xorshifted >> rot) | (xorshifted << ((-rot) & 31)) */ } /*********************************** amalgamated original file "/home/jvoe/open62541/deps/base64.c" ***********************************/ /* * Base64 encoding: Copyright (c) 2005-2011, Jouni Malinen * This software may be distributed under the terms of the BSD license. * * Base64 decoding: Copyright (c) 2016, polfosol * Posted at https://stackoverflow.com/a/37109258 under the CC-BY-SA Creative * Commons license. */ static const unsigned char base64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; unsigned char * UA_base64(const unsigned char *src, size_t len, size_t *out_len) { if(len == 0) { *out_len = 0; return (unsigned char*)UA_EMPTY_ARRAY_SENTINEL; } size_t olen = 4*((len + 2) / 3); /* 3-byte blocks to 4-byte */ if(olen < len) return NULL; /* integer overflow */ unsigned char *out = (unsigned char*)UA_malloc(olen); if(!out) return NULL; const unsigned char *end = src + len; const unsigned char *in = src; unsigned char *pos = out; while(end - in >= 3) { *pos++ = base64_table[in[0] >> 2]; *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)]; *pos++ = base64_table[in[2] & 0x3f]; in += 3; } if(end - in) { *pos++ = base64_table[in[0] >> 2]; if(end - in == 1) { *pos++ = base64_table[(in[0] & 0x03) << 4]; *pos++ = '='; } else { *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; *pos++ = base64_table[(in[1] & 0x0f) << 2]; } *pos++ = '='; } *out_len = (size_t)(pos - out); return out; } static const uint32_t from_b64[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51}; unsigned char * UA_unbase64(const unsigned char *src, size_t len, size_t *out_len) { if(len == 0) { *out_len = 0; return (unsigned char*)UA_EMPTY_ARRAY_SENTINEL; } const unsigned char *p = src; size_t pad1 = len % 4 || p[len - 1] == '='; size_t pad2 = pad1 && (len % 4 > 2 || p[len - 2] != '='); const size_t last = (len - pad1) / 4 << 2; unsigned char *str = (unsigned char*)UA_malloc(last / 4 * 3 + pad1 + pad2); if(!str) return NULL; unsigned char *pos = str; for(size_t i = 0; i < last; i += 4) { uint32_t n = from_b64[p[i]] << 18 | from_b64[p[i + 1]] << 12 | from_b64[p[i + 2]] << 6 | from_b64[p[i + 3]]; *pos++ = (unsigned char)(n >> 16); *pos++ = (unsigned char)(n >> 8 & 0xFF); *pos++ = (unsigned char)(n & 0xFF); } if(pad1) { uint32_t n = from_b64[p[last]] << 18 | from_b64[p[last + 1]] << 12; *pos++ = (unsigned char)(n >> 16); if(pad2) { n |= from_b64[p[last + 2]] << 6; *pos++ = (unsigned char)(n >> 8 & 0xFF); } } *out_len = (uintptr_t)(pos - str); return str; } /*********************************** amalgamated original file "/home/jvoe/open62541/build/src_generated/open62541/namespace0_generated.c" ***********************************/ /* WARNING: This is a generated file. * Any manual changes will be overwritten. */ /* HasAddIn - ns=0;i=17604 */ static UA_StatusCode function_namespace0_generated_0_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ReferenceTypeAttributes attr = UA_ReferenceTypeAttributes_default; attr.inverseName = UA_LOCALIZEDTEXT("", "AddInOf"); attr.displayName = UA_LOCALIZEDTEXT("", "HasAddIn"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_REFERENCETYPE, UA_NODEID_NUMERIC(ns[0], 17604), UA_NODEID_NUMERIC(ns[0], 32), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "HasAddIn"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_0_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 17604) ); } /* HasInterface - ns=0;i=17603 */ static UA_StatusCode function_namespace0_generated_1_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ReferenceTypeAttributes attr = UA_ReferenceTypeAttributes_default; attr.inverseName = UA_LOCALIZEDTEXT("", "InterfaceOf"); attr.displayName = UA_LOCALIZEDTEXT("", "HasInterface"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_REFERENCETYPE, UA_NODEID_NUMERIC(ns[0], 17603), UA_NODEID_NUMERIC(ns[0], 32), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "HasInterface"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_1_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 17603) ); } /* ExpandedNodeId - ns=0;i=18 */ static UA_StatusCode function_namespace0_generated_2_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "ExpandedNodeId"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 18), UA_NODEID_NUMERIC(ns[0], 24), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "ExpandedNodeId"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_2_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 18) ); } /* StatusCode - ns=0;i=19 */ static UA_StatusCode function_namespace0_generated_3_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "StatusCode"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 19), UA_NODEID_NUMERIC(ns[0], 24), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "StatusCode"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_3_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 19) ); } /* ByteString - ns=0;i=15 */ static UA_StatusCode function_namespace0_generated_4_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "ByteString"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 15), UA_NODEID_NUMERIC(ns[0], 24), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "ByteString"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_4_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 15) ); } /* Image - ns=0;i=30 */ static UA_StatusCode function_namespace0_generated_5_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.isAbstract = true; attr.displayName = UA_LOCALIZEDTEXT("", "Image"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 30), UA_NODEID_NUMERIC(ns[0], 15), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "Image"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_5_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 30) ); } /* DataValue - ns=0;i=23 */ static UA_StatusCode function_namespace0_generated_6_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "DataValue"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 23), UA_NODEID_NUMERIC(ns[0], 24), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "DataValue"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_6_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 23) ); } /* Structure - ns=0;i=22 */ static UA_StatusCode function_namespace0_generated_7_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.isAbstract = true; attr.displayName = UA_LOCALIZEDTEXT("", "Structure"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 22), UA_NODEID_NUMERIC(ns[0], 24), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "Structure"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_7_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 22) ); } /* BuildInfo - ns=0;i=338 */ static UA_StatusCode function_namespace0_generated_8_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "BuildInfo"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 338), UA_NODEID_NUMERIC(ns[0], 22), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "BuildInfo"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_8_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 338) ); } /* ServerStatusDataType - ns=0;i=862 */ static UA_StatusCode function_namespace0_generated_9_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "ServerStatusDataType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 862), UA_NODEID_NUMERIC(ns[0], 22), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "ServerStatusDataType"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_9_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 862) ); } /* EnumValueType - ns=0;i=7594 */ static UA_StatusCode function_namespace0_generated_10_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "EnumValueType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 7594), UA_NODEID_NUMERIC(ns[0], 22), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "EnumValueType"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_10_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 7594) ); } /* ServerDiagnosticsSummaryDataType - ns=0;i=859 */ static UA_StatusCode function_namespace0_generated_11_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "ServerDiagnosticsSummaryDataType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 859), UA_NODEID_NUMERIC(ns[0], 22), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "ServerDiagnosticsSummaryDataType"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_11_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 859) ); } /* SignedSoftwareCertificate - ns=0;i=344 */ static UA_StatusCode function_namespace0_generated_12_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "SignedSoftwareCertificate"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 344), UA_NODEID_NUMERIC(ns[0], 22), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "SignedSoftwareCertificate"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_12_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 344) ); } /* Argument - ns=0;i=296 */ static UA_StatusCode function_namespace0_generated_13_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "Argument"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 296), UA_NODEID_NUMERIC(ns[0], 22), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "Argument"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_13_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 296) ); } /* LocalizedText - ns=0;i=21 */ static UA_StatusCode function_namespace0_generated_14_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "LocalizedText"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 21), UA_NODEID_NUMERIC(ns[0], 24), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "LocalizedText"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_14_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 21) ); } /* QualifiedName - ns=0;i=20 */ static UA_StatusCode function_namespace0_generated_15_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "QualifiedName"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 20), UA_NODEID_NUMERIC(ns[0], 24), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "QualifiedName"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_15_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 20) ); } /* Number - ns=0;i=26 */ static UA_StatusCode function_namespace0_generated_16_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.isAbstract = true; attr.displayName = UA_LOCALIZEDTEXT("", "Number"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 26), UA_NODEID_NUMERIC(ns[0], 24), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "Number"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_16_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 26) ); } /* Decimal - ns=0;i=50 */ static UA_StatusCode function_namespace0_generated_17_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "Decimal"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 50), UA_NODEID_NUMERIC(ns[0], 26), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "Decimal"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_17_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 50) ); } /* UInteger - ns=0;i=28 */ static UA_StatusCode function_namespace0_generated_18_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.isAbstract = true; attr.displayName = UA_LOCALIZEDTEXT("", "UInteger"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 28), UA_NODEID_NUMERIC(ns[0], 26), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "UInteger"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_18_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 28) ); } /* UInt16 - ns=0;i=5 */ static UA_StatusCode function_namespace0_generated_19_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "UInt16"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 5), UA_NODEID_NUMERIC(ns[0], 28), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "UInt16"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_19_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 5) ); } /* UInt32 - ns=0;i=7 */ static UA_StatusCode function_namespace0_generated_20_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "UInt32"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 7), UA_NODEID_NUMERIC(ns[0], 28), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "UInt32"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_20_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 7) ); } /* UInt64 - ns=0;i=9 */ static UA_StatusCode function_namespace0_generated_21_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "UInt64"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 9), UA_NODEID_NUMERIC(ns[0], 28), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "UInt64"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_21_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 9) ); } /* Byte - ns=0;i=3 */ static UA_StatusCode function_namespace0_generated_22_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "Byte"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 3), UA_NODEID_NUMERIC(ns[0], 28), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "Byte"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_22_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 3) ); } /* Integer - ns=0;i=27 */ static UA_StatusCode function_namespace0_generated_23_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.isAbstract = true; attr.displayName = UA_LOCALIZEDTEXT("", "Integer"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 27), UA_NODEID_NUMERIC(ns[0], 26), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "Integer"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_23_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 27) ); } /* SByte - ns=0;i=2 */ static UA_StatusCode function_namespace0_generated_24_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "SByte"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 2), UA_NODEID_NUMERIC(ns[0], 27), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "SByte"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_24_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2) ); } /* Int64 - ns=0;i=8 */ static UA_StatusCode function_namespace0_generated_25_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "Int64"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 8), UA_NODEID_NUMERIC(ns[0], 27), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "Int64"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_25_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 8) ); } /* Int32 - ns=0;i=6 */ static UA_StatusCode function_namespace0_generated_26_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "Int32"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 6), UA_NODEID_NUMERIC(ns[0], 27), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "Int32"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_26_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 6) ); } /* Int16 - ns=0;i=4 */ static UA_StatusCode function_namespace0_generated_27_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "Int16"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 4), UA_NODEID_NUMERIC(ns[0], 27), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "Int16"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_27_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 4) ); } /* Float - ns=0;i=10 */ static UA_StatusCode function_namespace0_generated_28_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "Float"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 10), UA_NODEID_NUMERIC(ns[0], 26), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "Float"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_28_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 10) ); } /* Double - ns=0;i=11 */ static UA_StatusCode function_namespace0_generated_29_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "Double"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 11), UA_NODEID_NUMERIC(ns[0], 26), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "Double"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_29_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11) ); } /* Duration - ns=0;i=290 */ static UA_StatusCode function_namespace0_generated_30_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "Duration"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 290), UA_NODEID_NUMERIC(ns[0], 11), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "Duration"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_30_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 290) ); } /* DiagnosticInfo - ns=0;i=25 */ static UA_StatusCode function_namespace0_generated_31_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "DiagnosticInfo"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 25), UA_NODEID_NUMERIC(ns[0], 24), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "DiagnosticInfo"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_31_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 25) ); } /* Enumeration - ns=0;i=29 */ static UA_StatusCode function_namespace0_generated_32_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.isAbstract = true; attr.displayName = UA_LOCALIZEDTEXT("", "Enumeration"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 29), UA_NODEID_NUMERIC(ns[0], 24), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "Enumeration"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_32_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 29) ); } /* NamingRuleType - ns=0;i=120 */ static UA_StatusCode function_namespace0_generated_33_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "NamingRuleType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 120), UA_NODEID_NUMERIC(ns[0], 29), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "NamingRuleType"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_33_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 120) ); } /* EnumValues - ns=0;i=12169 */ static UA_StatusCode function_namespace0_generated_34_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; attr.valueRank = 1; attr.arrayDimensionsSize = 1; UA_UInt32 arrayDimensions[1]; arrayDimensions[0] = 0; attr.arrayDimensions = &arrayDimensions[0]; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7594); UA_EnumValueType variablenode_ns_0_i_12169_variant_DataContents[3]; UA_init(&variablenode_ns_0_i_12169_variant_DataContents[0], &UA_TYPES[UA_TYPES_ENUMVALUETYPE]); variablenode_ns_0_i_12169_variant_DataContents[0].value = (UA_Int64) 1; variablenode_ns_0_i_12169_variant_DataContents[0].displayName = UA_LOCALIZEDTEXT("", "Mandatory"); variablenode_ns_0_i_12169_variant_DataContents[0].description = UA_LOCALIZEDTEXT("", "The BrowseName must appear in all instances of the type."); UA_init(&variablenode_ns_0_i_12169_variant_DataContents[1], &UA_TYPES[UA_TYPES_ENUMVALUETYPE]); variablenode_ns_0_i_12169_variant_DataContents[1].value = (UA_Int64) 2; variablenode_ns_0_i_12169_variant_DataContents[1].displayName = UA_LOCALIZEDTEXT("", "Optional"); variablenode_ns_0_i_12169_variant_DataContents[1].description = UA_LOCALIZEDTEXT("", "The BrowseName may appear in an instance of the type."); UA_init(&variablenode_ns_0_i_12169_variant_DataContents[2], &UA_TYPES[UA_TYPES_ENUMVALUETYPE]); variablenode_ns_0_i_12169_variant_DataContents[2].value = (UA_Int64) 3; variablenode_ns_0_i_12169_variant_DataContents[2].displayName = UA_LOCALIZEDTEXT("", "Constraint"); variablenode_ns_0_i_12169_variant_DataContents[2].description = UA_LOCALIZEDTEXT("", "The modelling rule defines a constraint and the BrowseName is not used in an instance of the type."); UA_Variant_setArray(&attr.value, &variablenode_ns_0_i_12169_variant_DataContents, (UA_Int32) 3, &UA_TYPES[UA_TYPES_ENUMVALUETYPE]); attr.displayName = UA_LOCALIZEDTEXT("", "EnumValues"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 12169), UA_NODEID_NUMERIC(ns[0], 120), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "EnumValues"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_34_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 12169) ); } /* RedundancySupport - ns=0;i=851 */ static UA_StatusCode function_namespace0_generated_35_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "RedundancySupport"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 851), UA_NODEID_NUMERIC(ns[0], 29), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "RedundancySupport"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_35_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 851) ); } /* EnumStrings - ns=0;i=7611 */ static UA_StatusCode function_namespace0_generated_36_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; attr.valueRank = 1; attr.arrayDimensionsSize = 1; UA_UInt32 arrayDimensions[1]; arrayDimensions[0] = 0; attr.arrayDimensions = &arrayDimensions[0]; attr.dataType = UA_NODEID_NUMERIC(ns[0], 21); UA_LocalizedText variablenode_ns_0_i_7611_variant_DataContents[6]; variablenode_ns_0_i_7611_variant_DataContents[0] = UA_LOCALIZEDTEXT("", "None"); variablenode_ns_0_i_7611_variant_DataContents[1] = UA_LOCALIZEDTEXT("", "Cold"); variablenode_ns_0_i_7611_variant_DataContents[2] = UA_LOCALIZEDTEXT("", "Warm"); variablenode_ns_0_i_7611_variant_DataContents[3] = UA_LOCALIZEDTEXT("", "Hot"); variablenode_ns_0_i_7611_variant_DataContents[4] = UA_LOCALIZEDTEXT("", "Transparent"); variablenode_ns_0_i_7611_variant_DataContents[5] = UA_LOCALIZEDTEXT("", "HotAndMirrored"); UA_Variant_setArray(&attr.value, &variablenode_ns_0_i_7611_variant_DataContents, (UA_Int32) 6, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]); attr.displayName = UA_LOCALIZEDTEXT("", "EnumStrings"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 7611), UA_NODEID_NUMERIC(ns[0], 851), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "EnumStrings"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_36_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 7611) ); } /* ServerState - ns=0;i=852 */ static UA_StatusCode function_namespace0_generated_37_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "ServerState"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 852), UA_NODEID_NUMERIC(ns[0], 29), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "ServerState"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_37_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 852) ); } /* HasHistoricalConfiguration - ns=0;i=56 */ static UA_StatusCode function_namespace0_generated_38_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ReferenceTypeAttributes attr = UA_ReferenceTypeAttributes_default; attr.inverseName = UA_LOCALIZEDTEXT("", "HistoricalConfigurationOf"); attr.displayName = UA_LOCALIZEDTEXT("", "HasHistoricalConfiguration"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_REFERENCETYPE, UA_NODEID_NUMERIC(ns[0], 56), UA_NODEID_NUMERIC(ns[0], 44), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "HasHistoricalConfiguration"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_38_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 56) ); } /* HasEffect - ns=0;i=54 */ static UA_StatusCode function_namespace0_generated_39_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ReferenceTypeAttributes attr = UA_ReferenceTypeAttributes_default; attr.inverseName = UA_LOCALIZEDTEXT("", "MayBeEffectedBy"); attr.displayName = UA_LOCALIZEDTEXT("", "HasEffect"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_REFERENCETYPE, UA_NODEID_NUMERIC(ns[0], 54), UA_NODEID_NUMERIC(ns[0], 32), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "HasEffect"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_39_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 54) ); } /* ToState - ns=0;i=52 */ static UA_StatusCode function_namespace0_generated_40_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ReferenceTypeAttributes attr = UA_ReferenceTypeAttributes_default; attr.inverseName = UA_LOCALIZEDTEXT("", "FromTransition"); attr.displayName = UA_LOCALIZEDTEXT("", "ToState"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_REFERENCETYPE, UA_NODEID_NUMERIC(ns[0], 52), UA_NODEID_NUMERIC(ns[0], 32), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "ToState"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_40_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 52) ); } /* HasCause - ns=0;i=53 */ static UA_StatusCode function_namespace0_generated_41_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ReferenceTypeAttributes attr = UA_ReferenceTypeAttributes_default; attr.inverseName = UA_LOCALIZEDTEXT("", "MayBeCausedBy"); attr.displayName = UA_LOCALIZEDTEXT("", "HasCause"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_REFERENCETYPE, UA_NODEID_NUMERIC(ns[0], 53), UA_NODEID_NUMERIC(ns[0], 32), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "HasCause"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_41_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 53) ); } /* FromState - ns=0;i=51 */ static UA_StatusCode function_namespace0_generated_42_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ReferenceTypeAttributes attr = UA_ReferenceTypeAttributes_default; attr.inverseName = UA_LOCALIZEDTEXT("", "ToTransition"); attr.displayName = UA_LOCALIZEDTEXT("", "FromState"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_REFERENCETYPE, UA_NODEID_NUMERIC(ns[0], 51), UA_NODEID_NUMERIC(ns[0], 32), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "FromState"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_42_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 51) ); } /* String - ns=0;i=12 */ static UA_StatusCode function_namespace0_generated_43_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "String"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 12), UA_NODEID_NUMERIC(ns[0], 24), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "String"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_43_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 12) ); } /* LocaleId - ns=0;i=295 */ static UA_StatusCode function_namespace0_generated_44_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "LocaleId"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 295), UA_NODEID_NUMERIC(ns[0], 12), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "LocaleId"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_44_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 295) ); } /* DateTime - ns=0;i=13 */ static UA_StatusCode function_namespace0_generated_45_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "DateTime"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 13), UA_NODEID_NUMERIC(ns[0], 24), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "DateTime"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_45_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 13) ); } /* UtcTime - ns=0;i=294 */ static UA_StatusCode function_namespace0_generated_46_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "UtcTime"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 294), UA_NODEID_NUMERIC(ns[0], 13), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "UtcTime"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_46_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 294) ); } /* NodeId - ns=0;i=17 */ static UA_StatusCode function_namespace0_generated_47_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "NodeId"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 17), UA_NODEID_NUMERIC(ns[0], 24), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "NodeId"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_47_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 17) ); } /* Guid - ns=0;i=14 */ static UA_StatusCode function_namespace0_generated_48_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "Guid"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 14), UA_NODEID_NUMERIC(ns[0], 24), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "Guid"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_48_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 14) ); } /* Boolean - ns=0;i=1 */ static UA_StatusCode function_namespace0_generated_49_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "Boolean"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 1), UA_NODEID_NUMERIC(ns[0], 24), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "Boolean"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_49_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 1) ); } /* XmlElement - ns=0;i=16 */ static UA_StatusCode function_namespace0_generated_50_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "XmlElement"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 16), UA_NODEID_NUMERIC(ns[0], 24), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "XmlElement"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_50_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 16) ); } /* ServerDiagnosticsType - ns=0;i=2020 */ static UA_StatusCode function_namespace0_generated_51_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectTypeAttributes attr = UA_ObjectTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "ServerDiagnosticsType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECTTYPE, UA_NODEID_NUMERIC(ns[0], 2020), UA_NODEID_NUMERIC(ns[0], 58), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "ServerDiagnosticsType"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_51_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2020) ); } /* Default Binary - ns=0;i=3062 */ static UA_StatusCode function_namespace0_generated_52_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "Default Binary"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 3062), UA_NODEID_NUMERIC(ns[0], 0), UA_NODEID_NUMERIC(ns[0], 0), UA_QUALIFIEDNAME(ns[0], "Default Binary"), UA_NODEID_NUMERIC(ns[0], 58), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_52_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 3062) ); } /* Default XML - ns=0;i=3063 */ static UA_StatusCode function_namespace0_generated_53_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "Default XML"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 3063), UA_NODEID_NUMERIC(ns[0], 0), UA_NODEID_NUMERIC(ns[0], 0), UA_QUALIFIEDNAME(ns[0], "Default XML"), UA_NODEID_NUMERIC(ns[0], 58), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_53_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 3063) ); } /* ServerStatusType - ns=0;i=2138 */ static UA_StatusCode function_namespace0_generated_54_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableTypeAttributes attr = UA_VariableTypeAttributes_default; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 862); attr.displayName = UA_LOCALIZEDTEXT("", "ServerStatusType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLETYPE, UA_NODEID_NUMERIC(ns[0], 2138), UA_NODEID_NUMERIC(ns[0], 63), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "ServerStatusType"), UA_NODEID_NUMERIC(ns[0], 0), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_54_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2138) ); } /* VendorServerInfoType - ns=0;i=2033 */ static UA_StatusCode function_namespace0_generated_55_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectTypeAttributes attr = UA_ObjectTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "VendorServerInfoType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECTTYPE, UA_NODEID_NUMERIC(ns[0], 2033), UA_NODEID_NUMERIC(ns[0], 58), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "VendorServerInfoType"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_55_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2033) ); } /* DataTypeDescriptionType - ns=0;i=69 */ static UA_StatusCode function_namespace0_generated_56_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableTypeAttributes attr = UA_VariableTypeAttributes_default; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12); attr.displayName = UA_LOCALIZEDTEXT("", "DataTypeDescriptionType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLETYPE, UA_NODEID_NUMERIC(ns[0], 69), UA_NODEID_NUMERIC(ns[0], 63), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "DataTypeDescriptionType"), UA_NODEID_NUMERIC(ns[0], 0), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_56_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 69) ); } /* DictionaryFragment - ns=0;i=105 */ static UA_StatusCode function_namespace0_generated_57_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 15); attr.displayName = UA_LOCALIZEDTEXT("", "DictionaryFragment"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 105), UA_NODEID_NUMERIC(ns[0], 69), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "DictionaryFragment"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_57_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 105) ); } /* DataTypeVersion - ns=0;i=104 */ static UA_StatusCode function_namespace0_generated_58_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12); attr.displayName = UA_LOCALIZEDTEXT("", "DataTypeVersion"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 104), UA_NODEID_NUMERIC(ns[0], 69), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "DataTypeVersion"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_58_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 104) ); } /* DataTypeDictionaryType - ns=0;i=72 */ static UA_StatusCode function_namespace0_generated_59_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableTypeAttributes attr = UA_VariableTypeAttributes_default; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 15); attr.displayName = UA_LOCALIZEDTEXT("", "DataTypeDictionaryType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLETYPE, UA_NODEID_NUMERIC(ns[0], 72), UA_NODEID_NUMERIC(ns[0], 63), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "DataTypeDictionaryType"), UA_NODEID_NUMERIC(ns[0], 0), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_59_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 72) ); } /* NamespaceUri - ns=0;i=107 */ static UA_StatusCode function_namespace0_generated_60_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12); attr.displayName = UA_LOCALIZEDTEXT("", "NamespaceUri"); #ifdef UA_ENABLE_NODESET_COMPILER_DESCRIPTIONS attr.description = UA_LOCALIZEDTEXT("", "A URI that uniquely identifies the dictionary."); #endif retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 107), UA_NODEID_NUMERIC(ns[0], 72), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "NamespaceUri"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_60_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 107) ); } /* DataTypeVersion - ns=0;i=106 */ static UA_StatusCode function_namespace0_generated_61_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12); attr.displayName = UA_LOCALIZEDTEXT("", "DataTypeVersion"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 106), UA_NODEID_NUMERIC(ns[0], 72), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "DataTypeVersion"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_61_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 106) ); } /* DataTypeSystemType - ns=0;i=75 */ static UA_StatusCode function_namespace0_generated_62_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectTypeAttributes attr = UA_ObjectTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "DataTypeSystemType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECTTYPE, UA_NODEID_NUMERIC(ns[0], 75), UA_NODEID_NUMERIC(ns[0], 58), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "DataTypeSystemType"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_62_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 75) ); } /* OPC Binary - ns=0;i=93 */ static UA_StatusCode function_namespace0_generated_63_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "OPC Binary"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 93), UA_NODEID_NUMERIC(ns[0], 90), UA_NODEID_NUMERIC(ns[0], 35), UA_QUALIFIEDNAME(ns[0], "OPC Binary"), UA_NODEID_NUMERIC(ns[0], 75), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_63_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 93) ); } /* Opc.Ua - ns=0;i=7617 */ static UA_StatusCode function_namespace0_generated_64_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 15); UA_ByteString *variablenode_ns_0_i_7617_variant_DataContents = UA_ByteString_new(); if (!variablenode_ns_0_i_7617_variant_DataContents) return UA_STATUSCODE_BADOUTOFMEMORY; UA_ByteString_init(variablenode_ns_0_i_7617_variant_DataContents); *variablenode_ns_0_i_7617_variant_DataContents = UA_BYTESTRING_NULL; UA_Variant_setScalar(&attr.value, variablenode_ns_0_i_7617_variant_DataContents, &UA_TYPES[UA_TYPES_BYTESTRING]); attr.displayName = UA_LOCALIZEDTEXT("", "Opc.Ua"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 7617), UA_NODEID_NUMERIC(ns[0], 93), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "Opc.Ua"), UA_NODEID_NUMERIC(ns[0], 72), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); variablenode_ns_0_i_7617_variant_DataContents->data = NULL; variablenode_ns_0_i_7617_variant_DataContents->length = 0; UA_ByteString_delete(variablenode_ns_0_i_7617_variant_DataContents); return retVal; } static UA_StatusCode function_namespace0_generated_64_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 7617) ); } /* Argument - ns=0;i=7650 */ static UA_StatusCode function_namespace0_generated_65_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12); UA_String *variablenode_ns_0_i_7650_variant_DataContents = UA_String_new(); if (!variablenode_ns_0_i_7650_variant_DataContents) return UA_STATUSCODE_BADOUTOFMEMORY; UA_String_init(variablenode_ns_0_i_7650_variant_DataContents); *variablenode_ns_0_i_7650_variant_DataContents = UA_STRING_ALLOC("Argument"); UA_Variant_setScalar(&attr.value, variablenode_ns_0_i_7650_variant_DataContents, &UA_TYPES[UA_TYPES_STRING]); attr.displayName = UA_LOCALIZEDTEXT("", "Argument"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 7650), UA_NODEID_NUMERIC(ns[0], 7617), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "Argument"), UA_NODEID_NUMERIC(ns[0], 69), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); UA_String_delete(variablenode_ns_0_i_7650_variant_DataContents); return retVal; } static UA_StatusCode function_namespace0_generated_65_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 7650) ); } /* EnumValueType - ns=0;i=7656 */ static UA_StatusCode function_namespace0_generated_66_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12); UA_String *variablenode_ns_0_i_7656_variant_DataContents = UA_String_new(); if (!variablenode_ns_0_i_7656_variant_DataContents) return UA_STATUSCODE_BADOUTOFMEMORY; UA_String_init(variablenode_ns_0_i_7656_variant_DataContents); *variablenode_ns_0_i_7656_variant_DataContents = UA_STRING_ALLOC("EnumValueType"); UA_Variant_setScalar(&attr.value, variablenode_ns_0_i_7656_variant_DataContents, &UA_TYPES[UA_TYPES_STRING]); attr.displayName = UA_LOCALIZEDTEXT("", "EnumValueType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 7656), UA_NODEID_NUMERIC(ns[0], 7617), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "EnumValueType"), UA_NODEID_NUMERIC(ns[0], 69), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); UA_String_delete(variablenode_ns_0_i_7656_variant_DataContents); return retVal; } static UA_StatusCode function_namespace0_generated_66_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 7656) ); } /* XML Schema - ns=0;i=92 */ static UA_StatusCode function_namespace0_generated_67_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "XML Schema"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 92), UA_NODEID_NUMERIC(ns[0], 90), UA_NODEID_NUMERIC(ns[0], 35), UA_QUALIFIEDNAME(ns[0], "XML Schema"), UA_NODEID_NUMERIC(ns[0], 75), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_67_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 92) ); } /* DataTypeEncodingType - ns=0;i=76 */ static UA_StatusCode function_namespace0_generated_68_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectTypeAttributes attr = UA_ObjectTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "DataTypeEncodingType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECTTYPE, UA_NODEID_NUMERIC(ns[0], 76), UA_NODEID_NUMERIC(ns[0], 58), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "DataTypeEncodingType"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_68_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 76) ); } /* Default Binary - ns=0;i=8251 */ static UA_StatusCode function_namespace0_generated_69_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "Default Binary"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 8251), UA_NODEID_NUMERIC(ns[0], 0), UA_NODEID_NUMERIC(ns[0], 0), UA_QUALIFIEDNAME(ns[0], "Default Binary"), UA_NODEID_NUMERIC(ns[0], 76), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 8251), UA_NODEID_NUMERIC(ns[0], 38), UA_EXPANDEDNODEID_NUMERIC(ns[0], 7594), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 8251), UA_NODEID_NUMERIC(ns[0], 39), UA_EXPANDEDNODEID_NUMERIC(ns[0], 7656), true); return retVal; } static UA_StatusCode function_namespace0_generated_69_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 8251) ); } /* Default Binary - ns=0;i=298 */ static UA_StatusCode function_namespace0_generated_70_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "Default Binary"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 298), UA_NODEID_NUMERIC(ns[0], 0), UA_NODEID_NUMERIC(ns[0], 0), UA_QUALIFIEDNAME(ns[0], "Default Binary"), UA_NODEID_NUMERIC(ns[0], 76), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 298), UA_NODEID_NUMERIC(ns[0], 38), UA_EXPANDEDNODEID_NUMERIC(ns[0], 296), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 298), UA_NODEID_NUMERIC(ns[0], 39), UA_EXPANDEDNODEID_NUMERIC(ns[0], 7650), true); return retVal; } static UA_StatusCode function_namespace0_generated_70_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 298) ); } /* ModellingRuleType - ns=0;i=77 */ static UA_StatusCode function_namespace0_generated_71_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectTypeAttributes attr = UA_ObjectTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "ModellingRuleType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECTTYPE, UA_NODEID_NUMERIC(ns[0], 77), UA_NODEID_NUMERIC(ns[0], 58), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "ModellingRuleType"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_71_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 77) ); } /* Mandatory - ns=0;i=78 */ static UA_StatusCode function_namespace0_generated_72_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "Mandatory"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 78), UA_NODEID_NUMERIC(ns[0], 0), UA_NODEID_NUMERIC(ns[0], 0), UA_QUALIFIEDNAME(ns[0], "Mandatory"), UA_NODEID_NUMERIC(ns[0], 77), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 78), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 12169), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 78), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 7611), false); return retVal; } static UA_StatusCode function_namespace0_generated_72_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 78) ); } /* NamingRule - ns=0;i=112 */ static UA_StatusCode function_namespace0_generated_73_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 120); UA_Int32 *variablenode_ns_0_i_112_variant_DataContents = UA_Int32_new(); if (!variablenode_ns_0_i_112_variant_DataContents) return UA_STATUSCODE_BADOUTOFMEMORY; UA_Int32_init(variablenode_ns_0_i_112_variant_DataContents); *variablenode_ns_0_i_112_variant_DataContents = (UA_Int32) 1; UA_Variant_setScalar(&attr.value, variablenode_ns_0_i_112_variant_DataContents, &UA_TYPES[UA_TYPES_INT32]); attr.displayName = UA_LOCALIZEDTEXT("", "NamingRule"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 112), UA_NODEID_NUMERIC(ns[0], 78), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "NamingRule"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); UA_Int32_delete(variablenode_ns_0_i_112_variant_DataContents); return retVal; } static UA_StatusCode function_namespace0_generated_73_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 112) ); } /* NamingRule - ns=0;i=111 */ static UA_StatusCode function_namespace0_generated_74_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 120); UA_Int32 *variablenode_ns_0_i_111_variant_DataContents = UA_Int32_new(); if (!variablenode_ns_0_i_111_variant_DataContents) return UA_STATUSCODE_BADOUTOFMEMORY; UA_Int32_init(variablenode_ns_0_i_111_variant_DataContents); *variablenode_ns_0_i_111_variant_DataContents = (UA_Int32) 1; UA_Variant_setScalar(&attr.value, variablenode_ns_0_i_111_variant_DataContents, &UA_TYPES[UA_TYPES_INT32]); attr.displayName = UA_LOCALIZEDTEXT("", "NamingRule"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 111), UA_NODEID_NUMERIC(ns[0], 77), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "NamingRule"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); UA_Int32_delete(variablenode_ns_0_i_111_variant_DataContents); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 111), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 78), true); return retVal; } static UA_StatusCode function_namespace0_generated_74_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 111) ); } /* Optional - ns=0;i=80 */ static UA_StatusCode function_namespace0_generated_75_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "Optional"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 80), UA_NODEID_NUMERIC(ns[0], 0), UA_NODEID_NUMERIC(ns[0], 0), UA_QUALIFIEDNAME(ns[0], "Optional"), UA_NODEID_NUMERIC(ns[0], 77), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 80), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 104), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 80), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 105), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 80), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 107), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 80), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 106), false); return retVal; } static UA_StatusCode function_namespace0_generated_75_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 80) ); } /* NamingRule - ns=0;i=113 */ static UA_StatusCode function_namespace0_generated_76_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 120); UA_Int32 *variablenode_ns_0_i_113_variant_DataContents = UA_Int32_new(); if (!variablenode_ns_0_i_113_variant_DataContents) return UA_STATUSCODE_BADOUTOFMEMORY; UA_Int32_init(variablenode_ns_0_i_113_variant_DataContents); *variablenode_ns_0_i_113_variant_DataContents = (UA_Int32) 2; UA_Variant_setScalar(&attr.value, variablenode_ns_0_i_113_variant_DataContents, &UA_TYPES[UA_TYPES_INT32]); attr.displayName = UA_LOCALIZEDTEXT("", "NamingRule"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 113), UA_NODEID_NUMERIC(ns[0], 80), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "NamingRule"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); UA_Int32_delete(variablenode_ns_0_i_113_variant_DataContents); return retVal; } static UA_StatusCode function_namespace0_generated_76_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 113) ); } /* ServerRedundancyType - ns=0;i=2034 */ static UA_StatusCode function_namespace0_generated_77_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectTypeAttributes attr = UA_ObjectTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "ServerRedundancyType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECTTYPE, UA_NODEID_NUMERIC(ns[0], 2034), UA_NODEID_NUMERIC(ns[0], 58), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "ServerRedundancyType"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_77_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2034) ); } /* RedundancySupport - ns=0;i=2035 */ static UA_StatusCode function_namespace0_generated_78_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 851); attr.displayName = UA_LOCALIZEDTEXT("", "RedundancySupport"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2035), UA_NODEID_NUMERIC(ns[0], 2034), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "RedundancySupport"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 2035), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 78), true); return retVal; } static UA_StatusCode function_namespace0_generated_78_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2035) ); } /* BuildInfoType - ns=0;i=3051 */ static UA_StatusCode function_namespace0_generated_79_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableTypeAttributes attr = UA_VariableTypeAttributes_default; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 338); attr.displayName = UA_LOCALIZEDTEXT("", "BuildInfoType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLETYPE, UA_NODEID_NUMERIC(ns[0], 3051), UA_NODEID_NUMERIC(ns[0], 63), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "BuildInfoType"), UA_NODEID_NUMERIC(ns[0], 0), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_79_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 3051) ); } /* ServerDiagnosticsSummaryType - ns=0;i=2150 */ static UA_StatusCode function_namespace0_generated_80_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableTypeAttributes attr = UA_VariableTypeAttributes_default; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 859); attr.displayName = UA_LOCALIZEDTEXT("", "ServerDiagnosticsSummaryType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLETYPE, UA_NODEID_NUMERIC(ns[0], 2150), UA_NODEID_NUMERIC(ns[0], 63), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "ServerDiagnosticsSummaryType"), UA_NODEID_NUMERIC(ns[0], 0), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_80_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2150) ); } /* PublishingIntervalCount - ns=0;i=2159 */ static UA_StatusCode function_namespace0_generated_81_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "PublishingIntervalCount"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2159), UA_NODEID_NUMERIC(ns[0], 2150), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "PublishingIntervalCount"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 2159), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 78), true); return retVal; } static UA_StatusCode function_namespace0_generated_81_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2159) ); } /* SecurityRejectedSessionCount - ns=0;i=2154 */ static UA_StatusCode function_namespace0_generated_82_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "SecurityRejectedSessionCount"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2154), UA_NODEID_NUMERIC(ns[0], 2150), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "SecurityRejectedSessionCount"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 2154), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 78), true); return retVal; } static UA_StatusCode function_namespace0_generated_82_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2154) ); } /* SecurityRejectedRequestsCount - ns=0;i=2162 */ static UA_StatusCode function_namespace0_generated_83_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "SecurityRejectedRequestsCount"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2162), UA_NODEID_NUMERIC(ns[0], 2150), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "SecurityRejectedRequestsCount"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 2162), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 78), true); return retVal; } static UA_StatusCode function_namespace0_generated_83_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2162) ); } /* RejectedRequestsCount - ns=0;i=2163 */ static UA_StatusCode function_namespace0_generated_84_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "RejectedRequestsCount"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2163), UA_NODEID_NUMERIC(ns[0], 2150), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "RejectedRequestsCount"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 2163), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 78), true); return retVal; } static UA_StatusCode function_namespace0_generated_84_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2163) ); } /* RejectedSessionCount - ns=0;i=2155 */ static UA_StatusCode function_namespace0_generated_85_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "RejectedSessionCount"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2155), UA_NODEID_NUMERIC(ns[0], 2150), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "RejectedSessionCount"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 2155), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 78), true); return retVal; } static UA_StatusCode function_namespace0_generated_85_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2155) ); } /* CumulatedSubscriptionCount - ns=0;i=2161 */ static UA_StatusCode function_namespace0_generated_86_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "CumulatedSubscriptionCount"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2161), UA_NODEID_NUMERIC(ns[0], 2150), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "CumulatedSubscriptionCount"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 2161), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 78), true); return retVal; } static UA_StatusCode function_namespace0_generated_86_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2161) ); } /* CumulatedSessionCount - ns=0;i=2153 */ static UA_StatusCode function_namespace0_generated_87_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "CumulatedSessionCount"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2153), UA_NODEID_NUMERIC(ns[0], 2150), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "CumulatedSessionCount"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 2153), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 78), true); return retVal; } static UA_StatusCode function_namespace0_generated_87_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2153) ); } /* CurrentSessionCount - ns=0;i=2152 */ static UA_StatusCode function_namespace0_generated_88_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "CurrentSessionCount"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2152), UA_NODEID_NUMERIC(ns[0], 2150), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "CurrentSessionCount"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 2152), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 78), true); return retVal; } static UA_StatusCode function_namespace0_generated_88_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2152) ); } /* ServerViewCount - ns=0;i=2151 */ static UA_StatusCode function_namespace0_generated_89_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "ServerViewCount"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2151), UA_NODEID_NUMERIC(ns[0], 2150), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "ServerViewCount"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 2151), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 78), true); return retVal; } static UA_StatusCode function_namespace0_generated_89_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2151) ); } /* SessionTimeoutCount - ns=0;i=2156 */ static UA_StatusCode function_namespace0_generated_90_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "SessionTimeoutCount"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2156), UA_NODEID_NUMERIC(ns[0], 2150), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "SessionTimeoutCount"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 2156), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 78), true); return retVal; } static UA_StatusCode function_namespace0_generated_90_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2156) ); } /* CurrentSubscriptionCount - ns=0;i=2160 */ static UA_StatusCode function_namespace0_generated_91_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "CurrentSubscriptionCount"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2160), UA_NODEID_NUMERIC(ns[0], 2150), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "CurrentSubscriptionCount"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 2160), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 78), true); return retVal; } static UA_StatusCode function_namespace0_generated_91_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2160) ); } /* SessionAbortCount - ns=0;i=2157 */ static UA_StatusCode function_namespace0_generated_92_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "SessionAbortCount"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2157), UA_NODEID_NUMERIC(ns[0], 2150), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "SessionAbortCount"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 2157), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 78), true); return retVal; } static UA_StatusCode function_namespace0_generated_92_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2157) ); } /* ServerType - ns=0;i=2004 */ static UA_StatusCode function_namespace0_generated_93_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectTypeAttributes attr = UA_ObjectTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "ServerType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECTTYPE, UA_NODEID_NUMERIC(ns[0], 2004), UA_NODEID_NUMERIC(ns[0], 58), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "ServerType"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_93_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2004) ); } /* Server - ns=0;i=2253 */ static UA_StatusCode function_namespace0_generated_94_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.eventNotifier = true; attr.displayName = UA_LOCALIZEDTEXT("", "Server"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 2253), UA_NODEID_NUMERIC(ns[0], 85), UA_NODEID_NUMERIC(ns[0], 35), UA_QUALIFIEDNAME(ns[0], "Server"), UA_NODEID_NUMERIC(ns[0], 2004), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_94_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2253) ); } /* Auditing - ns=0;i=2994 */ static UA_StatusCode function_namespace0_generated_95_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 1000.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 1); attr.displayName = UA_LOCALIZEDTEXT("", "Auditing"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2994), UA_NODEID_NUMERIC(ns[0], 2253), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "Auditing"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_95_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2994) ); } /* GetMonitoredItems - ns=0;i=11492 */ static UA_StatusCode function_namespace0_generated_96_begin(UA_Server *server, UA_UInt16* ns) { #ifdef UA_ENABLE_METHODCALLS UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_MethodAttributes attr = UA_MethodAttributes_default; attr.executable = true; attr.userExecutable = true; attr.displayName = UA_LOCALIZEDTEXT("", "GetMonitoredItems"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_METHOD, UA_NODEID_NUMERIC(ns[0], 11492), UA_NODEID_NUMERIC(ns[0], 2253), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "GetMonitoredItems"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_METHODATTRIBUTES],NULL, NULL); return retVal; #else return UA_STATUSCODE_GOOD; #endif /* UA_ENABLE_METHODCALLS */ } static UA_StatusCode function_namespace0_generated_96_finish(UA_Server *server, UA_UInt16* ns) { #ifdef UA_ENABLE_METHODCALLS return UA_Server_addMethodNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11492) , NULL, 0, NULL, 0, NULL); #else return UA_STATUSCODE_GOOD; #endif /* UA_ENABLE_METHODCALLS */ } /* OutputArguments - ns=0;i=11494 */ static UA_StatusCode function_namespace0_generated_97_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; attr.valueRank = 1; attr.arrayDimensionsSize = 1; UA_UInt32 arrayDimensions[1]; arrayDimensions[0] = 0; attr.arrayDimensions = &arrayDimensions[0]; attr.dataType = UA_NODEID_NUMERIC(ns[0], 296); UA_Argument variablenode_ns_0_i_11494_variant_DataContents[2]; UA_init(&variablenode_ns_0_i_11494_variant_DataContents[0], &UA_TYPES[UA_TYPES_ARGUMENT]); variablenode_ns_0_i_11494_variant_DataContents[0].name = UA_STRING("ServerHandles"); variablenode_ns_0_i_11494_variant_DataContents[0].dataType = UA_NODEID_NUMERIC(ns[0], 7); variablenode_ns_0_i_11494_variant_DataContents[0].valueRank = (UA_Int32) 1; UA_STACKARRAY(UA_UInt32, variablenode_ns_0_i_11494_variant_DataContents0_arrayDimensions, 1); UA_init(variablenode_ns_0_i_11494_variant_DataContents0_arrayDimensions, &UA_TYPES[UA_TYPES_UINT32]); variablenode_ns_0_i_11494_variant_DataContents0_arrayDimensions[0] = (UA_UInt32) 0; variablenode_ns_0_i_11494_variant_DataContents[0].arrayDimensions = variablenode_ns_0_i_11494_variant_DataContents0_arrayDimensions; UA_init(&variablenode_ns_0_i_11494_variant_DataContents[1], &UA_TYPES[UA_TYPES_ARGUMENT]); variablenode_ns_0_i_11494_variant_DataContents[1].name = UA_STRING("ClientHandles"); variablenode_ns_0_i_11494_variant_DataContents[1].dataType = UA_NODEID_NUMERIC(ns[0], 7); variablenode_ns_0_i_11494_variant_DataContents[1].valueRank = (UA_Int32) 1; UA_STACKARRAY(UA_UInt32, variablenode_ns_0_i_11494_variant_DataContents1_arrayDimensions, 1); UA_init(variablenode_ns_0_i_11494_variant_DataContents1_arrayDimensions, &UA_TYPES[UA_TYPES_UINT32]); variablenode_ns_0_i_11494_variant_DataContents1_arrayDimensions[0] = (UA_UInt32) 0; variablenode_ns_0_i_11494_variant_DataContents[1].arrayDimensions = variablenode_ns_0_i_11494_variant_DataContents1_arrayDimensions; UA_Variant_setArray(&attr.value, &variablenode_ns_0_i_11494_variant_DataContents, (UA_Int32) 2, &UA_TYPES[UA_TYPES_ARGUMENT]); attr.displayName = UA_LOCALIZEDTEXT("", "OutputArguments"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11494), UA_NODEID_NUMERIC(ns[0], 11492), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "OutputArguments"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_97_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11494) ); } /* InputArguments - ns=0;i=11493 */ static UA_StatusCode function_namespace0_generated_98_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; attr.valueRank = 1; attr.arrayDimensionsSize = 1; UA_UInt32 arrayDimensions[1]; arrayDimensions[0] = 0; attr.arrayDimensions = &arrayDimensions[0]; attr.dataType = UA_NODEID_NUMERIC(ns[0], 296); UA_Argument variablenode_ns_0_i_11493_variant_DataContents[1]; UA_init(&variablenode_ns_0_i_11493_variant_DataContents[0], &UA_TYPES[UA_TYPES_ARGUMENT]); variablenode_ns_0_i_11493_variant_DataContents[0].name = UA_STRING("SubscriptionId"); variablenode_ns_0_i_11493_variant_DataContents[0].dataType = UA_NODEID_NUMERIC(ns[0], 7); variablenode_ns_0_i_11493_variant_DataContents[0].valueRank = (UA_Int32) -1; UA_Variant_setArray(&attr.value, &variablenode_ns_0_i_11493_variant_DataContents, (UA_Int32) 1, &UA_TYPES[UA_TYPES_ARGUMENT]); attr.displayName = UA_LOCALIZEDTEXT("", "InputArguments"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11493), UA_NODEID_NUMERIC(ns[0], 11492), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "InputArguments"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_98_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11493) ); } /* ServerStatus - ns=0;i=2256 */ static UA_StatusCode function_namespace0_generated_99_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 1000.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 862); attr.displayName = UA_LOCALIZEDTEXT("", "ServerStatus"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2256), UA_NODEID_NUMERIC(ns[0], 2253), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "ServerStatus"), UA_NODEID_NUMERIC(ns[0], 2138), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_99_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2256) ); } /* BuildInfo - ns=0;i=2260 */ static UA_StatusCode function_namespace0_generated_100_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 338); attr.displayName = UA_LOCALIZEDTEXT("", "BuildInfo"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2260), UA_NODEID_NUMERIC(ns[0], 2256), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "BuildInfo"), UA_NODEID_NUMERIC(ns[0], 3051), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_100_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2260) ); } /* BuildDate - ns=0;i=2266 */ static UA_StatusCode function_namespace0_generated_101_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 1000.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 294); attr.displayName = UA_LOCALIZEDTEXT("", "BuildDate"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2266), UA_NODEID_NUMERIC(ns[0], 2260), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "BuildDate"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_101_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2266) ); } /* BuildNumber - ns=0;i=2265 */ static UA_StatusCode function_namespace0_generated_102_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 1000.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12); attr.displayName = UA_LOCALIZEDTEXT("", "BuildNumber"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2265), UA_NODEID_NUMERIC(ns[0], 2260), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "BuildNumber"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_102_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2265) ); } /* SoftwareVersion - ns=0;i=2264 */ static UA_StatusCode function_namespace0_generated_103_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 1000.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12); attr.displayName = UA_LOCALIZEDTEXT("", "SoftwareVersion"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2264), UA_NODEID_NUMERIC(ns[0], 2260), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "SoftwareVersion"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_103_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2264) ); } /* ManufacturerName - ns=0;i=2263 */ static UA_StatusCode function_namespace0_generated_104_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 1000.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12); attr.displayName = UA_LOCALIZEDTEXT("", "ManufacturerName"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2263), UA_NODEID_NUMERIC(ns[0], 2260), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "ManufacturerName"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_104_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2263) ); } /* ProductUri - ns=0;i=2262 */ static UA_StatusCode function_namespace0_generated_105_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 1000.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12); attr.displayName = UA_LOCALIZEDTEXT("", "ProductUri"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2262), UA_NODEID_NUMERIC(ns[0], 2260), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "ProductUri"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_105_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2262) ); } /* ProductName - ns=0;i=2261 */ static UA_StatusCode function_namespace0_generated_106_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 1000.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12); attr.displayName = UA_LOCALIZEDTEXT("", "ProductName"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2261), UA_NODEID_NUMERIC(ns[0], 2260), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "ProductName"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_106_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2261) ); } /* ShutdownReason - ns=0;i=2993 */ static UA_StatusCode function_namespace0_generated_107_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 21); attr.displayName = UA_LOCALIZEDTEXT("", "ShutdownReason"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2993), UA_NODEID_NUMERIC(ns[0], 2256), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "ShutdownReason"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_107_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2993) ); } /* State - ns=0;i=2259 */ static UA_StatusCode function_namespace0_generated_108_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 852); attr.displayName = UA_LOCALIZEDTEXT("", "State"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2259), UA_NODEID_NUMERIC(ns[0], 2256), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "State"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_108_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2259) ); } /* CurrentTime - ns=0;i=2258 */ static UA_StatusCode function_namespace0_generated_109_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 294); attr.displayName = UA_LOCALIZEDTEXT("", "CurrentTime"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2258), UA_NODEID_NUMERIC(ns[0], 2256), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "CurrentTime"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_109_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2258) ); } /* StartTime - ns=0;i=2257 */ static UA_StatusCode function_namespace0_generated_110_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 294); attr.displayName = UA_LOCALIZEDTEXT("", "StartTime"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2257), UA_NODEID_NUMERIC(ns[0], 2256), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "StartTime"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_110_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2257) ); } /* SecondsTillShutdown - ns=0;i=2992 */ static UA_StatusCode function_namespace0_generated_111_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "SecondsTillShutdown"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2992), UA_NODEID_NUMERIC(ns[0], 2256), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "SecondsTillShutdown"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_111_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2992) ); } /* ServerDiagnostics - ns=0;i=2274 */ static UA_StatusCode function_namespace0_generated_112_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "ServerDiagnostics"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 2274), UA_NODEID_NUMERIC(ns[0], 2253), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "ServerDiagnostics"), UA_NODEID_NUMERIC(ns[0], 2020), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_112_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2274) ); } /* ServerDiagnosticsSummary - ns=0;i=2275 */ static UA_StatusCode function_namespace0_generated_113_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 859); attr.displayName = UA_LOCALIZEDTEXT("", "ServerDiagnosticsSummary"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2275), UA_NODEID_NUMERIC(ns[0], 2274), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "ServerDiagnosticsSummary"), UA_NODEID_NUMERIC(ns[0], 2150), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_113_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2275) ); } /* SecurityRejectedRequestsCount - ns=0;i=2287 */ static UA_StatusCode function_namespace0_generated_114_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "SecurityRejectedRequestsCount"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2287), UA_NODEID_NUMERIC(ns[0], 2275), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "SecurityRejectedRequestsCount"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_114_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2287) ); } /* CumulatedSubscriptionCount - ns=0;i=2286 */ static UA_StatusCode function_namespace0_generated_115_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "CumulatedSubscriptionCount"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2286), UA_NODEID_NUMERIC(ns[0], 2275), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "CumulatedSubscriptionCount"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_115_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2286) ); } /* CurrentSubscriptionCount - ns=0;i=2285 */ static UA_StatusCode function_namespace0_generated_116_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "CurrentSubscriptionCount"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2285), UA_NODEID_NUMERIC(ns[0], 2275), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "CurrentSubscriptionCount"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_116_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2285) ); } /* PublishingIntervalCount - ns=0;i=2284 */ static UA_StatusCode function_namespace0_generated_117_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "PublishingIntervalCount"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2284), UA_NODEID_NUMERIC(ns[0], 2275), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "PublishingIntervalCount"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_117_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2284) ); } /* SessionAbortCount - ns=0;i=2282 */ static UA_StatusCode function_namespace0_generated_118_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "SessionAbortCount"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2282), UA_NODEID_NUMERIC(ns[0], 2275), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "SessionAbortCount"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_118_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2282) ); } /* SessionTimeoutCount - ns=0;i=2281 */ static UA_StatusCode function_namespace0_generated_119_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "SessionTimeoutCount"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2281), UA_NODEID_NUMERIC(ns[0], 2275), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "SessionTimeoutCount"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_119_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2281) ); } /* RejectedSessionCount - ns=0;i=3705 */ static UA_StatusCode function_namespace0_generated_120_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "RejectedSessionCount"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 3705), UA_NODEID_NUMERIC(ns[0], 2275), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "RejectedSessionCount"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_120_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 3705) ); } /* RejectedRequestsCount - ns=0;i=2288 */ static UA_StatusCode function_namespace0_generated_121_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "RejectedRequestsCount"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2288), UA_NODEID_NUMERIC(ns[0], 2275), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "RejectedRequestsCount"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_121_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2288) ); } /* ServerViewCount - ns=0;i=2276 */ static UA_StatusCode function_namespace0_generated_122_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "ServerViewCount"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2276), UA_NODEID_NUMERIC(ns[0], 2275), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "ServerViewCount"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_122_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2276) ); } /* CurrentSessionCount - ns=0;i=2277 */ static UA_StatusCode function_namespace0_generated_123_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "CurrentSessionCount"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2277), UA_NODEID_NUMERIC(ns[0], 2275), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "CurrentSessionCount"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_123_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2277) ); } /* CumulatedSessionCount - ns=0;i=2278 */ static UA_StatusCode function_namespace0_generated_124_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "CumulatedSessionCount"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2278), UA_NODEID_NUMERIC(ns[0], 2275), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "CumulatedSessionCount"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_124_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2278) ); } /* SecurityRejectedSessionCount - ns=0;i=2279 */ static UA_StatusCode function_namespace0_generated_125_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "SecurityRejectedSessionCount"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2279), UA_NODEID_NUMERIC(ns[0], 2275), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "SecurityRejectedSessionCount"), UA_NODEID_NUMERIC(ns[0], 63), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_125_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2279) ); } /* EnabledFlag - ns=0;i=2294 */ static UA_StatusCode function_namespace0_generated_126_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 3; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 1); attr.displayName = UA_LOCALIZEDTEXT("", "EnabledFlag"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2294), UA_NODEID_NUMERIC(ns[0], 2274), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "EnabledFlag"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_126_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2294) ); } /* VendorServerInfo - ns=0;i=2295 */ static UA_StatusCode function_namespace0_generated_127_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "VendorServerInfo"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 2295), UA_NODEID_NUMERIC(ns[0], 2253), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "VendorServerInfo"), UA_NODEID_NUMERIC(ns[0], 2033), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_127_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2295) ); } /* NamespaceArray - ns=0;i=2255 */ static UA_StatusCode function_namespace0_generated_128_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 1000.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; attr.valueRank = 1; attr.arrayDimensionsSize = 1; UA_UInt32 arrayDimensions[1]; arrayDimensions[0] = 0; attr.arrayDimensions = &arrayDimensions[0]; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12); attr.displayName = UA_LOCALIZEDTEXT("", "NamespaceArray"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2255), UA_NODEID_NUMERIC(ns[0], 2253), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "NamespaceArray"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_128_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2255) ); } /* ServerArray - ns=0;i=2254 */ static UA_StatusCode function_namespace0_generated_129_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 1000.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; attr.valueRank = 1; attr.arrayDimensionsSize = 1; UA_UInt32 arrayDimensions[1]; arrayDimensions[0] = 0; attr.arrayDimensions = &arrayDimensions[0]; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12); attr.displayName = UA_LOCALIZEDTEXT("", "ServerArray"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2254), UA_NODEID_NUMERIC(ns[0], 2253), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "ServerArray"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_129_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2254) ); } /* ServiceLevel - ns=0;i=2267 */ static UA_StatusCode function_namespace0_generated_130_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 1000.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 3); attr.displayName = UA_LOCALIZEDTEXT("", "ServiceLevel"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2267), UA_NODEID_NUMERIC(ns[0], 2253), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "ServiceLevel"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_130_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2267) ); } /* ServerRedundancy - ns=0;i=2296 */ static UA_StatusCode function_namespace0_generated_131_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "ServerRedundancy"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 2296), UA_NODEID_NUMERIC(ns[0], 2253), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "ServerRedundancy"), UA_NODEID_NUMERIC(ns[0], 2034), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_131_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2296) ); } /* RedundancySupport - ns=0;i=3709 */ static UA_StatusCode function_namespace0_generated_132_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 851); attr.displayName = UA_LOCALIZEDTEXT("", "RedundancySupport"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 3709), UA_NODEID_NUMERIC(ns[0], 2296), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "RedundancySupport"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_132_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 3709) ); } /* VendorServerInfo - ns=0;i=2011 */ static UA_StatusCode function_namespace0_generated_133_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "VendorServerInfo"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 2011), UA_NODEID_NUMERIC(ns[0], 2004), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "VendorServerInfo"), UA_NODEID_NUMERIC(ns[0], 2033), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 2011), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 78), true); return retVal; } static UA_StatusCode function_namespace0_generated_133_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2011) ); } /* InterfaceTypes - ns=0;i=17708 */ static UA_StatusCode function_namespace0_generated_134_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "InterfaceTypes"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 17708), UA_NODEID_NUMERIC(ns[0], 86), UA_NODEID_NUMERIC(ns[0], 35), UA_QUALIFIEDNAME(ns[0], "InterfaceTypes"), UA_NODEID_NUMERIC(ns[0], 61), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_134_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 17708) ); } /* BaseInterfaceType - ns=0;i=17602 */ static UA_StatusCode function_namespace0_generated_135_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectTypeAttributes attr = UA_ObjectTypeAttributes_default; attr.isAbstract = true; attr.displayName = UA_LOCALIZEDTEXT("", "BaseInterfaceType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECTTYPE, UA_NODEID_NUMERIC(ns[0], 17602), UA_NODEID_NUMERIC(ns[0], 58), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "BaseInterfaceType"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 17602), UA_NODEID_NUMERIC(ns[0], 35), UA_EXPANDEDNODEID_NUMERIC(ns[0], 17708), false); return retVal; } static UA_StatusCode function_namespace0_generated_135_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 17602) ); } /* OperationLimitsType - ns=0;i=11564 */ static UA_StatusCode function_namespace0_generated_136_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectTypeAttributes attr = UA_ObjectTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "OperationLimitsType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECTTYPE, UA_NODEID_NUMERIC(ns[0], 11564), UA_NODEID_NUMERIC(ns[0], 61), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "OperationLimitsType"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_136_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11564) ); } /* MaxNodesPerWrite - ns=0;i=11567 */ static UA_StatusCode function_namespace0_generated_137_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerWrite"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11567), UA_NODEID_NUMERIC(ns[0], 11564), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerWrite"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 11567), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 80), true); return retVal; } static UA_StatusCode function_namespace0_generated_137_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11567) ); } /* MaxNodesPerRead - ns=0;i=11565 */ static UA_StatusCode function_namespace0_generated_138_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerRead"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11565), UA_NODEID_NUMERIC(ns[0], 11564), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerRead"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 11565), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 80), true); return retVal; } static UA_StatusCode function_namespace0_generated_138_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11565) ); } /* MaxNodesPerMethodCall - ns=0;i=11569 */ static UA_StatusCode function_namespace0_generated_139_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerMethodCall"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11569), UA_NODEID_NUMERIC(ns[0], 11564), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerMethodCall"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 11569), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 80), true); return retVal; } static UA_StatusCode function_namespace0_generated_139_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11569) ); } /* MaxNodesPerRegisterNodes - ns=0;i=11571 */ static UA_StatusCode function_namespace0_generated_140_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerRegisterNodes"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11571), UA_NODEID_NUMERIC(ns[0], 11564), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerRegisterNodes"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 11571), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 80), true); return retVal; } static UA_StatusCode function_namespace0_generated_140_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11571) ); } /* MaxNodesPerBrowse - ns=0;i=11570 */ static UA_StatusCode function_namespace0_generated_141_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerBrowse"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11570), UA_NODEID_NUMERIC(ns[0], 11564), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerBrowse"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 11570), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 80), true); return retVal; } static UA_StatusCode function_namespace0_generated_141_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11570) ); } /* MaxNodesPerNodeManagement - ns=0;i=11573 */ static UA_StatusCode function_namespace0_generated_142_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerNodeManagement"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11573), UA_NODEID_NUMERIC(ns[0], 11564), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerNodeManagement"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 11573), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 80), true); return retVal; } static UA_StatusCode function_namespace0_generated_142_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11573) ); } /* MaxNodesPerTranslateBrowsePathsToNodeIds - ns=0;i=11572 */ static UA_StatusCode function_namespace0_generated_143_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerTranslateBrowsePathsToNodeIds"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11572), UA_NODEID_NUMERIC(ns[0], 11564), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerTranslateBrowsePathsToNodeIds"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 11572), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 80), true); return retVal; } static UA_StatusCode function_namespace0_generated_143_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11572) ); } /* MaxMonitoredItemsPerCall - ns=0;i=11574 */ static UA_StatusCode function_namespace0_generated_144_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "MaxMonitoredItemsPerCall"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11574), UA_NODEID_NUMERIC(ns[0], 11564), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "MaxMonitoredItemsPerCall"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 11574), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 80), true); return retVal; } static UA_StatusCode function_namespace0_generated_144_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11574) ); } /* ServerCapabilitiesType - ns=0;i=2013 */ static UA_StatusCode function_namespace0_generated_145_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectTypeAttributes attr = UA_ObjectTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "ServerCapabilitiesType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECTTYPE, UA_NODEID_NUMERIC(ns[0], 2013), UA_NODEID_NUMERIC(ns[0], 58), UA_NODEID_NUMERIC(ns[0], 45), UA_QUALIFIEDNAME(ns[0], "ServerCapabilitiesType"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_145_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2013) ); } /* OperationLimits - ns=0;i=11551 */ static UA_StatusCode function_namespace0_generated_146_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "OperationLimits"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 11551), UA_NODEID_NUMERIC(ns[0], 2013), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "OperationLimits"), UA_NODEID_NUMERIC(ns[0], 11564), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 11551), UA_NODEID_NUMERIC(ns[0], 37), UA_EXPANDEDNODEID_NUMERIC(ns[0], 80), true); return retVal; } static UA_StatusCode function_namespace0_generated_146_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11551) ); } /* ServerCapabilities - ns=0;i=2268 */ static UA_StatusCode function_namespace0_generated_147_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "ServerCapabilities"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 2268), UA_NODEID_NUMERIC(ns[0], 2253), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "ServerCapabilities"), UA_NODEID_NUMERIC(ns[0], 2013), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_147_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2268) ); } /* ServerProfileArray - ns=0;i=2269 */ static UA_StatusCode function_namespace0_generated_148_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; attr.valueRank = 1; attr.arrayDimensionsSize = 1; UA_UInt32 arrayDimensions[1]; arrayDimensions[0] = 0; attr.arrayDimensions = &arrayDimensions[0]; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12); attr.displayName = UA_LOCALIZEDTEXT("", "ServerProfileArray"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2269), UA_NODEID_NUMERIC(ns[0], 2268), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "ServerProfileArray"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_148_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2269) ); } /* AggregateFunctions - ns=0;i=2997 */ static UA_StatusCode function_namespace0_generated_149_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "AggregateFunctions"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 2997), UA_NODEID_NUMERIC(ns[0], 2268), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "AggregateFunctions"), UA_NODEID_NUMERIC(ns[0], 61), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_149_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2997) ); } /* ModellingRules - ns=0;i=2996 */ static UA_StatusCode function_namespace0_generated_150_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "ModellingRules"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 2996), UA_NODEID_NUMERIC(ns[0], 2268), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "ModellingRules"), UA_NODEID_NUMERIC(ns[0], 61), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_150_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2996) ); } /* OperationLimits - ns=0;i=11704 */ static UA_StatusCode function_namespace0_generated_151_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "OperationLimits"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 11704), UA_NODEID_NUMERIC(ns[0], 2268), UA_NODEID_NUMERIC(ns[0], 47), UA_QUALIFIEDNAME(ns[0], "OperationLimits"), UA_NODEID_NUMERIC(ns[0], 11564), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_151_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11704) ); } /* MaxNodesPerWrite - ns=0;i=11707 */ static UA_StatusCode function_namespace0_generated_152_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerWrite"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11707), UA_NODEID_NUMERIC(ns[0], 11704), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerWrite"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_152_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11707) ); } /* MaxNodesPerRead - ns=0;i=11705 */ static UA_StatusCode function_namespace0_generated_153_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerRead"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11705), UA_NODEID_NUMERIC(ns[0], 11704), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerRead"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_153_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11705) ); } /* MaxMonitoredItemsPerCall - ns=0;i=11714 */ static UA_StatusCode function_namespace0_generated_154_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "MaxMonitoredItemsPerCall"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11714), UA_NODEID_NUMERIC(ns[0], 11704), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "MaxMonitoredItemsPerCall"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_154_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11714) ); } /* MaxNodesPerRegisterNodes - ns=0;i=11711 */ static UA_StatusCode function_namespace0_generated_155_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerRegisterNodes"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11711), UA_NODEID_NUMERIC(ns[0], 11704), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerRegisterNodes"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_155_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11711) ); } /* MaxNodesPerBrowse - ns=0;i=11710 */ static UA_StatusCode function_namespace0_generated_156_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerBrowse"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11710), UA_NODEID_NUMERIC(ns[0], 11704), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerBrowse"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_156_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11710) ); } /* MaxNodesPerNodeManagement - ns=0;i=11713 */ static UA_StatusCode function_namespace0_generated_157_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerNodeManagement"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11713), UA_NODEID_NUMERIC(ns[0], 11704), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerNodeManagement"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_157_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11713) ); } /* MaxNodesPerTranslateBrowsePathsToNodeIds - ns=0;i=11712 */ static UA_StatusCode function_namespace0_generated_158_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerTranslateBrowsePathsToNodeIds"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11712), UA_NODEID_NUMERIC(ns[0], 11704), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerTranslateBrowsePathsToNodeIds"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_158_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11712) ); } /* MaxNodesPerMethodCall - ns=0;i=11709 */ static UA_StatusCode function_namespace0_generated_159_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerMethodCall"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11709), UA_NODEID_NUMERIC(ns[0], 11704), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerMethodCall"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_159_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11709) ); } /* SoftwareCertificates - ns=0;i=3704 */ static UA_StatusCode function_namespace0_generated_160_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; attr.valueRank = 1; attr.arrayDimensionsSize = 1; UA_UInt32 arrayDimensions[1]; arrayDimensions[0] = 0; attr.arrayDimensions = &arrayDimensions[0]; attr.dataType = UA_NODEID_NUMERIC(ns[0], 344); attr.displayName = UA_LOCALIZEDTEXT("", "SoftwareCertificates"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 3704), UA_NODEID_NUMERIC(ns[0], 2268), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "SoftwareCertificates"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_160_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 3704) ); } /* MinSupportedSampleRate - ns=0;i=2272 */ static UA_StatusCode function_namespace0_generated_161_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 290); attr.displayName = UA_LOCALIZEDTEXT("", "MinSupportedSampleRate"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2272), UA_NODEID_NUMERIC(ns[0], 2268), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "MinSupportedSampleRate"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_161_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2272) ); } /* LocaleIdArray - ns=0;i=2271 */ static UA_StatusCode function_namespace0_generated_162_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; attr.valueRank = 1; attr.arrayDimensionsSize = 1; UA_UInt32 arrayDimensions[1]; arrayDimensions[0] = 0; attr.arrayDimensions = &arrayDimensions[0]; attr.dataType = UA_NODEID_NUMERIC(ns[0], 295); attr.displayName = UA_LOCALIZEDTEXT("", "LocaleIdArray"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2271), UA_NODEID_NUMERIC(ns[0], 2268), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "LocaleIdArray"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_162_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2271) ); } /* MaxQueryContinuationPoints - ns=0;i=2736 */ static UA_StatusCode function_namespace0_generated_163_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 5); attr.displayName = UA_LOCALIZEDTEXT("", "MaxQueryContinuationPoints"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2736), UA_NODEID_NUMERIC(ns[0], 2268), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "MaxQueryContinuationPoints"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_163_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2736) ); } /* MaxHistoryContinuationPoints - ns=0;i=2737 */ static UA_StatusCode function_namespace0_generated_164_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 5); attr.displayName = UA_LOCALIZEDTEXT("", "MaxHistoryContinuationPoints"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2737), UA_NODEID_NUMERIC(ns[0], 2268), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "MaxHistoryContinuationPoints"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_164_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2737) ); } /* MaxBrowseContinuationPoints - ns=0;i=2735 */ static UA_StatusCode function_namespace0_generated_165_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.minimumSamplingInterval = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -1; attr.dataType = UA_NODEID_NUMERIC(ns[0], 5); attr.displayName = UA_LOCALIZEDTEXT("", "MaxBrowseContinuationPoints"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2735), UA_NODEID_NUMERIC(ns[0], 2268), UA_NODEID_NUMERIC(ns[0], 46), UA_QUALIFIEDNAME(ns[0], "MaxBrowseContinuationPoints"), UA_NODEID_NUMERIC(ns[0], 68), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_165_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2735) ); } UA_StatusCode namespace0_generated(UA_Server *server) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; /* Use namespace ids generated by the server */ UA_UInt16 ns[1]; ns[0] = UA_Server_addNamespace(server, "http://opcfoundation.org/UA/"); bool dummy = ( !(retVal = function_namespace0_generated_0_begin(server, ns)) && !(retVal = function_namespace0_generated_1_begin(server, ns)) && !(retVal = function_namespace0_generated_2_begin(server, ns)) && !(retVal = function_namespace0_generated_3_begin(server, ns)) && !(retVal = function_namespace0_generated_4_begin(server, ns)) && !(retVal = function_namespace0_generated_5_begin(server, ns)) && !(retVal = function_namespace0_generated_6_begin(server, ns)) && !(retVal = function_namespace0_generated_7_begin(server, ns)) && !(retVal = function_namespace0_generated_8_begin(server, ns)) && !(retVal = function_namespace0_generated_9_begin(server, ns)) && !(retVal = function_namespace0_generated_10_begin(server, ns)) && !(retVal = function_namespace0_generated_11_begin(server, ns)) && !(retVal = function_namespace0_generated_12_begin(server, ns)) && !(retVal = function_namespace0_generated_13_begin(server, ns)) && !(retVal = function_namespace0_generated_14_begin(server, ns)) && !(retVal = function_namespace0_generated_15_begin(server, ns)) && !(retVal = function_namespace0_generated_16_begin(server, ns)) && !(retVal = function_namespace0_generated_17_begin(server, ns)) && !(retVal = function_namespace0_generated_18_begin(server, ns)) && !(retVal = function_namespace0_generated_19_begin(server, ns)) && !(retVal = function_namespace0_generated_20_begin(server, ns)) && !(retVal = function_namespace0_generated_21_begin(server, ns)) && !(retVal = function_namespace0_generated_22_begin(server, ns)) && !(retVal = function_namespace0_generated_23_begin(server, ns)) && !(retVal = function_namespace0_generated_24_begin(server, ns)) && !(retVal = function_namespace0_generated_25_begin(server, ns)) && !(retVal = function_namespace0_generated_26_begin(server, ns)) && !(retVal = function_namespace0_generated_27_begin(server, ns)) && !(retVal = function_namespace0_generated_28_begin(server, ns)) && !(retVal = function_namespace0_generated_29_begin(server, ns)) && !(retVal = function_namespace0_generated_30_begin(server, ns)) && !(retVal = function_namespace0_generated_31_begin(server, ns)) && !(retVal = function_namespace0_generated_32_begin(server, ns)) && !(retVal = function_namespace0_generated_33_begin(server, ns)) && !(retVal = function_namespace0_generated_34_begin(server, ns)) && !(retVal = function_namespace0_generated_35_begin(server, ns)) && !(retVal = function_namespace0_generated_36_begin(server, ns)) && !(retVal = function_namespace0_generated_37_begin(server, ns)) && !(retVal = function_namespace0_generated_38_begin(server, ns)) && !(retVal = function_namespace0_generated_39_begin(server, ns)) && !(retVal = function_namespace0_generated_40_begin(server, ns)) && !(retVal = function_namespace0_generated_41_begin(server, ns)) && !(retVal = function_namespace0_generated_42_begin(server, ns)) && !(retVal = function_namespace0_generated_43_begin(server, ns)) && !(retVal = function_namespace0_generated_44_begin(server, ns)) && !(retVal = function_namespace0_generated_45_begin(server, ns)) && !(retVal = function_namespace0_generated_46_begin(server, ns)) && !(retVal = function_namespace0_generated_47_begin(server, ns)) && !(retVal = function_namespace0_generated_48_begin(server, ns)) && !(retVal = function_namespace0_generated_49_begin(server, ns)) && !(retVal = function_namespace0_generated_50_begin(server, ns)) && !(retVal = function_namespace0_generated_51_begin(server, ns)) && !(retVal = function_namespace0_generated_52_begin(server, ns)) && !(retVal = function_namespace0_generated_53_begin(server, ns)) && !(retVal = function_namespace0_generated_54_begin(server, ns)) && !(retVal = function_namespace0_generated_55_begin(server, ns)) && !(retVal = function_namespace0_generated_56_begin(server, ns)) && !(retVal = function_namespace0_generated_57_begin(server, ns)) && !(retVal = function_namespace0_generated_58_begin(server, ns)) && !(retVal = function_namespace0_generated_59_begin(server, ns)) && !(retVal = function_namespace0_generated_60_begin(server, ns)) && !(retVal = function_namespace0_generated_61_begin(server, ns)) && !(retVal = function_namespace0_generated_62_begin(server, ns)) && !(retVal = function_namespace0_generated_63_begin(server, ns)) && !(retVal = function_namespace0_generated_64_begin(server, ns)) && !(retVal = function_namespace0_generated_65_begin(server, ns)) && !(retVal = function_namespace0_generated_66_begin(server, ns)) && !(retVal = function_namespace0_generated_67_begin(server, ns)) && !(retVal = function_namespace0_generated_68_begin(server, ns)) && !(retVal = function_namespace0_generated_69_begin(server, ns)) && !(retVal = function_namespace0_generated_70_begin(server, ns)) && !(retVal = function_namespace0_generated_71_begin(server, ns)) && !(retVal = function_namespace0_generated_72_begin(server, ns)) && !(retVal = function_namespace0_generated_73_begin(server, ns)) && !(retVal = function_namespace0_generated_74_begin(server, ns)) && !(retVal = function_namespace0_generated_75_begin(server, ns)) && !(retVal = function_namespace0_generated_76_begin(server, ns)) && !(retVal = function_namespace0_generated_77_begin(server, ns)) && !(retVal = function_namespace0_generated_78_begin(server, ns)) && !(retVal = function_namespace0_generated_79_begin(server, ns)) && !(retVal = function_namespace0_generated_80_begin(server, ns)) && !(retVal = function_namespace0_generated_81_begin(server, ns)) && !(retVal = function_namespace0_generated_82_begin(server, ns)) && !(retVal = function_namespace0_generated_83_begin(server, ns)) && !(retVal = function_namespace0_generated_84_begin(server, ns)) && !(retVal = function_namespace0_generated_85_begin(server, ns)) && !(retVal = function_namespace0_generated_86_begin(server, ns)) && !(retVal = function_namespace0_generated_87_begin(server, ns)) && !(retVal = function_namespace0_generated_88_begin(server, ns)) && !(retVal = function_namespace0_generated_89_begin(server, ns)) && !(retVal = function_namespace0_generated_90_begin(server, ns)) && !(retVal = function_namespace0_generated_91_begin(server, ns)) && !(retVal = function_namespace0_generated_92_begin(server, ns)) && !(retVal = function_namespace0_generated_93_begin(server, ns)) && !(retVal = function_namespace0_generated_94_begin(server, ns)) && !(retVal = function_namespace0_generated_95_begin(server, ns)) && !(retVal = function_namespace0_generated_96_begin(server, ns)) && !(retVal = function_namespace0_generated_97_begin(server, ns)) && !(retVal = function_namespace0_generated_98_begin(server, ns)) && !(retVal = function_namespace0_generated_99_begin(server, ns)) && !(retVal = function_namespace0_generated_100_begin(server, ns)) && !(retVal = function_namespace0_generated_101_begin(server, ns)) && !(retVal = function_namespace0_generated_102_begin(server, ns)) && !(retVal = function_namespace0_generated_103_begin(server, ns)) && !(retVal = function_namespace0_generated_104_begin(server, ns)) && !(retVal = function_namespace0_generated_105_begin(server, ns)) && !(retVal = function_namespace0_generated_106_begin(server, ns)) && !(retVal = function_namespace0_generated_107_begin(server, ns)) && !(retVal = function_namespace0_generated_108_begin(server, ns)) && !(retVal = function_namespace0_generated_109_begin(server, ns)) && !(retVal = function_namespace0_generated_110_begin(server, ns)) && !(retVal = function_namespace0_generated_111_begin(server, ns)) && !(retVal = function_namespace0_generated_112_begin(server, ns)) && !(retVal = function_namespace0_generated_113_begin(server, ns)) && !(retVal = function_namespace0_generated_114_begin(server, ns)) && !(retVal = function_namespace0_generated_115_begin(server, ns)) && !(retVal = function_namespace0_generated_116_begin(server, ns)) && !(retVal = function_namespace0_generated_117_begin(server, ns)) && !(retVal = function_namespace0_generated_118_begin(server, ns)) && !(retVal = function_namespace0_generated_119_begin(server, ns)) && !(retVal = function_namespace0_generated_120_begin(server, ns)) && !(retVal = function_namespace0_generated_121_begin(server, ns)) && !(retVal = function_namespace0_generated_122_begin(server, ns)) && !(retVal = function_namespace0_generated_123_begin(server, ns)) && !(retVal = function_namespace0_generated_124_begin(server, ns)) && !(retVal = function_namespace0_generated_125_begin(server, ns)) && !(retVal = function_namespace0_generated_126_begin(server, ns)) && !(retVal = function_namespace0_generated_127_begin(server, ns)) && !(retVal = function_namespace0_generated_128_begin(server, ns)) && !(retVal = function_namespace0_generated_129_begin(server, ns)) && !(retVal = function_namespace0_generated_130_begin(server, ns)) && !(retVal = function_namespace0_generated_131_begin(server, ns)) && !(retVal = function_namespace0_generated_132_begin(server, ns)) && !(retVal = function_namespace0_generated_133_begin(server, ns)) && !(retVal = function_namespace0_generated_134_begin(server, ns)) && !(retVal = function_namespace0_generated_135_begin(server, ns)) && !(retVal = function_namespace0_generated_136_begin(server, ns)) && !(retVal = function_namespace0_generated_137_begin(server, ns)) && !(retVal = function_namespace0_generated_138_begin(server, ns)) && !(retVal = function_namespace0_generated_139_begin(server, ns)) && !(retVal = function_namespace0_generated_140_begin(server, ns)) && !(retVal = function_namespace0_generated_141_begin(server, ns)) && !(retVal = function_namespace0_generated_142_begin(server, ns)) && !(retVal = function_namespace0_generated_143_begin(server, ns)) && !(retVal = function_namespace0_generated_144_begin(server, ns)) && !(retVal = function_namespace0_generated_145_begin(server, ns)) && !(retVal = function_namespace0_generated_146_begin(server, ns)) && !(retVal = function_namespace0_generated_147_begin(server, ns)) && !(retVal = function_namespace0_generated_148_begin(server, ns)) && !(retVal = function_namespace0_generated_149_begin(server, ns)) && !(retVal = function_namespace0_generated_150_begin(server, ns)) && !(retVal = function_namespace0_generated_151_begin(server, ns)) && !(retVal = function_namespace0_generated_152_begin(server, ns)) && !(retVal = function_namespace0_generated_153_begin(server, ns)) && !(retVal = function_namespace0_generated_154_begin(server, ns)) && !(retVal = function_namespace0_generated_155_begin(server, ns)) && !(retVal = function_namespace0_generated_156_begin(server, ns)) && !(retVal = function_namespace0_generated_157_begin(server, ns)) && !(retVal = function_namespace0_generated_158_begin(server, ns)) && !(retVal = function_namespace0_generated_159_begin(server, ns)) && !(retVal = function_namespace0_generated_160_begin(server, ns)) && !(retVal = function_namespace0_generated_161_begin(server, ns)) && !(retVal = function_namespace0_generated_162_begin(server, ns)) && !(retVal = function_namespace0_generated_163_begin(server, ns)) && !(retVal = function_namespace0_generated_164_begin(server, ns)) && !(retVal = function_namespace0_generated_165_begin(server, ns)) && !(retVal = function_namespace0_generated_165_finish(server, ns)) && !(retVal = function_namespace0_generated_164_finish(server, ns)) && !(retVal = function_namespace0_generated_163_finish(server, ns)) && !(retVal = function_namespace0_generated_162_finish(server, ns)) && !(retVal = function_namespace0_generated_161_finish(server, ns)) && !(retVal = function_namespace0_generated_160_finish(server, ns)) && !(retVal = function_namespace0_generated_159_finish(server, ns)) && !(retVal = function_namespace0_generated_158_finish(server, ns)) && !(retVal = function_namespace0_generated_157_finish(server, ns)) && !(retVal = function_namespace0_generated_156_finish(server, ns)) && !(retVal = function_namespace0_generated_155_finish(server, ns)) && !(retVal = function_namespace0_generated_154_finish(server, ns)) && !(retVal = function_namespace0_generated_153_finish(server, ns)) && !(retVal = function_namespace0_generated_152_finish(server, ns)) && !(retVal = function_namespace0_generated_151_finish(server, ns)) && !(retVal = function_namespace0_generated_150_finish(server, ns)) && !(retVal = function_namespace0_generated_149_finish(server, ns)) && !(retVal = function_namespace0_generated_148_finish(server, ns)) && !(retVal = function_namespace0_generated_147_finish(server, ns)) && !(retVal = function_namespace0_generated_146_finish(server, ns)) && !(retVal = function_namespace0_generated_145_finish(server, ns)) && !(retVal = function_namespace0_generated_144_finish(server, ns)) && !(retVal = function_namespace0_generated_143_finish(server, ns)) && !(retVal = function_namespace0_generated_142_finish(server, ns)) && !(retVal = function_namespace0_generated_141_finish(server, ns)) && !(retVal = function_namespace0_generated_140_finish(server, ns)) && !(retVal = function_namespace0_generated_139_finish(server, ns)) && !(retVal = function_namespace0_generated_138_finish(server, ns)) && !(retVal = function_namespace0_generated_137_finish(server, ns)) && !(retVal = function_namespace0_generated_136_finish(server, ns)) && !(retVal = function_namespace0_generated_135_finish(server, ns)) && !(retVal = function_namespace0_generated_134_finish(server, ns)) && !(retVal = function_namespace0_generated_133_finish(server, ns)) && !(retVal = function_namespace0_generated_132_finish(server, ns)) && !(retVal = function_namespace0_generated_131_finish(server, ns)) && !(retVal = function_namespace0_generated_130_finish(server, ns)) && !(retVal = function_namespace0_generated_129_finish(server, ns)) && !(retVal = function_namespace0_generated_128_finish(server, ns)) && !(retVal = function_namespace0_generated_127_finish(server, ns)) && !(retVal = function_namespace0_generated_126_finish(server, ns)) && !(retVal = function_namespace0_generated_125_finish(server, ns)) && !(retVal = function_namespace0_generated_124_finish(server, ns)) && !(retVal = function_namespace0_generated_123_finish(server, ns)) && !(retVal = function_namespace0_generated_122_finish(server, ns)) && !(retVal = function_namespace0_generated_121_finish(server, ns)) && !(retVal = function_namespace0_generated_120_finish(server, ns)) && !(retVal = function_namespace0_generated_119_finish(server, ns)) && !(retVal = function_namespace0_generated_118_finish(server, ns)) && !(retVal = function_namespace0_generated_117_finish(server, ns)) && !(retVal = function_namespace0_generated_116_finish(server, ns)) && !(retVal = function_namespace0_generated_115_finish(server, ns)) && !(retVal = function_namespace0_generated_114_finish(server, ns)) && !(retVal = function_namespace0_generated_113_finish(server, ns)) && !(retVal = function_namespace0_generated_112_finish(server, ns)) && !(retVal = function_namespace0_generated_111_finish(server, ns)) && !(retVal = function_namespace0_generated_110_finish(server, ns)) && !(retVal = function_namespace0_generated_109_finish(server, ns)) && !(retVal = function_namespace0_generated_108_finish(server, ns)) && !(retVal = function_namespace0_generated_107_finish(server, ns)) && !(retVal = function_namespace0_generated_106_finish(server, ns)) && !(retVal = function_namespace0_generated_105_finish(server, ns)) && !(retVal = function_namespace0_generated_104_finish(server, ns)) && !(retVal = function_namespace0_generated_103_finish(server, ns)) && !(retVal = function_namespace0_generated_102_finish(server, ns)) && !(retVal = function_namespace0_generated_101_finish(server, ns)) && !(retVal = function_namespace0_generated_100_finish(server, ns)) && !(retVal = function_namespace0_generated_99_finish(server, ns)) && !(retVal = function_namespace0_generated_98_finish(server, ns)) && !(retVal = function_namespace0_generated_97_finish(server, ns)) && !(retVal = function_namespace0_generated_96_finish(server, ns)) && !(retVal = function_namespace0_generated_95_finish(server, ns)) && !(retVal = function_namespace0_generated_94_finish(server, ns)) && !(retVal = function_namespace0_generated_93_finish(server, ns)) && !(retVal = function_namespace0_generated_92_finish(server, ns)) && !(retVal = function_namespace0_generated_91_finish(server, ns)) && !(retVal = function_namespace0_generated_90_finish(server, ns)) && !(retVal = function_namespace0_generated_89_finish(server, ns)) && !(retVal = function_namespace0_generated_88_finish(server, ns)) && !(retVal = function_namespace0_generated_87_finish(server, ns)) && !(retVal = function_namespace0_generated_86_finish(server, ns)) && !(retVal = function_namespace0_generated_85_finish(server, ns)) && !(retVal = function_namespace0_generated_84_finish(server, ns)) && !(retVal = function_namespace0_generated_83_finish(server, ns)) && !(retVal = function_namespace0_generated_82_finish(server, ns)) && !(retVal = function_namespace0_generated_81_finish(server, ns)) && !(retVal = function_namespace0_generated_80_finish(server, ns)) && !(retVal = function_namespace0_generated_79_finish(server, ns)) && !(retVal = function_namespace0_generated_78_finish(server, ns)) && !(retVal = function_namespace0_generated_77_finish(server, ns)) && !(retVal = function_namespace0_generated_76_finish(server, ns)) && !(retVal = function_namespace0_generated_75_finish(server, ns)) && !(retVal = function_namespace0_generated_74_finish(server, ns)) && !(retVal = function_namespace0_generated_73_finish(server, ns)) && !(retVal = function_namespace0_generated_72_finish(server, ns)) && !(retVal = function_namespace0_generated_71_finish(server, ns)) && !(retVal = function_namespace0_generated_70_finish(server, ns)) && !(retVal = function_namespace0_generated_69_finish(server, ns)) && !(retVal = function_namespace0_generated_68_finish(server, ns)) && !(retVal = function_namespace0_generated_67_finish(server, ns)) && !(retVal = function_namespace0_generated_66_finish(server, ns)) && !(retVal = function_namespace0_generated_65_finish(server, ns)) && !(retVal = function_namespace0_generated_64_finish(server, ns)) && !(retVal = function_namespace0_generated_63_finish(server, ns)) && !(retVal = function_namespace0_generated_62_finish(server, ns)) && !(retVal = function_namespace0_generated_61_finish(server, ns)) && !(retVal = function_namespace0_generated_60_finish(server, ns)) && !(retVal = function_namespace0_generated_59_finish(server, ns)) && !(retVal = function_namespace0_generated_58_finish(server, ns)) && !(retVal = function_namespace0_generated_57_finish(server, ns)) && !(retVal = function_namespace0_generated_56_finish(server, ns)) && !(retVal = function_namespace0_generated_55_finish(server, ns)) && !(retVal = function_namespace0_generated_54_finish(server, ns)) && !(retVal = function_namespace0_generated_53_finish(server, ns)) && !(retVal = function_namespace0_generated_52_finish(server, ns)) && !(retVal = function_namespace0_generated_51_finish(server, ns)) && !(retVal = function_namespace0_generated_50_finish(server, ns)) && !(retVal = function_namespace0_generated_49_finish(server, ns)) && !(retVal = function_namespace0_generated_48_finish(server, ns)) && !(retVal = function_namespace0_generated_47_finish(server, ns)) && !(retVal = function_namespace0_generated_46_finish(server, ns)) && !(retVal = function_namespace0_generated_45_finish(server, ns)) && !(retVal = function_namespace0_generated_44_finish(server, ns)) && !(retVal = function_namespace0_generated_43_finish(server, ns)) && !(retVal = function_namespace0_generated_42_finish(server, ns)) && !(retVal = function_namespace0_generated_41_finish(server, ns)) && !(retVal = function_namespace0_generated_40_finish(server, ns)) && !(retVal = function_namespace0_generated_39_finish(server, ns)) && !(retVal = function_namespace0_generated_38_finish(server, ns)) && !(retVal = function_namespace0_generated_37_finish(server, ns)) && !(retVal = function_namespace0_generated_36_finish(server, ns)) && !(retVal = function_namespace0_generated_35_finish(server, ns)) && !(retVal = function_namespace0_generated_34_finish(server, ns)) && !(retVal = function_namespace0_generated_33_finish(server, ns)) && !(retVal = function_namespace0_generated_32_finish(server, ns)) && !(retVal = function_namespace0_generated_31_finish(server, ns)) && !(retVal = function_namespace0_generated_30_finish(server, ns)) && !(retVal = function_namespace0_generated_29_finish(server, ns)) && !(retVal = function_namespace0_generated_28_finish(server, ns)) && !(retVal = function_namespace0_generated_27_finish(server, ns)) && !(retVal = function_namespace0_generated_26_finish(server, ns)) && !(retVal = function_namespace0_generated_25_finish(server, ns)) && !(retVal = function_namespace0_generated_24_finish(server, ns)) && !(retVal = function_namespace0_generated_23_finish(server, ns)) && !(retVal = function_namespace0_generated_22_finish(server, ns)) && !(retVal = function_namespace0_generated_21_finish(server, ns)) && !(retVal = function_namespace0_generated_20_finish(server, ns)) && !(retVal = function_namespace0_generated_19_finish(server, ns)) && !(retVal = function_namespace0_generated_18_finish(server, ns)) && !(retVal = function_namespace0_generated_17_finish(server, ns)) && !(retVal = function_namespace0_generated_16_finish(server, ns)) && !(retVal = function_namespace0_generated_15_finish(server, ns)) && !(retVal = function_namespace0_generated_14_finish(server, ns)) && !(retVal = function_namespace0_generated_13_finish(server, ns)) && !(retVal = function_namespace0_generated_12_finish(server, ns)) && !(retVal = function_namespace0_generated_11_finish(server, ns)) && !(retVal = function_namespace0_generated_10_finish(server, ns)) && !(retVal = function_namespace0_generated_9_finish(server, ns)) && !(retVal = function_namespace0_generated_8_finish(server, ns)) && !(retVal = function_namespace0_generated_7_finish(server, ns)) && !(retVal = function_namespace0_generated_6_finish(server, ns)) && !(retVal = function_namespace0_generated_5_finish(server, ns)) && !(retVal = function_namespace0_generated_4_finish(server, ns)) && !(retVal = function_namespace0_generated_3_finish(server, ns)) && !(retVal = function_namespace0_generated_2_finish(server, ns)) && !(retVal = function_namespace0_generated_1_finish(server, ns)) && !(retVal = function_namespace0_generated_0_finish(server, ns)) ); (void)(dummy); return retVal; } /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_discovery_manager.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2014-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2014, 2017 (c) Florian Palm * Copyright 2015-2016, 2019 (c) Sten Grüner * Copyright 2015 (c) Chris Iatrou * Copyright 2015-2016 (c) Oleksiy Vasylyev * Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2017 (c) Julian Grothoff */ #ifdef UA_ENABLE_DISCOVERY #ifdef UA_ENABLE_DISCOVERY_MULTICAST #ifndef IN_ZERONET #define IN_ZERONET(addr) ((addr & IN_CLASSA_NET) == 0) #endif /* Create multicast 224.0.0.251:5353 socket */ static UA_SOCKET discovery_createMulticastSocket(UA_Server* server) { UA_SOCKET s; int flag = 1, ittl = 255; struct sockaddr_in in; struct ip_mreq mc; char ttl = (char)255; // publish to complete net, not only subnet. See: // https://docs.oracle.com/cd/E23824_01/html/821-1602/sockets-137.html memset(&in, 0, sizeof(in)); in.sin_family = AF_INET; in.sin_port = htons(5353); in.sin_addr.s_addr = 0; if((s = UA_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == UA_INVALID_SOCKET) return UA_INVALID_SOCKET; #ifdef SO_REUSEPORT UA_setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (char *)&flag, sizeof(flag)); #endif UA_setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag)); if(UA_bind(s, (struct sockaddr *)&in, sizeof(in))) { UA_close(s); return UA_INVALID_SOCKET; } /* Custom outbound multicast interface */ size_t length = server->config.discovery.mdnsInterfaceIP.length; if(length > 0){ char* interfaceName = (char*)UA_malloc(length+1); if (!interfaceName) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_NETWORK, "Multicast DNS: cannot alloc memory for iface name"); return 0; } struct in_addr ina; memset(&ina, 0, sizeof(ina)); memcpy(interfaceName, server->config.discovery.mdnsInterfaceIP.data, length); interfaceName[length] = '\0'; inet_pton(AF_INET, interfaceName, &ina); UA_free(interfaceName); /* Set interface for outbound multicast */ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char*)&ina, sizeof(ina)) < 0) UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Multicast DNS: failed setting IP_MULTICAST_IF to %s: %s", inet_ntoa(ina), strerror(errno)); } /* Check outbound multicast interface parameters */ struct in_addr interface_addr; socklen_t addr_size = sizeof(struct in_addr); if (getsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char*)&interface_addr, &addr_size) < 0) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_NETWORK, "Multicast DNS: getsockopt(IP_MULTICAST_IF) failed"); } if(IN_ZERONET(ntohl(interface_addr.s_addr))){ UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_NETWORK, "Multicast DNS: outbound interface 0.0.0.0, it means that the first OS interface is used (you can explicitly set the interface by using 'discovery.mdnsInterfaceIP' config parameter)"); }else{ char buf[16]; inet_ntop(AF_INET, &interface_addr, buf, 16); UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_NETWORK, "Multicast DNS: outbound interface is %s", buf); } mc.imr_multiaddr.s_addr = inet_addr("224.0.0.251"); mc.imr_interface.s_addr = htonl(INADDR_ANY); UA_setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mc, sizeof(mc)); UA_setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ttl, sizeof(ttl)); UA_setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ittl, sizeof(ittl)); UA_socket_set_nonblocking(s); //TODO: check return value return s; } static UA_StatusCode initMulticastDiscoveryServer(UA_DiscoveryManager *dm, UA_Server* server) { server->discoveryManager.mdnsDaemon = mdnsd_new(QCLASS_IN, 1000); UA_initialize_architecture_network(); if((server->discoveryManager.mdnsSocket = discovery_createMulticastSocket(server)) == UA_INVALID_SOCKET) { UA_LOG_SOCKET_ERRNO_WRAP( UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Could not create multicast socket. Error: %s", errno_str)); return UA_STATUSCODE_BADUNEXPECTEDERROR; } mdnsd_register_receive_callback(server->discoveryManager.mdnsDaemon, mdns_record_received, server); return UA_STATUSCODE_GOOD; } static void destroyMulticastDiscoveryServer(UA_DiscoveryManager *dm) { if (!dm->mdnsDaemon) return; mdnsd_shutdown(dm->mdnsDaemon); mdnsd_free(dm->mdnsDaemon); if(dm->mdnsSocket != UA_INVALID_SOCKET) { UA_close(dm->mdnsSocket); dm->mdnsSocket = UA_INVALID_SOCKET; } } #endif /* UA_ENABLE_DISCOVERY_MULTICAST */ void UA_DiscoveryManager_init(UA_DiscoveryManager *dm, UA_Server *server) { LIST_INIT(&dm->registeredServers); dm->registeredServersSize = 0; LIST_INIT(&dm->periodicServerRegisterCallbacks); dm->registerServerCallback = NULL; dm->registerServerCallbackData = NULL; #ifdef UA_ENABLE_DISCOVERY_MULTICAST dm->mdnsDaemon = NULL; dm->mdnsSocket = UA_INVALID_SOCKET; dm->mdnsMainSrvAdded = false; if(server->config.discovery.mdnsEnable) initMulticastDiscoveryServer(dm, server); LIST_INIT(&dm->serverOnNetwork); dm->serverOnNetworkSize = 0; dm->serverOnNetworkRecordIdCounter = 0; dm->serverOnNetworkRecordIdLastReset = UA_DateTime_now(); memset(dm->serverOnNetworkHash, 0, sizeof(struct serverOnNetwork_hash_entry*) * SERVER_ON_NETWORK_HASH_PRIME); dm->serverOnNetworkCallback = NULL; dm->serverOnNetworkCallbackData = NULL; #endif /* UA_ENABLE_DISCOVERY_MULTICAST */ } void UA_DiscoveryManager_deleteMembers(UA_DiscoveryManager *dm, UA_Server *server) { registeredServer_list_entry *rs, *rs_tmp; LIST_FOREACH_SAFE(rs, &dm->registeredServers, pointers, rs_tmp) { LIST_REMOVE(rs, pointers); UA_RegisteredServer_deleteMembers(&rs->registeredServer); UA_free(rs); } periodicServerRegisterCallback_entry *ps, *ps_tmp; LIST_FOREACH_SAFE(ps, &dm->periodicServerRegisterCallbacks, pointers, ps_tmp) { LIST_REMOVE(ps, pointers); if (ps->callback->discovery_server_url) UA_free(ps->callback->discovery_server_url); UA_free(ps->callback); UA_free(ps); } # ifdef UA_ENABLE_DISCOVERY_MULTICAST if(server->config.discovery.mdnsEnable) destroyMulticastDiscoveryServer(dm); serverOnNetwork_list_entry *son, *son_tmp; LIST_FOREACH_SAFE(son, &dm->serverOnNetwork, pointers, son_tmp) { LIST_REMOVE(son, pointers); UA_ServerOnNetwork_deleteMembers(&son->serverOnNetwork); if(son->pathTmp) UA_free(son->pathTmp); UA_free(son); } for(size_t i = 0; i < SERVER_ON_NETWORK_HASH_PRIME; i++) { serverOnNetwork_hash_entry* currHash = dm->serverOnNetworkHash[i]; while(currHash) { serverOnNetwork_hash_entry* nextHash = currHash->next; UA_free(currHash); currHash = nextHash; } } # endif /* UA_ENABLE_DISCOVERY_MULTICAST */ } #endif /* UA_ENABLE_DISCOVERY */ /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_subscription.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2015-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2015 (c) Chris Iatrou * Copyright 2015-2016 (c) Sten Grüner * Copyright 2017-2018 (c) Thomas Stalder, Blue Time Concept SA * Copyright 2015 (c) Joakim L. Gilje * Copyright 2016-2017 (c) Florian Palm * Copyright 2015-2016 (c) Oleksiy Vasylyev * Copyright 2017 (c) frax2222 * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2017 (c) Ari Breitkreuz, fortiss GmbH * Copyright 2017 (c) Mattias Bornhager * Copyright 2018 (c) Hilscher Gesellschaft für Systemautomation mbH (Author: Martin Lang) */ #ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */ UA_Subscription * UA_Subscription_new(UA_Session *session, UA_UInt32 subscriptionId) { /* Allocate the memory */ UA_Subscription *newSub = (UA_Subscription*)UA_calloc(1, sizeof(UA_Subscription)); if(!newSub) return NULL; /* Remaining members are covered by calloc zeroing out the memory */ newSub->session = session; newSub->subscriptionId = subscriptionId; newSub->state = UA_SUBSCRIPTIONSTATE_NORMAL; /* The first publish response is sent immediately */ /* Even if the first publish response is a keepalive the sequence number is 1. * This can happen by a subscription without a monitored item (see CTT test scripts). */ newSub->nextSequenceNumber = 1; TAILQ_INIT(&newSub->retransmissionQueue); TAILQ_INIT(&newSub->notificationQueue); return newSub; } void UA_Subscription_deleteMembers(UA_Server *server, UA_Subscription *sub) { Subscription_unregisterPublishCallback(server, sub); /* Delete monitored Items */ UA_MonitoredItem *mon, *tmp_mon; LIST_FOREACH_SAFE(mon, &sub->monitoredItems, listEntry, tmp_mon) { LIST_REMOVE(mon, listEntry); UA_LOG_INFO_SESSION(&server->config.logger, sub->session, "Subscription %u | MonitoredItem %i | " "Deleted the MonitoredItem", sub->subscriptionId, mon->monitoredItemId); UA_MonitoredItem_delete(server, mon); } UA_assert(server->numMonitoredItems >= sub->monitoredItemsSize); server->numMonitoredItems -= sub->monitoredItemsSize; sub->monitoredItemsSize = 0; /* Delete Retransmission Queue */ UA_NotificationMessageEntry *nme, *nme_tmp; TAILQ_FOREACH_SAFE(nme, &sub->retransmissionQueue, listEntry, nme_tmp) { TAILQ_REMOVE(&sub->retransmissionQueue, nme, listEntry); UA_NotificationMessage_deleteMembers(&nme->message); UA_free(nme); --sub->session->totalRetransmissionQueueSize; --sub->retransmissionQueueSize; } UA_assert(sub->retransmissionQueueSize == 0); UA_LOG_INFO_SESSION(&server->config.logger, sub->session, "Subscription %u | Deleted the Subscription", sub->subscriptionId); } UA_MonitoredItem * UA_Subscription_getMonitoredItem(UA_Subscription *sub, UA_UInt32 monitoredItemId) { UA_MonitoredItem *mon; LIST_FOREACH(mon, &sub->monitoredItems, listEntry) { if(mon->monitoredItemId == monitoredItemId) break; } return mon; } UA_StatusCode UA_Subscription_deleteMonitoredItem(UA_Server *server, UA_Subscription *sub, UA_UInt32 monitoredItemId) { /* Find the MonitoredItem */ UA_MonitoredItem *mon; LIST_FOREACH(mon, &sub->monitoredItems, listEntry) { if(mon->monitoredItemId == monitoredItemId) break; } if(!mon) return UA_STATUSCODE_BADMONITOREDITEMIDINVALID; UA_LOG_INFO_SESSION(&server->config.logger, sub->session, "Subscription %u | MonitoredItem %i | " "Delete the MonitoredItem", sub->subscriptionId, mon->monitoredItemId); /* Remove the MonitoredItem */ LIST_REMOVE(mon, listEntry); UA_assert(sub->monitoredItemsSize > 0); UA_assert(server->numMonitoredItems > 0); sub->monitoredItemsSize--; server->numMonitoredItems--; /* Remove content and delayed free */ UA_MonitoredItem_delete(server, mon); return UA_STATUSCODE_GOOD; } void UA_Subscription_addMonitoredItem(UA_Server *server, UA_Subscription *sub, UA_MonitoredItem *newMon) { sub->monitoredItemsSize++; server->numMonitoredItems++; LIST_INSERT_HEAD(&sub->monitoredItems, newMon, listEntry); } static void removeOldestRetransmissionMessage(UA_Session *session) { UA_NotificationMessageEntry *oldestEntry = NULL; UA_Subscription *oldestSub = NULL; UA_Subscription *sub; LIST_FOREACH(sub, &session->serverSubscriptions, listEntry) { UA_NotificationMessageEntry *first = TAILQ_LAST(&sub->retransmissionQueue, ListOfNotificationMessages); if(!first) continue; if(!oldestEntry || oldestEntry->message.publishTime > first->message.publishTime) { oldestEntry = first; oldestSub = sub; } } UA_assert(oldestEntry); UA_assert(oldestSub); TAILQ_REMOVE(&oldestSub->retransmissionQueue, oldestEntry, listEntry); UA_NotificationMessage_deleteMembers(&oldestEntry->message); UA_free(oldestEntry); --session->totalRetransmissionQueueSize; --oldestSub->retransmissionQueueSize; } static void UA_Subscription_addRetransmissionMessage(UA_Server *server, UA_Subscription *sub, UA_NotificationMessageEntry *entry) { /* Release the oldest entry if there is not enough space */ if(server->config.maxRetransmissionQueueSize > 0 && sub->session->totalRetransmissionQueueSize >= server->config.maxRetransmissionQueueSize) { UA_LOG_WARNING_SESSION(&server->config.logger, sub->session, "Subscription %u | " "Retransmission queue overflow", sub->subscriptionId); removeOldestRetransmissionMessage(sub->session); } /* Add entry */ TAILQ_INSERT_TAIL(&sub->retransmissionQueue, entry, listEntry); ++sub->session->totalRetransmissionQueueSize; ++sub->retransmissionQueueSize; } UA_StatusCode UA_Subscription_removeRetransmissionMessage(UA_Subscription *sub, UA_UInt32 sequenceNumber) { /* Find the retransmission message */ UA_NotificationMessageEntry *entry; TAILQ_FOREACH(entry, &sub->retransmissionQueue, listEntry) { if(entry->message.sequenceNumber == sequenceNumber) break; } if(!entry) return UA_STATUSCODE_BADSEQUENCENUMBERUNKNOWN; /* Remove the retransmission message */ TAILQ_REMOVE(&sub->retransmissionQueue, entry, listEntry); --sub->session->totalRetransmissionQueueSize; --sub->retransmissionQueueSize; UA_NotificationMessage_deleteMembers(&entry->message); UA_free(entry); return UA_STATUSCODE_GOOD; } static UA_StatusCode prepareNotificationMessage(UA_Server *server, UA_Subscription *sub, UA_NotificationMessage *message, size_t notifications) { UA_assert(notifications > 0); /* Allocate an ExtensionObject for events and data */ message->notificationData = (UA_ExtensionObject*) UA_Array_new(2, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]); if(!message->notificationData) return UA_STATUSCODE_BADOUTOFMEMORY; message->notificationDataSize = 2; /* Pre-allocate DataChangeNotifications */ size_t notificationDataIdx = 0; UA_DataChangeNotification *dcn = NULL; if(sub->dataChangeNotifications > 0) { dcn = UA_DataChangeNotification_new(); if(!dcn) { UA_NotificationMessage_deleteMembers(message); return UA_STATUSCODE_BADOUTOFMEMORY; } message->notificationData->encoding = UA_EXTENSIONOBJECT_DECODED; message->notificationData->content.decoded.data = dcn; message->notificationData->content.decoded.type = &UA_TYPES[UA_TYPES_DATACHANGENOTIFICATION]; size_t dcnSize = sub->dataChangeNotifications; if(dcnSize > notifications) dcnSize = notifications; dcn->monitoredItems = (UA_MonitoredItemNotification*) UA_Array_new(dcnSize, &UA_TYPES[UA_TYPES_MONITOREDITEMNOTIFICATION]); if(!dcn->monitoredItems) { UA_NotificationMessage_deleteMembers(message); return UA_STATUSCODE_BADOUTOFMEMORY; } dcn->monitoredItemsSize = dcnSize; notificationDataIdx++; } #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS UA_EventNotificationList *enl = NULL; UA_StatusChangeNotification *scn = NULL; /* Pre-allocate either StatusChange or EventNotifications. Sending a * (single) StatusChangeNotification has priority. */ if(sub->statusChangeNotifications > 0) { scn = UA_StatusChangeNotification_new(); if(!scn) { UA_NotificationMessage_deleteMembers(message); return UA_STATUSCODE_BADOUTOFMEMORY; } message->notificationData[notificationDataIdx].encoding = UA_EXTENSIONOBJECT_DECODED; message->notificationData[notificationDataIdx].content.decoded.data = scn; message->notificationData[notificationDataIdx].content.decoded.type = &UA_TYPES[UA_TYPES_STATUSCHANGENOTIFICATION]; notificationDataIdx++; } else if(sub->eventNotifications > 0) { enl = UA_EventNotificationList_new(); if(!enl) { UA_NotificationMessage_deleteMembers(message); return UA_STATUSCODE_BADOUTOFMEMORY; } message->notificationData[notificationDataIdx].encoding = UA_EXTENSIONOBJECT_DECODED; message->notificationData[notificationDataIdx].content.decoded.data = enl; message->notificationData[notificationDataIdx].content.decoded.type = &UA_TYPES[UA_TYPES_EVENTNOTIFICATIONLIST]; size_t enlSize = sub->eventNotifications; if(enlSize > notifications) enlSize = notifications; enl->events = (UA_EventFieldList*) UA_Array_new(enlSize, &UA_TYPES[UA_TYPES_EVENTFIELDLIST]); if(!enl->events) { UA_NotificationMessage_deleteMembers(message); return UA_STATUSCODE_BADOUTOFMEMORY; } enl->eventsSize = enlSize; notificationDataIdx++; } #endif UA_assert(notificationDataIdx > 0); message->notificationDataSize = notificationDataIdx; /* <-- The point of no return --> */ size_t totalNotifications = 0; /* How many notifications were moved to the response overall? */ size_t dcnPos = 0; /* How many DataChangeNotifications were put into the list? */ #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS size_t enlPos = 0; /* How many EventNotifications were moved into the list */ #endif UA_Notification *notification, *notification_tmp; TAILQ_FOREACH_SAFE(notification, &sub->notificationQueue, globalEntry, notification_tmp) { if(totalNotifications >= notifications) break; UA_MonitoredItem *mon = notification->mon; /* Remove from the queues and decrease the counters */ UA_Notification_dequeue(server, notification); /* Move the content to the response */ #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) { UA_assert(enl != NULL); /* Have at least one event notification */ /* Move the content to the response */ UA_EventFieldList *efl = &enl->events[enlPos]; *efl = notification->data.event.fields; UA_EventFieldList_init(¬ification->data.event.fields); efl->clientHandle = mon->clientHandle; enlPos++; } else #endif { UA_assert(dcn != NULL); /* Have at least one change notification */ /* Move the content to the response */ UA_MonitoredItemNotification *min = &dcn->monitoredItems[dcnPos]; min->clientHandle = mon->clientHandle; min->value = notification->data.value; UA_DataValue_init(¬ification->data.value); /* Reset after the value has been moved */ dcnPos++; } UA_Notification_delete(notification); totalNotifications++; } /* Set sizes */ if(dcn) { dcn->monitoredItemsSize = dcnPos; if(dcnPos == 0) { UA_free(dcn->monitoredItems); dcn->monitoredItems = NULL; } } #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS if(enl) { enl->eventsSize = enlPos; if(enlPos == 0) { UA_free(enl->events); enl->events = NULL; } } #endif return UA_STATUSCODE_GOOD; } /* According to OPC Unified Architecture, Part 4 5.13.1.1 i) The value 0 is * never used for the sequence number */ static UA_UInt32 UA_Subscription_nextSequenceNumber(UA_UInt32 sequenceNumber) { UA_UInt32 nextSequenceNumber = sequenceNumber + 1; if(nextSequenceNumber == 0) nextSequenceNumber = 1; return nextSequenceNumber; } static void publishCallback(UA_Server *server, UA_Subscription *sub) { sub->readyNotifications = sub->notificationQueueSize; UA_Subscription_publish(server, sub); } void UA_Subscription_publish(UA_Server *server, UA_Subscription *sub) { UA_LOG_DEBUG_SESSION(&server->config.logger, sub->session, "Subscription %u | " "Publish Callback", sub->subscriptionId); /* Dequeue a response */ UA_PublishResponseEntry *pre = UA_Session_dequeuePublishReq(sub->session); if(pre) { sub->currentLifetimeCount = 0; /* Reset the LifetimeCounter */ } else { UA_LOG_DEBUG_SESSION(&server->config.logger, sub->session, "Subscription %u | The publish queue is empty", sub->subscriptionId); ++sub->currentLifetimeCount; if(sub->currentLifetimeCount > sub->lifeTimeCount) { UA_LOG_DEBUG_SESSION(&server->config.logger, sub->session, "Subscription %u | End of lifetime " "for subscription", sub->subscriptionId); UA_Session_deleteSubscription(server, sub->session, sub->subscriptionId); /* TODO: send a StatusChangeNotification with Bad_Timeout */ return; } } /* If there are several late publish responses... */ if(sub->readyNotifications > sub->notificationQueueSize) sub->readyNotifications = sub->notificationQueueSize; /* Count the available notifications */ UA_UInt32 notifications = sub->readyNotifications; if(!sub->publishingEnabled) notifications = 0; UA_Boolean moreNotifications = false; if(notifications > sub->notificationsPerPublish) { notifications = sub->notificationsPerPublish; moreNotifications = true; } /* Return if no notifications and no keepalive */ if(notifications == 0) { ++sub->currentKeepAliveCount; if(sub->currentKeepAliveCount < sub->maxKeepAliveCount) { if(pre) UA_Session_queuePublishReq(sub->session, pre, true); /* Re-enqueue */ return; } UA_LOG_DEBUG_SESSION(&server->config.logger, sub->session, "Subscription %u | Sending a KeepAlive", sub->subscriptionId); } /* We want to send a response. Is the channel open? */ UA_SecureChannel *channel = sub->session->header.channel; if(!channel || !pre) { UA_LOG_DEBUG_SESSION(&server->config.logger, sub->session, "Subscription %u | Want to send a publish response but can't. " "The subscription is late.", sub->subscriptionId); sub->state = UA_SUBSCRIPTIONSTATE_LATE; if(pre) UA_Session_queuePublishReq(sub->session, pre, true); /* Re-enqueue */ return; } /* Prepare the response */ UA_PublishResponse *response = &pre->response; UA_NotificationMessage *message = &response->notificationMessage; UA_NotificationMessageEntry *retransmission = NULL; if(notifications > 0) { if(server->config.enableRetransmissionQueue) { /* Allocate the retransmission entry */ retransmission = (UA_NotificationMessageEntry*)UA_malloc(sizeof(UA_NotificationMessageEntry)); if(!retransmission) { UA_LOG_WARNING_SESSION(&server->config.logger, sub->session, "Subscription %u | Could not allocate memory for retransmission. " "The subscription is late.", sub->subscriptionId); sub->state = UA_SUBSCRIPTIONSTATE_LATE; UA_Session_queuePublishReq(sub->session, pre, true); /* Re-enqueue */ return; } } /* Prepare the response */ UA_StatusCode retval = prepareNotificationMessage(server, sub, message, notifications); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING_SESSION(&server->config.logger, sub->session, "Subscription %u | Could not prepare the notification message. " "The subscription is late.", sub->subscriptionId); /* If the retransmission queue is enabled a retransmission message is allocated */ if(retransmission) UA_free(retransmission); sub->state = UA_SUBSCRIPTIONSTATE_LATE; UA_Session_queuePublishReq(sub->session, pre, true); /* Re-enqueue */ return; } } /* <-- The point of no return --> */ /* Adjust the number of ready notifications */ UA_assert(sub->readyNotifications >= notifications); sub->readyNotifications -= notifications; /* Set up the response */ response->responseHeader.timestamp = UA_DateTime_now(); response->subscriptionId = sub->subscriptionId; response->moreNotifications = moreNotifications; message->publishTime = response->responseHeader.timestamp; /* Set sequence number to message. Started at 1 which is given * during creating a new subscription. The 1 is required for * initial publish response with or without an monitored item. */ message->sequenceNumber = sub->nextSequenceNumber; if(notifications > 0) { /* If the retransmission queue is enabled a retransmission message is allocated */ if(retransmission) { /* Put the notification message into the retransmission queue. This * needs to be done here, so that the message itself is included in the * available sequence numbers for acknowledgement. */ retransmission->message = response->notificationMessage; UA_Subscription_addRetransmissionMessage(server, sub, retransmission); } /* Only if a notification was created, the sequence number must be increased. * For a keepalive the sequence number can be reused. */ sub->nextSequenceNumber = UA_Subscription_nextSequenceNumber(sub->nextSequenceNumber); } /* Get the available sequence numbers from the retransmission queue */ size_t available = sub->retransmissionQueueSize; UA_STACKARRAY(UA_UInt32, seqNumbers, available); if(available > 0) { response->availableSequenceNumbers = seqNumbers; response->availableSequenceNumbersSize = available; size_t i = 0; UA_NotificationMessageEntry *nme; TAILQ_FOREACH(nme, &sub->retransmissionQueue, listEntry) { response->availableSequenceNumbers[i] = nme->message.sequenceNumber; ++i; } } /* Send the response */ UA_LOG_DEBUG_SESSION(&server->config.logger, sub->session, "Subscription %u | Sending out a publish response " "with %u notifications", sub->subscriptionId, (UA_UInt32)notifications); UA_SecureChannel_sendSymmetricMessage(sub->session->header.channel, pre->requestId, UA_MESSAGETYPE_MSG, response, &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]); /* Reset subscription state to normal */ sub->state = UA_SUBSCRIPTIONSTATE_NORMAL; sub->currentKeepAliveCount = 0; /* Free the response */ UA_Array_delete(response->results, response->resultsSize, &UA_TYPES[UA_TYPES_UINT32]); UA_free(pre); /* No need for UA_PublishResponse_deleteMembers */ /* Repeat sending responses if there are more notifications to send */ if(moreNotifications) UA_Subscription_publish(server, sub); } UA_Boolean UA_Subscription_reachedPublishReqLimit(UA_Server *server, UA_Session *session) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Reached number of publish request limit"); /* Dequeue a response */ UA_PublishResponseEntry *pre = UA_Session_dequeuePublishReq(session); /* Cannot publish without a response */ if(!pre) { UA_LOG_FATAL_SESSION(&server->config.logger, session, "No publish requests available"); return false; } /* <-- The point of no return --> */ UA_PublishResponse *response = &pre->response; UA_NotificationMessage *message = &response->notificationMessage; /* Set up the response. Note that this response has no related subscription id */ response->responseHeader.timestamp = UA_DateTime_now(); response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYPUBLISHREQUESTS; response->subscriptionId = 0; response->moreNotifications = false; message->publishTime = response->responseHeader.timestamp; message->sequenceNumber = 0; response->availableSequenceNumbersSize = 0; /* Send the response */ UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Sending out a publish response triggered by too many publish requests"); UA_SecureChannel_sendSymmetricMessage(session->header.channel, pre->requestId, UA_MESSAGETYPE_MSG, response, &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]); /* Free the response */ UA_Array_delete(response->results, response->resultsSize, &UA_TYPES[UA_TYPES_UINT32]); UA_free(pre); /* no need for UA_PublishResponse_deleteMembers */ return true; } UA_StatusCode Subscription_registerPublishCallback(UA_Server *server, UA_Subscription *sub) { UA_LOG_DEBUG_SESSION(&server->config.logger, sub->session, "Subscription %u | Register subscription " "publishing callback", sub->subscriptionId); if(sub->publishCallbackIsRegistered) return UA_STATUSCODE_GOOD; UA_StatusCode retval = UA_Server_addRepeatedCallback(server, (UA_ServerCallback)publishCallback, sub, (UA_UInt32)sub->publishingInterval, &sub->publishCallbackId); if(retval != UA_STATUSCODE_GOOD) return retval; sub->publishCallbackIsRegistered = true; return UA_STATUSCODE_GOOD; } void Subscription_unregisterPublishCallback(UA_Server *server, UA_Subscription *sub) { UA_LOG_DEBUG_SESSION(&server->config.logger, sub->session, "Subscription %u | " "Unregister subscription publishing callback", sub->subscriptionId); if(!sub->publishCallbackIsRegistered) return; UA_Server_removeRepeatedCallback(server, sub->publishCallbackId); sub->publishCallbackIsRegistered = false; } /* When the session has publish requests stored but the last subscription is * deleted... Send out empty responses */ void UA_Subscription_answerPublishRequestsNoSubscription(UA_Server *server, UA_Session *session) { /* No session or there are remaining subscriptions */ if(!session || LIST_FIRST(&session->serverSubscriptions)) return; /* Send a response for every queued request */ UA_PublishResponseEntry *pre; while((pre = UA_Session_dequeuePublishReq(session))) { UA_PublishResponse *response = &pre->response; response->responseHeader.serviceResult = UA_STATUSCODE_BADNOSUBSCRIPTION; response->responseHeader.timestamp = UA_DateTime_now(); UA_SecureChannel_sendSymmetricMessage(session->header.channel, pre->requestId, UA_MESSAGETYPE_MSG, response, &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]); UA_PublishResponse_deleteMembers(response); UA_free(pre); } } #endif /* UA_ENABLE_SUBSCRIPTIONS */ /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_subscription_monitoreditem.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2017-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2018 (c) Ari Breitkreuz, fortiss GmbH * Copyright 2018 (c) Thomas Stalder, Blue Time Concept SA * Copyright 2018 (c) Fabian Arndt, Root-Core */ #ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */ /****************/ /* Notification */ /****************/ #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS static const UA_NodeId overflowEventType = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_EVENTQUEUEOVERFLOWEVENTTYPE}}; static const UA_NodeId simpleOverflowEventType = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_SIMPLEOVERFLOWEVENTTYPE}}; static UA_Boolean UA_Notification_isOverflowEvent(UA_Server *server, UA_Notification *n) { UA_MonitoredItem *mon = n->mon; if(mon->attributeId != UA_ATTRIBUTEID_EVENTNOTIFIER) return false; UA_EventFieldList *efl = &n->data.event.fields; if(efl->eventFieldsSize >= 1 && efl->eventFields[0].type == &UA_TYPES[UA_TYPES_NODEID] && isNodeInTree(server->nsCtx, (const UA_NodeId *)efl->eventFields[0].data, &overflowEventType, &subtypeId, 1)) { return true; } return false; } /* The specification states in Part 4 5.12.1.5 that an EventQueueOverflowEvent * "is generated when the first Event has to be discarded [...] without * discarding any other event". So only generate one for all deleted events. */ static UA_StatusCode createEventOverflowNotification(UA_Server *server, UA_Subscription *sub, UA_MonitoredItem *mon, UA_Notification *indicator) { /* Avoid two redundant overflow events in a row */ if(UA_Notification_isOverflowEvent(server, indicator)) return UA_STATUSCODE_GOOD; /* A notification is inserted into the queue which includes only the * NodeId of the overflowEventType. It is up to the client to check for * possible overflows. */ /* Allocate the notification */ UA_Notification *overflowNotification = (UA_Notification *) UA_malloc(sizeof(UA_Notification)); if(!overflowNotification) return UA_STATUSCODE_BADOUTOFMEMORY;; /* Set the notification fields */ overflowNotification->mon = mon; UA_EventFieldList_init(&overflowNotification->data.event.fields); overflowNotification->data.event.fields.eventFields = UA_Variant_new(); if(!overflowNotification->data.event.fields.eventFields) { UA_free(overflowNotification); return UA_STATUSCODE_BADOUTOFMEMORY;; } overflowNotification->data.event.fields.eventFieldsSize = 1; UA_StatusCode retval = UA_Variant_setScalarCopy(overflowNotification->data.event.fields.eventFields, &simpleOverflowEventType, &UA_TYPES[UA_TYPES_NODEID]); if(retval != UA_STATUSCODE_GOOD) { UA_EventFieldList_deleteMembers(&overflowNotification->data.event.fields); UA_free(overflowNotification); return retval; } /* Insert before the "indicator notification". This is either first in the * queue (if the oldest notification was removed) or before the new event * that remains the last element of the queue. */ TAILQ_INSERT_BEFORE(indicator, overflowNotification, listEntry); ++mon->eventOverflows; ++mon->queueSize; TAILQ_NEXT(overflowNotification, globalEntry) = UA_SUBSCRIPTION_QUEUE_SENTINEL; if(mon->monitoringMode == UA_MONITORINGMODE_REPORTING) { TAILQ_INSERT_BEFORE(indicator, overflowNotification, globalEntry); ++sub->notificationQueueSize; ++sub->eventNotifications; } return UA_STATUSCODE_GOOD; } #endif /* !!! The enqueue and dequeue operations need to match the reporting * disable/enable logic in Operation_SetMonitoringMode !!! */ void UA_Notification_enqueue(UA_Server *server, UA_Subscription *sub, UA_MonitoredItem *mon, UA_Notification *n) { /* Add to the MonitoredItem */ TAILQ_INSERT_TAIL(&mon->queue, n, listEntry); ++mon->queueSize; #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER && UA_Notification_isOverflowEvent(server, n)) ++mon->eventOverflows; #endif /* Add to the subscription if reporting is enabled */ TAILQ_NEXT(n, globalEntry) = UA_SUBSCRIPTION_QUEUE_SENTINEL; if(mon->monitoringMode == UA_MONITORINGMODE_REPORTING) { TAILQ_INSERT_TAIL(&sub->notificationQueue, n, globalEntry); ++sub->notificationQueueSize; #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) { ++sub->eventNotifications; } else #endif { ++sub->dataChangeNotifications; } } /* Ensure enough space is available in the MonitoredItem. Do this only after * adding the new Notification. */ UA_MonitoredItem_ensureQueueSpace(server, mon); } void UA_Notification_dequeue(UA_Server *server, UA_Notification *n) { UA_MonitoredItem *mon = n->mon; UA_Subscription *sub = mon->subscription; /* Remove from the MonitoredItem queue */ #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER && UA_Notification_isOverflowEvent(server, n)) --mon->eventOverflows; #endif TAILQ_REMOVE(&mon->queue, n, listEntry); --mon->queueSize; /* Remove from the subscription's queue */ if(TAILQ_NEXT(n, globalEntry) != UA_SUBSCRIPTION_QUEUE_SENTINEL) { #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) { --sub->eventNotifications; } else #endif { --sub->dataChangeNotifications; } TAILQ_REMOVE(&sub->notificationQueue, n, globalEntry); --sub->notificationQueueSize; } } void UA_Notification_delete(UA_Notification *n) { #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS UA_MonitoredItem *mon = n->mon; if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) { UA_EventFieldList_deleteMembers(&n->data.event.fields); /* EventFilterResult currently isn't being used * UA_EventFilterResult_delete(notification->data.event->result); */ } else #endif { UA_DataValue_deleteMembers(&n->data.value); } UA_free(n); } /*****************/ /* MonitoredItem */ /*****************/ void UA_MonitoredItem_init(UA_MonitoredItem *mon, UA_Subscription *sub) { memset(mon, 0, sizeof(UA_MonitoredItem)); mon->subscription = sub; TAILQ_INIT(&mon->queue); } void UA_MonitoredItem_delete(UA_Server *server, UA_MonitoredItem *monitoredItem) { /* Remove the sampling callback */ UA_MonitoredItem_unregisterSampleCallback(server, monitoredItem); /* Remove the queued notifications if attached to a subscription (not a * local MonitoredItem) */ if(monitoredItem->subscription) { UA_Notification *notification, *notification_tmp; TAILQ_FOREACH_SAFE(notification, &monitoredItem->queue, listEntry, notification_tmp) { /* Remove the item from the queues and free the memory */ UA_Notification_dequeue(server, notification); UA_Notification_delete(notification); } } #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS if(monitoredItem->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) { /* Remove the monitored item from the node queue */ UA_Server_editNode(server, NULL, &monitoredItem->monitoredNodeId, UA_MonitoredItem_removeNodeEventCallback, monitoredItem); UA_EventFilter_clear(&monitoredItem->filter.eventFilter); } else #endif { /* UA_DataChangeFilter does not hold dynamic content we need to free */ /* UA_DataChangeFilter_clear(&monitoredItem->filter.dataChangeFilter); */ } /* Deregister MonitoredItem in userland */ if(server->config.monitoredItemRegisterCallback && monitoredItem->registered) { /* Get the session context. Local MonitoredItems don't have a subscription. */ UA_Session *session = NULL; if(monitoredItem->subscription) session = monitoredItem->subscription->session; if(!session) session = &server->adminSession; /* Get the node context */ void *targetContext = NULL; UA_Server_getNodeContext(server, monitoredItem->monitoredNodeId, &targetContext); /* Deregister */ server->config.monitoredItemRegisterCallback(server, &session->sessionId, session->sessionHandle, &monitoredItem->monitoredNodeId, targetContext, monitoredItem->attributeId, true); } /* Remove the monitored item */ if(monitoredItem->listEntry.le_prev != NULL) LIST_REMOVE(monitoredItem, listEntry); UA_String_deleteMembers(&monitoredItem->indexRange); UA_ByteString_deleteMembers(&monitoredItem->lastSampledValue); UA_Variant_deleteMembers(&monitoredItem->lastValue); UA_NodeId_deleteMembers(&monitoredItem->monitoredNodeId); /* No actual callback, just remove the structure */ monitoredItem->delayedFreePointers.callback = NULL; UA_WorkQueue_enqueueDelayed(&server->workQueue, &monitoredItem->delayedFreePointers); } UA_StatusCode UA_MonitoredItem_ensureQueueSpace(UA_Server *server, UA_MonitoredItem *mon) { /* Assert: The eventoverflow are counted in the queue size; There can be * only one eventoverflow more than normal entries */ UA_assert(mon->queueSize >= mon->eventOverflows); UA_assert(mon->eventOverflows <= mon->queueSize - mon->eventOverflows + 1); /* Nothing to do */ if(mon->queueSize - mon->eventOverflows <= mon->maxQueueSize) return UA_STATUSCODE_GOOD; #ifdef __clang_analyzer__ return UA_STATUSCODE_GOOD; #endif /* Remove notifications until the queue size is reached */ UA_Subscription *sub = mon->subscription; while(mon->queueSize - mon->eventOverflows > mon->maxQueueSize) { /* At least two notifications that are not eventOverflows in the queue */ UA_assert(mon->queueSize - mon->eventOverflows >= 2); /* Select the next notification to delete. Skip over overflow events. */ UA_Notification *del = NULL; if(mon->discardOldest) { /* Remove the oldest */ del = TAILQ_FIRST(&mon->queue); #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS while(UA_Notification_isOverflowEvent(server, del)) del = TAILQ_NEXT(del, listEntry); /* skip overflow events */ #endif } else { /* Remove the second newest (to keep the up-to-date notification). * The last entry is not an OverflowEvent -- we just added it. */ del = TAILQ_LAST(&mon->queue, NotificationQueue); del = TAILQ_PREV(del, NotificationQueue, listEntry); #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS while(UA_Notification_isOverflowEvent(server, del)) del = TAILQ_PREV(del, NotificationQueue, listEntry); /* skip overflow events */ #endif } UA_assert(del); /* There must have been one entry that can be deleted */ /* If reporting is activated (entries are also in the subscriptions * global queue): Move the entry after del in the per-MonitoredItem * queue right after del in the global queue. (It is already right after * del in the per-MonitoredItem queue.) This is required so we don't * starve MonitoredItems with a high sampling interval by always * removing their first appearance in the gloal queue for the * Subscription. */ if(TAILQ_NEXT(del, globalEntry) != UA_SUBSCRIPTION_QUEUE_SENTINEL) { UA_Notification *after_del = TAILQ_NEXT(del, listEntry); UA_assert(after_del); /* There must be one remaining element after del */ TAILQ_REMOVE(&sub->notificationQueue, after_del, globalEntry); TAILQ_INSERT_AFTER(&sub->notificationQueue, del, after_del, globalEntry); } /* Delete the notification */ UA_Notification_dequeue(server, del); UA_Notification_delete(del); } /* Get the element where the overflow shall be announced (infobits or * overflowevent) */ UA_Notification *indicator; if(mon->discardOldest) indicator = TAILQ_FIRST(&mon->queue); else indicator = TAILQ_LAST(&mon->queue, NotificationQueue); UA_assert(indicator); /* Create an overflow notification */ #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) { return createEventOverflowNotification(server, sub, mon, indicator); } else #endif { /* Set the infobits of a datachange notification */ if(mon->maxQueueSize > 1) { /* Add the infobits either to the newest or the new last entry */ indicator->data.value.hasStatus = true; indicator->data.value.status |= (UA_STATUSCODE_INFOTYPE_DATAVALUE | UA_STATUSCODE_INFOBITS_OVERFLOW); } } return UA_STATUSCODE_GOOD; } UA_StatusCode UA_MonitoredItem_registerSampleCallback(UA_Server *server, UA_MonitoredItem *mon) { if(mon->sampleCallbackIsRegistered) return UA_STATUSCODE_GOOD; /* Only DataChange MonitoredItems have a callback with a sampling interval */ if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) return UA_STATUSCODE_GOOD; UA_StatusCode retval = UA_Server_addRepeatedCallback(server, (UA_ServerCallback)UA_MonitoredItem_sampleCallback, mon, mon->samplingInterval, &mon->sampleCallbackId); if(retval == UA_STATUSCODE_GOOD) mon->sampleCallbackIsRegistered = true; return retval; } void UA_MonitoredItem_unregisterSampleCallback(UA_Server *server, UA_MonitoredItem *mon) { if(!mon->sampleCallbackIsRegistered) return; UA_Server_removeRepeatedCallback(server, mon->sampleCallbackId); mon->sampleCallbackIsRegistered = false; } #endif /* UA_ENABLE_SUBSCRIPTIONS */ /*********************************** amalgamated original file "/home/jvoe/open62541/src/server/ua_subscription_datachange.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2018 (c) Ari Breitkreuz, fortiss GmbH * Copyright 2018 (c) Thomas Stalder, Blue Time Concept SA * Copyright 2018 (c) Fabian Arndt, Root-Core */ #ifdef UA_ENABLE_DA #include // fabs #endif #ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */ #define UA_VALUENCODING_MAXSTACK 512 #define ABS_SUBTRACT_TYPE_INDEPENDENT(a,b) ((a)>(b)?(a)-(b):(b)-(a)) static UA_Boolean outOfDeadBand(const void *data1, const void *data2, const size_t arrayPos, const UA_DataType *type, const UA_Double deadbandValue) { if(type == &UA_TYPES[UA_TYPES_BOOLEAN]) { if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Boolean*)data1)[arrayPos], ((const UA_Boolean*)data2)[arrayPos]) <= deadbandValue) return false; } else if(type == &UA_TYPES[UA_TYPES_SBYTE]) { if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_SByte*)data1)[arrayPos], ((const UA_SByte*)data2)[arrayPos]) <= deadbandValue) return false; } else if(type == &UA_TYPES[UA_TYPES_BYTE]) { if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Byte*)data1)[arrayPos], ((const UA_Byte*)data2)[arrayPos]) <= deadbandValue) return false; } else if(type == &UA_TYPES[UA_TYPES_INT16]) { if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Int16*)data1)[arrayPos], ((const UA_Int16*)data2)[arrayPos]) <= deadbandValue) return false; } else if(type == &UA_TYPES[UA_TYPES_UINT16]) { if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_UInt16*)data1)[arrayPos], ((const UA_UInt16*)data2)[arrayPos]) <= deadbandValue) return false; } else if(type == &UA_TYPES[UA_TYPES_INT32]) { if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Int32*)data1)[arrayPos], ((const UA_Int32*)data2)[arrayPos]) <= deadbandValue) return false; } else if(type == &UA_TYPES[UA_TYPES_UINT32]) { if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_UInt32*)data1)[arrayPos], ((const UA_UInt32*)data2)[arrayPos]) <= deadbandValue) return false; } else if(type == &UA_TYPES[UA_TYPES_INT64]) { if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Int64*)data1)[arrayPos], ((const UA_Int64*)data2)[arrayPos]) <= deadbandValue) return false; } else if(type == &UA_TYPES[UA_TYPES_UINT64]) { if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_UInt64*)data1)[arrayPos], ((const UA_UInt64*)data2)[arrayPos]) <= deadbandValue) return false; } else if(type == &UA_TYPES[UA_TYPES_FLOAT]) { if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Float*)data1)[arrayPos], ((const UA_Float*)data2)[arrayPos]) <= deadbandValue) return false; } else if(type == &UA_TYPES[UA_TYPES_DOUBLE]) { if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Double*)data1)[arrayPos], ((const UA_Double*)data2)[arrayPos]) <= deadbandValue) return false; } return true; } #ifdef UA_ENABLE_DA static UA_INLINE UA_Boolean outOfPercentDeadBand(const void *data1, const void *data2, const size_t index, const UA_DataType *type, const UA_Double deadbandValue, UA_Range* range) { if(type == &UA_TYPES[UA_TYPES_SBYTE]) { if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_SByte*)data1)[index], ((const UA_SByte*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) || *(const UA_SByte*)data1 > range->high) return false; } else if(type == &UA_TYPES[UA_TYPES_BYTE]) { if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Byte*)data1)[index], ((const UA_Byte*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) || *(const UA_Byte*)data1 > range->high) return false; } else if(type == &UA_TYPES[UA_TYPES_INT16]) { if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Int16*)data1)[index], ((const UA_Int16*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) || *(const UA_Int16*)data1 > range->high) return false; } else if(type == &UA_TYPES[UA_TYPES_UINT16]) { if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_UInt16*)data1)[index], ((const UA_UInt16*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) || *(const UA_UInt16*)data1 > range->high) return false; } else if(type == &UA_TYPES[UA_TYPES_INT32]) { if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Int32*)data1)[index], ((const UA_Int32*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) || *(const UA_Int32*)data1 > range->high) return false; } else if(type == &UA_TYPES[UA_TYPES_UINT32]) { if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_UInt32*)data1)[index], ((const UA_UInt32*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) || *(const UA_UInt32*)data1 > range->high) return false; } else if(type == &UA_TYPES[UA_TYPES_INT64]) { if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Int64*)data1)[index], ((const UA_Int64*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) || *(const UA_Int64*)data1 > range->high) return false; } else if(type == &UA_TYPES[UA_TYPES_UINT64]) { if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_UInt64*)data1)[index], ((const UA_UInt64*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) || *(const UA_UInt64*)data1 > range->high) return false; } else if(type == &UA_TYPES[UA_TYPES_FLOAT]) { if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Float*)data1)[index], ((const UA_Float*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) || *(const UA_Float*)data1 > range->high) return false; } else if(type == &UA_TYPES[UA_TYPES_DOUBLE]) { if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Double*)data1)[index], ((const UA_Double*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) || *(const UA_Double*)data1 > range->high) return false; } return true; } #endif /* UA_ENABLE_DA */ static UA_INLINE UA_Boolean updateNeededForFilteredValue(const UA_Variant *value, const UA_Variant *oldValue, const UA_Double deadbandValue) { if(value->arrayLength != oldValue->arrayLength) return true; if(value->type != oldValue->type) return true; if (UA_Variant_isScalar(value)) { return outOfDeadBand(value->data, oldValue->data, 0, value->type, deadbandValue); } for (size_t i = 0; i < value->arrayLength; ++i) { if (outOfDeadBand(value->data, oldValue->data, i, value->type, deadbandValue)) return true; } return false; } #ifdef UA_ENABLE_DA static UA_INLINE UA_Boolean updateNeededForFilteredPercentValue(const UA_Variant *value, const UA_Variant *oldValue, const UA_Double deadbandValue, UA_Range* euRange) { if(value->arrayLength != oldValue->arrayLength) return true; if(value->type != oldValue->type) return true; if (UA_Variant_isScalar(value)) { return outOfPercentDeadBand(value->data, oldValue->data, 0, value->type, deadbandValue, euRange); } for (size_t i = 0; i < value->arrayLength; ++i) { if (outOfPercentDeadBand(value->data, oldValue->data, i, value->type, deadbandValue, euRange)) return true; } return false; } static UA_Boolean updateNeededForStatusCode(const UA_DataValue *value, const UA_MonitoredItem *mon) { if (UA_Variant_isScalar(&value->value)) { if(value->status != mon->lastStatus) return true; } return false; } #endif /* When a change is detected, encoding contains the heap-allocated binary * encoded value. The default for changed is false. */ static UA_StatusCode detectValueChangeWithFilter(UA_Server *server, UA_MonitoredItem *mon, UA_DataValue *value, UA_ByteString *encoding, UA_Boolean *changed) { if(UA_DataType_isNumeric(value->value.type) && (mon->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUSVALUE || mon->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP)) { if(mon->filter.dataChangeFilter.deadbandType == UA_DEADBANDTYPE_ABSOLUTE) { if(!updateNeededForFilteredValue(&value->value, &mon->lastValue, mon->filter.dataChangeFilter.deadbandValue)) return UA_STATUSCODE_GOOD; } #ifdef UA_ENABLE_DA else if(mon->filter.dataChangeFilter.deadbandType == UA_DEADBANDTYPE_PERCENT) { UA_QualifiedName qn = UA_QUALIFIEDNAME(0, "EURange"); UA_BrowsePathResult bpr = UA_Server_browseSimplifiedBrowsePath(server, mon->monitoredNodeId, 1, &qn); if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) { //if branch is not entried, property has been found UA_BrowsePathResult_deleteMembers(&bpr); return UA_STATUSCODE_GOOD; } const UA_VariableNode* node = (const UA_VariableNode*) UA_Nodestore_getNode(server->nsCtx, &bpr.targets->targetId.nodeId); UA_Range* euRange = (UA_Range*) node->value.data.value.value.data; if(!updateNeededForFilteredPercentValue(&value->value, &mon->lastValue, mon->filter.dataChangeFilter.deadbandValue, euRange)) { if(!updateNeededForStatusCode(value, mon)) //when same value, but different status code is written return UA_STATUSCODE_GOOD; } } #endif } /* Stack-allocate some memory for the value encoding. We might heap-allocate * more memory if needed. This is just enough for scalars and small * structures. */ UA_STACKARRAY(UA_Byte, stackValueEncoding, UA_VALUENCODING_MAXSTACK); UA_ByteString valueEncoding; valueEncoding.data = stackValueEncoding; valueEncoding.length = UA_VALUENCODING_MAXSTACK; /* Encode the value */ UA_Byte *bufPos = valueEncoding.data; const UA_Byte *bufEnd = &valueEncoding.data[valueEncoding.length]; UA_StatusCode retval = UA_encodeBinary(value, &UA_TYPES[UA_TYPES_DATAVALUE], &bufPos, &bufEnd, NULL, NULL); if(retval == UA_STATUSCODE_BADENCODINGERROR) { size_t binsize = UA_calcSizeBinary(value, &UA_TYPES[UA_TYPES_DATAVALUE]); if(binsize == 0) return UA_STATUSCODE_BADENCODINGERROR; if(binsize > UA_VALUENCODING_MAXSTACK) { retval = UA_ByteString_allocBuffer(&valueEncoding, binsize); if(retval == UA_STATUSCODE_GOOD) { bufPos = valueEncoding.data; bufEnd = &valueEncoding.data[valueEncoding.length]; retval = UA_encodeBinary(value, &UA_TYPES[UA_TYPES_DATAVALUE], &bufPos, &bufEnd, NULL, NULL); } } } if(retval != UA_STATUSCODE_GOOD) { if(valueEncoding.data != stackValueEncoding) UA_ByteString_deleteMembers(&valueEncoding); return retval; } /* Has the value changed? */ valueEncoding.length = (uintptr_t)bufPos - (uintptr_t)valueEncoding.data; *changed = (!mon->lastSampledValue.data || !UA_String_equal(&valueEncoding, &mon->lastSampledValue)); /* No change */ if(!(*changed)) { if(valueEncoding.data != stackValueEncoding) UA_ByteString_deleteMembers(&valueEncoding); return UA_STATUSCODE_GOOD; } /* Change detected. Copy encoding on the heap if necessary. */ if(valueEncoding.data == stackValueEncoding) return UA_ByteString_copy(&valueEncoding, encoding); *encoding = valueEncoding; return UA_STATUSCODE_GOOD; } /* Has this sample changed from the last one? The method may allocate additional * space for the encoding buffer. Detect the change in encoding->data. */ static UA_StatusCode detectValueChange(UA_Server *server, UA_MonitoredItem *mon, UA_DataValue value, UA_ByteString *encoding, UA_Boolean *changed) { /* Apply Filter */ if(mon->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUS) value.hasValue = false; value.hasServerTimestamp = false; value.hasServerPicoseconds = false; if(mon->filter.dataChangeFilter.trigger < UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP) { value.hasSourceTimestamp = false; value.hasSourcePicoseconds = false; } /* Detect the value change */ return detectValueChangeWithFilter(server, mon, &value, encoding, changed); } /* movedValue returns whether the sample was moved to the notification. The * default is false. */ static UA_StatusCode sampleCallbackWithValue(UA_Server *server, UA_Session *session, UA_Subscription *sub, UA_MonitoredItem *mon, UA_DataValue *value, UA_Boolean *movedValue) { UA_assert(mon->attributeId != UA_ATTRIBUTEID_EVENTNOTIFIER); /* Contains heap-allocated binary encoding of the value if a change was detected */ UA_ByteString binValueEncoding = UA_BYTESTRING_NULL; /* Has the value changed? Allocates memory in binValueEncoding if necessary. * value is edited internally so we make a shallow copy. */ UA_Boolean changed = false; UA_StatusCode retval = detectValueChange(server, mon, *value, &binValueEncoding, &changed); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING_SESSION(&server->config.logger, session, "Subscription %u | " "MonitoredItem %i | Value change detection failed with StatusCode %s", sub ? sub->subscriptionId : 0, mon->monitoredItemId, UA_StatusCode_name(retval)); return retval; } if(!changed) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Subscription %u | " "MonitoredItem %i | The value has not changed", sub ? sub->subscriptionId : 0, mon->monitoredItemId); return UA_STATUSCODE_GOOD; } /* The MonitoredItem is attached to a subscription (not server-local). * Prepare a notification and enqueue it. */ if(sub) { /* Allocate a new notification */ UA_Notification *newNotification = (UA_Notification *)UA_malloc(sizeof(UA_Notification)); if(!newNotification) { UA_ByteString_deleteMembers(&binValueEncoding); return UA_STATUSCODE_BADOUTOFMEMORY; } if(value->value.storageType == UA_VARIANT_DATA) { newNotification->data.value = *value; /* Move the value to the notification */ *movedValue = true; } else { /* => (value->value.storageType == UA_VARIANT_DATA_NODELETE) */ retval = UA_DataValue_copy(value, &newNotification->data.value); if(retval != UA_STATUSCODE_GOOD) { UA_ByteString_deleteMembers(&binValueEncoding); UA_free(newNotification); return retval; } } /* <-- Point of no return --> */ UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Subscription %u | " "MonitoredItem %i | Enqueue a new notification", sub ? sub->subscriptionId : 0, mon->monitoredItemId); newNotification->mon = mon; UA_Notification_enqueue(server, sub, mon, newNotification); } /* Store the encoding for comparison */ UA_ByteString_deleteMembers(&mon->lastSampledValue); mon->lastSampledValue = binValueEncoding; /* Store the value for filter comparison (we don't want to decode * lastSampledValue in every iteration). Don't test the return code here. If * this fails, lastValue is empty and a notification will be forced for the * next deadband comparison. */ if((mon->filter.dataChangeFilter.deadbandType == UA_DEADBANDTYPE_NONE || mon->filter.dataChangeFilter.deadbandType == UA_DEADBANDTYPE_ABSOLUTE || mon->filter.dataChangeFilter.deadbandType == UA_DEADBANDTYPE_PERCENT) && (mon->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUS || mon->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUSVALUE || mon->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP)) { UA_Variant_deleteMembers(&mon->lastValue); UA_Variant_copy(&value->value, &mon->lastValue); #ifdef UA_ENABLE_DA UA_StatusCode_deleteMembers(&mon->lastStatus); UA_StatusCode_copy(&value->status, &mon->lastStatus); #endif } /* Call the local callback if the MonitoredItem is not attached to a * subscription. Do this at the very end. Because the callback might delete * the subscription. */ if(!sub) { UA_LocalMonitoredItem *localMon = (UA_LocalMonitoredItem*) mon; void *nodeContext = NULL; UA_Server_getNodeContext(server, mon->monitoredNodeId, &nodeContext); localMon->callback.dataChangeCallback(server, mon->monitoredItemId, localMon->context, &mon->monitoredNodeId, nodeContext, mon->attributeId, value); } return UA_STATUSCODE_GOOD; } void UA_MonitoredItem_sampleCallback(UA_Server *server, UA_MonitoredItem *monitoredItem) { UA_Subscription *sub = monitoredItem->subscription; UA_Session *session = &server->adminSession; if(sub) session = sub->session; UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Subscription %u | " "MonitoredItem %i | Sample callback called", sub ? sub->subscriptionId : 0, monitoredItem->monitoredItemId); UA_assert(monitoredItem->attributeId != UA_ATTRIBUTEID_EVENTNOTIFIER); /* Get the node */ const UA_Node *node = UA_Nodestore_getNode(server->nsCtx, &monitoredItem->monitoredNodeId); /* Sample the value. The sample can still point into the node. */ UA_DataValue value; UA_DataValue_init(&value); if(node) { UA_ReadValueId rvid; UA_ReadValueId_init(&rvid); rvid.nodeId = monitoredItem->monitoredNodeId; rvid.attributeId = monitoredItem->attributeId; rvid.indexRange = monitoredItem->indexRange; ReadWithNode(node, server, session, monitoredItem->timestampsToReturn, &rvid, &value); } else { value.hasStatus = true; value.status = UA_STATUSCODE_BADNODEIDUNKNOWN; } /* Operate on the sample */ UA_Boolean movedValue = false; UA_StatusCode retval = sampleCallbackWithValue(server, session, sub, monitoredItem, &value, &movedValue); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING_SESSION(&server->config.logger, session, "Subscription %u | " "MonitoredItem %i | Sampling returned the statuscode %s", sub ? sub->subscriptionId : 0, monitoredItem->monitoredItemId, UA_StatusCode_name(retval)); } /* Delete the sample if it was not moved to the notification. */ if(!movedValue) UA_DataValue_deleteMembers(&value); /* Does nothing for UA_VARIANT_DATA_NODELETE */ if(node) UA_Nodestore_releaseNode(server->nsCtx, node); } #endif /* UA_ENABLE_SUBSCRIPTIONS */ /*********************************** amalgamated original file "/home/jvoe/open62541/plugins/ua_log_stdout.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 2016-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2017 (c) Thomas Stalder, Blue Time Concept SA */ #include #ifdef UA_ENABLE_MULTITHREADING #include static pthread_mutex_t printf_mutex = PTHREAD_MUTEX_INITIALIZER; #endif /* ANSI escape sequences for color output taken from here: * https://stackoverflow.com/questions/3219393/stdlib-and-colored-output-in-c*/ #ifdef UA_ENABLE_LOG_COLORS # define ANSI_COLOR_RED "\x1b[31m" # define ANSI_COLOR_GREEN "\x1b[32m" # define ANSI_COLOR_YELLOW "\x1b[33m" # define ANSI_COLOR_BLUE "\x1b[34m" # define ANSI_COLOR_MAGENTA "\x1b[35m" # define ANSI_COLOR_CYAN "\x1b[36m" # define ANSI_COLOR_RESET "\x1b[0m" #else # define ANSI_COLOR_RED "" # define ANSI_COLOR_GREEN "" # define ANSI_COLOR_YELLOW "" # define ANSI_COLOR_BLUE "" # define ANSI_COLOR_MAGENTA "" # define ANSI_COLOR_CYAN "" # define ANSI_COLOR_RESET "" #endif const char *logLevelNames[6] = {"trace", "debug", ANSI_COLOR_GREEN "info", ANSI_COLOR_YELLOW "warn", ANSI_COLOR_RED "error", ANSI_COLOR_MAGENTA "fatal"}; const char *logCategoryNames[7] = {"network", "channel", "session", "server", "client", "userland", "securitypolicy"}; #ifdef __clang__ __attribute__((__format__(__printf__, 4 , 0))) #endif void UA_Log_Stdout_log(void *_, UA_LogLevel level, UA_LogCategory category, const char *msg, va_list args) { UA_Int64 tOffset = UA_DateTime_localTimeUtcOffset(); UA_DateTimeStruct dts = UA_DateTime_toStruct(UA_DateTime_now() + tOffset); #ifdef UA_ENABLE_MULTITHREADING pthread_mutex_lock(&printf_mutex); #endif printf("[%04u-%02u-%02u %02u:%02u:%02u.%03u (UTC%+05d)] %s/%s" ANSI_COLOR_RESET "\t", dts.year, dts.month, dts.day, dts.hour, dts.min, dts.sec, dts.milliSec, (int)(tOffset / UA_DATETIME_SEC / 36), logLevelNames[level], logCategoryNames[category]); vprintf(msg, args); printf("\n"); fflush(stdout); #ifdef UA_ENABLE_MULTITHREADING pthread_mutex_unlock(&printf_mutex); #endif } void UA_Log_Stdout_clear(void *logContext) { } const UA_Logger UA_Log_Stdout_ = {UA_Log_Stdout_log, NULL, UA_Log_Stdout_clear}; const UA_Logger *UA_Log_Stdout = &UA_Log_Stdout_; /*********************************** amalgamated original file "/home/jvoe/open62541/plugins/ua_accesscontrol_default.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 2016-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2017 (c) Stefan Profanter, fortiss GmbH */ /* Example access control management. Anonymous and username / password login. * The access rights are maximally permissive. */ typedef struct { UA_Boolean allowAnonymous; size_t usernamePasswordLoginSize; UA_UsernamePasswordLogin *usernamePasswordLogin; } AccessControlContext; #define ANONYMOUS_POLICY "open62541-anonymous-policy" #define USERNAME_POLICY "open62541-username-policy" const UA_String anonymous_policy = UA_STRING_STATIC(ANONYMOUS_POLICY); const UA_String username_policy = UA_STRING_STATIC(USERNAME_POLICY); /************************/ /* Access Control Logic */ /************************/ static UA_StatusCode activateSession_default(UA_Server *server, UA_AccessControl *ac, const UA_EndpointDescription *endpointDescription, const UA_ByteString *secureChannelRemoteCertificate, const UA_NodeId *sessionId, const UA_ExtensionObject *userIdentityToken, void **sessionContext) { AccessControlContext *context = (AccessControlContext*)ac->context; /* The empty token is interpreted as anonymous */ if(userIdentityToken->encoding == UA_EXTENSIONOBJECT_ENCODED_NOBODY) { if(!context->allowAnonymous) return UA_STATUSCODE_BADIDENTITYTOKENINVALID; /* No userdata atm */ *sessionContext = NULL; return UA_STATUSCODE_GOOD; } /* Could the token be decoded? */ if(userIdentityToken->encoding < UA_EXTENSIONOBJECT_DECODED) return UA_STATUSCODE_BADIDENTITYTOKENINVALID; /* Anonymous login */ if(userIdentityToken->content.decoded.type == &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN]) { if(!context->allowAnonymous) return UA_STATUSCODE_BADIDENTITYTOKENINVALID; const UA_AnonymousIdentityToken *token = (UA_AnonymousIdentityToken*) userIdentityToken->content.decoded.data; /* Compatibility notice: Siemens OPC Scout v10 provides an empty * policyId. This is not compliant. For compatibility, assume that empty * policyId == ANONYMOUS_POLICY */ if(token->policyId.data && !UA_String_equal(&token->policyId, &anonymous_policy)) return UA_STATUSCODE_BADIDENTITYTOKENINVALID; /* No userdata atm */ *sessionContext = NULL; return UA_STATUSCODE_GOOD; } /* Username and password */ if(userIdentityToken->content.decoded.type == &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]) { const UA_UserNameIdentityToken *userToken = (UA_UserNameIdentityToken*)userIdentityToken->content.decoded.data; if(!UA_String_equal(&userToken->policyId, &username_policy)) return UA_STATUSCODE_BADIDENTITYTOKENINVALID; /* The userToken has been decrypted by the server before forwarding * it to the plugin. This information can be used here. */ /* if(userToken->encryptionAlgorithm.length > 0) {} */ /* Empty username and password */ if(userToken->userName.length == 0 && userToken->password.length == 0) return UA_STATUSCODE_BADIDENTITYTOKENINVALID; /* Try to match username/pw */ UA_Boolean match = false; for(size_t i = 0; i < context->usernamePasswordLoginSize; i++) { if(UA_String_equal(&userToken->userName, &context->usernamePasswordLogin[i].username) && UA_String_equal(&userToken->password, &context->usernamePasswordLogin[i].password)) { match = true; break; } } if(!match) return UA_STATUSCODE_BADUSERACCESSDENIED; /* No userdata atm */ *sessionContext = NULL; return UA_STATUSCODE_GOOD; } /* Unsupported token type */ return UA_STATUSCODE_BADIDENTITYTOKENINVALID; } static void closeSession_default(UA_Server *server, UA_AccessControl *ac, const UA_NodeId *sessionId, void *sessionContext) { /* no context to clean up */ } static UA_UInt32 getUserRightsMask_default(UA_Server *server, UA_AccessControl *ac, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, void *nodeContext) { return 0xFFFFFFFF; } static UA_Byte getUserAccessLevel_default(UA_Server *server, UA_AccessControl *ac, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, void *nodeContext) { return 0xFF; } static UA_Boolean getUserExecutable_default(UA_Server *server, UA_AccessControl *ac, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *methodId, void *methodContext) { return true; } static UA_Boolean getUserExecutableOnObject_default(UA_Server *server, UA_AccessControl *ac, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId, void *objectContext) { return true; } static UA_Boolean allowAddNode_default(UA_Server *server, UA_AccessControl *ac, const UA_NodeId *sessionId, void *sessionContext, const UA_AddNodesItem *item) { return true; } static UA_Boolean allowAddReference_default(UA_Server *server, UA_AccessControl *ac, const UA_NodeId *sessionId, void *sessionContext, const UA_AddReferencesItem *item) { return true; } static UA_Boolean allowDeleteNode_default(UA_Server *server, UA_AccessControl *ac, const UA_NodeId *sessionId, void *sessionContext, const UA_DeleteNodesItem *item) { return true; } static UA_Boolean allowDeleteReference_default(UA_Server *server, UA_AccessControl *ac, const UA_NodeId *sessionId, void *sessionContext, const UA_DeleteReferencesItem *item) { return true; } #ifdef UA_ENABLE_HISTORIZING static UA_Boolean allowHistoryUpdateUpdateData_default(UA_Server *server, UA_AccessControl *ac, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, UA_PerformUpdateType performInsertReplace, const UA_DataValue *value) { return true; } static UA_Boolean allowHistoryUpdateDeleteRawModified_default(UA_Server *server, UA_AccessControl *ac, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, UA_DateTime startTimestamp, UA_DateTime endTimestamp, bool isDeleteModified) { return true; } #endif /***************************************/ /* Create Delete Access Control Plugin */ /***************************************/ static void deleteMembers_default(UA_AccessControl *ac) { UA_Array_delete((void*)(uintptr_t)ac->userTokenPolicies, ac->userTokenPoliciesSize, &UA_TYPES[UA_TYPES_USERTOKENPOLICY]); ac->userTokenPolicies = NULL; ac->userTokenPoliciesSize = 0; AccessControlContext *context = (AccessControlContext*)ac->context; if (context) { for(size_t i = 0; i < context->usernamePasswordLoginSize; i++) { UA_String_deleteMembers(&context->usernamePasswordLogin[i].username); UA_String_deleteMembers(&context->usernamePasswordLogin[i].password); } if(context->usernamePasswordLoginSize > 0) UA_free(context->usernamePasswordLogin); UA_free(ac->context); } } UA_StatusCode UA_AccessControl_default(UA_ServerConfig *config, UA_Boolean allowAnonymous, const UA_ByteString *userTokenPolicyUri, size_t usernamePasswordLoginSize, const UA_UsernamePasswordLogin *usernamePasswordLogin) { UA_AccessControl *ac = &config->accessControl; ac->deleteMembers = deleteMembers_default; ac->activateSession = activateSession_default; ac->closeSession = closeSession_default; ac->getUserRightsMask = getUserRightsMask_default; ac->getUserAccessLevel = getUserAccessLevel_default; ac->getUserExecutable = getUserExecutable_default; ac->getUserExecutableOnObject = getUserExecutableOnObject_default; ac->allowAddNode = allowAddNode_default; ac->allowAddReference = allowAddReference_default; #ifdef UA_ENABLE_HISTORIZING ac->allowHistoryUpdateUpdateData = allowHistoryUpdateUpdateData_default; ac->allowHistoryUpdateDeleteRawModified = allowHistoryUpdateDeleteRawModified_default; #endif ac->allowDeleteNode = allowDeleteNode_default; ac->allowDeleteReference = allowDeleteReference_default; AccessControlContext *context = (AccessControlContext*) UA_malloc(sizeof(AccessControlContext)); if (!context) return UA_STATUSCODE_BADOUTOFMEMORY; memset(context, 0, sizeof(AccessControlContext)); ac->context = context; /* Allow anonymous? */ context->allowAnonymous = allowAnonymous; /* Copy username/password to the access control plugin */ if(usernamePasswordLoginSize > 0) { context->usernamePasswordLogin = (UA_UsernamePasswordLogin*) UA_malloc(usernamePasswordLoginSize * sizeof(UA_UsernamePasswordLogin)); if(!context->usernamePasswordLogin) return UA_STATUSCODE_BADOUTOFMEMORY; context->usernamePasswordLoginSize = usernamePasswordLoginSize; for(size_t i = 0; i < usernamePasswordLoginSize; i++) { UA_String_copy(&usernamePasswordLogin[i].username, &context->usernamePasswordLogin[i].username); UA_String_copy(&usernamePasswordLogin[i].password, &context->usernamePasswordLogin[i].password); } } /* Set the allowed policies */ size_t policies = 0; if(allowAnonymous) policies++; if(usernamePasswordLoginSize > 0) policies++; ac->userTokenPoliciesSize = 0; ac->userTokenPolicies = (UA_UserTokenPolicy *) UA_Array_new(policies, &UA_TYPES[UA_TYPES_USERTOKENPOLICY]); if(!ac->userTokenPolicies) return UA_STATUSCODE_BADOUTOFMEMORY; ac->userTokenPoliciesSize = policies; policies = 0; if(allowAnonymous) { ac->userTokenPolicies[policies].tokenType = UA_USERTOKENTYPE_ANONYMOUS; ac->userTokenPolicies[policies].policyId = UA_STRING_ALLOC(ANONYMOUS_POLICY); if (!ac->userTokenPolicies[policies].policyId.data) return UA_STATUSCODE_BADOUTOFMEMORY; policies++; } if(usernamePasswordLoginSize > 0) { ac->userTokenPolicies[policies].tokenType = UA_USERTOKENTYPE_USERNAME; ac->userTokenPolicies[policies].policyId = UA_STRING_ALLOC(USERNAME_POLICY); if(!ac->userTokenPolicies[policies].policyId.data) return UA_STATUSCODE_BADOUTOFMEMORY; #if UA_LOGLEVEL <= 400 const UA_String noneUri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None"); if(UA_ByteString_equal(userTokenPolicyUri, &noneUri)) { UA_LOG_WARNING(&config->logger, UA_LOGCATEGORY_SERVER, "Username/Password configured, but no encrypting SecurityPolicy. " "This can leak credentials on the network."); } #endif return UA_ByteString_copy(userTokenPolicyUri, &ac->userTokenPolicies[policies].securityPolicyUri); } return UA_STATUSCODE_GOOD; } /*********************************** amalgamated original file "/home/jvoe/open62541/plugins/ua_pki_default.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 2018 (c) Mark Giraud, Fraunhofer IOSB * Copyright 2019 (c) Kalycito Infotech Private Limited * Copyright 2019 (c) Julius Pfrommer, Fraunhofer IOSB */ #ifdef UA_ENABLE_ENCRYPTION #include #include #include #endif #define REMOTECERTIFICATETRUSTED 1 #define ISSUERKNOWN 2 #define DUALPARENT 3 #define PARENTFOUND 4 /************/ /* AllowAll */ /************/ static UA_StatusCode verifyCertificateAllowAll(void *verificationContext, const UA_ByteString *certificate) { return UA_STATUSCODE_GOOD; } static UA_StatusCode verifyApplicationURIAllowAll(void *verificationContext, const UA_ByteString *certificate, const UA_String *applicationURI) { return UA_STATUSCODE_GOOD; } static void deleteVerifyAllowAll(UA_CertificateVerification *cv) { } void UA_CertificateVerification_AcceptAll(UA_CertificateVerification *cv) { cv->verifyCertificate = verifyCertificateAllowAll; cv->verifyApplicationURI = verifyApplicationURIAllowAll; cv->deleteMembers = deleteVerifyAllowAll; } #ifdef UA_ENABLE_ENCRYPTION typedef struct { /* If the folders are defined, we use them to reload the certificates during * runtime */ UA_String trustListFolder; UA_String issuerListFolder; UA_String revocationListFolder; mbedtls_x509_crt certificateTrustList; mbedtls_x509_crt certificateIssuerList; mbedtls_x509_crl certificateRevocationList; } CertInfo; #ifdef __linux__ /* Linux only so far */ #include #include static UA_StatusCode fileNamesFromFolder(const UA_String *folder, size_t *pathsSize, UA_String **paths) { char buf[PATH_MAX + 1]; if(folder->length > PATH_MAX) return UA_STATUSCODE_BADINTERNALERROR; memcpy(buf, folder->data, folder->length); buf[folder->length] = 0; DIR *dir = opendir(buf); if(!dir) return UA_STATUSCODE_BADINTERNALERROR; *paths = (UA_String*)UA_Array_new(256, &UA_TYPES[UA_TYPES_STRING]); if(*paths == NULL) { closedir(dir); return UA_STATUSCODE_BADOUTOFMEMORY; } struct dirent *ent; char buf2[PATH_MAX + 1]; realpath(buf, buf2); size_t pathlen = strlen(buf2); *pathsSize = 0; while((ent = readdir (dir)) != NULL && *pathsSize < 256) { if(ent->d_type != DT_REG) continue; buf2[pathlen] = '/'; buf2[pathlen+1] = 0; strcat(buf2, ent->d_name); (*paths)[*pathsSize] = UA_STRING_ALLOC(buf2); *pathsSize += 1; } closedir(dir); if(*pathsSize == 0) { UA_free(*paths); *paths = NULL; } return UA_STATUSCODE_GOOD; } static UA_StatusCode reloadCertificates(CertInfo *ci) { UA_StatusCode retval = UA_STATUSCODE_GOOD; int err = 0; /* Load the trustlists */ if(ci->trustListFolder.length > 0) { UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Reloading the trust-list"); mbedtls_x509_crt_free(&ci->certificateTrustList); mbedtls_x509_crt_init(&ci->certificateTrustList); char f[PATH_MAX]; memcpy(f, ci->trustListFolder.data, ci->trustListFolder.length); f[ci->trustListFolder.length] = 0; err = mbedtls_x509_crt_parse_path(&ci->certificateTrustList, f); if(err == 0) { UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Loaded certificate from %s", f); } else { char errBuff[300]; mbedtls_strerror(err, errBuff, 300); UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Failed to load certificate from %s", f); } } /* Load the revocationlists */ if(ci->revocationListFolder.length > 0) { UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Reloading the revocation-list"); size_t pathsSize = 0; UA_String *paths = NULL; retval = fileNamesFromFolder(&ci->revocationListFolder, &pathsSize, &paths); if(retval != UA_STATUSCODE_GOOD) return retval; mbedtls_x509_crl_free(&ci->certificateRevocationList); mbedtls_x509_crl_init(&ci->certificateRevocationList); for(size_t i = 0; i < pathsSize; i++) { char f[PATH_MAX]; memcpy(f, paths[i].data, paths[i].length); f[paths[i].length] = 0; err = mbedtls_x509_crl_parse_file(&ci->certificateRevocationList, f); if(err == 0) { UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Loaded certificate from %.*s", (int)paths[i].length, paths[i].data); } else { UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Failed to load certificate from %.*s", (int)paths[i].length, paths[i].data); } } UA_Array_delete(paths, pathsSize, &UA_TYPES[UA_TYPES_STRING]); paths = NULL; pathsSize = 0; } /* Load the issuerlists */ if(ci->issuerListFolder.length > 0) { UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Reloading the issuer-list"); mbedtls_x509_crt_free(&ci->certificateIssuerList); mbedtls_x509_crt_init(&ci->certificateIssuerList); char f[PATH_MAX]; memcpy(f, ci->issuerListFolder.data, ci->issuerListFolder.length); f[ci->issuerListFolder.length] = 0; err = mbedtls_x509_crt_parse_path(&ci->certificateIssuerList, f); if(err == 0) { UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Loaded certificate from %s", f); } else { UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Failed to load certificate from %s", f); } } return retval; } #endif static UA_StatusCode certificateVerification_verify(void *verificationContext, const UA_ByteString *certificate) { CertInfo *ci = (CertInfo*)verificationContext; if(!ci) return UA_STATUSCODE_BADINTERNALERROR; #ifdef __linux__ /* Reload certificates if folder paths are specified */ reloadCertificates(ci); #endif /* if(ci->certificateTrustList.raw.len == 0) { */ /* UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, */ /* "No Trustlist loaded. Accepting the certificate."); */ /* return UA_STATUSCODE_GOOD; */ /* } */ /* Parse the certificate */ mbedtls_x509_crt remoteCertificate; /* Temporary Object to parse the trustList */ mbedtls_x509_crt *tempCert; /* Temporary Object to parse the revocationList */ mbedtls_x509_crl *tempCrl; /* Temporary Object to identify the parent CA when there is no intermediate CA */ mbedtls_x509_crt *parentCert; /* Temporary Object to identify the parent CA when there is intermediate CA */ mbedtls_x509_crt *parentCert_2; /* Flag value to identify if the issuer certificate is found */ int issuerKnown = 0; /* Flag value to identify if the parent certificate found */ int parentFound = 0; mbedtls_x509_crt_init(&remoteCertificate); int mbedErr = mbedtls_x509_crt_parse(&remoteCertificate, certificate->data, certificate->length); if(mbedErr) { /* char errBuff[300]; */ /* mbedtls_strerror(mbedErr, errBuff, 300); */ /* UA_LOG_WARNING(data->policyContext->securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, */ /* "Could not parse the remote certificate with error: %s", errBuff); */ return UA_STATUSCODE_BADSECURITYCHECKSFAILED; } /* Verify */ mbedtls_x509_crt_profile crtProfile = { MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA1) | MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA256), 0xFFFFFF, 0x000000, 128 * 8 // in bits }; // TODO: remove magic numbers uint32_t flags = 0; mbedErr = mbedtls_x509_crt_verify_with_profile(&remoteCertificate, &ci->certificateTrustList, &ci->certificateRevocationList, &crtProfile, NULL, &flags, NULL, NULL); /* Flag to check if the remote certificate is trusted or not */ int TRUSTED = 0; /* Check if the remoteCertificate is present in the trustList while mbedErr value is not zero */ if(mbedErr && !(flags & MBEDTLS_X509_BADCERT_EXPIRED) && !(flags & MBEDTLS_X509_BADCERT_FUTURE)) { for(tempCert = &ci->certificateTrustList; tempCert != NULL; tempCert = tempCert->next) { if(remoteCertificate.raw.len == tempCert->raw.len && memcmp(remoteCertificate.raw.p, tempCert->raw.p, remoteCertificate.raw.len) == 0) { TRUSTED = REMOTECERTIFICATETRUSTED; break; } } } /* If the remote certificate is present in the trustList then check if the issuer certificate * of remoteCertificate is present in issuerList */ if(TRUSTED && mbedErr) { mbedErr = mbedtls_x509_crt_verify_with_profile(&remoteCertificate, &ci->certificateIssuerList, &ci->certificateRevocationList, &crtProfile, NULL, &flags, NULL, NULL); /* Check if the parent certificate has a CRL file available */ if(!mbedErr) { /* Flag value to identify if that there is an intermediate CA present */ int dualParent = 0; /* Identify the topmost parent certificate for the remoteCertificate */ for( parentCert = &ci->certificateIssuerList; parentCert != NULL; parentCert = parentCert->next ) { if(memcmp(remoteCertificate.issuer_raw.p, parentCert->subject_raw.p, parentCert->subject_raw.len) == 0) { for(parentCert_2 = &ci->certificateTrustList; parentCert_2 != NULL; parentCert_2 = parentCert_2->next) { if(memcmp(parentCert->issuer_raw.p, parentCert_2->subject_raw.p, parentCert_2->subject_raw.len) == 0) { dualParent = DUALPARENT; parentFound = PARENTFOUND; break; } } parentFound = PARENTFOUND; } if(parentFound == PARENTFOUND) { break; } } /* Check if there is an intermediate certificate between the topmost parent * certificate and child certificate * If yes the topmost parent certificate is to be checked whether it has a * CRL file avaiable */ if(dualParent == DUALPARENT && parentFound == PARENTFOUND) { parentCert = parentCert_2; } /* If a parent certificate is found traverse the revocationList and identify * if there is any CRL file that corresponds to the parentCertificate */ if(parentFound == PARENTFOUND) { tempCrl = &ci->certificateRevocationList; while(tempCrl != NULL) { if(tempCrl->version != 0 && tempCrl->issuer_raw.len == parentCert->subject_raw.len && memcmp(tempCrl->issuer_raw.p, parentCert->subject_raw.p, tempCrl->issuer_raw.len) == 0) { issuerKnown = ISSUERKNOWN; break; } tempCrl = tempCrl->next; } /* If the CRL file corresponding to the parent certificate is not present * then return UA_STATUSCODE_BADCERTIFICATEISSUERREVOCATIONUNKNOWN */ if(!issuerKnown) { return UA_STATUSCODE_BADCERTIFICATEISSUERREVOCATIONUNKNOWN; } } } } else if(!mbedErr && !TRUSTED) { /* This else if section is to identify if the parent certificate which is present in trustList * has CRL file corresponding to it */ /* Identify the parent certificate of the remoteCertificate */ for(parentCert = &ci->certificateTrustList; parentCert != NULL; parentCert = parentCert->next) { if(memcmp(remoteCertificate.issuer_raw.p, parentCert->subject_raw.p, parentCert->subject_raw.len) == 0) { parentFound = PARENTFOUND; break; } } /* If the parent certificate is found traverse the revocationList and identify * if there is any CRL file that corresponds to the parentCertificate */ if(parentFound == PARENTFOUND && memcmp(remoteCertificate.issuer_raw.p, remoteCertificate.subject_raw.p, remoteCertificate.subject_raw.len) != 0) { tempCrl = &ci->certificateRevocationList; while(tempCrl != NULL) { if(tempCrl->version != 0 && tempCrl->issuer_raw.len == parentCert->subject_raw.len && memcmp(tempCrl->issuer_raw.p, parentCert->subject_raw.p, tempCrl->issuer_raw.len) == 0) { issuerKnown = ISSUERKNOWN; break; } tempCrl = tempCrl->next; } /* If the CRL file corresponding to the parent certificate is not present * then return UA_STATUSCODE_BADCERTIFICATEREVOCATIONUNKNOWN */ if(!issuerKnown) { return UA_STATUSCODE_BADCERTIFICATEREVOCATIONUNKNOWN; } } } // TODO: Extend verification /* This condition will check whether the certificate is a User certificate * or a CA certificate. If the MBEDTLS_X509_KU_KEY_CERT_SIGN and * MBEDTLS_X509_KU_CRL_SIGN of key_usage are set, then the certificate * shall be condidered as CA Certificate and cannot be used to establish a * connection. Refer the test case CTT/Security/Security Certificate Validation/029.js * for more details */ if((remoteCertificate.key_usage & MBEDTLS_X509_KU_KEY_CERT_SIGN) && (remoteCertificate.key_usage & MBEDTLS_X509_KU_CRL_SIGN)) { return UA_STATUSCODE_BADCERTIFICATEUSENOTALLOWED; } UA_StatusCode retval = UA_STATUSCODE_GOOD; if(mbedErr) { /* char buff[100]; */ /* mbedtls_x509_crt_verify_info(buff, 100, "", flags); */ /* UA_LOG_ERROR(channelContextData->policyContext->securityPolicy->logger, */ /* UA_LOGCATEGORY_SECURITYPOLICY, */ /* "Verifying the certificate failed with error: %s", buff); */ if(flags & (uint32_t)MBEDTLS_X509_BADCERT_NOT_TRUSTED) { retval = UA_STATUSCODE_BADCERTIFICATEUNTRUSTED; } else if(flags & (uint32_t)MBEDTLS_X509_BADCERT_FUTURE || flags & (uint32_t)MBEDTLS_X509_BADCERT_EXPIRED) { retval = UA_STATUSCODE_BADCERTIFICATETIMEINVALID; } else if(flags & (uint32_t)MBEDTLS_X509_BADCERT_REVOKED || flags & (uint32_t)MBEDTLS_X509_BADCRL_EXPIRED) { retval = UA_STATUSCODE_BADCERTIFICATEREVOKED; } else { retval = UA_STATUSCODE_BADSECURITYCHECKSFAILED; } } mbedtls_x509_crt_free(&remoteCertificate); return retval; } /* Find binary substring. Taken and adjusted from * http://tungchingkai.blogspot.com/2011/07/binary-strstr.html */ static const unsigned char * bstrchr(const unsigned char *s, const unsigned char ch, size_t l) { /* find first occurrence of c in char s[] for length l*/ /* handle special case */ if(l == 0) return (NULL); for(; *s != ch; ++s, --l) if(l == 0) return (NULL); return s; } static const unsigned char * bstrstr(const unsigned char *s1, size_t l1, const unsigned char *s2, size_t l2) { /* find first occurrence of s2[] in s1[] for length l1*/ const unsigned char *ss1 = s1; const unsigned char *ss2 = s2; /* handle special case */ if(l1 == 0) return (NULL); if(l2 == 0) return s1; /* match prefix */ for (; (s1 = bstrchr(s1, *s2, (uintptr_t)ss1-(uintptr_t)s1+(uintptr_t)l1)) != NULL && (uintptr_t)ss1-(uintptr_t)s1+(uintptr_t)l1 != 0; ++s1) { /* match rest of prefix */ const unsigned char *sc1, *sc2; for (sc1 = s1, sc2 = s2; ;) if (++sc2 >= ss2+l2) return s1; else if (*++sc1 != *sc2) break; } return NULL; } static UA_StatusCode certificateVerification_verifyApplicationURI(void *verificationContext, const UA_ByteString *certificate, const UA_String *applicationURI) { CertInfo *ci = (CertInfo*)verificationContext; if(!ci) return UA_STATUSCODE_BADINTERNALERROR; /* Parse the certificate */ mbedtls_x509_crt remoteCertificate; mbedtls_x509_crt_init(&remoteCertificate); int mbedErr = mbedtls_x509_crt_parse(&remoteCertificate, certificate->data, certificate->length); if(mbedErr) return UA_STATUSCODE_BADSECURITYCHECKSFAILED; /* Poor man's ApplicationUri verification. mbedTLS does not parse all fields * of the Alternative Subject Name. Instead test whether the URI-string is * present in the v3_ext field in general. * * TODO: Improve parsing of the Alternative Subject Name */ UA_StatusCode retval = UA_STATUSCODE_GOOD; if(bstrstr(remoteCertificate.v3_ext.p, remoteCertificate.v3_ext.len, applicationURI->data, applicationURI->length) == NULL) retval = UA_STATUSCODE_BADCERTIFICATEURIINVALID; mbedtls_x509_crt_free(&remoteCertificate); return retval; } static void certificateVerification_deleteMembers(UA_CertificateVerification *cv) { CertInfo *ci = (CertInfo*)cv->context; if(!ci) return; mbedtls_x509_crt_free(&ci->certificateTrustList); mbedtls_x509_crl_free(&ci->certificateRevocationList); mbedtls_x509_crt_free(&ci->certificateIssuerList); UA_String_clear(&ci->trustListFolder); UA_String_clear(&ci->issuerListFolder); UA_String_clear(&ci->revocationListFolder); UA_free(ci); cv->context = NULL; } UA_StatusCode UA_CertificateVerification_Trustlist(UA_CertificateVerification *cv, const UA_ByteString *certificateTrustList, size_t certificateTrustListSize, const UA_ByteString *certificateIssuerList, size_t certificateIssuerListSize, const UA_ByteString *certificateRevocationList, size_t certificateRevocationListSize) { CertInfo *ci = (CertInfo*)UA_malloc(sizeof(CertInfo)); if(!ci) return UA_STATUSCODE_BADOUTOFMEMORY; memset(ci, 0, sizeof(CertInfo)); mbedtls_x509_crt_init(&ci->certificateTrustList); mbedtls_x509_crl_init(&ci->certificateRevocationList); mbedtls_x509_crt_init(&ci->certificateIssuerList); cv->context = (void*)ci; if(certificateTrustListSize > 0) cv->verifyCertificate = certificateVerification_verify; else cv->verifyCertificate = verifyCertificateAllowAll; cv->deleteMembers = certificateVerification_deleteMembers; cv->verifyApplicationURI = certificateVerification_verifyApplicationURI; int err = 0; for(size_t i = 0; i < certificateTrustListSize; i++) { err = mbedtls_x509_crt_parse(&ci->certificateTrustList, certificateTrustList[i].data, certificateTrustList[i].length); if(err) goto error; } for(size_t i = 0; i < certificateIssuerListSize; i++) { err = mbedtls_x509_crt_parse(&ci->certificateIssuerList, certificateIssuerList[i].data, certificateIssuerList[i].length); if(err) goto error; } for(size_t i = 0; i < certificateRevocationListSize; i++) { err = mbedtls_x509_crl_parse(&ci->certificateRevocationList, certificateRevocationList[i].data, certificateRevocationList[i].length); if(err) goto error; } return UA_STATUSCODE_GOOD; error: certificateVerification_deleteMembers(cv); return UA_STATUSCODE_BADINTERNALERROR; } #ifdef __linux__ /* Linux only so far */ UA_StatusCode UA_CertificateVerification_CertFolders(UA_CertificateVerification *cv, const char *trustListFolder, const char *issuerListFolder, const char *revocationListFolder) { CertInfo *ci = (CertInfo*)UA_malloc(sizeof(CertInfo)); if(!ci) return UA_STATUSCODE_BADOUTOFMEMORY; memset(ci, 0, sizeof(CertInfo)); mbedtls_x509_crt_init(&ci->certificateTrustList); mbedtls_x509_crl_init(&ci->certificateRevocationList); mbedtls_x509_crt_init(&ci->certificateIssuerList); /* Only set the folder paths. They will be reloaded during runtime. * TODO: Add a more efficient reloading of only the changes */ ci->trustListFolder = UA_STRING_ALLOC(trustListFolder); ci->issuerListFolder = UA_STRING_ALLOC(issuerListFolder); ci->revocationListFolder = UA_STRING_ALLOC(revocationListFolder); reloadCertificates(ci); cv->context = (void*)ci; cv->verifyCertificate = certificateVerification_verify; cv->deleteMembers = certificateVerification_deleteMembers; cv->verifyApplicationURI = certificateVerification_verifyApplicationURI; return UA_STATUSCODE_GOOD; } #endif #endif /*********************************** amalgamated original file "/home/jvoe/open62541/plugins/ua_nodestore_default.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 2014-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2017 (c) Julian Grothoff * Copyright 2017 (c) Stefan Profanter, fortiss GmbH */ #ifndef UA_ENABLE_CUSTOM_NODESTORE #ifdef UA_ENABLE_MULTITHREADING #include #define BEGIN_CRITSECT(NODEMAP) pthread_mutex_lock(&(NODEMAP)->mutex) #define END_CRITSECT(NODEMAP) pthread_mutex_unlock(&(NODEMAP)->mutex) #else #define BEGIN_CRITSECT(NODEMAP) do {} while(0) #define END_CRITSECT(NODEMAP) do {} while(0) #endif /* container_of */ #define container_of(ptr, type, member) \ (type *)((uintptr_t)ptr - offsetof(type,member)) struct NodeEntry; typedef struct NodeEntry NodeEntry; struct NodeEntry { ZIP_ENTRY(NodeEntry) zipfields; UA_UInt32 nodeIdHash; UA_UInt16 refCount; /* How many consumers have a reference to the node? */ UA_Boolean deleted; /* Node was marked as deleted and can be deleted when refCount == 0 */ NodeEntry *orig; /* If a copy is made to replace a node, track that we * replace only the node from which the copy was made. * Important for concurrent operations. */ UA_NodeId nodeId; /* This is actually a UA_Node that also starts with a NodeId */ }; /* Absolute ordering for NodeIds */ static enum ZIP_CMP cmpNodeId(const void *a, const void *b) { const NodeEntry *aa = (const NodeEntry*)a; const NodeEntry *bb = (const NodeEntry*)b; /* Compare hash */ if(aa->nodeIdHash < bb->nodeIdHash) return ZIP_CMP_LESS; if(aa->nodeIdHash > bb->nodeIdHash) return ZIP_CMP_MORE; /* Compore nodes in detail */ return (enum ZIP_CMP)UA_NodeId_order(&aa->nodeId, &bb->nodeId); } ZIP_HEAD(NodeTree, NodeEntry); typedef struct NodeTree NodeTree; typedef struct { NodeTree root; #ifdef UA_ENABLE_MULTITHREADING pthread_mutex_t mutex; /* Protect access */ #endif } NodeMap; ZIP_PROTTYPE(NodeTree, NodeEntry, NodeEntry) ZIP_IMPL(NodeTree, NodeEntry, zipfields, NodeEntry, zipfields, cmpNodeId) static NodeEntry * newEntry(UA_NodeClass nodeClass) { size_t size = sizeof(NodeEntry) - sizeof(UA_NodeId); switch(nodeClass) { case UA_NODECLASS_OBJECT: size += sizeof(UA_ObjectNode); break; case UA_NODECLASS_VARIABLE: size += sizeof(UA_VariableNode); break; case UA_NODECLASS_METHOD: size += sizeof(UA_MethodNode); break; case UA_NODECLASS_OBJECTTYPE: size += sizeof(UA_ObjectTypeNode); break; case UA_NODECLASS_VARIABLETYPE: size += sizeof(UA_VariableTypeNode); break; case UA_NODECLASS_REFERENCETYPE: size += sizeof(UA_ReferenceTypeNode); break; case UA_NODECLASS_DATATYPE: size += sizeof(UA_DataTypeNode); break; case UA_NODECLASS_VIEW: size += sizeof(UA_ViewNode); break; default: return NULL; } NodeEntry *entry = (NodeEntry*)UA_calloc(1, size); if(!entry) return NULL; UA_Node *node = (UA_Node*)&entry->nodeId; node->nodeClass = nodeClass; return entry; } static void deleteEntry(NodeEntry *entry) { UA_Node_deleteMembers((UA_Node*)&entry->nodeId); UA_free(entry); } static void cleanupEntry(NodeEntry *entry) { if(entry->deleted && entry->refCount == 0) deleteEntry(entry); } /***********************/ /* Interface functions */ /***********************/ /* Not yet inserted into the NodeMap */ UA_Node * UA_Nodestore_newNode(void *nsCtx, UA_NodeClass nodeClass) { NodeEntry *entry = newEntry(nodeClass); if(!entry) return NULL; return (UA_Node*)&entry->nodeId; } /* Not yet inserted into the NodeMap */ void UA_Nodestore_deleteNode(void *nsCtx, UA_Node *node) { deleteEntry(container_of(node, NodeEntry, nodeId)); } const UA_Node * UA_Nodestore_getNode(void *nsCtx, const UA_NodeId *nodeId) { NodeMap *ns = (NodeMap*)nsCtx; BEGIN_CRITSECT(ns); NodeEntry dummy; dummy.nodeIdHash = UA_NodeId_hash(nodeId); dummy.nodeId = *nodeId; NodeEntry *entry = ZIP_FIND(NodeTree, &ns->root, &dummy); if(!entry) { END_CRITSECT(ns); return NULL; } ++entry->refCount; END_CRITSECT(ns); return (const UA_Node*)&entry->nodeId; } void UA_Nodestore_releaseNode(void *nsCtx, const UA_Node *node) { if(!node) return; #ifdef UA_ENABLE_MULTITHREADING NodeMap *ns = (NodeMap*)nsCtx; #endif BEGIN_CRITSECT(ns); NodeEntry *entry = container_of(node, NodeEntry, nodeId); UA_assert(entry->refCount > 0); --entry->refCount; cleanupEntry(entry); END_CRITSECT(ns); } UA_StatusCode UA_Nodestore_getNodeCopy(void *nsCtx, const UA_NodeId *nodeId, UA_Node **outNode) { /* Find the node */ const UA_Node *node = UA_Nodestore_getNode(nsCtx, nodeId); if(!node) return UA_STATUSCODE_BADNODEIDUNKNOWN; /* Create the new entry */ NodeEntry *ne = newEntry(node->nodeClass); if(!ne) { UA_Nodestore_releaseNode(nsCtx, node); return UA_STATUSCODE_BADOUTOFMEMORY; } /* Copy the node content */ UA_Node *nnode = (UA_Node*)&ne->nodeId; UA_StatusCode retval = UA_Node_copy(node, nnode); UA_Nodestore_releaseNode(nsCtx, node); if(retval != UA_STATUSCODE_GOOD) { deleteEntry(ne); return retval; } ne->orig = container_of(node, NodeEntry, nodeId); *outNode = nnode; return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Nodestore_insertNode(void *nsCtx, UA_Node *node, UA_NodeId *addedNodeId) { NodeEntry *entry = container_of(node, NodeEntry, nodeId); NodeMap *ns = (NodeMap*)nsCtx; BEGIN_CRITSECT(ns); /* Ensure that the NodeId is unique */ NodeEntry dummy; dummy.nodeId = node->nodeId; if(node->nodeId.identifierType == UA_NODEIDTYPE_NUMERIC && node->nodeId.identifier.numeric == 0) { do { /* Create a random nodeid until we find an unoccupied id */ node->nodeId.identifier.numeric = UA_UInt32_random(); dummy.nodeId.identifier.numeric = node->nodeId.identifier.numeric; dummy.nodeIdHash = UA_NodeId_hash(&node->nodeId); } while(ZIP_FIND(NodeTree, &ns->root, &dummy)); } else { dummy.nodeIdHash = UA_NodeId_hash(&node->nodeId); if(ZIP_FIND(NodeTree, &ns->root, &dummy)) { /* The nodeid exists */ deleteEntry(entry); END_CRITSECT(ns); return UA_STATUSCODE_BADNODEIDEXISTS; } } /* Copy the NodeId */ if(addedNodeId) { UA_StatusCode retval = UA_NodeId_copy(&node->nodeId, addedNodeId); if(retval != UA_STATUSCODE_GOOD) { deleteEntry(entry); END_CRITSECT(ns); return retval; } } /* Insert the node */ entry->nodeIdHash = dummy.nodeIdHash; ZIP_INSERT(NodeTree, &ns->root, entry, ZIP_FFS32(UA_UInt32_random())); END_CRITSECT(ns); return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Nodestore_replaceNode(void *nsCtx, UA_Node *node) { /* Find the node */ const UA_Node *oldNode = UA_Nodestore_getNode(nsCtx, &node->nodeId); if(!oldNode) return UA_STATUSCODE_BADNODEIDUNKNOWN; /* Test if the copy is current */ NodeEntry *entry = container_of(node, NodeEntry, nodeId); NodeEntry *oldEntry = container_of(oldNode, NodeEntry, nodeId); if(oldEntry != entry->orig) { /* The node was already updated since the copy was made */ deleteEntry(entry); UA_Nodestore_releaseNode(nsCtx, oldNode); return UA_STATUSCODE_BADINTERNALERROR; } /* Replace */ NodeMap *ns = (NodeMap*)nsCtx; BEGIN_CRITSECT(ns); ZIP_REMOVE(NodeTree, &ns->root, oldEntry); entry->nodeIdHash = oldEntry->nodeIdHash; ZIP_INSERT(NodeTree, &ns->root, entry, ZIP_RANK(entry, zipfields)); oldEntry->deleted = true; END_CRITSECT(ns); UA_Nodestore_releaseNode(nsCtx, oldNode); return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Nodestore_removeNode(void *nsCtx, const UA_NodeId *nodeId) { NodeMap *ns = (NodeMap*)nsCtx; BEGIN_CRITSECT(ns); NodeEntry dummy; dummy.nodeIdHash = UA_NodeId_hash(nodeId); dummy.nodeId = *nodeId; NodeEntry *entry = ZIP_FIND(NodeTree, &ns->root, &dummy); if(!entry) { END_CRITSECT(ns); return UA_STATUSCODE_BADNODEIDUNKNOWN; } ZIP_REMOVE(NodeTree, &ns->root, entry); entry->deleted = true; cleanupEntry(entry); END_CRITSECT(ns); return UA_STATUSCODE_GOOD; } struct VisitorData { UA_NodestoreVisitor visitor; void *visitorContext; }; static void nodeVisitor(NodeEntry *entry, void *data) { struct VisitorData *d = (struct VisitorData*)data; d->visitor(d->visitorContext, (UA_Node*)&entry->nodeId); } void UA_Nodestore_iterate(void *nsCtx, UA_NodestoreVisitor visitor, void *visitorCtx) { struct VisitorData d; d.visitor = visitor; d.visitorContext = visitorCtx; NodeMap *ns = (NodeMap*)nsCtx; BEGIN_CRITSECT(ns); ZIP_ITER(NodeTree, &ns->root, nodeVisitor, &d); END_CRITSECT(ns); } static void deleteNodeVisitor(NodeEntry *entry, void *data) { deleteEntry(entry); } /***********************/ /* Nodestore Lifecycle */ /***********************/ const UA_Boolean inPlaceEditAllowed = true; UA_StatusCode UA_Nodestore_new(void **nsCtx) { /* Allocate and initialize the nodemap */ NodeMap *nodemap = (NodeMap*)UA_malloc(sizeof(NodeMap)); if(!nodemap) return UA_STATUSCODE_BADOUTOFMEMORY; #ifdef UA_ENABLE_MULTITHREADING pthread_mutex_init(&nodemap->mutex, NULL); #endif ZIP_INIT(&nodemap->root); /* Populate the nodestore */ *nsCtx = (void*)nodemap; return UA_STATUSCODE_GOOD; } void UA_Nodestore_delete(void *nsCtx) { if (!nsCtx) return; NodeMap *ns = (NodeMap*)nsCtx; #ifdef UA_ENABLE_MULTITHREADING pthread_mutex_destroy(&ns->mutex); #endif ZIP_ITER(NodeTree, &ns->root, deleteNodeVisitor, NULL); UA_free(ns); } #endif /* UA_ENABLE_CUSTOM_NODESTORE */ /*********************************** amalgamated original file "/home/jvoe/open62541/plugins/ua_config_default.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 2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2017 (c) Julian Grothoff * Copyright 2017-2018 (c) Mark Giraud, Fraunhofer IOSB * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2017 (c) Thomas Stalder, Blue Time Concept SA * Copyright 2018 (c) Daniel Feist, Precitec GmbH & Co. KG * Copyright 2018 (c) Fabian Arndt, Root-Core * Copyright 2019 (c) Kalycito Infotech Private Limited */ /* Struct initialization works across ANSI C/C99/C++ if it is done when the * variable is first declared. Assigning values to existing structs is * heterogeneous across the three. */ static UA_INLINE UA_UInt32Range UA_UINT32RANGE(UA_UInt32 min, UA_UInt32 max) { UA_UInt32Range range = {min, max}; return range; } static UA_INLINE UA_DurationRange UA_DURATIONRANGE(UA_Duration min, UA_Duration max) { UA_DurationRange range = {min, max}; return range; } /*******************************/ /* Default Connection Settings */ /*******************************/ const UA_ConnectionConfig UA_ConnectionConfig_default = { 0, /* .protocolVersion */ 65535, /* .sendBufferSize, 64k per chunk */ 65535, /* .recvBufferSize, 64k per chunk */ 0, /* .maxMessageSize, 0 -> unlimited */ 0 /* .maxChunkCount, 0 -> unlimited */ }; /***************************/ /* Default Server Settings */ /***************************/ #define MANUFACTURER_NAME "open62541" #define PRODUCT_NAME "open62541 OPC UA Server" #define PRODUCT_URI "http://open62541.org" #define APPLICATION_NAME "open62541-based OPC UA Application" #define APPLICATION_URI "urn:unconfigured:application" #define APPLICATION_URI_SERVER "urn:open62541.server.application" #define STRINGIFY(arg) #arg #define VERSION(MAJOR, MINOR, PATCH, LABEL) \ STRINGIFY(MAJOR) "." STRINGIFY(MINOR) "." STRINGIFY(PATCH) LABEL static UA_StatusCode createEndpoint(UA_ServerConfig *conf, UA_EndpointDescription *endpoint, const UA_SecurityPolicy *securityPolicy, UA_MessageSecurityMode securityMode) { UA_EndpointDescription_init(endpoint); endpoint->securityMode = securityMode; UA_String_copy(&securityPolicy->policyUri, &endpoint->securityPolicyUri); endpoint->transportProfileUri = UA_STRING_ALLOC("http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary"); /* Add security level value for the corresponding message security mode */ endpoint->securityLevel = (UA_Byte) securityMode; /* Enable all login mechanisms from the access control plugin */ UA_StatusCode retval = UA_Array_copy(conf->accessControl.userTokenPolicies, conf->accessControl.userTokenPoliciesSize, (void **)&endpoint->userIdentityTokens, &UA_TYPES[UA_TYPES_USERTOKENPOLICY]); if(retval != UA_STATUSCODE_GOOD) return retval; endpoint->userIdentityTokensSize = conf->accessControl.userTokenPoliciesSize; UA_String_copy(&securityPolicy->localCertificate, &endpoint->serverCertificate); UA_ApplicationDescription_copy(&conf->applicationDescription, &endpoint->server); return UA_STATUSCODE_GOOD; } static const size_t usernamePasswordsSize = 2; static UA_UsernamePasswordLogin usernamePasswords[2] = { {UA_STRING_STATIC("user1"), UA_STRING_STATIC("password")}, {UA_STRING_STATIC("user2"), UA_STRING_STATIC("password1")}}; static UA_StatusCode setDefaultConfig(UA_ServerConfig *conf) { if (!conf) return UA_STATUSCODE_BADINVALIDARGUMENT; /* Zero out.. All members have a valid initial value */ UA_ServerConfig_clean(conf); memset(conf, 0, sizeof(UA_ServerConfig)); /* --> Start setting the default static config <-- */ conf->nThreads = 1; conf->logger = UA_Log_Stdout_; conf->shutdownDelay = 0.0; /* Server Description */ conf->buildInfo.productUri = UA_STRING_ALLOC(PRODUCT_URI); conf->buildInfo.manufacturerName = UA_STRING_ALLOC(MANUFACTURER_NAME); conf->buildInfo.productName = UA_STRING_ALLOC(PRODUCT_NAME); conf->buildInfo.softwareVersion = UA_STRING_ALLOC(VERSION(UA_OPEN62541_VER_MAJOR, UA_OPEN62541_VER_MINOR, UA_OPEN62541_VER_PATCH, UA_OPEN62541_VER_LABEL)); #ifdef UA_PACK_DEBIAN conf->buildInfo.buildNumber = UA_STRING_ALLOC("deb"); #else conf->buildInfo.buildNumber = UA_STRING_ALLOC(__DATE__ " " __TIME__); #endif conf->buildInfo.buildDate = UA_DateTime_now(); conf->applicationDescription.applicationUri = UA_STRING_ALLOC(APPLICATION_URI_SERVER); conf->applicationDescription.productUri = UA_STRING_ALLOC(PRODUCT_URI); conf->applicationDescription.applicationName = UA_LOCALIZEDTEXT_ALLOC("en", APPLICATION_NAME); conf->applicationDescription.applicationType = UA_APPLICATIONTYPE_SERVER; /* conf->applicationDescription.gatewayServerUri = UA_STRING_NULL; */ /* conf->applicationDescription.discoveryProfileUri = UA_STRING_NULL; */ /* conf->applicationDescription.discoveryUrlsSize = 0; */ /* conf->applicationDescription.discoveryUrls = NULL; */ #ifdef UA_ENABLE_DISCOVERY_MULTICAST UA_MdnsDiscoveryConfiguration_init(&conf->discovery.mdns); conf->discovery.mdnsInterfaceIP = UA_STRING_NULL; #endif /* Custom DataTypes */ /* conf->customDataTypesSize = 0; */ /* conf->customDataTypes = NULL; */ /* Networking */ /* conf->networkLayersSize = 0; */ /* conf->networkLayers = NULL; */ /* conf->customHostname = UA_STRING_NULL; */ /* Endpoints */ /* conf->endpoints = {0, NULL}; */ /* Certificate Verification that accepts every certificate. Can be * overwritten when the policy is specialized. */ UA_CertificateVerification_AcceptAll(&conf->certificateVerification); /* Global Node Lifecycle */ conf->nodeLifecycle.constructor = NULL; conf->nodeLifecycle.destructor = NULL; conf->nodeLifecycle.createOptionalChild = NULL; conf->nodeLifecycle.generateChildNodeId = NULL; /* Relax constraints for the InformationModel */ conf->relaxEmptyValueConstraint = true; /* Allow empty values */ /* Limits for SecureChannels */ conf->maxSecureChannels = 40; conf->maxSecurityTokenLifetime = 10 * 60 * 1000; /* 10 minutes */ /* Limits for Sessions */ conf->maxSessions = 100; conf->maxSessionTimeout = 60.0 * 60.0 * 1000.0; /* 1h */ /* Limits for Subscriptions */ conf->publishingIntervalLimits = UA_DURATIONRANGE(100.0, 3600.0 * 1000.0); conf->lifeTimeCountLimits = UA_UINT32RANGE(3, 15000); conf->keepAliveCountLimits = UA_UINT32RANGE(1, 100); conf->maxNotificationsPerPublish = 1000; conf->enableRetransmissionQueue = true; conf->maxRetransmissionQueueSize = 0; /* unlimited */ #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS conf->maxEventsPerNode = 0; /* unlimited */ #endif /* Limits for MonitoredItems */ conf->samplingIntervalLimits = UA_DURATIONRANGE(50.0, 24.0 * 3600.0 * 1000.0); conf->queueSizeLimits = UA_UINT32RANGE(1, 100); #ifdef UA_ENABLE_DISCOVERY conf->discovery.cleanupTimeout = 60 * 60; #endif #ifdef UA_ENABLE_HISTORIZING /* conf->accessHistoryDataCapability = UA_FALSE; */ /* conf->maxReturnDataValues = 0; */ /* conf->accessHistoryEventsCapability = UA_FALSE; */ /* conf->maxReturnEventValues = 0; */ /* conf->insertDataCapability = UA_FALSE; */ /* conf->insertEventCapability = UA_FALSE; */ /* conf->insertAnnotationsCapability = UA_FALSE; */ /* conf->replaceDataCapability = UA_FALSE; */ /* conf->replaceEventCapability = UA_FALSE; */ /* conf->updateDataCapability = UA_FALSE; */ /* conf->updateEventCapability = UA_FALSE; */ /* conf->deleteRawCapability = UA_FALSE; */ /* conf->deleteEventCapability = UA_FALSE; */ /* conf->deleteAtTimeDataCapability = UA_FALSE; */ #endif /* --> Finish setting the default static config <-- */ return UA_STATUSCODE_GOOD; } UA_EXPORT UA_StatusCode UA_ServerConfig_setBasics(UA_ServerConfig* conf) { return setDefaultConfig(conf); } static UA_StatusCode addDefaultNetworkLayers(UA_ServerConfig *conf, UA_UInt16 portNumber, UA_UInt32 sendBufferSize, UA_UInt32 recvBufferSize) { return UA_ServerConfig_addNetworkLayerTCP(conf, portNumber, sendBufferSize, recvBufferSize); } UA_EXPORT UA_StatusCode UA_ServerConfig_addNetworkLayerTCP(UA_ServerConfig *conf, UA_UInt16 portNumber, UA_UInt32 sendBufferSize, UA_UInt32 recvBufferSize) { /* Add a network layer */ UA_ServerNetworkLayer *tmp = (UA_ServerNetworkLayer *) UA_realloc(conf->networkLayers, sizeof(UA_ServerNetworkLayer) * (1 + conf->networkLayersSize)); if(!tmp) return UA_STATUSCODE_BADOUTOFMEMORY; conf->networkLayers = tmp; UA_ConnectionConfig config = UA_ConnectionConfig_default; if (sendBufferSize > 0) config.sendBufferSize = sendBufferSize; if (recvBufferSize > 0) config.recvBufferSize = recvBufferSize; conf->networkLayers[conf->networkLayersSize] = UA_ServerNetworkLayerTCP(config, portNumber, &conf->logger); if (!conf->networkLayers[conf->networkLayersSize].handle) return UA_STATUSCODE_BADOUTOFMEMORY; conf->networkLayersSize++; return UA_STATUSCODE_GOOD; } UA_EXPORT UA_StatusCode UA_ServerConfig_addSecurityPolicyNone(UA_ServerConfig *config, const UA_ByteString *certificate) { UA_StatusCode retval; /* Allocate the SecurityPolicies */ UA_SecurityPolicy *tmp = (UA_SecurityPolicy *) UA_realloc(config->securityPolicies, sizeof(UA_SecurityPolicy) * (1 + config->securityPoliciesSize)); if(!tmp) return UA_STATUSCODE_BADOUTOFMEMORY; config->securityPolicies = tmp; /* Populate the SecurityPolicies */ UA_ByteString localCertificate = UA_BYTESTRING_NULL; if(certificate) localCertificate = *certificate; retval = UA_SecurityPolicy_None(&config->securityPolicies[config->securityPoliciesSize], NULL, localCertificate, &config->logger); if(retval != UA_STATUSCODE_GOOD) return retval; config->securityPoliciesSize++; return UA_STATUSCODE_GOOD; } UA_EXPORT UA_StatusCode UA_ServerConfig_addEndpoint(UA_ServerConfig *config, const UA_String securityPolicyUri, UA_MessageSecurityMode securityMode) { UA_StatusCode retval; /* Allocate the endpoint */ UA_EndpointDescription * tmp = (UA_EndpointDescription *) UA_realloc(config->endpoints, sizeof(UA_EndpointDescription) * (1 + config->endpointsSize)); if(!tmp) { return UA_STATUSCODE_BADOUTOFMEMORY; } config->endpoints = tmp; /* Lookup the security policy */ const UA_SecurityPolicy *policy = NULL; for (size_t i = 0; i < config->securityPoliciesSize; ++i) { if (UA_String_equal(&securityPolicyUri, &config->securityPolicies[i].policyUri)) { policy = &config->securityPolicies[i]; break; } } if (!policy) return UA_STATUSCODE_BADINVALIDARGUMENT; /* Populate the endpoint */ retval = createEndpoint(config, &config->endpoints[config->endpointsSize], policy, securityMode); if(retval != UA_STATUSCODE_GOOD) return retval; config->endpointsSize++; return UA_STATUSCODE_GOOD; } UA_EXPORT UA_StatusCode UA_ServerConfig_addAllEndpoints(UA_ServerConfig *config) { UA_StatusCode retval; /* Allocate the endpoints */ UA_EndpointDescription * tmp = (UA_EndpointDescription *) UA_realloc(config->endpoints, sizeof(UA_EndpointDescription) * (2 * config->securityPoliciesSize + config->endpointsSize)); if(!tmp) { return UA_STATUSCODE_BADOUTOFMEMORY; } config->endpoints = tmp; /* Populate the endpoints */ for (size_t i = 0; i < config->securityPoliciesSize; ++i) { if (UA_String_equal(&UA_SECURITY_POLICY_NONE_URI, &config->securityPolicies[i].policyUri)) { retval = createEndpoint(config, &config->endpoints[config->endpointsSize], &config->securityPolicies[i], UA_MESSAGESECURITYMODE_NONE); if(retval != UA_STATUSCODE_GOOD) return retval; config->endpointsSize++; } else { retval = createEndpoint(config, &config->endpoints[config->endpointsSize], &config->securityPolicies[i], UA_MESSAGESECURITYMODE_SIGN); if(retval != UA_STATUSCODE_GOOD) return retval; config->endpointsSize++; retval = createEndpoint(config, &config->endpoints[config->endpointsSize], &config->securityPolicies[i], UA_MESSAGESECURITYMODE_SIGNANDENCRYPT); if(retval != UA_STATUSCODE_GOOD) return retval; config->endpointsSize++; } } return UA_STATUSCODE_GOOD; } UA_EXPORT UA_StatusCode UA_ServerConfig_setMinimalCustomBuffer(UA_ServerConfig *config, UA_UInt16 portNumber, const UA_ByteString *certificate, UA_UInt32 sendBufferSize, UA_UInt32 recvBufferSize) { if (!config) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_StatusCode retval = setDefaultConfig(config); if(retval != UA_STATUSCODE_GOOD) { UA_ServerConfig_clean(config); return retval; } retval = addDefaultNetworkLayers(config, portNumber, sendBufferSize, recvBufferSize); if(retval != UA_STATUSCODE_GOOD) { UA_ServerConfig_clean(config); return retval; } /* Allocate the SecurityPolicies */ retval = UA_ServerConfig_addSecurityPolicyNone(config, certificate); if(retval != UA_STATUSCODE_GOOD) { UA_ServerConfig_clean(config); return retval; } /* Initialize the Access Control plugin */ retval = UA_AccessControl_default(config, true, &config->securityPolicies[config->securityPoliciesSize-1].policyUri, usernamePasswordsSize, usernamePasswords); if(retval != UA_STATUSCODE_GOOD) { UA_ServerConfig_clean(config); return retval; } /* Allocate the endpoint */ retval = UA_ServerConfig_addEndpoint(config, UA_SECURITY_POLICY_NONE_URI, UA_MESSAGESECURITYMODE_NONE); if(retval != UA_STATUSCODE_GOOD) { UA_ServerConfig_clean(config); return retval; } return UA_STATUSCODE_GOOD; } #ifdef UA_ENABLE_ENCRYPTION UA_EXPORT UA_StatusCode UA_ServerConfig_addSecurityPolicyBasic128Rsa15(UA_ServerConfig *config, const UA_ByteString *certificate, const UA_ByteString *privateKey) { UA_StatusCode retval; /* Allocate the SecurityPolicies */ UA_SecurityPolicy *tmp = (UA_SecurityPolicy *) UA_realloc(config->securityPolicies, sizeof(UA_SecurityPolicy) * (1 + config->securityPoliciesSize)); if(!tmp) return UA_STATUSCODE_BADOUTOFMEMORY; config->securityPolicies = tmp; /* Populate the SecurityPolicies */ UA_ByteString localCertificate = UA_BYTESTRING_NULL; UA_ByteString localPrivateKey = UA_BYTESTRING_NULL; if(certificate) localCertificate = *certificate; if(privateKey) localPrivateKey = *privateKey; retval = UA_SecurityPolicy_Basic128Rsa15(&config->securityPolicies[config->securityPoliciesSize], &config->certificateVerification, localCertificate, localPrivateKey, &config->logger); if(retval != UA_STATUSCODE_GOOD) return retval; config->securityPoliciesSize++; return UA_STATUSCODE_GOOD; } UA_EXPORT UA_StatusCode UA_ServerConfig_addSecurityPolicyBasic256(UA_ServerConfig *config, const UA_ByteString *certificate, const UA_ByteString *privateKey) { UA_StatusCode retval; /* Allocate the SecurityPolicies */ UA_SecurityPolicy *tmp = (UA_SecurityPolicy *) UA_realloc(config->securityPolicies, sizeof(UA_SecurityPolicy) * (1 + config->securityPoliciesSize)); if(!tmp) return UA_STATUSCODE_BADOUTOFMEMORY; config->securityPolicies = tmp; /* Populate the SecurityPolicies */ UA_ByteString localCertificate = UA_BYTESTRING_NULL; UA_ByteString localPrivateKey = UA_BYTESTRING_NULL; if(certificate) localCertificate = *certificate; if(privateKey) localPrivateKey = *privateKey; retval = UA_SecurityPolicy_Basic256(&config->securityPolicies[config->securityPoliciesSize], &config->certificateVerification, localCertificate, localPrivateKey, &config->logger); if(retval != UA_STATUSCODE_GOOD) return retval; config->securityPoliciesSize++; return UA_STATUSCODE_GOOD; } UA_EXPORT UA_StatusCode UA_ServerConfig_addSecurityPolicyBasic256Sha256(UA_ServerConfig *config, const UA_ByteString *certificate, const UA_ByteString *privateKey) { UA_StatusCode retval; /* Allocate the SecurityPolicies */ UA_SecurityPolicy *tmp = (UA_SecurityPolicy *) UA_realloc(config->securityPolicies, sizeof(UA_SecurityPolicy) * (1 + config->securityPoliciesSize)); if(!tmp) return UA_STATUSCODE_BADOUTOFMEMORY; config->securityPolicies = tmp; /* Populate the SecurityPolicies */ UA_ByteString localCertificate = UA_BYTESTRING_NULL; UA_ByteString localPrivateKey = UA_BYTESTRING_NULL; if(certificate) localCertificate = *certificate; if(privateKey) localPrivateKey = *privateKey; retval = UA_SecurityPolicy_Basic256Sha256(&config->securityPolicies[config->securityPoliciesSize], &config->certificateVerification, localCertificate, localPrivateKey, &config->logger); if(retval != UA_STATUSCODE_GOOD) return retval; config->securityPoliciesSize++; return UA_STATUSCODE_GOOD; } UA_EXPORT UA_StatusCode UA_ServerConfig_addAllSecurityPolicies(UA_ServerConfig *config, const UA_ByteString *certificate, const UA_ByteString *privateKey) { UA_StatusCode retval; /* Allocate the SecurityPolicies */ UA_SecurityPolicy *tmp = (UA_SecurityPolicy *) UA_realloc(config->securityPolicies, sizeof(UA_SecurityPolicy) * (4 + config->securityPoliciesSize)); if(!tmp) return UA_STATUSCODE_BADOUTOFMEMORY; config->securityPolicies = tmp; /* Populate the SecurityPolicies */ UA_ByteString localCertificate = UA_BYTESTRING_NULL; UA_ByteString localPrivateKey = UA_BYTESTRING_NULL; if(certificate) localCertificate = *certificate; if(privateKey) localPrivateKey = *privateKey; retval = UA_SecurityPolicy_None(&config->securityPolicies[config->securityPoliciesSize], NULL, localCertificate, &config->logger); if(retval != UA_STATUSCODE_GOOD) return retval; config->securityPoliciesSize++; retval = UA_SecurityPolicy_Basic128Rsa15(&config->securityPolicies[config->securityPoliciesSize], &config->certificateVerification, localCertificate, localPrivateKey, &config->logger); if(retval != UA_STATUSCODE_GOOD) return retval; config->securityPoliciesSize++; retval = UA_SecurityPolicy_Basic256(&config->securityPolicies[config->securityPoliciesSize], &config->certificateVerification, localCertificate, localPrivateKey, &config->logger); if(retval != UA_STATUSCODE_GOOD) return retval; config->securityPoliciesSize++; retval = UA_SecurityPolicy_Basic256Sha256(&config->securityPolicies[config->securityPoliciesSize], &config->certificateVerification, localCertificate, localPrivateKey, &config->logger); if(retval != UA_STATUSCODE_GOOD) return retval; config->securityPoliciesSize++; return retval; } UA_EXPORT UA_StatusCode UA_ServerConfig_setDefaultWithSecurityPolicies(UA_ServerConfig *conf, UA_UInt16 portNumber, const UA_ByteString *certificate, const UA_ByteString *privateKey, const UA_ByteString *trustList, size_t trustListSize, const UA_ByteString *issuerList, size_t issuerListSize, const UA_ByteString *revocationList, size_t revocationListSize) { UA_StatusCode retval = setDefaultConfig(conf); if(retval != UA_STATUSCODE_GOOD) { UA_ServerConfig_clean(conf); return retval; } retval = UA_CertificateVerification_Trustlist(&conf->certificateVerification, trustList, trustListSize, issuerList, issuerListSize, revocationList, revocationListSize); if (retval != UA_STATUSCODE_GOOD) return retval; if(trustListSize == 0) UA_LOG_WARNING(&conf->logger, UA_LOGCATEGORY_USERLAND, "No CA trust-list provided. " "Any remote certificate will be accepted."); retval = addDefaultNetworkLayers(conf, portNumber, 0, 0); if(retval != UA_STATUSCODE_GOOD) { UA_ServerConfig_clean(conf); return retval; } retval = UA_ServerConfig_addAllSecurityPolicies(conf, certificate, privateKey); if(retval != UA_STATUSCODE_GOOD) { UA_ServerConfig_clean(conf); return retval; } retval = UA_AccessControl_default(conf, true, &conf->securityPolicies[conf->securityPoliciesSize-1].policyUri, usernamePasswordsSize, usernamePasswords); if(retval != UA_STATUSCODE_GOOD) { UA_ServerConfig_clean(conf); return retval; } retval = UA_ServerConfig_addAllEndpoints(conf); if(retval != UA_STATUSCODE_GOOD) { UA_ServerConfig_clean(conf); return retval; } return UA_STATUSCODE_GOOD; } #endif /***************************/ /* Default Client Settings */ /***************************/ static UA_INLINE void UA_ClientConnectionTCP_poll_callback(UA_Client *client, void *data) { UA_ClientConnectionTCP_poll(client, data); } UA_StatusCode UA_ClientConfig_setDefault(UA_ClientConfig *config) { config->timeout = 5000; config->secureChannelLifeTime = 10 * 60 * 1000; /* 10 minutes */ config->logger.log = UA_Log_Stdout_log; config->logger.context = NULL; config->logger.clear = UA_Log_Stdout_clear; config->localConnectionConfig = UA_ConnectionConfig_default; /* Certificate Verification that accepts every certificate. Can be * overwritten when the policy is specialized. */ UA_CertificateVerification_AcceptAll(&config->certificateVerification); /* With encryption enabled, the applicationUri needs to match the URI from * the certificate */ config->clientDescription.applicationUri = UA_STRING_ALLOC(APPLICATION_URI); config->clientDescription.applicationType = UA_APPLICATIONTYPE_CLIENT; if(config->securityPoliciesSize > 0) { UA_LOG_ERROR(&config->logger, UA_LOGCATEGORY_NETWORK, "Could not initialize a config that already has SecurityPolicies"); return UA_STATUSCODE_BADINTERNALERROR; } config->securityPolicies = (UA_SecurityPolicy*)UA_malloc(sizeof(UA_SecurityPolicy)); if(!config->securityPolicies) return UA_STATUSCODE_BADOUTOFMEMORY; UA_StatusCode retval = UA_SecurityPolicy_None(config->securityPolicies, NULL, UA_BYTESTRING_NULL, &config->logger); if(retval != UA_STATUSCODE_GOOD) { UA_free(config->securityPolicies); config->securityPolicies = NULL; return retval; } config->securityPoliciesSize = 1; config->connectionFunc = UA_ClientConnectionTCP; config->initConnectionFunc = UA_ClientConnectionTCP_init; /* for async client */ config->pollConnectionFunc = UA_ClientConnectionTCP_poll_callback; /* for async connection */ config->customDataTypes = NULL; config->stateCallback = NULL; config->connectivityCheckInterval = 0; config->requestedSessionTimeout = 1200000; /* requestedSessionTimeout */ config->inactivityCallback = NULL; config->clientContext = NULL; #ifdef UA_ENABLE_SUBSCRIPTIONS config->outStandingPublishRequests = 10; config->subscriptionInactivityCallback = NULL; #endif return UA_STATUSCODE_GOOD; } #ifdef UA_ENABLE_ENCRYPTION UA_StatusCode UA_ClientConfig_setDefaultEncryption(UA_ClientConfig *config, UA_ByteString localCertificate, UA_ByteString privateKey, const UA_ByteString *trustList, size_t trustListSize, const UA_ByteString *revocationList, size_t revocationListSize) { UA_StatusCode retval = UA_ClientConfig_setDefault(config); if(retval != UA_STATUSCODE_GOOD) return retval; retval = UA_CertificateVerification_Trustlist(&config->certificateVerification, trustList, trustListSize, NULL, 0, revocationList, revocationListSize); if(retval != UA_STATUSCODE_GOOD) return retval; /* Populate SecurityPolicies */ UA_SecurityPolicy *sp = (UA_SecurityPolicy*) UA_realloc(config->securityPolicies, sizeof(UA_SecurityPolicy) * 4); if(!sp) return UA_STATUSCODE_BADOUTOFMEMORY; config->securityPolicies = sp; retval = UA_SecurityPolicy_Basic128Rsa15(&config->securityPolicies[1], &config->certificateVerification, localCertificate, privateKey, &config->logger); if(retval != UA_STATUSCODE_GOOD) return retval; ++config->securityPoliciesSize; retval = UA_SecurityPolicy_Basic256(&config->securityPolicies[2], &config->certificateVerification, localCertificate, privateKey, &config->logger); if(retval != UA_STATUSCODE_GOOD) return retval; ++config->securityPoliciesSize; retval = UA_SecurityPolicy_Basic256Sha256(&config->securityPolicies[3], &config->certificateVerification, localCertificate, privateKey, &config->logger); if(retval != UA_STATUSCODE_GOOD) return retval; ++config->securityPoliciesSize; return UA_STATUSCODE_GOOD; } #endif /*********************************** amalgamated original file "/home/jvoe/open62541/plugins/securityPolicies/ua_securitypolicy_none.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 2017-2018 (c) Mark Giraud, Fraunhofer IOSB * Copyright 2017 (c) Stefan Profanter, fortiss GmbH */ static UA_StatusCode verify_none(const UA_SecurityPolicy *securityPolicy, void *channelContext, const UA_ByteString *message, const UA_ByteString *signature) { return UA_STATUSCODE_GOOD; } static UA_StatusCode sign_none(const UA_SecurityPolicy *securityPolicy, void *channelContext, const UA_ByteString *message, UA_ByteString *signature) { return UA_STATUSCODE_GOOD; } static size_t length_none(const UA_SecurityPolicy *securityPolicy, const void *channelContext) { return 0; } static UA_StatusCode encrypt_none(const UA_SecurityPolicy *securityPolicy, void *channelContext, UA_ByteString *data) { return UA_STATUSCODE_GOOD; } static UA_StatusCode decrypt_none(const UA_SecurityPolicy *securityPolicy, void *channelContext, UA_ByteString *data) { return UA_STATUSCODE_GOOD; } static UA_StatusCode makeThumbprint_none(const UA_SecurityPolicy *securityPolicy, const UA_ByteString *certificate, UA_ByteString *thumbprint) { return UA_STATUSCODE_GOOD; } static UA_StatusCode compareThumbprint_none(const UA_SecurityPolicy *securityPolicy, const UA_ByteString *certificateThumbprint) { return UA_STATUSCODE_GOOD; } static UA_StatusCode generateKey_none(const UA_SecurityPolicy *securityPolicy, const UA_ByteString *secret, const UA_ByteString *seed, UA_ByteString *out) { return UA_STATUSCODE_GOOD; } /* Use the non-cryptographic RNG to set the nonce */ static UA_StatusCode generateNonce_none(const UA_SecurityPolicy *securityPolicy, UA_ByteString *out) { if(securityPolicy == NULL || out == NULL) return UA_STATUSCODE_BADINTERNALERROR; if(out->length == 0) return UA_STATUSCODE_GOOD; /* Fill blocks of four byte */ size_t i = 0; while(i + 3 < out->length) { UA_UInt32 rand = UA_UInt32_random(); memcpy(&out->data[i], &rand, 4); i = i+4; } /* Fill the remaining byte */ UA_UInt32 rand = UA_UInt32_random(); memcpy(&out->data[i], &rand, out->length % 4); return UA_STATUSCODE_GOOD; } static UA_StatusCode newContext_none(const UA_SecurityPolicy *securityPolicy, const UA_ByteString *remoteCertificate, void **channelContext) { return UA_STATUSCODE_GOOD; } static void deleteContext_none(void *channelContext) { } static UA_StatusCode setContextValue_none(void *channelContext, const UA_ByteString *key) { return UA_STATUSCODE_GOOD; } static UA_StatusCode compareCertificate_none(const void *channelContext, const UA_ByteString *certificate) { return UA_STATUSCODE_GOOD; } static UA_StatusCode updateCertificateAndPrivateKey_none(UA_SecurityPolicy *policy, const UA_ByteString newCertificate, const UA_ByteString newPrivateKey) { UA_ByteString_deleteMembers(&policy->localCertificate); UA_ByteString_copy(&newCertificate, &policy->localCertificate); return UA_STATUSCODE_GOOD; } static void policy_deletemembers_none(UA_SecurityPolicy *policy) { UA_ByteString_deleteMembers(&policy->localCertificate); } UA_StatusCode UA_SecurityPolicy_None(UA_SecurityPolicy *policy, UA_CertificateVerification *certificateVerification, const UA_ByteString localCertificate, const UA_Logger *logger) { policy->policyContext = (void *)(uintptr_t)logger; policy->policyUri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None"); policy->logger = logger; UA_ByteString_copy(&localCertificate, &policy->localCertificate); policy->certificateVerification = certificateVerification; policy->symmetricModule.generateKey = generateKey_none; policy->symmetricModule.generateNonce = generateNonce_none; UA_SecurityPolicySignatureAlgorithm *sym_signatureAlgorithm = &policy->symmetricModule.cryptoModule.signatureAlgorithm; sym_signatureAlgorithm->uri = UA_STRING_NULL; sym_signatureAlgorithm->verify = verify_none; sym_signatureAlgorithm->sign = sign_none; sym_signatureAlgorithm->getLocalSignatureSize = length_none; sym_signatureAlgorithm->getRemoteSignatureSize = length_none; sym_signatureAlgorithm->getLocalKeyLength = length_none; sym_signatureAlgorithm->getRemoteKeyLength = length_none; UA_SecurityPolicyEncryptionAlgorithm *sym_encryptionAlgorithm = &policy->symmetricModule.cryptoModule.encryptionAlgorithm; sym_encryptionAlgorithm->encrypt = encrypt_none; sym_encryptionAlgorithm->decrypt = decrypt_none; sym_encryptionAlgorithm->getLocalKeyLength = length_none; sym_encryptionAlgorithm->getRemoteKeyLength = length_none; sym_encryptionAlgorithm->getLocalBlockSize = length_none; sym_encryptionAlgorithm->getRemoteBlockSize = length_none; sym_encryptionAlgorithm->getLocalPlainTextBlockSize = length_none; sym_encryptionAlgorithm->getRemotePlainTextBlockSize = length_none; policy->symmetricModule.secureChannelNonceLength = 0; policy->asymmetricModule.makeCertificateThumbprint = makeThumbprint_none; policy->asymmetricModule.compareCertificateThumbprint = compareThumbprint_none; // This only works for none since symmetric and asymmetric crypto modules do the same i.e. nothing policy->asymmetricModule.cryptoModule = policy->symmetricModule.cryptoModule; // Use the same signing algorithm as for asymmetric signing policy->certificateSigningAlgorithm = policy->asymmetricModule.cryptoModule.signatureAlgorithm; policy->channelModule.newContext = newContext_none; policy->channelModule.deleteContext = deleteContext_none; policy->channelModule.setLocalSymEncryptingKey = setContextValue_none; policy->channelModule.setLocalSymSigningKey = setContextValue_none; policy->channelModule.setLocalSymIv = setContextValue_none; policy->channelModule.setRemoteSymEncryptingKey = setContextValue_none; policy->channelModule.setRemoteSymSigningKey = setContextValue_none; policy->channelModule.setRemoteSymIv = setContextValue_none; policy->channelModule.compareCertificate = compareCertificate_none; policy->updateCertificateAndPrivateKey = updateCertificateAndPrivateKey_none; policy->deleteMembers = policy_deletemembers_none; return UA_STATUSCODE_GOOD; } /*********************************** amalgamated original file "/home/jvoe/open62541/plugins/securityPolicies/securitypolicy_mbedtls_common.c" ***********************************/ #ifdef UA_ENABLE_ENCRYPTION #include #include #include #include #include #include #include #include #include void swapBuffers(UA_ByteString *const bufA, UA_ByteString *const bufB) { UA_ByteString tmp = *bufA; *bufA = *bufB; *bufB = tmp; } void mbedtls_hmac(mbedtls_md_context_t *context, const UA_ByteString *key, const UA_ByteString *in, unsigned char *out) { mbedtls_md_hmac_starts(context, key->data, key->length); mbedtls_md_hmac_update(context, in->data, in->length); mbedtls_md_hmac_finish(context, out); } UA_StatusCode mbedtls_generateKey(mbedtls_md_context_t *context, const UA_ByteString *secret, const UA_ByteString *seed, UA_ByteString *out) { size_t hashLen = (size_t)mbedtls_md_get_size(context->md_info); UA_ByteString A_and_seed; UA_ByteString_allocBuffer(&A_and_seed, hashLen + seed->length); memcpy(A_and_seed.data + hashLen, seed->data, seed->length); UA_ByteString ANext_and_seed; UA_ByteString_allocBuffer(&ANext_and_seed, hashLen + seed->length); memcpy(ANext_and_seed.data + hashLen, seed->data, seed->length); UA_ByteString A = { hashLen, A_and_seed.data }; UA_ByteString ANext = { hashLen, ANext_and_seed.data }; mbedtls_hmac(context, secret, seed, A.data); UA_StatusCode retval = 0; for(size_t offset = 0; offset < out->length; offset += hashLen) { UA_ByteString outSegment = { hashLen, out->data + offset }; UA_Boolean bufferAllocated = UA_FALSE; // Not enough room in out buffer to write the hash. if(offset + hashLen > out->length) { outSegment.data = NULL; outSegment.length = 0; retval = UA_ByteString_allocBuffer(&outSegment, hashLen); if(retval != UA_STATUSCODE_GOOD) { UA_ByteString_deleteMembers(&A_and_seed); UA_ByteString_deleteMembers(&ANext_and_seed); return retval; } bufferAllocated = UA_TRUE; } mbedtls_hmac(context, secret, &A_and_seed, outSegment.data); mbedtls_hmac(context, secret, &A, ANext.data); if(retval != UA_STATUSCODE_GOOD) { if(bufferAllocated) UA_ByteString_deleteMembers(&outSegment); UA_ByteString_deleteMembers(&A_and_seed); UA_ByteString_deleteMembers(&ANext_and_seed); return retval; } if(bufferAllocated) { memcpy(out->data + offset, outSegment.data, out->length - offset); UA_ByteString_deleteMembers(&outSegment); } swapBuffers(&ANext_and_seed, &A_and_seed); swapBuffers(&ANext, &A); } UA_ByteString_deleteMembers(&A_and_seed); UA_ByteString_deleteMembers(&ANext_and_seed); return UA_STATUSCODE_GOOD; } UA_StatusCode mbedtls_verifySig_sha1(mbedtls_x509_crt *certificate, const UA_ByteString *message, const UA_ByteString *signature) { /* Compute the sha1 hash */ unsigned char hash[UA_SHA1_LENGTH]; #if MBEDTLS_VERSION_NUMBER >= 0x02070000 mbedtls_sha1_ret(message->data, message->length, hash); #else mbedtls_sha1(message->data, message->length, hash); #endif /* Set the RSA settings */ mbedtls_rsa_context *rsaContext = mbedtls_pk_rsa(certificate->pk); if(!rsaContext) return UA_STATUSCODE_BADINTERNALERROR; mbedtls_rsa_set_padding(rsaContext, MBEDTLS_RSA_PKCS_V15, 0); /* Verify */ int mbedErr = mbedtls_pk_verify(&certificate->pk, MBEDTLS_MD_SHA1, hash, UA_SHA1_LENGTH, signature->data, signature->length); if(mbedErr) return UA_STATUSCODE_BADSECURITYCHECKSFAILED; return UA_STATUSCODE_GOOD; } UA_StatusCode mbedtls_sign_sha1(mbedtls_pk_context *localPrivateKey, mbedtls_ctr_drbg_context *drbgContext, const UA_ByteString *message, UA_ByteString *signature) { unsigned char hash[UA_SHA1_LENGTH]; #if MBEDTLS_VERSION_NUMBER >= 0x02070000 mbedtls_sha1_ret(message->data, message->length, hash); #else mbedtls_sha1(message->data, message->length, hash); #endif mbedtls_rsa_context *rsaContext = mbedtls_pk_rsa(*localPrivateKey); mbedtls_rsa_set_padding(rsaContext, MBEDTLS_RSA_PKCS_V15, 0); size_t sigLen = 0; int mbedErr = mbedtls_pk_sign(localPrivateKey, MBEDTLS_MD_SHA1, hash, UA_SHA1_LENGTH, signature->data, &sigLen, mbedtls_ctr_drbg_random, drbgContext); if(mbedErr) return UA_STATUSCODE_BADINTERNALERROR; return UA_STATUSCODE_GOOD; } UA_StatusCode mbedtls_thumbprint_sha1(const UA_ByteString *certificate, UA_ByteString *thumbprint) { if(UA_ByteString_equal(certificate, &UA_BYTESTRING_NULL)) return UA_STATUSCODE_BADINTERNALERROR; if(thumbprint->length != UA_SHA1_LENGTH) return UA_STATUSCODE_BADINTERNALERROR; /* The certificate thumbprint is always a 20 bit sha1 hash, see Part 4 of the Specification. */ #if MBEDTLS_VERSION_NUMBER >= 0x02070000 mbedtls_sha1_ret(certificate->data, certificate->length, thumbprint->data); #else mbedtls_sha1(certificate->data, certificate->length, thumbprint->data); #endif return UA_STATUSCODE_GOOD; } UA_StatusCode mbedtls_encrypt_rsaOaep(mbedtls_rsa_context *context, mbedtls_ctr_drbg_context *drbgContext, UA_ByteString *data, const size_t plainTextBlockSize) { if(data->length % plainTextBlockSize != 0) return UA_STATUSCODE_BADINTERNALERROR; size_t max_blocks = data->length / plainTextBlockSize; UA_ByteString encrypted; UA_StatusCode retval = UA_ByteString_allocBuffer(&encrypted, max_blocks * context->len); if(retval != UA_STATUSCODE_GOOD) return retval; size_t lenDataToEncrypt = data->length; size_t inOffset = 0; size_t offset = 0; const unsigned char *label = NULL; while(lenDataToEncrypt >= plainTextBlockSize) { int mbedErr = mbedtls_rsa_rsaes_oaep_encrypt(context, mbedtls_ctr_drbg_random, drbgContext, MBEDTLS_RSA_PUBLIC, label, 0, plainTextBlockSize, data->data + inOffset, encrypted.data + offset); if(mbedErr) { UA_ByteString_deleteMembers(&encrypted); return UA_STATUSCODE_BADINTERNALERROR; } inOffset += plainTextBlockSize; offset += context->len; lenDataToEncrypt -= plainTextBlockSize; } memcpy(data->data, encrypted.data, offset); UA_ByteString_deleteMembers(&encrypted); return UA_STATUSCODE_GOOD; } UA_StatusCode mbedtls_decrypt_rsaOaep(mbedtls_pk_context *localPrivateKey, mbedtls_ctr_drbg_context *drbgContext, UA_ByteString *data) { mbedtls_rsa_context *rsaContext = mbedtls_pk_rsa(*localPrivateKey); mbedtls_rsa_set_padding(rsaContext, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA1); if(data->length % rsaContext->len != 0) return UA_STATUSCODE_BADINTERNALERROR; size_t inOffset = 0; size_t outOffset = 0; size_t outLength = 0; unsigned char buf[512]; while(inOffset < data->length) { int mbedErr = mbedtls_rsa_rsaes_oaep_decrypt(rsaContext, mbedtls_ctr_drbg_random, drbgContext, MBEDTLS_RSA_PRIVATE, NULL, 0, &outLength, data->data + inOffset, buf, 512); if(mbedErr) return UA_STATUSCODE_BADSECURITYCHECKSFAILED; memcpy(data->data + outOffset, buf, outLength); inOffset += rsaContext->len; outOffset += outLength; } data->length = outOffset; return UA_STATUSCODE_GOOD; } #endif /*********************************** amalgamated original file "/home/jvoe/open62541/plugins/securityPolicies/ua_securitypolicy_basic128rsa15.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2018 (c) Mark Giraud, Fraunhofer IOSB * Copyright 2019 (c) Kalycito Infotech Private Limited * */ #ifdef UA_ENABLE_ENCRYPTION #include #include #include #include #include #include #include #include #include /* Notes: * mbedTLS' AES allows in-place encryption and decryption. Sow we don't have to * allocate temp buffers. * https://tls.mbed.org/discussions/generic/in-place-decryption-with-aes256-same-input-output-buffer */ #define UA_SECURITYPOLICY_BASIC128RSA15_RSAPADDING_LEN 11 #define UA_SECURITYPOLICY_BASIC128RSA15_SYM_KEY_LENGTH 16 #define UA_BASIC128RSA15_SYM_SIGNING_KEY_LENGTH 16 #define UA_SECURITYPOLICY_BASIC128RSA15_SYM_ENCRYPTION_BLOCK_SIZE 16 #define UA_SECURITYPOLICY_BASIC128RSA15_SYM_PLAIN_TEXT_BLOCK_SIZE 16 #define UA_SECURITYPOLICY_BASIC128RSA15_MINASYMKEYLENGTH 128 #define UA_SECURITYPOLICY_BASIC128RSA15_MAXASYMKEYLENGTH 512 typedef struct { const UA_SecurityPolicy *securityPolicy; UA_ByteString localCertThumbprint; mbedtls_ctr_drbg_context drbgContext; mbedtls_entropy_context entropyContext; mbedtls_md_context_t sha1MdContext; mbedtls_pk_context localPrivateKey; } Basic128Rsa15_PolicyContext; typedef struct { Basic128Rsa15_PolicyContext *policyContext; UA_ByteString localSymSigningKey; UA_ByteString localSymEncryptingKey; UA_ByteString localSymIv; UA_ByteString remoteSymSigningKey; UA_ByteString remoteSymEncryptingKey; UA_ByteString remoteSymIv; mbedtls_x509_crt remoteCertificate; } Basic128Rsa15_ChannelContext; /********************/ /* AsymmetricModule */ /********************/ static UA_StatusCode asym_verify_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy, Basic128Rsa15_ChannelContext *cc, const UA_ByteString *message, const UA_ByteString *signature) { if(securityPolicy == NULL || message == NULL || signature == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; return mbedtls_verifySig_sha1(&cc->remoteCertificate, message, signature); } static UA_StatusCode asym_sign_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy, Basic128Rsa15_ChannelContext *cc, const UA_ByteString *message, UA_ByteString *signature) { if(securityPolicy == NULL || message == NULL || signature == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; Basic128Rsa15_PolicyContext *pc = cc->policyContext; return mbedtls_sign_sha1(&pc->localPrivateKey, &pc->drbgContext, message, signature); } static size_t asym_getLocalSignatureSize_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy, const Basic128Rsa15_ChannelContext *cc) { if(securityPolicy == NULL || cc == NULL) return 0; return mbedtls_pk_rsa(cc->policyContext->localPrivateKey)->len; } static size_t asym_getRemoteSignatureSize_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy, const Basic128Rsa15_ChannelContext *cc) { if(securityPolicy == NULL || cc == NULL) return 0; return mbedtls_pk_rsa(cc->remoteCertificate.pk)->len; } static UA_StatusCode asym_encrypt_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy, Basic128Rsa15_ChannelContext *cc, UA_ByteString *data) { if(securityPolicy == NULL || cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; const size_t plainTextBlockSize = securityPolicy->asymmetricModule.cryptoModule.encryptionAlgorithm. getRemotePlainTextBlockSize(securityPolicy, cc); if(data->length % plainTextBlockSize != 0) return UA_STATUSCODE_BADINTERNALERROR; mbedtls_rsa_context *remoteRsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); mbedtls_rsa_set_padding(remoteRsaContext, MBEDTLS_RSA_PKCS_V15, 0); UA_ByteString encrypted; const size_t bufferOverhead = UA_SecurityPolicy_getRemoteAsymEncryptionBufferLengthOverhead(securityPolicy, cc, data->length); UA_StatusCode retval = UA_ByteString_allocBuffer(&encrypted, data->length + bufferOverhead); if(retval != UA_STATUSCODE_GOOD) return retval; size_t lenDataToEncrypt = data->length; size_t inOffset = 0; size_t offset = 0; size_t outLength = 0; Basic128Rsa15_PolicyContext *pc = cc->policyContext; while(lenDataToEncrypt >= plainTextBlockSize) { int mbedErr = mbedtls_pk_encrypt(&cc->remoteCertificate.pk, data->data + inOffset, plainTextBlockSize, encrypted.data + offset, &outLength, encrypted.length - offset, mbedtls_ctr_drbg_random, &pc->drbgContext); if(mbedErr) { UA_ByteString_deleteMembers(&encrypted); return UA_STATUSCODE_BADINTERNALERROR; } inOffset += plainTextBlockSize; offset += outLength; lenDataToEncrypt -= plainTextBlockSize; } memcpy(data->data, encrypted.data, offset); UA_ByteString_deleteMembers(&encrypted); return UA_STATUSCODE_GOOD; } static UA_StatusCode asym_decrypt_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy, Basic128Rsa15_ChannelContext *cc, UA_ByteString *data) { if(securityPolicy == NULL || cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; mbedtls_rsa_context *rsaContext = mbedtls_pk_rsa(cc->policyContext->localPrivateKey); mbedtls_rsa_set_padding(rsaContext, MBEDTLS_RSA_PKCS_V15, 0); if(data->length % rsaContext->len != 0) return UA_STATUSCODE_BADINTERNALERROR; size_t inOffset = 0; size_t outOffset = 0; size_t outLength = 0; unsigned char buf[512]; while(inOffset < data->length) { int mbedErr = mbedtls_pk_decrypt(&cc->policyContext->localPrivateKey, data->data + inOffset, rsaContext->len, buf, &outLength, 512, NULL, NULL); if(mbedErr) return UA_STATUSCODE_BADSECURITYCHECKSFAILED; memcpy(data->data + outOffset, buf, outLength); inOffset += rsaContext->len; outOffset += outLength; } data->length = outOffset; return UA_STATUSCODE_GOOD; } static size_t asym_getRemoteEncryptionKeyLength_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy, const Basic128Rsa15_ChannelContext *cc) { return mbedtls_pk_get_len(&cc->remoteCertificate.pk) * 8; } static size_t asym_getRemoteBlockSize_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy, const Basic128Rsa15_ChannelContext *cc) { mbedtls_rsa_context *const rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); return rsaContext->len; } static size_t asym_getRemotePlainTextBlockSize_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy, const Basic128Rsa15_ChannelContext *cc) { mbedtls_rsa_context *const rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); return rsaContext->len - UA_SECURITYPOLICY_BASIC128RSA15_RSAPADDING_LEN; } static UA_StatusCode asym_makeThumbprint_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy, const UA_ByteString *certificate, UA_ByteString *thumbprint) { if(securityPolicy == NULL || certificate == NULL || thumbprint == NULL) return UA_STATUSCODE_BADINTERNALERROR; return mbedtls_thumbprint_sha1(certificate, thumbprint); } static UA_StatusCode asymmetricModule_compareCertificateThumbprint_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy, const UA_ByteString *certificateThumbprint) { if(securityPolicy == NULL || certificateThumbprint == NULL) return UA_STATUSCODE_BADINTERNALERROR; Basic128Rsa15_PolicyContext *pc = (Basic128Rsa15_PolicyContext *)securityPolicy->policyContext; if(!UA_ByteString_equal(certificateThumbprint, &pc->localCertThumbprint)) return UA_STATUSCODE_BADCERTIFICATEINVALID; return UA_STATUSCODE_GOOD; } /*******************/ /* SymmetricModule */ /*******************/ static UA_StatusCode sym_verify_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy, Basic128Rsa15_ChannelContext *cc, const UA_ByteString *message, const UA_ByteString *signature) { if(securityPolicy == NULL || cc == NULL || message == NULL || signature == NULL) return UA_STATUSCODE_BADINTERNALERROR; /* Compute MAC */ if(signature->length != UA_SHA1_LENGTH) { UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Signature size does not have the desired size defined by the security policy"); return UA_STATUSCODE_BADSECURITYCHECKSFAILED; } Basic128Rsa15_PolicyContext *pc = (Basic128Rsa15_PolicyContext *)securityPolicy->policyContext; unsigned char mac[UA_SHA1_LENGTH]; mbedtls_hmac(&pc->sha1MdContext, &cc->remoteSymSigningKey, message, mac); /* Compare with Signature */ if(!UA_constantTimeEqual(signature->data, mac, UA_SHA1_LENGTH)) return UA_STATUSCODE_BADSECURITYCHECKSFAILED; return UA_STATUSCODE_GOOD; } static UA_StatusCode sym_sign_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy, const Basic128Rsa15_ChannelContext *cc, const UA_ByteString *message, UA_ByteString *signature) { if(signature->length != UA_SHA1_LENGTH) return UA_STATUSCODE_BADINTERNALERROR; mbedtls_hmac(&cc->policyContext->sha1MdContext, &cc->localSymSigningKey, message, signature->data); return UA_STATUSCODE_GOOD; } static size_t sym_getSignatureSize_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy, const void *channelContext) { return UA_SHA1_LENGTH; } static size_t sym_getSigningKeyLength_sp_basic128rsa15(const UA_SecurityPolicy *const securityPolicy, const void *const channelContext) { return UA_BASIC128RSA15_SYM_SIGNING_KEY_LENGTH; } static size_t sym_getEncryptionKeyLength_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy, const void *channelContext) { return UA_SECURITYPOLICY_BASIC128RSA15_SYM_KEY_LENGTH; } static size_t sym_getEncryptionBlockSize_sp_basic128rsa15(const UA_SecurityPolicy *const securityPolicy, const void *const channelContext) { return UA_SECURITYPOLICY_BASIC128RSA15_SYM_ENCRYPTION_BLOCK_SIZE; } static size_t sym_getPlainTextBlockSize_sp_basic128rsa15(const UA_SecurityPolicy *const securityPolicy, const void *const channelContext) { return UA_SECURITYPOLICY_BASIC128RSA15_SYM_PLAIN_TEXT_BLOCK_SIZE; } static UA_StatusCode sym_encrypt_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy, const Basic128Rsa15_ChannelContext *cc, UA_ByteString *data) { if(securityPolicy == NULL || cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; if(cc->localSymIv.length != securityPolicy->symmetricModule.cryptoModule.encryptionAlgorithm.getLocalBlockSize(securityPolicy, cc)) return UA_STATUSCODE_BADINTERNALERROR; size_t plainTextBlockSize = securityPolicy->symmetricModule.cryptoModule.encryptionAlgorithm.getLocalPlainTextBlockSize(securityPolicy, cc); if(data->length % plainTextBlockSize != 0) { UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Length of data to encrypt is not a multiple of the plain text block size." "Padding might not have been calculated appropriately."); return UA_STATUSCODE_BADINTERNALERROR; } /* Keylength in bits */ unsigned int keylength = (unsigned int)(cc->localSymEncryptingKey.length * 8); mbedtls_aes_context aesContext; int mbedErr = mbedtls_aes_setkey_enc(&aesContext, cc->localSymEncryptingKey.data, keylength); if(mbedErr) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString ivCopy; UA_StatusCode retval = UA_ByteString_copy(&cc->localSymIv, &ivCopy); if(retval != UA_STATUSCODE_GOOD) return retval; mbedErr = mbedtls_aes_crypt_cbc(&aesContext, MBEDTLS_AES_ENCRYPT, data->length, ivCopy.data, data->data, data->data); if(mbedErr) retval = UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_deleteMembers(&ivCopy); return retval; } static UA_StatusCode sym_decrypt_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy, const Basic128Rsa15_ChannelContext *cc, UA_ByteString *data) { if(securityPolicy == NULL || cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; size_t encryptionBlockSize = securityPolicy->symmetricModule.cryptoModule. encryptionAlgorithm.getRemoteBlockSize(securityPolicy, cc); if(cc->remoteSymIv.length != encryptionBlockSize) return UA_STATUSCODE_BADINTERNALERROR; if(data->length % encryptionBlockSize != 0) { UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Length of data to decrypt is not a multiple of the encryptingBlock size."); return UA_STATUSCODE_BADINTERNALERROR; } unsigned int keylength = (unsigned int)(cc->remoteSymEncryptingKey.length * 8); mbedtls_aes_context aesContext; int mbedErr = mbedtls_aes_setkey_dec(&aesContext, cc->remoteSymEncryptingKey.data, keylength); if(mbedErr) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString ivCopy; UA_StatusCode retval = UA_ByteString_copy(&cc->remoteSymIv, &ivCopy); if(retval != UA_STATUSCODE_GOOD) return retval; mbedErr = mbedtls_aes_crypt_cbc(&aesContext, MBEDTLS_AES_DECRYPT, data->length, ivCopy.data, data->data, data->data); if(mbedErr) retval = UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_deleteMembers(&ivCopy); return retval; } static UA_StatusCode sym_generateKey_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy, const UA_ByteString *secret, const UA_ByteString *seed, UA_ByteString *out) { if(securityPolicy == NULL || secret == NULL || seed == NULL || out == NULL) return UA_STATUSCODE_BADINTERNALERROR; Basic128Rsa15_PolicyContext *pc = (Basic128Rsa15_PolicyContext *)securityPolicy->policyContext; return mbedtls_generateKey(&pc->sha1MdContext, secret, seed, out); } static UA_StatusCode sym_generateNonce_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy, UA_ByteString *out) { if(securityPolicy == NULL || securityPolicy->policyContext == NULL || out == NULL) return UA_STATUSCODE_BADINTERNALERROR; Basic128Rsa15_PolicyContext *pc = (Basic128Rsa15_PolicyContext *)securityPolicy->policyContext; int mbedErr = mbedtls_ctr_drbg_random(&pc->drbgContext, out->data, out->length); if(mbedErr) return UA_STATUSCODE_BADUNEXPECTEDERROR; return UA_STATUSCODE_GOOD; } /*****************/ /* ChannelModule */ /*****************/ /* Assumes that the certificate has been verified externally */ static UA_StatusCode parseRemoteCertificate_sp_basic128rsa15(Basic128Rsa15_ChannelContext *cc, const UA_ByteString *remoteCertificate) { if(remoteCertificate == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; /* Parse the certificate */ int mbedErr = mbedtls_x509_crt_parse(&cc->remoteCertificate, remoteCertificate->data, remoteCertificate->length); if(mbedErr) return UA_STATUSCODE_BADSECURITYCHECKSFAILED; /* Check the key length */ mbedtls_rsa_context *rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); if(rsaContext->len < UA_SECURITYPOLICY_BASIC128RSA15_MINASYMKEYLENGTH || rsaContext->len > UA_SECURITYPOLICY_BASIC128RSA15_MAXASYMKEYLENGTH) return UA_STATUSCODE_BADCERTIFICATEUSENOTALLOWED; return UA_STATUSCODE_GOOD; } static void channelContext_deleteContext_sp_basic128rsa15(Basic128Rsa15_ChannelContext *cc) { UA_ByteString_deleteMembers(&cc->localSymSigningKey); UA_ByteString_deleteMembers(&cc->localSymEncryptingKey); UA_ByteString_deleteMembers(&cc->localSymIv); UA_ByteString_deleteMembers(&cc->remoteSymSigningKey); UA_ByteString_deleteMembers(&cc->remoteSymEncryptingKey); UA_ByteString_deleteMembers(&cc->remoteSymIv); mbedtls_x509_crt_free(&cc->remoteCertificate); UA_free(cc); } static UA_StatusCode channelContext_newContext_sp_basic128rsa15(const UA_SecurityPolicy *securityPolicy, const UA_ByteString *remoteCertificate, void **pp_contextData) { if(securityPolicy == NULL || remoteCertificate == NULL || pp_contextData == NULL) return UA_STATUSCODE_BADINTERNALERROR; /* Allocate the channel context */ *pp_contextData = UA_malloc(sizeof(Basic128Rsa15_ChannelContext)); if(*pp_contextData == NULL) return UA_STATUSCODE_BADOUTOFMEMORY; Basic128Rsa15_ChannelContext *cc = (Basic128Rsa15_ChannelContext *)*pp_contextData; /* Initialize the channel context */ cc->policyContext = (Basic128Rsa15_PolicyContext *)securityPolicy->policyContext; UA_ByteString_init(&cc->localSymSigningKey); UA_ByteString_init(&cc->localSymEncryptingKey); UA_ByteString_init(&cc->localSymIv); UA_ByteString_init(&cc->remoteSymSigningKey); UA_ByteString_init(&cc->remoteSymEncryptingKey); UA_ByteString_init(&cc->remoteSymIv); mbedtls_x509_crt_init(&cc->remoteCertificate); // TODO: this can be optimized so that we dont allocate memory before parsing the certificate UA_StatusCode retval = parseRemoteCertificate_sp_basic128rsa15(cc, remoteCertificate); if(retval != UA_STATUSCODE_GOOD) { channelContext_deleteContext_sp_basic128rsa15(cc); *pp_contextData = NULL; } return retval; } static UA_StatusCode channelContext_setLocalSymEncryptingKey_sp_basic128rsa15(Basic128Rsa15_ChannelContext *cc, const UA_ByteString *key) { if(key == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_deleteMembers(&cc->localSymEncryptingKey); return UA_ByteString_copy(key, &cc->localSymEncryptingKey); } static UA_StatusCode channelContext_setLocalSymSigningKey_sp_basic128rsa15(Basic128Rsa15_ChannelContext *cc, const UA_ByteString *key) { if(key == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_deleteMembers(&cc->localSymSigningKey); return UA_ByteString_copy(key, &cc->localSymSigningKey); } static UA_StatusCode channelContext_setLocalSymIv_sp_basic128rsa15(Basic128Rsa15_ChannelContext *cc, const UA_ByteString *iv) { if(iv == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_deleteMembers(&cc->localSymIv); return UA_ByteString_copy(iv, &cc->localSymIv); } static UA_StatusCode channelContext_setRemoteSymEncryptingKey_sp_basic128rsa15(Basic128Rsa15_ChannelContext *cc, const UA_ByteString *key) { if(key == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_deleteMembers(&cc->remoteSymEncryptingKey); return UA_ByteString_copy(key, &cc->remoteSymEncryptingKey); } static UA_StatusCode channelContext_setRemoteSymSigningKey_sp_basic128rsa15(Basic128Rsa15_ChannelContext *cc, const UA_ByteString *key) { if(key == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_deleteMembers(&cc->remoteSymSigningKey); return UA_ByteString_copy(key, &cc->remoteSymSigningKey); } static UA_StatusCode channelContext_setRemoteSymIv_sp_basic128rsa15(Basic128Rsa15_ChannelContext *cc, const UA_ByteString *iv) { if(iv == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_deleteMembers(&cc->remoteSymIv); return UA_ByteString_copy(iv, &cc->remoteSymIv); } static UA_StatusCode channelContext_compareCertificate_sp_basic128rsa15(const Basic128Rsa15_ChannelContext *cc, const UA_ByteString *certificate) { if(cc == NULL || certificate == NULL) return UA_STATUSCODE_BADINTERNALERROR; mbedtls_x509_crt cert; mbedtls_x509_crt_init(&cert); int mbedErr = mbedtls_x509_crt_parse(&cert, certificate->data, certificate->length); if(mbedErr) return UA_STATUSCODE_BADSECURITYCHECKSFAILED; UA_StatusCode retval = UA_STATUSCODE_GOOD; if(cert.raw.len != cc->remoteCertificate.raw.len || memcmp(cert.raw.p, cc->remoteCertificate.raw.p, cert.raw.len) != 0) retval = UA_STATUSCODE_BADSECURITYCHECKSFAILED; mbedtls_x509_crt_free(&cert); return retval; } static void deleteMembers_sp_basic128rsa15(UA_SecurityPolicy *securityPolicy) { if(securityPolicy == NULL) return; if(securityPolicy->policyContext == NULL) return; UA_ByteString_deleteMembers(&securityPolicy->localCertificate); /* delete all allocated members in the context */ Basic128Rsa15_PolicyContext *pc = (Basic128Rsa15_PolicyContext *) securityPolicy->policyContext; mbedtls_ctr_drbg_free(&pc->drbgContext); mbedtls_entropy_free(&pc->entropyContext); mbedtls_pk_free(&pc->localPrivateKey); mbedtls_md_free(&pc->sha1MdContext); UA_ByteString_deleteMembers(&pc->localCertThumbprint); UA_LOG_DEBUG(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Deleted members of EndpointContext for sp_basic128rsa15"); UA_free(pc); securityPolicy->policyContext = NULL; } static UA_StatusCode updateCertificateAndPrivateKey_sp_basic128rsa15(UA_SecurityPolicy *securityPolicy, const UA_ByteString newCertificate, const UA_ByteString newPrivateKey) { if(securityPolicy == NULL) return UA_STATUSCODE_BADINTERNALERROR; if(securityPolicy->policyContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; Basic128Rsa15_PolicyContext *pc = (Basic128Rsa15_PolicyContext *)securityPolicy->policyContext; UA_ByteString_deleteMembers(&securityPolicy->localCertificate); UA_StatusCode retval = UA_ByteString_allocBuffer(&securityPolicy->localCertificate, newCertificate.length + 1); if(retval != UA_STATUSCODE_GOOD) return retval; memcpy(securityPolicy->localCertificate.data, newCertificate.data, newCertificate.length); securityPolicy->localCertificate.data[newCertificate.length] = '\0'; securityPolicy->localCertificate.length--; /* Set the new private key */ mbedtls_pk_free(&pc->localPrivateKey); mbedtls_pk_init(&pc->localPrivateKey); int mbedErr = mbedtls_pk_parse_key(&pc->localPrivateKey, newPrivateKey.data, newPrivateKey.length, NULL, 0); if(mbedErr) { retval = UA_STATUSCODE_BADSECURITYCHECKSFAILED; goto error; } retval = asym_makeThumbprint_sp_basic128rsa15(pc->securityPolicy, &securityPolicy->localCertificate, &pc->localCertThumbprint); if(retval != UA_STATUSCODE_GOOD) goto error; return retval; error: UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Could not update certificate and private key"); if(securityPolicy->policyContext != NULL) deleteMembers_sp_basic128rsa15(securityPolicy); return retval; } static UA_StatusCode policyContext_newContext_sp_basic128rsa15(UA_SecurityPolicy *securityPolicy, const UA_ByteString localPrivateKey) { UA_StatusCode retval = UA_STATUSCODE_GOOD; if(securityPolicy == NULL) return UA_STATUSCODE_BADINTERNALERROR; if (localPrivateKey.length == 0) { UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Can not initialize security policy. Private key is empty."); return UA_STATUSCODE_BADINVALIDARGUMENT; } Basic128Rsa15_PolicyContext *pc = (Basic128Rsa15_PolicyContext *) UA_malloc(sizeof(Basic128Rsa15_PolicyContext)); securityPolicy->policyContext = (void *)pc; if(!pc) { retval = UA_STATUSCODE_BADOUTOFMEMORY; goto error; } /* Initialize the PolicyContext */ memset(pc, 0, sizeof(Basic128Rsa15_PolicyContext)); mbedtls_ctr_drbg_init(&pc->drbgContext); mbedtls_entropy_init(&pc->entropyContext); mbedtls_pk_init(&pc->localPrivateKey); mbedtls_md_init(&pc->sha1MdContext); pc->securityPolicy = securityPolicy; /* Initialized the message digest */ const mbedtls_md_info_t *const mdInfo = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1); int mbedErr = mbedtls_md_setup(&pc->sha1MdContext, mdInfo, MBEDTLS_MD_SHA1); if(mbedErr) { retval = UA_STATUSCODE_BADOUTOFMEMORY; goto error; } /* Add the system entropy source */ mbedErr = mbedtls_entropy_add_source(&pc->entropyContext, mbedtls_platform_entropy_poll, NULL, 0, MBEDTLS_ENTROPY_SOURCE_STRONG); if(mbedErr) { retval = UA_STATUSCODE_BADSECURITYCHECKSFAILED; goto error; } /* Seed the RNG */ char *personalization = "open62541-drbg"; mbedErr = mbedtls_ctr_drbg_seed(&pc->drbgContext, mbedtls_entropy_func, &pc->entropyContext, (const unsigned char *)personalization, 14); if(mbedErr) { retval = UA_STATUSCODE_BADSECURITYCHECKSFAILED; goto error; } /* Set the private key */ mbedErr = mbedtls_pk_parse_key(&pc->localPrivateKey, localPrivateKey.data, localPrivateKey.length, NULL, 0); if(mbedErr) { retval = UA_STATUSCODE_BADSECURITYCHECKSFAILED; goto error; } /* Set the local certificate thumbprint */ retval = UA_ByteString_allocBuffer(&pc->localCertThumbprint, UA_SHA1_LENGTH); if(retval != UA_STATUSCODE_GOOD) goto error; retval = asym_makeThumbprint_sp_basic128rsa15(pc->securityPolicy, &securityPolicy->localCertificate, &pc->localCertThumbprint); if(retval != UA_STATUSCODE_GOOD) goto error; return UA_STATUSCODE_GOOD; error: UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Could not create securityContext: %s", UA_StatusCode_name(retval)); if(securityPolicy->policyContext != NULL) deleteMembers_sp_basic128rsa15(securityPolicy); return retval; } UA_StatusCode UA_SecurityPolicy_Basic128Rsa15(UA_SecurityPolicy *policy, UA_CertificateVerification *certificateVerification, const UA_ByteString localCertificate, const UA_ByteString localPrivateKey, const UA_Logger *logger) { memset(policy, 0, sizeof(UA_SecurityPolicy)); policy->logger = logger; policy->policyUri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15"); UA_SecurityPolicyAsymmetricModule *const asymmetricModule = &policy->asymmetricModule; UA_SecurityPolicySymmetricModule *const symmetricModule = &policy->symmetricModule; UA_SecurityPolicyChannelModule *const channelModule = &policy->channelModule; /* Copy the certificate and add a NULL to the end */ UA_StatusCode retval = UA_ByteString_allocBuffer(&policy->localCertificate, localCertificate.length + 1); if(retval != UA_STATUSCODE_GOOD) return retval; memcpy(policy->localCertificate.data, localCertificate.data, localCertificate.length); policy->localCertificate.data[localCertificate.length] = '\0'; policy->localCertificate.length--; policy->certificateVerification = certificateVerification; /* AsymmetricModule */ UA_SecurityPolicySignatureAlgorithm *asym_signatureAlgorithm = &asymmetricModule->cryptoModule.signatureAlgorithm; asym_signatureAlgorithm->uri = UA_STRING("http://www.w3.org/2000/09/xmldsig#rsa-sha1\0"); asym_signatureAlgorithm->verify = (UA_StatusCode (*)(const UA_SecurityPolicy *, void *, const UA_ByteString *, const UA_ByteString *))asym_verify_sp_basic128rsa15; asym_signatureAlgorithm->sign = (UA_StatusCode (*)(const UA_SecurityPolicy *, void *, const UA_ByteString *, UA_ByteString *))asym_sign_sp_basic128rsa15; asym_signatureAlgorithm->getLocalSignatureSize = (size_t (*)(const UA_SecurityPolicy *, const void *))asym_getLocalSignatureSize_sp_basic128rsa15; asym_signatureAlgorithm->getRemoteSignatureSize = (size_t (*)(const UA_SecurityPolicy *, const void *))asym_getRemoteSignatureSize_sp_basic128rsa15; asym_signatureAlgorithm->getLocalKeyLength = NULL; // TODO: Write function asym_signatureAlgorithm->getRemoteKeyLength = NULL; // TODO: Write function UA_SecurityPolicyEncryptionAlgorithm *asym_encryptionAlgorithm = &asymmetricModule->cryptoModule.encryptionAlgorithm; asym_encryptionAlgorithm->uri = UA_STRING("http://www.w3.org/2001/04/xmlenc#rsa-1_5"); asym_encryptionAlgorithm->encrypt = (UA_StatusCode(*)(const UA_SecurityPolicy *, void *, UA_ByteString *))asym_encrypt_sp_basic128rsa15; asym_encryptionAlgorithm->decrypt = (UA_StatusCode(*)(const UA_SecurityPolicy *, void *, UA_ByteString *)) asym_decrypt_sp_basic128rsa15; asym_encryptionAlgorithm->getLocalKeyLength = NULL; // TODO: Write function asym_encryptionAlgorithm->getRemoteKeyLength = (size_t (*)(const UA_SecurityPolicy *, const void *))asym_getRemoteEncryptionKeyLength_sp_basic128rsa15; asym_encryptionAlgorithm->getLocalBlockSize = NULL; // TODO: Write function asym_encryptionAlgorithm->getRemoteBlockSize = (size_t (*)(const UA_SecurityPolicy *, const void *))asym_getRemoteBlockSize_sp_basic128rsa15; asym_encryptionAlgorithm->getLocalPlainTextBlockSize = NULL; // TODO: Write function asym_encryptionAlgorithm->getRemotePlainTextBlockSize = (size_t (*)(const UA_SecurityPolicy *, const void *))asym_getRemotePlainTextBlockSize_sp_basic128rsa15; asymmetricModule->makeCertificateThumbprint = asym_makeThumbprint_sp_basic128rsa15; asymmetricModule->compareCertificateThumbprint = asymmetricModule_compareCertificateThumbprint_sp_basic128rsa15; /* SymmetricModule */ symmetricModule->generateKey = sym_generateKey_sp_basic128rsa15; symmetricModule->generateNonce = sym_generateNonce_sp_basic128rsa15; UA_SecurityPolicySignatureAlgorithm *sym_signatureAlgorithm = &symmetricModule->cryptoModule.signatureAlgorithm; sym_signatureAlgorithm->uri = UA_STRING("http://www.w3.org/2000/09/xmldsig#hmac-sha1\0"); sym_signatureAlgorithm->verify = (UA_StatusCode (*)(const UA_SecurityPolicy *, void *, const UA_ByteString *, const UA_ByteString *))sym_verify_sp_basic128rsa15; sym_signatureAlgorithm->sign = (UA_StatusCode (*)(const UA_SecurityPolicy *, void *, const UA_ByteString *, UA_ByteString *))sym_sign_sp_basic128rsa15; sym_signatureAlgorithm->getLocalSignatureSize = sym_getSignatureSize_sp_basic128rsa15; sym_signatureAlgorithm->getRemoteSignatureSize = sym_getSignatureSize_sp_basic128rsa15; sym_signatureAlgorithm->getLocalKeyLength = (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getSigningKeyLength_sp_basic128rsa15; sym_signatureAlgorithm->getRemoteKeyLength = (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getSigningKeyLength_sp_basic128rsa15; UA_SecurityPolicyEncryptionAlgorithm *sym_encryptionAlgorithm = &symmetricModule->cryptoModule.encryptionAlgorithm; sym_encryptionAlgorithm->uri = UA_STRING("http://www.w3.org/2001/04/xmlenc#aes128-cbc"); sym_encryptionAlgorithm->encrypt = (UA_StatusCode(*)(const UA_SecurityPolicy *, void *, UA_ByteString *))sym_encrypt_sp_basic128rsa15; sym_encryptionAlgorithm->decrypt = (UA_StatusCode(*)(const UA_SecurityPolicy *, void *, UA_ByteString *))sym_decrypt_sp_basic128rsa15; sym_encryptionAlgorithm->getLocalKeyLength = sym_getEncryptionKeyLength_sp_basic128rsa15; sym_encryptionAlgorithm->getRemoteKeyLength = sym_getEncryptionKeyLength_sp_basic128rsa15; sym_encryptionAlgorithm->getLocalBlockSize = (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getEncryptionBlockSize_sp_basic128rsa15; sym_encryptionAlgorithm->getRemoteBlockSize = (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getEncryptionBlockSize_sp_basic128rsa15; sym_encryptionAlgorithm->getLocalPlainTextBlockSize = (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getPlainTextBlockSize_sp_basic128rsa15; sym_encryptionAlgorithm->getRemotePlainTextBlockSize = (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getPlainTextBlockSize_sp_basic128rsa15; symmetricModule->secureChannelNonceLength = 16; // Use the same signature algorithm as the asymmetric component for certificate signing (see standard) policy->certificateSigningAlgorithm = policy->asymmetricModule.cryptoModule.signatureAlgorithm; /* ChannelModule */ channelModule->newContext = channelContext_newContext_sp_basic128rsa15; channelModule->deleteContext = (void (*)(void *)) channelContext_deleteContext_sp_basic128rsa15; channelModule->setLocalSymEncryptingKey = (UA_StatusCode (*)(void *, const UA_ByteString *)) channelContext_setLocalSymEncryptingKey_sp_basic128rsa15; channelModule->setLocalSymSigningKey = (UA_StatusCode (*)(void *, const UA_ByteString *)) channelContext_setLocalSymSigningKey_sp_basic128rsa15; channelModule->setLocalSymIv = (UA_StatusCode (*)(void *, const UA_ByteString *)) channelContext_setLocalSymIv_sp_basic128rsa15; channelModule->setRemoteSymEncryptingKey = (UA_StatusCode (*)(void *, const UA_ByteString *)) channelContext_setRemoteSymEncryptingKey_sp_basic128rsa15; channelModule->setRemoteSymSigningKey = (UA_StatusCode (*)(void *, const UA_ByteString *)) channelContext_setRemoteSymSigningKey_sp_basic128rsa15; channelModule->setRemoteSymIv = (UA_StatusCode (*)(void *, const UA_ByteString *)) channelContext_setRemoteSymIv_sp_basic128rsa15; channelModule->compareCertificate = (UA_StatusCode (*)(const void *, const UA_ByteString *)) channelContext_compareCertificate_sp_basic128rsa15; policy->updateCertificateAndPrivateKey = updateCertificateAndPrivateKey_sp_basic128rsa15; policy->deleteMembers = deleteMembers_sp_basic128rsa15; return policyContext_newContext_sp_basic128rsa15(policy, localPrivateKey); } #endif /*********************************** amalgamated original file "/home/jvoe/open62541/plugins/securityPolicies/ua_securitypolicy_basic256.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2018 (c) Mark Giraud, Fraunhofer IOSB * Copyright 2018 (c) Daniel Feist, Precitec GmbH & Co. KG * Copyright 2019 (c) Kalycito Infotech Private Limited * */ #ifdef UA_ENABLE_ENCRYPTION #include #include #include #include #include #include /* Notes: * mbedTLS' AES allows in-place encryption and decryption. Sow we don't have to * allocate temp buffers. * https://tls.mbed.org/discussions/generic/in-place-decryption-with-aes256-same-input-output-buffer */ #define UA_SECURITYPOLICY_BASIC256SHA1_RSAPADDING_LEN 42 #define UA_SHA1_LENGTH 20 #define UA_BASIC256_SYM_SIGNING_KEY_LENGTH 24 #define UA_SECURITYPOLICY_BASIC256_SYM_KEY_LENGTH 32 #define UA_SECURITYPOLICY_BASIC256_SYM_ENCRYPTION_BLOCK_SIZE 16 #define UA_SECURITYPOLICY_BASIC256_SYM_PLAIN_TEXT_BLOCK_SIZE 16 #define UA_SECURITYPOLICY_BASIC256_MINASYMKEYLENGTH 128 #define UA_SECURITYPOLICY_BASIC256_MAXASYMKEYLENGTH 512 typedef struct { const UA_SecurityPolicy *securityPolicy; UA_ByteString localCertThumbprint; mbedtls_ctr_drbg_context drbgContext; mbedtls_entropy_context entropyContext; mbedtls_md_context_t sha1MdContext; mbedtls_pk_context localPrivateKey; } Basic256_PolicyContext; typedef struct { Basic256_PolicyContext *policyContext; UA_ByteString localSymSigningKey; UA_ByteString localSymEncryptingKey; UA_ByteString localSymIv; UA_ByteString remoteSymSigningKey; UA_ByteString remoteSymEncryptingKey; UA_ByteString remoteSymIv; mbedtls_x509_crt remoteCertificate; } Basic256_ChannelContext; /********************/ /* AsymmetricModule */ /********************/ /* VERIFY AsymmetricSignatureAlgorithm_RSA-PKCS15-SHA2-256 */ static UA_StatusCode asym_verify_sp_basic256(const UA_SecurityPolicy *securityPolicy, Basic256_ChannelContext *cc, const UA_ByteString *message, const UA_ByteString *signature) { if(securityPolicy == NULL || message == NULL || signature == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; return mbedtls_verifySig_sha1(&cc->remoteCertificate, message, signature); } /* AsymmetricSignatureAlgorithm_RSA-PKCS15-SHA2-256 */ static UA_StatusCode asym_sign_sp_basic256(const UA_SecurityPolicy *securityPolicy, Basic256_ChannelContext *cc, const UA_ByteString *message, UA_ByteString *signature) { if(securityPolicy == NULL || message == NULL || signature == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; Basic256_PolicyContext *pc = cc->policyContext; return mbedtls_sign_sha1(&pc->localPrivateKey, &pc->drbgContext, message, signature); } static size_t asym_getLocalSignatureSize_sp_basic256(const UA_SecurityPolicy *securityPolicy, const Basic256_ChannelContext *cc) { if(securityPolicy == NULL || cc == NULL) return 0; return mbedtls_pk_rsa(cc->policyContext->localPrivateKey)->len; } static size_t asym_getRemoteSignatureSize_sp_basic256(const UA_SecurityPolicy *securityPolicy, const Basic256_ChannelContext *cc) { if(securityPolicy == NULL || cc == NULL) return 0; return mbedtls_pk_rsa(cc->remoteCertificate.pk)->len; } /* AsymmetricEncryptionAlgorithm_RSA-OAEP-SHA1 */ static UA_StatusCode asym_encrypt_sp_basic256(const UA_SecurityPolicy *securityPolicy, Basic256_ChannelContext *cc, UA_ByteString *data) { if(securityPolicy == NULL || cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; const size_t plainTextBlockSize = securityPolicy->asymmetricModule.cryptoModule. encryptionAlgorithm.getRemotePlainTextBlockSize(securityPolicy, cc); mbedtls_rsa_context *remoteRsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); mbedtls_rsa_set_padding(remoteRsaContext, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA1); return mbedtls_encrypt_rsaOaep(remoteRsaContext, &cc->policyContext->drbgContext, data, plainTextBlockSize); } /* AsymmetricEncryptionAlgorithm_RSA-OAEP-SHA1 */ static UA_StatusCode asym_decrypt_sp_basic256(const UA_SecurityPolicy *securityPolicy, Basic256_ChannelContext *cc, UA_ByteString *data) { if(securityPolicy == NULL || cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; return mbedtls_decrypt_rsaOaep(&cc->policyContext->localPrivateKey, &cc->policyContext->drbgContext, data); } static size_t asym_getRemoteEncryptionKeyLength_sp_basic256(const UA_SecurityPolicy *securityPolicy, const Basic256_ChannelContext *cc) { return mbedtls_pk_get_len(&cc->remoteCertificate.pk) * 8; } static size_t asym_getRemoteBlockSize_sp_basic256(const UA_SecurityPolicy *securityPolicy, const Basic256_ChannelContext *cc) { mbedtls_rsa_context *const rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); return rsaContext->len; } static size_t asym_getRemotePlainTextBlockSize_sp_basic256(const UA_SecurityPolicy *securityPolicy, const Basic256_ChannelContext *cc) { mbedtls_rsa_context *const rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); return rsaContext->len - UA_SECURITYPOLICY_BASIC256SHA1_RSAPADDING_LEN; } static UA_StatusCode asym_makeThumbprint_sp_basic256(const UA_SecurityPolicy *securityPolicy, const UA_ByteString *certificate, UA_ByteString *thumbprint) { if(securityPolicy == NULL || certificate == NULL || thumbprint == NULL) return UA_STATUSCODE_BADINTERNALERROR; return mbedtls_thumbprint_sha1(certificate, thumbprint); } static UA_StatusCode asymmetricModule_compareCertificateThumbprint_sp_basic256(const UA_SecurityPolicy *securityPolicy, const UA_ByteString *certificateThumbprint) { if(securityPolicy == NULL || certificateThumbprint == NULL) return UA_STATUSCODE_BADINTERNALERROR; Basic256_PolicyContext *pc = (Basic256_PolicyContext *)securityPolicy->policyContext; if(!UA_ByteString_equal(certificateThumbprint, &pc->localCertThumbprint)) return UA_STATUSCODE_BADCERTIFICATEINVALID; return UA_STATUSCODE_GOOD; } /*******************/ /* SymmetricModule */ /*******************/ static UA_StatusCode sym_verify_sp_basic256(const UA_SecurityPolicy *securityPolicy, Basic256_ChannelContext *cc, const UA_ByteString *message, const UA_ByteString *signature) { if(securityPolicy == NULL || cc == NULL || message == NULL || signature == NULL) return UA_STATUSCODE_BADINTERNALERROR; /* Compute MAC */ if(signature->length != UA_SHA1_LENGTH) { UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Signature size does not have the desired size defined by the security policy"); return UA_STATUSCODE_BADSECURITYCHECKSFAILED; } Basic256_PolicyContext *pc = (Basic256_PolicyContext *)securityPolicy->policyContext; unsigned char mac[UA_SHA1_LENGTH]; mbedtls_hmac(&pc->sha1MdContext, &cc->remoteSymSigningKey, message, mac); /* Compare with Signature */ if(!UA_constantTimeEqual(signature->data, mac, UA_SHA1_LENGTH)) return UA_STATUSCODE_BADSECURITYCHECKSFAILED; return UA_STATUSCODE_GOOD; } static UA_StatusCode sym_sign_sp_basic256(const UA_SecurityPolicy *securityPolicy, const Basic256_ChannelContext *cc, const UA_ByteString *message, UA_ByteString *signature) { if(signature->length != UA_SHA1_LENGTH) return UA_STATUSCODE_BADINTERNALERROR; mbedtls_hmac(&cc->policyContext->sha1MdContext, &cc->localSymSigningKey, message, signature->data); return UA_STATUSCODE_GOOD; } static size_t sym_getSignatureSize_sp_basic256(const UA_SecurityPolicy *securityPolicy, const void *channelContext) { return UA_SHA1_LENGTH; } static size_t sym_getSigningKeyLength_sp_basic256(const UA_SecurityPolicy *const securityPolicy, const void *const channelContext) { return UA_BASIC256_SYM_SIGNING_KEY_LENGTH; } static size_t sym_getEncryptionKeyLength_sp_basic256(const UA_SecurityPolicy *securityPolicy, const void *channelContext) { return UA_SECURITYPOLICY_BASIC256_SYM_KEY_LENGTH; } static size_t sym_getEncryptionBlockSize_sp_basic256(const UA_SecurityPolicy *const securityPolicy, const void *const channelContext) { return UA_SECURITYPOLICY_BASIC256_SYM_ENCRYPTION_BLOCK_SIZE; } static size_t sym_getPlainTextBlockSize_sp_basic256(const UA_SecurityPolicy *const securityPolicy, const void *const channelContext) { return UA_SECURITYPOLICY_BASIC256_SYM_PLAIN_TEXT_BLOCK_SIZE; } static UA_StatusCode sym_encrypt_sp_basic256(const UA_SecurityPolicy *securityPolicy, const Basic256_ChannelContext *cc, UA_ByteString *data) { if(securityPolicy == NULL || cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; if(cc->localSymIv.length != securityPolicy->symmetricModule.cryptoModule.encryptionAlgorithm. getLocalBlockSize(securityPolicy, cc)) return UA_STATUSCODE_BADINTERNALERROR; size_t plainTextBlockSize = securityPolicy->symmetricModule.cryptoModule.encryptionAlgorithm. getLocalPlainTextBlockSize(securityPolicy, cc); if(data->length % plainTextBlockSize != 0) { UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Length of data to encrypt is not a multiple of the plain text block size." "Padding might not have been calculated appropriately."); return UA_STATUSCODE_BADINTERNALERROR; } /* Keylength in bits */ unsigned int keylength = (unsigned int)(cc->localSymEncryptingKey.length * 8); mbedtls_aes_context aesContext; int mbedErr = mbedtls_aes_setkey_enc(&aesContext, cc->localSymEncryptingKey.data, keylength); if(mbedErr) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString ivCopy; UA_StatusCode retval = UA_ByteString_copy(&cc->localSymIv, &ivCopy); if(retval != UA_STATUSCODE_GOOD) return retval; mbedErr = mbedtls_aes_crypt_cbc(&aesContext, MBEDTLS_AES_ENCRYPT, data->length, ivCopy.data, data->data, data->data); if(mbedErr) retval = UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_deleteMembers(&ivCopy); return retval; } static UA_StatusCode sym_decrypt_sp_basic256(const UA_SecurityPolicy *securityPolicy, const Basic256_ChannelContext *cc, UA_ByteString *data) { if(securityPolicy == NULL || cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; size_t encryptionBlockSize = securityPolicy->symmetricModule.cryptoModule.encryptionAlgorithm. getRemoteBlockSize(securityPolicy, cc); if(cc->remoteSymIv.length != encryptionBlockSize) return UA_STATUSCODE_BADINTERNALERROR; if(data->length % encryptionBlockSize != 0) { UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Length of data to decrypt is not a multiple of the encryptingBlock size."); return UA_STATUSCODE_BADINTERNALERROR; } unsigned int keylength = (unsigned int)(cc->remoteSymEncryptingKey.length * 8); mbedtls_aes_context aesContext; int mbedErr = mbedtls_aes_setkey_dec(&aesContext, cc->remoteSymEncryptingKey.data, keylength); if(mbedErr) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString ivCopy; UA_StatusCode retval = UA_ByteString_copy(&cc->remoteSymIv, &ivCopy); if(retval != UA_STATUSCODE_GOOD) return retval; mbedErr = mbedtls_aes_crypt_cbc(&aesContext, MBEDTLS_AES_DECRYPT, data->length, ivCopy.data, data->data, data->data); if(mbedErr) retval = UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_deleteMembers(&ivCopy); return retval; } static UA_StatusCode sym_generateKey_sp_basic256(const UA_SecurityPolicy *securityPolicy, const UA_ByteString *secret, const UA_ByteString *seed, UA_ByteString *out) { if(securityPolicy == NULL || secret == NULL || seed == NULL || out == NULL) return UA_STATUSCODE_BADINTERNALERROR; Basic256_PolicyContext *pc = (Basic256_PolicyContext *)securityPolicy->policyContext; return mbedtls_generateKey(&pc->sha1MdContext, secret, seed, out); } static UA_StatusCode sym_generateNonce_sp_basic256(const UA_SecurityPolicy *securityPolicy, UA_ByteString *out) { if(securityPolicy == NULL || securityPolicy->policyContext == NULL || out == NULL) return UA_STATUSCODE_BADINTERNALERROR; Basic256_PolicyContext *pc = (Basic256_PolicyContext *)securityPolicy->policyContext; int mbedErr = mbedtls_ctr_drbg_random(&pc->drbgContext, out->data, out->length); if(mbedErr) return UA_STATUSCODE_BADUNEXPECTEDERROR; return UA_STATUSCODE_GOOD; } /*****************/ /* ChannelModule */ /*****************/ /* Assumes that the certificate has been verified externally */ static UA_StatusCode parseRemoteCertificate_sp_basic256(Basic256_ChannelContext *cc, const UA_ByteString *remoteCertificate) { if(remoteCertificate == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; /* Parse the certificate */ int mbedErr = mbedtls_x509_crt_parse(&cc->remoteCertificate, remoteCertificate->data, remoteCertificate->length); if(mbedErr) return UA_STATUSCODE_BADSECURITYCHECKSFAILED; /* Check the key length */ mbedtls_rsa_context *rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); if(rsaContext->len < UA_SECURITYPOLICY_BASIC256_MINASYMKEYLENGTH || rsaContext->len > UA_SECURITYPOLICY_BASIC256_MAXASYMKEYLENGTH) return UA_STATUSCODE_BADCERTIFICATEUSENOTALLOWED; return UA_STATUSCODE_GOOD; } static void channelContext_deleteContext_sp_basic256(Basic256_ChannelContext *cc) { UA_ByteString_deleteMembers(&cc->localSymSigningKey); UA_ByteString_deleteMembers(&cc->localSymEncryptingKey); UA_ByteString_deleteMembers(&cc->localSymIv); UA_ByteString_deleteMembers(&cc->remoteSymSigningKey); UA_ByteString_deleteMembers(&cc->remoteSymEncryptingKey); UA_ByteString_deleteMembers(&cc->remoteSymIv); mbedtls_x509_crt_free(&cc->remoteCertificate); UA_free(cc); } static UA_StatusCode channelContext_newContext_sp_basic256(const UA_SecurityPolicy *securityPolicy, const UA_ByteString *remoteCertificate, void **pp_contextData) { if(securityPolicy == NULL || remoteCertificate == NULL || pp_contextData == NULL) return UA_STATUSCODE_BADINTERNALERROR; /* Allocate the channel context */ *pp_contextData = UA_malloc(sizeof(Basic256_ChannelContext)); if(*pp_contextData == NULL) return UA_STATUSCODE_BADOUTOFMEMORY; Basic256_ChannelContext *cc = (Basic256_ChannelContext *)*pp_contextData; /* Initialize the channel context */ cc->policyContext = (Basic256_PolicyContext *)securityPolicy->policyContext; UA_ByteString_init(&cc->localSymSigningKey); UA_ByteString_init(&cc->localSymEncryptingKey); UA_ByteString_init(&cc->localSymIv); UA_ByteString_init(&cc->remoteSymSigningKey); UA_ByteString_init(&cc->remoteSymEncryptingKey); UA_ByteString_init(&cc->remoteSymIv); mbedtls_x509_crt_init(&cc->remoteCertificate); // TODO: this can be optimized so that we dont allocate memory before parsing the certificate UA_StatusCode retval = parseRemoteCertificate_sp_basic256(cc, remoteCertificate); if(retval != UA_STATUSCODE_GOOD) { channelContext_deleteContext_sp_basic256(cc); *pp_contextData = NULL; } return retval; } static UA_StatusCode channelContext_setLocalSymEncryptingKey_sp_basic256(Basic256_ChannelContext *cc, const UA_ByteString *key) { if(key == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_deleteMembers(&cc->localSymEncryptingKey); return UA_ByteString_copy(key, &cc->localSymEncryptingKey); } static UA_StatusCode channelContext_setLocalSymSigningKey_sp_basic256(Basic256_ChannelContext *cc, const UA_ByteString *key) { if(key == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_deleteMembers(&cc->localSymSigningKey); return UA_ByteString_copy(key, &cc->localSymSigningKey); } static UA_StatusCode channelContext_setLocalSymIv_sp_basic256(Basic256_ChannelContext *cc, const UA_ByteString *iv) { if(iv == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_deleteMembers(&cc->localSymIv); return UA_ByteString_copy(iv, &cc->localSymIv); } static UA_StatusCode channelContext_setRemoteSymEncryptingKey_sp_basic256(Basic256_ChannelContext *cc, const UA_ByteString *key) { if(key == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_deleteMembers(&cc->remoteSymEncryptingKey); return UA_ByteString_copy(key, &cc->remoteSymEncryptingKey); } static UA_StatusCode channelContext_setRemoteSymSigningKey_sp_basic256(Basic256_ChannelContext *cc, const UA_ByteString *key) { if(key == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_deleteMembers(&cc->remoteSymSigningKey); return UA_ByteString_copy(key, &cc->remoteSymSigningKey); } static UA_StatusCode channelContext_setRemoteSymIv_sp_basic256(Basic256_ChannelContext *cc, const UA_ByteString *iv) { if(iv == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_deleteMembers(&cc->remoteSymIv); return UA_ByteString_copy(iv, &cc->remoteSymIv); } static UA_StatusCode channelContext_compareCertificate_sp_basic256(const Basic256_ChannelContext *cc, const UA_ByteString *certificate) { if(cc == NULL || certificate == NULL) return UA_STATUSCODE_BADINTERNALERROR; mbedtls_x509_crt cert; mbedtls_x509_crt_init(&cert); int mbedErr = mbedtls_x509_crt_parse(&cert, certificate->data, certificate->length); if(mbedErr) return UA_STATUSCODE_BADSECURITYCHECKSFAILED; UA_StatusCode retval = UA_STATUSCODE_GOOD; if(cert.raw.len != cc->remoteCertificate.raw.len || memcmp(cert.raw.p, cc->remoteCertificate.raw.p, cert.raw.len) != 0) retval = UA_STATUSCODE_BADSECURITYCHECKSFAILED; mbedtls_x509_crt_free(&cert); return retval; } static void deleteMembers_sp_basic256(UA_SecurityPolicy *securityPolicy) { if(securityPolicy == NULL) return; if(securityPolicy->policyContext == NULL) return; UA_ByteString_deleteMembers(&securityPolicy->localCertificate); /* delete all allocated members in the context */ Basic256_PolicyContext *pc = (Basic256_PolicyContext *) securityPolicy->policyContext; mbedtls_ctr_drbg_free(&pc->drbgContext); mbedtls_entropy_free(&pc->entropyContext); mbedtls_pk_free(&pc->localPrivateKey); mbedtls_md_free(&pc->sha1MdContext); UA_ByteString_deleteMembers(&pc->localCertThumbprint); UA_LOG_DEBUG(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Deleted members of EndpointContext for sp_basic256"); UA_free(pc); securityPolicy->policyContext = NULL; } static UA_StatusCode updateCertificateAndPrivateKey_sp_basic256(UA_SecurityPolicy *securityPolicy, const UA_ByteString newCertificate, const UA_ByteString newPrivateKey) { if(securityPolicy == NULL) return UA_STATUSCODE_BADINTERNALERROR; if(securityPolicy->policyContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; Basic256_PolicyContext *pc = (Basic256_PolicyContext *) securityPolicy->policyContext; UA_ByteString_deleteMembers(&securityPolicy->localCertificate); UA_StatusCode retval = UA_ByteString_allocBuffer(&securityPolicy->localCertificate, newCertificate.length + 1); if(retval != UA_STATUSCODE_GOOD) return retval; memcpy(securityPolicy->localCertificate.data, newCertificate.data, newCertificate.length); securityPolicy->localCertificate.data[newCertificate.length] = '\0'; securityPolicy->localCertificate.length--; /* Set the new private key */ mbedtls_pk_free(&pc->localPrivateKey); mbedtls_pk_init(&pc->localPrivateKey); int mbedErr = mbedtls_pk_parse_key(&pc->localPrivateKey, newPrivateKey.data, newPrivateKey.length, NULL, 0); if(mbedErr) { retval = UA_STATUSCODE_BADSECURITYCHECKSFAILED; goto error; } retval = asym_makeThumbprint_sp_basic256(pc->securityPolicy, &securityPolicy->localCertificate, &pc->localCertThumbprint); if(retval != UA_STATUSCODE_GOOD) goto error; return retval; error: UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Could not update certificate and private key"); if(securityPolicy->policyContext != NULL) deleteMembers_sp_basic256(securityPolicy); return retval; } static UA_StatusCode policyContext_newContext_sp_basic256(UA_SecurityPolicy *securityPolicy, const UA_ByteString localPrivateKey) { UA_StatusCode retval = UA_STATUSCODE_GOOD; if(securityPolicy == NULL) return UA_STATUSCODE_BADINTERNALERROR; if (localPrivateKey.length == 0) { UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Can not initialize security policy. Private key is empty."); return UA_STATUSCODE_BADINVALIDARGUMENT; } Basic256_PolicyContext *pc = (Basic256_PolicyContext *) UA_malloc(sizeof(Basic256_PolicyContext)); securityPolicy->policyContext = (void *)pc; if(!pc) { retval = UA_STATUSCODE_BADOUTOFMEMORY; goto error; } /* Initialize the PolicyContext */ memset(pc, 0, sizeof(Basic256_PolicyContext)); mbedtls_ctr_drbg_init(&pc->drbgContext); mbedtls_entropy_init(&pc->entropyContext); mbedtls_pk_init(&pc->localPrivateKey); mbedtls_md_init(&pc->sha1MdContext); pc->securityPolicy = securityPolicy; /* Initialized the message digest */ const mbedtls_md_info_t *mdInfo = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1); int mbedErr = mbedtls_md_setup(&pc->sha1MdContext, mdInfo, MBEDTLS_MD_SHA1); if(mbedErr) { retval = UA_STATUSCODE_BADOUTOFMEMORY; goto error; } /* Add the system entropy source */ mbedErr = mbedtls_entropy_add_source(&pc->entropyContext, mbedtls_platform_entropy_poll, NULL, 0, MBEDTLS_ENTROPY_SOURCE_STRONG); if(mbedErr) { retval = UA_STATUSCODE_BADSECURITYCHECKSFAILED; goto error; } /* Seed the RNG */ char *personalization = "open62541-drbg"; mbedErr = mbedtls_ctr_drbg_seed(&pc->drbgContext, mbedtls_entropy_func, &pc->entropyContext, (const unsigned char *)personalization, 14); if(mbedErr) { retval = UA_STATUSCODE_BADSECURITYCHECKSFAILED; goto error; } /* Set the private key */ mbedErr = mbedtls_pk_parse_key(&pc->localPrivateKey, localPrivateKey.data, localPrivateKey.length, NULL, 0); if(mbedErr) { retval = UA_STATUSCODE_BADSECURITYCHECKSFAILED; goto error; } /* Set the local certificate thumbprint */ retval = UA_ByteString_allocBuffer(&pc->localCertThumbprint, UA_SHA1_LENGTH); if(retval != UA_STATUSCODE_GOOD) goto error; retval = asym_makeThumbprint_sp_basic256(pc->securityPolicy, &securityPolicy->localCertificate, &pc->localCertThumbprint); if(retval != UA_STATUSCODE_GOOD) goto error; return UA_STATUSCODE_GOOD; error: UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Could not create securityContext: %s", UA_StatusCode_name(retval)); if(securityPolicy->policyContext != NULL) deleteMembers_sp_basic256(securityPolicy); return retval; } UA_StatusCode UA_SecurityPolicy_Basic256(UA_SecurityPolicy *policy, UA_CertificateVerification *certificateVerification, const UA_ByteString localCertificate, const UA_ByteString localPrivateKey, const UA_Logger *logger) { memset(policy, 0, sizeof(UA_SecurityPolicy)); policy->logger = logger; policy->policyUri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Basic256"); UA_SecurityPolicyAsymmetricModule *const asymmetricModule = &policy->asymmetricModule; UA_SecurityPolicySymmetricModule *const symmetricModule = &policy->symmetricModule; UA_SecurityPolicyChannelModule *const channelModule = &policy->channelModule; /* Copy the certificate and add a NULL to the end */ UA_StatusCode retval = UA_ByteString_allocBuffer(&policy->localCertificate, localCertificate.length + 1); if(retval != UA_STATUSCODE_GOOD) return retval; memcpy(policy->localCertificate.data, localCertificate.data, localCertificate.length); policy->localCertificate.data[localCertificate.length] = '\0'; policy->localCertificate.length--; policy->certificateVerification = certificateVerification; /* AsymmetricModule */ UA_SecurityPolicySignatureAlgorithm *asym_signatureAlgorithm = &asymmetricModule->cryptoModule.signatureAlgorithm; asym_signatureAlgorithm->uri = UA_STRING("http://www.w3.org/2000/09/xmldsig#rsa-sha1\0"); asym_signatureAlgorithm->verify = (UA_StatusCode (*)(const UA_SecurityPolicy *, void *, const UA_ByteString *, const UA_ByteString *))asym_verify_sp_basic256; asym_signatureAlgorithm->sign = (UA_StatusCode (*)(const UA_SecurityPolicy *, void *, const UA_ByteString *, UA_ByteString *))asym_sign_sp_basic256; asym_signatureAlgorithm->getLocalSignatureSize = (size_t (*)(const UA_SecurityPolicy *, const void *))asym_getLocalSignatureSize_sp_basic256; asym_signatureAlgorithm->getRemoteSignatureSize = (size_t (*)(const UA_SecurityPolicy *, const void *))asym_getRemoteSignatureSize_sp_basic256; asym_signatureAlgorithm->getLocalKeyLength = NULL; // TODO: Write function asym_signatureAlgorithm->getRemoteKeyLength = NULL; // TODO: Write function UA_SecurityPolicyEncryptionAlgorithm *asym_encryptionAlgorithm = &asymmetricModule->cryptoModule.encryptionAlgorithm; asym_encryptionAlgorithm->uri = UA_STRING("http://www.w3.org/2001/04/xmlenc#rsa-oaep\0"); asym_encryptionAlgorithm->encrypt = (UA_StatusCode(*)(const UA_SecurityPolicy *, void *, UA_ByteString *))asym_encrypt_sp_basic256; asym_encryptionAlgorithm->decrypt = (UA_StatusCode(*)(const UA_SecurityPolicy *, void *, UA_ByteString *)) asym_decrypt_sp_basic256; asym_encryptionAlgorithm->getLocalKeyLength = NULL; // TODO: Write function asym_encryptionAlgorithm->getRemoteKeyLength = (size_t (*)(const UA_SecurityPolicy *, const void *))asym_getRemoteEncryptionKeyLength_sp_basic256; asym_encryptionAlgorithm->getLocalBlockSize = NULL; // TODO: Write function asym_encryptionAlgorithm->getRemoteBlockSize = (size_t (*)(const UA_SecurityPolicy *, const void *))asym_getRemoteBlockSize_sp_basic256; asym_encryptionAlgorithm->getLocalPlainTextBlockSize = NULL; // TODO: Write function asym_encryptionAlgorithm->getRemotePlainTextBlockSize = (size_t (*)(const UA_SecurityPolicy *, const void *))asym_getRemotePlainTextBlockSize_sp_basic256; asymmetricModule->makeCertificateThumbprint = asym_makeThumbprint_sp_basic256; asymmetricModule->compareCertificateThumbprint = asymmetricModule_compareCertificateThumbprint_sp_basic256; /* SymmetricModule */ symmetricModule->generateKey = sym_generateKey_sp_basic256; symmetricModule->generateNonce = sym_generateNonce_sp_basic256; UA_SecurityPolicySignatureAlgorithm *sym_signatureAlgorithm = &symmetricModule->cryptoModule.signatureAlgorithm; sym_signatureAlgorithm->uri = UA_STRING("http://www.w3.org/2000/09/xmldsig#hmac-sha1\0"); sym_signatureAlgorithm->verify = (UA_StatusCode (*)(const UA_SecurityPolicy *, void *, const UA_ByteString *, const UA_ByteString *))sym_verify_sp_basic256; sym_signatureAlgorithm->sign = (UA_StatusCode (*)(const UA_SecurityPolicy *, void *, const UA_ByteString *, UA_ByteString *))sym_sign_sp_basic256; sym_signatureAlgorithm->getLocalSignatureSize = sym_getSignatureSize_sp_basic256; sym_signatureAlgorithm->getRemoteSignatureSize = sym_getSignatureSize_sp_basic256; sym_signatureAlgorithm->getLocalKeyLength = (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getSigningKeyLength_sp_basic256; sym_signatureAlgorithm->getRemoteKeyLength = (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getSigningKeyLength_sp_basic256; UA_SecurityPolicyEncryptionAlgorithm *sym_encryptionAlgorithm = &symmetricModule->cryptoModule.encryptionAlgorithm; sym_encryptionAlgorithm->uri = UA_STRING("http://www.w3.org/2001/04/xmlenc#aes256-cbc\0"); sym_encryptionAlgorithm->encrypt = (UA_StatusCode(*)(const UA_SecurityPolicy *, void *, UA_ByteString *))sym_encrypt_sp_basic256; sym_encryptionAlgorithm->decrypt = (UA_StatusCode(*)(const UA_SecurityPolicy *, void *, UA_ByteString *))sym_decrypt_sp_basic256; sym_encryptionAlgorithm->getLocalKeyLength = sym_getEncryptionKeyLength_sp_basic256; sym_encryptionAlgorithm->getRemoteKeyLength = sym_getEncryptionKeyLength_sp_basic256; sym_encryptionAlgorithm->getLocalBlockSize = (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getEncryptionBlockSize_sp_basic256; sym_encryptionAlgorithm->getRemoteBlockSize = (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getEncryptionBlockSize_sp_basic256; sym_encryptionAlgorithm->getLocalPlainTextBlockSize = (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getPlainTextBlockSize_sp_basic256; sym_encryptionAlgorithm->getRemotePlainTextBlockSize = (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getPlainTextBlockSize_sp_basic256; symmetricModule->secureChannelNonceLength = 32; // Use the same signature algorithm as the asymmetric component for certificate signing (see standard) policy->certificateSigningAlgorithm = policy->asymmetricModule.cryptoModule.signatureAlgorithm; /* ChannelModule */ channelModule->newContext = channelContext_newContext_sp_basic256; channelModule->deleteContext = (void (*)(void *)) channelContext_deleteContext_sp_basic256; channelModule->setLocalSymEncryptingKey = (UA_StatusCode (*)(void *, const UA_ByteString *)) channelContext_setLocalSymEncryptingKey_sp_basic256; channelModule->setLocalSymSigningKey = (UA_StatusCode (*)(void *, const UA_ByteString *)) channelContext_setLocalSymSigningKey_sp_basic256; channelModule->setLocalSymIv = (UA_StatusCode (*)(void *, const UA_ByteString *)) channelContext_setLocalSymIv_sp_basic256; channelModule->setRemoteSymEncryptingKey = (UA_StatusCode (*)(void *, const UA_ByteString *)) channelContext_setRemoteSymEncryptingKey_sp_basic256; channelModule->setRemoteSymSigningKey = (UA_StatusCode (*)(void *, const UA_ByteString *)) channelContext_setRemoteSymSigningKey_sp_basic256; channelModule->setRemoteSymIv = (UA_StatusCode (*)(void *, const UA_ByteString *)) channelContext_setRemoteSymIv_sp_basic256; channelModule->compareCertificate = (UA_StatusCode (*)(const void *, const UA_ByteString *)) channelContext_compareCertificate_sp_basic256; policy->updateCertificateAndPrivateKey = updateCertificateAndPrivateKey_sp_basic256; policy->deleteMembers = deleteMembers_sp_basic256; return policyContext_newContext_sp_basic256(policy, localPrivateKey); } #endif /*********************************** amalgamated original file "/home/jvoe/open62541/plugins/securityPolicies/ua_securitypolicy_basic256sha256.c" ***********************************/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2018 (c) Mark Giraud, Fraunhofer IOSB * Copyright 2018 (c) Daniel Feist, Precitec GmbH & Co. KG */ #ifdef UA_ENABLE_ENCRYPTION #include #include #include #include #include #include #include #include #include #include /* Notes: * mbedTLS' AES allows in-place encryption and decryption. Sow we don't have to * allocate temp buffers. * https://tls.mbed.org/discussions/generic/in-place-decryption-with-aes256-same-input-output-buffer */ #define UA_SECURITYPOLICY_BASIC256SHA256_RSAPADDING_LEN 42 #define UA_SHA1_LENGTH 20 #define UA_SHA256_LENGTH 32 #define UA_BASIC256SHA256_SYM_SIGNING_KEY_LENGTH 32 #define UA_SECURITYPOLICY_BASIC256SHA256_SYM_KEY_LENGTH 32 #define UA_SECURITYPOLICY_BASIC256SHA256_SYM_ENCRYPTION_BLOCK_SIZE 16 #define UA_SECURITYPOLICY_BASIC256SHA256_SYM_PLAIN_TEXT_BLOCK_SIZE 16 #define UA_SECURITYPOLICY_BASIC256SHA256_MINASYMKEYLENGTH 256 #define UA_SECURITYPOLICY_BASIC256SHA256_MAXASYMKEYLENGTH 512 typedef struct { const UA_SecurityPolicy *securityPolicy; UA_ByteString localCertThumbprint; mbedtls_ctr_drbg_context drbgContext; mbedtls_entropy_context entropyContext; mbedtls_md_context_t sha256MdContext; mbedtls_pk_context localPrivateKey; } Basic256Sha256_PolicyContext; typedef struct { Basic256Sha256_PolicyContext *policyContext; UA_ByteString localSymSigningKey; UA_ByteString localSymEncryptingKey; UA_ByteString localSymIv; UA_ByteString remoteSymSigningKey; UA_ByteString remoteSymEncryptingKey; UA_ByteString remoteSymIv; mbedtls_x509_crt remoteCertificate; } Basic256Sha256_ChannelContext; /********************/ /* AsymmetricModule */ /********************/ /* VERIFY AsymmetricSignatureAlgorithm_RSA-PKCS15-SHA2-256 */ static UA_StatusCode asym_verify_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy, Basic256Sha256_ChannelContext *cc, const UA_ByteString *message, const UA_ByteString *signature) { if(securityPolicy == NULL || message == NULL || signature == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; unsigned char hash[UA_SHA256_LENGTH]; #if MBEDTLS_VERSION_NUMBER >= 0x02070000 // TODO check return status mbedtls_sha256_ret(message->data, message->length, hash, 0); #else mbedtls_sha256(message->data, message->length, hash, 0); #endif /* Set the RSA settings */ mbedtls_rsa_context *rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); mbedtls_rsa_set_padding(rsaContext, MBEDTLS_RSA_PKCS_V15, MBEDTLS_MD_SHA256); /* For RSA keys, the default padding type is PKCS#1 v1.5 in mbedtls_pk_verify() */ /* Alternatively, use more specific function mbedtls_rsa_rsassa_pkcs1_v15_verify(), i.e. */ /* int mbedErr = mbedtls_rsa_rsassa_pkcs1_v15_verify(rsaContext, NULL, NULL, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA256, UA_SHA256_LENGTH, hash, signature->data); */ int mbedErr = mbedtls_pk_verify(&cc->remoteCertificate.pk, MBEDTLS_MD_SHA256, hash, UA_SHA256_LENGTH, signature->data, signature->length); if(mbedErr) return UA_STATUSCODE_BADSECURITYCHECKSFAILED; return UA_STATUSCODE_GOOD; } /* AsymmetricSignatureAlgorithm_RSA-PKCS15-SHA2-256 */ static UA_StatusCode asym_sign_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy, Basic256Sha256_ChannelContext *cc, const UA_ByteString *message, UA_ByteString *signature) { if(securityPolicy == NULL || message == NULL || signature == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; unsigned char hash[UA_SHA256_LENGTH]; #if MBEDTLS_VERSION_NUMBER >= 0x02070000 // TODO check return status mbedtls_sha256_ret(message->data, message->length, hash, 0); #else mbedtls_sha256(message->data, message->length, hash, 0); #endif Basic256Sha256_PolicyContext *pc = cc->policyContext; mbedtls_rsa_context *rsaContext = mbedtls_pk_rsa(pc->localPrivateKey); mbedtls_rsa_set_padding(rsaContext, MBEDTLS_RSA_PKCS_V15, MBEDTLS_MD_SHA256); size_t sigLen = 0; /* For RSA keys, the default padding type is PKCS#1 v1.5 in mbedtls_pk_sign */ /* Alternatively use more specific function mbedtls_rsa_rsassa_pkcs1_v15_sign() */ int mbedErr = mbedtls_pk_sign(&pc->localPrivateKey, MBEDTLS_MD_SHA256, hash, UA_SHA256_LENGTH, signature->data, &sigLen, mbedtls_ctr_drbg_random, &pc->drbgContext); if(mbedErr) return UA_STATUSCODE_BADINTERNALERROR; return UA_STATUSCODE_GOOD; } static size_t asym_getLocalSignatureSize_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy, const Basic256Sha256_ChannelContext *cc) { if(securityPolicy == NULL || cc == NULL) return 0; return mbedtls_pk_rsa(cc->policyContext->localPrivateKey)->len; } static size_t asym_getRemoteSignatureSize_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy, const Basic256Sha256_ChannelContext *cc) { if(securityPolicy == NULL || cc == NULL) return 0; return mbedtls_pk_rsa(cc->remoteCertificate.pk)->len; } /* AsymmetricEncryptionAlgorithm_RSA-OAEP-SHA1 */ static UA_StatusCode asym_encrypt_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy, Basic256Sha256_ChannelContext *cc, UA_ByteString *data) { if(securityPolicy == NULL || cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; const size_t plainTextBlockSize = securityPolicy->asymmetricModule.cryptoModule. encryptionAlgorithm.getRemotePlainTextBlockSize(securityPolicy, cc); mbedtls_rsa_context *remoteRsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); mbedtls_rsa_set_padding(remoteRsaContext, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA1); return mbedtls_encrypt_rsaOaep(remoteRsaContext, &cc->policyContext->drbgContext, data, plainTextBlockSize); } /* AsymmetricEncryptionAlgorithm_RSA-OAEP-SHA1 */ static UA_StatusCode asym_decrypt_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy, Basic256Sha256_ChannelContext *cc, UA_ByteString *data) { if(securityPolicy == NULL || cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; return mbedtls_decrypt_rsaOaep(&cc->policyContext->localPrivateKey, &cc->policyContext->drbgContext, data); } static size_t asym_getRemoteEncryptionKeyLength_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy, const Basic256Sha256_ChannelContext *cc) { return mbedtls_pk_get_len(&cc->remoteCertificate.pk) * 8; } static size_t asym_getRemoteBlockSize_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy, const Basic256Sha256_ChannelContext *cc) { mbedtls_rsa_context *const rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); return rsaContext->len; } static size_t asym_getRemotePlainTextBlockSize_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy, const Basic256Sha256_ChannelContext *cc) { mbedtls_rsa_context *const rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); return rsaContext->len - UA_SECURITYPOLICY_BASIC256SHA256_RSAPADDING_LEN; } static UA_StatusCode asym_makeThumbprint_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy, const UA_ByteString *certificate, UA_ByteString *thumbprint) { if(securityPolicy == NULL || certificate == NULL || thumbprint == NULL) return UA_STATUSCODE_BADINTERNALERROR; return mbedtls_thumbprint_sha1(certificate, thumbprint); } static UA_StatusCode asymmetricModule_compareCertificateThumbprint_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy, const UA_ByteString *certificateThumbprint) { if(securityPolicy == NULL || certificateThumbprint == NULL) return UA_STATUSCODE_BADINTERNALERROR; Basic256Sha256_PolicyContext *pc = (Basic256Sha256_PolicyContext *)securityPolicy->policyContext; if(!UA_ByteString_equal(certificateThumbprint, &pc->localCertThumbprint)) return UA_STATUSCODE_BADCERTIFICATEINVALID; return UA_STATUSCODE_GOOD; } /*******************/ /* SymmetricModule */ /*******************/ static UA_StatusCode sym_verify_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy, Basic256Sha256_ChannelContext *cc, const UA_ByteString *message, const UA_ByteString *signature) { if(securityPolicy == NULL || cc == NULL || message == NULL || signature == NULL) return UA_STATUSCODE_BADINTERNALERROR; /* Compute MAC */ if(signature->length != UA_SHA256_LENGTH) { UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Signature size does not have the desired size defined by the security policy"); return UA_STATUSCODE_BADSECURITYCHECKSFAILED; } Basic256Sha256_PolicyContext *pc = (Basic256Sha256_PolicyContext *)securityPolicy->policyContext; unsigned char mac[UA_SHA256_LENGTH]; mbedtls_hmac(&pc->sha256MdContext, &cc->remoteSymSigningKey, message, mac); /* Compare with Signature */ if(!UA_constantTimeEqual(signature->data, mac, UA_SHA256_LENGTH)) return UA_STATUSCODE_BADSECURITYCHECKSFAILED; return UA_STATUSCODE_GOOD; } static UA_StatusCode sym_sign_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy, const Basic256Sha256_ChannelContext *cc, const UA_ByteString *message, UA_ByteString *signature) { if(signature->length != UA_SHA256_LENGTH) return UA_STATUSCODE_BADINTERNALERROR; mbedtls_hmac(&cc->policyContext->sha256MdContext, &cc->localSymSigningKey, message, signature->data); return UA_STATUSCODE_GOOD; } static size_t sym_getSignatureSize_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy, const void *channelContext) { return UA_SHA256_LENGTH; } static size_t sym_getSigningKeyLength_sp_basic256sha256(const UA_SecurityPolicy *const securityPolicy, const void *const channelContext) { return UA_BASIC256SHA256_SYM_SIGNING_KEY_LENGTH; } static size_t sym_getEncryptionKeyLength_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy, const void *channelContext) { return UA_SECURITYPOLICY_BASIC256SHA256_SYM_KEY_LENGTH; } static size_t sym_getEncryptionBlockSize_sp_basic256sha256(const UA_SecurityPolicy *const securityPolicy, const void *const channelContext) { return UA_SECURITYPOLICY_BASIC256SHA256_SYM_ENCRYPTION_BLOCK_SIZE; } static size_t sym_getPlainTextBlockSize_sp_basic256sha256(const UA_SecurityPolicy *const securityPolicy, const void *const channelContext) { return UA_SECURITYPOLICY_BASIC256SHA256_SYM_PLAIN_TEXT_BLOCK_SIZE; } static UA_StatusCode sym_encrypt_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy, const Basic256Sha256_ChannelContext *cc, UA_ByteString *data) { if(securityPolicy == NULL || cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; if(cc->localSymIv.length != securityPolicy->symmetricModule.cryptoModule. encryptionAlgorithm.getLocalBlockSize(securityPolicy, cc)) return UA_STATUSCODE_BADINTERNALERROR; size_t plainTextBlockSize = securityPolicy->symmetricModule.cryptoModule. encryptionAlgorithm.getLocalPlainTextBlockSize(securityPolicy, cc); if(data->length % plainTextBlockSize != 0) { UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Length of data to encrypt is not a multiple of the plain text block size." "Padding might not have been calculated appropriately."); return UA_STATUSCODE_BADINTERNALERROR; } /* Keylength in bits */ unsigned int keylength = (unsigned int)(cc->localSymEncryptingKey.length * 8); mbedtls_aes_context aesContext; int mbedErr = mbedtls_aes_setkey_enc(&aesContext, cc->localSymEncryptingKey.data, keylength); if(mbedErr) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString ivCopy; UA_StatusCode retval = UA_ByteString_copy(&cc->localSymIv, &ivCopy); if(retval != UA_STATUSCODE_GOOD) return retval; mbedErr = mbedtls_aes_crypt_cbc(&aesContext, MBEDTLS_AES_ENCRYPT, data->length, ivCopy.data, data->data, data->data); if(mbedErr) retval = UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_deleteMembers(&ivCopy); return retval; } static UA_StatusCode sym_decrypt_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy, const Basic256Sha256_ChannelContext *cc, UA_ByteString *data) { if(securityPolicy == NULL || cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; size_t encryptionBlockSize = securityPolicy->symmetricModule.cryptoModule. encryptionAlgorithm.getRemoteBlockSize(securityPolicy, cc); if(cc->remoteSymIv.length != encryptionBlockSize) return UA_STATUSCODE_BADINTERNALERROR; if(data->length % encryptionBlockSize != 0) { UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Length of data to decrypt is not a multiple of the encryptingBlock size."); return UA_STATUSCODE_BADINTERNALERROR; } unsigned int keylength = (unsigned int)(cc->remoteSymEncryptingKey.length * 8); mbedtls_aes_context aesContext; int mbedErr = mbedtls_aes_setkey_dec(&aesContext, cc->remoteSymEncryptingKey.data, keylength); if(mbedErr) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString ivCopy; UA_StatusCode retval = UA_ByteString_copy(&cc->remoteSymIv, &ivCopy); if(retval != UA_STATUSCODE_GOOD) return retval; mbedErr = mbedtls_aes_crypt_cbc(&aesContext, MBEDTLS_AES_DECRYPT, data->length, ivCopy.data, data->data, data->data); if(mbedErr) retval = UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_deleteMembers(&ivCopy); return retval; } static UA_StatusCode sym_generateKey_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy, const UA_ByteString *secret, const UA_ByteString *seed, UA_ByteString *out) { if(securityPolicy == NULL || secret == NULL || seed == NULL || out == NULL) return UA_STATUSCODE_BADINTERNALERROR; Basic256Sha256_PolicyContext *pc = (Basic256Sha256_PolicyContext *)securityPolicy->policyContext; return mbedtls_generateKey(&pc->sha256MdContext, secret, seed, out); } static UA_StatusCode sym_generateNonce_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy, UA_ByteString *out) { if(securityPolicy == NULL || securityPolicy->policyContext == NULL || out == NULL) return UA_STATUSCODE_BADINTERNALERROR; Basic256Sha256_PolicyContext *pc = (Basic256Sha256_PolicyContext *)securityPolicy->policyContext; int mbedErr = mbedtls_ctr_drbg_random(&pc->drbgContext, out->data, out->length); if(mbedErr) return UA_STATUSCODE_BADUNEXPECTEDERROR; return UA_STATUSCODE_GOOD; } /*****************/ /* ChannelModule */ /*****************/ /* Assumes that the certificate has been verified externally */ static UA_StatusCode parseRemoteCertificate_sp_basic256sha256(Basic256Sha256_ChannelContext *cc, const UA_ByteString *remoteCertificate) { if(remoteCertificate == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; /* Parse the certificate */ int mbedErr = mbedtls_x509_crt_parse(&cc->remoteCertificate, remoteCertificate->data, remoteCertificate->length); if(mbedErr) return UA_STATUSCODE_BADSECURITYCHECKSFAILED; /* Check the key length */ mbedtls_rsa_context *rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); if(rsaContext->len < UA_SECURITYPOLICY_BASIC256SHA256_MINASYMKEYLENGTH || rsaContext->len > UA_SECURITYPOLICY_BASIC256SHA256_MAXASYMKEYLENGTH) return UA_STATUSCODE_BADCERTIFICATEUSENOTALLOWED; return UA_STATUSCODE_GOOD; } static void channelContext_deleteContext_sp_basic256sha256(Basic256Sha256_ChannelContext *cc) { UA_ByteString_deleteMembers(&cc->localSymSigningKey); UA_ByteString_deleteMembers(&cc->localSymEncryptingKey); UA_ByteString_deleteMembers(&cc->localSymIv); UA_ByteString_deleteMembers(&cc->remoteSymSigningKey); UA_ByteString_deleteMembers(&cc->remoteSymEncryptingKey); UA_ByteString_deleteMembers(&cc->remoteSymIv); mbedtls_x509_crt_free(&cc->remoteCertificate); UA_free(cc); } static UA_StatusCode channelContext_newContext_sp_basic256sha256(const UA_SecurityPolicy *securityPolicy, const UA_ByteString *remoteCertificate, void **pp_contextData) { if(securityPolicy == NULL || remoteCertificate == NULL || pp_contextData == NULL) return UA_STATUSCODE_BADINTERNALERROR; /* Allocate the channel context */ *pp_contextData = UA_malloc(sizeof(Basic256Sha256_ChannelContext)); if(*pp_contextData == NULL) return UA_STATUSCODE_BADOUTOFMEMORY; Basic256Sha256_ChannelContext *cc = (Basic256Sha256_ChannelContext *)*pp_contextData; /* Initialize the channel context */ cc->policyContext = (Basic256Sha256_PolicyContext *)securityPolicy->policyContext; UA_ByteString_init(&cc->localSymSigningKey); UA_ByteString_init(&cc->localSymEncryptingKey); UA_ByteString_init(&cc->localSymIv); UA_ByteString_init(&cc->remoteSymSigningKey); UA_ByteString_init(&cc->remoteSymEncryptingKey); UA_ByteString_init(&cc->remoteSymIv); mbedtls_x509_crt_init(&cc->remoteCertificate); // TODO: this can be optimized so that we dont allocate memory before parsing the certificate UA_StatusCode retval = parseRemoteCertificate_sp_basic256sha256(cc, remoteCertificate); if(retval != UA_STATUSCODE_GOOD) { channelContext_deleteContext_sp_basic256sha256(cc); *pp_contextData = NULL; } return retval; } static UA_StatusCode channelContext_setLocalSymEncryptingKey_sp_basic256sha256(Basic256Sha256_ChannelContext *cc, const UA_ByteString *key) { if(key == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_deleteMembers(&cc->localSymEncryptingKey); return UA_ByteString_copy(key, &cc->localSymEncryptingKey); } static UA_StatusCode channelContext_setLocalSymSigningKey_sp_basic256sha256(Basic256Sha256_ChannelContext *cc, const UA_ByteString *key) { if(key == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_deleteMembers(&cc->localSymSigningKey); return UA_ByteString_copy(key, &cc->localSymSigningKey); } static UA_StatusCode channelContext_setLocalSymIv_sp_basic256sha256(Basic256Sha256_ChannelContext *cc, const UA_ByteString *iv) { if(iv == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_deleteMembers(&cc->localSymIv); return UA_ByteString_copy(iv, &cc->localSymIv); } static UA_StatusCode channelContext_setRemoteSymEncryptingKey_sp_basic256sha256(Basic256Sha256_ChannelContext *cc, const UA_ByteString *key) { if(key == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_deleteMembers(&cc->remoteSymEncryptingKey); return UA_ByteString_copy(key, &cc->remoteSymEncryptingKey); } static UA_StatusCode channelContext_setRemoteSymSigningKey_sp_basic256sha256(Basic256Sha256_ChannelContext *cc, const UA_ByteString *key) { if(key == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_deleteMembers(&cc->remoteSymSigningKey); return UA_ByteString_copy(key, &cc->remoteSymSigningKey); } static UA_StatusCode channelContext_setRemoteSymIv_sp_basic256sha256(Basic256Sha256_ChannelContext *cc, const UA_ByteString *iv) { if(iv == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_deleteMembers(&cc->remoteSymIv); return UA_ByteString_copy(iv, &cc->remoteSymIv); } static UA_StatusCode channelContext_compareCertificate_sp_basic256sha256(const Basic256Sha256_ChannelContext *cc, const UA_ByteString *certificate) { if(cc == NULL || certificate == NULL) return UA_STATUSCODE_BADINTERNALERROR; mbedtls_x509_crt cert; mbedtls_x509_crt_init(&cert); int mbedErr = mbedtls_x509_crt_parse(&cert, certificate->data, certificate->length); if(mbedErr) return UA_STATUSCODE_BADSECURITYCHECKSFAILED; UA_StatusCode retval = UA_STATUSCODE_GOOD; if(cert.raw.len != cc->remoteCertificate.raw.len || memcmp(cert.raw.p, cc->remoteCertificate.raw.p, cert.raw.len) != 0) retval = UA_STATUSCODE_BADSECURITYCHECKSFAILED; mbedtls_x509_crt_free(&cert); return retval; } static void deleteMembers_sp_basic256sha256(UA_SecurityPolicy *securityPolicy) { if(securityPolicy == NULL) return; if(securityPolicy->policyContext == NULL) return; UA_ByteString_deleteMembers(&securityPolicy->localCertificate); /* delete all allocated members in the context */ Basic256Sha256_PolicyContext *pc = (Basic256Sha256_PolicyContext *) securityPolicy->policyContext; mbedtls_ctr_drbg_free(&pc->drbgContext); mbedtls_entropy_free(&pc->entropyContext); mbedtls_pk_free(&pc->localPrivateKey); mbedtls_md_free(&pc->sha256MdContext); UA_ByteString_deleteMembers(&pc->localCertThumbprint); UA_LOG_DEBUG(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Deleted members of EndpointContext for sp_basic256sha256"); UA_free(pc); securityPolicy->policyContext = NULL; } static UA_StatusCode updateCertificateAndPrivateKey_sp_basic256sha256(UA_SecurityPolicy *securityPolicy, const UA_ByteString newCertificate, const UA_ByteString newPrivateKey) { if(securityPolicy == NULL) return UA_STATUSCODE_BADINTERNALERROR; if(securityPolicy->policyContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; Basic256Sha256_PolicyContext *pc = (Basic256Sha256_PolicyContext *) securityPolicy->policyContext; UA_ByteString_deleteMembers(&securityPolicy->localCertificate); UA_StatusCode retval = UA_ByteString_allocBuffer(&securityPolicy->localCertificate, newCertificate.length + 1); if(retval != UA_STATUSCODE_GOOD) return retval; memcpy(securityPolicy->localCertificate.data, newCertificate.data, newCertificate.length); securityPolicy->localCertificate.data[newCertificate.length] = '\0'; securityPolicy->localCertificate.length--; /* Set the new private key */ mbedtls_pk_free(&pc->localPrivateKey); mbedtls_pk_init(&pc->localPrivateKey); int mbedErr = mbedtls_pk_parse_key(&pc->localPrivateKey, newPrivateKey.data, newPrivateKey.length, NULL, 0); if(mbedErr) { retval = UA_STATUSCODE_BADSECURITYCHECKSFAILED; goto error; } retval = asym_makeThumbprint_sp_basic256sha256(pc->securityPolicy, &securityPolicy->localCertificate, &pc->localCertThumbprint); if(retval != UA_STATUSCODE_GOOD) goto error; return retval; error: UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Could not update certificate and private key"); if(securityPolicy->policyContext != NULL) deleteMembers_sp_basic256sha256(securityPolicy); return retval; } static UA_StatusCode policyContext_newContext_sp_basic256sha256(UA_SecurityPolicy *securityPolicy, const UA_ByteString localPrivateKey) { UA_StatusCode retval = UA_STATUSCODE_GOOD; if(securityPolicy == NULL) return UA_STATUSCODE_BADINTERNALERROR; if (localPrivateKey.length == 0) { UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Can not initialize security policy. Private key is empty."); return UA_STATUSCODE_BADINVALIDARGUMENT; } Basic256Sha256_PolicyContext *pc = (Basic256Sha256_PolicyContext *) UA_malloc(sizeof(Basic256Sha256_PolicyContext)); securityPolicy->policyContext = (void *)pc; if(!pc) { retval = UA_STATUSCODE_BADOUTOFMEMORY; goto error; } /* Initialize the PolicyContext */ memset(pc, 0, sizeof(Basic256Sha256_PolicyContext)); mbedtls_ctr_drbg_init(&pc->drbgContext); mbedtls_entropy_init(&pc->entropyContext); mbedtls_pk_init(&pc->localPrivateKey); mbedtls_md_init(&pc->sha256MdContext); pc->securityPolicy = securityPolicy; /* Initialized the message digest */ const mbedtls_md_info_t *const mdInfo = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); int mbedErr = mbedtls_md_setup(&pc->sha256MdContext, mdInfo, MBEDTLS_MD_SHA256); if(mbedErr) { retval = UA_STATUSCODE_BADOUTOFMEMORY; goto error; } /* Add the system entropy source */ mbedErr = mbedtls_entropy_add_source(&pc->entropyContext, mbedtls_platform_entropy_poll, NULL, 0, MBEDTLS_ENTROPY_SOURCE_STRONG); if(mbedErr) { retval = UA_STATUSCODE_BADSECURITYCHECKSFAILED; goto error; } /* Seed the RNG */ char *personalization = "open62541-drbg"; mbedErr = mbedtls_ctr_drbg_seed(&pc->drbgContext, mbedtls_entropy_func, &pc->entropyContext, (const unsigned char *)personalization, 14); if(mbedErr) { retval = UA_STATUSCODE_BADSECURITYCHECKSFAILED; goto error; } /* Set the private key */ mbedErr = mbedtls_pk_parse_key(&pc->localPrivateKey, localPrivateKey.data, localPrivateKey.length, NULL, 0); if(mbedErr) { retval = UA_STATUSCODE_BADSECURITYCHECKSFAILED; goto error; } /* Set the local certificate thumbprint */ retval = UA_ByteString_allocBuffer(&pc->localCertThumbprint, UA_SHA1_LENGTH); if(retval != UA_STATUSCODE_GOOD) goto error; retval = asym_makeThumbprint_sp_basic256sha256(pc->securityPolicy, &securityPolicy->localCertificate, &pc->localCertThumbprint); if(retval != UA_STATUSCODE_GOOD) goto error; return UA_STATUSCODE_GOOD; error: UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Could not create securityContext: %s", UA_StatusCode_name(retval)); if(securityPolicy->policyContext != NULL) deleteMembers_sp_basic256sha256(securityPolicy); return retval; } UA_StatusCode UA_SecurityPolicy_Basic256Sha256(UA_SecurityPolicy *policy, UA_CertificateVerification *certificateVerification, const UA_ByteString localCertificate, const UA_ByteString localPrivateKey, const UA_Logger *logger) { memset(policy, 0, sizeof(UA_SecurityPolicy)); policy->logger = logger; policy->policyUri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256"); UA_SecurityPolicyAsymmetricModule *const asymmetricModule = &policy->asymmetricModule; UA_SecurityPolicySymmetricModule *const symmetricModule = &policy->symmetricModule; UA_SecurityPolicyChannelModule *const channelModule = &policy->channelModule; /* Copy the certificate and add a NULL to the end */ UA_StatusCode retval = UA_ByteString_allocBuffer(&policy->localCertificate, localCertificate.length + 1); if(retval != UA_STATUSCODE_GOOD) return retval; memcpy(policy->localCertificate.data, localCertificate.data, localCertificate.length); policy->localCertificate.data[localCertificate.length] = '\0'; policy->localCertificate.length--; policy->certificateVerification = certificateVerification; /* AsymmetricModule */ UA_SecurityPolicySignatureAlgorithm *asym_signatureAlgorithm = &asymmetricModule->cryptoModule.signatureAlgorithm; asym_signatureAlgorithm->uri = UA_STRING("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\0"); asym_signatureAlgorithm->verify = (UA_StatusCode (*)(const UA_SecurityPolicy *, void *, const UA_ByteString *, const UA_ByteString *))asym_verify_sp_basic256sha256; asym_signatureAlgorithm->sign = (UA_StatusCode (*)(const UA_SecurityPolicy *, void *, const UA_ByteString *, UA_ByteString *))asym_sign_sp_basic256sha256; asym_signatureAlgorithm->getLocalSignatureSize = (size_t (*)(const UA_SecurityPolicy *, const void *))asym_getLocalSignatureSize_sp_basic256sha256; asym_signatureAlgorithm->getRemoteSignatureSize = (size_t (*)(const UA_SecurityPolicy *, const void *))asym_getRemoteSignatureSize_sp_basic256sha256; asym_signatureAlgorithm->getLocalKeyLength = NULL; // TODO: Write function asym_signatureAlgorithm->getRemoteKeyLength = NULL; // TODO: Write function UA_SecurityPolicyEncryptionAlgorithm *asym_encryptionAlgorithm = &asymmetricModule->cryptoModule.encryptionAlgorithm; asym_encryptionAlgorithm->uri = UA_STRING("http://www.w3.org/2001/04/xmlenc#rsa-oaep\0"); asym_encryptionAlgorithm->encrypt = (UA_StatusCode(*)(const UA_SecurityPolicy *, void *, UA_ByteString *))asym_encrypt_sp_basic256sha256; asym_encryptionAlgorithm->decrypt = (UA_StatusCode(*)(const UA_SecurityPolicy *, void *, UA_ByteString *)) asym_decrypt_sp_basic256sha256; asym_encryptionAlgorithm->getLocalKeyLength = NULL; // TODO: Write function asym_encryptionAlgorithm->getRemoteKeyLength = (size_t (*)(const UA_SecurityPolicy *, const void *))asym_getRemoteEncryptionKeyLength_sp_basic256sha256; asym_encryptionAlgorithm->getLocalBlockSize = NULL; // TODO: Write function asym_encryptionAlgorithm->getRemoteBlockSize = (size_t (*)(const UA_SecurityPolicy *, const void *))asym_getRemoteBlockSize_sp_basic256sha256; asym_encryptionAlgorithm->getLocalPlainTextBlockSize = NULL; // TODO: Write function asym_encryptionAlgorithm->getRemotePlainTextBlockSize = (size_t (*)(const UA_SecurityPolicy *, const void *))asym_getRemotePlainTextBlockSize_sp_basic256sha256; asymmetricModule->makeCertificateThumbprint = asym_makeThumbprint_sp_basic256sha256; asymmetricModule->compareCertificateThumbprint = asymmetricModule_compareCertificateThumbprint_sp_basic256sha256; /* SymmetricModule */ symmetricModule->generateKey = sym_generateKey_sp_basic256sha256; symmetricModule->generateNonce = sym_generateNonce_sp_basic256sha256; UA_SecurityPolicySignatureAlgorithm *sym_signatureAlgorithm = &symmetricModule->cryptoModule.signatureAlgorithm; sym_signatureAlgorithm->uri = UA_STRING("http://www.w3.org/2000/09/xmldsig#hmac-sha1\0"); sym_signatureAlgorithm->verify = (UA_StatusCode (*)(const UA_SecurityPolicy *, void *, const UA_ByteString *, const UA_ByteString *))sym_verify_sp_basic256sha256; sym_signatureAlgorithm->sign = (UA_StatusCode (*)(const UA_SecurityPolicy *, void *, const UA_ByteString *, UA_ByteString *))sym_sign_sp_basic256sha256; sym_signatureAlgorithm->getLocalSignatureSize = sym_getSignatureSize_sp_basic256sha256; sym_signatureAlgorithm->getRemoteSignatureSize = sym_getSignatureSize_sp_basic256sha256; sym_signatureAlgorithm->getLocalKeyLength = (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getSigningKeyLength_sp_basic256sha256; sym_signatureAlgorithm->getRemoteKeyLength = (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getSigningKeyLength_sp_basic256sha256; UA_SecurityPolicyEncryptionAlgorithm *sym_encryptionAlgorithm = &symmetricModule->cryptoModule.encryptionAlgorithm; sym_encryptionAlgorithm->uri = UA_STRING("http://www.w3.org/2001/04/xmlenc#aes128-cbc"); sym_encryptionAlgorithm->encrypt = (UA_StatusCode(*)(const UA_SecurityPolicy *, void *, UA_ByteString *))sym_encrypt_sp_basic256sha256; sym_encryptionAlgorithm->decrypt = (UA_StatusCode(*)(const UA_SecurityPolicy *, void *, UA_ByteString *))sym_decrypt_sp_basic256sha256; sym_encryptionAlgorithm->getLocalKeyLength = sym_getEncryptionKeyLength_sp_basic256sha256; sym_encryptionAlgorithm->getRemoteKeyLength = sym_getEncryptionKeyLength_sp_basic256sha256; sym_encryptionAlgorithm->getLocalBlockSize = (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getEncryptionBlockSize_sp_basic256sha256; sym_encryptionAlgorithm->getRemoteBlockSize = (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getEncryptionBlockSize_sp_basic256sha256; sym_encryptionAlgorithm->getLocalPlainTextBlockSize = (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getPlainTextBlockSize_sp_basic256sha256; sym_encryptionAlgorithm->getRemotePlainTextBlockSize = (size_t (*)(const UA_SecurityPolicy *, const void *))sym_getPlainTextBlockSize_sp_basic256sha256; symmetricModule->secureChannelNonceLength = 32; // Use the same signature algorithm as the asymmetric component for certificate signing (see standard) policy->certificateSigningAlgorithm = policy->asymmetricModule.cryptoModule.signatureAlgorithm; /* ChannelModule */ channelModule->newContext = channelContext_newContext_sp_basic256sha256; channelModule->deleteContext = (void (*)(void *)) channelContext_deleteContext_sp_basic256sha256; channelModule->setLocalSymEncryptingKey = (UA_StatusCode (*)(void *, const UA_ByteString *)) channelContext_setLocalSymEncryptingKey_sp_basic256sha256; channelModule->setLocalSymSigningKey = (UA_StatusCode (*)(void *, const UA_ByteString *)) channelContext_setLocalSymSigningKey_sp_basic256sha256; channelModule->setLocalSymIv = (UA_StatusCode (*)(void *, const UA_ByteString *)) channelContext_setLocalSymIv_sp_basic256sha256; channelModule->setRemoteSymEncryptingKey = (UA_StatusCode (*)(void *, const UA_ByteString *)) channelContext_setRemoteSymEncryptingKey_sp_basic256sha256; channelModule->setRemoteSymSigningKey = (UA_StatusCode (*)(void *, const UA_ByteString *)) channelContext_setRemoteSymSigningKey_sp_basic256sha256; channelModule->setRemoteSymIv = (UA_StatusCode (*)(void *, const UA_ByteString *)) channelContext_setRemoteSymIv_sp_basic256sha256; channelModule->compareCertificate = (UA_StatusCode (*)(const void *, const UA_ByteString *)) channelContext_compareCertificate_sp_basic256sha256; policy->updateCertificateAndPrivateKey = updateCertificateAndPrivateKey_sp_basic256sha256; policy->deleteMembers = deleteMembers_sp_basic256sha256; return policyContext_newContext_sp_basic256sha256(policy, localPrivateKey); } #endif /*********************************** amalgamated original file "/home/jvoe/open62541/arch/posix/ua_clock.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 2016-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2017 (c) Thomas Stalder, Blue Time Concept SA */ #ifdef UA_ARCHITECTURE_POSIX #include #include #if defined(__APPLE__) || defined(__MACH__) # include # include #endif UA_DateTime UA_DateTime_now(void) { struct timeval tv; gettimeofday(&tv, NULL); return (tv.tv_sec * UA_DATETIME_SEC) + (tv.tv_usec * UA_DATETIME_USEC) + UA_DATETIME_UNIX_EPOCH; } /* Credit to https://stackoverflow.com/questions/13804095/get-the-time-zone-gmt-offset-in-c */ UA_Int64 UA_DateTime_localTimeUtcOffset(void) { time_t gmt, rawtime = time(NULL); struct tm *ptm; struct tm gbuf; ptm = gmtime_r(&rawtime, &gbuf); // Request that mktime() looksup dst in timezone database ptm->tm_isdst = -1; gmt = mktime(ptm); return (UA_Int64) (difftime(rawtime, gmt) * UA_DATETIME_SEC); } UA_DateTime UA_DateTime_nowMonotonic(void) { #if defined(__APPLE__) || defined(__MACH__) /* OS X does not have clock_gettime, use clock_get_time */ clock_serv_t cclock; mach_timespec_t mts; host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); clock_get_time(cclock, &mts); mach_port_deallocate(mach_task_self(), cclock); return (mts.tv_sec * UA_DATETIME_SEC) + (mts.tv_nsec / 100); #elif !defined(CLOCK_MONOTONIC_RAW) struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (ts.tv_sec * UA_DATETIME_SEC) + (ts.tv_nsec / 100); #else struct timespec ts; clock_gettime(CLOCK_MONOTONIC_RAW, &ts); return (ts.tv_sec * UA_DATETIME_SEC) + (ts.tv_nsec / 100); #endif } #endif /* UA_ARCHITECTURE_POSIX */ /*********************************** amalgamated original file "/home/jvoe/open62541/arch/posix/ua_architecture_functions.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 2018 (c) Jose Cabral, fortiss GmbH */ #ifdef UA_ARCHITECTURE_POSIX /* Global malloc singletons */ #ifdef UA_ENABLE_MALLOC_SINGLETON void * (*UA_globalMalloc)(size_t size) = malloc; void (*UA_globalFree)(void *ptr) = free; void * (*UA_globalCalloc)(size_t nelem, size_t elsize) = calloc; void * (*UA_globalRealloc)(void *ptr, size_t size) = realloc; #endif unsigned int UA_socket_set_blocking(UA_SOCKET sockfd){ int opts = fcntl(sockfd, F_GETFL); if(opts < 0 || fcntl(sockfd, F_SETFL, opts & (~O_NONBLOCK)) < 0) return UA_STATUSCODE_BADINTERNALERROR; return UA_STATUSCODE_GOOD; } unsigned int UA_socket_set_nonblocking(UA_SOCKET sockfd){ int opts = fcntl(sockfd, F_GETFL); if(opts < 0 || fcntl(sockfd, F_SETFL, opts | O_NONBLOCK) < 0) return UA_STATUSCODE_BADINTERNALERROR; return UA_STATUSCODE_GOOD; } void UA_initialize_architecture_network(void){ } void UA_deinitialize_architecture_network(void){ } #endif /* UA_ARCHITECTURE_POSIX */ /*********************************** amalgamated original file "/home/jvoe/open62541/arch/win32/ua_clock.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 2016-2017 (c) Julius Pfrommer, Fraunhofer IOSB * Copyright 2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2017 (c) Thomas Stalder */ #ifdef UA_ARCHITECTURE_WIN32 #ifndef _BSD_SOURCE # define _BSD_SOURCE #endif #include /* Backup definition of SLIST_ENTRY on mingw winnt.h */ # ifdef SLIST_ENTRY # pragma push_macro("SLIST_ENTRY") # undef SLIST_ENTRY # define POP_SLIST_ENTRY # endif # include /* restore definition */ # ifdef POP_SLIST_ENTRY # undef SLIST_ENTRY # undef POP_SLIST_ENTRY # pragma pop_macro("SLIST_ENTRY") # endif UA_DateTime UA_DateTime_now(void) { /* Windows filetime has the same definition as UA_DateTime */ FILETIME ft; SYSTEMTIME st; GetSystemTime(&st); SystemTimeToFileTime(&st, &ft); ULARGE_INTEGER ul; ul.LowPart = ft.dwLowDateTime; ul.HighPart = ft.dwHighDateTime; return (UA_DateTime)ul.QuadPart; } /* Credit to https://stackoverflow.com/questions/13804095/get-the-time-zone-gmt-offset-in-c */ UA_Int64 UA_DateTime_localTimeUtcOffset(void) { time_t gmt, rawtime = time(NULL); struct tm ptm; gmtime_s(&ptm, &rawtime); // Request that mktime() looksup dst in timezone database ptm.tm_isdst = -1; gmt = mktime(&ptm); return (UA_Int64) (difftime(rawtime, gmt) * UA_DATETIME_SEC); } UA_DateTime UA_DateTime_nowMonotonic(void) { LARGE_INTEGER freq, ticks; QueryPerformanceFrequency(&freq); QueryPerformanceCounter(&ticks); UA_Double ticks2dt = UA_DATETIME_SEC / (UA_Double)freq.QuadPart; return (UA_DateTime)(ticks.QuadPart * ticks2dt); } #endif /* UA_ARCHITECTURE_WIN32 */ /*********************************** amalgamated original file "/home/jvoe/open62541/arch/win32/ua_architecture_functions.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 2018 (c) Jose Cabral, fortiss GmbH */ #ifdef UA_ARCHITECTURE_WIN32 unsigned int UA_socket_set_blocking(UA_SOCKET sockfd){ u_long iMode = 0; if(ioctlsocket(sockfd, FIONBIO, &iMode) != NO_ERROR) return UA_STATUSCODE_BADINTERNALERROR; return UA_STATUSCODE_GOOD;; } unsigned int UA_socket_set_nonblocking(UA_SOCKET sockfd){ u_long iMode = 1; if(ioctlsocket(sockfd, FIONBIO, &iMode) != NO_ERROR) return UA_STATUSCODE_BADINTERNALERROR; return UA_STATUSCODE_GOOD;; } void UA_initialize_architecture_network(void){ WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); } void UA_deinitialize_architecture_network(void){ WSACleanup(); } #endif /* UA_ARCHITECTURE_WIN32 */ /*********************************** amalgamated original file "/home/jvoe/open62541/arch/network_tcp.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 2016-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2017 (c) frax2222 * Copyright 2017 (c) Jose Cabral * Copyright 2017 (c) Thomas Stalder, Blue Time Concept SA */ #define UA_INTERNAL #include // memset #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 #endif /****************************/ /* Generic Socket Functions */ /****************************/ static UA_StatusCode connection_getsendbuffer(UA_Connection *connection, size_t length, UA_ByteString *buf) { if(length > connection->config.sendBufferSize) return UA_STATUSCODE_BADCOMMUNICATIONERROR; return UA_ByteString_allocBuffer(buf, length); } static void connection_releasesendbuffer(UA_Connection *connection, UA_ByteString *buf) { UA_ByteString_deleteMembers(buf); } static void connection_releaserecvbuffer(UA_Connection *connection, UA_ByteString *buf) { UA_ByteString_deleteMembers(buf); } static UA_StatusCode connection_write(UA_Connection *connection, UA_ByteString *buf) { if(connection->state == UA_CONNECTION_CLOSED) { UA_ByteString_deleteMembers(buf); return UA_STATUSCODE_BADCONNECTIONCLOSED; } /* Prevent OS signals when sending to a closed socket */ int flags = 0; flags |= MSG_NOSIGNAL; /* Send the full buffer. This may require several calls to send */ size_t nWritten = 0; do { ssize_t n = 0; do { size_t bytes_to_send = buf->length - nWritten; n = UA_send(connection->sockfd, (const char*)buf->data + nWritten, bytes_to_send, flags); if(n < 0 && UA_ERRNO != UA_INTERRUPTED && UA_ERRNO != UA_AGAIN) { connection->close(connection); UA_ByteString_deleteMembers(buf); return UA_STATUSCODE_BADCONNECTIONCLOSED; } } while(n < 0); nWritten += (size_t)n; } while(nWritten < buf->length); /* Free the buffer */ UA_ByteString_deleteMembers(buf); return UA_STATUSCODE_GOOD; } static UA_StatusCode connection_recv(UA_Connection *connection, UA_ByteString *response, UA_UInt32 timeout) { if(connection->state == UA_CONNECTION_CLOSED) return UA_STATUSCODE_BADCONNECTIONCLOSED; /* Listen on the socket for the given timeout until a message arrives */ if(timeout > 0) { fd_set fdset; FD_ZERO(&fdset); UA_fd_set(connection->sockfd, &fdset); UA_UInt32 timeout_usec = timeout * 1000; struct timeval tmptv = {(long int)(timeout_usec / 1000000), (int)(timeout_usec % 1000000)}; int resultsize = UA_select(connection->sockfd+1, &fdset, NULL, NULL, &tmptv); /* No result */ if(resultsize == 0) return UA_STATUSCODE_GOODNONCRITICALTIMEOUT; if(resultsize == -1) { /* The call to select was interrupted manually. Act as if it timed * out */ if(UA_ERRNO == EINTR) return UA_STATUSCODE_GOODNONCRITICALTIMEOUT; /* The error cannot be recovered. Close the connection. */ connection->close(connection); return UA_STATUSCODE_BADCONNECTIONCLOSED; } } response->data = (UA_Byte*)UA_malloc(connection->config.recvBufferSize); if(!response->data) { response->length = 0; return UA_STATUSCODE_BADOUTOFMEMORY; /* not enough memory retry */ } #ifdef _WIN32 // windows requires int parameter for length int offset = (int)connection->incompleteChunk.length; int remaining = connection->config.recvBufferSize - offset; #else size_t offset = connection->incompleteChunk.length; size_t remaining = connection->config.recvBufferSize - offset; #endif /* Get the received packet(s) */ ssize_t ret = UA_recv(connection->sockfd, (char*)&response->data[offset], remaining, 0); /* The remote side closed the connection */ if(ret == 0) { UA_ByteString_deleteMembers(response); connection->close(connection); return UA_STATUSCODE_BADCONNECTIONCLOSED; } /* Error case */ if(ret < 0) { UA_ByteString_deleteMembers(response); if(UA_ERRNO == UA_INTERRUPTED || (timeout > 0) ? false : (UA_ERRNO == UA_EAGAIN || UA_ERRNO == UA_WOULDBLOCK)) return UA_STATUSCODE_GOOD; /* statuscode_good but no data -> retry */ connection->close(connection); return UA_STATUSCODE_BADCONNECTIONCLOSED; } /* Preprend the last incompleteChunk into the buffer */ if (connection->incompleteChunk.length > 0) { memcpy(response->data, connection->incompleteChunk.data, connection->incompleteChunk.length); UA_ByteString_deleteMembers(&connection->incompleteChunk); } /* Set the length of the received buffer */ response->length = offset + (size_t)ret; return UA_STATUSCODE_GOOD; } /***************************/ /* Server NetworkLayer TCP */ /***************************/ #define MAXBACKLOG 100 #define NOHELLOTIMEOUT 120000 /* timeout in ms before close the connection * if server does not receive Hello Message */ typedef struct ConnectionEntry { UA_Connection connection; LIST_ENTRY(ConnectionEntry) pointers; } ConnectionEntry; typedef struct { const UA_Logger *logger; UA_UInt16 port; UA_SOCKET serverSockets[FD_SETSIZE]; UA_UInt16 serverSocketsSize; LIST_HEAD(, ConnectionEntry) connections; } ServerNetworkLayerTCP; static void ServerNetworkLayerTCP_freeConnection(UA_Connection *connection) { UA_Connection_deleteMembers(connection); UA_free(connection); } /* This performs only 'shutdown'. 'close' is called when the shutdown * socket is returned from select. */ static void ServerNetworkLayerTCP_close(UA_Connection *connection) { if (connection->state == UA_CONNECTION_CLOSED) return; UA_shutdown((UA_SOCKET)connection->sockfd, 2); connection->state = UA_CONNECTION_CLOSED; } static UA_StatusCode ServerNetworkLayerTCP_add(UA_ServerNetworkLayer *nl, ServerNetworkLayerTCP *layer, UA_Int32 newsockfd, struct sockaddr_storage *remote) { /* Set nonblocking */ UA_socket_set_nonblocking(newsockfd);//TODO: check return value /* Do not merge packets on the socket (disable Nagle's algorithm) */ int dummy = 1; if(UA_setsockopt(newsockfd, IPPROTO_TCP, TCP_NODELAY, (const char *)&dummy, sizeof(dummy)) < 0) { UA_LOG_SOCKET_ERRNO_WRAP( UA_LOG_ERROR(layer->logger, UA_LOGCATEGORY_NETWORK, "Cannot set socket option TCP_NODELAY. Error: %s", errno_str)); return UA_STATUSCODE_BADUNEXPECTEDERROR; } #if defined(UA_getnameinfo) /* Get the peer name for logging */ char remote_name[100]; int res = UA_getnameinfo((struct sockaddr*)remote, sizeof(struct sockaddr_storage), remote_name, sizeof(remote_name), NULL, 0, NI_NUMERICHOST); if(res == 0) { UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK, "Connection %i | New connection over TCP from %s", (int)newsockfd, remote_name); } else { UA_LOG_SOCKET_ERRNO_WRAP(UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Connection %i | New connection over TCP, " "getnameinfo failed with error: %s", (int)newsockfd, errno_str)); } #else UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK, "Connection %i | New connection over TCP", (int)newsockfd); #endif /* Allocate and initialize the connection */ ConnectionEntry *e = (ConnectionEntry*)UA_malloc(sizeof(ConnectionEntry)); if(!e){ UA_close(newsockfd); return UA_STATUSCODE_BADOUTOFMEMORY; } UA_Connection *c = &e->connection; memset(c, 0, sizeof(UA_Connection)); c->sockfd = newsockfd; c->handle = layer; c->config = nl->localConnectionConfig; c->send = connection_write; c->close = ServerNetworkLayerTCP_close; c->free = ServerNetworkLayerTCP_freeConnection; c->getSendBuffer = connection_getsendbuffer; c->releaseSendBuffer = connection_releasesendbuffer; c->releaseRecvBuffer = connection_releaserecvbuffer; c->state = UA_CONNECTION_OPENING; c->openingDate = UA_DateTime_nowMonotonic(); /* Add to the linked list */ LIST_INSERT_HEAD(&layer->connections, e, pointers); return UA_STATUSCODE_GOOD; } static void addServerSocket(ServerNetworkLayerTCP *layer, struct addrinfo *ai) { /* Create the server socket */ UA_SOCKET newsock = UA_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if(newsock == UA_INVALID_SOCKET) { UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Error opening the server socket"); return; } /* Some Linux distributions have net.ipv6.bindv6only not activated. So * sockets can double-bind to IPv4 and IPv6. This leads to problems. Use * AF_INET6 sockets only for IPv6. */ int optval = 1; #if UA_IPV6 if(ai->ai_family == AF_INET6 && UA_setsockopt(newsock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&optval, sizeof(optval)) == -1) { UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Could not set an IPv6 socket to IPv6 only"); UA_close(newsock); return; } #endif if(UA_setsockopt(newsock, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof(optval)) == -1) { UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Could not make the socket reusable"); UA_close(newsock); return; } if(UA_socket_set_nonblocking(newsock) != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Could not set the server socket to nonblocking"); UA_close(newsock); return; } /* Bind socket to address */ if(UA_bind(newsock, ai->ai_addr, (socklen_t)ai->ai_addrlen) < 0) { UA_LOG_SOCKET_ERRNO_WRAP( UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Error binding a server socket: %s", errno_str)); UA_close(newsock); return; } /* Start listening */ if(UA_listen(newsock, MAXBACKLOG) < 0) { UA_LOG_SOCKET_ERRNO_WRAP( UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Error listening on server socket: %s", errno_str)); UA_close(newsock); return; } if (layer->port == 0) { /* Port was automatically chosen. Read it from the OS */ struct sockaddr_in returned_addr; memset(&returned_addr, 0, sizeof(returned_addr)); socklen_t len = sizeof(returned_addr); UA_getsockname(newsock, (struct sockaddr *)&returned_addr, &len); layer->port = ntohs(returned_addr.sin_port); } layer->serverSockets[layer->serverSocketsSize] = newsock; layer->serverSocketsSize++; } static UA_StatusCode ServerNetworkLayerTCP_start(UA_ServerNetworkLayer *nl, const UA_String *customHostname) { UA_initialize_architecture_network(); ServerNetworkLayerTCP *layer = (ServerNetworkLayerTCP *)nl->handle; /* Get addrinfo of the server and create server sockets */ char portno[6]; UA_snprintf(portno, 6, "%d", layer->port); struct addrinfo hints, *res; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; hints.ai_protocol = IPPROTO_TCP; if(UA_getaddrinfo(NULL, portno, &hints, &res) != 0) return UA_STATUSCODE_BADINTERNALERROR; /* There might be serveral addrinfos (for different network cards, * IPv4/IPv6). Add a server socket for all of them. */ struct addrinfo *ai = res; for(layer->serverSocketsSize = 0; layer->serverSocketsSize < FD_SETSIZE && ai != NULL; ai = ai->ai_next) addServerSocket(layer, ai); UA_freeaddrinfo(res); /* Get the discovery url from the hostname */ UA_String du = UA_STRING_NULL; char discoveryUrlBuffer[256]; if (customHostname->length) { du.length = (size_t)UA_snprintf(discoveryUrlBuffer, 255, "opc.tcp://%.*s:%d/", (int)customHostname->length, customHostname->data, layer->port); du.data = (UA_Byte*)discoveryUrlBuffer; }else{ char hostnameBuffer[256]; if(UA_gethostname(hostnameBuffer, 255) == 0) { du.length = (size_t)UA_snprintf(discoveryUrlBuffer, 255, "opc.tcp://%s:%d/", hostnameBuffer, layer->port); du.data = (UA_Byte*)discoveryUrlBuffer; } else { UA_LOG_ERROR(layer->logger, UA_LOGCATEGORY_NETWORK, "Could not get the hostname"); return UA_STATUSCODE_BADINTERNALERROR; } } UA_String_copy(&du, &nl->discoveryUrl); UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK, "TCP network layer listening on %.*s", (int)nl->discoveryUrl.length, nl->discoveryUrl.data); return UA_STATUSCODE_GOOD; } /* After every select, reset the sockets to listen on */ static UA_Int32 setFDSet(ServerNetworkLayerTCP *layer, fd_set *fdset) { FD_ZERO(fdset); UA_Int32 highestfd = 0; for(UA_UInt16 i = 0; i < layer->serverSocketsSize; i++) { UA_fd_set(layer->serverSockets[i], fdset); if((UA_Int32)layer->serverSockets[i] > highestfd) highestfd = (UA_Int32)layer->serverSockets[i]; } ConnectionEntry *e; LIST_FOREACH(e, &layer->connections, pointers) { UA_fd_set(e->connection.sockfd, fdset); if((UA_Int32)e->connection.sockfd > highestfd) highestfd = (UA_Int32)e->connection.sockfd; } return highestfd; } static UA_StatusCode ServerNetworkLayerTCP_listen(UA_ServerNetworkLayer *nl, UA_Server *server, UA_UInt16 timeout) { /* Every open socket can generate two jobs */ ServerNetworkLayerTCP *layer = (ServerNetworkLayerTCP *)nl->handle; if (layer->serverSocketsSize == 0) return UA_STATUSCODE_GOOD; /* Listen on open sockets (including the server) */ fd_set fdset, errset; UA_Int32 highestfd = setFDSet(layer, &fdset); setFDSet(layer, &errset); struct timeval tmptv = {0, timeout * 1000}; if (UA_select(highestfd+1, &fdset, NULL, &errset, &tmptv) < 0) { UA_LOG_SOCKET_ERRNO_WRAP( UA_LOG_DEBUG(layer->logger, UA_LOGCATEGORY_NETWORK, "Socket select failed with %s", errno_str)); // we will retry, so do not return bad return UA_STATUSCODE_GOOD; } /* Accept new connections via the server sockets */ for(UA_UInt16 i = 0; i < layer->serverSocketsSize; i++) { if(!UA_fd_isset(layer->serverSockets[i], &fdset)) continue; struct sockaddr_storage remote; socklen_t remote_size = sizeof(remote); UA_SOCKET newsockfd = UA_accept((UA_SOCKET)layer->serverSockets[i], (struct sockaddr*)&remote, &remote_size); if(newsockfd == UA_INVALID_SOCKET) continue; UA_LOG_TRACE(layer->logger, UA_LOGCATEGORY_NETWORK, "Connection %i | New TCP connection on server socket %i", (int)newsockfd, (int)(layer->serverSockets[i])); ServerNetworkLayerTCP_add(nl, layer, (UA_Int32)newsockfd, &remote); } /* Read from established sockets */ ConnectionEntry *e, *e_tmp; UA_DateTime now = UA_DateTime_nowMonotonic(); LIST_FOREACH_SAFE(e, &layer->connections, pointers, e_tmp) { if ((e->connection.state == UA_CONNECTION_OPENING) && (now > (e->connection.openingDate + (NOHELLOTIMEOUT * UA_DATETIME_MSEC)))){ UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK, "Connection %i | Closed by the server (no Hello Message)", (int)(e->connection.sockfd)); LIST_REMOVE(e, pointers); UA_close(e->connection.sockfd); UA_Server_removeConnection(server, &e->connection); continue; } if(!UA_fd_isset(e->connection.sockfd, &errset) && !UA_fd_isset(e->connection.sockfd, &fdset)) continue; UA_LOG_TRACE(layer->logger, UA_LOGCATEGORY_NETWORK, "Connection %i | Activity on the socket", (int)(e->connection.sockfd)); UA_ByteString buf = UA_BYTESTRING_NULL; UA_StatusCode retval = connection_recv(&e->connection, &buf, 0); if(retval == UA_STATUSCODE_GOOD) { /* Process packets */ UA_Server_processBinaryMessage(server, &e->connection, &buf); connection_releaserecvbuffer(&e->connection, &buf); } else if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED) { /* The socket is shutdown but not closed */ UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK, "Connection %i | Closed", (int)(e->connection.sockfd)); LIST_REMOVE(e, pointers); UA_close(e->connection.sockfd); UA_Server_removeConnection(server, &e->connection); } } return UA_STATUSCODE_GOOD; } static void ServerNetworkLayerTCP_stop(UA_ServerNetworkLayer *nl, UA_Server *server) { ServerNetworkLayerTCP *layer = (ServerNetworkLayerTCP *)nl->handle; UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK, "Shutting down the TCP network layer"); /* Close the server sockets */ for(UA_UInt16 i = 0; i < layer->serverSocketsSize; i++) { UA_shutdown(layer->serverSockets[i], 2); UA_close(layer->serverSockets[i]); } layer->serverSocketsSize = 0; /* Close open connections */ ConnectionEntry *e; LIST_FOREACH(e, &layer->connections, pointers) ServerNetworkLayerTCP_close(&e->connection); /* Run recv on client sockets. This picks up the closed sockets and frees * the connection. */ ServerNetworkLayerTCP_listen(nl, server, 0); UA_deinitialize_architecture_network(); } /* run only when the server is stopped */ static void ServerNetworkLayerTCP_deleteMembers(UA_ServerNetworkLayer *nl) { ServerNetworkLayerTCP *layer = (ServerNetworkLayerTCP *)nl->handle; UA_String_deleteMembers(&nl->discoveryUrl); /* Hard-close and remove remaining connections. The server is no longer * running. So this is safe. */ ConnectionEntry *e, *e_tmp; LIST_FOREACH_SAFE(e, &layer->connections, pointers, e_tmp) { LIST_REMOVE(e, pointers); UA_close(e->connection.sockfd); UA_free(e); } /* Free the layer */ UA_free(layer); } UA_ServerNetworkLayer UA_ServerNetworkLayerTCP(UA_ConnectionConfig config, UA_UInt16 port, UA_Logger *logger) { UA_ServerNetworkLayer nl; memset(&nl, 0, sizeof(UA_ServerNetworkLayer)); nl.deleteMembers = ServerNetworkLayerTCP_deleteMembers; nl.localConnectionConfig = config; nl.start = ServerNetworkLayerTCP_start; nl.listen = ServerNetworkLayerTCP_listen; nl.stop = ServerNetworkLayerTCP_stop; nl.handle = NULL; ServerNetworkLayerTCP *layer = (ServerNetworkLayerTCP*) UA_calloc(1,sizeof(ServerNetworkLayerTCP)); if(!layer) return nl; nl.handle = layer; layer->logger = logger; layer->port = port; return nl; } typedef struct TCPClientConnection { struct addrinfo hints, *server; UA_DateTime connStart; char* endpointURL; UA_UInt32 timeout; } TCPClientConnection; /***************************/ /* Client NetworkLayer TCP */ /***************************/ static void ClientNetworkLayerTCP_close(UA_Connection *connection) { if (connection->state == UA_CONNECTION_CLOSED) return; if(connection->sockfd != UA_INVALID_SOCKET) { UA_shutdown(connection->sockfd, 2); UA_close(connection->sockfd); } connection->state = UA_CONNECTION_CLOSED; } static void ClientNetworkLayerTCP_free(UA_Connection *connection) { if(connection->handle) { TCPClientConnection *tcpConnection = (TCPClientConnection *)connection->handle; if(tcpConnection->server) UA_freeaddrinfo(tcpConnection->server); UA_free(tcpConnection); connection->handle = NULL; } } UA_StatusCode UA_ClientConnectionTCP_poll(UA_Client *client, void *data) { UA_Connection *connection = (UA_Connection*) data; if (connection->state == UA_CONNECTION_CLOSED) return UA_STATUSCODE_BADDISCONNECT; TCPClientConnection *tcpConnection = (TCPClientConnection*) connection->handle; UA_DateTime connStart = UA_DateTime_nowMonotonic(); UA_SOCKET clientsockfd = connection->sockfd; UA_ClientConfig *config = UA_Client_getConfig(client); if (connection->state == UA_CONNECTION_ESTABLISHED) { UA_Client_removeRepeatedCallback(client, connection->connectCallbackID); connection->connectCallbackID = 0; return UA_STATUSCODE_GOOD; } if ((UA_Double) (UA_DateTime_nowMonotonic() - tcpConnection->connStart) > tcpConnection->timeout* UA_DATETIME_MSEC ) { // connection timeout ClientNetworkLayerTCP_close(connection); UA_LOG_WARNING(&config->logger, UA_LOGCATEGORY_NETWORK, "Timed out"); return UA_STATUSCODE_BADDISCONNECT; } /* On linux connect may immediately return with ECONNREFUSED but we still want to try to connect */ /* Thus use a loop and retry until timeout is reached */ /* Get a socket */ if(clientsockfd <= 0) { clientsockfd = UA_socket(tcpConnection->server->ai_family, tcpConnection->server->ai_socktype, tcpConnection->server->ai_protocol); connection->sockfd = (UA_Int32)clientsockfd; /* cast for win32 */ } if(clientsockfd == UA_INVALID_SOCKET) { UA_LOG_WARNING(&config->logger, UA_LOGCATEGORY_NETWORK, "Could not create client socket: %s", strerror(UA_ERRNO)); ClientNetworkLayerTCP_close(connection); return UA_STATUSCODE_BADDISCONNECT; } /* Non blocking connect to be able to timeout */ if(UA_socket_set_nonblocking(clientsockfd) != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&config->logger, UA_LOGCATEGORY_NETWORK, "Could not set the client socket to nonblocking"); ClientNetworkLayerTCP_close(connection); return UA_STATUSCODE_BADDISCONNECT; } /* Non blocking connect */ int error = UA_connect(clientsockfd, tcpConnection->server->ai_addr, tcpConnection->server->ai_addrlen); if ((error == -1) && (UA_ERRNO != UA_ERR_CONNECTION_PROGRESS)) { ClientNetworkLayerTCP_close(connection); UA_LOG_WARNING(&config->logger, UA_LOGCATEGORY_NETWORK, "Connection to failed with error: %s", strerror(UA_ERRNO)); return UA_STATUSCODE_BADDISCONNECT; } /* Use select to wait and check if connected */ if (error == -1 && (UA_ERRNO == UA_ERR_CONNECTION_PROGRESS)) { /* connection in progress. Wait until connected using select */ UA_UInt32 timeSinceStart = (UA_UInt32) ((UA_Double) (UA_DateTime_nowMonotonic() - connStart) / UA_DATETIME_MSEC); #ifdef _OS9000 /* OS-9 can't use select for checking write sockets. * Therefore, we need to use connect until success or failed */ UA_UInt32 timeout_usec = (tcpConnection->timeout - timeSinceStart) * 1000; int resultsize = 0; do { u_int32 time = 0x80000001; signal_code sig; timeout_usec -= 1000000/256; // Sleep 1/256 second if (timeout_usec < 0) break; _os_sleep(&time,&sig); error = connect(clientsockfd, tcpConnection->server->ai_addr, tcpConnection->server->ai_addrlen); if ((error == -1 && UA_ERRNO == EISCONN) || (error == 0)) resultsize = 1; if (error == -1 && UA_ERRNO != EALREADY && UA_ERRNO != EINPROGRESS) break; } while(resultsize == 0); #else fd_set fdset; FD_ZERO(&fdset); UA_fd_set(clientsockfd, &fdset); UA_UInt32 timeout_usec = (tcpConnection->timeout - timeSinceStart) * 1000; struct timeval tmptv = { (long int) (timeout_usec / 1000000), (int) (timeout_usec % 1000000) }; int resultsize = UA_select((UA_Int32) (clientsockfd + 1), NULL, &fdset, NULL, &tmptv); #endif if (resultsize == 1) { /* Windows does not have any getsockopt equivalent and it is not needed there */ #ifdef _WIN32 connection->sockfd = clientsockfd; connection->state = UA_CONNECTION_ESTABLISHED; return UA_STATUSCODE_GOOD; #else OPTVAL_TYPE so_error; socklen_t len = sizeof so_error; int ret = UA_getsockopt(clientsockfd, SOL_SOCKET, SO_ERROR, &so_error, &len); if (ret != 0 || so_error != 0) { /* on connection refused we should still try to connect */ /* connection refused happens on localhost or local ip without timeout */ if (so_error != ECONNREFUSED) { // general error ClientNetworkLayerTCP_close(connection); UA_LOG_WARNING(&config->logger, UA_LOGCATEGORY_NETWORK, "Connection to failed with error: %s", strerror(ret == 0 ? so_error : UA_ERRNO)); return UA_STATUSCODE_BADDISCONNECT; } /* wait until we try a again. Do not make this too small, otherwise the * timeout is somehow wrong */ } else { connection->state = UA_CONNECTION_ESTABLISHED; return UA_STATUSCODE_GOOD; } #endif } } else { connection->state = UA_CONNECTION_ESTABLISHED; return UA_STATUSCODE_GOOD; } #ifdef SO_NOSIGPIPE int val = 1; int sso_result = setsockopt(connection->sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&val, sizeof(val)); if(sso_result < 0) UA_LOG_WARNING(&config->logger, UA_LOGCATEGORY_NETWORK, "Couldn't set SO_NOSIGPIPE"); #endif return UA_STATUSCODE_GOOD; } UA_Connection UA_ClientConnectionTCP_init(UA_ConnectionConfig config, const UA_String endpointUrl, UA_UInt32 timeout, UA_Logger *logger) { UA_Connection connection; memset(&connection, 0, sizeof(UA_Connection)); connection.state = UA_CONNECTION_OPENING; connection.config = config; connection.send = connection_write; connection.recv = connection_recv; connection.close = ClientNetworkLayerTCP_close; connection.free = ClientNetworkLayerTCP_free; connection.getSendBuffer = connection_getsendbuffer; connection.releaseSendBuffer = connection_releasesendbuffer; connection.releaseRecvBuffer = connection_releaserecvbuffer; TCPClientConnection *tcpClientConnection = (TCPClientConnection*) UA_malloc( sizeof(TCPClientConnection)); connection.handle = (void*) tcpClientConnection; tcpClientConnection->timeout = timeout; UA_String hostnameString = UA_STRING_NULL; UA_String pathString = UA_STRING_NULL; UA_UInt16 port = 0; char hostname[512]; tcpClientConnection->connStart = UA_DateTime_nowMonotonic(); UA_StatusCode parse_retval = UA_parseEndpointUrl(&endpointUrl, &hostnameString, &port, &pathString); if (parse_retval != UA_STATUSCODE_GOOD || hostnameString.length > 511) { UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, "Server url is invalid: %.*s", (int)endpointUrl.length, endpointUrl.data); connection.state = UA_CONNECTION_CLOSED; return connection; } memcpy(hostname, hostnameString.data, hostnameString.length); hostname[hostnameString.length] = 0; if (port == 0) { port = 4840; UA_LOG_INFO(logger, UA_LOGCATEGORY_NETWORK, "No port defined, using default port %d", port); } memset(&tcpClientConnection->hints, 0, sizeof(tcpClientConnection->hints)); tcpClientConnection->hints.ai_family = AF_UNSPEC; tcpClientConnection->hints.ai_socktype = SOCK_STREAM; char portStr[6]; UA_snprintf(portStr, 6, "%d", port); int error = UA_getaddrinfo(hostname, portStr, &tcpClientConnection->hints, &tcpClientConnection->server); if (error != 0 || !tcpClientConnection->server) { UA_LOG_SOCKET_ERRNO_GAI_WRAP(UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, "DNS lookup of %s failed with error %s", hostname, errno_str)); connection.state = UA_CONNECTION_CLOSED; return connection; } return connection; } UA_Connection UA_ClientConnectionTCP(UA_ConnectionConfig config, const UA_String endpointUrl, UA_UInt32 timeout, UA_Logger *logger) { UA_initialize_architecture_network(); UA_Connection connection; memset(&connection, 0, sizeof(UA_Connection)); connection.state = UA_CONNECTION_CLOSED; connection.config = config; connection.send = connection_write; connection.recv = connection_recv; connection.close = ClientNetworkLayerTCP_close; connection.free = ClientNetworkLayerTCP_free; connection.getSendBuffer = connection_getsendbuffer; connection.releaseSendBuffer = connection_releasesendbuffer; connection.releaseRecvBuffer = connection_releaserecvbuffer; connection.handle = NULL; UA_String hostnameString = UA_STRING_NULL; UA_String pathString = UA_STRING_NULL; UA_UInt16 port = 0; char hostname[512]; UA_StatusCode parse_retval = UA_parseEndpointUrl(&endpointUrl, &hostnameString, &port, &pathString); if(parse_retval != UA_STATUSCODE_GOOD || hostnameString.length > 511) { UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, "Server url is invalid: %.*s", (int)endpointUrl.length, endpointUrl.data); return connection; } memcpy(hostname, hostnameString.data, hostnameString.length); hostname[hostnameString.length] = 0; if(port == 0) { port = 4840; UA_LOG_INFO(logger, UA_LOGCATEGORY_NETWORK, "No port defined, using default port %d", port); } struct addrinfo hints, *server; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; char portStr[6]; UA_snprintf(portStr, 6, "%d", port); int error = UA_getaddrinfo(hostname, portStr, &hints, &server); if(error != 0 || !server) { UA_LOG_SOCKET_ERRNO_GAI_WRAP(UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, "DNS lookup of %s failed with error %s", hostname, errno_str)); return connection; } UA_Boolean connected = false; UA_DateTime dtTimeout = timeout * UA_DATETIME_MSEC; UA_DateTime connStart = UA_DateTime_nowMonotonic(); UA_SOCKET clientsockfd; /* On linux connect may immediately return with ECONNREFUSED but we still * want to try to connect. So use a loop and retry until timeout is * reached. */ do { /* Get a socket */ clientsockfd = UA_socket(server->ai_family, server->ai_socktype, server->ai_protocol); if(clientsockfd == UA_INVALID_SOCKET) { UA_LOG_SOCKET_ERRNO_WRAP(UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, "Could not create client socket: %s", errno_str)); UA_freeaddrinfo(server); return connection; } connection.state = UA_CONNECTION_OPENING; /* Connect to the server */ connection.sockfd = clientsockfd; /* Non blocking connect to be able to timeout */ if (UA_socket_set_nonblocking(clientsockfd) != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, "Could not set the client socket to nonblocking"); ClientNetworkLayerTCP_close(&connection); UA_freeaddrinfo(server); return connection; } /* Non blocking connect */ error = UA_connect(clientsockfd, server->ai_addr, (socklen_t)server->ai_addrlen); if ((error == -1) && (UA_ERRNO != UA_ERR_CONNECTION_PROGRESS)) { ClientNetworkLayerTCP_close(&connection); UA_LOG_SOCKET_ERRNO_WRAP( UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, "Connection to %.*s failed with error: %s", (int)endpointUrl.length, endpointUrl.data, errno_str)); UA_freeaddrinfo(server); return connection; } /* Use select to wait and check if connected */ if (error == -1 && (UA_ERRNO == UA_ERR_CONNECTION_PROGRESS)) { /* connection in progress. Wait until connected using select */ UA_DateTime timeSinceStart = UA_DateTime_nowMonotonic() - connStart; if(timeSinceStart > dtTimeout) break; #ifdef _OS9000 /* OS-9 can't use select for checking write sockets. * Therefore, we need to use connect until success or failed */ UA_DateTime timeout_usec = (dtTimeout - timeSinceStart) / UA_DATETIME_USEC; int resultsize = 0; do { u_int32 time = 0x80000001; signal_code sig; timeout_usec -= 1000000/256; // Sleep 1/256 second if (timeout_usec < 0) break; _os_sleep(&time,&sig); error = connect(clientsockfd, server->ai_addr, server->ai_addrlen); if ((error == -1 && UA_ERRNO == EISCONN) || (error == 0)) resultsize = 1; if (error == -1 && UA_ERRNO != EALREADY && UA_ERRNO != EINPROGRESS) break; } while(resultsize == 0); #else fd_set fdset; FD_ZERO(&fdset); UA_fd_set(clientsockfd, &fdset); UA_DateTime timeout_usec = (dtTimeout - timeSinceStart) / UA_DATETIME_USEC; struct timeval tmptv = {(long int) (timeout_usec / 1000000), (int) (timeout_usec % 1000000)}; int resultsize = UA_select((UA_Int32)(clientsockfd + 1), NULL, &fdset, NULL, &tmptv); #endif if(resultsize == 1) { #ifdef _WIN32 /* Windows does not have any getsockopt equivalent and it is not * needed there */ connected = true; break; #else OPTVAL_TYPE so_error; socklen_t len = sizeof so_error; int ret = UA_getsockopt(clientsockfd, SOL_SOCKET, SO_ERROR, &so_error, &len); if (ret != 0 || so_error != 0) { /* on connection refused we should still try to connect */ /* connection refused happens on localhost or local ip without timeout */ if (so_error != ECONNREFUSED) { ClientNetworkLayerTCP_close(&connection); UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, "Connection to %.*s failed with error: %s", (int)endpointUrl.length, endpointUrl.data, strerror(ret == 0 ? so_error : UA_ERRNO)); UA_freeaddrinfo(server); return connection; } /* wait until we try a again. Do not make this too small, otherwise the * timeout is somehow wrong */ UA_sleep_ms(100); } else { connected = true; break; } #endif } } else { connected = true; break; } ClientNetworkLayerTCP_close(&connection); } while ((UA_DateTime_nowMonotonic() - connStart) < dtTimeout); UA_freeaddrinfo(server); if(!connected) { /* connection timeout */ if (connection.state != UA_CONNECTION_CLOSED) ClientNetworkLayerTCP_close(&connection); UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, "Trying to connect to %.*s timed out", (int)endpointUrl.length, endpointUrl.data); return connection; } /* We are connected. Reset socket to blocking */ if(UA_socket_set_blocking(clientsockfd) != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, "Could not set the client socket to blocking"); ClientNetworkLayerTCP_close(&connection); return connection; } #ifdef SO_NOSIGPIPE int val = 1; int sso_result = UA_setsockopt(connection.sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&val, sizeof(val)); if(sso_result < 0) UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, "Couldn't set SO_NOSIGPIPE"); #endif return connection; }