/* THIS IS A SINGLE-FILE DISTRIBUTION CONCATENATED FROM THE OPEN62541 SOURCES * visit http://open62541.org/ for information about this software * Git-Revision: v1.3.9 */ /* * Copyright (C) 2014-2021 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 */ #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) # define _CRT_SECURE_NO_WARNINGS #endif #include "open62541.h" /**** amalgamated original file "/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) do { \ SLIST_FIRST(head) = SLIST_END(head); \ } while(0) #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 "/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 "/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 "/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 "/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_encodeBinaryInternal(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_decodeBinaryInternal(const UA_ByteString *src, size_t *offset, void *dst, const UA_DataType *type, const UA_DataTypeArray *customTypes) UA_FUNC_ATTR_WARN_UNUSED_RESULT; const UA_DataType * UA_findDataTypeByBinary(const UA_NodeId *typeId); _UA_END_DECLS /**** amalgamated original file "/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 * Copyright 2021 (c) Fraunhofer IOSB (Author: Jan Hermes) */ #define UA_INTERNAL _UA_BEGIN_DECLS /* Macro-Expand for MSVC workarounds */ #define UA_MACRO_EXPAND(x) x /* Print a NodeId in logs */ #define UA_LOG_NODEID_INTERNAL(NODEID, LOG) \ do { \ UA_String nodeIdStr = UA_STRING_NULL; \ UA_NodeId_print(NODEID, &nodeIdStr); \ LOG; \ UA_String_clear(&nodeIdStr); \ } while(0) #if UA_LOGLEVEL <= 100 # define UA_LOG_NODEID_TRACE(NODEID, LOG) \ UA_LOG_NODEID_INTERNAL(NODEID, LOG) #else # define UA_LOG_NODEID_TRACE(NODEID, LOG) #endif #if UA_LOGLEVEL <= 200 # define UA_LOG_NODEID_DEBUG(NODEID, LOG) \ UA_LOG_NODEID_INTERNAL(NODEID, LOG) #else # define UA_LOG_NODEID_DEBUG(NODEID, LOG) #endif #if UA_LOGLEVEL <= 300 # define UA_LOG_NODEID_INFO(NODEID, LOG) \ UA_LOG_NODEID_INTERNAL(NODEID, LOG) #else # define UA_LOG_NODEID_INFO(NODEID, LOG) #endif #if UA_LOGLEVEL <= 400 # define UA_LOG_NODEID_WARNING(NODEID, LOG) \ UA_LOG_NODEID_INTERNAL(NODEID, LOG) #else # define UA_LOG_NODEID_WARNING(NODEID, LOG) #endif #if UA_LOGLEVEL <= 500 # define UA_LOG_NODEID_ERROR(NODEID, LOG) \ UA_LOG_NODEID_INTERNAL(NODEID, LOG) #else # define UA_LOG_NODEID_ERROR(NODEID, LOG) #endif #if UA_LOGLEVEL <= 600 # define UA_LOG_NODEID_FATAL(NODEID, LOG) \ UA_LOG_NODEID_INTERNAL(NODEID, LOG) #else # define UA_LOG_NODEID_FATAL(NODEID, LOG) #endif /* Short names for integer. 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; /** * Error checking macros */ static UA_INLINE UA_Boolean isGood(UA_StatusCode code) { return code == UA_STATUSCODE_GOOD; } static UA_INLINE UA_Boolean isNonNull(const void *ptr) { return ptr != NULL; } static UA_INLINE UA_Boolean isTrue(uint8_t expr) { return expr; } #define UA_CHECK(A, EVAL_ON_ERROR) \ do { \ if(UA_UNLIKELY(!isTrue(A))) { \ EVAL_ON_ERROR; \ } \ } while(0) #define UA_CHECK_STATUS(STATUSCODE, EVAL_ON_ERROR) \ UA_CHECK(isGood(STATUSCODE), EVAL_ON_ERROR) #define UA_CHECK_MEM(STATUSCODE, EVAL_ON_ERROR) \ UA_CHECK(isNonNull(STATUSCODE), EVAL_ON_ERROR) #ifdef UA_DEBUG_FILE_LINE_INFO #define UA_CHECK_LOG_INTERNAL(A, STATUSCODE, EVAL, LOG, LOGGER, CAT, MSG, ...) \ UA_MACRO_EXPAND( \ UA_CHECK(A, LOG(LOGGER, CAT, "" MSG "%s (%s:%d: statuscode: %s)", __VA_ARGS__, \ __FILE__, __LINE__, UA_StatusCode_name(STATUSCODE)); \ EVAL)) #else #define UA_CHECK_LOG_INTERNAL(A, STATUSCODE, EVAL, LOG, LOGGER, CAT, MSG, ...) \ UA_MACRO_EXPAND( \ UA_CHECK(A, LOG(LOGGER, CAT, "" MSG "%s (statuscode: %s)", __VA_ARGS__, \ UA_StatusCode_name(STATUSCODE)); \ EVAL)) #endif #define UA_CHECK_LOG(A, EVAL, LEVEL, LOGGER, CAT, ...) \ UA_MACRO_EXPAND(UA_CHECK_LOG_INTERNAL(A, UA_STATUSCODE_BAD, EVAL, UA_LOG_##LEVEL, \ LOGGER, CAT, __VA_ARGS__, "")) #define UA_CHECK_STATUS_LOG(STATUSCODE, EVAL, LEVEL, LOGGER, CAT, ...) \ UA_MACRO_EXPAND(UA_CHECK_LOG_INTERNAL(isGood(STATUSCODE), STATUSCODE, \ EVAL, UA_LOG_##LEVEL, LOGGER, CAT, \ __VA_ARGS__, "")) #define UA_CHECK_MEM_LOG(PTR, EVAL, LEVEL, LOGGER, CAT, ...) \ UA_MACRO_EXPAND(UA_CHECK_LOG_INTERNAL(isNonNull(PTR), UA_STATUSCODE_BADOUTOFMEMORY, \ EVAL, UA_LOG_##LEVEL, LOGGER, CAT, \ __VA_ARGS__, "")) /** * Check Macros * Usage examples: * * void *data = malloc(...); * UA_CHECK(data, return error); * * UA_StatusCode rv = some_func(...); * UA_CHECK_STATUS(rv, return rv); * * UA_Logger *logger = &server->config.logger; * rv = bar_func(...); * UA_CHECK_STATUS_WARN(rv, return rv, logger, UA_LOGCATEGORY_SERVER, "msg & args %s", "arg"); */ #define UA_CHECK_FATAL(A, EVAL, LOGGER, CAT, ...) \ UA_MACRO_EXPAND(UA_CHECK_LOG(A, EVAL, FATAL, LOGGER, CAT, __VA_ARGS__)) #define UA_CHECK_ERROR(A, EVAL, LOGGER, CAT, ...) \ UA_MACRO_EXPAND(UA_CHECK_LOG(A, EVAL, ERROR, LOGGER, CAT, __VA_ARGS__)) #define UA_CHECK_WARN(A, EVAL, LOGGER, CAT, ...) \ UA_MACRO_EXPAND(UA_CHECK_LOG(A, EVAL, WARNING, LOGGER, CAT, __VA_ARGS__)) #define UA_CHECK_INFO(A, EVAL, LOGGER, CAT, ...) \ UA_MACRO_EXPAND(UA_CHECK_LOG(A, EVAL, INFO, LOGGER, CAT, __VA_ARGS__)) #define UA_CHECK_STATUS_FATAL(STATUSCODE, EVAL, LOGGER, CAT, ...) \ UA_MACRO_EXPAND( \ UA_CHECK_STATUS_LOG(STATUSCODE, EVAL, FATAL, LOGGER, CAT, __VA_ARGS__)) #define UA_CHECK_STATUS_ERROR(STATUSCODE, EVAL, LOGGER, CAT, ...) \ UA_MACRO_EXPAND( \ UA_CHECK_STATUS_LOG(STATUSCODE, EVAL, ERROR, LOGGER, CAT, __VA_ARGS__)) #define UA_CHECK_STATUS_WARN(STATUSCODE, EVAL, LOGGER, CAT, ...) \ UA_MACRO_EXPAND( \ UA_CHECK_STATUS_LOG(STATUSCODE, EVAL, WARNING, LOGGER, CAT, __VA_ARGS__)) #define UA_CHECK_STATUS_INFO(STATUSCODE, EVAL, LOGGER, CAT, ...) \ UA_MACRO_EXPAND( \ UA_CHECK_STATUS_LOG(STATUSCODE, EVAL, INFO, LOGGER, CAT, __VA_ARGS__)) #define UA_CHECK_MEM_FATAL(PTR, EVAL, LOGGER, CAT, ...) \ UA_MACRO_EXPAND( \ UA_CHECK_MEM_LOG(PTR, EVAL, FATAL, LOGGER, CAT, __VA_ARGS__)) #define UA_CHECK_MEM_ERROR(PTR, EVAL, LOGGER, CAT, ...) \ UA_MACRO_EXPAND( \ UA_CHECK_MEM_LOG(PTR, EVAL, ERROR, LOGGER, CAT, __VA_ARGS__)) #define UA_CHECK_MEM_WARN(PTR, EVAL, LOGGER, CAT, ...) \ UA_MACRO_EXPAND( \ UA_CHECK_MEM_LOG(PTR, EVAL, WARNING, LOGGER, CAT, __VA_ARGS__)) #define UA_CHECK_MEM_INFO(PTR, EVAL, LOGGER, CAT, ...) \ UA_MACRO_EXPAND( \ UA_CHECK_MEM_LOG(PTR, EVAL, INFO, LOGGER, CAT, __VA_ARGS__)) /** * Utility Functions * ----------------- */ const UA_DataType * UA_findDataTypeWithCustom(const UA_NodeId *typeId, const UA_DataTypeArray *customTypes); /* Get the number of optional fields contained in an structure type */ size_t UA_EXPORT getCountOfOptionalFields(const UA_DataType *type); /* Dump packet for debugging / fuzzing */ #ifdef UA_DEBUG_DUMP_PKGS void UA_EXPORT UA_dump_hex_pkg(UA_Byte* buffer, size_t bufferLen); #endif /* Unions that represent any of the supported request or response message */ typedef union { UA_RequestHeader requestHeader; UA_FindServersRequest findServersRequest; UA_GetEndpointsRequest getEndpointsRequest; #ifdef UA_ENABLE_DISCOVERY # ifdef UA_ENABLE_DISCOVERY_MULTICAST UA_FindServersOnNetworkRequest findServersOnNetworkRequest; # endif UA_RegisterServerRequest registerServerRequest; UA_RegisterServer2Request registerServer2Request; #endif UA_OpenSecureChannelRequest openSecureChannelRequest; UA_CreateSessionRequest createSessionRequest; UA_ActivateSessionRequest activateSessionRequest; UA_CloseSessionRequest closeSessionRequest; UA_AddNodesRequest addNodesRequest; UA_AddReferencesRequest addReferencesRequest; UA_DeleteNodesRequest deleteNodesRequest; UA_DeleteReferencesRequest deleteReferencesRequest; UA_BrowseRequest browseRequest; UA_BrowseNextRequest browseNextRequest; UA_TranslateBrowsePathsToNodeIdsRequest translateBrowsePathsToNodeIdsRequest; UA_RegisterNodesRequest registerNodesRequest; UA_UnregisterNodesRequest unregisterNodesRequest; UA_ReadRequest readRequest; UA_WriteRequest writeRequest; #ifdef UA_ENABLE_HISTORIZING UA_HistoryReadRequest historyReadRequest; UA_HistoryUpdateRequest historyUpdateRequest; #endif #ifdef UA_ENABLE_METHODCALLS UA_CallRequest callRequest; #endif #ifdef UA_ENABLE_SUBSCRIPTIONS UA_CreateMonitoredItemsRequest createMonitoredItemsRequest; UA_DeleteMonitoredItemsRequest deleteMonitoredItemsRequest; UA_ModifyMonitoredItemsRequest modifyMonitoredItemsRequest; UA_SetMonitoringModeRequest setMonitoringModeRequest; UA_CreateSubscriptionRequest createSubscriptionRequest; UA_ModifySubscriptionRequest modifySubscriptionRequest; UA_SetPublishingModeRequest setPublishingModeRequest; UA_PublishRequest publishRequest; UA_RepublishRequest republishRequest; UA_DeleteSubscriptionsRequest deleteSubscriptionsRequest; #endif } UA_Request; typedef union { UA_ResponseHeader responseHeader; UA_FindServersResponse findServersResponse; UA_GetEndpointsResponse getEndpointsResponse; #ifdef UA_ENABLE_DISCOVERY # ifdef UA_ENABLE_DISCOVERY_MULTICAST UA_FindServersOnNetworkResponse findServersOnNetworkResponse; # endif UA_RegisterServerResponse registerServerResponse; UA_RegisterServer2Response registerServer2Response; #endif UA_OpenSecureChannelResponse openSecureChannelResponse; UA_CreateSessionResponse createSessionResponse; UA_ActivateSessionResponse activateSessionResponse; UA_CloseSessionResponse closeSessionResponse; UA_AddNodesResponse addNodesResponse; UA_AddReferencesResponse addReferencesResponse; UA_DeleteNodesResponse deleteNodesResponse; UA_DeleteReferencesResponse deleteReferencesResponse; UA_BrowseResponse browseResponse; UA_BrowseNextResponse browseNextResponse; UA_TranslateBrowsePathsToNodeIdsResponse translateBrowsePathsToNodeIdsResponse; UA_RegisterNodesResponse registerNodesResponse; UA_UnregisterNodesResponse unregisterNodesResponse; UA_ReadResponse readResponse; UA_WriteResponse writeResponse; #ifdef UA_ENABLE_HISTORIZING UA_HistoryReadResponse historyReadResponse; UA_HistoryUpdateResponse historyUpdateResponse; #endif #ifdef UA_ENABLE_METHODCALLS UA_CallResponse callResponse; #endif #ifdef UA_ENABLE_SUBSCRIPTIONS UA_CreateMonitoredItemsResponse createMonitoredItemsResponse; UA_DeleteMonitoredItemsResponse deleteMonitoredItemsResponse; UA_ModifyMonitoredItemsResponse modifyMonitoredItemsResponse; UA_SetMonitoringModeResponse setMonitoringModeResponse; UA_CreateSubscriptionResponse createSubscriptionResponse; UA_ModifySubscriptionResponse modifySubscriptionResponse; UA_SetPublishingModeResponse setPublishingModeResponse; UA_PublishResponse publishResponse; UA_RepublishResponse republishResponse; UA_DeleteSubscriptionsResponse deleteSubscriptionsResponse; #endif } UA_Response; /* Do not expose UA_String_equal_ignorecase to public API as it currently only handles * ASCII strings, and not UTF8! */ UA_Boolean UA_EXPORT UA_String_equal_ignorecase(const UA_String *s1, const UA_String *s2); /********************/ /* Encoding Helpers */ /********************/ #define UA_ENCODING_HELPERS(TYPE, UPCASE_TYPE) \ static UA_INLINE size_t \ UA_##TYPE##_calcSizeBinary(const UA_##TYPE *src) { \ return UA_calcSizeBinary(src, &UA_TYPES[UA_TYPES_##UPCASE_TYPE]); \ } \ static UA_INLINE UA_StatusCode \ UA_##TYPE##_encodeBinary(const UA_##TYPE *src, UA_Byte **bufPos, const UA_Byte *bufEnd) { \ return UA_encodeBinaryInternal(src, &UA_TYPES[UA_TYPES_##UPCASE_TYPE], \ bufPos, &bufEnd, NULL, NULL); \ } \ static UA_INLINE UA_StatusCode \ UA_##TYPE##_decodeBinary(const UA_ByteString *src, size_t *offset, UA_##TYPE *dst) { \ return UA_decodeBinaryInternal(src, offset, dst, \ &UA_TYPES[UA_TYPES_##UPCASE_TYPE], NULL); \ } UA_ENCODING_HELPERS(Boolean, BOOLEAN) UA_ENCODING_HELPERS(SByte, SBYTE) UA_ENCODING_HELPERS(Byte, BYTE) UA_ENCODING_HELPERS(Int16, INT16) UA_ENCODING_HELPERS(UInt16, UINT16) UA_ENCODING_HELPERS(Int32, INT32) UA_ENCODING_HELPERS(UInt32, UINT32) UA_ENCODING_HELPERS(Int64, INT64) UA_ENCODING_HELPERS(UInt64, UINT64) UA_ENCODING_HELPERS(Float, FLOAT) UA_ENCODING_HELPERS(Double, DOUBLE) UA_ENCODING_HELPERS(String, STRING) UA_ENCODING_HELPERS(DateTime, DATETIME) UA_ENCODING_HELPERS(Guid, GUID) UA_ENCODING_HELPERS(ByteString, BYTESTRING) UA_ENCODING_HELPERS(XmlElement, XMLELEMENT) UA_ENCODING_HELPERS(NodeId, NODEID) UA_ENCODING_HELPERS(ExpandedNodeId, EXPANDEDNODEID) UA_ENCODING_HELPERS(StatusCode, STATUSCODE) UA_ENCODING_HELPERS(QualifiedName, QUALIFIEDNAME) UA_ENCODING_HELPERS(LocalizedText, LOCALIZEDTEXT) UA_ENCODING_HELPERS(ExtensionObject, EXTENSIONOBJECT) UA_ENCODING_HELPERS(DataValue, DATAVALUE) UA_ENCODING_HELPERS(Variant, VARIANT) UA_ENCODING_HELPERS(DiagnosticInfo, DIAGNOSTICINFO) _UA_END_DECLS /**** amalgamated original file "/build/src_generated/open62541/transport_generated.h" ****/ /********************************** * Autogenerated -- do not modify * **********************************/ #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 8 extern UA_EXPORT const UA_DataType UA_TRANSPORT[UA_TRANSPORT_COUNT]; /** * 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_INVALID = 0x0, __UA_MESSAGETYPE_FORCE32BIT = 0x7fffffff } UA_MessageType; UA_STATIC_ASSERT(sizeof(UA_MessageType) == sizeof(UA_Int32), enum_must_be_32bit); #define UA_TRANSPORT_MESSAGETYPE 0 /** * 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 1 /** * TcpMessageHeader * ^^^^^^^^^^^^^^^^ * TCP Header */ typedef struct { UA_UInt32 messageTypeAndChunkType; UA_UInt32 messageSize; } UA_TcpMessageHeader; #define UA_TRANSPORT_TCPMESSAGEHEADER 2 /** * 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 3 /** * 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 4 /** * TcpErrorMessage * ^^^^^^^^^^^^^^^ * Error Message */ typedef struct { UA_UInt32 error; UA_String reason; } UA_TcpErrorMessage; #define UA_TRANSPORT_TCPERRORMESSAGE 5 /** * AsymmetricAlgorithmSecurityHeader * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Asymmetric Security Header */ typedef struct { UA_ByteString securityPolicyUri; UA_ByteString senderCertificate; UA_ByteString receiverCertificateThumbprint; } UA_AsymmetricAlgorithmSecurityHeader; #define UA_TRANSPORT_ASYMMETRICALGORITHMSECURITYHEADER 6 /** * SequenceHeader * ^^^^^^^^^^^^^^ * Secure Layer Sequence Header */ typedef struct { UA_UInt32 sequenceNumber; UA_UInt32 requestId; } UA_SequenceHeader; #define UA_TRANSPORT_SEQUENCEHEADER 7 _UA_END_DECLS /**** amalgamated original file "/build/src_generated/open62541/transport_generated_handling.h" ****/ /********************************** * Autogenerated -- do not modify * **********************************/ _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 /* 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) { return UA_copy(src, dst, &UA_TRANSPORT[UA_TRANSPORT_MESSAGETYPE]); } UA_DEPRECATED static UA_INLINE void UA_MessageType_deleteMembers(UA_MessageType *p) { UA_clear(p, &UA_TRANSPORT[UA_TRANSPORT_MESSAGETYPE]); } static UA_INLINE void UA_MessageType_clear(UA_MessageType *p) { UA_clear(p, &UA_TRANSPORT[UA_TRANSPORT_MESSAGETYPE]); } static UA_INLINE void UA_MessageType_delete(UA_MessageType *p) { UA_delete(p, &UA_TRANSPORT[UA_TRANSPORT_MESSAGETYPE]); } /* 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) { return UA_copy(src, dst, &UA_TRANSPORT[UA_TRANSPORT_CHUNKTYPE]); } UA_DEPRECATED static UA_INLINE void UA_ChunkType_deleteMembers(UA_ChunkType *p) { UA_clear(p, &UA_TRANSPORT[UA_TRANSPORT_CHUNKTYPE]); } static UA_INLINE void UA_ChunkType_clear(UA_ChunkType *p) { UA_clear(p, &UA_TRANSPORT[UA_TRANSPORT_CHUNKTYPE]); } static UA_INLINE void UA_ChunkType_delete(UA_ChunkType *p) { UA_delete(p, &UA_TRANSPORT[UA_TRANSPORT_CHUNKTYPE]); } /* 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) { return UA_copy(src, dst, &UA_TRANSPORT[UA_TRANSPORT_TCPMESSAGEHEADER]); } UA_DEPRECATED static UA_INLINE void UA_TcpMessageHeader_deleteMembers(UA_TcpMessageHeader *p) { UA_clear(p, &UA_TRANSPORT[UA_TRANSPORT_TCPMESSAGEHEADER]); } static UA_INLINE void UA_TcpMessageHeader_clear(UA_TcpMessageHeader *p) { UA_clear(p, &UA_TRANSPORT[UA_TRANSPORT_TCPMESSAGEHEADER]); } static UA_INLINE void UA_TcpMessageHeader_delete(UA_TcpMessageHeader *p) { UA_delete(p, &UA_TRANSPORT[UA_TRANSPORT_TCPMESSAGEHEADER]); } /* 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]); } UA_DEPRECATED 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]); } /* 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) { return UA_copy(src, dst, &UA_TRANSPORT[UA_TRANSPORT_TCPACKNOWLEDGEMESSAGE]); } UA_DEPRECATED static UA_INLINE void UA_TcpAcknowledgeMessage_deleteMembers(UA_TcpAcknowledgeMessage *p) { UA_clear(p, &UA_TRANSPORT[UA_TRANSPORT_TCPACKNOWLEDGEMESSAGE]); } static UA_INLINE void UA_TcpAcknowledgeMessage_clear(UA_TcpAcknowledgeMessage *p) { UA_clear(p, &UA_TRANSPORT[UA_TRANSPORT_TCPACKNOWLEDGEMESSAGE]); } static UA_INLINE void UA_TcpAcknowledgeMessage_delete(UA_TcpAcknowledgeMessage *p) { UA_delete(p, &UA_TRANSPORT[UA_TRANSPORT_TCPACKNOWLEDGEMESSAGE]); } /* 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]); } UA_DEPRECATED 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]); } /* 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]); } UA_DEPRECATED 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]); } /* 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) { return UA_copy(src, dst, &UA_TRANSPORT[UA_TRANSPORT_SEQUENCEHEADER]); } UA_DEPRECATED static UA_INLINE void UA_SequenceHeader_deleteMembers(UA_SequenceHeader *p) { UA_clear(p, &UA_TRANSPORT[UA_TRANSPORT_SEQUENCEHEADER]); } static UA_INLINE void UA_SequenceHeader_clear(UA_SequenceHeader *p) { UA_clear(p, &UA_TRANSPORT[UA_TRANSPORT_SEQUENCEHEADER]); } static UA_INLINE void UA_SequenceHeader_delete(UA_SequenceHeader *p) { UA_delete(p, &UA_TRANSPORT[UA_TRANSPORT_SEQUENCEHEADER]); } #if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6 # pragma GCC diagnostic pop #endif _UA_END_DECLS /**** amalgamated original file "/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 /* 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 "/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-2020 (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 /* The message header of the OPC UA binary protocol is structured as follows: * * - MessageType (3 Byte) * - IsFinal (1 Byte) * - MessageSize (4 Byte) * *** UA_SECURECHANNEL_MESSAGEHEADER_LENGTH *** * - SecureChannelId (4 Byte) * *** UA_SECURECHANNEL_CHANNELHEADER_LENGTH *** * - SecurityHeader (4 Byte TokenId for symmetric, otherwise dynamic length) * - SequenceHeader (8 Byte) * - SequenceNumber * - RequestId */ #define UA_SECURECHANNEL_MESSAGEHEADER_LENGTH 8 #define UA_SECURECHANNEL_CHANNELHEADER_LENGTH 12 #define UA_SECURECHANNEL_SYMMETRIC_SECURITYHEADER_LENGTH 4 #define UA_SECURECHANNEL_SEQUENCEHEADER_LENGTH 8 #define UA_SECURECHANNEL_SYMMETRIC_HEADER_UNENCRYPTEDLENGTH \ (UA_SECURECHANNEL_CHANNELHEADER_LENGTH + \ UA_SECURECHANNEL_SYMMETRIC_SECURITYHEADER_LENGTH) #define UA_SECURECHANNEL_SYMMETRIC_HEADER_TOTALLENGTH \ (UA_SECURECHANNEL_CHANNELHEADER_LENGTH + \ UA_SECURECHANNEL_SYMMETRIC_SECURITYHEADER_LENGTH + \ UA_SECURECHANNEL_SEQUENCEHEADER_LENGTH) /* Minimum length of a valid message (ERR message with an empty reason) */ #define UA_SECURECHANNEL_MESSAGE_MIN_LENGTH 16 /* 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 { SLIST_ENTRY(UA_SessionHeader) next; UA_NodeId authenticationToken; UA_Boolean serverSession; /* Disambiguate client and server session */ UA_SecureChannel *channel; /* The pointer back to the SecureChannel in the session. */ } UA_SessionHeader; /* For chunked requests */ typedef struct UA_Chunk { SIMPLEQ_ENTRY(UA_Chunk) pointers; UA_ByteString bytes; UA_MessageType messageType; UA_ChunkType chunkType; UA_UInt32 requestId; UA_Boolean copied; /* Do the bytes point to a buffer from the network or was * memory allocated for the chunk separately */ } UA_Chunk; typedef SIMPLEQ_HEAD(UA_ChunkQueue, UA_Chunk) UA_ChunkQueue; typedef enum { UA_SECURECHANNELRENEWSTATE_NORMAL, /* Client has sent an OPN, but not received a response so far. */ UA_SECURECHANNELRENEWSTATE_SENT, /* The server waits for the first request with the new token for the rollover. * The new token is stored in the altSecurityToken. The configured local and * remote symmetric encryption keys are the old ones. */ UA_SECURECHANNELRENEWSTATE_NEWTOKEN_SERVER, /* The client already uses the new token. But he waits for the server to respond * with the new token to complete the rollover. The old token is stored in * altSecurityToken. The local symmetric encryption key is new. The remote * encryption key is the old one. */ UA_SECURECHANNELRENEWSTATE_NEWTOKEN_CLIENT } UA_SecureChannelRenewState; struct UA_SecureChannel { UA_SecureChannelState state; UA_SecureChannelRenewState renewState; UA_MessageSecurityMode securityMode; UA_ConnectionConfig config; /* Rules for revolving the token with a renew OPN request: The client is * allowed to accept messages with the old token until the OPN response has * arrived. The server accepts the old token until one message secured with * the new token has arrived. * * We recognize whether nextSecurityToken contains a valid next token if the * ChannelId is not 0. */ UA_ChannelSecurityToken securityToken; /* Also contains the channelId */ UA_ChannelSecurityToken altSecurityToken; /* Alternative token for the rollover. * See the renewState. */ /* 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 nonces. These are used to generate the key material * and must not be reused once the keys are in place. * * Nonces are also used during the CreateSession / ActivateSession * handshake. These are not handled here, as the Session handling can * overlap with a RenewSecureChannel. */ UA_ByteString remoteNonce; UA_ByteString localNonce; UA_UInt32 receiveSequenceNumber; UA_UInt32 sendSequenceNumber; /* Sessions that are bound to the SecureChannel */ SLIST_HEAD(, UA_SessionHeader) sessions; /* If a buffer is received, first all chunks are put into the completeChunks * queue. Then they are processed in order. This ensures that processing * buffers is reentrant with the correct processing order. (This has lead to * problems in the client in the past.) */ UA_ChunkQueue completeChunks; /* Received full chunks that have not been * decrypted so far */ UA_ChunkQueue decryptedChunks; /* Received chunks that were decrypted but * not processed */ size_t decryptedChunksCount; size_t decryptedChunksLength; UA_ByteString incompleteChunk; /* A half-received chunk (TCP is a * streaming protocol) is stored here */ UA_CertificateVerification *certificateVerification; UA_StatusCode (*processOPNHeader)(void *application, UA_SecureChannel *channel, const UA_AsymmetricAlgorithmSecurityHeader *asymHeader); }; void UA_SecureChannel_init(UA_SecureChannel *channel, const UA_ConnectionConfig *config); void UA_SecureChannel_close(UA_SecureChannel *channel); /* Process the remote configuration in the HEL/ACK handshake. The connection * config is initialized with the local settings. */ UA_StatusCode UA_SecureChannel_processHELACK(UA_SecureChannel *channel, const UA_TcpAcknowledgeMessage *remoteConfig); UA_StatusCode UA_SecureChannel_setSecurityPolicy(UA_SecureChannel *channel, const UA_SecurityPolicy *securityPolicy, const UA_ByteString *remoteCertificate); /* Remove (partially) received unprocessed chunks */ void UA_SecureChannel_deleteBuffered(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_StatusCode UA_SecureChannel_generateLocalKeys(const UA_SecureChannel *channel); UA_StatusCode generateRemoteKeys(const 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 * --------------- */ typedef UA_StatusCode (UA_ProcessMessageCallback)(void *application, UA_SecureChannel *channel, UA_MessageType messageType, UA_UInt32 requestId, UA_ByteString *message); /* Process a received buffer. The callback function is called with the message * body if the message is complete. The message is removed afterwards. Returns * if an irrecoverable error occured. * * Note that only MSG and CLO messages are decrypted. HEL/ACK/OPN/... are * forwarded verbatim to the application. */ UA_StatusCode UA_SecureChannel_processBuffer(UA_SecureChannel *channel, void *application, UA_ProcessMessageCallback callback, const UA_ByteString *buffer); /* Try to receive at least one complete chunk on the connection. This blocks the * current thread up to the given timeout. It will return once the first buffer * has been received (and possibly processed when the message is complete). * * @param channel The SecureChannel * @param application The client or server application * @param callback The function pointer for processing complete messages * @param timeout The timeout (in milliseconds) the method will block at most. * @return Returns UA_STATUSCODE_GOOD or an error code. A timeout does not * create an error. */ UA_StatusCode UA_SecureChannel_receive(UA_SecureChannel *channel, void *application, UA_ProcessMessageCallback callback, UA_UInt32 timeout); /* Internal methods in ua_securechannel_crypto.h */ void hideBytesAsym(const UA_SecureChannel *channel, UA_Byte **buf_start, const UA_Byte **buf_end); /* Decrypt and verify via the signature. The chunk buffer is reused to hold the * decrypted data after the MessageHeader and SecurityHeader. The chunk length * is reduced by the signature, padding and encryption overhead. * * The offset argument points to the start of the encrypted content (beginning * with the SequenceHeader).*/ UA_StatusCode decryptAndVerifyChunk(const UA_SecureChannel *channel, const UA_SecurityPolicyCryptoModule *cryptoModule, UA_MessageType messageType, UA_ByteString *chunk, size_t offset); size_t calculateAsymAlgSecurityHeaderLength(const UA_SecureChannel *channel); 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); void setBufPos(UA_MessageContext *mc); UA_StatusCode checkSymHeader(UA_SecureChannel *channel, const UA_UInt32 tokenId); UA_StatusCode checkAsymHeader(UA_SecureChannel *channel, const UA_AsymmetricAlgorithmSecurityHeader *asymHeader); void padChunk(UA_SecureChannel *channel, const UA_SecurityPolicyCryptoModule *cm, const UA_Byte *start, UA_Byte **pos); UA_StatusCode signAndEncryptAsym(UA_SecureChannel *channel, size_t preSignLength, UA_ByteString *buf, size_t securityHeaderLength, size_t totalLength); UA_StatusCode signAndEncryptSym(UA_MessageContext *messageContext, size_t preSigLength, size_t totalLength); /** * 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 %" PRIu32 " | " 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 %" PRIu32 " | " 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 %" PRIu32 " | " 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 %" PRIu32 " | " 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 %" PRIu32 " | " 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 %" PRIu32 " | " 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 "/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, 2021 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2017 (c) Stefan Profanter, fortiss GmbH */ _UA_BEGIN_DECLS /* The timer is protected by its own mutex. The mutex is released before calling * into the callbacks. So the timer can be modified from the callbacks it is * executing. Also, the timer mutex can never lead to locking. Because the timer * mutex will be left without acquiring another mutex. * * Obviously, the timer must not be deleted from within one of its * callbacks. */ /* Callback where the application is either a client or a server */ typedef void (*UA_ApplicationCallback)(void *application, void *data); typedef struct UA_TimerEntry { struct aa_entry treeEntry; UA_TimerPolicy timerPolicy; /* Timer policy to handle cycle misses */ UA_DateTime nextTime; /* The next time when the callback * is to be executed */ UA_UInt64 interval; /* Interval in 100ns resolution. If the interval is zero, the callback is not repeated and removed after execution. */ UA_ApplicationCallback callback; void *application; void *data; struct aa_entry idTreeEntry; UA_UInt64 id; /* Id of the entry */ } UA_TimerEntry; typedef struct { struct aa_head root; /* The root of the time-sorted tree */ struct aa_head idRoot; /* The root of the id-sorted tree */ UA_UInt64 idCounter; /* Generate unique identifiers. Identifiers are * always above zero. */ #if UA_MULTITHREADING >= 100 UA_Lock timerMutex; #endif } 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); /* Add a pre-allocated and pre-filled UA_TimerEntry. This cannot fail. It is * used, for example, for delayed memory reclamation where the data structure * begins with a UA_TimerEntry. */ void UA_Timer_addTimerEntry(UA_Timer *t, UA_TimerEntry *te, UA_UInt64 *callbackId); UA_StatusCode UA_Timer_addRepeatedCallback(UA_Timer *t, UA_ApplicationCallback callback, void *application, void *data, UA_Double interval_ms, UA_DateTime *baseTime, UA_TimerPolicy timerPolicy, UA_UInt64 *callbackId); UA_StatusCode UA_Timer_changeRepeatedCallback(UA_Timer *t, UA_UInt64 callbackId, UA_Double interval_ms, UA_DateTime *baseTime, UA_TimerPolicy timerPolicy); 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_clear(UA_Timer *t); _UA_END_DECLS /**** amalgamated original file "/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) * Copyright 2019 (c) HMS Industrial Networks AB (Author: Jonas Green) */ _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; /* in ms */ UA_DateTime validTill; UA_ByteString serverNonce; UA_UInt16 availableContinuationPoints; ContinuationPoint *continuationPoints; size_t paramsSize; UA_KeyValuePair *params; /* Localization information */ size_t localeIdsSize; UA_String *localeIds; #ifdef UA_ENABLE_SUBSCRIPTIONS /* The queue is ordered according to the priority byte (higher bytes come * first). When a late subscription finally publishes, then it is pushed to * the back within the sub-set of subscriptions that has the same priority * (round-robin scheduling). */ size_t subscriptionsSize; TAILQ_HEAD(, UA_Subscription) subscriptions; size_t responseQueueSize; SIMPLEQ_HEAD(, UA_PublishResponseEntry) responseQueue; size_t totalRetransmissionQueueSize; /* Retransmissions of all subscriptions */ #endif #ifdef UA_ENABLE_DIAGNOSTICS UA_SessionSecurityDiagnosticsDataType securityDiagnostics; UA_SessionDiagnosticsDataType diagnostics; #endif } UA_Session; /** * Session Lifecycle * ----------------- */ void UA_Session_init(UA_Session *session); void UA_Session_clear(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_attachSubscription(UA_Session *session, UA_Subscription *sub); /* If releasePublishResponses is true and the last subscription is removed, all * outstanding PublishResponse are sent with a StatusCode. But we don't do that * if a Subscription is only detached for modification. */ void UA_Session_detachSubscription(UA_Server *server, UA_Session *session, UA_Subscription *sub, UA_Boolean releasePublishResponses); UA_Subscription * UA_Session_getSubscriptionById(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 { \ int nameLen = (SESSION) ? (int)(SESSION)->sessionName.length : 0; \ const char *nameStr = (SESSION) ? \ (const char*)(SESSION)->sessionName.data : NULL; \ UA_UInt32 chanId = ((SESSION) && (SESSION)->header.channel) ? \ (SESSION)->header.channel->securityToken.channelId : 0; \ UA_LOG_##LEVEL(LOGGER, UA_LOGCATEGORY_SESSION, \ "SecureChannel %" PRIu32 " | Session \"%.*s\" | " MSG "%.0s", \ chanId, nameLen, nameStr, __VA_ARGS__); \ } 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, ...) #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, ...) #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, ...) #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, ...) #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, ...) #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, ...) #endif _UA_END_DECLS /**** amalgamated original file "/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, 2021-2022 (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 * Copyright 2019 (c) HMS Industrial Networks AB (Author: Jonas Green) * Copyright 2020 (c) Christian von Arnim, ISW University of Stuttgart (for VDW and umati) * Copyright 2021 (c) Fraunhofer IOSB (Author: Andreas Ebner) */ _UA_BEGIN_DECLS #ifdef UA_ENABLE_SUBSCRIPTIONS /* 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. */ /*****************/ /* Notifications */ /*****************/ /* 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) typedef struct UA_Notification { TAILQ_ENTRY(UA_Notification) localEntry; /* Notification list for the MonitoredItem */ TAILQ_ENTRY(UA_Notification) globalEntry; /* Notification list for the Subscription */ UA_MonitoredItem *mon; /* Always set */ /* The event field is used if mon->attributeId is the EventNotifier */ union { UA_MonitoredItemNotification dataChange; #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS UA_EventFieldList event; #endif } data; #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS UA_Boolean isOverflowEvent; /* Counted manually */ UA_EventFilterResult result; #endif } UA_Notification; /* Initializes and sets the sentinel pointers */ UA_Notification * UA_Notification_new(void); /* Notifications are always added to the queue of the MonitoredItem. That queue * can overflow. If Notifications are reported, they are also added to the * global queue of the Subscription. There they are picked up by the publishing * callback. * * There are two ways Notifications can be put into the global queue of the * Subscription: They are added because the MonitoringMode of the MonitoredItem * is "reporting". Or the MonitoringMode is "sampling" and a link is trigered * that puts the last Notification into the global queue. */ void UA_Notification_enqueueAndTrigger(UA_Server *server, UA_Notification *n); /* Dequeue and delete the notification */ void UA_Notification_delete(UA_Notification *n); /* A NotificationMessage contains an array of notifications. * Sent NotificationMessages are stored for the republish service. */ typedef struct UA_NotificationMessageEntry { TAILQ_ENTRY(UA_NotificationMessageEntry) listEntry; UA_NotificationMessage message; } UA_NotificationMessageEntry; /* Queue Definitions */ typedef TAILQ_HEAD(NotificationQueue, UA_Notification) NotificationQueue; typedef TAILQ_HEAD(NotificationMessageQueue, UA_NotificationMessageEntry) NotificationMessageQueue; /*****************/ /* MonitoredItem */ /*****************/ /* The type of sampling for MonitoredItems depends on the sampling interval. * * >0: Cyclic callback * =0: Attached to the node. Sampling is triggered after every "write". * <0: Attached to the subscription. Triggered just before every "publish". */ typedef enum { UA_MONITOREDITEMSAMPLINGTYPE_NONE = 0, UA_MONITOREDITEMSAMPLINGTYPE_CYCLIC, /* Cyclic callback */ UA_MONITOREDITEMSAMPLINGTYPE_EVENT, /* Attached to the node. Can be a "write * event" for DataChange MonitoredItems * with a zero sampling interval .*/ UA_MONITOREDITEMSAMPLINGTYPE_PUBLISH /* Attached to the subscription */ } UA_MonitoredItemSamplingType; struct UA_MonitoredItem { UA_TimerEntry delayedFreePointers; LIST_ENTRY(UA_MonitoredItem) listEntry; /* Linked list in the Subscription */ UA_Subscription *subscription; /* If NULL, then this is a Local MonitoredItem */ UA_UInt32 monitoredItemId; /* Status and Settings */ UA_ReadValueId itemToMonitor; UA_MonitoringMode monitoringMode; UA_TimestampsToReturn timestampsToReturn; UA_Boolean registered; /* Registered in the server / Subscription */ UA_DateTime triggeredUntil; /* If the MonitoringMode is SAMPLING, * triggering the MonitoredItem puts the latest * Notification into the publishing queue (of * the Subscription). In addition, the first * new sample is also published (and not just * sampled) if it occurs within the duration of * one publishing cycle after the triggering. */ /* If the filter is a UA_DataChangeFilter: The DataChangeFilter always * contains an absolute deadband definition. Part 8, §6.2 gives the * following formula to test for percentage deadbands: * * DataChange if (absolute value of (last cached value - current value) * > (deadbandValue/100.0) * ((high–low) of EURange))) * * So we can convert from a percentage to an absolute deadband and keep * the hot code path simple. * * TODO: Store the percentage deadband to recompute when the UARange is * changed at runtime of the MonitoredItem */ UA_MonitoringParameters parameters; /* Sampling */ UA_MonitoredItemSamplingType samplingType; union { UA_UInt64 callbackId; UA_MonitoredItem *nodeListNext; /* Event-Based: Attached to Node */ LIST_ENTRY(UA_MonitoredItem) samplingListEntry; /* Publish-interval: Linked in * Subscription */ } sampling; UA_DataValue lastValue; /* Triggering Links */ size_t triggeringLinksSize; UA_UInt32 *triggeringLinks; /* Notification Queue */ NotificationQueue queue; size_t queueSize; /* This is the current size. See also the configured * (maximum) queueSize in the parameters. */ size_t eventOverflows; /* Separate counter for the queue. Can at most double * the queue size */ }; void UA_MonitoredItem_init(UA_MonitoredItem *mon); void UA_MonitoredItem_delete(UA_Server *server, UA_MonitoredItem *monitoredItem); void UA_MonitoredItem_removeOverflowInfoBits(UA_MonitoredItem *mon); void UA_Server_registerMonitoredItem(UA_Server *server, UA_MonitoredItem *mon); /* Register sampling. Either by adding a repeated callback or by adding the * MonitoredItem to a linked list in the node. */ UA_StatusCode UA_MonitoredItem_registerSampling(UA_Server *server, UA_MonitoredItem *mon); void UA_MonitoredItem_unregisterSampling(UA_Server *server, UA_MonitoredItem *mon); UA_StatusCode UA_MonitoredItem_setMonitoringMode(UA_Server *server, UA_MonitoredItem *mon, UA_MonitoringMode monitoringMode); void UA_MonitoredItem_sampleCallback(UA_Server *server, UA_MonitoredItem *monitoredItem); UA_StatusCode sampleCallbackWithValue(UA_Server *server, UA_Subscription *sub, UA_MonitoredItem *mon, UA_DataValue *value); UA_StatusCode UA_MonitoredItem_removeLink(UA_Subscription *sub, UA_MonitoredItem *mon, UA_UInt32 linkId); UA_StatusCode UA_MonitoredItem_addLink(UA_Subscription *sub, UA_MonitoredItem *mon, UA_UInt32 linkId); UA_StatusCode UA_MonitoredItem_createDataChangeNotification(UA_Server *server, UA_Subscription *sub, UA_MonitoredItem *mon, const UA_DataValue *value); UA_StatusCode UA_Event_addEventToMonitoredItem(UA_Server *server, const UA_NodeId *event, UA_MonitoredItem *mon); UA_StatusCode UA_Event_generateEventId(UA_ByteString *generatedId); void UA_Event_staticSelectClauseValidation(UA_Server *server, const UA_EventFilter *eventFilter, UA_StatusCode *result); UA_StatusCode UA_Event_staticWhereClauseValidation(UA_Server *server, const UA_ContentFilter *filter, UA_ContentFilterResult *); /* Remove entries until mon->maxQueueSize is reached. Sets infobits for lost * data if required. */ void UA_MonitoredItem_ensureQueueSpace(UA_Server *server, UA_MonitoredItem *mon); /****************/ /* Subscription */ /****************/ /* 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; /* Subscriptions are managed in a server-wide linked list. If they are attached * to a Session, then they are additionaly in the per-Session linked-list. A * subscription is always generated for a Session. But the CloseSession Service * may keep Subscriptions intact beyond the Session lifetime. They can then be * re-bound to a new Session with the TransferSubscription Service. */ struct UA_Subscription { UA_TimerEntry delayedFreePointers; LIST_ENTRY(UA_Subscription) serverListEntry; /* Ordered according to the priority byte and round-robin scheduling for * late subscriptions. See ua_session.h. Only set if session != NULL. */ TAILQ_ENTRY(UA_Subscription) sessionListEntry; UA_Session *session; /* May be NULL if no session is attached. */ UA_UInt32 subscriptionId; /* Settings */ UA_UInt32 lifeTimeCount; UA_UInt32 maxKeepAliveCount; UA_Double publishingInterval; /* in ms */ UA_UInt32 notificationsPerPublish; UA_Boolean publishingEnabled; UA_Byte priority; /* Runtime information */ UA_SubscriptionState state; UA_StatusCode statusChange; /* If set, a notification is generated and the * Subscription is deleted within * UA_Subscription_publish. */ UA_UInt32 nextSequenceNumber; UA_UInt32 currentKeepAliveCount; UA_UInt32 currentLifetimeCount; /* Publish Callback. Registered if id > 0. */ UA_UInt64 publishCallbackId; /* MonitoredItems */ UA_UInt32 lastMonitoredItemId; /* increase the identifiers */ LIST_HEAD(, UA_MonitoredItem) monitoredItems; UA_UInt32 monitoredItemsSize; /* MonitoredItems that are sampled in every publish callback (with the * publish interval of the subscription) */ LIST_HEAD(, UA_MonitoredItem) samplingMonitoredItems; /* Global list of notifications from the MonitoredItems */ TAILQ_HEAD(, UA_Notification) notificationQueue; UA_UInt32 notificationQueueSize; /* Total queue size */ UA_UInt32 dataChangeNotifications; UA_UInt32 eventNotifications; /* Retransmission Queue */ NotificationMessageQueue retransmissionQueue; size_t retransmissionQueueSize; /* Statistics for the server diagnostics. The fields are defined according * to the SubscriptionDiagnosticsDataType (Part 5, §12.15). */ #ifdef UA_ENABLE_DIAGNOSTICS UA_UInt32 modifyCount; UA_UInt32 enableCount; UA_UInt32 disableCount; UA_UInt32 republishRequestCount; UA_UInt32 republishMessageCount; UA_UInt32 transferRequestCount; UA_UInt32 transferredToAltClientCount; UA_UInt32 transferredToSameClientCount; UA_UInt32 publishRequestCount; UA_UInt32 dataChangeNotificationsCount; UA_UInt32 eventNotificationsCount; UA_UInt32 notificationsCount; UA_UInt32 latePublishRequestCount; UA_UInt32 discardedMessageCount; UA_UInt32 monitoringQueueOverflowCount; UA_UInt32 eventQueueOverFlowCount; #endif }; UA_Subscription * UA_Subscription_new(void); void UA_Subscription_delete(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); UA_MonitoredItem * UA_Subscription_getMonitoredItem(UA_Subscription *sub, UA_UInt32 monitoredItemId); void UA_Subscription_sampleAndPublish(UA_Server *server, UA_Subscription *sub); UA_Boolean UA_Subscription_publishOnce(UA_Server *server, UA_Subscription *sub); void UA_Subscription_publish(UA_Server *server, UA_Subscription *sub); UA_StatusCode UA_Subscription_removeRetransmissionMessage(UA_Subscription *sub, UA_UInt32 sequenceNumber); UA_Boolean UA_Session_reachedPublishReqLimit(UA_Server *server, UA_Session *session); /* Forward declaration for A&C used in ua_server_internal.h" */ struct UA_ConditionSource; typedef struct UA_ConditionSource UA_ConditionSource; /***********/ /* Helpers */ /***********/ /* Evaluate content filter, Only for unit testing */ #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS UA_StatusCode UA_Server_evaluateWhereClauseContentFilter(UA_Server *server, UA_Session *session, const UA_NodeId *eventNode, const UA_ContentFilter *contentFilter, UA_ContentFilterResult *contentFilterResult); #endif /* Setting an integer value within bounds */ #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; \ } /* Logging * See a description of the tricks used in ua_session.h */ #define UA_LOG_SUBSCRIPTION_INTERNAL(LOGGER, LEVEL, SUB, MSG, ...) \ do { \ if((SUB) && (SUB)->session) { \ UA_LOG_##LEVEL##_SESSION(LOGGER, (SUB)->session, \ "Subscription %" PRIu32 " | " MSG "%.0s", \ (SUB)->subscriptionId, __VA_ARGS__); \ } else { \ UA_LOG_##LEVEL(LOGGER, UA_LOGCATEGORY_SERVER, \ "Subscription %" PRIu32 " | " MSG "%.0s", \ (SUB) ? (SUB)->subscriptionId : 0, __VA_ARGS__); \ } \ } while(0) #if UA_LOGLEVEL <= 100 # define UA_LOG_TRACE_SUBSCRIPTION(LOGGER, SUB, ...) \ UA_MACRO_EXPAND(UA_LOG_SUBSCRIPTION_INTERNAL(LOGGER, TRACE, SUB, __VA_ARGS__, "")) #else # define UA_LOG_TRACE_SUBSCRIPTION(LOGGER, SUB, ...) do {} while(0) #endif #if UA_LOGLEVEL <= 200 # define UA_LOG_DEBUG_SUBSCRIPTION(LOGGER, SUB, ...) \ UA_MACRO_EXPAND(UA_LOG_SUBSCRIPTION_INTERNAL(LOGGER, DEBUG, SUB, __VA_ARGS__, "")) #else # define UA_LOG_DEBUG_SUBSCRIPTION(LOGGER, SUB, ...) do {} while(0) #endif #if UA_LOGLEVEL <= 300 # define UA_LOG_INFO_SUBSCRIPTION(LOGGER, SUB, ...) \ UA_MACRO_EXPAND(UA_LOG_SUBSCRIPTION_INTERNAL(LOGGER, INFO, SUB, __VA_ARGS__, "")) #else # define UA_LOG_INFO_SUBSCRIPTION(LOGGER, SUB, ...) do {} while(0) #endif #if UA_LOGLEVEL <= 400 # define UA_LOG_WARNING_SUBSCRIPTION(LOGGER, SUB, ...) \ UA_MACRO_EXPAND(UA_LOG_SUBSCRIPTION_INTERNAL(LOGGER, WARNING, SUB, __VA_ARGS__, "")) #else # define UA_LOG_WARNING_SUBSCRIPTION(LOGGER, SUB, ...) do {} while(0) #endif #if UA_LOGLEVEL <= 500 # define UA_LOG_ERROR_SUBSCRIPTION(LOGGER, SUB, ...) \ UA_MACRO_EXPAND(UA_LOG_SUBSCRIPTION_INTERNAL(LOGGER, ERROR, SUB, __VA_ARGS__, "")) #else # define UA_LOG_ERROR_SUBSCRIPTION(LOGGER, SUB, ...) do {} while(0) #endif #if UA_LOGLEVEL <= 600 # define UA_LOG_FATAL_SUBSCRIPTION(LOGGER, SUB, ...) \ UA_MACRO_EXPAND(UA_LOG_SUBSCRIPTION_INTERNAL(LOGGER, FATAL, SUB, __VA_ARGS__, "")) #else # define UA_LOG_FATAL_SUBSCRIPTION(LOGGER, SUB, ...) do {} while(0) #endif #endif /* UA_ENABLE_SUBSCRIPTIONS */ _UA_END_DECLS /**** amalgamated original file "/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) * Copyright (c) 2017-2019 Fraunhofer IOSB (Author: Andreas Ebner) */ _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_UNKNOWN = 3 } 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; /** * DataSetMessage * ^^^^^^^^^^^^^^ */ typedef struct { UA_UInt16 fieldCount; UA_DataValue* dataSetFields; UA_ByteString rawFields; /* 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; 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_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_NetworkMessage; /**********************************************/ /* Network Message Offsets */ /**********************************************/ /* Offsets for buffered messages in the PubSub fast path. */ typedef enum { UA_PUBSUB_OFFSETTYPE_DATASETMESSAGE_SEQUENCENUMBER, UA_PUBSUB_OFFSETTYPE_NETWORKMESSAGE_SEQUENCENUMBER, UA_PUBSUB_OFFSETTYPE_NETWORKMESSAGE_FIELDENCDODING, UA_PUBSUB_OFFSETTYPE_TIMESTAMP_PICOSECONDS, UA_PUBSUB_OFFSETTYPE_TIMESTAMP, /* source pointer */ UA_PUBSUB_OFFSETTYPE_TIMESTAMP_NOW, /* no source */ UA_PUBSUB_OFFSETTYPE_PAYLOAD_DATAVALUE, UA_PUBSUB_OFFSETTYPE_PAYLOAD_VARIANT, UA_PUBSUB_OFFSETTYPE_PAYLOAD_RAW, /* For subscriber RT */ UA_PUBSUB_OFFSETTYPE_PUBLISHERID, UA_PUBSUB_OFFSETTYPE_WRITERGROUPID, UA_PUBSUB_OFFSETTYPE_DATASETWRITERID /* Add more offset types as needed */ } UA_NetworkMessageOffsetType; typedef struct { UA_NetworkMessageOffsetType contentType; union { struct { UA_DataValue *value; size_t valueBinarySize; } value; UA_DateTime *timestamp; } offsetData; size_t offset; } UA_NetworkMessageOffset; typedef struct { UA_ByteString buffer; /* The precomputed message buffer */ UA_NetworkMessageOffset *offsets; /* Offsets for changes in the message buffer */ size_t offsetsSize; UA_Boolean RTsubscriberEnabled; /* Addtional offsets computation like publisherId, WGId if this bool enabled */ UA_NetworkMessage *nm; /* The precomputed NetworkMessage for subscriber */ size_t rawMessageLength; } UA_NetworkMessageOffsetBuffer; void UA_NetworkMessageOffsetBuffer_clear(UA_NetworkMessageOffsetBuffer *ob); /** * DataSetMessage * ^^^^^^^^^^^^^^ */ 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); 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, UA_UInt16 dsmSize); size_t UA_DataSetMessage_calcSizeBinary(UA_DataSetMessage *p, UA_NetworkMessageOffsetBuffer *offsetBuffer, size_t currentOffset); void UA_DataSetMessage_clear(const UA_DataSetMessage* p); /** * NetworkMessage * ^^^^^^^^^^^^^^ */ UA_StatusCode UA_NetworkMessage_updateBufferedMessage(UA_NetworkMessageOffsetBuffer *buffer); UA_StatusCode UA_NetworkMessage_updateBufferedNwMessage(UA_NetworkMessageOffsetBuffer *buffer, const UA_ByteString *src, size_t *bufferPosition); /** * NetworkMessage Encoding * ^^^^^^^^^^^^^^^^^^^^^^^ */ /* If dataToEncryptStart not-NULL, then it will be set to the start-position of * the payload in the buffer. */ UA_StatusCode UA_NetworkMessage_encodeBinary(const UA_NetworkMessage* src, UA_Byte **bufPos, const UA_Byte *bufEnd, UA_Byte **dataToEncryptStart); UA_StatusCode UA_NetworkMessage_encodeHeaders(const UA_NetworkMessage* src, UA_Byte **bufPos, const UA_Byte *bufEnd); UA_StatusCode UA_NetworkMessage_encodePayload(const UA_NetworkMessage* src, UA_Byte **bufPos, const UA_Byte *bufEnd); UA_StatusCode UA_NetworkMessage_encodeFooters(const UA_NetworkMessage* src, UA_Byte **bufPos, const UA_Byte *bufEnd); /** * NetworkMessage Decoding * ^^^^^^^^^^^^^^^^^^^^^^^ */ UA_StatusCode UA_NetworkMessage_decodeHeaders(const UA_ByteString *src, size_t *offset, UA_NetworkMessage *dst); UA_StatusCode UA_NetworkMessage_decodePayload(const UA_ByteString *src, size_t *offset, UA_NetworkMessage *dst); UA_StatusCode UA_NetworkMessage_decodeFooters(const UA_ByteString *src, size_t *offset, UA_NetworkMessage *dst); UA_StatusCode UA_NetworkMessage_decodeBinary(const UA_ByteString *src, size_t *offset, UA_NetworkMessage* dst); UA_StatusCode UA_NetworkMessageHeader_decodeBinary(const UA_ByteString *src, size_t *offset, UA_NetworkMessage *dst); size_t UA_NetworkMessage_calcSizeBinary(UA_NetworkMessage *p, UA_NetworkMessageOffsetBuffer *offsetBuffer); #ifdef UA_ENABLE_PUBSUB_ENCRYPTION UA_StatusCode UA_NetworkMessage_signEncrypt(UA_NetworkMessage *nm, UA_MessageSecurityMode securityMode, UA_PubSubSecurityPolicy *policy, void *policyContext, UA_Byte *messageStart, UA_Byte *encryptStart, UA_Byte *sigStart); #endif void UA_NetworkMessage_clear(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 "/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 * Copyright (c) 2020 Yannick Wallerer, Siemens AG * Copyright (c) 2020 Thomas Fischer, Siemens AG * Copyright (c) 2021 Fraunhofer IOSB (Author: Jan Hermes) */ /* The public configuration structs are defined in include/ua_plugin_pubsub.h */ _UA_BEGIN_DECLS #ifdef UA_ENABLE_PUBSUB struct UA_WriterGroup; typedef struct UA_WriterGroup UA_WriterGroup; struct UA_ReaderGroup; typedef struct UA_ReaderGroup UA_ReaderGroup; /**********************************************/ /* PublishedDataSet */ /**********************************************/ typedef struct UA_PublishedDataSet { UA_PublishedDataSetConfig config; UA_DataSetMetaDataType dataSetMetaData; TAILQ_HEAD(UA_ListOfDataSetField, UA_DataSetField) fields; UA_NodeId identifier; UA_UInt16 fieldSize; UA_UInt16 promotedFieldsCount; UA_UInt16 configurationFreezeCounter; TAILQ_ENTRY(UA_PublishedDataSet) listEntry; UA_Boolean configurationFrozen; } 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_clear(UA_Server *server, UA_PublishedDataSet *publishedDataSet); /**********************************************/ /* Connection */ /**********************************************/ typedef struct UA_PubSubConnection { UA_PubSubComponentEnumType componentType; UA_PubSubConnectionConfig *config; UA_PubSubChannel *channel; UA_NodeId identifier; LIST_HEAD(UA_ListOfWriterGroup, UA_WriterGroup) writerGroups; size_t writerGroupsSize; LIST_HEAD(UA_ListOfPubSubReaderGroup, UA_ReaderGroup) readerGroups; size_t readerGroupsSize; TAILQ_ENTRY(UA_PubSubConnection) listEntry; UA_UInt16 configurationFreezeCounter; UA_Boolean isRegistered; /* Subscriber requires connection channel regist */ UA_Boolean configurationFrozen; } 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_clear(UA_PubSubConnectionConfig *connectionConfig); void UA_PubSubConnection_clear(UA_Server *server, UA_PubSubConnection *connection); /* Register channel for given connectionIdentifier */ UA_StatusCode UA_PubSubConnection_regist(UA_Server *server, UA_NodeId *connectionIdentifier); /* Process Network Message for a ReaderGroup. But we the ReaderGroup needs to be * identified first. */ UA_StatusCode UA_Server_processNetworkMessage(UA_Server *server, UA_PubSubConnection *connection, UA_NetworkMessage *msg); /**********************************************/ /* DataSetWriter */ /**********************************************/ #ifdef UA_ENABLE_PUBSUB_DELTAFRAMES typedef struct UA_DataSetWriterSample { UA_Boolean valueChanged; UA_DataValue value; } UA_DataSetWriterSample; #endif typedef struct UA_DataSetWriter { UA_PubSubComponentEnumType componentType; UA_DataSetWriterConfig config; LIST_ENTRY(UA_DataSetWriter) listEntry; UA_NodeId identifier; UA_NodeId linkedWriterGroup; UA_NodeId connectedDataSet; UA_ConfigurationVersionDataType connectedDataSetVersion; UA_PubSubState state; #ifdef UA_ENABLE_PUBSUB_DELTAFRAMES UA_UInt16 deltaFrameCounter; /* count of sent deltaFrames */ size_t lastSamplesCount; UA_DataSetWriterSample *lastSamples; #endif UA_UInt16 actualDataSetMessageSequenceCount; UA_Boolean configurationFrozen; } 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); UA_StatusCode UA_DataSetWriter_setPubSubState(UA_Server *server, UA_PubSubState state, UA_DataSetWriter *dataSetWriter); UA_StatusCode UA_DataSetWriter_generateDataSetMessage(UA_Server *server, UA_DataSetMessage *dataSetMessage, UA_DataSetWriter *dataSetWriter); UA_StatusCode UA_DataSetWriter_remove(UA_Server *server, UA_WriterGroup *linkedWriterGroup, UA_DataSetWriter *dataSetWriter); /**********************************************/ /* WriterGroup */ /**********************************************/ struct UA_WriterGroup { UA_PubSubComponentEnumType componentType; UA_WriterGroupConfig config; LIST_ENTRY(UA_WriterGroup) listEntry; UA_NodeId identifier; UA_PubSubConnection *linkedConnection; LIST_HEAD(UA_ListOfDataSetWriter, UA_DataSetWriter) writers; UA_UInt32 writersCount; UA_UInt64 publishCallbackId; UA_Boolean publishCallbackIsRegistered; UA_PubSubState state; UA_NetworkMessageOffsetBuffer bufferedMessage; UA_UInt16 sequenceNumber; /* Increased after every succressuly sent message */ UA_Boolean configurationFrozen; #ifdef UA_ENABLE_PUBSUB_ENCRYPTION UA_UInt32 securityTokenId; UA_UInt32 nonceSequenceNumber; /* To be part of the MessageNonce */ void *securityPolicyContext; #endif }; UA_StatusCode UA_WriterGroupConfig_copy(const UA_WriterGroupConfig *src, UA_WriterGroupConfig *dst); UA_WriterGroup * UA_WriterGroup_findWGbyId(UA_Server *server, UA_NodeId identifier); UA_StatusCode UA_WriterGroup_setPubSubState(UA_Server *server, UA_PubSubState state, UA_WriterGroup *writerGroup); /**********************************************/ /* DataSetField */ /**********************************************/ typedef struct UA_DataSetField { UA_DataSetFieldConfig config; TAILQ_ENTRY(UA_DataSetField) listEntry; UA_NodeId identifier; UA_NodeId publishedDataSet; /* parent pds */ UA_FieldMetaData fieldMetaData; /* contains the dataSetFieldId */ UA_UInt64 sampleCallbackId; UA_Boolean sampleCallbackIsRegistered; UA_Boolean configurationFrozen; } 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 */ /**********************************************/ /* DataSetReader Type definition */ typedef struct UA_DataSetReader { UA_PubSubComponentEnumType componentType; UA_DataSetReaderConfig config; UA_NodeId identifier; UA_NodeId linkedReaderGroup; LIST_ENTRY(UA_DataSetReader) listEntry; UA_PubSubState state; /* non std */ UA_Boolean configurationFrozen; UA_NetworkMessageOffsetBuffer bufferedMessage; #ifdef UA_ENABLE_PUBSUB_MONITORING /* MessageReceiveTimeout handling */ UA_ServerCallback msgRcvTimeoutTimerCallback; UA_UInt64 msgRcvTimeoutTimerId; UA_Boolean msgRcvTimeoutTimerRunning; #endif } UA_DataSetReader; /* Process Network Message using DataSetReader */ void UA_DataSetReader_process(UA_Server *server, UA_ReaderGroup *readerGroup, UA_DataSetReader *dataSetReader, UA_DataSetMessage *dataSetMsg); /* Copy the configuration of DataSetReader */ UA_StatusCode UA_DataSetReaderConfig_copy(const UA_DataSetReaderConfig *src, UA_DataSetReaderConfig *dst); /* Clear the configuration of a DataSetReader */ void UA_DataSetReaderConfig_clear(UA_DataSetReaderConfig *cfg); /* Copy the configuration of Target Variables */ UA_StatusCode UA_TargetVariables_copy(const UA_TargetVariables *src, UA_TargetVariables *dst); /* Clear the Target Variables configuration */ void UA_TargetVariables_clear(UA_TargetVariables *subscribedDataSetTarget); /* Copy the configuration of Field Target Variables */ UA_StatusCode UA_FieldTargetVariable_copy(const UA_FieldTargetVariable *src, UA_FieldTargetVariable *dst); UA_StatusCode UA_DataSetReader_setPubSubState(UA_Server *server, UA_PubSubState state, UA_DataSetReader *dataSetReader); #ifdef UA_ENABLE_PUBSUB_MONITORING /* Check if DataSetReader has a message receive timeout */ void UA_DataSetReader_checkMessageReceiveTimeout(UA_Server *server, UA_DataSetReader *dataSetReader); /* DataSetReader MessageReceiveTimeout callback for generic PubSub component * timeout handling */ void UA_DataSetReader_handleMessageReceiveTimeout(UA_Server *server, void *dataSetReader); #endif /* UA_ENABLE_PUBSUB_MONITORING */ UA_StatusCode UA_DataSetReader_generateNetworkMessage(UA_PubSubConnection *pubSubConnection, UA_DataSetReader *dataSetReader, UA_DataSetMessage *dsm, UA_UInt16 *writerId, UA_Byte dsmCount, UA_NetworkMessage *nm); UA_StatusCode UA_DataSetReader_generateDataSetMessage(UA_Server *server, UA_DataSetMessage *dataSetMessage, UA_DataSetReader *dataSetReader); /**********************************************/ /* ReaderGroup */ /**********************************************/ struct UA_ReaderGroup { UA_PubSubComponentEnumType componentType; 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_PubSubState state; UA_Boolean configurationFrozen; #ifdef UA_ENABLE_PUBSUB_ENCRYPTION UA_UInt32 securityTokenId; UA_UInt32 nonceSequenceNumber; /* To be part of the MessageNonce */ void *securityPolicyContext; #endif }; UA_StatusCode UA_ReaderGroupConfig_copy(const UA_ReaderGroupConfig *src, UA_ReaderGroupConfig *dst); /* 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); UA_StatusCode UA_ReaderGroup_setPubSubState(UA_Server *server, UA_PubSubState state, UA_ReaderGroup *readerGroup); /*********************************************************/ /* 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_removeSubscribeCallback(UA_Server *server, UA_ReaderGroup *readerGroup); void UA_ReaderGroup_subscribeCallback(UA_Server *server, UA_ReaderGroup *readerGroup); /*********************************************************/ /* Reading Message handling */ /*********************************************************/ #ifdef UA_ENABLE_PUBSUB_ENCRYPTION UA_StatusCode verifyAndDecrypt(const UA_Logger *logger, UA_ByteString *buffer, const size_t *currentPosition, const UA_NetworkMessage *nm, UA_Boolean doValidate, UA_Boolean doDecrypt, void *channelContext, UA_PubSubSecurityPolicy *securityPolicy); UA_StatusCode verifyAndDecryptNetworkMessage(const UA_Logger *logger, UA_ByteString *buffer, size_t *currentPosition, UA_NetworkMessage *nm, UA_ReaderGroup *readerGroup); #endif /* Takes a value (and not a pointer) to the buffer. The original buffer is const. Internally we may adjust the length during decryption. */ UA_StatusCode decodeNetworkMessage(UA_Server *server, UA_ByteString *buffer, size_t *pos, UA_NetworkMessage *nm, UA_PubSubConnection *connection); UA_StatusCode receiveBufferedNetworkMessage(UA_Server *server, UA_ReaderGroup *readerGroup, UA_PubSubConnection *connection); #endif /* UA_ENABLE_PUBSUB */ _UA_END_DECLS /**** amalgamated original file "/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-2019 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; TAILQ_HEAD(UA_ListOfPubSubConnection, UA_PubSubConnection) connections; size_t publishedDataSetsSize; TAILQ_HEAD(UA_ListOfPublishedDataSet, UA_PublishedDataSet) publishedDataSets; #ifndef UA_ENABLE_PUBSUB_INFORMATIONMODEL UA_UInt32 uniqueIdCount; #endif } UA_PubSubManager; void UA_PubSubManager_delete(UA_Server *server, UA_PubSubManager *pubSubManager); #ifndef UA_ENABLE_PUBSUB_INFORMATIONMODEL void UA_PubSubManager_generateUniqueNodeId(UA_PubSubManager *psm, UA_NodeId *nodeId); #endif UA_Guid UA_PubSubManager_generateUniqueGuid(UA_Server *server); 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_DateTime *baseTime, UA_TimerPolicy timerPolicy, UA_UInt64 *callbackId); UA_StatusCode UA_PubSubManager_changeRepeatedCallback(UA_Server *server, UA_UInt64 callbackId, UA_Double interval_ms, UA_DateTime *baseTime, UA_TimerPolicy timerPolicy); void UA_PubSubManager_removeRepeatedPubSubCallback(UA_Server *server, UA_UInt64 callbackId); /*************************************************/ /* PubSub component monitoring */ /*************************************************/ #ifdef UA_ENABLE_PUBSUB_MONITORING UA_StatusCode UA_PubSubManager_setDefaultMonitoringCallbacks(UA_PubSubMonitoringInterface *monitoringInterface); #endif /* UA_ENABLE_PUBSUB_MONITORING */ #endif /* UA_ENABLE_PUBSUB */ _UA_END_DECLS /**** amalgamated original file "/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); UA_StatusCode removeReaderGroupRepresentation(UA_Server *server, UA_ReaderGroup *readerGroup); #endif /* UA_ENABLE_PUBSUB_INFORMATIONMODEL */ _UA_END_DECLS #endif /* UA_PUBSUB_NS0_H_ */ /**** amalgamated original file "/src/server/ua_server_async.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 2019 (c) Fraunhofer IOSB (Author: Klaus Schick) * based on * 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 #if UA_MULTITHREADING >= 100 struct UA_AsyncResponse; typedef struct UA_AsyncResponse UA_AsyncResponse; /* A single operation (of a larger request) */ typedef struct UA_AsyncOperation { TAILQ_ENTRY(UA_AsyncOperation) pointers; UA_CallMethodRequest request; UA_CallMethodResult response; size_t index; /* Index of the operation in the array of ops in * request/response */ UA_AsyncResponse *parent; /* Always non-NULL. The parent is only removed * when its operations are removed */ } UA_AsyncOperation; struct UA_AsyncResponse { TAILQ_ENTRY(UA_AsyncResponse) pointers; /* Insert new at the end */ UA_UInt32 requestId; UA_NodeId sessionId; UA_UInt32 requestHandle; UA_DateTime timeout; UA_AsyncOperationType operationType; union { UA_CallResponse callResponse; UA_ReadResponse readResponse; UA_WriteResponse writeResponse; } response; UA_UInt32 opCountdown; /* Counter for outstanding operations. The AR can * only be deleted when all have returned. */ }; typedef TAILQ_HEAD(UA_AsyncOperationQueue, UA_AsyncOperation) UA_AsyncOperationQueue; typedef struct { /* Requests / Responses */ TAILQ_HEAD(, UA_AsyncResponse) asyncResponses; size_t asyncResponsesCount; /* Operations for the workers. The queues are all FIFO: Put in at the tail, * take out at the head.*/ UA_Lock queueLock; UA_AsyncOperationQueue newQueue; /* New operations for the workers */ UA_AsyncOperationQueue dispatchedQueue; /* Operations taken by a worker. When a result is * returned, we search for the op here to see if it * is still "alive" (not timed out). */ UA_AsyncOperationQueue resultQueue; /* Results to be integrated */ size_t opsCount; /* How many operations are transient (in one of the three queues)? */ UA_UInt64 checkTimeoutCallbackId; /* Registered repeated callbacks */ } UA_AsyncManager; void UA_AsyncManager_init(UA_AsyncManager *am, UA_Server *server); void UA_AsyncManager_clear(UA_AsyncManager *am, UA_Server *server); UA_StatusCode UA_AsyncManager_createAsyncResponse(UA_AsyncManager *am, UA_Server *server, const UA_NodeId *sessionId, const UA_UInt32 requestId, const UA_UInt32 requestHandle, const UA_AsyncOperationType operationType, UA_AsyncResponse **outAr); /* Only remove the AsyncResponse when the operation count is zero */ void UA_AsyncManager_removeAsyncResponse(UA_AsyncManager *am, UA_AsyncResponse *ar); UA_StatusCode UA_AsyncManager_createAsyncOp(UA_AsyncManager *am, UA_Server *server, UA_AsyncResponse *ar, size_t opIndex, const UA_CallMethodRequest *opRequest); typedef void (*UA_AsyncServiceOperation)(UA_Server *server, UA_Session *session, UA_UInt32 requestId, UA_UInt32 requestHandle, size_t opIndex, const void *requestOperation, void *responseOperation, UA_AsyncResponse **ar); /* Creates an AsyncResponse in-situ when an async operation is encountered. If * that is the case, the sync responses are moved to the AsyncResponse. */ UA_StatusCode UA_Server_processServiceOperationsAsync(UA_Server *server, UA_Session *session, UA_UInt32 requestId, UA_UInt32 requestHandle, UA_AsyncServiceOperation operationCallback, const size_t *requestOperations, const UA_DataType *requestOperationsType, size_t *responseOperations, const UA_DataType *responseOperationsType, UA_AsyncResponse **ar) UA_FUNC_ATTR_WARN_UNUSED_RESULT; #endif /* UA_MULTITHREADING >= 100 */ _UA_END_DECLS /**** amalgamated original file "/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 2019 (c) Fraunhofer IOSB (Author: Klaus Schick) * 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 * Copyright 2019 (c) HMS Industrial Networks AB (Author: Jonas Green) */ _UA_BEGIN_DECLS #if UA_MULTITHREADING >= 100 #undef UA_THREADSAFE #define UA_THREADSAFE UA_DEPRECATED #endif #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_DIAGNOSTICEVENT_CLOSE, UA_DIAGNOSTICEVENT_REJECT, UA_DIAGNOSTICEVENT_SECURITYREJECT, UA_DIAGNOSTICEVENT_TIMEOUT, UA_DIAGNOSTICEVENT_ABORT, UA_DIAGNOSTICEVENT_PURGE } UA_DiagnosticEvent; typedef struct channel_entry { UA_TimerEntry cleanupCallback; TAILQ_ENTRY(channel_entry) pointers; UA_SecureChannel channel; } channel_entry; typedef struct session_list_entry { UA_TimerEntry cleanupCallback; LIST_ENTRY(session_list_entry) pointers; UA_Session session; } session_list_entry; 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 */ UA_ServerLifecycle state; /* SecureChannels */ TAILQ_HEAD(, channel_entry) channels; UA_UInt32 lastChannelId; UA_UInt32 lastTokenId; #if UA_MULTITHREADING >= 100 UA_AsyncManager asyncManager; #endif /* Session Management */ LIST_HEAD(session_list, session_list_entry) sessions; UA_UInt32 sessionCount; UA_UInt32 activeSessionCount; 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; /* 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 /* Subscriptions */ #ifdef UA_ENABLE_SUBSCRIPTIONS size_t subscriptionsSize; /* Number of active subscriptions */ size_t monitoredItemsSize; /* Number of active monitored items */ LIST_HEAD(, UA_Subscription) subscriptions; /* All subscriptions in the * server. They may be detached * from a session. */ UA_UInt32 lastSubscriptionId; /* To generate unique SubscriptionIds */ /* To be cast to UA_LocalMonitoredItem to get the callback and context */ LIST_HEAD(, UA_MonitoredItem) localMonitoredItems; UA_UInt32 lastLocalMonitoredItemId; # ifdef UA_ENABLE_SUBSCRIPTIONS_ALARMS_CONDITIONS LIST_HEAD(, UA_ConditionSource) conditionSources; # endif #endif /* Publish/Subscribe */ #ifdef UA_ENABLE_PUBSUB UA_PubSubManager pubSubManager; #endif #if UA_MULTITHREADING >= 100 UA_Lock networkMutex; UA_Lock serviceMutex; #endif /* Statistics */ UA_NetworkStatistics networkStatistics; UA_SecureChannelStatistics secureChannelStatistics; UA_ServerDiagnosticsSummaryDataType serverDiagnosticsSummary; }; /***********************/ /* References Handling */ /***********************/ extern const struct aa_head refNameTree; const UA_ReferenceTarget * UA_NodeReferenceKind_findTarget(const UA_NodeReferenceKind *rk, const UA_ExpandedNodeId *targetId); /**************************/ /* SecureChannel Handling */ /**************************/ /* Remove all securechannels */ void UA_Server_deleteSecureChannels(UA_Server *server); /* Remove timed out securechannels with a delayed callback. So all currently * scheduled jobs with a pointer to a securechannel can finish first. */ void UA_Server_cleanupTimedOutSecureChannels(UA_Server *server, UA_DateTime nowMonotonic); UA_StatusCode UA_Server_createSecureChannel(UA_Server *server, UA_Connection *connection); UA_StatusCode UA_Server_configSecureChannel(void *application, UA_SecureChannel *channel, const UA_AsymmetricAlgorithmSecurityHeader *asymHeader); UA_StatusCode sendServiceFault(UA_SecureChannel *channel, UA_UInt32 requestId, UA_UInt32 requestHandle, UA_StatusCode statusCode); void UA_Server_closeSecureChannel(UA_Server *server, UA_SecureChannel *channel, UA_DiagnosticEvent event); /* Gets the a pointer to the context of a security policy supported by the * server matched by the security policy uri. */ UA_SecurityPolicy * getSecurityPolicyByUri(const UA_Server *server, const UA_ByteString *securityPolicyUri); /********************/ /* Session Handling */ /********************/ UA_StatusCode getNamespaceByName(UA_Server *server, const UA_String namespaceUri, size_t *foundIndex); UA_StatusCode getNamespaceByIndex(UA_Server *server, const size_t namespaceIndex, UA_String *foundUri); UA_StatusCode getBoundSession(UA_Server *server, const UA_SecureChannel *channel, const UA_NodeId *token, UA_Session **session); UA_StatusCode UA_Server_createSession(UA_Server *server, UA_SecureChannel *channel, const UA_CreateSessionRequest *request, UA_Session **session); void UA_Server_removeSession(UA_Server *server, session_list_entry *sentry, UA_DiagnosticEvent event); UA_StatusCode UA_Server_removeSessionByToken(UA_Server *server, const UA_NodeId *token, UA_DiagnosticEvent event); void UA_Server_cleanupSessions(UA_Server *server, UA_DateTime nowMonotonic); UA_Session * getSessionByToken(UA_Server *server, const UA_NodeId *token); UA_Session * UA_Server_getSessionById(UA_Server *server, const UA_NodeId *sessionId); /*****************/ /* Node Handling */ /*****************/ /* 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 */ /*********************/ void setupNs1Uri(UA_Server *server); UA_UInt16 addNamespace(UA_Server *server, const UA_String name); UA_Boolean UA_Node_hasSubTypeOrInstances(const UA_NodeHead *head); /* Recursively searches "upwards" in the tree following specific reference types */ UA_Boolean isNodeInTree(UA_Server *server, const UA_NodeId *leafNode, const UA_NodeId *nodeToFind, const UA_ReferenceTypeSet *relevantRefs); /* Convenience function with just a single ReferenceTypeIndex */ UA_Boolean isNodeInTree_singleRef(UA_Server *server, const UA_NodeId *leafNode, const UA_NodeId *nodeToFind, const UA_Byte relevantRefTypeIndex); /* Returns an array with the hierarchy of nodes. The start nodes can be returned * as well. The returned array starts at the leaf and continues "upwards" or * "downwards". Duplicate entries are removed. */ UA_StatusCode browseRecursive(UA_Server *server, size_t startNodesSize, const UA_NodeId *startNodes, UA_BrowseDirection browseDirection, const UA_ReferenceTypeSet *refTypes, UA_UInt32 nodeClassMask, UA_Boolean includeStartNodes, size_t *resultsSize, UA_ExpandedNodeId **results); /* Get the bitfield indices of a ReferenceType and possibly its subtypes. * refType must point to a ReferenceTypeNode. */ UA_StatusCode referenceTypeIndices(UA_Server *server, const UA_NodeId *refType, UA_ReferenceTypeSet *indices, UA_Boolean includeSubtypes); /* 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 recursive interface hierarchy of the node */ UA_StatusCode getAllInterfaceChildNodeIds(UA_Server *server, const UA_NodeId *objectNode, const UA_NodeId *objectTypeNode, UA_NodeId **interfaceChildNodes, size_t *interfaceChildNodesSize); #ifdef UA_ENABLE_SUBSCRIPTIONS_ALARMS_CONDITIONS UA_StatusCode UA_getConditionId(UA_Server *server, const UA_NodeId *conditionNodeId, UA_NodeId *outConditionId); void UA_ConditionList_delete(UA_Server *server); UA_Boolean isConditionOrBranch(UA_Server *server, const UA_NodeId *condition, const UA_NodeId *conditionSource, UA_Boolean *isCallerAC); #endif /* UA_ENABLE_SUBSCRIPTIONS_ALARMS_CONDITIONS */ /* 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_NodeHead *nodeHead); UA_StatusCode sendResponse(UA_Server *server, UA_Session *session, UA_SecureChannel *channel, UA_UInt32 requestId, UA_Response *response, const UA_DataType *responseType); /* 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; /******************************************/ /* Internal function calls, without locks */ /******************************************/ UA_StatusCode deleteNode(UA_Server *server, const UA_NodeId nodeId, UA_Boolean deleteReferences); UA_StatusCode 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); UA_StatusCode addRef(UA_Server *server, UA_Session *session, const UA_NodeId *sourceId, const UA_NodeId *referenceTypeId, const UA_NodeId *targetId, UA_Boolean forward); UA_StatusCode setVariableNode_dataSource(UA_Server *server, const UA_NodeId nodeId, const UA_DataSource dataSource); UA_StatusCode writeAttribute(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId, const UA_AttributeId attributeId, const void *attr, const UA_DataType *attr_type); static UA_INLINE UA_StatusCode writeValueAttribute(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId, const UA_Variant *value) { return writeAttribute(server, session, nodeId, UA_ATTRIBUTEID_VALUE, value, &UA_TYPES[UA_TYPES_VARIANT]); } UA_DataValue readAttribute(UA_Server *server, const UA_ReadValueId *item, UA_TimestampsToReturn timestamps); UA_StatusCode readWithReadValue(UA_Server *server, const UA_NodeId *nodeId, const UA_AttributeId attributeId, void *v); UA_StatusCode readObjectProperty(UA_Server *server, const UA_NodeId objectId, const UA_QualifiedName propertyName, UA_Variant *value); UA_BrowsePathResult translateBrowsePathToNodeIds(UA_Server *server, const UA_BrowsePath *browsePath); #ifdef UA_ENABLE_SUBSCRIPTIONS void monitoredItem_sampleCallback(UA_Server *server, UA_MonitoredItem *monitoredItem); UA_Subscription * UA_Server_getSubscriptionById(UA_Server *server, UA_UInt32 subscriptionId); #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS UA_StatusCode triggerEvent(UA_Server *server, const UA_NodeId eventNodeId, const UA_NodeId origin, UA_ByteString *outEventId, const UA_Boolean deleteEventNode); /* Filters the given event with the given filter and writes the results into a * notification */ UA_StatusCode filterEvent(UA_Server *server, UA_Session *session, const UA_NodeId *eventNode, UA_EventFilter *filter, UA_EventFieldList *efl, UA_EventFilterResult *result); #endif /* UA_ENABLE_SUBSCRIPTIONS_EVENTS */ #endif /* UA_ENABLE_SUBSCRIPTIONS */ UA_BrowsePathResult browseSimplifiedBrowsePath(UA_Server *server, const UA_NodeId origin, size_t browsePathSize, const UA_QualifiedName *browsePath); UA_StatusCode writeObjectProperty(UA_Server *server, const UA_NodeId objectId, const UA_QualifiedName propertyName, const UA_Variant value); UA_StatusCode getNodeContext(UA_Server *server, UA_NodeId nodeId, void **nodeContext); UA_StatusCode setNodeContext(UA_Server *server, UA_NodeId nodeId, void *nodeContext); void removeCallback(UA_Server *server, UA_UInt64 callbackId); UA_StatusCode changeRepeatedCallbackInterval(UA_Server *server, UA_UInt64 callbackId, UA_Double interval_ms); UA_StatusCode addRepeatedCallback(UA_Server *server, UA_ServerCallback callback, void *data, UA_Double interval_ms, UA_UInt64 *callbackId); #ifdef UA_ENABLE_DISCOVERY UA_StatusCode register_server_with_discovery_server(UA_Server *server, void *client, const UA_Boolean isUnregister, const char* semaphoreFilePath); #endif /***********/ /* RefTree */ /***********/ /* A RefTree is a sorted set of NodeIds that ensures we consider each node just * once. It holds a single array for both the ExpandedNodeIds and the entries of * a tree-structure for fast lookup. A single realloc operation (with some * pointer repairing) can be used to increase the capacity of the RefTree. * * When the RefTree is complete, the tree-part at the end of the targets array * can be ignored / cut away to use it as a simple ExpandedNodeId array. * * The layout of the targets array is as follows: * * | Targets [ExpandedNodeId, n times] | Tree [RefEntry, n times] | */ #define UA_REFTREE_INITIAL_SIZE 16 typedef struct RefEntry { ZIP_ENTRY(RefEntry) zipfields; const UA_ExpandedNodeId *target; UA_UInt32 targetHash; /* Hash of the target nodeid */ } RefEntry; ZIP_HEAD(RefHead, RefEntry); typedef struct RefHead RefHead; typedef struct { UA_ExpandedNodeId *targets; RefHead head; size_t capacity; /* available space */ size_t size; /* used space */ } RefTree; UA_StatusCode UA_FUNC_ATTR_WARN_UNUSED_RESULT RefTree_init(RefTree *rt); void RefTree_clear(RefTree *rt); UA_StatusCode UA_FUNC_ATTR_WARN_UNUSED_RESULT RefTree_addNodeId(RefTree *rt, const UA_NodeId *target, UA_Boolean *duplicate); UA_Boolean RefTree_contains(RefTree *rt, const UA_ExpandedNodeId *target); UA_Boolean RefTree_containsNodeId(RefTree *rt, const UA_NodeId *target); /***************************************/ /* 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. * * The reason is set whenever the return value is false */ 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, const char **reason); /* Is the DataType compatible */ UA_Boolean compatibleDataTypes(UA_Server *server, const UA_NodeId *dataType, const UA_NodeId *constraintDataType); /* Set to the target type if compatible */ void adjustValueType(UA_Server *server, UA_Variant *value, const UA_NodeId *targetDataTypeId); /* Is the Value compatible with the DataType? Can perform additional checks * compared to compatibleDataTypes. */ UA_Boolean compatibleValueDataType(UA_Server *server, const UA_DataType *dataType, const UA_NodeId *constraintDataType); 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 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); #ifdef UA_ENABLE_DIAGNOSTICS void createSessionObject(UA_Server *server, UA_Session *session); void createSubscriptionObject(UA_Server *server, UA_Session *session, UA_Subscription *sub); UA_StatusCode readDiagnostics(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); UA_StatusCode readSubscriptionDiagnosticsArray(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); UA_StatusCode readSessionDiagnosticsArray(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); UA_StatusCode readSessionSecurityDiagnostics(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); #endif /***************************/ /* Nodestore Access Macros */ /***************************/ #define UA_NODESTORE_NEW(server, nodeClass) \ server->config.nodestore.newNode(server->config.nodestore.context, nodeClass) #define UA_NODESTORE_DELETE(server, node) \ server->config.nodestore.deleteNode(server->config.nodestore.context, node) #define UA_NODESTORE_GET(server, nodeid) \ server->config.nodestore.getNode(server->config.nodestore.context, nodeid) /* Returns NULL if the target is an external Reference (per the ExpandedNodeId) */ const UA_Node * UA_NODESTORE_GETFROMREF(UA_Server *server, UA_NodePointer target); #define UA_NODESTORE_RELEASE(server, node) \ server->config.nodestore.releaseNode(server->config.nodestore.context, node) #define UA_NODESTORE_GETCOPY(server, nodeid, outnode) \ server->config.nodestore.getNodeCopy(server->config.nodestore.context, \ nodeid, outnode) #define UA_NODESTORE_INSERT(server, node, addedNodeId) \ server->config.nodestore.insertNode(server->config.nodestore.context, \ node, addedNodeId) #define UA_NODESTORE_REPLACE(server, node) \ server->config.nodestore.replaceNode(server->config.nodestore.context, node) #define UA_NODESTORE_REMOVE(server, nodeId) \ server->config.nodestore.removeNode(server->config.nodestore.context, nodeId) #define UA_NODESTORE_GETREFERENCETYPEID(server, index) \ server->config.nodestore.getReferenceTypeId(server->config.nodestore.context, \ index) _UA_END_DECLS /**** amalgamated original file "/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); typedef void (*UA_ChannelService)(UA_Server*, UA_SecureChannel*, 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, const UA_ActivateSessionRequest *request, UA_ActivateSessionResponse *response); /** * CloseSession * ^^^^^^^^^^^^ * Used to terminate a Session. */ void Service_CloseSession(UA_Server *server, UA_SecureChannel *channel, 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. * If the type or one of the supertypes has any HasInterface references * (see OPC 10001-7 - Amendment 7, 4.9.2), the child nodes of the interfaces * are added to the new object. */ 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); # if UA_MULTITHREADING >= 100 void Service_CallAsync(UA_Server *server, UA_Session *session, UA_UInt32 requestId, const UA_CallRequest *request, UA_CallResponse *response, UA_Boolean *finished); #endif #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. */ void Service_SetTriggering(UA_Server *server, UA_Session *session, const UA_SetTriggeringRequest *request, UA_SetTriggeringResponse *response); /** * 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. * * Also, this is the only service method that returns a StatusCode. This * simplifies keeping track of the diagnostics statistics. */ UA_StatusCode 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. */ void Service_TransferSubscriptions(UA_Server *server, UA_Session *session, const UA_TransferSubscriptionsRequest *request, UA_TransferSubscriptionsResponse *response); #endif /* UA_ENABLE_SUBSCRIPTIONS */ _UA_END_DECLS /**** amalgamated original file "/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_Client_MonitoredItem) monitoredItems; } UA_Client_Subscription; void UA_Client_Subscriptions_clean(UA_Client *client); /* Exposed for fuzzing */ UA_StatusCode UA_Client_preparePublishRequest(UA_Client *client, UA_PublishRequest *request); void UA_Client_Subscriptions_backgroundPublish(UA_Client *client); void UA_Client_Subscriptions_backgroundPublishInactivityCheck(UA_Client *client); #endif /* UA_ENABLE_SUBSCRIPTIONS */ /**********/ /* 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 { UA_UInt32 callbackId; UA_ClientAsyncServiceCallback userCallback; void *userData; void *clientData; } CustomCallback; struct UA_Client { UA_ClientConfig config; UA_Timer timer; /* Overall connection status */ UA_StatusCode connectStatus; /* Old status to notify only changes */ UA_SecureChannelState oldChannelState; UA_SessionState oldSessionState; UA_StatusCode oldConnectStatus; UA_Boolean findServersHandshake; /* Ongoing FindServers */ UA_Boolean endpointsHandshake; /* Ongoing GetEndpoints */ UA_Boolean noSession; /* Don't open a session */ /* Connection */ UA_Connection connection; UA_String endpointUrl; /* Used to extract address and port */ UA_String discoveryUrl; /* The discoveryUrl (also used to signal which application we want to connect to in the HEL/ACK handshake). */ /* SecureChannel */ UA_SecureChannel channel; UA_UInt32 requestId; UA_DateTime nextChannelRenewal; /* Session */ UA_SessionState sessionState; UA_NodeId authenticationToken; UA_UInt32 requestHandle; UA_ByteString remoteNonce; UA_ByteString localNonce; /* Connectivity check */ UA_DateTime lastConnectivityCheck; UA_Boolean pendingConnectivityCheck; /* Async Service */ LIST_HEAD(, AsyncServiceCall) asyncServiceCalls; /* Subscriptions */ #ifdef UA_ENABLE_SUBSCRIPTIONS LIST_HEAD(, UA_Client_NotificationsAckNumber) pendingNotificationsAcks; LIST_HEAD(, UA_Client_Subscription) subscriptions; UA_UInt32 monitoredItemHandles; UA_UInt16 currentlyOutStandingPublishRequests; #endif }; UA_StatusCode connectSync(UA_Client *client); /* Only exposed for unit tests */ void notifyClientState(UA_Client *client); void processERRResponse(UA_Client *client, const UA_ByteString *chunk); void processACKResponse(UA_Client *client, const UA_ByteString *chunk); void processOPNResponse(UA_Client *client, const UA_ByteString *message); void closeSecureChannel(UA_Client *client); UA_StatusCode connectIterate(UA_Client *client, UA_UInt32 timeout); UA_StatusCode receiveResponseAsync(UA_Client *client, UA_UInt32 timeout); void Client_warnEndpointsResult(UA_Client *client, const UA_GetEndpointsResponse *response, const UA_String *endpointUrl); _UA_END_DECLS /**** amalgamated original file "/src/pubsub/ua_pubsub_config.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) 2020 Yannick Wallerer, Siemens AG * Copyright (c) 2020 Thomas Fischer, Siemens AG */ #ifdef UA_ENABLE_PUBSUB_FILE_CONFIG /* Decodes the information from the ByteString. If the decoded content is a * PubSubConfiguration in a UABinaryFileDataType-object. It will overwrite the * current PubSub configuration from the server. */ UA_StatusCode UA_PubSubManager_loadPubSubConfigFromByteString(UA_Server *server, const UA_ByteString buffer); /* Saves the current PubSub configuration of a server in a byteString. */ UA_StatusCode UA_PubSubManager_getEncodedPubSubConfiguration(UA_Server *server, UA_ByteString *buffer); #endif /* UA_ENABLE_PUBSUB_FILE_CONFIG */ /**** amalgamated original file "/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 "/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 2020 (c) Fraunhofer IOSB (Author: Andreas Ebner) * 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 */ #define UA_MAX_ARRAY_DIMS 100 /* Max dimensions of an array */ /* 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}; const UA_ByteString UA_BYTESTRING_NULL = {0}; const UA_Guid UA_GUID_NULL = {0}; const UA_NodeId UA_NODEID_NULL = {0}; const UA_ExpandedNodeId UA_EXPANDEDNODEID_NULL = {0}; typedef UA_StatusCode (*UA_copySignature)(const void *src, void *dst, const UA_DataType *type); extern const UA_copySignature copyJumpTable[UA_DATATYPEKINDS]; typedef void (*UA_clearSignature)(void *p, const UA_DataType *type); extern const UA_clearSignature clearJumpTable[UA_DATATYPEKINDS]; typedef UA_Order (*UA_orderSignature)(const void *p1, const void *p2, const UA_DataType *type); extern const UA_orderSignature orderJumpTable[UA_DATATYPEKINDS]; const UA_DataType * UA_findDataTypeWithCustom(const UA_NodeId *typeId, const UA_DataTypeArray *customTypes) { /* Always look in built-in types first (may contain data types from all * namespaces). * * TODO: The standard-defined types are ordered. See if binary search is * more efficient. */ for(size_t i = 0; i < UA_TYPES_COUNT; ++i) { if(UA_NodeId_equal(&UA_TYPES[i].typeId, typeId)) return &UA_TYPES[i]; } /* Search in the customTypes */ while(customTypes) { for(size_t i = 0; i < customTypes->typesSize; ++i) { if(UA_NodeId_equal(&customTypes->types[i].typeId, typeId)) return &customTypes->types[i]; } customTypes = customTypes->next; } return NULL; } const UA_DataType * UA_findDataType(const UA_NodeId *typeId) { return UA_findDataTypeWithCustom(typeId, 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(UA_UNLIKELY(!s.data)) { s.length = 0; return s; } memcpy(s.data, src, s.length); } else { s.data = (u8*)UA_EMPTY_ARRAY_SENTINEL; } return s; } static UA_Order stringOrder(const UA_String *p1, const UA_String *p2, const UA_DataType *type); static UA_Order guidOrder(const UA_Guid *p1, const UA_Guid *p2, const UA_DataType *type); static UA_Order qualifiedNameOrder(const UA_QualifiedName *p1, const UA_QualifiedName *p2, const UA_DataType *type); UA_Boolean UA_String_equal(const UA_String *s1, const UA_String *s2) { return (stringOrder(s1, s2, NULL) == UA_ORDER_EQ); } /* Do not expose UA_String_equal_ignorecase to public API as it currently only handles * ASCII strings, and not UTF8! */ UA_Boolean UA_String_equal_ignorecase(const UA_String *s1, const UA_String *s2) { if(s1->length != s2->length) return false; if(s1->length == 0) return true; if(s2->data == NULL) return false; //FIXME this currently does not handle UTF8 return UA_strncasecmp((const char*)s1->data, (const char*)s2->data, s1->length) == 0; } static UA_StatusCode String_copy(UA_String const *src, UA_String *dst, const UA_DataType *_) { UA_StatusCode res = UA_Array_copy(src->data, src->length, (void**)&dst->data, &UA_TYPES[UA_TYPES_BYTE]); if(res == UA_STATUSCODE_GOOD) dst->length = src->length; return res; } 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); } u32 UA_QualifiedName_hash(const UA_QualifiedName *q) { return UA_ByteString_hash(q->namespaceIndex, q->name.data, q->name.length); } UA_Boolean UA_QualifiedName_equal(const UA_QualifiedName *qn1, const UA_QualifiedName *qn2) { return (qualifiedNameOrder(qn1, qn2, NULL) == UA_ORDER_EQ); } /* DateTime */ UA_DateTimeStruct UA_DateTime_toStruct(UA_DateTime t) { /* Divide, then subtract -> avoid underflow. Also, negative numbers are * rounded up, not down. */ long long secSinceUnixEpoch = (long long)(t / UA_DATETIME_SEC) - (long long)(UA_DATETIME_UNIX_EPOCH / UA_DATETIME_SEC); /* Negative fractions of a second? Remove one full second from the epoch * distance and allow only a positive fraction. */ UA_DateTime frac = t % UA_DATETIME_SEC; if(frac < 0) { secSinceUnixEpoch--; frac += UA_DATETIME_SEC; } struct mytm ts; memset(&ts, 0, sizeof(struct mytm)); __secs_to_tm(secSinceUnixEpoch, &ts); UA_DateTimeStruct dateTimeStruct; dateTimeStruct.year = (i16)(ts.tm_year + 1900); dateTimeStruct.month = (u16)(ts.tm_mon + 1); dateTimeStruct.day = (u16)ts.tm_mday; dateTimeStruct.hour = (u16)ts.tm_hour; dateTimeStruct.min = (u16)ts.tm_min; dateTimeStruct.sec = (u16)ts.tm_sec; dateTimeStruct.milliSec = (u16)((frac % 10000000) / 10000); dateTimeStruct.microSec = (u16)((frac % 10000) / 10); dateTimeStruct.nanoSec = (u16)((frac % 10) * 100); return dateTimeStruct; } UA_DateTime UA_DateTime_fromStruct(UA_DateTimeStruct ts) { /* Seconds since the Unix epoch */ struct mytm tm; memset(&tm, 0, sizeof(struct mytm)); tm.tm_year = ts.year - 1900; tm.tm_mon = ts.month - 1; tm.tm_mday = ts.day; tm.tm_hour = ts.hour; tm.tm_min = ts.min; tm.tm_sec = ts.sec; long long sec_epoch = __tm_to_secs(&tm); UA_DateTime t = UA_DATETIME_UNIX_EPOCH; t += sec_epoch * UA_DATETIME_SEC; t += ts.milliSec * UA_DATETIME_MSEC; t += ts.microSec * UA_DATETIME_USEC; t += ts.nanoSec / 100; return t; } /* Guid */ UA_Boolean UA_Guid_equal(const UA_Guid *g1, const UA_Guid *g2) { return (guidOrder(g1, g2, NULL) == UA_ORDER_EQ); } 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(UA_UNLIKELY(!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: case UA_NODEIDTYPE_BYTESTRING: retval |= String_copy(&src->identifier.string, &dst->identifier.string, NULL); break; case UA_NODEIDTYPE_GUID: dst->identifier.guid = src->identifier.guid; 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: case UA_NODEIDTYPE_BYTESTRING: return (p->identifier.string.length == 0); /* Null and empty string */ case UA_NODEIDTYPE_GUID: return UA_Guid_equal(&p->identifier.guid, &UA_GUID_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 (n1->namespaceIndex < n2->namespaceIndex) ? UA_ORDER_LESS : UA_ORDER_MORE; /* Compare identifierType */ if(n1->identifierType != n2->identifierType) return (n1->identifierType < n2->identifierType) ? UA_ORDER_LESS : UA_ORDER_MORE; /* Compare the identifier */ switch(n1->identifierType) { case UA_NODEIDTYPE_NUMERIC: default: if(n1->identifier.numeric != n2->identifier.numeric) return (n1->identifier.numeric < n2->identifier.numeric) ? UA_ORDER_LESS : UA_ORDER_MORE; return UA_ORDER_EQ; case UA_NODEIDTYPE_GUID: return guidOrder(&n1->identifier.guid, &n2->identifier.guid, NULL); case UA_NODEIDTYPE_STRING: case UA_NODEIDTYPE_BYTESTRING: return stringOrder(&n1->identifier.string, &n2->identifier.string, NULL); } } /* sdbm-hash (http://www.cse.yorku.ca/~oz/hash.html) */ u32 UA_ByteString_hash(u32 initialHashValue, const u8 *data, size_t size) { u32 h = initialHashValue; for(size_t i = 0; i < size; i++) h = data[i] + (h << 6) + (h << 16) - h; return h; } u32 UA_NodeId_hash(const UA_NodeId *n) { switch(n->identifierType) { case UA_NODEIDTYPE_NUMERIC: default: return UA_ByteString_hash(n->namespaceIndex, (const u8*)&n->identifier.numeric, sizeof(UA_UInt32)); case UA_NODEIDTYPE_STRING: case UA_NODEIDTYPE_BYTESTRING: return UA_ByteString_hash(n->namespaceIndex, n->identifier.string.data, n->identifier.string.length); case UA_NODEIDTYPE_GUID: return UA_ByteString_hash(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 |= String_copy(&src->namespaceUri, &dst->namespaceUri, NULL); dst->serverIndex = src->serverIndex; return retval; } UA_Boolean UA_ExpandedNodeId_isLocal(const UA_ExpandedNodeId *n) { return (n->namespaceUri.length == 0 && n->serverIndex == 0); } UA_Order UA_ExpandedNodeId_order(const UA_ExpandedNodeId *n1, const UA_ExpandedNodeId *n2) { if(n1->serverIndex != n2->serverIndex) return (n1->serverIndex < n2->serverIndex) ? UA_ORDER_LESS : UA_ORDER_MORE; UA_Order o = stringOrder(&n1->namespaceUri, &n2->namespaceUri, NULL); if(o != UA_ORDER_EQ) return o; return UA_NodeId_order(&n1->nodeId, &n2->nodeId); } u32 UA_ExpandedNodeId_hash(const UA_ExpandedNodeId *n) { u32 h = UA_NodeId_hash(&n->nodeId); if(n->serverIndex != 0) h = UA_ByteString_hash(h, (const UA_Byte*)&n->serverIndex, 4); if(n->namespaceUri.length != 0) h = UA_ByteString_hash(h, n->namespaceUri.data, n->namespaceUri.length); return h; } /* 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); /* ByteString -> copy as string */ retval |= String_copy(&src->content.encoded.body, &dst->content.encoded.body, NULL); 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; } void UA_ExtensionObject_setValue(UA_ExtensionObject *eo, void * UA_RESTRICT p, const UA_DataType *type) { UA_ExtensionObject_init(eo); eo->content.decoded.data = p; eo->content.decoded.type = type; eo->encoding = UA_EXTENSIONOBJECT_DECODED; } void UA_ExtensionObject_setValueNoDelete(UA_ExtensionObject *eo, void * UA_RESTRICT p, const UA_DataType *type) { UA_ExtensionObject_init(eo); eo->content.decoded.data = p; eo->content.decoded.type = type; eo->encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE; } UA_StatusCode UA_ExtensionObject_setValueCopy(UA_ExtensionObject *eo, void * UA_RESTRICT p, const UA_DataType *type) { UA_ExtensionObject_init(eo); /* Make a copy of the value */ void *val = UA_malloc(type->memSize); if(UA_UNLIKELY(!val)) return UA_STATUSCODE_BADOUTOFMEMORY; UA_StatusCode res = UA_copy(p, val, type); if(UA_UNLIKELY(res != UA_STATUSCODE_GOOD)) { UA_free(val); return res; } /* Set the ExtensionObject */ eo->content.decoded.data = val; eo->content.decoded.type = type; eo->encoding = UA_EXTENSIONOBJECT_DECODED; return UA_STATUSCODE_GOOD; } /* Variant */ static void Variant_clear(UA_Variant *p, const UA_DataType *_) { /* The content is "borrowed" */ if(p->storageType == UA_VARIANT_DATA_NODELETE) return; /* Delete the value */ 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; } /* Delete the array dimensions */ 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 * UA_RESTRICT p, const UA_DataType *type) { void *n = UA_malloc(type->memSize); if(UA_UNLIKELY(!n)) return UA_STATUSCODE_BADOUTOFMEMORY; UA_StatusCode retval = UA_copy(p, n, type); if(UA_UNLIKELY(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 * UA_RESTRICT 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. This may adjust the upper bound * (max) in order to fit the variant. */ static UA_StatusCode checkAdjustRange(const UA_Variant *v, UA_NumericRange *range) { /* Test for max array size (64bit only) */ #if (SIZE_MAX > 0xffffffff) if(v->arrayLength > UA_UINT32_MAX) return UA_STATUSCODE_BADINTERNALERROR; #endif u32 arrayLength = (u32)v->arrayLength; /* Assume one array dimension if none defined */ const u32 *dims = v->arrayDimensions; size_t dims_count = v->arrayDimensionsSize; if(v->arrayDimensionsSize == 0) { dims_count = 1; dims = &arrayLength; } /* Does the range match the dimension of the variant? */ if(range->dimensionsSize != dims_count) return UA_STATUSCODE_BADINDEXRANGENODATA; /* Check that the number of elements in the variant matches the array * dimensions */ size_t elements = 1; for(size_t i = 0; i < dims_count; ++i) elements *= dims[i]; if(elements != v->arrayLength) return UA_STATUSCODE_BADINTERNALERROR; /* 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. */ 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; /* Reduce the max to fit the variant */ if(range->dimensions[i].max >= dims[i]) range->dimensions[i].max = dims[i] - 1; } return UA_STATUSCODE_GOOD; } /* Computes the stride for copying the range elements. * - 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 void computeStrides(const UA_Variant *v, const UA_NumericRange range, size_t *total, size_t *block, size_t *stride, size_t *first) { /* Number of total elements to be copied */ size_t count = 1; for(size_t i = 0; i < range.dimensionsSize; ++i) count *= (range.dimensions[i].max - range.dimensions[i].min) + 1; *total = count; /* Assume one array dimension if none defined */ u32 arrayLength = (u32)v->arrayLength; const u32 *dims = v->arrayDimensions; size_t dims_count = v->arrayDimensionsSize; if(v->arrayDimensionsSize == 0) { dims_count = 1; dims = &arrayLength; } /* 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 + range.dimensions[k].max - 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]; } } /* 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 * UA_RESTRICT 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); /* Upper bound of the dimensions for stack-allocation */ if(range.dimensionsSize > UA_MAX_ARRAY_DIMS) return UA_STATUSCODE_BADINTERNALERROR; /* Copy the const range to a mutable stack location */ UA_NumericRangeDimension thisrangedims[UA_MAX_ARRAY_DIMS]; memcpy(thisrangedims, range.dimensions, sizeof(UA_NumericRangeDimension) * range.dimensionsSize); UA_NumericRange thisrange = {range.dimensionsSize, thisrangedims}; UA_NumericRangeDimension scalarThisDimension = {0,0}; /* a single entry */ UA_NumericRange nextrange = {0, NULL}; /* 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_Variant arraySrc; 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.dimensionsSize = dims; nextrange.dimensions = &range.dimensions[dims]; nextrange.dimensionsSize = range.dimensionsSize - dims; } UA_StatusCode retval = checkAdjustRange(src, &thisrange); if(retval != UA_STATUSCODE_GOOD) return retval; /* Compute the strides */ size_t count, block, stride, first; computeStrides(src, thisrange, &count, &block, &stride, &first); /* 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) { if(!v->type) return UA_STATUSCODE_BADINVALIDARGUMENT; /* Upper bound of the dimensions for stack-allocation */ if(range.dimensionsSize > UA_MAX_ARRAY_DIMS) return UA_STATUSCODE_BADINTERNALERROR; /* Copy the const range to a mutable stack location */ UA_NumericRangeDimension thisrangedims[UA_MAX_ARRAY_DIMS]; memcpy(thisrangedims, range.dimensions, sizeof(UA_NumericRangeDimension) * range.dimensionsSize); UA_NumericRange thisrange = {range.dimensionsSize, thisrangedims}; UA_StatusCode retval = checkAdjustRange(v, &thisrange); if(retval != UA_STATUSCODE_GOOD) return retval; /* Compute the strides */ size_t count, block, stride, first; computeStrides(v, range, &count, &block, &stride, &first); 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 * UA_RESTRICT 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 = String_copy(&src->locale, &dst->locale, NULL); retval |= String_copy(&src->text, &dst->text, NULL); 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; } UA_StatusCode UA_DataValue_copyVariantRange(const UA_DataValue *src, UA_DataValue * UA_RESTRICT dst, const UA_NumericRange range) { memcpy(dst, src, sizeof(UA_DataValue)); UA_Variant_init(&dst->value); UA_StatusCode retval = UA_Variant_copyRange(&src->value, &dst->value, range); 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 = String_copy(&src->additionalInfo, &dst->additionalInfo, NULL); if(src->hasInnerDiagnosticInfo && src->innerDiagnosticInfo) { dst->innerDiagnosticInfo = (UA_DiagnosticInfo*) UA_malloc(sizeof(UA_DiagnosticInfo)); if(UA_LIKELY(dst->innerDiagnosticInfo != NULL)) { 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; for(size_t i = 0; i < type->membersSize; ++i) { const UA_DataTypeMember *m = &type->members[i]; const UA_DataType *mt = m->memberType; ptrs += m->padding; ptrd += m->padding; if(!m->isOptional) { if(!m->isArray) { retval |= copyJumpTable[mt->typeKind]((const void *)ptrs, (void *)ptrd, mt); ptrs += mt->memSize; ptrd += mt->memSize; } else { 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*); } } else { if(!m->isArray) { if(*(void* const*)ptrs != NULL) retval |= UA_Array_copy(*(void* const*)ptrs, 1, (void**)ptrd, mt); } else { if(*(void* const*)(ptrs+sizeof(size_t)) != NULL) { 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; } else { ptrs += sizeof(size_t); ptrd += sizeof(size_t); } } ptrs += sizeof(void*); ptrd += sizeof(void*); } } return retval; } static UA_StatusCode copyUnion(const void *src, void *dst, const UA_DataType *type) { uintptr_t ptrs = (uintptr_t) src; uintptr_t ptrd = (uintptr_t) dst; UA_UInt32 selection = *(UA_UInt32 *)ptrs; UA_copy((const UA_UInt32 *) ptrs, (UA_UInt32 *) ptrd, &UA_TYPES[UA_TYPES_UINT32]); if(selection == 0) return UA_STATUSCODE_GOOD; const UA_DataTypeMember *m = &type->members[selection-1]; const UA_DataType *mt = m->memberType; ptrs += m->padding; ptrd += m->padding; UA_StatusCode retval = UA_STATUSCODE_GOOD; if (m->isArray) { 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; } else { retval = copyJumpTable[mt->typeKind]((const void *)ptrs, (void *)ptrd, mt); } 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)copyStructure, /* Structure with Optional Fields */ (UA_copySignature)copyUnion, /* 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; for(size_t i = 0; i < type->membersSize; ++i) { const UA_DataTypeMember *m = &type->members[i]; const UA_DataType *mt = m->memberType; ptr += m->padding; if(!m->isOptional) { if(!m->isArray) { clearJumpTable[mt->typeKind]((void*)ptr, mt); ptr += mt->memSize; } else { size_t length = *(size_t*)ptr; ptr += sizeof(size_t); UA_Array_delete(*(void**)ptr, length, mt); ptr += sizeof(void*); } } else { /* field is optional */ if(!m->isArray) { /* optional scalar field is contained */ if((*(void *const *)ptr != NULL)) UA_Array_delete(*(void **)ptr, 1, mt); ptr += sizeof(void *); } else { /* optional array field is contained */ if((*(void *const *)(ptr + sizeof(size_t)) != NULL)) { size_t length = *(size_t *)ptr; ptr += sizeof(size_t); UA_Array_delete(*(void **)ptr, length, mt); ptr += sizeof(void *); } else { /* optional array field not contained */ ptr += sizeof(size_t); ptr += sizeof(void *); } } } } } static void clearUnion(void *p, const UA_DataType *type) { uintptr_t ptr = (uintptr_t) p; UA_UInt32 selection = *(UA_UInt32 *)ptr; if(selection == 0) return; const UA_DataTypeMember *m = &type->members[selection-1]; const UA_DataType *mt = m->memberType; ptr += m->padding; if (m->isArray) { size_t length = *(size_t *)ptr; ptr += sizeof(size_t); UA_Array_delete(*(void **)ptr, length, mt); } else { UA_clear((void *) ptr, mt); } } 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)clearStructure, /* Struct with Optional Fields*/ (UA_clearSignature)clearUnion, /* Union*/ (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); } /******************/ /* Value Ordering */ /******************/ #define UA_NUMERICORDER(NAME, TYPE) \ static UA_Order \ NAME(const TYPE *p1, const TYPE *p2, const UA_DataType *type) { \ if(*p1 != *p2) \ return (*p1 < *p2) ? UA_ORDER_LESS : UA_ORDER_MORE; \ return UA_ORDER_EQ; \ } UA_NUMERICORDER(booleanOrder, UA_Boolean) UA_NUMERICORDER(sByteOrder, UA_SByte) UA_NUMERICORDER(byteOrder, UA_Byte) UA_NUMERICORDER(int16Order, UA_Int16) UA_NUMERICORDER(uInt16Order, UA_UInt16) UA_NUMERICORDER(int32Order, UA_Int32) UA_NUMERICORDER(uInt32Order, UA_UInt32) UA_NUMERICORDER(int64Order, UA_Int64) UA_NUMERICORDER(uInt64Order, UA_UInt64) #define UA_FLOATORDER(NAME, TYPE) \ static UA_Order \ NAME(const TYPE *p1, const TYPE *p2, const UA_DataType *type) { \ if(*p1 != *p2) { \ /* p1 is NaN */ \ if(*p1 != *p1) { \ if(*p2 != *p2) \ return UA_ORDER_EQ; \ return UA_ORDER_LESS; \ } \ /* p2 is NaN */ \ if(*p2 != *p2) \ return UA_ORDER_MORE; \ return (*p1 < *p2) ? UA_ORDER_LESS : UA_ORDER_MORE; \ } \ return UA_ORDER_EQ; \ } UA_FLOATORDER(floatOrder, UA_Float) UA_FLOATORDER(doubleOrder, UA_Double) static UA_Order guidOrder(const UA_Guid *p1, const UA_Guid *p2, const UA_DataType *type) { if(p1->data1 != p2->data1) return (p1->data1 < p2->data1) ? UA_ORDER_LESS : UA_ORDER_MORE; if(p1->data2 != p2->data2) return (p1->data2 < p2->data2) ? UA_ORDER_LESS : UA_ORDER_MORE; if(p1->data3 != p2->data3) return (p1->data3 < p2->data3) ? UA_ORDER_LESS : UA_ORDER_MORE; int cmp = memcmp(p1->data4, p2->data4, 8); if(cmp != 0) return (cmp < 0) ? UA_ORDER_LESS : UA_ORDER_MORE; return UA_ORDER_EQ; } static UA_Order stringOrder(const UA_String *p1, const UA_String *p2, const UA_DataType *type) { if(p1->length != p2->length) return (p1->length < p2->length) ? UA_ORDER_LESS : UA_ORDER_MORE; /* For zero-length arrays, every pointer not NULL is considered a * UA_EMPTY_ARRAY_SENTINEL. */ if(p1->data == p2->data) return UA_ORDER_EQ; if(p1->data == NULL) return UA_ORDER_LESS; if(p2->data == NULL) return UA_ORDER_MORE; int cmp = memcmp((const char*)p1->data, (const char*)p2->data, p1->length); if(cmp != 0) return (cmp < 0) ? UA_ORDER_LESS : UA_ORDER_MORE; return UA_ORDER_EQ; } static UA_Order nodeIdOrder(const UA_NodeId *p1, const UA_NodeId *p2, const UA_DataType *type) { return UA_NodeId_order(p1, p2); } static UA_Order expandedNodeIdOrder(const UA_ExpandedNodeId *p1, const UA_ExpandedNodeId *p2, const UA_DataType *type) { return UA_ExpandedNodeId_order(p1, p2); } static UA_Order qualifiedNameOrder(const UA_QualifiedName *p1, const UA_QualifiedName *p2, const UA_DataType *type) { if(p1->namespaceIndex != p2->namespaceIndex) return (p1->namespaceIndex < p2->namespaceIndex) ? UA_ORDER_LESS : UA_ORDER_MORE; return stringOrder(&p1->name, &p2->name, NULL); } static UA_Order localizedTextOrder(const UA_LocalizedText *p1, const UA_LocalizedText *p2, const UA_DataType *type) { UA_Order o = stringOrder(&p1->locale, &p2->locale, NULL); if(o != UA_ORDER_EQ) return o; return stringOrder(&p1->text, &p2->text, NULL); } static UA_Order extensionObjectOrder(const UA_ExtensionObject *p1, const UA_ExtensionObject *p2, const UA_DataType *type) { UA_ExtensionObjectEncoding enc1 = p1->encoding; UA_ExtensionObjectEncoding enc2 = p2->encoding; if(enc1 > UA_EXTENSIONOBJECT_DECODED) enc1 = UA_EXTENSIONOBJECT_DECODED; if(enc2 > UA_EXTENSIONOBJECT_DECODED) enc2 = UA_EXTENSIONOBJECT_DECODED; if(enc1 != enc2) return (enc1 < enc2) ? UA_ORDER_LESS : UA_ORDER_MORE; switch(enc1) { case UA_EXTENSIONOBJECT_ENCODED_NOBODY: return UA_ORDER_EQ; case UA_EXTENSIONOBJECT_ENCODED_BYTESTRING: case UA_EXTENSIONOBJECT_ENCODED_XML: { UA_Order o = UA_NodeId_order(&p1->content.encoded.typeId, &p2->content.encoded.typeId); if(o == UA_ORDER_EQ) o = stringOrder((const UA_String*)&p1->content.encoded.body, (const UA_String*)&p2->content.encoded.body, NULL); return o; } case UA_EXTENSIONOBJECT_DECODED: default: { const UA_DataType *type1 = p1->content.decoded.type; const UA_DataType *type2 = p1->content.decoded.type; if(type1 != type2) return ((uintptr_t)type1 < (uintptr_t)type2) ? UA_ORDER_LESS : UA_ORDER_MORE; if(!type1) return UA_ORDER_EQ; return orderJumpTable[type1->typeKind] (p1->content.decoded.data, p2->content.decoded.data, type1); } } } static UA_Order arrayOrder(const void *p1, size_t p1Length, const void *p2, size_t p2Length, const UA_DataType *type) { if(p1Length != p2Length) return (p1Length < p2Length) ? UA_ORDER_LESS : UA_ORDER_MORE; /* For zero-length arrays, every pointer not NULL is considered a * UA_EMPTY_ARRAY_SENTINEL. */ if(p1 == p2) return UA_ORDER_EQ; if(p1 == NULL) return UA_ORDER_LESS; if(p2 == NULL) return UA_ORDER_MORE; uintptr_t u1 = (uintptr_t)p1; uintptr_t u2 = (uintptr_t)p2; for(size_t i = 0; i < p1Length; i++) { UA_Order o = orderJumpTable[type->typeKind]((const void*)u1, (const void*)u2, type); if(o != UA_ORDER_EQ) return o; u1 += type->memSize; u2 += type->memSize; } return UA_ORDER_EQ; } static UA_Order variantOrder(const UA_Variant *p1, const UA_Variant *p2, const UA_DataType *type) { if(p1->type != p2->type) return ((uintptr_t)p1->type < (uintptr_t)p2->type) ? UA_ORDER_LESS : UA_ORDER_MORE; UA_Order o; if(p1->type != NULL) { /* Check if both variants are scalars or arrays */ UA_Boolean s1 = UA_Variant_isScalar(p1); UA_Boolean s2 = UA_Variant_isScalar(p2); if(s1 != s2) return s1 ? UA_ORDER_LESS : UA_ORDER_MORE; if(s1) { o = orderJumpTable[p1->type->typeKind](p1->data, p2->data, p1->type); } else { /* Mismatching array length? */ if(p1->arrayLength != p2->arrayLength) return (p1->arrayLength < p2->arrayLength) ? UA_ORDER_LESS : UA_ORDER_MORE; o = arrayOrder(p1->data, p1->arrayLength, p2->data, p2->arrayLength, p1->type); } if(o != UA_ORDER_EQ) return o; } if(p1->arrayDimensionsSize != p2->arrayDimensionsSize) return (p1->arrayDimensionsSize < p2->arrayDimensionsSize) ? UA_ORDER_LESS : UA_ORDER_MORE; o = UA_ORDER_EQ; if(p1->arrayDimensionsSize > 0) o = arrayOrder(p1->arrayDimensions, p1->arrayDimensionsSize, p2->arrayDimensions, p2->arrayDimensionsSize, &UA_TYPES[UA_TYPES_UINT32]); return o; } static UA_Order dataValueOrder(const UA_DataValue *p1, const UA_DataValue *p2, const UA_DataType *type) { /* Value */ if(p1->hasValue != p2->hasValue) return (!p1->hasValue) ? UA_ORDER_LESS : UA_ORDER_MORE; if(p1->hasValue) { UA_Order o = variantOrder(&p1->value, &p2->value, NULL); if(o != UA_ORDER_EQ) return o; } /* Status */ if(p1->hasStatus != p2->hasStatus) return (!p1->hasStatus) ? UA_ORDER_LESS : UA_ORDER_MORE; if(p1->hasStatus && p1->status != p2->status) return (p1->status < p2->status) ? UA_ORDER_LESS : UA_ORDER_MORE; /* SourceTimestamp */ if(p1->hasSourceTimestamp != p2->hasSourceTimestamp) return (!p1->hasSourceTimestamp) ? UA_ORDER_LESS : UA_ORDER_MORE; if(p1->hasSourceTimestamp && p1->sourceTimestamp != p2->sourceTimestamp) return (p1->sourceTimestamp < p2->sourceTimestamp) ? UA_ORDER_LESS : UA_ORDER_MORE; /* ServerTimestamp */ if(p1->hasServerTimestamp != p2->hasServerTimestamp) return (!p1->hasServerTimestamp) ? UA_ORDER_LESS : UA_ORDER_MORE; if(p1->hasServerTimestamp && p1->serverTimestamp != p2->serverTimestamp) return (p1->serverTimestamp < p2->serverTimestamp) ? UA_ORDER_LESS : UA_ORDER_MORE; /* SourcePicoseconds */ if(p1->hasSourcePicoseconds != p2->hasSourcePicoseconds) return (!p1->hasSourcePicoseconds) ? UA_ORDER_LESS : UA_ORDER_MORE; if(p1->hasSourcePicoseconds && p1->sourcePicoseconds != p2->sourcePicoseconds) return (p1->sourcePicoseconds < p2->sourcePicoseconds) ? UA_ORDER_LESS : UA_ORDER_MORE; /* ServerPicoseconds */ if(p1->hasServerPicoseconds != p2->hasServerPicoseconds) return (!p1->hasServerPicoseconds) ? UA_ORDER_LESS : UA_ORDER_MORE; if(p1->hasServerPicoseconds && p1->serverPicoseconds != p2->serverPicoseconds) return (p1->serverPicoseconds < p2->serverPicoseconds) ? UA_ORDER_LESS : UA_ORDER_MORE; return UA_ORDER_EQ; } static UA_Order diagnosticInfoOrder(const UA_DiagnosticInfo *p1, const UA_DiagnosticInfo *p2, const UA_DataType *type) { /* SymbolicId */ if(p1->hasSymbolicId != p2->hasSymbolicId) return (!p1->hasSymbolicId) ? UA_ORDER_LESS : UA_ORDER_MORE; if(p1->hasSymbolicId && p1->symbolicId != p2->symbolicId) return (p1->symbolicId < p2->symbolicId) ? UA_ORDER_LESS : UA_ORDER_MORE; /* NamespaceUri */ if(p1->hasNamespaceUri != p2->hasNamespaceUri) return (!p1->hasNamespaceUri) ? UA_ORDER_LESS : UA_ORDER_MORE; if(p1->hasNamespaceUri && p1->namespaceUri != p2->namespaceUri) return (p1->namespaceUri < p2->namespaceUri) ? UA_ORDER_LESS : UA_ORDER_MORE; /* LocalizedText */ if(p1->hasLocalizedText != p2->hasLocalizedText) return (!p1->hasLocalizedText) ? UA_ORDER_LESS : UA_ORDER_MORE; if(p1->hasLocalizedText && p1->localizedText != p2->localizedText) return (p1->localizedText < p2->localizedText) ? UA_ORDER_LESS : UA_ORDER_MORE; /* Locale */ if(p1->hasLocale != p2->hasLocale) return (!p1->hasLocale) ? UA_ORDER_LESS : UA_ORDER_MORE; if(p1->hasLocale && p1->locale != p2->locale) return (p1->locale < p2->locale) ? UA_ORDER_LESS : UA_ORDER_MORE; /* AdditionalInfo */ if(p1->hasAdditionalInfo != p2->hasAdditionalInfo) return (!p1->hasAdditionalInfo) ? UA_ORDER_LESS : UA_ORDER_MORE; if(p1->hasAdditionalInfo) { UA_Order o = stringOrder(&p1->additionalInfo, &p2->additionalInfo, NULL); if(o != UA_ORDER_EQ) return o; } /* InnerStatusCode */ if(p1->hasInnerStatusCode != p2->hasInnerStatusCode) return (!p1->hasInnerStatusCode) ? UA_ORDER_LESS : UA_ORDER_MORE; if(p1->hasInnerStatusCode && p1->innerStatusCode != p2->innerStatusCode) return (p1->innerStatusCode < p2->innerStatusCode) ? UA_ORDER_LESS : UA_ORDER_MORE; /* InnerDiagnosticInfo */ if(p1->hasInnerDiagnosticInfo != p2->hasInnerDiagnosticInfo) return (!p1->hasInnerDiagnosticInfo) ? UA_ORDER_LESS : UA_ORDER_MORE; if(p1->innerDiagnosticInfo == p2->innerDiagnosticInfo) return UA_ORDER_EQ; if(!p1->innerDiagnosticInfo || !p2->innerDiagnosticInfo) return (!p1->innerDiagnosticInfo) ? UA_ORDER_LESS : UA_ORDER_MORE; return diagnosticInfoOrder(p1->innerDiagnosticInfo, p2->innerDiagnosticInfo, NULL); } static UA_Order structureOrder(const void *p1, const void *p2, const UA_DataType *type) { uintptr_t u1 = (uintptr_t)p1; uintptr_t u2 = (uintptr_t)p2; UA_Order o = UA_ORDER_EQ; for(size_t i = 0; i < type->membersSize; ++i) { const UA_DataTypeMember *m = &type->members[i]; const UA_DataType *mt = m->memberType; u1 += m->padding; u2 += m->padding; if(!m->isOptional) { if(!m->isArray) { o = orderJumpTable[mt->typeKind]((const void *)u1, (const void *)u2, mt); u1 += mt->memSize; u2 += mt->memSize; } else { size_t size1 = *(size_t*)u1; size_t size2 = *(size_t*)u2; u1 += sizeof(size_t); u2 += sizeof(size_t); o = arrayOrder(*(void* const*)u1, size1, *(void* const*)u2, size2, mt); u1 += sizeof(void*); u2 += sizeof(void*); } } else { if(!m->isArray) { const void *pp1 = *(void* const*)u1; const void *pp2 = *(void* const*)u2; if(pp1 == pp2) { o = UA_ORDER_EQ; } else if(pp1 == NULL) { o = UA_ORDER_LESS; } else if(pp2 == NULL) { o = UA_ORDER_MORE; } else { o = orderJumpTable[mt->typeKind](pp1, pp2, mt); } } else { size_t sa1 = *(size_t*)u1; size_t sa2 = *(size_t*)u2; u1 += sizeof(size_t); u2 += sizeof(size_t); o = arrayOrder(*(void* const*)u1, sa1, *(void* const*)u2, sa2, mt); } u1 += sizeof(void*); u2 += sizeof(void*); } if(o != UA_ORDER_EQ) break; } return o; } static UA_Order unionOrder(const void *p1, const void *p2, const UA_DataType *type) { UA_UInt32 sel1 = *(const UA_UInt32 *)p1; UA_UInt32 sel2 = *(const UA_UInt32 *)p2; if(sel1 != sel2) return (sel1 < sel2) ? UA_ORDER_LESS : UA_ORDER_MORE; if(sel1 == 0) { return UA_ORDER_EQ; } const UA_DataTypeMember *m = &type->members[sel1-1]; const UA_DataType *mt = m->memberType; uintptr_t u1 = ((uintptr_t)p1) + m->padding; /* includes switchfield length */ uintptr_t u2 = ((uintptr_t)p2) + m->padding; if(m->isArray) { size_t sa1 = *(size_t*)u1; size_t sa2 = *(size_t*)u2; u1 += sizeof(size_t); u2 += sizeof(size_t); return arrayOrder(*(void* const*)u1, sa1, *(void* const*)u2, sa2, mt); } return orderJumpTable[mt->typeKind]((const void*)u1, (const void*)u2, mt); } static UA_Order notImplementedOrder(const void *p1, const void *p2, const UA_DataType *type) { return UA_ORDER_EQ; } const UA_orderSignature orderJumpTable[UA_DATATYPEKINDS] = { (UA_orderSignature)booleanOrder, (UA_orderSignature)sByteOrder, (UA_orderSignature)byteOrder, (UA_orderSignature)int16Order, (UA_orderSignature)uInt16Order, (UA_orderSignature)int32Order, (UA_orderSignature)uInt32Order, (UA_orderSignature)int64Order, (UA_orderSignature)uInt64Order, (UA_orderSignature)floatOrder, (UA_orderSignature)doubleOrder, (UA_orderSignature)stringOrder, (UA_orderSignature)int64Order, /* DateTime */ (UA_orderSignature)guidOrder, (UA_orderSignature)stringOrder, /* ByteString */ (UA_orderSignature)stringOrder, /* XmlElement */ (UA_orderSignature)nodeIdOrder, (UA_orderSignature)expandedNodeIdOrder, (UA_orderSignature)uInt32Order, /* StatusCode */ (UA_orderSignature)qualifiedNameOrder, (UA_orderSignature)localizedTextOrder, (UA_orderSignature)extensionObjectOrder, (UA_orderSignature)dataValueOrder, (UA_orderSignature)variantOrder, (UA_orderSignature)diagnosticInfoOrder, notImplementedOrder, /* Decimal, not implemented */ (UA_orderSignature)uInt32Order, /* Enumeration */ (UA_orderSignature)structureOrder, (UA_orderSignature)structureOrder, /* Struct with Optional Fields*/ (UA_orderSignature)unionOrder, /* Union*/ notImplementedOrder /* BitfieldCluster, not implemented */ }; UA_Order UA_order(const void *p1, const void *p2, const UA_DataType *type) { return orderJumpTable[type->typeKind](p1, p2, type); } /******************/ /* 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; } UA_StatusCode UA_Array_resize(void **p, size_t *size, size_t newSize, const UA_DataType *type) { if(*size == newSize) return UA_STATUSCODE_GOOD; /* Empty array? */ if(newSize == 0) { UA_Array_delete(*p, *size, type); *p = UA_EMPTY_ARRAY_SENTINEL; *size = 0; return UA_STATUSCODE_GOOD; } /* Make a copy of the members that shall be removed. Realloc can fail during * trimming. So we cannot clear the members already here. */ void *deleteMembers = NULL; if(newSize < *size && !type->pointerFree) { size_t deleteSize = *size - newSize; deleteMembers = UA_malloc(deleteSize * type->memSize); if(!deleteMembers) return UA_STATUSCODE_BADOUTOFMEMORY; memcpy(deleteMembers, (void*)((uintptr_t)*p + (newSize * type->memSize)), deleteSize * type->memSize); /* shallow copy */ } void *oldP = *p; if(oldP == UA_EMPTY_ARRAY_SENTINEL) oldP = NULL; /* Realloc */ void *newP = UA_realloc(oldP, newSize * type->memSize); if(!newP) { if(deleteMembers) UA_free(deleteMembers); return UA_STATUSCODE_BADOUTOFMEMORY; } /* Clear removed members or initialize the new ones. Note that deleteMembers * depends on type->pointerFree. */ if(newSize > *size) memset((void*)((uintptr_t)newP + (*size * type->memSize)), 0, (newSize - *size) * type->memSize); else if(deleteMembers) UA_Array_delete(deleteMembers, *size - newSize, type); /* Set the new array */ *p = newP; *size = newSize; return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Array_append(void **p, size_t *size, void *newElem, const UA_DataType *type) { /* Resize the array */ size_t oldSize = *size; UA_StatusCode res = UA_Array_resize(p, size, oldSize+1, type); if(res != UA_STATUSCODE_GOOD) return res; /* Move the value */ memcpy((void*)((uintptr_t)*p + (oldSize * type->memSize)), newElem, type->memSize); UA_init(newElem, type); return UA_STATUSCODE_GOOD; } UA_StatusCode UA_EXPORT UA_Array_appendCopy(void **p, size_t *size, const void *newElem, const UA_DataType *type) { char scratch[512]; if(type->memSize > 512) return UA_STATUSCODE_BADINTERNALERROR; /* Copy the value */ UA_StatusCode res = UA_copy(newElem, (void*)scratch, type); if(res != UA_STATUSCODE_GOOD) return res; /* Append */ res = UA_Array_append(p, size, (void*)scratch, type); if(res != UA_STATUSCODE_GOOD) UA_clear((void*)scratch, type); return res; } 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)); } #ifdef UA_ENABLE_TYPEDESCRIPTION UA_Boolean UA_DataType_getStructMember(const UA_DataType *type, const char *memberName, size_t *outOffset, const UA_DataType **outMemberType, UA_Boolean *outIsArray) { if(type->typeKind != UA_DATATYPEKIND_STRUCTURE && type->typeKind != UA_DATATYPEKIND_OPTSTRUCT) return false; size_t offset = 0; for(size_t i = 0; i < type->membersSize; ++i) { const UA_DataTypeMember *m = &type->members[i]; const UA_DataType *mt = m->memberType; offset += m->padding; if(strcmp(memberName, m->memberName) == 0) { *outOffset = offset; *outMemberType = mt; *outIsArray = m->isArray; return true; } if(!m->isOptional) { if(!m->isArray) { offset += mt->memSize; } else { offset += sizeof(size_t); offset += sizeof(void*); } } else { /* field is optional */ if(!m->isArray) { offset += sizeof(void *); } else { offset += sizeof(size_t); offset += sizeof(void *); } } } return false; } #endif UA_Boolean UA_DataType_isNumeric(const UA_DataType *type) { switch(type->typeKind) { case UA_DATATYPEKIND_SBYTE: case UA_DATATYPEKIND_BYTE: case UA_DATATYPEKIND_INT16: case UA_DATATYPEKIND_UINT16: case UA_DATATYPEKIND_INT32: case UA_DATATYPEKIND_UINT32: case UA_DATATYPEKIND_INT64: case UA_DATATYPEKIND_UINT64: case UA_DATATYPEKIND_FLOAT: case UA_DATATYPEKIND_DOUBLE: /* not implemented: UA_DATATYPEKIND_DECIMAL */ return true; default: return false; } } UA_Int16 UA_DataType_getPrecedence(const UA_DataType *type){ //Defined in Part 4 Table 123 "Data Precedence Rules" switch(type->typeKind) { case UA_DATATYPEKIND_DOUBLE: return 1; case UA_DATATYPEKIND_FLOAT: return 2; case UA_DATATYPEKIND_INT64: return 3; case UA_DATATYPEKIND_UINT64: return 4; case UA_DATATYPEKIND_INT32: return 5; case UA_DATATYPEKIND_UINT32: return 6; case UA_DATATYPEKIND_STATUSCODE: return 7; case UA_DATATYPEKIND_INT16: return 8; case UA_DATATYPEKIND_UINT16: return 9; case UA_DATATYPEKIND_SBYTE: return 10; case UA_DATATYPEKIND_BYTE: return 11; case UA_DATATYPEKIND_BOOLEAN: return 12; case UA_DATATYPEKIND_GUID: return 13; case UA_DATATYPEKIND_STRING: return 14; case UA_DATATYPEKIND_EXPANDEDNODEID: return 15; case UA_DATATYPEKIND_NODEID: return 16; case UA_DATATYPEKIND_LOCALIZEDTEXT: return 17; case UA_DATATYPEKIND_QUALIFIEDNAME: return 18; default: return -1; } } /**********************/ /* 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_parse(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 "/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 2020 (c) Fraunhofer IOSB (Author: Andreas Ebner) * Copyright 2020 (c) Grigory Friedman * Copyright 2014-2021 (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. * * 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. */ /* 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 and last buffer position */ u8 *pos; const u8 *end; /* How often did we en-/decoding recurse? */ u16 depth; 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 *type); #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]; /* 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 */ #ifndef NDEBUG /* We have to ensure that the buffer was not exchanged AND * BADENCODINGLIMITSEXCEEDED was returned. If that were the case, oldpos * would be invalid. That means, a type encoding must never return * BADENCODINGLIMITSEXCEEDED once the buffer could have been exchanged. This * is achieved by the use of encodeWithExchangeBuffer. */ const u8 *oldend = ctx->end; (void)oldend; /* For compilers who don't understand NDEBUG... */ #endif status ret = encodeBinaryJumpTable[type->typeKind](ptr, type, ctx); if(ret == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED) { UA_assert(ctx->end == oldend); ctx->pos = oldpos; /* Set to the last known good position and exchange */ ret = exchangeBuffer(ctx); UA_CHECK_STATUS(ret, return ret); ret = encodeBinaryJumpTable[type->typeKind](ptr, type, ctx); } return ret; } /*****************/ /* 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) { UA_CHECK(ctx->pos + 1 <= ctx->end, return UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); *ctx->pos = *(const u8*)src; ++ctx->pos; return UA_STATUSCODE_GOOD; } DECODE_BINARY(Boolean) { UA_CHECK(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) { UA_CHECK(ctx->pos + sizeof(u8) <= ctx->end, return UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); *ctx->pos = *(const u8*)src; ++ctx->pos; return UA_STATUSCODE_GOOD; } DECODE_BINARY(Byte) { UA_CHECK(ctx->pos + sizeof(u8) <= ctx->end, return UA_STATUSCODE_BADDECODINGERROR); *dst = *ctx->pos; ++ctx->pos; return UA_STATUSCODE_GOOD; } /* UInt16 */ ENCODE_BINARY(UInt16) { UA_CHECK(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) { UA_CHECK(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) { UA_CHECK(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) { UA_CHECK(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) { UA_CHECK(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) { UA_CHECK(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); UA_CHECK_STATUS(ret, 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 memSize, Ctx *ctx) { /* Loop as long as more elements remain than fit into the chunk */ while(ctx->end < ctx->pos + memSize) { size_t possible = ((uintptr_t)ctx->end - (uintptr_t)ctx->pos); memcpy(ctx->pos, (void*)ptr, possible); ctx->pos += possible; ptr += possible; status ret = exchangeBuffer(ctx); UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); UA_CHECK_STATUS(ret, return ret); memSize -= possible; } /* Encode the remaining elements */ memcpy(ctx->pos, (void*)ptr, memSize); ctx->pos += memSize; 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; UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); UA_CHECK_STATUS(ret, 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 = encodeWithExchangeBuffer(&signed_length, &UA_TYPES[UA_TYPES_INT32], ctx); UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); UA_CHECK_STATUS(ret, return ret); /* Encode the content */ if(length > 0) { if(type->overlayable) ret = Array_encodeBinaryOverlayable((uintptr_t)src, length * type->memSize, ctx); else ret = Array_encodeBinaryComplex((uintptr_t)src, length, type, ctx); } UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); return ret; } 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 */ UA_CHECK_STATUS(ret, 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. * * The worst known case (so far) is UA_DataValue. It has * sizeof(UA_DataValue) == 80 and an empty DataValue is encoded with just * one byte. We use 128 as the smallest power of 2 larger than 80. */ size_t length = (size_t)signed_length; UA_CHECK(ctx->pos + ((type->memSize * length) / 128) <= ctx->end, return UA_STATUSCODE_BADDECODINGERROR); /* Allocate memory */ *dst = UA_calloc(length, type->memSize); UA_CHECK_MEM(*dst, return UA_STATUSCODE_BADOUTOFMEMORY); if(type->overlayable) { /* memcpy overlayable array */ UA_CHECK(ctx->pos + (type->memSize * length) <= ctx->end, 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); UA_CHECK_STATUS(ret, /* +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); UA_CHECK(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); UA_CHECK(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); UA_CHECK_STATUS(ret, return ret); /* Can exchange the buffer */ ret = ENCODE_DIRECT(&src->identifier.string, String); UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); 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); UA_CHECK_STATUS(ret, return ret); /* Can exchange the buffer */ ret = ENCODE_DIRECT(&src->identifier.byteString, String); /* ByteString */ UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); 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); UA_CHECK_STATUS(ret, 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. Can exchange the buffer. */ status ret = NodeId_encodeBinaryWithEncodingMask(&src->nodeId, encoding, ctx); UA_CHECK_STATUS(ret, return ret); /* Encode the namespace. Internally uses encodeWithExchangeBuffer * everywhere. So it will never return * UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED. */ if((void*)src->namespaceUri.data > UA_EMPTY_ARRAY_SENTINEL) { ret = ENCODE_DIRECT(&src->namespaceUri, String); UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); UA_CHECK_STATUS(ret, return ret); } /* Encode the serverIndex */ if(src->serverIndex > 0) ret = encodeWithExchangeBuffer(&src->serverIndex, &UA_TYPES[UA_TYPES_UINT32], ctx); UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); return ret; } DECODE_BINARY(ExpandedNodeId) { /* Decode the encoding mask */ UA_CHECK(ctx->pos + 1 <= 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); /* Must check here so we can exchange the buffer in the string encoding */ UA_CHECK_STATUS(ret, return ret); 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); /* Must check here so we can exchange the buffer in the string encoding */ UA_CHECK_STATUS(ret, 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); UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); 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) { /* Always look in the built-in types first. Assume that only numeric * identifiers are used for the builtin types. (They may contain data types * from all namespaces though.) */ if(typeId->identifierType == UA_NODEIDTYPE_NUMERIC) { for(size_t i = 0; i < UA_TYPES_COUNT; ++i) { if(UA_TYPES[i].binaryEncodingId.identifier.numeric == typeId->identifier.numeric && UA_TYPES[i].binaryEncodingId.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(UA_NodeId_equal(typeId, &customTypes->types[i].binaryEncodingId)) 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) { /* Can exchange the buffer */ status ret = ENCODE_DIRECT(&src->content.encoded.typeId, NodeId); UA_CHECK_STATUS(ret, return ret); ret = encodeWithExchangeBuffer(&encoding, &UA_TYPES[UA_TYPES_BYTE], ctx); UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); UA_CHECK_STATUS(ret, return ret); switch(src->encoding) { case UA_EXTENSIONOBJECT_ENCODED_NOBODY: break; case UA_EXTENSIONOBJECT_ENCODED_BYTESTRING: case UA_EXTENSIONOBJECT_ENCODED_XML: /* ByteString in disguise. Array encoding can exchange the buffer */ ret = ENCODE_DIRECT(&src->content.encoded.body, String); break; default: ret = UA_STATUSCODE_BADINTERNALERROR; } UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); 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. This could perform a buffer * exchange, but can also return UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED. */ status ret = ENCODE_DIRECT(&src->content.decoded.type->binaryEncodingId, NodeId); UA_CHECK_STATUS(ret, return ret); /* Encode the encoding byte */ encoding = UA_EXTENSIONOBJECT_ENCODED_BYTESTRING; ret = encodeWithExchangeBuffer(&encoding, &UA_TYPES[UA_TYPES_BYTE], ctx); UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); UA_CHECK_STATUS(ret, return ret); /* Encode the content length */ const UA_DataType *contentType = src->content.decoded.type; size_t len = UA_calcSizeBinary(src->content.decoded.data, contentType); UA_CHECK(len <= UA_INT32_MAX, return UA_STATUSCODE_BADENCODINGERROR); i32 signed_len = (i32)len; ret = encodeWithExchangeBuffer(&signed_len, &UA_TYPES[UA_TYPES_INT32], ctx); UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); UA_CHECK_STATUS(ret, return ret); /* Encode the content */ ret = encodeWithExchangeBuffer(src->content.decoded.data, contentType, ctx); UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); return ret; } 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); UA_CHECK_MEM(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; UA_NodeId_init(&binTypeId); status ret = UA_STATUSCODE_GOOD; ret |= DECODE_DIRECT(&binTypeId, NodeId); ret |= DECODE_DIRECT(&encoding, Byte); UA_CHECK_STATUS(ret, UA_NodeId_clear(&binTypeId); return ret); switch(encoding) { case UA_EXTENSIONOBJECT_ENCODED_BYTESTRING: ret = ExtensionObject_decodeBinaryContent(dst, &binTypeId, ctx); UA_NodeId_clear(&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 */ UA_CHECK_STATUS(ret, 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) { size_t length = 1; /* Default to 1 for a scalar. */ /* Encode the array length if required */ status ret = UA_STATUSCODE_GOOD; if(isArray) { UA_CHECK(src->arrayLength <= UA_INT32_MAX, return UA_STATUSCODE_BADENCODINGERROR); length = src->arrayLength; i32 encodedLength = (i32)src->arrayLength; ret = ENCODE_DIRECT(&encodedLength, UInt32); /* Int32 */ UA_CHECK_STATUS(ret, 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); UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); 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); UA_CHECK_STATUS(ret, return ret); /* Encode the content. This can exchange the buffer. */ if(!isBuiltin && !isEnum) { /* This could return UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED. But we * have not exchanged the buffer so far. */ ret = Variant_encodeBinaryWrapExtensionObject(src, isArray, ctx); } else if(!isArray) { ret = encodeWithExchangeBuffer(src->data, src->type, ctx); UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); } else { ret = Array_encodeBinary(src->data, src->arrayLength, src->type, ctx); UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); } UA_CHECK_STATUS(ret, return ret); /* Encode the array dimensions */ if(hasDimensions && ret == UA_STATUSCODE_GOOD) ret = Array_encodeBinary(src->arrayDimensions, src->arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32], ctx); UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); 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); UA_CHECK_STATUS(ret, return ret); /* Decode the EncodingByte */ u8 encoding; ret = DECODE_DIRECT(&encoding, Byte); UA_CHECK_STATUS(ret, 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); UA_CHECK_MEM(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); UA_CHECK_STATUS(ret, 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); UA_CHECK(typeKind <= UA_DATATYPEKIND_DIAGNOSTICINFO, return UA_STATUSCODE_BADDECODINGERROR); /* A variant cannot contain a variant. But it can contain an array of * variants */ UA_CHECK(typeKind != UA_DATATYPEKIND_VARIANT || isArray, return UA_STATUSCODE_BADDECODINGERROR); /* Check the recursion limit */ UA_CHECK(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); UA_CHECK_MEM(dst->data, ctx->depth--; 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); UA_CHECK_STATUS(ret, 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 |= encodeWithExchangeBuffer(&src->status, &UA_TYPES[UA_TYPES_STATUSCODE], ctx); if(src->hasSourceTimestamp) ret |= encodeWithExchangeBuffer(&src->sourceTimestamp, &UA_TYPES[UA_TYPES_DATETIME], ctx); if(src->hasSourcePicoseconds) ret |= encodeWithExchangeBuffer(&src->sourcePicoseconds, &UA_TYPES[UA_TYPES_UINT16], ctx); if(src->hasServerTimestamp) ret |= encodeWithExchangeBuffer(&src->serverTimestamp, &UA_TYPES[UA_TYPES_DATETIME], ctx); if(src->hasServerPicoseconds) ret |= encodeWithExchangeBuffer(&src->serverPicoseconds, &UA_TYPES[UA_TYPES_UINT16], ctx); UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); return ret; } #define MAX_PICO_SECONDS 9999 DECODE_BINARY(DataValue) { /* Decode the encoding mask */ u8 encodingMask; status ret = DECODE_DIRECT(&encodingMask, Byte); UA_CHECK_STATUS(ret, return ret); /* Check the recursion limit */ UA_CHECK(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->hasInnerStatusCode << 5u); encodingMask |= (u8)(src->hasInnerDiagnosticInfo << 6u); /* 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. Can exchange the buffer. */ if(src->hasAdditionalInfo) { ret = ENCODE_DIRECT(&src->additionalInfo, String); UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); UA_CHECK_STATUS(ret, return ret); } /* Encode the inner status code */ if(src->hasInnerStatusCode) { ret = encodeWithExchangeBuffer(&src->innerStatusCode, &UA_TYPES[UA_TYPES_UINT32], ctx); UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); UA_CHECK_STATUS(ret, return ret); } /* Encode the inner diagnostic info */ if(src->hasInnerDiagnosticInfo) { ret = encodeWithExchangeBuffer(src->innerDiagnosticInfo, &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], ctx); UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); } return ret; } DECODE_BINARY(DiagnosticInfo) { /* Decode the encoding mask */ u8 encodingMask; status ret = DECODE_DIRECT(&encodingMask, Byte); UA_CHECK_STATUS(ret, 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)); UA_CHECK_MEM(dst->innerDiagnosticInfo, return UA_STATUSCODE_BADOUTOFMEMORY); dst->hasInnerDiagnosticInfo = true; /* Check the recursion limit */ UA_CHECK(ctx->depth <= UA_ENCODING_MAX_RECURSION, return UA_STATUSCODE_BADENCODINGERROR); ctx->depth++; ret |= DECODE_DIRECT(dst->innerDiagnosticInfo, DiagnosticInfo); ctx->depth--; } return ret; } /********************/ /* Structured Types */ /********************/ static status encodeBinaryStruct(const void *src, const UA_DataType *type, Ctx *ctx) { /* Check the recursion limit */ UA_CHECK(ctx->depth <= UA_ENCODING_MAX_RECURSION, return UA_STATUSCODE_BADENCODINGERROR); ctx->depth++; /* Loop over members */ uintptr_t ptr = (uintptr_t)src; status ret = UA_STATUSCODE_GOOD; for(size_t i = 0; i < type->membersSize && ret == UA_STATUSCODE_GOOD; ++i) { const UA_DataTypeMember *m = &type->members[i]; const UA_DataType *mt = m->memberType; 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); UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); ptr += sizeof(void*); continue; } /* Scalar */ ret = encodeWithExchangeBuffer((const void*)ptr, mt, ctx); UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); ptr += mt->memSize; } ctx->depth--; return ret; } static status encodeBinaryStructWithOptFields(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++; /* Creating the encoding mask, marking the available optional fields */ uintptr_t ptr = (uintptr_t)src; size_t optFieldCounter = 0; UA_UInt32 encodingMask = 0; for(size_t j = 0; j < type->membersSize; ++j) { const UA_DataTypeMember *m = &type->members[j]; const UA_DataType *mt = m->memberType; ptr += m->padding; if(m->isOptional) { if(m->isArray) ptr += sizeof(size_t); if(*(void* const*)ptr != NULL) encodingMask |= (UA_UInt32) 1 << optFieldCounter; ptr += sizeof(void *); optFieldCounter++; } else if (m->isArray) { ptr += sizeof(size_t); ptr += sizeof(void *); } else { ptr += mt->memSize; } } /* Encode the mask */ status ret = ENCODE_DIRECT(&encodingMask, UInt32); UA_CHECK_STATUS(ret, ctx->depth--; return ret); /* Loop over members */ ptr = (uintptr_t)src; for(size_t i = 0, o = 0; i < type->membersSize && UA_LIKELY(ret == UA_STATUSCODE_GOOD); ++i) { const UA_DataTypeMember *m = &type->members[i]; const UA_DataType *mt = m->memberType; ptr += m->padding; if(m->isOptional) { if(!(encodingMask & (UA_UInt32) ( (UA_UInt32) 1<<(o++)))) { /* Optional and not contained */ if(m->isArray) ptr += sizeof(size_t); } else if(m->isArray) { /* Optional Array */ const size_t length = *((const size_t *) ptr); ptr += sizeof(size_t); ret = Array_encodeBinary(*(void *UA_RESTRICT const *) ptr, length, mt, ctx); } else { /* Optional Scalar */ ret = encodeWithExchangeBuffer(*(void* const*) ptr, mt, ctx); } ptr += sizeof(void *); continue; } /* Mandatory Array */ 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; } /* Mandatory Scalar */ ret = encodeWithExchangeBuffer((const void*)ptr, mt, ctx); ptr += mt->memSize; } UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); ctx->depth--; return ret; } static status encodeBinaryUnion(const void *src, const UA_DataType *type, Ctx *ctx) { /* Check the recursion limit */ UA_CHECK(ctx->depth <= UA_ENCODING_MAX_RECURSION, return UA_STATUSCODE_BADENCODINGERROR); ctx->depth++; /* Encode the selection */ const UA_UInt32 selection = *(const UA_UInt32*)src; status ret = ENCODE_DIRECT(&selection, UInt32); if(UA_UNLIKELY(ret != UA_STATUSCODE_GOOD) || selection == 0) { ctx->depth--; return ret; } /* Select the member */ const UA_DataTypeMember *m = &type->members[selection-1]; const UA_DataType *mt = m->memberType; /* Encode the member */ uintptr_t ptr = ((uintptr_t)src) + m->padding; /* includes the switchfield length */ if(!m->isArray) { ret = encodeWithExchangeBuffer((const void*)ptr, mt, ctx); } else { const size_t length = *((const size_t*)ptr); ptr += sizeof(size_t); ret = Array_encodeBinary(*(void *UA_RESTRICT const *)ptr, length, mt, ctx); } UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); 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; } 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)encodeBinaryStructWithOptFields, /* Structure with Optional Fields */ (encodeBinarySignature)encodeBinaryUnion, /* Union */ (encodeBinarySignature)encodeBinaryStruct /* BitfieldCluster */ }; status UA_encodeBinaryInternal(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; UA_CHECK_MEM(ctx.pos, return UA_STATUSCODE_BADINVALIDARGUMENT); /* Encode */ status ret = encodeWithExchangeBuffer(src, type, &ctx); UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED); /* 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; } UA_StatusCode UA_encodeBinary(const void *p, const UA_DataType *type, UA_ByteString *outBuf) { /* Allocate buffer */ UA_Boolean allocated = false; status res = UA_STATUSCODE_GOOD; if(outBuf->length == 0) { size_t len = UA_calcSizeBinary(p, type); res = UA_ByteString_allocBuffer(outBuf, len); if(res != UA_STATUSCODE_GOOD) return res; allocated = true; } /* Encode */ u8 *pos = outBuf->data; const u8 *posEnd = &outBuf->data[outBuf->length]; res = UA_encodeBinaryInternal(p, type, &pos, &posEnd, NULL, NULL); /* Clean up */ if(res == UA_STATUSCODE_GOOD) { outBuf->length = (size_t)((uintptr_t)pos - (uintptr_t)outBuf->data); } else if(allocated) { UA_ByteString_clear(outBuf); } return res; } 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 */ UA_CHECK(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; /* 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 = m->memberType; 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; } static status decodeBinaryStructureWithOptFields(void *dst, const UA_DataType *type, Ctx *ctx) { /* Check the recursion limit */ UA_CHECK(ctx->depth <= UA_ENCODING_MAX_RECURSION, return UA_STATUSCODE_BADENCODINGERROR); ctx->depth++; uintptr_t ptr = (uintptr_t)dst; UA_UInt32 encodingMask = 0; status ret = UInt32_decodeBinary(&encodingMask, &UA_TYPES[UA_TYPES_UINT32], ctx); UA_CHECK_STATUS(ret, ctx->depth--; return ret); /* Loop over members */ for(size_t i = 0, o = 0; i < type->membersSize && ret == UA_STATUSCODE_GOOD; ++i) { const UA_DataTypeMember *m = &type->members[i]; const UA_DataType *mt = m->memberType; ptr += m->padding; if(m->isOptional) { if(!(encodingMask & (UA_UInt32) ( (UA_UInt32) 1<<(o++)))) { /* Optional field is not contained */ if(m->isArray) ptr += sizeof(size_t); } else if(m->isArray) { /* Optional Array */ size_t *length = (size_t*)ptr; ptr += sizeof(size_t); ret = Array_decodeBinary((void *UA_RESTRICT *UA_RESTRICT)ptr, length, mt , ctx); } else { /* Optional Scalar */ *(void *UA_RESTRICT *UA_RESTRICT) ptr = UA_calloc(1, mt->memSize); UA_CHECK_MEM(*(void *UA_RESTRICT *UA_RESTRICT) ptr, return UA_STATUSCODE_BADOUTOFMEMORY); ret = decodeBinaryJumpTable[mt->typeKind](*(void *UA_RESTRICT *UA_RESTRICT) ptr, mt, ctx); } ptr += sizeof(void *); continue; } /* 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; } static status decodeBinaryUnion(void *UA_RESTRICT dst, const UA_DataType *type, Ctx *ctx) { /* Check the recursion limit */ UA_CHECK(ctx->depth <= UA_ENCODING_MAX_RECURSION, return UA_STATUSCODE_BADENCODINGERROR); /* Decode the selection directly into the switchfield */ status ret = DECODE_DIRECT(dst, UInt32); UA_CHECK_STATUS(ret, return ret); /* No content? */ UA_UInt32 selection = *(UA_UInt32*)dst; if(selection == 0) return UA_STATUSCODE_GOOD; /* Sanity check the selection */ UA_CHECK(selection-1 < type->membersSize, return UA_STATUSCODE_BADDECODINGERROR); /* Select the member */ const UA_DataTypeMember *m = &type->members[selection-1]; const UA_DataType *mt = m->memberType; /* Decode */ ctx->depth++; uintptr_t ptr = ((uintptr_t)dst) + m->padding; /* includes the switchfield */ if(!m->isArray) { ret = decodeBinaryJumpTable[mt->typeKind]((void *UA_RESTRICT)ptr, mt, ctx); } else { size_t *length = (size_t *)ptr; ptr += sizeof(size_t); ret = Array_decodeBinary((void *UA_RESTRICT *UA_RESTRICT)ptr, length, mt, ctx); } 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)decodeBinaryStructureWithOptFields, /* Structure with optional fields */ (decodeBinarySignature)decodeBinaryUnion, /* Union */ (decodeBinarySignature)decodeBinaryNotImplemented /* BitfieldCluster */ }; status UA_decodeBinaryInternal(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(UA_LIKELY(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; } UA_StatusCode UA_decodeBinary(const UA_ByteString *inBuf, void *p, const UA_DataType *type, const UA_DecodeBinaryOptions *options) { size_t offset = 0; const UA_DataTypeArray *customTypes = options ? options->customTypes : NULL; return UA_decodeBinaryInternal(inBuf, &offset, p, type, customTypes); } /** * 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->binaryEncodingId, 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->binaryEncodingId, 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; /* Loop over members */ for(size_t i = 0; i < membersSize; ++i) { const UA_DataTypeMember *member = &type->members[i]; const UA_DataType *membertype = member->memberType; 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 calcSizeBinaryStructureWithOptFields(const void *p, const UA_DataType *type) { /* Start with the size of the encoding mask */ size_t s = sizeof(UA_UInt32); /* Loop over members */ uintptr_t ptr = (uintptr_t)p; for(size_t i = 0; i < type->membersSize; ++i) { const UA_DataTypeMember *member = &type->members[i]; const UA_DataType *membertype = member->memberType; ptr += member->padding; if(member->isOptional) { if((member->isArray && ((*(void* const*)(ptr+sizeof(size_t))) == NULL)) || (!member->isArray && (*(void* const*)ptr == NULL))) { /* Optional member not contained */ if(member->isArray) ptr += sizeof(size_t); ptr += sizeof(void *); continue; } /* Fallthrough to take the size into account */ } /* 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 */ if (member->isOptional) { s += calcSizeBinaryJumpTable[membertype->typeKind](*(void* const*)ptr, membertype); ptr += sizeof(void *); } else { s += calcSizeBinaryJumpTable[membertype->typeKind]((const void*)ptr, membertype); ptr += membertype->memSize; } } return s; } static size_t calcSizeBinaryUnion(const void *p, const UA_DataType *type) { size_t s = 4; /* UA_TYPES[UA_TYPES_UINT32].memSize; */ const UA_UInt32 selection = *(const UA_UInt32 *)p; if(selection == 0) return s; const UA_DataTypeMember *m = &type->members[selection-1]; const UA_DataType *mt = m->memberType; uintptr_t ptr = ((uintptr_t)p) + m->padding; /* includes switchfield length */ if(!m->isArray) { s += UA_calcSizeBinary((const void*)ptr, mt); } else { const size_t length = *((const size_t*)ptr); ptr += sizeof(size_t); s += Array_calcSizeBinary(*(void *UA_RESTRICT const *)ptr, length, mt); } 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)calcSizeBinaryStructureWithOptFields, /* Structure with Optional Fields */ (calcSizeBinarySignature)calcSizeBinaryUnion, /* Union */ (calcSizeBinarySignature)calcSizeBinaryNotImplemented /* BitfieldCluster */ }; size_t UA_calcSizeBinary(const void *p, const UA_DataType *type) { return calcSizeBinaryJumpTable[type->typeKind](p, type); } /**** amalgamated original file "/src/ua_types_print.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 2020 (c) Fraunhofer IOSB (Author: Julius Pfrommer) */ /* Printing of NodeIds is always enabled. We need it for logging. */ UA_StatusCode UA_NodeId_print(const UA_NodeId *id, UA_String *output) { UA_String_clear(output); if(!id) return UA_STATUSCODE_GOOD; char *nsStr = NULL; long snprintfLen = 0; size_t nsLen = 0; if(id->namespaceIndex != 0) { nsStr = (char*)UA_malloc(9+1); // strlen("ns=XXXXX;") = 9 + Nullbyte if(!nsStr) return UA_STATUSCODE_BADOUTOFMEMORY; snprintfLen = UA_snprintf(nsStr, 10, "ns=%d;", id->namespaceIndex); if(snprintfLen < 0 || snprintfLen >= 10) { UA_free(nsStr); return UA_STATUSCODE_BADINTERNALERROR; } nsLen = (size_t)(snprintfLen); } UA_ByteString byteStr = UA_BYTESTRING_NULL; switch (id->identifierType) { case UA_NODEIDTYPE_NUMERIC: /* ns (2 byte, 65535) = 5 chars, numeric (4 byte, 4294967295) = 10 * chars, delim = 1 , nullbyte = 1-> 17 chars */ output->length = nsLen + 2 + 10 + 1; output->data = (UA_Byte*)UA_malloc(output->length); if(output->data == NULL) { output->length = 0; UA_free(nsStr); return UA_STATUSCODE_BADOUTOFMEMORY; } snprintfLen = UA_snprintf((char*)output->data, output->length, "%si=%lu", nsLen > 0 ? nsStr : "", (unsigned long )id->identifier.numeric); break; case UA_NODEIDTYPE_STRING: /* ns (16bit) = 5 chars, strlen + nullbyte */ output->length = nsLen + 2 + id->identifier.string.length + 1; output->data = (UA_Byte*)UA_malloc(output->length); if(output->data == NULL) { output->length = 0; UA_free(nsStr); return UA_STATUSCODE_BADOUTOFMEMORY; } snprintfLen = UA_snprintf((char*)output->data, output->length, "%ss=%.*s", nsLen > 0 ? nsStr : "", (int)id->identifier.string.length, id->identifier.string.data); break; case UA_NODEIDTYPE_GUID: /* ns (16bit) = 5 chars + strlen(A123456C-0ABC-1A2B-815F-687212AAEE1B)=36 + nullbyte */ output->length = nsLen + 2 + 36 + 1; output->data = (UA_Byte*)UA_malloc(output->length); if(output->data == NULL) { output->length = 0; UA_free(nsStr); return UA_STATUSCODE_BADOUTOFMEMORY; } snprintfLen = UA_snprintf((char*)output->data, output->length, "%sg=" UA_PRINTF_GUID_FORMAT, nsLen > 0 ? nsStr : "", UA_PRINTF_GUID_DATA(id->identifier.guid)); break; case UA_NODEIDTYPE_BYTESTRING: UA_ByteString_toBase64(&id->identifier.byteString, &byteStr); /* ns (16bit) = 5 chars + LEN + nullbyte */ output->length = nsLen + 2 + byteStr.length + 1; output->data = (UA_Byte*)UA_malloc(output->length); if(output->data == NULL) { output->length = 0; UA_String_clear(&byteStr); UA_free(nsStr); return UA_STATUSCODE_BADOUTOFMEMORY; } snprintfLen = UA_snprintf((char*)output->data, output->length, "%sb=%.*s", nsLen > 0 ? nsStr : "", (int)byteStr.length, byteStr.data); UA_String_clear(&byteStr); break; } UA_free(nsStr); if(snprintfLen < 0 || snprintfLen >= (long) output->length) { UA_free(output->data); output->data = NULL; output->length = 0; return UA_STATUSCODE_BADINTERNALERROR; } output->length = (size_t)snprintfLen; return UA_STATUSCODE_GOOD; } UA_StatusCode UA_ExpandedNodeId_print(const UA_ExpandedNodeId *id, UA_String *output) { /* Don't print the namespace-index if a NamespaceUri is set */ UA_NodeId nid = id->nodeId; if(id->namespaceUri.data != NULL) nid.namespaceIndex = 0; /* Encode the NodeId */ UA_String outNid = UA_STRING_NULL; UA_StatusCode res = UA_NodeId_print(&nid, &outNid); if(res != UA_STATUSCODE_GOOD) return res; /* Encode the ServerIndex */ char svr[100]; if(id->serverIndex == 0) svr[0] = 0; else UA_snprintf(svr, 100, "svr=%"PRIu32";", id->serverIndex); size_t svrlen = strlen(svr); /* Encode the NamespaceUri */ char nsu[100]; if(id->namespaceUri.data == NULL) nsu[0] = 0; else UA_snprintf(nsu, 100, "nsu=%.*s;", (int)id->namespaceUri.length, id->namespaceUri.data); size_t nsulen = strlen(nsu); /* Combine everything */ res = UA_ByteString_allocBuffer((UA_String*)output, outNid.length + svrlen + nsulen); if(res == UA_STATUSCODE_GOOD) { memcpy(output->data, svr, svrlen); memcpy(&output->data[svrlen], nsu, nsulen); memcpy(&output->data[svrlen+nsulen], outNid.data, outNid.length); } UA_String_clear(&outNid); return res; } #ifdef UA_ENABLE_TYPEDESCRIPTION /***********************/ /* Jumptable Signature */ /***********************/ typedef struct UA_PrintElement { TAILQ_ENTRY(UA_PrintElement) next; size_t length; UA_Byte data[]; } UA_PrintOutput; typedef struct { size_t depth; TAILQ_HEAD(, UA_PrintElement) outputs; } UA_PrintContext; typedef UA_StatusCode (*UA_printSignature)(UA_PrintContext *ctx, const void *p, const UA_DataType *type); extern const UA_printSignature printJumpTable[UA_DATATYPEKINDS]; /********************/ /* Helper Functions */ /********************/ static UA_PrintOutput * UA_PrintContext_addOutput(UA_PrintContext *ctx, size_t length) { /* Protect against overlong output in pretty-printing */ if(length > 2<<16) return NULL; UA_PrintOutput *output = (UA_PrintOutput*)UA_malloc(sizeof(UA_PrintOutput) + length + 1); if(!output) return NULL; output->length = length; TAILQ_INSERT_TAIL(&ctx->outputs, output, next); return output; } static UA_StatusCode UA_PrintContext_addNewlineTabs(UA_PrintContext *ctx, size_t tabs) { UA_PrintOutput *out = UA_PrintContext_addOutput(ctx, tabs+1); if(!out) return UA_STATUSCODE_BADOUTOFMEMORY; out->data[0] = '\n'; for(size_t i = 1; i <= tabs; i++) out->data[i] = '\t'; return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_PrintContext_addName(UA_PrintContext *ctx, const char *name) { size_t nameLen = strlen(name); UA_PrintOutput *out = UA_PrintContext_addOutput(ctx, nameLen+2); if(!out) return UA_STATUSCODE_BADOUTOFMEMORY; memcpy(&out->data, name, nameLen); out->data[nameLen] = ':'; out->data[nameLen+1] = ' '; return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_PrintContext_addString(UA_PrintContext *ctx, const char *str) { size_t len = strlen(str); UA_PrintOutput *out = UA_PrintContext_addOutput(ctx, len); if(!out) return UA_STATUSCODE_BADOUTOFMEMORY; memcpy(&out->data, str, len); return UA_STATUSCODE_GOOD; } /*********************/ /* Printing Routines */ /*********************/ static UA_StatusCode printArray(UA_PrintContext *ctx, const void *p, const size_t length, const UA_DataType *type); static UA_StatusCode printBoolean(UA_PrintContext *ctx, const UA_Boolean *p, const UA_DataType *_) { if(*p) return UA_PrintContext_addString(ctx, "true"); return UA_PrintContext_addString(ctx, "false"); } static UA_StatusCode printSByte(UA_PrintContext *ctx, const UA_SByte *p, const UA_DataType *_) { char out[32]; UA_snprintf(out, 32, "%"PRIi8, *p); return UA_PrintContext_addString(ctx, out); } static UA_StatusCode printByte(UA_PrintContext *ctx, const UA_Byte *p, const UA_DataType *_) { char out[32]; UA_snprintf(out, 32, "%"PRIu8, *p); return UA_PrintContext_addString(ctx, out); } static UA_StatusCode printInt16(UA_PrintContext *ctx, const UA_Int16 *p, const UA_DataType *_) { char out[32]; UA_snprintf(out, 32, "%"PRIi16, *p); return UA_PrintContext_addString(ctx, out); } static UA_StatusCode printUInt16(UA_PrintContext *ctx, const UA_UInt16 *p, const UA_DataType *_) { char out[32]; UA_snprintf(out, 32, "%"PRIu16, *p); return UA_PrintContext_addString(ctx, out); } static UA_StatusCode printInt32(UA_PrintContext *ctx, const UA_Int32 *p, const UA_DataType *_) { char out[32]; UA_snprintf(out, 32, "%"PRIi32, *p); return UA_PrintContext_addString(ctx, out); } static UA_StatusCode printUInt32(UA_PrintContext *ctx, const UA_UInt32 *p, const UA_DataType *_) { char out[32]; UA_snprintf(out, 32, "%"PRIu32, *p); return UA_PrintContext_addString(ctx, out); } static UA_StatusCode printInt64(UA_PrintContext *ctx, const UA_Int64 *p, const UA_DataType *_) { char out[64]; UA_snprintf(out, 64, "%"PRIi64, *p); return UA_PrintContext_addString(ctx, out); } static UA_StatusCode printUInt64(UA_PrintContext *ctx, const UA_UInt64 *p, const UA_DataType *_) { char out[64]; UA_snprintf(out, 64, "%"PRIu64, *p); return UA_PrintContext_addString(ctx, out); } static UA_StatusCode printFloat(UA_PrintContext *ctx, const UA_Float *p, const UA_DataType *_) { char out[64]; UA_snprintf(out, 32, "%f", *p); return UA_PrintContext_addString(ctx, out); } static UA_StatusCode printDouble(UA_PrintContext *ctx, const UA_Double *p, const UA_DataType *_) { char out[64]; UA_snprintf(out, 64, "%lf", *p); return UA_PrintContext_addString(ctx, out); } static UA_StatusCode printStatusCode(UA_PrintContext *ctx, const UA_StatusCode *p, const UA_DataType *_) { return UA_PrintContext_addString(ctx, UA_StatusCode_name(*p)); } static UA_StatusCode printNodeId(UA_PrintContext *ctx, const UA_NodeId *p, const UA_DataType *_) { UA_String out; UA_String_init(&out); UA_StatusCode res = UA_NodeId_print(p, &out); if(res != UA_STATUSCODE_GOOD) return res; UA_PrintOutput *po = UA_PrintContext_addOutput(ctx, out.length); if(po) memcpy(po->data, out.data, out.length); else res = UA_STATUSCODE_BADOUTOFMEMORY; UA_String_clear(&out); return res; } static UA_StatusCode printExpandedNodeId(UA_PrintContext *ctx, const UA_ExpandedNodeId *p, const UA_DataType *_) { UA_String out; UA_String_init(&out); UA_StatusCode res = UA_ExpandedNodeId_print(p, &out); if(res != UA_STATUSCODE_GOOD) return res; UA_PrintOutput *po = UA_PrintContext_addOutput(ctx, out.length); if(!po) return UA_STATUSCODE_BADOUTOFMEMORY; memcpy(po->data, out.data, out.length); UA_String_clear(&out); return UA_STATUSCODE_GOOD; } static UA_StatusCode printDateTime(UA_PrintContext *ctx, const UA_DateTime *p, const UA_DataType *_) { UA_Int64 tOffset = UA_DateTime_localTimeUtcOffset(); UA_DateTimeStruct dts = UA_DateTime_toStruct(*p); char dateString[100]; UA_snprintf((char*)dateString, 100, "%04u-%02u-%02u %02u:%02u:%02u.%03u (UTC%+05d)", dts.year, dts.month, dts.day, dts.hour, dts.min, dts.sec, dts.milliSec, (int)(tOffset / UA_DATETIME_SEC / 36)); return UA_PrintContext_addString(ctx, dateString); } static UA_StatusCode printGuid(UA_PrintContext *ctx, const UA_Guid *p, const UA_DataType *_) { char tmp[100]; UA_snprintf(tmp, 100, UA_PRINTF_GUID_FORMAT, UA_PRINTF_GUID_DATA(*p)); return UA_PrintContext_addString(ctx, tmp); } static UA_StatusCode printString(UA_PrintContext *ctx, const UA_String *p, const UA_DataType *_) { if(!p->data) return UA_PrintContext_addString(ctx, "NullString"); UA_PrintOutput *out = UA_PrintContext_addOutput(ctx, p->length+2); if(!out) return UA_STATUSCODE_BADOUTOFMEMORY; UA_snprintf((char*)out->data, p->length+3, "\"%.*s\"", (int)p->length, p->data); return UA_STATUSCODE_GOOD; } static UA_StatusCode printByteString(UA_PrintContext *ctx, const UA_ByteString *p, const UA_DataType *_) { if(!p->data) return UA_PrintContext_addString(ctx, "NullByteString"); UA_String str = UA_BYTESTRING_NULL; UA_StatusCode res = UA_ByteString_toBase64(p, &str); if(res != UA_STATUSCODE_GOOD) return res; res = printString(ctx, &str, NULL); UA_String_clear(&str); return res; } static UA_StatusCode printQualifiedName(UA_PrintContext *ctx, const UA_QualifiedName *p, const UA_DataType *_) { UA_StatusCode retval = UA_STATUSCODE_GOOD; retval |= UA_PrintContext_addString(ctx, "{"); ctx->depth++; retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addName(ctx, "NamespaceIndex"); retval |= printUInt16(ctx, &p->namespaceIndex, NULL); retval |= UA_PrintContext_addString(ctx, ","); retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addName(ctx, "Name"); retval |= printString(ctx, &p->name, NULL); ctx->depth--; retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addString(ctx, "}"); return retval; } static UA_StatusCode printLocalizedText(UA_PrintContext *ctx, const UA_LocalizedText *p, const UA_DataType *_) { UA_StatusCode retval = UA_STATUSCODE_GOOD; retval |= UA_PrintContext_addString(ctx, "{"); ctx->depth++; retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addName(ctx, "Locale"); retval |= printString(ctx, &p->locale, NULL); retval |= UA_PrintContext_addString(ctx, ","); retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addName(ctx, "Text"); retval |= printString(ctx, &p->text, NULL); ctx->depth--; retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addString(ctx, "}"); return retval; } static UA_StatusCode printVariant(UA_PrintContext *ctx, const UA_Variant *p, const UA_DataType *_) { if(!p->type) return UA_PrintContext_addString(ctx, "NullVariant"); UA_StatusCode retval = UA_STATUSCODE_GOOD; retval |= UA_PrintContext_addString(ctx, "{"); ctx->depth++; retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addName(ctx, "DataType"); retval |= UA_PrintContext_addString(ctx, p->type->typeName); retval |= UA_PrintContext_addString(ctx, ","); retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addName(ctx, "Value"); if(UA_Variant_isScalar(p)) retval |= printJumpTable[p->type->typeKind](ctx, p->data, p->type); else retval |= printArray(ctx, p->data, p->arrayLength, p->type); if(p->arrayDimensionsSize > 0) { retval |= UA_PrintContext_addString(ctx, ","); retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addName(ctx, "ArrayDimensions"); retval |= printArray(ctx, p->arrayDimensions, p->arrayDimensionsSize, &UA_TYPES[UA_TYPES_UINT32]); } ctx->depth--; retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addString(ctx, "}"); return retval; } static UA_StatusCode printExtensionObject(UA_PrintContext *ctx, const UA_ExtensionObject*p, const UA_DataType *_) { UA_StatusCode res = UA_STATUSCODE_GOOD; switch(p->encoding) { case UA_EXTENSIONOBJECT_ENCODED_NOBODY: return UA_PrintContext_addString(ctx, "ExtensionObject(No Body)"); case UA_EXTENSIONOBJECT_ENCODED_BYTESTRING: res |= UA_PrintContext_addString(ctx, "ExtensionObject(Binary Encoded) {"); ctx->depth++; res |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); res |= UA_PrintContext_addName(ctx, "DataType"); res |= printNodeId(ctx, &p->content.encoded.typeId, NULL); res |= UA_PrintContext_addString(ctx, ","); res |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); res |= UA_PrintContext_addName(ctx, "Body"); res |= printByteString(ctx, &p->content.encoded.body, NULL); ctx->depth--; res |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); res |= UA_PrintContext_addName(ctx, "}"); break; case UA_EXTENSIONOBJECT_ENCODED_XML: res |= UA_PrintContext_addString(ctx, "ExtensionObject(XML Encoded) {"); ctx->depth++; res |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); res |= UA_PrintContext_addName(ctx, "DataType"); res |= printNodeId(ctx, &p->content.encoded.typeId, NULL); res |= UA_PrintContext_addString(ctx, ","); res |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); res |= UA_PrintContext_addName(ctx, "Body"); res |= printString(ctx, (const UA_String*)&p->content.encoded.body, NULL); ctx->depth--; res |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); res |= UA_PrintContext_addName(ctx, "}"); break; case UA_EXTENSIONOBJECT_DECODED: case UA_EXTENSIONOBJECT_DECODED_NODELETE: res |= UA_PrintContext_addString(ctx, "ExtensionObject {"); ctx->depth++; res |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); res |= UA_PrintContext_addName(ctx, "DataType"); res |= UA_PrintContext_addString(ctx, p->content.decoded.type->typeName); res |= UA_PrintContext_addString(ctx, ","); res |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); res |= UA_PrintContext_addName(ctx, "Body"); res |= printJumpTable[p->content.decoded.type->typeKind](ctx, p->content.decoded.data, p->content.decoded.type); ctx->depth--; res |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); res |= UA_PrintContext_addName(ctx, "}"); break; default: res = UA_STATUSCODE_BADINTERNALERROR; break; } return res; } static UA_StatusCode printDataValue(UA_PrintContext *ctx, const UA_DataValue *p, const UA_DataType *_) { UA_StatusCode retval = UA_STATUSCODE_GOOD; retval |= UA_PrintContext_addString(ctx, "{"); ctx->depth++; UA_Boolean comma = false; if(p->hasValue) { retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addName(ctx, "Value"); retval |= printVariant(ctx, &p->value, NULL); comma = true; } if(p->hasStatus) { if(comma) retval |= UA_PrintContext_addString(ctx, ","); retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addName(ctx, "Status"); retval |= printStatusCode(ctx, &p->status, NULL); comma = true; } if(p->hasSourceTimestamp) { if(comma) retval |= UA_PrintContext_addString(ctx, ","); retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addName(ctx, "SourceTimestamp"); retval |= printDateTime(ctx, &p->sourceTimestamp, NULL); comma = true; } if(p->hasSourcePicoseconds) { if(comma) retval |= UA_PrintContext_addString(ctx, ","); retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addName(ctx, "SourcePicoseconds"); retval |= printUInt16(ctx, &p->sourcePicoseconds, NULL); comma = true; } if(p->hasServerTimestamp) { if(comma) retval |= UA_PrintContext_addString(ctx, ","); retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addName(ctx, "ServerTimestamp"); retval |= printDateTime(ctx, &p->serverTimestamp, NULL); comma = true; } if(p->hasServerPicoseconds) { if(comma) retval |= UA_PrintContext_addString(ctx, ","); retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addName(ctx, "ServerPicoseconds"); retval |= printUInt16(ctx, &p->serverPicoseconds, NULL); comma = true; } ctx->depth--; if(comma) { retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addString(ctx, "}"); } else { retval |= UA_PrintContext_addString(ctx, " }"); } return retval; } static UA_StatusCode printDiagnosticInfo(UA_PrintContext *ctx, const UA_DiagnosticInfo *p, const UA_DataType *_) { UA_StatusCode retval = UA_STATUSCODE_GOOD; retval |= UA_PrintContext_addString(ctx, "{"); ctx->depth++; UA_Boolean comma = false; if(p->hasSymbolicId) { retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addName(ctx, "SymbolicId"); retval |= printInt32(ctx, &p->symbolicId, NULL); comma = true; } if(p->hasNamespaceUri) { if(comma) retval |= UA_PrintContext_addString(ctx, ","); retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addName(ctx, "NamespaceUri"); retval |= printInt32(ctx, &p->namespaceUri, NULL); comma = true; } if(p->hasLocalizedText) { if(comma) retval |= UA_PrintContext_addString(ctx, ","); retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addName(ctx, "LocalizedText"); retval |= printInt32(ctx, &p->localizedText, NULL); comma = true; } if(p->hasLocale) { if(comma) retval |= UA_PrintContext_addString(ctx, ","); retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addName(ctx, "Locale"); retval |= printInt32(ctx, &p->locale, NULL); comma = true; } if(p->hasAdditionalInfo) { if(comma) retval |= UA_PrintContext_addString(ctx, ","); retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addName(ctx, "AdditionalInfo"); retval |= printString(ctx, &p->additionalInfo, NULL); comma = true; } if(p->hasInnerStatusCode) { if(comma) retval |= UA_PrintContext_addString(ctx, ","); retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addName(ctx, "InnerStatusCode"); retval |= printStatusCode(ctx, &p->innerStatusCode, NULL); comma = true; } if(p->hasInnerDiagnosticInfo) { if(comma) retval |= UA_PrintContext_addString(ctx, ","); retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addName(ctx, "InnerDiagnosticInfo"); retval |= printDiagnosticInfo(ctx, p->innerDiagnosticInfo, NULL); comma = true; } ctx->depth--; if(comma) { retval |= UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addString(ctx, "}"); } else { retval |= UA_PrintContext_addString(ctx, " }"); } return retval; } static UA_StatusCode printArray(UA_PrintContext *ctx, const void *p, const size_t length, const UA_DataType *type) { UA_StatusCode retval = UA_STATUSCODE_GOOD; if(!p) { retval |= UA_PrintContext_addString(ctx, "Array(-1, "); retval |= UA_PrintContext_addString(ctx, type->typeName); retval |= UA_PrintContext_addString(ctx, ")"); return retval; } UA_UInt32 length32 = (UA_UInt32)length; retval |= UA_PrintContext_addString(ctx, "Array("); retval |= printUInt32(ctx, &length32, NULL); retval |= UA_PrintContext_addString(ctx, ", "); retval |= UA_PrintContext_addString(ctx, type->typeName); retval |= UA_PrintContext_addString(ctx, ") {"); ctx->depth++; uintptr_t target = (uintptr_t)p; for(UA_UInt32 i = 0; i < length; i++) { UA_PrintContext_addNewlineTabs(ctx, ctx->depth); printUInt32(ctx, &i, NULL); retval |= UA_PrintContext_addString(ctx, ": "); printJumpTable[type->typeKind](ctx, (const void*)target, type); if(i < length - 1) retval |= UA_PrintContext_addString(ctx, ","); target += type->memSize; } ctx->depth--; UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addString(ctx, "}"); return retval; } static UA_StatusCode printStructure(UA_PrintContext *ctx, const void *p, const UA_DataType *type) { UA_StatusCode retval = UA_STATUSCODE_GOOD; uintptr_t ptrs = (uintptr_t)p; retval |= UA_PrintContext_addString(ctx, "{"); ctx->depth++; for(size_t i = 0; i < type->membersSize; ++i) { UA_PrintContext_addNewlineTabs(ctx, ctx->depth); const UA_DataTypeMember *m = &type->members[i]; const UA_DataType *mt = m->memberType; ptrs += m->padding; retval |= UA_PrintContext_addName(ctx, m->memberName); if(!m->isArray) { retval |= printJumpTable[mt->typeKind](ctx, (const void *)ptrs, mt); ptrs += mt->memSize; } else { const size_t size = *((const size_t*)ptrs); ptrs += sizeof(size_t); retval |= printArray(ctx, *(void* const*)ptrs, size, mt); ptrs += sizeof(void*); } if(i < (size_t)(type->membersSize - 1)) retval |= UA_PrintContext_addString(ctx, ","); } ctx->depth--; UA_PrintContext_addNewlineTabs(ctx, ctx->depth); retval |= UA_PrintContext_addString(ctx, "}"); return retval; } static UA_StatusCode printNotImplemented(UA_PrintContext *ctx, const void *p, const UA_DataType *type) { UA_StatusCode res = UA_STATUSCODE_GOOD; res |= UA_PrintContext_addString(ctx, type->typeName); res |= UA_PrintContext_addString(ctx, " (Printing Not Implemented)"); return res; } const UA_printSignature printJumpTable[UA_DATATYPEKINDS] = { (UA_printSignature)printBoolean, (UA_printSignature)printSByte, (UA_printSignature)printByte, (UA_printSignature)printInt16, (UA_printSignature)printUInt16, (UA_printSignature)printInt32, (UA_printSignature)printUInt32, (UA_printSignature)printInt64, (UA_printSignature)printUInt64, (UA_printSignature)printFloat, (UA_printSignature)printDouble, (UA_printSignature)printString, (UA_printSignature)printDateTime, (UA_printSignature)printGuid, (UA_printSignature)printByteString, (UA_printSignature)printString, /* XmlElement */ (UA_printSignature)printNodeId, (UA_printSignature)printExpandedNodeId, (UA_printSignature)printStatusCode, (UA_printSignature)printQualifiedName, (UA_printSignature)printLocalizedText, (UA_printSignature)printExtensionObject, (UA_printSignature)printDataValue, (UA_printSignature)printVariant, (UA_printSignature)printDiagnosticInfo, (UA_printSignature)printNotImplemented, /* Decimal */ (UA_printSignature)printUInt32, /* Enumeration */ (UA_printSignature)printStructure, (UA_printSignature)printNotImplemented, /* Structure with Optional Fields */ (UA_printSignature)printNotImplemented, /* Union */ (UA_printSignature)printNotImplemented /* BitfieldCluster*/ }; UA_StatusCode UA_print(const void *p, const UA_DataType *type, UA_String *output) { UA_PrintContext ctx; ctx.depth = 0; TAILQ_INIT(&ctx.outputs); /* Encode */ UA_StatusCode retval = printJumpTable[type->typeKind](&ctx, p, type); /* Allocate memory for the output */ if(retval == UA_STATUSCODE_GOOD) { size_t total = 0; UA_PrintOutput *out; TAILQ_FOREACH(out, &ctx.outputs, next) total += out->length; retval = UA_ByteString_allocBuffer((UA_String*)output, total); } /* Write the output */ if(retval == UA_STATUSCODE_GOOD) { size_t pos = 0; UA_PrintOutput *out; TAILQ_FOREACH(out, &ctx.outputs, next) { memcpy(&output->data[pos], out->data, out->length); pos += out->length; } } /* Free the context */ UA_PrintOutput *o, *o2; TAILQ_FOREACH_SAFE(o, &ctx.outputs, next, o2) { TAILQ_REMOVE(&ctx.outputs, o, next); UA_free(o); } return retval; } #endif /* UA_ENABLE_TYPEDESCRIPTION */ /**** amalgamated original file "/build/src_generated/open62541/types_generated.c" ****/ /********************************** * Autogenerated -- do not modify * **********************************/ /* 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 /* KeyValuePair */ static UA_DataTypeMember KeyValuePair_members[2] = { { UA_TYPENAME("Key") /* .memberName */ &UA_TYPES[UA_TYPES_QUALIFIEDNAME], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Value") /* .memberName */ &UA_TYPES[UA_TYPES_VARIANT], /* .memberType */ offsetof(UA_KeyValuePair, value) - offsetof(UA_KeyValuePair, key) - sizeof(UA_QualifiedName), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* NodeClass */ #define NodeClass_members NULL /* StructureType */ #define StructureType_members NULL /* StructureField */ static UA_DataTypeMember StructureField_members[7] = { { UA_TYPENAME("Name") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Description") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_StructureField, description) - offsetof(UA_StructureField, name) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DataType") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ offsetof(UA_StructureField, dataType) - offsetof(UA_StructureField, description) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ValueRank") /* .memberName */ &UA_TYPES[UA_TYPES_INT32], /* .memberType */ offsetof(UA_StructureField, valueRank) - offsetof(UA_StructureField, dataType) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ArrayDimensions") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_StructureField, arrayDimensionsSize) - offsetof(UA_StructureField, valueRank) - sizeof(UA_Int32), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("MaxStringLength") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_StructureField, maxStringLength) - offsetof(UA_StructureField, arrayDimensions) - sizeof(void *), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("IsOptional") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_StructureField, isOptional) - offsetof(UA_StructureField, maxStringLength) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* StructureDefinition */ static UA_DataTypeMember StructureDefinition_members[4] = { { UA_TYPENAME("DefaultEncodingId") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("BaseDataType") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ offsetof(UA_StructureDefinition, baseDataType) - offsetof(UA_StructureDefinition, defaultEncodingId) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("StructureType") /* .memberName */ &UA_TYPES[UA_TYPES_STRUCTURETYPE], /* .memberType */ offsetof(UA_StructureDefinition, structureType) - offsetof(UA_StructureDefinition, baseDataType) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Fields") /* .memberName */ &UA_TYPES[UA_TYPES_STRUCTUREFIELD], /* .memberType */ offsetof(UA_StructureDefinition, fieldsSize) - offsetof(UA_StructureDefinition, structureType) - sizeof(UA_StructureType), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* Argument */ static UA_DataTypeMember Argument_members[5] = { { UA_TYPENAME("Name") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DataType") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ offsetof(UA_Argument, dataType) - offsetof(UA_Argument, name) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ValueRank") /* .memberName */ &UA_TYPES[UA_TYPES_INT32], /* .memberType */ offsetof(UA_Argument, valueRank) - offsetof(UA_Argument, dataType) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ArrayDimensions") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_Argument, arrayDimensionsSize) - offsetof(UA_Argument, valueRank) - sizeof(UA_Int32), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Description") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_Argument, description) - offsetof(UA_Argument, arrayDimensions) - sizeof(void *), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* EnumValueType */ static UA_DataTypeMember EnumValueType_members[3] = { { UA_TYPENAME("Value") /* .memberName */ &UA_TYPES[UA_TYPES_INT64], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DisplayName") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_EnumValueType, displayName) - offsetof(UA_EnumValueType, value) - sizeof(UA_Int64), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Description") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_EnumValueType, description) - offsetof(UA_EnumValueType, displayName) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* EnumField */ static UA_DataTypeMember EnumField_members[4] = { { UA_TYPENAME("Value") /* .memberName */ &UA_TYPES[UA_TYPES_INT64], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DisplayName") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_EnumField, displayName) - offsetof(UA_EnumField, value) - sizeof(UA_Int64), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Description") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_EnumField, description) - offsetof(UA_EnumField, displayName) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Name") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_EnumField, name) - offsetof(UA_EnumField, description) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* Duration */ #define Duration_members NULL /* UtcTime */ #define UtcTime_members NULL /* LocaleId */ #define LocaleId_members NULL /* TimeZoneDataType */ static UA_DataTypeMember TimeZoneDataType_members[2] = { { UA_TYPENAME("Offset") /* .memberName */ &UA_TYPES[UA_TYPES_INT16], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DaylightSavingInOffset") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_TimeZoneDataType, daylightSavingInOffset) - offsetof(UA_TimeZoneDataType, offset) - sizeof(UA_Int16), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* ApplicationType */ #define ApplicationType_members NULL /* ApplicationDescription */ static UA_DataTypeMember ApplicationDescription_members[7] = { { UA_TYPENAME("ApplicationUri") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ProductUri") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_ApplicationDescription, productUri) - offsetof(UA_ApplicationDescription, applicationUri) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ApplicationName") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_ApplicationDescription, applicationName) - offsetof(UA_ApplicationDescription, productUri) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ApplicationType") /* .memberName */ &UA_TYPES[UA_TYPES_APPLICATIONTYPE], /* .memberType */ offsetof(UA_ApplicationDescription, applicationType) - offsetof(UA_ApplicationDescription, applicationName) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("GatewayServerUri") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_ApplicationDescription, gatewayServerUri) - offsetof(UA_ApplicationDescription, applicationType) - sizeof(UA_ApplicationType), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiscoveryProfileUri") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_ApplicationDescription, discoveryProfileUri) - offsetof(UA_ApplicationDescription, gatewayServerUri) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiscoveryUrls") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_ApplicationDescription, discoveryUrlsSize) - offsetof(UA_ApplicationDescription, discoveryProfileUri) - sizeof(UA_String), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* RequestHeader */ static UA_DataTypeMember RequestHeader_members[7] = { { UA_TYPENAME("AuthenticationToken") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Timestamp") /* .memberName */ &UA_TYPES[UA_TYPES_DATETIME], /* .memberType */ offsetof(UA_RequestHeader, timestamp) - offsetof(UA_RequestHeader, authenticationToken) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RequestHandle") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_RequestHeader, requestHandle) - offsetof(UA_RequestHeader, timestamp) - sizeof(UA_DateTime), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ReturnDiagnostics") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_RequestHeader, returnDiagnostics) - offsetof(UA_RequestHeader, requestHandle) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("AuditEntryId") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_RequestHeader, auditEntryId) - offsetof(UA_RequestHeader, returnDiagnostics) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("TimeoutHint") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_RequestHeader, timeoutHint) - offsetof(UA_RequestHeader, auditEntryId) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("AdditionalHeader") /* .memberName */ &UA_TYPES[UA_TYPES_EXTENSIONOBJECT], /* .memberType */ offsetof(UA_RequestHeader, additionalHeader) - offsetof(UA_RequestHeader, timeoutHint) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* ResponseHeader */ static UA_DataTypeMember ResponseHeader_members[6] = { { UA_TYPENAME("Timestamp") /* .memberName */ &UA_TYPES[UA_TYPES_DATETIME], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RequestHandle") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ResponseHeader, requestHandle) - offsetof(UA_ResponseHeader, timestamp) - sizeof(UA_DateTime), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ServiceResult") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ offsetof(UA_ResponseHeader, serviceResult) - offsetof(UA_ResponseHeader, requestHandle) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ServiceDiagnostics") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_ResponseHeader, serviceDiagnostics) - offsetof(UA_ResponseHeader, serviceResult) - sizeof(UA_StatusCode), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("StringTable") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_ResponseHeader, stringTableSize) - offsetof(UA_ResponseHeader, serviceDiagnostics) - sizeof(UA_DiagnosticInfo), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("AdditionalHeader") /* .memberName */ &UA_TYPES[UA_TYPES_EXTENSIONOBJECT], /* .memberType */ offsetof(UA_ResponseHeader, additionalHeader) - offsetof(UA_ResponseHeader, stringTable) - sizeof(void *), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* ServiceFault */ static UA_DataTypeMember ServiceFault_members[1] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* FindServersRequest */ static UA_DataTypeMember FindServersRequest_members[4] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("EndpointUrl") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_FindServersRequest, endpointUrl) - offsetof(UA_FindServersRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("LocaleIds") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_FindServersRequest, localeIdsSize) - offsetof(UA_FindServersRequest, endpointUrl) - sizeof(UA_String), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ServerUris") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_FindServersRequest, serverUrisSize) - offsetof(UA_FindServersRequest, localeIds) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* FindServersResponse */ static UA_DataTypeMember FindServersResponse_members[2] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Servers") /* .memberName */ &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION], /* .memberType */ offsetof(UA_FindServersResponse, serversSize) - offsetof(UA_FindServersResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* MessageSecurityMode */ #define MessageSecurityMode_members NULL /* UserTokenType */ #define UserTokenType_members NULL /* UserTokenPolicy */ static UA_DataTypeMember UserTokenPolicy_members[5] = { { UA_TYPENAME("PolicyId") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("TokenType") /* .memberName */ &UA_TYPES[UA_TYPES_USERTOKENTYPE], /* .memberType */ offsetof(UA_UserTokenPolicy, tokenType) - offsetof(UA_UserTokenPolicy, policyId) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("IssuedTokenType") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_UserTokenPolicy, issuedTokenType) - offsetof(UA_UserTokenPolicy, tokenType) - sizeof(UA_UserTokenType), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("IssuerEndpointUrl") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_UserTokenPolicy, issuerEndpointUrl) - offsetof(UA_UserTokenPolicy, issuedTokenType) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SecurityPolicyUri") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_UserTokenPolicy, securityPolicyUri) - offsetof(UA_UserTokenPolicy, issuerEndpointUrl) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* EndpointDescription */ static UA_DataTypeMember EndpointDescription_members[8] = { { UA_TYPENAME("EndpointUrl") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Server") /* .memberName */ &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION], /* .memberType */ offsetof(UA_EndpointDescription, server) - offsetof(UA_EndpointDescription, endpointUrl) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ServerCertificate") /* .memberName */ &UA_TYPES[UA_TYPES_BYTESTRING], /* .memberType */ offsetof(UA_EndpointDescription, serverCertificate) - offsetof(UA_EndpointDescription, server) - sizeof(UA_ApplicationDescription), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SecurityMode") /* .memberName */ &UA_TYPES[UA_TYPES_MESSAGESECURITYMODE], /* .memberType */ offsetof(UA_EndpointDescription, securityMode) - offsetof(UA_EndpointDescription, serverCertificate) - sizeof(UA_ByteString), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SecurityPolicyUri") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_EndpointDescription, securityPolicyUri) - offsetof(UA_EndpointDescription, securityMode) - sizeof(UA_MessageSecurityMode), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("UserIdentityTokens") /* .memberName */ &UA_TYPES[UA_TYPES_USERTOKENPOLICY], /* .memberType */ offsetof(UA_EndpointDescription, userIdentityTokensSize) - offsetof(UA_EndpointDescription, securityPolicyUri) - sizeof(UA_String), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("TransportProfileUri") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_EndpointDescription, transportProfileUri) - offsetof(UA_EndpointDescription, userIdentityTokens) - sizeof(void *), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SecurityLevel") /* .memberName */ &UA_TYPES[UA_TYPES_BYTE], /* .memberType */ offsetof(UA_EndpointDescription, securityLevel) - offsetof(UA_EndpointDescription, transportProfileUri) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* GetEndpointsRequest */ static UA_DataTypeMember GetEndpointsRequest_members[4] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("EndpointUrl") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_GetEndpointsRequest, endpointUrl) - offsetof(UA_GetEndpointsRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("LocaleIds") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_GetEndpointsRequest, localeIdsSize) - offsetof(UA_GetEndpointsRequest, endpointUrl) - sizeof(UA_String), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ProfileUris") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_GetEndpointsRequest, profileUrisSize) - offsetof(UA_GetEndpointsRequest, localeIds) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* GetEndpointsResponse */ static UA_DataTypeMember GetEndpointsResponse_members[2] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Endpoints") /* .memberName */ &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION], /* .memberType */ offsetof(UA_GetEndpointsResponse, endpointsSize) - offsetof(UA_GetEndpointsResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* SecurityTokenRequestType */ #define SecurityTokenRequestType_members NULL /* ChannelSecurityToken */ static UA_DataTypeMember ChannelSecurityToken_members[4] = { { UA_TYPENAME("ChannelId") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("TokenId") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ChannelSecurityToken, tokenId) - offsetof(UA_ChannelSecurityToken, channelId) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("CreatedAt") /* .memberName */ &UA_TYPES[UA_TYPES_DATETIME], /* .memberType */ offsetof(UA_ChannelSecurityToken, createdAt) - offsetof(UA_ChannelSecurityToken, tokenId) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RevisedLifetime") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ChannelSecurityToken, revisedLifetime) - offsetof(UA_ChannelSecurityToken, createdAt) - sizeof(UA_DateTime), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* OpenSecureChannelRequest */ static UA_DataTypeMember OpenSecureChannelRequest_members[6] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ClientProtocolVersion") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_OpenSecureChannelRequest, clientProtocolVersion) - offsetof(UA_OpenSecureChannelRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RequestType") /* .memberName */ &UA_TYPES[UA_TYPES_SECURITYTOKENREQUESTTYPE], /* .memberType */ offsetof(UA_OpenSecureChannelRequest, requestType) - offsetof(UA_OpenSecureChannelRequest, clientProtocolVersion) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SecurityMode") /* .memberName */ &UA_TYPES[UA_TYPES_MESSAGESECURITYMODE], /* .memberType */ offsetof(UA_OpenSecureChannelRequest, securityMode) - offsetof(UA_OpenSecureChannelRequest, requestType) - sizeof(UA_SecurityTokenRequestType), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ClientNonce") /* .memberName */ &UA_TYPES[UA_TYPES_BYTESTRING], /* .memberType */ offsetof(UA_OpenSecureChannelRequest, clientNonce) - offsetof(UA_OpenSecureChannelRequest, securityMode) - sizeof(UA_MessageSecurityMode), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RequestedLifetime") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_OpenSecureChannelRequest, requestedLifetime) - offsetof(UA_OpenSecureChannelRequest, clientNonce) - sizeof(UA_ByteString), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* OpenSecureChannelResponse */ static UA_DataTypeMember OpenSecureChannelResponse_members[4] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ServerProtocolVersion") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_OpenSecureChannelResponse, serverProtocolVersion) - offsetof(UA_OpenSecureChannelResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SecurityToken") /* .memberName */ &UA_TYPES[UA_TYPES_CHANNELSECURITYTOKEN], /* .memberType */ offsetof(UA_OpenSecureChannelResponse, securityToken) - offsetof(UA_OpenSecureChannelResponse, serverProtocolVersion) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ServerNonce") /* .memberName */ &UA_TYPES[UA_TYPES_BYTESTRING], /* .memberType */ offsetof(UA_OpenSecureChannelResponse, serverNonce) - offsetof(UA_OpenSecureChannelResponse, securityToken) - sizeof(UA_ChannelSecurityToken), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* CloseSecureChannelRequest */ static UA_DataTypeMember CloseSecureChannelRequest_members[1] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* CloseSecureChannelResponse */ static UA_DataTypeMember CloseSecureChannelResponse_members[1] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* SignedSoftwareCertificate */ static UA_DataTypeMember SignedSoftwareCertificate_members[2] = { { UA_TYPENAME("CertificateData") /* .memberName */ &UA_TYPES[UA_TYPES_BYTESTRING], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Signature") /* .memberName */ &UA_TYPES[UA_TYPES_BYTESTRING], /* .memberType */ offsetof(UA_SignedSoftwareCertificate, signature) - offsetof(UA_SignedSoftwareCertificate, certificateData) - sizeof(UA_ByteString), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* SignatureData */ static UA_DataTypeMember SignatureData_members[2] = { { UA_TYPENAME("Algorithm") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Signature") /* .memberName */ &UA_TYPES[UA_TYPES_BYTESTRING], /* .memberType */ offsetof(UA_SignatureData, signature) - offsetof(UA_SignatureData, algorithm) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* CreateSessionRequest */ static UA_DataTypeMember CreateSessionRequest_members[9] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ClientDescription") /* .memberName */ &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION], /* .memberType */ offsetof(UA_CreateSessionRequest, clientDescription) - offsetof(UA_CreateSessionRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ServerUri") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_CreateSessionRequest, serverUri) - offsetof(UA_CreateSessionRequest, clientDescription) - sizeof(UA_ApplicationDescription), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("EndpointUrl") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_CreateSessionRequest, endpointUrl) - offsetof(UA_CreateSessionRequest, serverUri) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SessionName") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_CreateSessionRequest, sessionName) - offsetof(UA_CreateSessionRequest, endpointUrl) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ClientNonce") /* .memberName */ &UA_TYPES[UA_TYPES_BYTESTRING], /* .memberType */ offsetof(UA_CreateSessionRequest, clientNonce) - offsetof(UA_CreateSessionRequest, sessionName) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ClientCertificate") /* .memberName */ &UA_TYPES[UA_TYPES_BYTESTRING], /* .memberType */ offsetof(UA_CreateSessionRequest, clientCertificate) - offsetof(UA_CreateSessionRequest, clientNonce) - sizeof(UA_ByteString), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RequestedSessionTimeout") /* .memberName */ &UA_TYPES[UA_TYPES_DOUBLE], /* .memberType */ offsetof(UA_CreateSessionRequest, requestedSessionTimeout) - offsetof(UA_CreateSessionRequest, clientCertificate) - sizeof(UA_ByteString), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("MaxResponseMessageSize") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_CreateSessionRequest, maxResponseMessageSize) - offsetof(UA_CreateSessionRequest, requestedSessionTimeout) - sizeof(UA_Double), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* CreateSessionResponse */ static UA_DataTypeMember CreateSessionResponse_members[10] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SessionId") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ offsetof(UA_CreateSessionResponse, sessionId) - offsetof(UA_CreateSessionResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("AuthenticationToken") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ offsetof(UA_CreateSessionResponse, authenticationToken) - offsetof(UA_CreateSessionResponse, sessionId) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RevisedSessionTimeout") /* .memberName */ &UA_TYPES[UA_TYPES_DOUBLE], /* .memberType */ offsetof(UA_CreateSessionResponse, revisedSessionTimeout) - offsetof(UA_CreateSessionResponse, authenticationToken) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ServerNonce") /* .memberName */ &UA_TYPES[UA_TYPES_BYTESTRING], /* .memberType */ offsetof(UA_CreateSessionResponse, serverNonce) - offsetof(UA_CreateSessionResponse, revisedSessionTimeout) - sizeof(UA_Double), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ServerCertificate") /* .memberName */ &UA_TYPES[UA_TYPES_BYTESTRING], /* .memberType */ offsetof(UA_CreateSessionResponse, serverCertificate) - offsetof(UA_CreateSessionResponse, serverNonce) - sizeof(UA_ByteString), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ServerEndpoints") /* .memberName */ &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION], /* .memberType */ offsetof(UA_CreateSessionResponse, serverEndpointsSize) - offsetof(UA_CreateSessionResponse, serverCertificate) - sizeof(UA_ByteString), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ServerSoftwareCertificates") /* .memberName */ &UA_TYPES[UA_TYPES_SIGNEDSOFTWARECERTIFICATE], /* .memberType */ offsetof(UA_CreateSessionResponse, serverSoftwareCertificatesSize) - offsetof(UA_CreateSessionResponse, serverEndpoints) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ServerSignature") /* .memberName */ &UA_TYPES[UA_TYPES_SIGNATUREDATA], /* .memberType */ offsetof(UA_CreateSessionResponse, serverSignature) - offsetof(UA_CreateSessionResponse, serverSoftwareCertificates) - sizeof(void *), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("MaxRequestMessageSize") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_CreateSessionResponse, maxRequestMessageSize) - offsetof(UA_CreateSessionResponse, serverSignature) - sizeof(UA_SignatureData), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* UserIdentityToken */ static UA_DataTypeMember UserIdentityToken_members[1] = { { UA_TYPENAME("PolicyId") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* AnonymousIdentityToken */ static UA_DataTypeMember AnonymousIdentityToken_members[1] = { { UA_TYPENAME("PolicyId") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* UserNameIdentityToken */ static UA_DataTypeMember UserNameIdentityToken_members[4] = { { UA_TYPENAME("PolicyId") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("UserName") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_UserNameIdentityToken, userName) - offsetof(UA_UserNameIdentityToken, policyId) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Password") /* .memberName */ &UA_TYPES[UA_TYPES_BYTESTRING], /* .memberType */ offsetof(UA_UserNameIdentityToken, password) - offsetof(UA_UserNameIdentityToken, userName) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("EncryptionAlgorithm") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_UserNameIdentityToken, encryptionAlgorithm) - offsetof(UA_UserNameIdentityToken, password) - sizeof(UA_ByteString), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* X509IdentityToken */ static UA_DataTypeMember X509IdentityToken_members[2] = { { UA_TYPENAME("PolicyId") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("CertificateData") /* .memberName */ &UA_TYPES[UA_TYPES_BYTESTRING], /* .memberType */ offsetof(UA_X509IdentityToken, certificateData) - offsetof(UA_X509IdentityToken, policyId) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* IssuedIdentityToken */ static UA_DataTypeMember IssuedIdentityToken_members[3] = { { UA_TYPENAME("PolicyId") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("TokenData") /* .memberName */ &UA_TYPES[UA_TYPES_BYTESTRING], /* .memberType */ offsetof(UA_IssuedIdentityToken, tokenData) - offsetof(UA_IssuedIdentityToken, policyId) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("EncryptionAlgorithm") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_IssuedIdentityToken, encryptionAlgorithm) - offsetof(UA_IssuedIdentityToken, tokenData) - sizeof(UA_ByteString), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* ActivateSessionRequest */ static UA_DataTypeMember ActivateSessionRequest_members[6] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ClientSignature") /* .memberName */ &UA_TYPES[UA_TYPES_SIGNATUREDATA], /* .memberType */ offsetof(UA_ActivateSessionRequest, clientSignature) - offsetof(UA_ActivateSessionRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ClientSoftwareCertificates") /* .memberName */ &UA_TYPES[UA_TYPES_SIGNEDSOFTWARECERTIFICATE], /* .memberType */ offsetof(UA_ActivateSessionRequest, clientSoftwareCertificatesSize) - offsetof(UA_ActivateSessionRequest, clientSignature) - sizeof(UA_SignatureData), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("LocaleIds") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_ActivateSessionRequest, localeIdsSize) - offsetof(UA_ActivateSessionRequest, clientSoftwareCertificates) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("UserIdentityToken") /* .memberName */ &UA_TYPES[UA_TYPES_EXTENSIONOBJECT], /* .memberType */ offsetof(UA_ActivateSessionRequest, userIdentityToken) - offsetof(UA_ActivateSessionRequest, localeIds) - sizeof(void *), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("UserTokenSignature") /* .memberName */ &UA_TYPES[UA_TYPES_SIGNATUREDATA], /* .memberType */ offsetof(UA_ActivateSessionRequest, userTokenSignature) - offsetof(UA_ActivateSessionRequest, userIdentityToken) - sizeof(UA_ExtensionObject), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* ActivateSessionResponse */ static UA_DataTypeMember ActivateSessionResponse_members[4] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ServerNonce") /* .memberName */ &UA_TYPES[UA_TYPES_BYTESTRING], /* .memberType */ offsetof(UA_ActivateSessionResponse, serverNonce) - offsetof(UA_ActivateSessionResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Results") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ offsetof(UA_ActivateSessionResponse, resultsSize) - offsetof(UA_ActivateSessionResponse, serverNonce) - sizeof(UA_ByteString), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_ActivateSessionResponse, diagnosticInfosSize) - offsetof(UA_ActivateSessionResponse, results) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* CloseSessionRequest */ static UA_DataTypeMember CloseSessionRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DeleteSubscriptions") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_CloseSessionRequest, deleteSubscriptions) - offsetof(UA_CloseSessionRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* CloseSessionResponse */ static UA_DataTypeMember CloseSessionResponse_members[1] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* NodeAttributesMask */ #define NodeAttributesMask_members NULL /* NodeAttributes */ static UA_DataTypeMember NodeAttributes_members[5] = { { UA_TYPENAME("SpecifiedAttributes") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DisplayName") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_NodeAttributes, displayName) - offsetof(UA_NodeAttributes, specifiedAttributes) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Description") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_NodeAttributes, description) - offsetof(UA_NodeAttributes, displayName) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("WriteMask") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_NodeAttributes, writeMask) - offsetof(UA_NodeAttributes, description) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("UserWriteMask") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_NodeAttributes, userWriteMask) - offsetof(UA_NodeAttributes, writeMask) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* ObjectAttributes */ static UA_DataTypeMember ObjectAttributes_members[6] = { { UA_TYPENAME("SpecifiedAttributes") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DisplayName") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_ObjectAttributes, displayName) - offsetof(UA_ObjectAttributes, specifiedAttributes) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Description") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_ObjectAttributes, description) - offsetof(UA_ObjectAttributes, displayName) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("WriteMask") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ObjectAttributes, writeMask) - offsetof(UA_ObjectAttributes, description) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("UserWriteMask") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ObjectAttributes, userWriteMask) - offsetof(UA_ObjectAttributes, writeMask) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("EventNotifier") /* .memberName */ &UA_TYPES[UA_TYPES_BYTE], /* .memberType */ offsetof(UA_ObjectAttributes, eventNotifier) - offsetof(UA_ObjectAttributes, userWriteMask) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* VariableAttributes */ static UA_DataTypeMember VariableAttributes_members[13] = { { UA_TYPENAME("SpecifiedAttributes") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DisplayName") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_VariableAttributes, displayName) - offsetof(UA_VariableAttributes, specifiedAttributes) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Description") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_VariableAttributes, description) - offsetof(UA_VariableAttributes, displayName) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("WriteMask") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_VariableAttributes, writeMask) - offsetof(UA_VariableAttributes, description) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("UserWriteMask") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_VariableAttributes, userWriteMask) - offsetof(UA_VariableAttributes, writeMask) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Value") /* .memberName */ &UA_TYPES[UA_TYPES_VARIANT], /* .memberType */ offsetof(UA_VariableAttributes, value) - offsetof(UA_VariableAttributes, userWriteMask) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DataType") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ offsetof(UA_VariableAttributes, dataType) - offsetof(UA_VariableAttributes, value) - sizeof(UA_Variant), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ValueRank") /* .memberName */ &UA_TYPES[UA_TYPES_INT32], /* .memberType */ offsetof(UA_VariableAttributes, valueRank) - offsetof(UA_VariableAttributes, dataType) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ArrayDimensions") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_VariableAttributes, arrayDimensionsSize) - offsetof(UA_VariableAttributes, valueRank) - sizeof(UA_Int32), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("AccessLevel") /* .memberName */ &UA_TYPES[UA_TYPES_BYTE], /* .memberType */ offsetof(UA_VariableAttributes, accessLevel) - offsetof(UA_VariableAttributes, arrayDimensions) - sizeof(void *), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("UserAccessLevel") /* .memberName */ &UA_TYPES[UA_TYPES_BYTE], /* .memberType */ offsetof(UA_VariableAttributes, userAccessLevel) - offsetof(UA_VariableAttributes, accessLevel) - sizeof(UA_Byte), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("MinimumSamplingInterval") /* .memberName */ &UA_TYPES[UA_TYPES_DOUBLE], /* .memberType */ offsetof(UA_VariableAttributes, minimumSamplingInterval) - offsetof(UA_VariableAttributes, userAccessLevel) - sizeof(UA_Byte), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Historizing") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_VariableAttributes, historizing) - offsetof(UA_VariableAttributes, minimumSamplingInterval) - sizeof(UA_Double), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* MethodAttributes */ static UA_DataTypeMember MethodAttributes_members[7] = { { UA_TYPENAME("SpecifiedAttributes") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DisplayName") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_MethodAttributes, displayName) - offsetof(UA_MethodAttributes, specifiedAttributes) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Description") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_MethodAttributes, description) - offsetof(UA_MethodAttributes, displayName) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("WriteMask") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_MethodAttributes, writeMask) - offsetof(UA_MethodAttributes, description) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("UserWriteMask") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_MethodAttributes, userWriteMask) - offsetof(UA_MethodAttributes, writeMask) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Executable") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_MethodAttributes, executable) - offsetof(UA_MethodAttributes, userWriteMask) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("UserExecutable") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_MethodAttributes, userExecutable) - offsetof(UA_MethodAttributes, executable) - sizeof(UA_Boolean), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* ObjectTypeAttributes */ static UA_DataTypeMember ObjectTypeAttributes_members[6] = { { UA_TYPENAME("SpecifiedAttributes") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DisplayName") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_ObjectTypeAttributes, displayName) - offsetof(UA_ObjectTypeAttributes, specifiedAttributes) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Description") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_ObjectTypeAttributes, description) - offsetof(UA_ObjectTypeAttributes, displayName) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("WriteMask") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ObjectTypeAttributes, writeMask) - offsetof(UA_ObjectTypeAttributes, description) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("UserWriteMask") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ObjectTypeAttributes, userWriteMask) - offsetof(UA_ObjectTypeAttributes, writeMask) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("IsAbstract") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_ObjectTypeAttributes, isAbstract) - offsetof(UA_ObjectTypeAttributes, userWriteMask) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* VariableTypeAttributes */ static UA_DataTypeMember VariableTypeAttributes_members[10] = { { UA_TYPENAME("SpecifiedAttributes") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DisplayName") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_VariableTypeAttributes, displayName) - offsetof(UA_VariableTypeAttributes, specifiedAttributes) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Description") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_VariableTypeAttributes, description) - offsetof(UA_VariableTypeAttributes, displayName) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("WriteMask") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_VariableTypeAttributes, writeMask) - offsetof(UA_VariableTypeAttributes, description) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("UserWriteMask") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_VariableTypeAttributes, userWriteMask) - offsetof(UA_VariableTypeAttributes, writeMask) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Value") /* .memberName */ &UA_TYPES[UA_TYPES_VARIANT], /* .memberType */ offsetof(UA_VariableTypeAttributes, value) - offsetof(UA_VariableTypeAttributes, userWriteMask) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DataType") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ offsetof(UA_VariableTypeAttributes, dataType) - offsetof(UA_VariableTypeAttributes, value) - sizeof(UA_Variant), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ValueRank") /* .memberName */ &UA_TYPES[UA_TYPES_INT32], /* .memberType */ offsetof(UA_VariableTypeAttributes, valueRank) - offsetof(UA_VariableTypeAttributes, dataType) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ArrayDimensions") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_VariableTypeAttributes, arrayDimensionsSize) - offsetof(UA_VariableTypeAttributes, valueRank) - sizeof(UA_Int32), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("IsAbstract") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_VariableTypeAttributes, isAbstract) - offsetof(UA_VariableTypeAttributes, arrayDimensions) - sizeof(void *), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* ReferenceTypeAttributes */ static UA_DataTypeMember ReferenceTypeAttributes_members[8] = { { UA_TYPENAME("SpecifiedAttributes") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DisplayName") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_ReferenceTypeAttributes, displayName) - offsetof(UA_ReferenceTypeAttributes, specifiedAttributes) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Description") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_ReferenceTypeAttributes, description) - offsetof(UA_ReferenceTypeAttributes, displayName) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("WriteMask") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ReferenceTypeAttributes, writeMask) - offsetof(UA_ReferenceTypeAttributes, description) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("UserWriteMask") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ReferenceTypeAttributes, userWriteMask) - offsetof(UA_ReferenceTypeAttributes, writeMask) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("IsAbstract") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_ReferenceTypeAttributes, isAbstract) - offsetof(UA_ReferenceTypeAttributes, userWriteMask) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Symmetric") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_ReferenceTypeAttributes, symmetric) - offsetof(UA_ReferenceTypeAttributes, isAbstract) - sizeof(UA_Boolean), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("InverseName") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_ReferenceTypeAttributes, inverseName) - offsetof(UA_ReferenceTypeAttributes, symmetric) - sizeof(UA_Boolean), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* DataTypeAttributes */ static UA_DataTypeMember DataTypeAttributes_members[6] = { { UA_TYPENAME("SpecifiedAttributes") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DisplayName") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_DataTypeAttributes, displayName) - offsetof(UA_DataTypeAttributes, specifiedAttributes) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Description") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_DataTypeAttributes, description) - offsetof(UA_DataTypeAttributes, displayName) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("WriteMask") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_DataTypeAttributes, writeMask) - offsetof(UA_DataTypeAttributes, description) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("UserWriteMask") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_DataTypeAttributes, userWriteMask) - offsetof(UA_DataTypeAttributes, writeMask) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("IsAbstract") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_DataTypeAttributes, isAbstract) - offsetof(UA_DataTypeAttributes, userWriteMask) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* ViewAttributes */ static UA_DataTypeMember ViewAttributes_members[7] = { { UA_TYPENAME("SpecifiedAttributes") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DisplayName") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_ViewAttributes, displayName) - offsetof(UA_ViewAttributes, specifiedAttributes) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Description") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_ViewAttributes, description) - offsetof(UA_ViewAttributes, displayName) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("WriteMask") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ViewAttributes, writeMask) - offsetof(UA_ViewAttributes, description) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("UserWriteMask") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ViewAttributes, userWriteMask) - offsetof(UA_ViewAttributes, writeMask) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ContainsNoLoops") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_ViewAttributes, containsNoLoops) - offsetof(UA_ViewAttributes, userWriteMask) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("EventNotifier") /* .memberName */ &UA_TYPES[UA_TYPES_BYTE], /* .memberType */ offsetof(UA_ViewAttributes, eventNotifier) - offsetof(UA_ViewAttributes, containsNoLoops) - sizeof(UA_Boolean), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* AddNodesItem */ static UA_DataTypeMember AddNodesItem_members[7] = { { UA_TYPENAME("ParentNodeId") /* .memberName */ &UA_TYPES[UA_TYPES_EXPANDEDNODEID], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ReferenceTypeId") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ offsetof(UA_AddNodesItem, referenceTypeId) - offsetof(UA_AddNodesItem, parentNodeId) - sizeof(UA_ExpandedNodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RequestedNewNodeId") /* .memberName */ &UA_TYPES[UA_TYPES_EXPANDEDNODEID], /* .memberType */ offsetof(UA_AddNodesItem, requestedNewNodeId) - offsetof(UA_AddNodesItem, referenceTypeId) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("BrowseName") /* .memberName */ &UA_TYPES[UA_TYPES_QUALIFIEDNAME], /* .memberType */ offsetof(UA_AddNodesItem, browseName) - offsetof(UA_AddNodesItem, requestedNewNodeId) - sizeof(UA_ExpandedNodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("NodeClass") /* .memberName */ &UA_TYPES[UA_TYPES_NODECLASS], /* .memberType */ offsetof(UA_AddNodesItem, nodeClass) - offsetof(UA_AddNodesItem, browseName) - sizeof(UA_QualifiedName), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("NodeAttributes") /* .memberName */ &UA_TYPES[UA_TYPES_EXTENSIONOBJECT], /* .memberType */ offsetof(UA_AddNodesItem, nodeAttributes) - offsetof(UA_AddNodesItem, nodeClass) - sizeof(UA_NodeClass), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("TypeDefinition") /* .memberName */ &UA_TYPES[UA_TYPES_EXPANDEDNODEID], /* .memberType */ offsetof(UA_AddNodesItem, typeDefinition) - offsetof(UA_AddNodesItem, nodeAttributes) - sizeof(UA_ExtensionObject), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* AddNodesResult */ static UA_DataTypeMember AddNodesResult_members[2] = { { UA_TYPENAME("StatusCode") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("AddedNodeId") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ offsetof(UA_AddNodesResult, addedNodeId) - offsetof(UA_AddNodesResult, statusCode) - sizeof(UA_StatusCode), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* AddNodesRequest */ static UA_DataTypeMember AddNodesRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("NodesToAdd") /* .memberName */ &UA_TYPES[UA_TYPES_ADDNODESITEM], /* .memberType */ offsetof(UA_AddNodesRequest, nodesToAddSize) - offsetof(UA_AddNodesRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* AddNodesResponse */ static UA_DataTypeMember AddNodesResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Results") /* .memberName */ &UA_TYPES[UA_TYPES_ADDNODESRESULT], /* .memberType */ offsetof(UA_AddNodesResponse, resultsSize) - offsetof(UA_AddNodesResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_AddNodesResponse, diagnosticInfosSize) - offsetof(UA_AddNodesResponse, results) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* AddReferencesItem */ static UA_DataTypeMember AddReferencesItem_members[6] = { { UA_TYPENAME("SourceNodeId") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ReferenceTypeId") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ offsetof(UA_AddReferencesItem, referenceTypeId) - offsetof(UA_AddReferencesItem, sourceNodeId) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("IsForward") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_AddReferencesItem, isForward) - offsetof(UA_AddReferencesItem, referenceTypeId) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("TargetServerUri") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_AddReferencesItem, targetServerUri) - offsetof(UA_AddReferencesItem, isForward) - sizeof(UA_Boolean), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("TargetNodeId") /* .memberName */ &UA_TYPES[UA_TYPES_EXPANDEDNODEID], /* .memberType */ offsetof(UA_AddReferencesItem, targetNodeId) - offsetof(UA_AddReferencesItem, targetServerUri) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("TargetNodeClass") /* .memberName */ &UA_TYPES[UA_TYPES_NODECLASS], /* .memberType */ offsetof(UA_AddReferencesItem, targetNodeClass) - offsetof(UA_AddReferencesItem, targetNodeId) - sizeof(UA_ExpandedNodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* AddReferencesRequest */ static UA_DataTypeMember AddReferencesRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ReferencesToAdd") /* .memberName */ &UA_TYPES[UA_TYPES_ADDREFERENCESITEM], /* .memberType */ offsetof(UA_AddReferencesRequest, referencesToAddSize) - offsetof(UA_AddReferencesRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* AddReferencesResponse */ static UA_DataTypeMember AddReferencesResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Results") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ offsetof(UA_AddReferencesResponse, resultsSize) - offsetof(UA_AddReferencesResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_AddReferencesResponse, diagnosticInfosSize) - offsetof(UA_AddReferencesResponse, results) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* DeleteNodesItem */ static UA_DataTypeMember DeleteNodesItem_members[2] = { { UA_TYPENAME("NodeId") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DeleteTargetReferences") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_DeleteNodesItem, deleteTargetReferences) - offsetof(UA_DeleteNodesItem, nodeId) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* DeleteNodesRequest */ static UA_DataTypeMember DeleteNodesRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("NodesToDelete") /* .memberName */ &UA_TYPES[UA_TYPES_DELETENODESITEM], /* .memberType */ offsetof(UA_DeleteNodesRequest, nodesToDeleteSize) - offsetof(UA_DeleteNodesRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* DeleteNodesResponse */ static UA_DataTypeMember DeleteNodesResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Results") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ offsetof(UA_DeleteNodesResponse, resultsSize) - offsetof(UA_DeleteNodesResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_DeleteNodesResponse, diagnosticInfosSize) - offsetof(UA_DeleteNodesResponse, results) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* DeleteReferencesItem */ static UA_DataTypeMember DeleteReferencesItem_members[5] = { { UA_TYPENAME("SourceNodeId") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ReferenceTypeId") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ offsetof(UA_DeleteReferencesItem, referenceTypeId) - offsetof(UA_DeleteReferencesItem, sourceNodeId) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("IsForward") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_DeleteReferencesItem, isForward) - offsetof(UA_DeleteReferencesItem, referenceTypeId) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("TargetNodeId") /* .memberName */ &UA_TYPES[UA_TYPES_EXPANDEDNODEID], /* .memberType */ offsetof(UA_DeleteReferencesItem, targetNodeId) - offsetof(UA_DeleteReferencesItem, isForward) - sizeof(UA_Boolean), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DeleteBidirectional") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_DeleteReferencesItem, deleteBidirectional) - offsetof(UA_DeleteReferencesItem, targetNodeId) - sizeof(UA_ExpandedNodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* DeleteReferencesRequest */ static UA_DataTypeMember DeleteReferencesRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ReferencesToDelete") /* .memberName */ &UA_TYPES[UA_TYPES_DELETEREFERENCESITEM], /* .memberType */ offsetof(UA_DeleteReferencesRequest, referencesToDeleteSize) - offsetof(UA_DeleteReferencesRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* DeleteReferencesResponse */ static UA_DataTypeMember DeleteReferencesResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Results") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ offsetof(UA_DeleteReferencesResponse, resultsSize) - offsetof(UA_DeleteReferencesResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_DeleteReferencesResponse, diagnosticInfosSize) - offsetof(UA_DeleteReferencesResponse, results) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* BrowseDirection */ #define BrowseDirection_members NULL /* ViewDescription */ static UA_DataTypeMember ViewDescription_members[3] = { { UA_TYPENAME("ViewId") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Timestamp") /* .memberName */ &UA_TYPES[UA_TYPES_DATETIME], /* .memberType */ offsetof(UA_ViewDescription, timestamp) - offsetof(UA_ViewDescription, viewId) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ViewVersion") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ViewDescription, viewVersion) - offsetof(UA_ViewDescription, timestamp) - sizeof(UA_DateTime), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* BrowseDescription */ static UA_DataTypeMember BrowseDescription_members[6] = { { UA_TYPENAME("NodeId") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("BrowseDirection") /* .memberName */ &UA_TYPES[UA_TYPES_BROWSEDIRECTION], /* .memberType */ offsetof(UA_BrowseDescription, browseDirection) - offsetof(UA_BrowseDescription, nodeId) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ReferenceTypeId") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ offsetof(UA_BrowseDescription, referenceTypeId) - offsetof(UA_BrowseDescription, browseDirection) - sizeof(UA_BrowseDirection), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("IncludeSubtypes") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_BrowseDescription, includeSubtypes) - offsetof(UA_BrowseDescription, referenceTypeId) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("NodeClassMask") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_BrowseDescription, nodeClassMask) - offsetof(UA_BrowseDescription, includeSubtypes) - sizeof(UA_Boolean), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ResultMask") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_BrowseDescription, resultMask) - offsetof(UA_BrowseDescription, nodeClassMask) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* BrowseResultMask */ #define BrowseResultMask_members NULL /* ReferenceDescription */ static UA_DataTypeMember ReferenceDescription_members[7] = { { UA_TYPENAME("ReferenceTypeId") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("IsForward") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_ReferenceDescription, isForward) - offsetof(UA_ReferenceDescription, referenceTypeId) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("NodeId") /* .memberName */ &UA_TYPES[UA_TYPES_EXPANDEDNODEID], /* .memberType */ offsetof(UA_ReferenceDescription, nodeId) - offsetof(UA_ReferenceDescription, isForward) - sizeof(UA_Boolean), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("BrowseName") /* .memberName */ &UA_TYPES[UA_TYPES_QUALIFIEDNAME], /* .memberType */ offsetof(UA_ReferenceDescription, browseName) - offsetof(UA_ReferenceDescription, nodeId) - sizeof(UA_ExpandedNodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DisplayName") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_ReferenceDescription, displayName) - offsetof(UA_ReferenceDescription, browseName) - sizeof(UA_QualifiedName), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("NodeClass") /* .memberName */ &UA_TYPES[UA_TYPES_NODECLASS], /* .memberType */ offsetof(UA_ReferenceDescription, nodeClass) - offsetof(UA_ReferenceDescription, displayName) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("TypeDefinition") /* .memberName */ &UA_TYPES[UA_TYPES_EXPANDEDNODEID], /* .memberType */ offsetof(UA_ReferenceDescription, typeDefinition) - offsetof(UA_ReferenceDescription, nodeClass) - sizeof(UA_NodeClass), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* BrowseResult */ static UA_DataTypeMember BrowseResult_members[3] = { { UA_TYPENAME("StatusCode") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ContinuationPoint") /* .memberName */ &UA_TYPES[UA_TYPES_BYTESTRING], /* .memberType */ offsetof(UA_BrowseResult, continuationPoint) - offsetof(UA_BrowseResult, statusCode) - sizeof(UA_StatusCode), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("References") /* .memberName */ &UA_TYPES[UA_TYPES_REFERENCEDESCRIPTION], /* .memberType */ offsetof(UA_BrowseResult, referencesSize) - offsetof(UA_BrowseResult, continuationPoint) - sizeof(UA_ByteString), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* BrowseRequest */ static UA_DataTypeMember BrowseRequest_members[4] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("View") /* .memberName */ &UA_TYPES[UA_TYPES_VIEWDESCRIPTION], /* .memberType */ offsetof(UA_BrowseRequest, view) - offsetof(UA_BrowseRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RequestedMaxReferencesPerNode") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_BrowseRequest, requestedMaxReferencesPerNode) - offsetof(UA_BrowseRequest, view) - sizeof(UA_ViewDescription), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("NodesToBrowse") /* .memberName */ &UA_TYPES[UA_TYPES_BROWSEDESCRIPTION], /* .memberType */ offsetof(UA_BrowseRequest, nodesToBrowseSize) - offsetof(UA_BrowseRequest, requestedMaxReferencesPerNode) - sizeof(UA_UInt32), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* BrowseResponse */ static UA_DataTypeMember BrowseResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Results") /* .memberName */ &UA_TYPES[UA_TYPES_BROWSERESULT], /* .memberType */ offsetof(UA_BrowseResponse, resultsSize) - offsetof(UA_BrowseResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_BrowseResponse, diagnosticInfosSize) - offsetof(UA_BrowseResponse, results) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* BrowseNextRequest */ static UA_DataTypeMember BrowseNextRequest_members[3] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ReleaseContinuationPoints") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_BrowseNextRequest, releaseContinuationPoints) - offsetof(UA_BrowseNextRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ContinuationPoints") /* .memberName */ &UA_TYPES[UA_TYPES_BYTESTRING], /* .memberType */ offsetof(UA_BrowseNextRequest, continuationPointsSize) - offsetof(UA_BrowseNextRequest, releaseContinuationPoints) - sizeof(UA_Boolean), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* BrowseNextResponse */ static UA_DataTypeMember BrowseNextResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Results") /* .memberName */ &UA_TYPES[UA_TYPES_BROWSERESULT], /* .memberType */ offsetof(UA_BrowseNextResponse, resultsSize) - offsetof(UA_BrowseNextResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_BrowseNextResponse, diagnosticInfosSize) - offsetof(UA_BrowseNextResponse, results) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* RelativePathElement */ static UA_DataTypeMember RelativePathElement_members[4] = { { UA_TYPENAME("ReferenceTypeId") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("IsInverse") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_RelativePathElement, isInverse) - offsetof(UA_RelativePathElement, referenceTypeId) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("IncludeSubtypes") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_RelativePathElement, includeSubtypes) - offsetof(UA_RelativePathElement, isInverse) - sizeof(UA_Boolean), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("TargetName") /* .memberName */ &UA_TYPES[UA_TYPES_QUALIFIEDNAME], /* .memberType */ offsetof(UA_RelativePathElement, targetName) - offsetof(UA_RelativePathElement, includeSubtypes) - sizeof(UA_Boolean), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* RelativePath */ static UA_DataTypeMember RelativePath_members[1] = { { UA_TYPENAME("Elements") /* .memberName */ &UA_TYPES[UA_TYPES_RELATIVEPATHELEMENT], /* .memberType */ 0, /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* BrowsePath */ static UA_DataTypeMember BrowsePath_members[2] = { { UA_TYPENAME("StartingNode") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RelativePath") /* .memberName */ &UA_TYPES[UA_TYPES_RELATIVEPATH], /* .memberType */ offsetof(UA_BrowsePath, relativePath) - offsetof(UA_BrowsePath, startingNode) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* BrowsePathTarget */ static UA_DataTypeMember BrowsePathTarget_members[2] = { { UA_TYPENAME("TargetId") /* .memberName */ &UA_TYPES[UA_TYPES_EXPANDEDNODEID], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RemainingPathIndex") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_BrowsePathTarget, remainingPathIndex) - offsetof(UA_BrowsePathTarget, targetId) - sizeof(UA_ExpandedNodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* BrowsePathResult */ static UA_DataTypeMember BrowsePathResult_members[2] = { { UA_TYPENAME("StatusCode") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Targets") /* .memberName */ &UA_TYPES[UA_TYPES_BROWSEPATHTARGET], /* .memberType */ offsetof(UA_BrowsePathResult, targetsSize) - offsetof(UA_BrowsePathResult, statusCode) - sizeof(UA_StatusCode), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* TranslateBrowsePathsToNodeIdsRequest */ static UA_DataTypeMember TranslateBrowsePathsToNodeIdsRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("BrowsePaths") /* .memberName */ &UA_TYPES[UA_TYPES_BROWSEPATH], /* .memberType */ offsetof(UA_TranslateBrowsePathsToNodeIdsRequest, browsePathsSize) - offsetof(UA_TranslateBrowsePathsToNodeIdsRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* TranslateBrowsePathsToNodeIdsResponse */ static UA_DataTypeMember TranslateBrowsePathsToNodeIdsResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Results") /* .memberName */ &UA_TYPES[UA_TYPES_BROWSEPATHRESULT], /* .memberType */ offsetof(UA_TranslateBrowsePathsToNodeIdsResponse, resultsSize) - offsetof(UA_TranslateBrowsePathsToNodeIdsResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_TranslateBrowsePathsToNodeIdsResponse, diagnosticInfosSize) - offsetof(UA_TranslateBrowsePathsToNodeIdsResponse, results) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* RegisterNodesRequest */ static UA_DataTypeMember RegisterNodesRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("NodesToRegister") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ offsetof(UA_RegisterNodesRequest, nodesToRegisterSize) - offsetof(UA_RegisterNodesRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* RegisterNodesResponse */ static UA_DataTypeMember RegisterNodesResponse_members[2] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RegisteredNodeIds") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ offsetof(UA_RegisterNodesResponse, registeredNodeIdsSize) - offsetof(UA_RegisterNodesResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* UnregisterNodesRequest */ static UA_DataTypeMember UnregisterNodesRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("NodesToUnregister") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ offsetof(UA_UnregisterNodesRequest, nodesToUnregisterSize) - offsetof(UA_UnregisterNodesRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* UnregisterNodesResponse */ static UA_DataTypeMember UnregisterNodesResponse_members[1] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* FilterOperator */ #define FilterOperator_members NULL /* ContentFilterElement */ static UA_DataTypeMember ContentFilterElement_members[2] = { { UA_TYPENAME("FilterOperator") /* .memberName */ &UA_TYPES[UA_TYPES_FILTEROPERATOR], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("FilterOperands") /* .memberName */ &UA_TYPES[UA_TYPES_EXTENSIONOBJECT], /* .memberType */ offsetof(UA_ContentFilterElement, filterOperandsSize) - offsetof(UA_ContentFilterElement, filterOperator) - sizeof(UA_FilterOperator), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* ContentFilter */ static UA_DataTypeMember ContentFilter_members[1] = { { UA_TYPENAME("Elements") /* .memberName */ &UA_TYPES[UA_TYPES_CONTENTFILTERELEMENT], /* .memberType */ 0, /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* ElementOperand */ static UA_DataTypeMember ElementOperand_members[1] = { { UA_TYPENAME("Index") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* LiteralOperand */ static UA_DataTypeMember LiteralOperand_members[1] = { { UA_TYPENAME("Value") /* .memberName */ &UA_TYPES[UA_TYPES_VARIANT], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* AttributeOperand */ static UA_DataTypeMember AttributeOperand_members[5] = { { UA_TYPENAME("NodeId") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Alias") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_AttributeOperand, alias) - offsetof(UA_AttributeOperand, nodeId) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("BrowsePath") /* .memberName */ &UA_TYPES[UA_TYPES_RELATIVEPATH], /* .memberType */ offsetof(UA_AttributeOperand, browsePath) - offsetof(UA_AttributeOperand, alias) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("AttributeId") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_AttributeOperand, attributeId) - offsetof(UA_AttributeOperand, browsePath) - sizeof(UA_RelativePath), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("IndexRange") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_AttributeOperand, indexRange) - offsetof(UA_AttributeOperand, attributeId) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* SimpleAttributeOperand */ static UA_DataTypeMember SimpleAttributeOperand_members[4] = { { UA_TYPENAME("TypeDefinitionId") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("BrowsePath") /* .memberName */ &UA_TYPES[UA_TYPES_QUALIFIEDNAME], /* .memberType */ offsetof(UA_SimpleAttributeOperand, browsePathSize) - offsetof(UA_SimpleAttributeOperand, typeDefinitionId) - sizeof(UA_NodeId), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("AttributeId") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_SimpleAttributeOperand, attributeId) - offsetof(UA_SimpleAttributeOperand, browsePath) - sizeof(void *), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("IndexRange") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_SimpleAttributeOperand, indexRange) - offsetof(UA_SimpleAttributeOperand, attributeId) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* ContentFilterElementResult */ static UA_DataTypeMember ContentFilterElementResult_members[3] = { { UA_TYPENAME("StatusCode") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("OperandStatusCodes") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ offsetof(UA_ContentFilterElementResult, operandStatusCodesSize) - offsetof(UA_ContentFilterElementResult, statusCode) - sizeof(UA_StatusCode), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("OperandDiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_ContentFilterElementResult, operandDiagnosticInfosSize) - offsetof(UA_ContentFilterElementResult, operandStatusCodes) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* ContentFilterResult */ static UA_DataTypeMember ContentFilterResult_members[2] = { { UA_TYPENAME("ElementResults") /* .memberName */ &UA_TYPES[UA_TYPES_CONTENTFILTERELEMENTRESULT], /* .memberType */ 0, /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ElementDiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_ContentFilterResult, elementDiagnosticInfosSize) - offsetof(UA_ContentFilterResult, elementResults) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* TimestampsToReturn */ #define TimestampsToReturn_members NULL /* ReadValueId */ static UA_DataTypeMember ReadValueId_members[4] = { { UA_TYPENAME("NodeId") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("AttributeId") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ReadValueId, attributeId) - offsetof(UA_ReadValueId, nodeId) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("IndexRange") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_ReadValueId, indexRange) - offsetof(UA_ReadValueId, attributeId) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DataEncoding") /* .memberName */ &UA_TYPES[UA_TYPES_QUALIFIEDNAME], /* .memberType */ offsetof(UA_ReadValueId, dataEncoding) - offsetof(UA_ReadValueId, indexRange) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* ReadRequest */ static UA_DataTypeMember ReadRequest_members[4] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("MaxAge") /* .memberName */ &UA_TYPES[UA_TYPES_DOUBLE], /* .memberType */ offsetof(UA_ReadRequest, maxAge) - offsetof(UA_ReadRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("TimestampsToReturn") /* .memberName */ &UA_TYPES[UA_TYPES_TIMESTAMPSTORETURN], /* .memberType */ offsetof(UA_ReadRequest, timestampsToReturn) - offsetof(UA_ReadRequest, maxAge) - sizeof(UA_Double), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("NodesToRead") /* .memberName */ &UA_TYPES[UA_TYPES_READVALUEID], /* .memberType */ offsetof(UA_ReadRequest, nodesToReadSize) - offsetof(UA_ReadRequest, timestampsToReturn) - sizeof(UA_TimestampsToReturn), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* ReadResponse */ static UA_DataTypeMember ReadResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Results") /* .memberName */ &UA_TYPES[UA_TYPES_DATAVALUE], /* .memberType */ offsetof(UA_ReadResponse, resultsSize) - offsetof(UA_ReadResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_ReadResponse, diagnosticInfosSize) - offsetof(UA_ReadResponse, results) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* HistoryReadValueId */ static UA_DataTypeMember HistoryReadValueId_members[4] = { { UA_TYPENAME("NodeId") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("IndexRange") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_HistoryReadValueId, indexRange) - offsetof(UA_HistoryReadValueId, nodeId) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DataEncoding") /* .memberName */ &UA_TYPES[UA_TYPES_QUALIFIEDNAME], /* .memberType */ offsetof(UA_HistoryReadValueId, dataEncoding) - offsetof(UA_HistoryReadValueId, indexRange) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ContinuationPoint") /* .memberName */ &UA_TYPES[UA_TYPES_BYTESTRING], /* .memberType */ offsetof(UA_HistoryReadValueId, continuationPoint) - offsetof(UA_HistoryReadValueId, dataEncoding) - sizeof(UA_QualifiedName), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* HistoryReadResult */ static UA_DataTypeMember HistoryReadResult_members[3] = { { UA_TYPENAME("StatusCode") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ContinuationPoint") /* .memberName */ &UA_TYPES[UA_TYPES_BYTESTRING], /* .memberType */ offsetof(UA_HistoryReadResult, continuationPoint) - offsetof(UA_HistoryReadResult, statusCode) - sizeof(UA_StatusCode), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("HistoryData") /* .memberName */ &UA_TYPES[UA_TYPES_EXTENSIONOBJECT], /* .memberType */ offsetof(UA_HistoryReadResult, historyData) - offsetof(UA_HistoryReadResult, continuationPoint) - sizeof(UA_ByteString), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* ReadRawModifiedDetails */ static UA_DataTypeMember ReadRawModifiedDetails_members[5] = { { UA_TYPENAME("IsReadModified") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("StartTime") /* .memberName */ &UA_TYPES[UA_TYPES_DATETIME], /* .memberType */ offsetof(UA_ReadRawModifiedDetails, startTime) - offsetof(UA_ReadRawModifiedDetails, isReadModified) - sizeof(UA_Boolean), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("EndTime") /* .memberName */ &UA_TYPES[UA_TYPES_DATETIME], /* .memberType */ offsetof(UA_ReadRawModifiedDetails, endTime) - offsetof(UA_ReadRawModifiedDetails, startTime) - sizeof(UA_DateTime), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("NumValuesPerNode") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ReadRawModifiedDetails, numValuesPerNode) - offsetof(UA_ReadRawModifiedDetails, endTime) - sizeof(UA_DateTime), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ReturnBounds") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_ReadRawModifiedDetails, returnBounds) - offsetof(UA_ReadRawModifiedDetails, numValuesPerNode) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* ReadAtTimeDetails */ static UA_DataTypeMember ReadAtTimeDetails_members[2] = { { UA_TYPENAME("ReqTimes") /* .memberName */ &UA_TYPES[UA_TYPES_DATETIME], /* .memberType */ 0, /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("UseSimpleBounds") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_ReadAtTimeDetails, useSimpleBounds) - offsetof(UA_ReadAtTimeDetails, reqTimes) - sizeof(void *), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* HistoryData */ static UA_DataTypeMember HistoryData_members[1] = { { UA_TYPENAME("DataValues") /* .memberName */ &UA_TYPES[UA_TYPES_DATAVALUE], /* .memberType */ 0, /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* HistoryReadRequest */ static UA_DataTypeMember HistoryReadRequest_members[5] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("HistoryReadDetails") /* .memberName */ &UA_TYPES[UA_TYPES_EXTENSIONOBJECT], /* .memberType */ offsetof(UA_HistoryReadRequest, historyReadDetails) - offsetof(UA_HistoryReadRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("TimestampsToReturn") /* .memberName */ &UA_TYPES[UA_TYPES_TIMESTAMPSTORETURN], /* .memberType */ offsetof(UA_HistoryReadRequest, timestampsToReturn) - offsetof(UA_HistoryReadRequest, historyReadDetails) - sizeof(UA_ExtensionObject), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ReleaseContinuationPoints") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_HistoryReadRequest, releaseContinuationPoints) - offsetof(UA_HistoryReadRequest, timestampsToReturn) - sizeof(UA_TimestampsToReturn), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("NodesToRead") /* .memberName */ &UA_TYPES[UA_TYPES_HISTORYREADVALUEID], /* .memberType */ offsetof(UA_HistoryReadRequest, nodesToReadSize) - offsetof(UA_HistoryReadRequest, releaseContinuationPoints) - sizeof(UA_Boolean), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* HistoryReadResponse */ static UA_DataTypeMember HistoryReadResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Results") /* .memberName */ &UA_TYPES[UA_TYPES_HISTORYREADRESULT], /* .memberType */ offsetof(UA_HistoryReadResponse, resultsSize) - offsetof(UA_HistoryReadResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_HistoryReadResponse, diagnosticInfosSize) - offsetof(UA_HistoryReadResponse, results) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* WriteValue */ static UA_DataTypeMember WriteValue_members[4] = { { UA_TYPENAME("NodeId") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("AttributeId") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_WriteValue, attributeId) - offsetof(UA_WriteValue, nodeId) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("IndexRange") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_WriteValue, indexRange) - offsetof(UA_WriteValue, attributeId) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Value") /* .memberName */ &UA_TYPES[UA_TYPES_DATAVALUE], /* .memberType */ offsetof(UA_WriteValue, value) - offsetof(UA_WriteValue, indexRange) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* WriteRequest */ static UA_DataTypeMember WriteRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("NodesToWrite") /* .memberName */ &UA_TYPES[UA_TYPES_WRITEVALUE], /* .memberType */ offsetof(UA_WriteRequest, nodesToWriteSize) - offsetof(UA_WriteRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* WriteResponse */ static UA_DataTypeMember WriteResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Results") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ offsetof(UA_WriteResponse, resultsSize) - offsetof(UA_WriteResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_WriteResponse, diagnosticInfosSize) - offsetof(UA_WriteResponse, results) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* HistoryUpdateType */ #define HistoryUpdateType_members NULL /* PerformUpdateType */ #define PerformUpdateType_members NULL /* UpdateDataDetails */ static UA_DataTypeMember UpdateDataDetails_members[3] = { { UA_TYPENAME("NodeId") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("PerformInsertReplace") /* .memberName */ &UA_TYPES[UA_TYPES_PERFORMUPDATETYPE], /* .memberType */ offsetof(UA_UpdateDataDetails, performInsertReplace) - offsetof(UA_UpdateDataDetails, nodeId) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("UpdateValues") /* .memberName */ &UA_TYPES[UA_TYPES_DATAVALUE], /* .memberType */ offsetof(UA_UpdateDataDetails, updateValuesSize) - offsetof(UA_UpdateDataDetails, performInsertReplace) - sizeof(UA_PerformUpdateType), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* DeleteRawModifiedDetails */ static UA_DataTypeMember DeleteRawModifiedDetails_members[4] = { { UA_TYPENAME("NodeId") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("IsDeleteModified") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_DeleteRawModifiedDetails, isDeleteModified) - offsetof(UA_DeleteRawModifiedDetails, nodeId) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("StartTime") /* .memberName */ &UA_TYPES[UA_TYPES_DATETIME], /* .memberType */ offsetof(UA_DeleteRawModifiedDetails, startTime) - offsetof(UA_DeleteRawModifiedDetails, isDeleteModified) - sizeof(UA_Boolean), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("EndTime") /* .memberName */ &UA_TYPES[UA_TYPES_DATETIME], /* .memberType */ offsetof(UA_DeleteRawModifiedDetails, endTime) - offsetof(UA_DeleteRawModifiedDetails, startTime) - sizeof(UA_DateTime), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* HistoryUpdateResult */ static UA_DataTypeMember HistoryUpdateResult_members[3] = { { UA_TYPENAME("StatusCode") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("OperationResults") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ offsetof(UA_HistoryUpdateResult, operationResultsSize) - offsetof(UA_HistoryUpdateResult, statusCode) - sizeof(UA_StatusCode), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_HistoryUpdateResult, diagnosticInfosSize) - offsetof(UA_HistoryUpdateResult, operationResults) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* HistoryUpdateRequest */ static UA_DataTypeMember HistoryUpdateRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("HistoryUpdateDetails") /* .memberName */ &UA_TYPES[UA_TYPES_EXTENSIONOBJECT], /* .memberType */ offsetof(UA_HistoryUpdateRequest, historyUpdateDetailsSize) - offsetof(UA_HistoryUpdateRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* HistoryUpdateResponse */ static UA_DataTypeMember HistoryUpdateResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Results") /* .memberName */ &UA_TYPES[UA_TYPES_HISTORYUPDATERESULT], /* .memberType */ offsetof(UA_HistoryUpdateResponse, resultsSize) - offsetof(UA_HistoryUpdateResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_HistoryUpdateResponse, diagnosticInfosSize) - offsetof(UA_HistoryUpdateResponse, results) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* CallMethodRequest */ static UA_DataTypeMember CallMethodRequest_members[3] = { { UA_TYPENAME("ObjectId") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("MethodId") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ offsetof(UA_CallMethodRequest, methodId) - offsetof(UA_CallMethodRequest, objectId) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("InputArguments") /* .memberName */ &UA_TYPES[UA_TYPES_VARIANT], /* .memberType */ offsetof(UA_CallMethodRequest, inputArgumentsSize) - offsetof(UA_CallMethodRequest, methodId) - sizeof(UA_NodeId), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* CallMethodResult */ static UA_DataTypeMember CallMethodResult_members[4] = { { UA_TYPENAME("StatusCode") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("InputArgumentResults") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ offsetof(UA_CallMethodResult, inputArgumentResultsSize) - offsetof(UA_CallMethodResult, statusCode) - sizeof(UA_StatusCode), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("InputArgumentDiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_CallMethodResult, inputArgumentDiagnosticInfosSize) - offsetof(UA_CallMethodResult, inputArgumentResults) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("OutputArguments") /* .memberName */ &UA_TYPES[UA_TYPES_VARIANT], /* .memberType */ offsetof(UA_CallMethodResult, outputArgumentsSize) - offsetof(UA_CallMethodResult, inputArgumentDiagnosticInfos) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* CallRequest */ static UA_DataTypeMember CallRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("MethodsToCall") /* .memberName */ &UA_TYPES[UA_TYPES_CALLMETHODREQUEST], /* .memberType */ offsetof(UA_CallRequest, methodsToCallSize) - offsetof(UA_CallRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* CallResponse */ static UA_DataTypeMember CallResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Results") /* .memberName */ &UA_TYPES[UA_TYPES_CALLMETHODRESULT], /* .memberType */ offsetof(UA_CallResponse, resultsSize) - offsetof(UA_CallResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_CallResponse, diagnosticInfosSize) - offsetof(UA_CallResponse, results) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* MonitoringMode */ #define MonitoringMode_members NULL /* DataChangeTrigger */ #define DataChangeTrigger_members NULL /* DeadbandType */ #define DeadbandType_members NULL /* DataChangeFilter */ static UA_DataTypeMember DataChangeFilter_members[3] = { { UA_TYPENAME("Trigger") /* .memberName */ &UA_TYPES[UA_TYPES_DATACHANGETRIGGER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DeadbandType") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_DataChangeFilter, deadbandType) - offsetof(UA_DataChangeFilter, trigger) - sizeof(UA_DataChangeTrigger), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DeadbandValue") /* .memberName */ &UA_TYPES[UA_TYPES_DOUBLE], /* .memberType */ offsetof(UA_DataChangeFilter, deadbandValue) - offsetof(UA_DataChangeFilter, deadbandType) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* EventFilter */ static UA_DataTypeMember EventFilter_members[2] = { { UA_TYPENAME("SelectClauses") /* .memberName */ &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND], /* .memberType */ 0, /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("WhereClause") /* .memberName */ &UA_TYPES[UA_TYPES_CONTENTFILTER], /* .memberType */ offsetof(UA_EventFilter, whereClause) - offsetof(UA_EventFilter, selectClauses) - sizeof(void *), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* AggregateConfiguration */ static UA_DataTypeMember AggregateConfiguration_members[5] = { { UA_TYPENAME("UseServerCapabilitiesDefaults") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("TreatUncertainAsBad") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_AggregateConfiguration, treatUncertainAsBad) - offsetof(UA_AggregateConfiguration, useServerCapabilitiesDefaults) - sizeof(UA_Boolean), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("PercentDataBad") /* .memberName */ &UA_TYPES[UA_TYPES_BYTE], /* .memberType */ offsetof(UA_AggregateConfiguration, percentDataBad) - offsetof(UA_AggregateConfiguration, treatUncertainAsBad) - sizeof(UA_Boolean), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("PercentDataGood") /* .memberName */ &UA_TYPES[UA_TYPES_BYTE], /* .memberType */ offsetof(UA_AggregateConfiguration, percentDataGood) - offsetof(UA_AggregateConfiguration, percentDataBad) - sizeof(UA_Byte), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("UseSlopedExtrapolation") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_AggregateConfiguration, useSlopedExtrapolation) - offsetof(UA_AggregateConfiguration, percentDataGood) - sizeof(UA_Byte), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* AggregateFilter */ static UA_DataTypeMember AggregateFilter_members[4] = { { UA_TYPENAME("StartTime") /* .memberName */ &UA_TYPES[UA_TYPES_DATETIME], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("AggregateType") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ offsetof(UA_AggregateFilter, aggregateType) - offsetof(UA_AggregateFilter, startTime) - sizeof(UA_DateTime), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ProcessingInterval") /* .memberName */ &UA_TYPES[UA_TYPES_DOUBLE], /* .memberType */ offsetof(UA_AggregateFilter, processingInterval) - offsetof(UA_AggregateFilter, aggregateType) - sizeof(UA_NodeId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("AggregateConfiguration") /* .memberName */ &UA_TYPES[UA_TYPES_AGGREGATECONFIGURATION], /* .memberType */ offsetof(UA_AggregateFilter, aggregateConfiguration) - offsetof(UA_AggregateFilter, processingInterval) - sizeof(UA_Double), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* EventFilterResult */ static UA_DataTypeMember EventFilterResult_members[3] = { { UA_TYPENAME("SelectClauseResults") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ 0, /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SelectClauseDiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_EventFilterResult, selectClauseDiagnosticInfosSize) - offsetof(UA_EventFilterResult, selectClauseResults) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("WhereClauseResult") /* .memberName */ &UA_TYPES[UA_TYPES_CONTENTFILTERRESULT], /* .memberType */ offsetof(UA_EventFilterResult, whereClauseResult) - offsetof(UA_EventFilterResult, selectClauseDiagnosticInfos) - sizeof(void *), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* MonitoringParameters */ static UA_DataTypeMember MonitoringParameters_members[5] = { { UA_TYPENAME("ClientHandle") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SamplingInterval") /* .memberName */ &UA_TYPES[UA_TYPES_DOUBLE], /* .memberType */ offsetof(UA_MonitoringParameters, samplingInterval) - offsetof(UA_MonitoringParameters, clientHandle) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Filter") /* .memberName */ &UA_TYPES[UA_TYPES_EXTENSIONOBJECT], /* .memberType */ offsetof(UA_MonitoringParameters, filter) - offsetof(UA_MonitoringParameters, samplingInterval) - sizeof(UA_Double), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("QueueSize") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_MonitoringParameters, queueSize) - offsetof(UA_MonitoringParameters, filter) - sizeof(UA_ExtensionObject), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiscardOldest") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_MonitoringParameters, discardOldest) - offsetof(UA_MonitoringParameters, queueSize) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* MonitoredItemCreateRequest */ static UA_DataTypeMember MonitoredItemCreateRequest_members[3] = { { UA_TYPENAME("ItemToMonitor") /* .memberName */ &UA_TYPES[UA_TYPES_READVALUEID], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("MonitoringMode") /* .memberName */ &UA_TYPES[UA_TYPES_MONITORINGMODE], /* .memberType */ offsetof(UA_MonitoredItemCreateRequest, monitoringMode) - offsetof(UA_MonitoredItemCreateRequest, itemToMonitor) - sizeof(UA_ReadValueId), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RequestedParameters") /* .memberName */ &UA_TYPES[UA_TYPES_MONITORINGPARAMETERS], /* .memberType */ offsetof(UA_MonitoredItemCreateRequest, requestedParameters) - offsetof(UA_MonitoredItemCreateRequest, monitoringMode) - sizeof(UA_MonitoringMode), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* MonitoredItemCreateResult */ static UA_DataTypeMember MonitoredItemCreateResult_members[5] = { { UA_TYPENAME("StatusCode") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("MonitoredItemId") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_MonitoredItemCreateResult, monitoredItemId) - offsetof(UA_MonitoredItemCreateResult, statusCode) - sizeof(UA_StatusCode), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RevisedSamplingInterval") /* .memberName */ &UA_TYPES[UA_TYPES_DOUBLE], /* .memberType */ offsetof(UA_MonitoredItemCreateResult, revisedSamplingInterval) - offsetof(UA_MonitoredItemCreateResult, monitoredItemId) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RevisedQueueSize") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_MonitoredItemCreateResult, revisedQueueSize) - offsetof(UA_MonitoredItemCreateResult, revisedSamplingInterval) - sizeof(UA_Double), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("FilterResult") /* .memberName */ &UA_TYPES[UA_TYPES_EXTENSIONOBJECT], /* .memberType */ offsetof(UA_MonitoredItemCreateResult, filterResult) - offsetof(UA_MonitoredItemCreateResult, revisedQueueSize) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* CreateMonitoredItemsRequest */ static UA_DataTypeMember CreateMonitoredItemsRequest_members[4] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SubscriptionId") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_CreateMonitoredItemsRequest, subscriptionId) - offsetof(UA_CreateMonitoredItemsRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("TimestampsToReturn") /* .memberName */ &UA_TYPES[UA_TYPES_TIMESTAMPSTORETURN], /* .memberType */ offsetof(UA_CreateMonitoredItemsRequest, timestampsToReturn) - offsetof(UA_CreateMonitoredItemsRequest, subscriptionId) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ItemsToCreate") /* .memberName */ &UA_TYPES[UA_TYPES_MONITOREDITEMCREATEREQUEST], /* .memberType */ offsetof(UA_CreateMonitoredItemsRequest, itemsToCreateSize) - offsetof(UA_CreateMonitoredItemsRequest, timestampsToReturn) - sizeof(UA_TimestampsToReturn), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* CreateMonitoredItemsResponse */ static UA_DataTypeMember CreateMonitoredItemsResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Results") /* .memberName */ &UA_TYPES[UA_TYPES_MONITOREDITEMCREATERESULT], /* .memberType */ offsetof(UA_CreateMonitoredItemsResponse, resultsSize) - offsetof(UA_CreateMonitoredItemsResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_CreateMonitoredItemsResponse, diagnosticInfosSize) - offsetof(UA_CreateMonitoredItemsResponse, results) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* MonitoredItemModifyRequest */ static UA_DataTypeMember MonitoredItemModifyRequest_members[2] = { { UA_TYPENAME("MonitoredItemId") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RequestedParameters") /* .memberName */ &UA_TYPES[UA_TYPES_MONITORINGPARAMETERS], /* .memberType */ offsetof(UA_MonitoredItemModifyRequest, requestedParameters) - offsetof(UA_MonitoredItemModifyRequest, monitoredItemId) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* MonitoredItemModifyResult */ static UA_DataTypeMember MonitoredItemModifyResult_members[4] = { { UA_TYPENAME("StatusCode") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RevisedSamplingInterval") /* .memberName */ &UA_TYPES[UA_TYPES_DOUBLE], /* .memberType */ offsetof(UA_MonitoredItemModifyResult, revisedSamplingInterval) - offsetof(UA_MonitoredItemModifyResult, statusCode) - sizeof(UA_StatusCode), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RevisedQueueSize") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_MonitoredItemModifyResult, revisedQueueSize) - offsetof(UA_MonitoredItemModifyResult, revisedSamplingInterval) - sizeof(UA_Double), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("FilterResult") /* .memberName */ &UA_TYPES[UA_TYPES_EXTENSIONOBJECT], /* .memberType */ offsetof(UA_MonitoredItemModifyResult, filterResult) - offsetof(UA_MonitoredItemModifyResult, revisedQueueSize) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* ModifyMonitoredItemsRequest */ static UA_DataTypeMember ModifyMonitoredItemsRequest_members[4] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SubscriptionId") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ModifyMonitoredItemsRequest, subscriptionId) - offsetof(UA_ModifyMonitoredItemsRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("TimestampsToReturn") /* .memberName */ &UA_TYPES[UA_TYPES_TIMESTAMPSTORETURN], /* .memberType */ offsetof(UA_ModifyMonitoredItemsRequest, timestampsToReturn) - offsetof(UA_ModifyMonitoredItemsRequest, subscriptionId) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ItemsToModify") /* .memberName */ &UA_TYPES[UA_TYPES_MONITOREDITEMMODIFYREQUEST], /* .memberType */ offsetof(UA_ModifyMonitoredItemsRequest, itemsToModifySize) - offsetof(UA_ModifyMonitoredItemsRequest, timestampsToReturn) - sizeof(UA_TimestampsToReturn), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* ModifyMonitoredItemsResponse */ static UA_DataTypeMember ModifyMonitoredItemsResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Results") /* .memberName */ &UA_TYPES[UA_TYPES_MONITOREDITEMMODIFYRESULT], /* .memberType */ offsetof(UA_ModifyMonitoredItemsResponse, resultsSize) - offsetof(UA_ModifyMonitoredItemsResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_ModifyMonitoredItemsResponse, diagnosticInfosSize) - offsetof(UA_ModifyMonitoredItemsResponse, results) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* SetMonitoringModeRequest */ static UA_DataTypeMember SetMonitoringModeRequest_members[4] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SubscriptionId") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_SetMonitoringModeRequest, subscriptionId) - offsetof(UA_SetMonitoringModeRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("MonitoringMode") /* .memberName */ &UA_TYPES[UA_TYPES_MONITORINGMODE], /* .memberType */ offsetof(UA_SetMonitoringModeRequest, monitoringMode) - offsetof(UA_SetMonitoringModeRequest, subscriptionId) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("MonitoredItemIds") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_SetMonitoringModeRequest, monitoredItemIdsSize) - offsetof(UA_SetMonitoringModeRequest, monitoringMode) - sizeof(UA_MonitoringMode), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* SetMonitoringModeResponse */ static UA_DataTypeMember SetMonitoringModeResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Results") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ offsetof(UA_SetMonitoringModeResponse, resultsSize) - offsetof(UA_SetMonitoringModeResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_SetMonitoringModeResponse, diagnosticInfosSize) - offsetof(UA_SetMonitoringModeResponse, results) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* SetTriggeringRequest */ static UA_DataTypeMember SetTriggeringRequest_members[5] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SubscriptionId") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_SetTriggeringRequest, subscriptionId) - offsetof(UA_SetTriggeringRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("TriggeringItemId") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_SetTriggeringRequest, triggeringItemId) - offsetof(UA_SetTriggeringRequest, subscriptionId) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("LinksToAdd") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_SetTriggeringRequest, linksToAddSize) - offsetof(UA_SetTriggeringRequest, triggeringItemId) - sizeof(UA_UInt32), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("LinksToRemove") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_SetTriggeringRequest, linksToRemoveSize) - offsetof(UA_SetTriggeringRequest, linksToAdd) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* SetTriggeringResponse */ static UA_DataTypeMember SetTriggeringResponse_members[5] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("AddResults") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ offsetof(UA_SetTriggeringResponse, addResultsSize) - offsetof(UA_SetTriggeringResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("AddDiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_SetTriggeringResponse, addDiagnosticInfosSize) - offsetof(UA_SetTriggeringResponse, addResults) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RemoveResults") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ offsetof(UA_SetTriggeringResponse, removeResultsSize) - offsetof(UA_SetTriggeringResponse, addDiagnosticInfos) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RemoveDiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_SetTriggeringResponse, removeDiagnosticInfosSize) - offsetof(UA_SetTriggeringResponse, removeResults) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* DeleteMonitoredItemsRequest */ static UA_DataTypeMember DeleteMonitoredItemsRequest_members[3] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SubscriptionId") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_DeleteMonitoredItemsRequest, subscriptionId) - offsetof(UA_DeleteMonitoredItemsRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("MonitoredItemIds") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_DeleteMonitoredItemsRequest, monitoredItemIdsSize) - offsetof(UA_DeleteMonitoredItemsRequest, subscriptionId) - sizeof(UA_UInt32), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* DeleteMonitoredItemsResponse */ static UA_DataTypeMember DeleteMonitoredItemsResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Results") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ offsetof(UA_DeleteMonitoredItemsResponse, resultsSize) - offsetof(UA_DeleteMonitoredItemsResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_DeleteMonitoredItemsResponse, diagnosticInfosSize) - offsetof(UA_DeleteMonitoredItemsResponse, results) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* CreateSubscriptionRequest */ static UA_DataTypeMember CreateSubscriptionRequest_members[7] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RequestedPublishingInterval") /* .memberName */ &UA_TYPES[UA_TYPES_DOUBLE], /* .memberType */ offsetof(UA_CreateSubscriptionRequest, requestedPublishingInterval) - offsetof(UA_CreateSubscriptionRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RequestedLifetimeCount") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_CreateSubscriptionRequest, requestedLifetimeCount) - offsetof(UA_CreateSubscriptionRequest, requestedPublishingInterval) - sizeof(UA_Double), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RequestedMaxKeepAliveCount") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_CreateSubscriptionRequest, requestedMaxKeepAliveCount) - offsetof(UA_CreateSubscriptionRequest, requestedLifetimeCount) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("MaxNotificationsPerPublish") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_CreateSubscriptionRequest, maxNotificationsPerPublish) - offsetof(UA_CreateSubscriptionRequest, requestedMaxKeepAliveCount) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("PublishingEnabled") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_CreateSubscriptionRequest, publishingEnabled) - offsetof(UA_CreateSubscriptionRequest, maxNotificationsPerPublish) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Priority") /* .memberName */ &UA_TYPES[UA_TYPES_BYTE], /* .memberType */ offsetof(UA_CreateSubscriptionRequest, priority) - offsetof(UA_CreateSubscriptionRequest, publishingEnabled) - sizeof(UA_Boolean), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* CreateSubscriptionResponse */ static UA_DataTypeMember CreateSubscriptionResponse_members[5] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SubscriptionId") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_CreateSubscriptionResponse, subscriptionId) - offsetof(UA_CreateSubscriptionResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RevisedPublishingInterval") /* .memberName */ &UA_TYPES[UA_TYPES_DOUBLE], /* .memberType */ offsetof(UA_CreateSubscriptionResponse, revisedPublishingInterval) - offsetof(UA_CreateSubscriptionResponse, subscriptionId) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RevisedLifetimeCount") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_CreateSubscriptionResponse, revisedLifetimeCount) - offsetof(UA_CreateSubscriptionResponse, revisedPublishingInterval) - sizeof(UA_Double), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RevisedMaxKeepAliveCount") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_CreateSubscriptionResponse, revisedMaxKeepAliveCount) - offsetof(UA_CreateSubscriptionResponse, revisedLifetimeCount) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* ModifySubscriptionRequest */ static UA_DataTypeMember ModifySubscriptionRequest_members[7] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SubscriptionId") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ModifySubscriptionRequest, subscriptionId) - offsetof(UA_ModifySubscriptionRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RequestedPublishingInterval") /* .memberName */ &UA_TYPES[UA_TYPES_DOUBLE], /* .memberType */ offsetof(UA_ModifySubscriptionRequest, requestedPublishingInterval) - offsetof(UA_ModifySubscriptionRequest, subscriptionId) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RequestedLifetimeCount") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ModifySubscriptionRequest, requestedLifetimeCount) - offsetof(UA_ModifySubscriptionRequest, requestedPublishingInterval) - sizeof(UA_Double), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RequestedMaxKeepAliveCount") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ModifySubscriptionRequest, requestedMaxKeepAliveCount) - offsetof(UA_ModifySubscriptionRequest, requestedLifetimeCount) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("MaxNotificationsPerPublish") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ModifySubscriptionRequest, maxNotificationsPerPublish) - offsetof(UA_ModifySubscriptionRequest, requestedMaxKeepAliveCount) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Priority") /* .memberName */ &UA_TYPES[UA_TYPES_BYTE], /* .memberType */ offsetof(UA_ModifySubscriptionRequest, priority) - offsetof(UA_ModifySubscriptionRequest, maxNotificationsPerPublish) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* ModifySubscriptionResponse */ static UA_DataTypeMember ModifySubscriptionResponse_members[4] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RevisedPublishingInterval") /* .memberName */ &UA_TYPES[UA_TYPES_DOUBLE], /* .memberType */ offsetof(UA_ModifySubscriptionResponse, revisedPublishingInterval) - offsetof(UA_ModifySubscriptionResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RevisedLifetimeCount") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ModifySubscriptionResponse, revisedLifetimeCount) - offsetof(UA_ModifySubscriptionResponse, revisedPublishingInterval) - sizeof(UA_Double), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RevisedMaxKeepAliveCount") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ModifySubscriptionResponse, revisedMaxKeepAliveCount) - offsetof(UA_ModifySubscriptionResponse, revisedLifetimeCount) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* SetPublishingModeRequest */ static UA_DataTypeMember SetPublishingModeRequest_members[3] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("PublishingEnabled") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_SetPublishingModeRequest, publishingEnabled) - offsetof(UA_SetPublishingModeRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SubscriptionIds") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_SetPublishingModeRequest, subscriptionIdsSize) - offsetof(UA_SetPublishingModeRequest, publishingEnabled) - sizeof(UA_Boolean), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* SetPublishingModeResponse */ static UA_DataTypeMember SetPublishingModeResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Results") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ offsetof(UA_SetPublishingModeResponse, resultsSize) - offsetof(UA_SetPublishingModeResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_SetPublishingModeResponse, diagnosticInfosSize) - offsetof(UA_SetPublishingModeResponse, results) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* NotificationMessage */ static UA_DataTypeMember NotificationMessage_members[3] = { { UA_TYPENAME("SequenceNumber") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("PublishTime") /* .memberName */ &UA_TYPES[UA_TYPES_DATETIME], /* .memberType */ offsetof(UA_NotificationMessage, publishTime) - offsetof(UA_NotificationMessage, sequenceNumber) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("NotificationData") /* .memberName */ &UA_TYPES[UA_TYPES_EXTENSIONOBJECT], /* .memberType */ offsetof(UA_NotificationMessage, notificationDataSize) - offsetof(UA_NotificationMessage, publishTime) - sizeof(UA_DateTime), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* MonitoredItemNotification */ static UA_DataTypeMember MonitoredItemNotification_members[2] = { { UA_TYPENAME("ClientHandle") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Value") /* .memberName */ &UA_TYPES[UA_TYPES_DATAVALUE], /* .memberType */ offsetof(UA_MonitoredItemNotification, value) - offsetof(UA_MonitoredItemNotification, clientHandle) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* EventFieldList */ static UA_DataTypeMember EventFieldList_members[2] = { { UA_TYPENAME("ClientHandle") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("EventFields") /* .memberName */ &UA_TYPES[UA_TYPES_VARIANT], /* .memberType */ offsetof(UA_EventFieldList, eventFieldsSize) - offsetof(UA_EventFieldList, clientHandle) - sizeof(UA_UInt32), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* HistoryEventFieldList */ static UA_DataTypeMember HistoryEventFieldList_members[1] = { { UA_TYPENAME("EventFields") /* .memberName */ &UA_TYPES[UA_TYPES_VARIANT], /* .memberType */ 0, /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* StatusChangeNotification */ static UA_DataTypeMember StatusChangeNotification_members[2] = { { UA_TYPENAME("Status") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiagnosticInfo") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_StatusChangeNotification, diagnosticInfo) - offsetof(UA_StatusChangeNotification, status) - sizeof(UA_StatusCode), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* SubscriptionAcknowledgement */ static UA_DataTypeMember SubscriptionAcknowledgement_members[2] = { { UA_TYPENAME("SubscriptionId") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SequenceNumber") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_SubscriptionAcknowledgement, sequenceNumber) - offsetof(UA_SubscriptionAcknowledgement, subscriptionId) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* PublishRequest */ static UA_DataTypeMember PublishRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SubscriptionAcknowledgements") /* .memberName */ &UA_TYPES[UA_TYPES_SUBSCRIPTIONACKNOWLEDGEMENT], /* .memberType */ offsetof(UA_PublishRequest, subscriptionAcknowledgementsSize) - offsetof(UA_PublishRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* PublishResponse */ static UA_DataTypeMember PublishResponse_members[7] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SubscriptionId") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_PublishResponse, subscriptionId) - offsetof(UA_PublishResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("AvailableSequenceNumbers") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_PublishResponse, availableSequenceNumbersSize) - offsetof(UA_PublishResponse, subscriptionId) - sizeof(UA_UInt32), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("MoreNotifications") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_PublishResponse, moreNotifications) - offsetof(UA_PublishResponse, availableSequenceNumbers) - sizeof(void *), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("NotificationMessage") /* .memberName */ &UA_TYPES[UA_TYPES_NOTIFICATIONMESSAGE], /* .memberType */ offsetof(UA_PublishResponse, notificationMessage) - offsetof(UA_PublishResponse, moreNotifications) - sizeof(UA_Boolean), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Results") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ offsetof(UA_PublishResponse, resultsSize) - offsetof(UA_PublishResponse, notificationMessage) - sizeof(UA_NotificationMessage), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_PublishResponse, diagnosticInfosSize) - offsetof(UA_PublishResponse, results) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* RepublishRequest */ static UA_DataTypeMember RepublishRequest_members[3] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SubscriptionId") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_RepublishRequest, subscriptionId) - offsetof(UA_RepublishRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RetransmitSequenceNumber") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_RepublishRequest, retransmitSequenceNumber) - offsetof(UA_RepublishRequest, subscriptionId) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* RepublishResponse */ static UA_DataTypeMember RepublishResponse_members[2] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("NotificationMessage") /* .memberName */ &UA_TYPES[UA_TYPES_NOTIFICATIONMESSAGE], /* .memberType */ offsetof(UA_RepublishResponse, notificationMessage) - offsetof(UA_RepublishResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* TransferResult */ static UA_DataTypeMember TransferResult_members[2] = { { UA_TYPENAME("StatusCode") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("AvailableSequenceNumbers") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_TransferResult, availableSequenceNumbersSize) - offsetof(UA_TransferResult, statusCode) - sizeof(UA_StatusCode), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* TransferSubscriptionsRequest */ static UA_DataTypeMember TransferSubscriptionsRequest_members[3] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SubscriptionIds") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_TransferSubscriptionsRequest, subscriptionIdsSize) - offsetof(UA_TransferSubscriptionsRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SendInitialValues") /* .memberName */ &UA_TYPES[UA_TYPES_BOOLEAN], /* .memberType */ offsetof(UA_TransferSubscriptionsRequest, sendInitialValues) - offsetof(UA_TransferSubscriptionsRequest, subscriptionIds) - sizeof(void *), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* TransferSubscriptionsResponse */ static UA_DataTypeMember TransferSubscriptionsResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Results") /* .memberName */ &UA_TYPES[UA_TYPES_TRANSFERRESULT], /* .memberType */ offsetof(UA_TransferSubscriptionsResponse, resultsSize) - offsetof(UA_TransferSubscriptionsResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_TransferSubscriptionsResponse, diagnosticInfosSize) - offsetof(UA_TransferSubscriptionsResponse, results) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* DeleteSubscriptionsRequest */ static UA_DataTypeMember DeleteSubscriptionsRequest_members[2] = { { UA_TYPENAME("RequestHeader") /* .memberName */ &UA_TYPES[UA_TYPES_REQUESTHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SubscriptionIds") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_DeleteSubscriptionsRequest, subscriptionIdsSize) - offsetof(UA_DeleteSubscriptionsRequest, requestHeader) - sizeof(UA_RequestHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* DeleteSubscriptionsResponse */ static UA_DataTypeMember DeleteSubscriptionsResponse_members[3] = { { UA_TYPENAME("ResponseHeader") /* .memberName */ &UA_TYPES[UA_TYPES_RESPONSEHEADER], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Results") /* .memberName */ &UA_TYPES[UA_TYPES_STATUSCODE], /* .memberType */ offsetof(UA_DeleteSubscriptionsResponse, resultsSize) - offsetof(UA_DeleteSubscriptionsResponse, responseHeader) - sizeof(UA_ResponseHeader), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_DeleteSubscriptionsResponse, diagnosticInfosSize) - offsetof(UA_DeleteSubscriptionsResponse, results) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* BuildInfo */ static UA_DataTypeMember BuildInfo_members[6] = { { UA_TYPENAME("ProductUri") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ManufacturerName") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_BuildInfo, manufacturerName) - offsetof(UA_BuildInfo, productUri) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ProductName") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_BuildInfo, productName) - offsetof(UA_BuildInfo, manufacturerName) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SoftwareVersion") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_BuildInfo, softwareVersion) - offsetof(UA_BuildInfo, productName) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("BuildNumber") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_BuildInfo, buildNumber) - offsetof(UA_BuildInfo, softwareVersion) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("BuildDate") /* .memberName */ &UA_TYPES[UA_TYPES_DATETIME], /* .memberType */ offsetof(UA_BuildInfo, buildDate) - offsetof(UA_BuildInfo, buildNumber) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* RedundancySupport */ #define RedundancySupport_members NULL /* ServerState */ #define ServerState_members NULL /* ServerDiagnosticsSummaryDataType */ static UA_DataTypeMember ServerDiagnosticsSummaryDataType_members[12] = { { UA_TYPENAME("ServerViewCount") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("CurrentSessionCount") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ServerDiagnosticsSummaryDataType, currentSessionCount) - offsetof(UA_ServerDiagnosticsSummaryDataType, serverViewCount) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("CumulatedSessionCount") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ServerDiagnosticsSummaryDataType, cumulatedSessionCount) - offsetof(UA_ServerDiagnosticsSummaryDataType, currentSessionCount) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SecurityRejectedSessionCount") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ServerDiagnosticsSummaryDataType, securityRejectedSessionCount) - offsetof(UA_ServerDiagnosticsSummaryDataType, cumulatedSessionCount) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RejectedSessionCount") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ServerDiagnosticsSummaryDataType, rejectedSessionCount) - offsetof(UA_ServerDiagnosticsSummaryDataType, securityRejectedSessionCount) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SessionTimeoutCount") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ServerDiagnosticsSummaryDataType, sessionTimeoutCount) - offsetof(UA_ServerDiagnosticsSummaryDataType, rejectedSessionCount) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SessionAbortCount") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ServerDiagnosticsSummaryDataType, sessionAbortCount) - offsetof(UA_ServerDiagnosticsSummaryDataType, sessionTimeoutCount) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("CurrentSubscriptionCount") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ServerDiagnosticsSummaryDataType, currentSubscriptionCount) - offsetof(UA_ServerDiagnosticsSummaryDataType, sessionAbortCount) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("CumulatedSubscriptionCount") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ServerDiagnosticsSummaryDataType, cumulatedSubscriptionCount) - offsetof(UA_ServerDiagnosticsSummaryDataType, currentSubscriptionCount) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("PublishingIntervalCount") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ServerDiagnosticsSummaryDataType, publishingIntervalCount) - offsetof(UA_ServerDiagnosticsSummaryDataType, cumulatedSubscriptionCount) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SecurityRejectedRequestsCount") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ServerDiagnosticsSummaryDataType, securityRejectedRequestsCount) - offsetof(UA_ServerDiagnosticsSummaryDataType, publishingIntervalCount) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RejectedRequestsCount") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ServerDiagnosticsSummaryDataType, rejectedRequestsCount) - offsetof(UA_ServerDiagnosticsSummaryDataType, securityRejectedRequestsCount) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* ServerStatusDataType */ static UA_DataTypeMember ServerStatusDataType_members[6] = { { UA_TYPENAME("StartTime") /* .memberName */ &UA_TYPES[UA_TYPES_DATETIME], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("CurrentTime") /* .memberName */ &UA_TYPES[UA_TYPES_DATETIME], /* .memberType */ offsetof(UA_ServerStatusDataType, currentTime) - offsetof(UA_ServerStatusDataType, startTime) - sizeof(UA_DateTime), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("State") /* .memberName */ &UA_TYPES[UA_TYPES_SERVERSTATE], /* .memberType */ offsetof(UA_ServerStatusDataType, state) - offsetof(UA_ServerStatusDataType, currentTime) - sizeof(UA_DateTime), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("BuildInfo") /* .memberName */ &UA_TYPES[UA_TYPES_BUILDINFO], /* .memberType */ offsetof(UA_ServerStatusDataType, buildInfo) - offsetof(UA_ServerStatusDataType, state) - sizeof(UA_ServerState), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SecondsTillShutdown") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_ServerStatusDataType, secondsTillShutdown) - offsetof(UA_ServerStatusDataType, buildInfo) - sizeof(UA_BuildInfo), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ShutdownReason") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_ServerStatusDataType, shutdownReason) - offsetof(UA_ServerStatusDataType, secondsTillShutdown) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* Range */ static UA_DataTypeMember Range_members[2] = { { UA_TYPENAME("Low") /* .memberName */ &UA_TYPES[UA_TYPES_DOUBLE], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("High") /* .memberName */ &UA_TYPES[UA_TYPES_DOUBLE], /* .memberType */ offsetof(UA_Range, high) - offsetof(UA_Range, low) - sizeof(UA_Double), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* EUInformation */ static UA_DataTypeMember EUInformation_members[4] = { { UA_TYPENAME("NamespaceUri") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("UnitId") /* .memberName */ &UA_TYPES[UA_TYPES_INT32], /* .memberType */ offsetof(UA_EUInformation, unitId) - offsetof(UA_EUInformation, namespaceUri) - sizeof(UA_String), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DisplayName") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_EUInformation, displayName) - offsetof(UA_EUInformation, unitId) - sizeof(UA_Int32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Description") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_EUInformation, description) - offsetof(UA_EUInformation, displayName) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* AxisScaleEnumeration */ #define AxisScaleEnumeration_members NULL /* ComplexNumberType */ static UA_DataTypeMember ComplexNumberType_members[2] = { { UA_TYPENAME("Real") /* .memberName */ &UA_TYPES[UA_TYPES_FLOAT], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Imaginary") /* .memberName */ &UA_TYPES[UA_TYPES_FLOAT], /* .memberType */ offsetof(UA_ComplexNumberType, imaginary) - offsetof(UA_ComplexNumberType, real) - sizeof(UA_Float), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* DoubleComplexNumberType */ static UA_DataTypeMember DoubleComplexNumberType_members[2] = { { UA_TYPENAME("Real") /* .memberName */ &UA_TYPES[UA_TYPES_DOUBLE], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Imaginary") /* .memberName */ &UA_TYPES[UA_TYPES_DOUBLE], /* .memberType */ offsetof(UA_DoubleComplexNumberType, imaginary) - offsetof(UA_DoubleComplexNumberType, real) - sizeof(UA_Double), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* AxisInformation */ static UA_DataTypeMember AxisInformation_members[5] = { { UA_TYPENAME("EngineeringUnits") /* .memberName */ &UA_TYPES[UA_TYPES_EUINFORMATION], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("EURange") /* .memberName */ &UA_TYPES[UA_TYPES_RANGE], /* .memberType */ offsetof(UA_AxisInformation, eURange) - offsetof(UA_AxisInformation, engineeringUnits) - sizeof(UA_EUInformation), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Title") /* .memberName */ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], /* .memberType */ offsetof(UA_AxisInformation, title) - offsetof(UA_AxisInformation, eURange) - sizeof(UA_Range), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("AxisScaleType") /* .memberName */ &UA_TYPES[UA_TYPES_AXISSCALEENUMERATION], /* .memberType */ offsetof(UA_AxisInformation, axisScaleType) - offsetof(UA_AxisInformation, title) - sizeof(UA_LocalizedText), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("AxisSteps") /* .memberName */ &UA_TYPES[UA_TYPES_DOUBLE], /* .memberType */ offsetof(UA_AxisInformation, axisStepsSize) - offsetof(UA_AxisInformation, axisScaleType) - sizeof(UA_AxisScaleEnumeration), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* XVType */ static UA_DataTypeMember XVType_members[2] = { { UA_TYPENAME("X") /* .memberName */ &UA_TYPES[UA_TYPES_DOUBLE], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Value") /* .memberName */ &UA_TYPES[UA_TYPES_FLOAT], /* .memberType */ offsetof(UA_XVType, value) - offsetof(UA_XVType, x) - sizeof(UA_Double), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* EnumDefinition */ static UA_DataTypeMember EnumDefinition_members[1] = { { UA_TYPENAME("Fields") /* .memberName */ &UA_TYPES[UA_TYPES_ENUMFIELD], /* .memberType */ 0, /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* ReadEventDetails */ static UA_DataTypeMember ReadEventDetails_members[4] = { { UA_TYPENAME("NumValuesPerNode") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("StartTime") /* .memberName */ &UA_TYPES[UA_TYPES_DATETIME], /* .memberType */ offsetof(UA_ReadEventDetails, startTime) - offsetof(UA_ReadEventDetails, numValuesPerNode) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("EndTime") /* .memberName */ &UA_TYPES[UA_TYPES_DATETIME], /* .memberType */ offsetof(UA_ReadEventDetails, endTime) - offsetof(UA_ReadEventDetails, startTime) - sizeof(UA_DateTime), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Filter") /* .memberName */ &UA_TYPES[UA_TYPES_EVENTFILTER], /* .memberType */ offsetof(UA_ReadEventDetails, filter) - offsetof(UA_ReadEventDetails, endTime) - sizeof(UA_DateTime), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* ReadProcessedDetails */ static UA_DataTypeMember ReadProcessedDetails_members[5] = { { UA_TYPENAME("StartTime") /* .memberName */ &UA_TYPES[UA_TYPES_DATETIME], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("EndTime") /* .memberName */ &UA_TYPES[UA_TYPES_DATETIME], /* .memberType */ offsetof(UA_ReadProcessedDetails, endTime) - offsetof(UA_ReadProcessedDetails, startTime) - sizeof(UA_DateTime), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ProcessingInterval") /* .memberName */ &UA_TYPES[UA_TYPES_DOUBLE], /* .memberType */ offsetof(UA_ReadProcessedDetails, processingInterval) - offsetof(UA_ReadProcessedDetails, endTime) - sizeof(UA_DateTime), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("AggregateType") /* .memberName */ &UA_TYPES[UA_TYPES_NODEID], /* .memberType */ offsetof(UA_ReadProcessedDetails, aggregateTypeSize) - offsetof(UA_ReadProcessedDetails, processingInterval) - sizeof(UA_Double), /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("AggregateConfiguration") /* .memberName */ &UA_TYPES[UA_TYPES_AGGREGATECONFIGURATION], /* .memberType */ offsetof(UA_ReadProcessedDetails, aggregateConfiguration) - offsetof(UA_ReadProcessedDetails, aggregateType) - sizeof(void *), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* ModificationInfo */ static UA_DataTypeMember ModificationInfo_members[3] = { { UA_TYPENAME("ModificationTime") /* .memberName */ &UA_TYPES[UA_TYPES_DATETIME], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("UpdateType") /* .memberName */ &UA_TYPES[UA_TYPES_HISTORYUPDATETYPE], /* .memberType */ offsetof(UA_ModificationInfo, updateType) - offsetof(UA_ModificationInfo, modificationTime) - sizeof(UA_DateTime), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("UserName") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_ModificationInfo, userName) - offsetof(UA_ModificationInfo, updateType) - sizeof(UA_HistoryUpdateType), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* HistoryModifiedData */ static UA_DataTypeMember HistoryModifiedData_members[2] = { { UA_TYPENAME("DataValues") /* .memberName */ &UA_TYPES[UA_TYPES_DATAVALUE], /* .memberType */ 0, /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ModificationInfos") /* .memberName */ &UA_TYPES[UA_TYPES_MODIFICATIONINFO], /* .memberType */ offsetof(UA_HistoryModifiedData, modificationInfosSize) - offsetof(UA_HistoryModifiedData, dataValues) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* HistoryEvent */ static UA_DataTypeMember HistoryEvent_members[1] = { { UA_TYPENAME("Events") /* .memberName */ &UA_TYPES[UA_TYPES_HISTORYEVENTFIELDLIST], /* .memberType */ 0, /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* DataChangeNotification */ static UA_DataTypeMember DataChangeNotification_members[2] = { { UA_TYPENAME("MonitoredItems") /* .memberName */ &UA_TYPES[UA_TYPES_MONITOREDITEMNOTIFICATION], /* .memberType */ 0, /* .padding */ true, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("DiagnosticInfos") /* .memberName */ &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], /* .memberType */ offsetof(UA_DataChangeNotification, diagnosticInfosSize) - offsetof(UA_DataChangeNotification, monitoredItems) - sizeof(void *), /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; /* EventNotificationList */ static UA_DataTypeMember EventNotificationList_members[1] = { { UA_TYPENAME("Events") /* .memberName */ &UA_TYPES[UA_TYPES_EVENTFIELDLIST], /* .memberType */ 0, /* .padding */ true, /* .isArray */ false /* .isOptional */ },}; const UA_DataType UA_TYPES[UA_TYPES_COUNT] = { /* Boolean */ { UA_TYPENAME("Boolean") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {1LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_Boolean), /* .memSize */ UA_DATATYPEKIND_BOOLEAN, /* .typeKind */ true, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ Boolean_members /* .members */ }, /* SByte */ { UA_TYPENAME("SByte") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {2LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_SByte), /* .memSize */ UA_DATATYPEKIND_SBYTE, /* .typeKind */ true, /* .pointerFree */ true, /* .overlayable */ 0, /* .membersSize */ SByte_members /* .members */ }, /* Byte */ { UA_TYPENAME("Byte") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {3LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_Byte), /* .memSize */ UA_DATATYPEKIND_BYTE, /* .typeKind */ true, /* .pointerFree */ true, /* .overlayable */ 0, /* .membersSize */ Byte_members /* .members */ }, /* Int16 */ { UA_TYPENAME("Int16") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {4LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_Int16), /* .memSize */ UA_DATATYPEKIND_INT16, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ Int16_members /* .members */ }, /* UInt16 */ { UA_TYPENAME("UInt16") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {5LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_UInt16), /* .memSize */ UA_DATATYPEKIND_UINT16, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ UInt16_members /* .members */ }, /* Int32 */ { UA_TYPENAME("Int32") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {6LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_Int32), /* .memSize */ UA_DATATYPEKIND_INT32, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ Int32_members /* .members */ }, /* UInt32 */ { UA_TYPENAME("UInt32") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {7LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_UInt32), /* .memSize */ UA_DATATYPEKIND_UINT32, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ UInt32_members /* .members */ }, /* Int64 */ { UA_TYPENAME("Int64") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {8LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_Int64), /* .memSize */ UA_DATATYPEKIND_INT64, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ Int64_members /* .members */ }, /* UInt64 */ { UA_TYPENAME("UInt64") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {9LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_UInt64), /* .memSize */ UA_DATATYPEKIND_UINT64, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ UInt64_members /* .members */ }, /* Float */ { UA_TYPENAME("Float") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {10LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_Float), /* .memSize */ UA_DATATYPEKIND_FLOAT, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_FLOAT, /* .overlayable */ 0, /* .membersSize */ Float_members /* .members */ }, /* Double */ { UA_TYPENAME("Double") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {11LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_Double), /* .memSize */ UA_DATATYPEKIND_DOUBLE, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_FLOAT, /* .overlayable */ 0, /* .membersSize */ Double_members /* .members */ }, /* String */ { UA_TYPENAME("String") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {12LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_String), /* .memSize */ UA_DATATYPEKIND_STRING, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ String_members /* .members */ }, /* DateTime */ { UA_TYPENAME("DateTime") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {13LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_DateTime), /* .memSize */ UA_DATATYPEKIND_DATETIME, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ DateTime_members /* .members */ }, /* Guid */ { UA_TYPENAME("Guid") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {14LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_Guid), /* .memSize */ 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 */ Guid_members /* .members */ }, /* ByteString */ { UA_TYPENAME("ByteString") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {15LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_ByteString), /* .memSize */ UA_DATATYPEKIND_BYTESTRING, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ ByteString_members /* .members */ }, /* XmlElement */ { UA_TYPENAME("XmlElement") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {16LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_XmlElement), /* .memSize */ UA_DATATYPEKIND_XMLELEMENT, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ XmlElement_members /* .members */ }, /* NodeId */ { UA_TYPENAME("NodeId") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {17LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_NodeId), /* .memSize */ UA_DATATYPEKIND_NODEID, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ NodeId_members /* .members */ }, /* ExpandedNodeId */ { UA_TYPENAME("ExpandedNodeId") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {18LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_ExpandedNodeId), /* .memSize */ UA_DATATYPEKIND_EXPANDEDNODEID, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ ExpandedNodeId_members /* .members */ }, /* StatusCode */ { UA_TYPENAME("StatusCode") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {19LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_StatusCode), /* .memSize */ UA_DATATYPEKIND_STATUSCODE, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ StatusCode_members /* .members */ }, /* QualifiedName */ { UA_TYPENAME("QualifiedName") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {20LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_QualifiedName), /* .memSize */ UA_DATATYPEKIND_QUALIFIEDNAME, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ QualifiedName_members /* .members */ }, /* LocalizedText */ { UA_TYPENAME("LocalizedText") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {21LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_LocalizedText), /* .memSize */ UA_DATATYPEKIND_LOCALIZEDTEXT, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ LocalizedText_members /* .members */ }, /* ExtensionObject */ { UA_TYPENAME("ExtensionObject") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {22LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_ExtensionObject), /* .memSize */ UA_DATATYPEKIND_EXTENSIONOBJECT, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ ExtensionObject_members /* .members */ }, /* DataValue */ { UA_TYPENAME("DataValue") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {23LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_DataValue), /* .memSize */ UA_DATATYPEKIND_DATAVALUE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ DataValue_members /* .members */ }, /* Variant */ { UA_TYPENAME("Variant") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {24LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_Variant), /* .memSize */ UA_DATATYPEKIND_VARIANT, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ Variant_members /* .members */ }, /* DiagnosticInfo */ { UA_TYPENAME("DiagnosticInfo") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {25LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_DiagnosticInfo), /* .memSize */ UA_DATATYPEKIND_DIAGNOSTICINFO, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ DiagnosticInfo_members /* .members */ }, /* KeyValuePair */ { UA_TYPENAME("KeyValuePair") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {14533LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {14846LU}}, /* .binaryEncodingId */ sizeof(UA_KeyValuePair), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ KeyValuePair_members /* .members */ }, /* NodeClass */ { UA_TYPENAME("NodeClass") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {257LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_NodeClass), /* .memSize */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ NodeClass_members /* .members */ }, /* StructureType */ { UA_TYPENAME("StructureType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {98LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_StructureType), /* .memSize */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ StructureType_members /* .members */ }, /* StructureField */ { UA_TYPENAME("StructureField") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {101LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {14844LU}}, /* .binaryEncodingId */ sizeof(UA_StructureField), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 7, /* .membersSize */ StructureField_members /* .members */ }, /* StructureDefinition */ { UA_TYPENAME("StructureDefinition") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {99LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {122LU}}, /* .binaryEncodingId */ sizeof(UA_StructureDefinition), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ StructureDefinition_members /* .members */ }, /* Argument */ { UA_TYPENAME("Argument") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {296LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {298LU}}, /* .binaryEncodingId */ sizeof(UA_Argument), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ Argument_members /* .members */ }, /* EnumValueType */ { UA_TYPENAME("EnumValueType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {7594LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {8251LU}}, /* .binaryEncodingId */ sizeof(UA_EnumValueType), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ EnumValueType_members /* .members */ }, /* EnumField */ { UA_TYPENAME("EnumField") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {102LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {14845LU}}, /* .binaryEncodingId */ sizeof(UA_EnumField), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ EnumField_members /* .members */ }, /* Duration */ { UA_TYPENAME("Duration") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {290LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_Duration), /* .memSize */ UA_DATATYPEKIND_DOUBLE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ Duration_members /* .members */ }, /* UtcTime */ { UA_TYPENAME("UtcTime") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {294LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_UtcTime), /* .memSize */ UA_DATATYPEKIND_DATETIME, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ UtcTime_members /* .members */ }, /* LocaleId */ { UA_TYPENAME("LocaleId") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {295LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_LocaleId), /* .memSize */ UA_DATATYPEKIND_STRING, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 0, /* .membersSize */ LocaleId_members /* .members */ }, /* TimeZoneDataType */ { UA_TYPENAME("TimeZoneDataType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {8912LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {8917LU}}, /* .binaryEncodingId */ sizeof(UA_TimeZoneDataType), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ TimeZoneDataType_members /* .members */ }, /* ApplicationType */ { UA_TYPENAME("ApplicationType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {307LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_ApplicationType), /* .memSize */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ ApplicationType_members /* .members */ }, /* ApplicationDescription */ { UA_TYPENAME("ApplicationDescription") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {308LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {310LU}}, /* .binaryEncodingId */ sizeof(UA_ApplicationDescription), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 7, /* .membersSize */ ApplicationDescription_members /* .members */ }, /* RequestHeader */ { UA_TYPENAME("RequestHeader") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {389LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {391LU}}, /* .binaryEncodingId */ sizeof(UA_RequestHeader), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 7, /* .membersSize */ RequestHeader_members /* .members */ }, /* ResponseHeader */ { UA_TYPENAME("ResponseHeader") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {392LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {394LU}}, /* .binaryEncodingId */ sizeof(UA_ResponseHeader), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 6, /* .membersSize */ ResponseHeader_members /* .members */ }, /* ServiceFault */ { UA_TYPENAME("ServiceFault") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {395LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {397LU}}, /* .binaryEncodingId */ sizeof(UA_ServiceFault), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ ServiceFault_members /* .members */ }, /* FindServersRequest */ { UA_TYPENAME("FindServersRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {420LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {422LU}}, /* .binaryEncodingId */ sizeof(UA_FindServersRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ FindServersRequest_members /* .members */ }, /* FindServersResponse */ { UA_TYPENAME("FindServersResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {423LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {425LU}}, /* .binaryEncodingId */ sizeof(UA_FindServersResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ FindServersResponse_members /* .members */ }, /* MessageSecurityMode */ { UA_TYPENAME("MessageSecurityMode") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {302LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_MessageSecurityMode), /* .memSize */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ MessageSecurityMode_members /* .members */ }, /* UserTokenType */ { UA_TYPENAME("UserTokenType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {303LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_UserTokenType), /* .memSize */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ UserTokenType_members /* .members */ }, /* UserTokenPolicy */ { UA_TYPENAME("UserTokenPolicy") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {304LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {306LU}}, /* .binaryEncodingId */ sizeof(UA_UserTokenPolicy), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ UserTokenPolicy_members /* .members */ }, /* EndpointDescription */ { UA_TYPENAME("EndpointDescription") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {312LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {314LU}}, /* .binaryEncodingId */ sizeof(UA_EndpointDescription), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 8, /* .membersSize */ EndpointDescription_members /* .members */ }, /* GetEndpointsRequest */ { UA_TYPENAME("GetEndpointsRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {426LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {428LU}}, /* .binaryEncodingId */ sizeof(UA_GetEndpointsRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ GetEndpointsRequest_members /* .members */ }, /* GetEndpointsResponse */ { UA_TYPENAME("GetEndpointsResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {429LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {431LU}}, /* .binaryEncodingId */ sizeof(UA_GetEndpointsResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ GetEndpointsResponse_members /* .members */ }, /* SecurityTokenRequestType */ { UA_TYPENAME("SecurityTokenRequestType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {315LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_SecurityTokenRequestType), /* .memSize */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ SecurityTokenRequestType_members /* .members */ }, /* ChannelSecurityToken */ { UA_TYPENAME("ChannelSecurityToken") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {441LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {443LU}}, /* .binaryEncodingId */ sizeof(UA_ChannelSecurityToken), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ ChannelSecurityToken_members /* .members */ }, /* OpenSecureChannelRequest */ { UA_TYPENAME("OpenSecureChannelRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {444LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {446LU}}, /* .binaryEncodingId */ sizeof(UA_OpenSecureChannelRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 6, /* .membersSize */ OpenSecureChannelRequest_members /* .members */ }, /* OpenSecureChannelResponse */ { UA_TYPENAME("OpenSecureChannelResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {447LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {449LU}}, /* .binaryEncodingId */ sizeof(UA_OpenSecureChannelResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ OpenSecureChannelResponse_members /* .members */ }, /* CloseSecureChannelRequest */ { UA_TYPENAME("CloseSecureChannelRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {450LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {452LU}}, /* .binaryEncodingId */ sizeof(UA_CloseSecureChannelRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ CloseSecureChannelRequest_members /* .members */ }, /* CloseSecureChannelResponse */ { UA_TYPENAME("CloseSecureChannelResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {453LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {455LU}}, /* .binaryEncodingId */ sizeof(UA_CloseSecureChannelResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ CloseSecureChannelResponse_members /* .members */ }, /* SignedSoftwareCertificate */ { UA_TYPENAME("SignedSoftwareCertificate") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {344LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {346LU}}, /* .binaryEncodingId */ sizeof(UA_SignedSoftwareCertificate), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ SignedSoftwareCertificate_members /* .members */ }, /* SignatureData */ { UA_TYPENAME("SignatureData") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {456LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {458LU}}, /* .binaryEncodingId */ sizeof(UA_SignatureData), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ SignatureData_members /* .members */ }, /* CreateSessionRequest */ { UA_TYPENAME("CreateSessionRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {459LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {461LU}}, /* .binaryEncodingId */ sizeof(UA_CreateSessionRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 9, /* .membersSize */ CreateSessionRequest_members /* .members */ }, /* CreateSessionResponse */ { UA_TYPENAME("CreateSessionResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {462LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {464LU}}, /* .binaryEncodingId */ sizeof(UA_CreateSessionResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 10, /* .membersSize */ CreateSessionResponse_members /* .members */ }, /* UserIdentityToken */ { UA_TYPENAME("UserIdentityToken") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {316LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {318LU}}, /* .binaryEncodingId */ sizeof(UA_UserIdentityToken), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ UserIdentityToken_members /* .members */ }, /* AnonymousIdentityToken */ { UA_TYPENAME("AnonymousIdentityToken") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {319LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {321LU}}, /* .binaryEncodingId */ sizeof(UA_AnonymousIdentityToken), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ AnonymousIdentityToken_members /* .members */ }, /* UserNameIdentityToken */ { UA_TYPENAME("UserNameIdentityToken") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {322LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {324LU}}, /* .binaryEncodingId */ sizeof(UA_UserNameIdentityToken), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ UserNameIdentityToken_members /* .members */ }, /* X509IdentityToken */ { UA_TYPENAME("X509IdentityToken") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {325LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {327LU}}, /* .binaryEncodingId */ sizeof(UA_X509IdentityToken), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ X509IdentityToken_members /* .members */ }, /* IssuedIdentityToken */ { UA_TYPENAME("IssuedIdentityToken") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {938LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {940LU}}, /* .binaryEncodingId */ sizeof(UA_IssuedIdentityToken), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ IssuedIdentityToken_members /* .members */ }, /* ActivateSessionRequest */ { UA_TYPENAME("ActivateSessionRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {465LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {467LU}}, /* .binaryEncodingId */ sizeof(UA_ActivateSessionRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 6, /* .membersSize */ ActivateSessionRequest_members /* .members */ }, /* ActivateSessionResponse */ { UA_TYPENAME("ActivateSessionResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {468LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {470LU}}, /* .binaryEncodingId */ sizeof(UA_ActivateSessionResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ ActivateSessionResponse_members /* .members */ }, /* CloseSessionRequest */ { UA_TYPENAME("CloseSessionRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {471LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {473LU}}, /* .binaryEncodingId */ sizeof(UA_CloseSessionRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ CloseSessionRequest_members /* .members */ }, /* CloseSessionResponse */ { UA_TYPENAME("CloseSessionResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {474LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {476LU}}, /* .binaryEncodingId */ sizeof(UA_CloseSessionResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ CloseSessionResponse_members /* .members */ }, /* NodeAttributesMask */ { UA_TYPENAME("NodeAttributesMask") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {348LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_NodeAttributesMask), /* .memSize */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ NodeAttributesMask_members /* .members */ }, /* NodeAttributes */ { UA_TYPENAME("NodeAttributes") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {349LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {351LU}}, /* .binaryEncodingId */ sizeof(UA_NodeAttributes), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ NodeAttributes_members /* .members */ }, /* ObjectAttributes */ { UA_TYPENAME("ObjectAttributes") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {352LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {354LU}}, /* .binaryEncodingId */ sizeof(UA_ObjectAttributes), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 6, /* .membersSize */ ObjectAttributes_members /* .members */ }, /* VariableAttributes */ { UA_TYPENAME("VariableAttributes") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {355LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {357LU}}, /* .binaryEncodingId */ sizeof(UA_VariableAttributes), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 13, /* .membersSize */ VariableAttributes_members /* .members */ }, /* MethodAttributes */ { UA_TYPENAME("MethodAttributes") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {358LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {360LU}}, /* .binaryEncodingId */ sizeof(UA_MethodAttributes), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 7, /* .membersSize */ MethodAttributes_members /* .members */ }, /* ObjectTypeAttributes */ { UA_TYPENAME("ObjectTypeAttributes") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {361LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {363LU}}, /* .binaryEncodingId */ sizeof(UA_ObjectTypeAttributes), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 6, /* .membersSize */ ObjectTypeAttributes_members /* .members */ }, /* VariableTypeAttributes */ { UA_TYPENAME("VariableTypeAttributes") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {364LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {366LU}}, /* .binaryEncodingId */ sizeof(UA_VariableTypeAttributes), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 10, /* .membersSize */ VariableTypeAttributes_members /* .members */ }, /* ReferenceTypeAttributes */ { UA_TYPENAME("ReferenceTypeAttributes") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {367LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {369LU}}, /* .binaryEncodingId */ sizeof(UA_ReferenceTypeAttributes), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 8, /* .membersSize */ ReferenceTypeAttributes_members /* .members */ }, /* DataTypeAttributes */ { UA_TYPENAME("DataTypeAttributes") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {370LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {372LU}}, /* .binaryEncodingId */ sizeof(UA_DataTypeAttributes), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 6, /* .membersSize */ DataTypeAttributes_members /* .members */ }, /* ViewAttributes */ { UA_TYPENAME("ViewAttributes") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {373LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {375LU}}, /* .binaryEncodingId */ sizeof(UA_ViewAttributes), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 7, /* .membersSize */ ViewAttributes_members /* .members */ }, /* AddNodesItem */ { UA_TYPENAME("AddNodesItem") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {376LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {378LU}}, /* .binaryEncodingId */ sizeof(UA_AddNodesItem), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 7, /* .membersSize */ AddNodesItem_members /* .members */ }, /* AddNodesResult */ { UA_TYPENAME("AddNodesResult") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {483LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {485LU}}, /* .binaryEncodingId */ sizeof(UA_AddNodesResult), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ AddNodesResult_members /* .members */ }, /* AddNodesRequest */ { UA_TYPENAME("AddNodesRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {486LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {488LU}}, /* .binaryEncodingId */ sizeof(UA_AddNodesRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ AddNodesRequest_members /* .members */ }, /* AddNodesResponse */ { UA_TYPENAME("AddNodesResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {489LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {491LU}}, /* .binaryEncodingId */ sizeof(UA_AddNodesResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ AddNodesResponse_members /* .members */ }, /* AddReferencesItem */ { UA_TYPENAME("AddReferencesItem") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {379LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {381LU}}, /* .binaryEncodingId */ sizeof(UA_AddReferencesItem), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 6, /* .membersSize */ AddReferencesItem_members /* .members */ }, /* AddReferencesRequest */ { UA_TYPENAME("AddReferencesRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {492LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {494LU}}, /* .binaryEncodingId */ sizeof(UA_AddReferencesRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ AddReferencesRequest_members /* .members */ }, /* AddReferencesResponse */ { UA_TYPENAME("AddReferencesResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {495LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {497LU}}, /* .binaryEncodingId */ sizeof(UA_AddReferencesResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ AddReferencesResponse_members /* .members */ }, /* DeleteNodesItem */ { UA_TYPENAME("DeleteNodesItem") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {382LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {384LU}}, /* .binaryEncodingId */ sizeof(UA_DeleteNodesItem), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ DeleteNodesItem_members /* .members */ }, /* DeleteNodesRequest */ { UA_TYPENAME("DeleteNodesRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {498LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {500LU}}, /* .binaryEncodingId */ sizeof(UA_DeleteNodesRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ DeleteNodesRequest_members /* .members */ }, /* DeleteNodesResponse */ { UA_TYPENAME("DeleteNodesResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {501LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {503LU}}, /* .binaryEncodingId */ sizeof(UA_DeleteNodesResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ DeleteNodesResponse_members /* .members */ }, /* DeleteReferencesItem */ { UA_TYPENAME("DeleteReferencesItem") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {385LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {387LU}}, /* .binaryEncodingId */ sizeof(UA_DeleteReferencesItem), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ DeleteReferencesItem_members /* .members */ }, /* DeleteReferencesRequest */ { UA_TYPENAME("DeleteReferencesRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {504LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {506LU}}, /* .binaryEncodingId */ sizeof(UA_DeleteReferencesRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ DeleteReferencesRequest_members /* .members */ }, /* DeleteReferencesResponse */ { UA_TYPENAME("DeleteReferencesResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {507LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {509LU}}, /* .binaryEncodingId */ sizeof(UA_DeleteReferencesResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ DeleteReferencesResponse_members /* .members */ }, /* BrowseDirection */ { UA_TYPENAME("BrowseDirection") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {510LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_BrowseDirection), /* .memSize */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ BrowseDirection_members /* .members */ }, /* ViewDescription */ { UA_TYPENAME("ViewDescription") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {511LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {513LU}}, /* .binaryEncodingId */ sizeof(UA_ViewDescription), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ ViewDescription_members /* .members */ }, /* BrowseDescription */ { UA_TYPENAME("BrowseDescription") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {514LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {516LU}}, /* .binaryEncodingId */ sizeof(UA_BrowseDescription), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 6, /* .membersSize */ BrowseDescription_members /* .members */ }, /* BrowseResultMask */ { UA_TYPENAME("BrowseResultMask") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {517LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_BrowseResultMask), /* .memSize */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ BrowseResultMask_members /* .members */ }, /* ReferenceDescription */ { UA_TYPENAME("ReferenceDescription") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {518LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {520LU}}, /* .binaryEncodingId */ sizeof(UA_ReferenceDescription), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 7, /* .membersSize */ ReferenceDescription_members /* .members */ }, /* BrowseResult */ { UA_TYPENAME("BrowseResult") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {522LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {524LU}}, /* .binaryEncodingId */ sizeof(UA_BrowseResult), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ BrowseResult_members /* .members */ }, /* BrowseRequest */ { UA_TYPENAME("BrowseRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {525LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {527LU}}, /* .binaryEncodingId */ sizeof(UA_BrowseRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ BrowseRequest_members /* .members */ }, /* BrowseResponse */ { UA_TYPENAME("BrowseResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {528LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {530LU}}, /* .binaryEncodingId */ sizeof(UA_BrowseResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ BrowseResponse_members /* .members */ }, /* BrowseNextRequest */ { UA_TYPENAME("BrowseNextRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {531LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {533LU}}, /* .binaryEncodingId */ sizeof(UA_BrowseNextRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ BrowseNextRequest_members /* .members */ }, /* BrowseNextResponse */ { UA_TYPENAME("BrowseNextResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {534LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {536LU}}, /* .binaryEncodingId */ sizeof(UA_BrowseNextResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ BrowseNextResponse_members /* .members */ }, /* RelativePathElement */ { UA_TYPENAME("RelativePathElement") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {537LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {539LU}}, /* .binaryEncodingId */ sizeof(UA_RelativePathElement), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ RelativePathElement_members /* .members */ }, /* RelativePath */ { UA_TYPENAME("RelativePath") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {540LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {542LU}}, /* .binaryEncodingId */ sizeof(UA_RelativePath), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ RelativePath_members /* .members */ }, /* BrowsePath */ { UA_TYPENAME("BrowsePath") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {543LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {545LU}}, /* .binaryEncodingId */ sizeof(UA_BrowsePath), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ BrowsePath_members /* .members */ }, /* BrowsePathTarget */ { UA_TYPENAME("BrowsePathTarget") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {546LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {548LU}}, /* .binaryEncodingId */ sizeof(UA_BrowsePathTarget), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ BrowsePathTarget_members /* .members */ }, /* BrowsePathResult */ { UA_TYPENAME("BrowsePathResult") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {549LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {551LU}}, /* .binaryEncodingId */ sizeof(UA_BrowsePathResult), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ BrowsePathResult_members /* .members */ }, /* TranslateBrowsePathsToNodeIdsRequest */ { UA_TYPENAME("TranslateBrowsePathsToNodeIdsRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {552LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {554LU}}, /* .binaryEncodingId */ sizeof(UA_TranslateBrowsePathsToNodeIdsRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ TranslateBrowsePathsToNodeIdsRequest_members /* .members */ }, /* TranslateBrowsePathsToNodeIdsResponse */ { UA_TYPENAME("TranslateBrowsePathsToNodeIdsResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {555LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {557LU}}, /* .binaryEncodingId */ sizeof(UA_TranslateBrowsePathsToNodeIdsResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ TranslateBrowsePathsToNodeIdsResponse_members /* .members */ }, /* RegisterNodesRequest */ { UA_TYPENAME("RegisterNodesRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {558LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {560LU}}, /* .binaryEncodingId */ sizeof(UA_RegisterNodesRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ RegisterNodesRequest_members /* .members */ }, /* RegisterNodesResponse */ { UA_TYPENAME("RegisterNodesResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {561LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {563LU}}, /* .binaryEncodingId */ sizeof(UA_RegisterNodesResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ RegisterNodesResponse_members /* .members */ }, /* UnregisterNodesRequest */ { UA_TYPENAME("UnregisterNodesRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {564LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {566LU}}, /* .binaryEncodingId */ sizeof(UA_UnregisterNodesRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ UnregisterNodesRequest_members /* .members */ }, /* UnregisterNodesResponse */ { UA_TYPENAME("UnregisterNodesResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {567LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {569LU}}, /* .binaryEncodingId */ sizeof(UA_UnregisterNodesResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ UnregisterNodesResponse_members /* .members */ }, /* FilterOperator */ { UA_TYPENAME("FilterOperator") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {576LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_FilterOperator), /* .memSize */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ FilterOperator_members /* .members */ }, /* ContentFilterElement */ { UA_TYPENAME("ContentFilterElement") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {583LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {585LU}}, /* .binaryEncodingId */ sizeof(UA_ContentFilterElement), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ ContentFilterElement_members /* .members */ }, /* ContentFilter */ { UA_TYPENAME("ContentFilter") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {586LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {588LU}}, /* .binaryEncodingId */ sizeof(UA_ContentFilter), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ ContentFilter_members /* .members */ }, /* ElementOperand */ { UA_TYPENAME("ElementOperand") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {592LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {594LU}}, /* .binaryEncodingId */ sizeof(UA_ElementOperand), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ ElementOperand_members /* .members */ }, /* LiteralOperand */ { UA_TYPENAME("LiteralOperand") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {595LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {597LU}}, /* .binaryEncodingId */ sizeof(UA_LiteralOperand), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ LiteralOperand_members /* .members */ }, /* AttributeOperand */ { UA_TYPENAME("AttributeOperand") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {598LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {600LU}}, /* .binaryEncodingId */ sizeof(UA_AttributeOperand), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ AttributeOperand_members /* .members */ }, /* SimpleAttributeOperand */ { UA_TYPENAME("SimpleAttributeOperand") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {601LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {603LU}}, /* .binaryEncodingId */ sizeof(UA_SimpleAttributeOperand), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ SimpleAttributeOperand_members /* .members */ }, /* ContentFilterElementResult */ { UA_TYPENAME("ContentFilterElementResult") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {604LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {606LU}}, /* .binaryEncodingId */ sizeof(UA_ContentFilterElementResult), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ ContentFilterElementResult_members /* .members */ }, /* ContentFilterResult */ { UA_TYPENAME("ContentFilterResult") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {607LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {609LU}}, /* .binaryEncodingId */ sizeof(UA_ContentFilterResult), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ ContentFilterResult_members /* .members */ }, /* TimestampsToReturn */ { UA_TYPENAME("TimestampsToReturn") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {625LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_TimestampsToReturn), /* .memSize */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ TimestampsToReturn_members /* .members */ }, /* ReadValueId */ { UA_TYPENAME("ReadValueId") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {626LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {628LU}}, /* .binaryEncodingId */ sizeof(UA_ReadValueId), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ ReadValueId_members /* .members */ }, /* ReadRequest */ { UA_TYPENAME("ReadRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {629LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {631LU}}, /* .binaryEncodingId */ sizeof(UA_ReadRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ ReadRequest_members /* .members */ }, /* ReadResponse */ { UA_TYPENAME("ReadResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {632LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {634LU}}, /* .binaryEncodingId */ sizeof(UA_ReadResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ ReadResponse_members /* .members */ }, /* HistoryReadValueId */ { UA_TYPENAME("HistoryReadValueId") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {635LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {637LU}}, /* .binaryEncodingId */ sizeof(UA_HistoryReadValueId), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ HistoryReadValueId_members /* .members */ }, /* HistoryReadResult */ { UA_TYPENAME("HistoryReadResult") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {638LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {640LU}}, /* .binaryEncodingId */ sizeof(UA_HistoryReadResult), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ HistoryReadResult_members /* .members */ }, /* ReadRawModifiedDetails */ { UA_TYPENAME("ReadRawModifiedDetails") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {647LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {649LU}}, /* .binaryEncodingId */ sizeof(UA_ReadRawModifiedDetails), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ ReadRawModifiedDetails_members /* .members */ }, /* ReadAtTimeDetails */ { UA_TYPENAME("ReadAtTimeDetails") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {653LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {655LU}}, /* .binaryEncodingId */ sizeof(UA_ReadAtTimeDetails), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ ReadAtTimeDetails_members /* .members */ }, /* HistoryData */ { UA_TYPENAME("HistoryData") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {656LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {658LU}}, /* .binaryEncodingId */ sizeof(UA_HistoryData), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ HistoryData_members /* .members */ }, /* HistoryReadRequest */ { UA_TYPENAME("HistoryReadRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {662LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {664LU}}, /* .binaryEncodingId */ sizeof(UA_HistoryReadRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ HistoryReadRequest_members /* .members */ }, /* HistoryReadResponse */ { UA_TYPENAME("HistoryReadResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {665LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {667LU}}, /* .binaryEncodingId */ sizeof(UA_HistoryReadResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ HistoryReadResponse_members /* .members */ }, /* WriteValue */ { UA_TYPENAME("WriteValue") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {668LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {670LU}}, /* .binaryEncodingId */ sizeof(UA_WriteValue), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ WriteValue_members /* .members */ }, /* WriteRequest */ { UA_TYPENAME("WriteRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {671LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {673LU}}, /* .binaryEncodingId */ sizeof(UA_WriteRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ WriteRequest_members /* .members */ }, /* WriteResponse */ { UA_TYPENAME("WriteResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {674LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {676LU}}, /* .binaryEncodingId */ sizeof(UA_WriteResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ WriteResponse_members /* .members */ }, /* HistoryUpdateType */ { UA_TYPENAME("HistoryUpdateType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {11234LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_HistoryUpdateType), /* .memSize */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ HistoryUpdateType_members /* .members */ }, /* PerformUpdateType */ { UA_TYPENAME("PerformUpdateType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {11293LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_PerformUpdateType), /* .memSize */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ PerformUpdateType_members /* .members */ }, /* UpdateDataDetails */ { UA_TYPENAME("UpdateDataDetails") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {680LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {682LU}}, /* .binaryEncodingId */ sizeof(UA_UpdateDataDetails), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ UpdateDataDetails_members /* .members */ }, /* DeleteRawModifiedDetails */ { UA_TYPENAME("DeleteRawModifiedDetails") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {686LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {688LU}}, /* .binaryEncodingId */ sizeof(UA_DeleteRawModifiedDetails), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ DeleteRawModifiedDetails_members /* .members */ }, /* HistoryUpdateResult */ { UA_TYPENAME("HistoryUpdateResult") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {695LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {697LU}}, /* .binaryEncodingId */ sizeof(UA_HistoryUpdateResult), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ HistoryUpdateResult_members /* .members */ }, /* HistoryUpdateRequest */ { UA_TYPENAME("HistoryUpdateRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {698LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {700LU}}, /* .binaryEncodingId */ sizeof(UA_HistoryUpdateRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ HistoryUpdateRequest_members /* .members */ }, /* HistoryUpdateResponse */ { UA_TYPENAME("HistoryUpdateResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {701LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {703LU}}, /* .binaryEncodingId */ sizeof(UA_HistoryUpdateResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ HistoryUpdateResponse_members /* .members */ }, /* CallMethodRequest */ { UA_TYPENAME("CallMethodRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {704LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {706LU}}, /* .binaryEncodingId */ sizeof(UA_CallMethodRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ CallMethodRequest_members /* .members */ }, /* CallMethodResult */ { UA_TYPENAME("CallMethodResult") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {707LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {709LU}}, /* .binaryEncodingId */ sizeof(UA_CallMethodResult), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ CallMethodResult_members /* .members */ }, /* CallRequest */ { UA_TYPENAME("CallRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {710LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {712LU}}, /* .binaryEncodingId */ sizeof(UA_CallRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ CallRequest_members /* .members */ }, /* CallResponse */ { UA_TYPENAME("CallResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {713LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {715LU}}, /* .binaryEncodingId */ sizeof(UA_CallResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ CallResponse_members /* .members */ }, /* MonitoringMode */ { UA_TYPENAME("MonitoringMode") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {716LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_MonitoringMode), /* .memSize */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ MonitoringMode_members /* .members */ }, /* DataChangeTrigger */ { UA_TYPENAME("DataChangeTrigger") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {717LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_DataChangeTrigger), /* .memSize */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ DataChangeTrigger_members /* .members */ }, /* DeadbandType */ { UA_TYPENAME("DeadbandType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {718LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_DeadbandType), /* .memSize */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ DeadbandType_members /* .members */ }, /* DataChangeFilter */ { UA_TYPENAME("DataChangeFilter") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {722LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {724LU}}, /* .binaryEncodingId */ sizeof(UA_DataChangeFilter), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ DataChangeFilter_members /* .members */ }, /* EventFilter */ { UA_TYPENAME("EventFilter") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {725LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {727LU}}, /* .binaryEncodingId */ sizeof(UA_EventFilter), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ EventFilter_members /* .members */ }, /* AggregateConfiguration */ { UA_TYPENAME("AggregateConfiguration") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {948LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {950LU}}, /* .binaryEncodingId */ sizeof(UA_AggregateConfiguration), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ AggregateConfiguration_members /* .members */ }, /* AggregateFilter */ { UA_TYPENAME("AggregateFilter") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {728LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {730LU}}, /* .binaryEncodingId */ sizeof(UA_AggregateFilter), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ AggregateFilter_members /* .members */ }, /* EventFilterResult */ { UA_TYPENAME("EventFilterResult") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {734LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {736LU}}, /* .binaryEncodingId */ sizeof(UA_EventFilterResult), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ EventFilterResult_members /* .members */ }, /* MonitoringParameters */ { UA_TYPENAME("MonitoringParameters") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {740LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {742LU}}, /* .binaryEncodingId */ sizeof(UA_MonitoringParameters), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ MonitoringParameters_members /* .members */ }, /* MonitoredItemCreateRequest */ { UA_TYPENAME("MonitoredItemCreateRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {743LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {745LU}}, /* .binaryEncodingId */ sizeof(UA_MonitoredItemCreateRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ MonitoredItemCreateRequest_members /* .members */ }, /* MonitoredItemCreateResult */ { UA_TYPENAME("MonitoredItemCreateResult") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {746LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {748LU}}, /* .binaryEncodingId */ sizeof(UA_MonitoredItemCreateResult), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ MonitoredItemCreateResult_members /* .members */ }, /* CreateMonitoredItemsRequest */ { UA_TYPENAME("CreateMonitoredItemsRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {749LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {751LU}}, /* .binaryEncodingId */ sizeof(UA_CreateMonitoredItemsRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ CreateMonitoredItemsRequest_members /* .members */ }, /* CreateMonitoredItemsResponse */ { UA_TYPENAME("CreateMonitoredItemsResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {752LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {754LU}}, /* .binaryEncodingId */ sizeof(UA_CreateMonitoredItemsResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ CreateMonitoredItemsResponse_members /* .members */ }, /* MonitoredItemModifyRequest */ { UA_TYPENAME("MonitoredItemModifyRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {755LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {757LU}}, /* .binaryEncodingId */ sizeof(UA_MonitoredItemModifyRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ MonitoredItemModifyRequest_members /* .members */ }, /* MonitoredItemModifyResult */ { UA_TYPENAME("MonitoredItemModifyResult") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {758LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {760LU}}, /* .binaryEncodingId */ sizeof(UA_MonitoredItemModifyResult), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ MonitoredItemModifyResult_members /* .members */ }, /* ModifyMonitoredItemsRequest */ { UA_TYPENAME("ModifyMonitoredItemsRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {761LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {763LU}}, /* .binaryEncodingId */ sizeof(UA_ModifyMonitoredItemsRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ ModifyMonitoredItemsRequest_members /* .members */ }, /* ModifyMonitoredItemsResponse */ { UA_TYPENAME("ModifyMonitoredItemsResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {764LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {766LU}}, /* .binaryEncodingId */ sizeof(UA_ModifyMonitoredItemsResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ ModifyMonitoredItemsResponse_members /* .members */ }, /* SetMonitoringModeRequest */ { UA_TYPENAME("SetMonitoringModeRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {767LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {769LU}}, /* .binaryEncodingId */ sizeof(UA_SetMonitoringModeRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ SetMonitoringModeRequest_members /* .members */ }, /* SetMonitoringModeResponse */ { UA_TYPENAME("SetMonitoringModeResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {770LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {772LU}}, /* .binaryEncodingId */ sizeof(UA_SetMonitoringModeResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ SetMonitoringModeResponse_members /* .members */ }, /* SetTriggeringRequest */ { UA_TYPENAME("SetTriggeringRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {773LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {775LU}}, /* .binaryEncodingId */ sizeof(UA_SetTriggeringRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ SetTriggeringRequest_members /* .members */ }, /* SetTriggeringResponse */ { UA_TYPENAME("SetTriggeringResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {776LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {778LU}}, /* .binaryEncodingId */ sizeof(UA_SetTriggeringResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ SetTriggeringResponse_members /* .members */ }, /* DeleteMonitoredItemsRequest */ { UA_TYPENAME("DeleteMonitoredItemsRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {779LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {781LU}}, /* .binaryEncodingId */ sizeof(UA_DeleteMonitoredItemsRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ DeleteMonitoredItemsRequest_members /* .members */ }, /* DeleteMonitoredItemsResponse */ { UA_TYPENAME("DeleteMonitoredItemsResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {782LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {784LU}}, /* .binaryEncodingId */ sizeof(UA_DeleteMonitoredItemsResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ DeleteMonitoredItemsResponse_members /* .members */ }, /* CreateSubscriptionRequest */ { UA_TYPENAME("CreateSubscriptionRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {785LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {787LU}}, /* .binaryEncodingId */ sizeof(UA_CreateSubscriptionRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 7, /* .membersSize */ CreateSubscriptionRequest_members /* .members */ }, /* CreateSubscriptionResponse */ { UA_TYPENAME("CreateSubscriptionResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {788LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {790LU}}, /* .binaryEncodingId */ sizeof(UA_CreateSubscriptionResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ CreateSubscriptionResponse_members /* .members */ }, /* ModifySubscriptionRequest */ { UA_TYPENAME("ModifySubscriptionRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {791LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {793LU}}, /* .binaryEncodingId */ sizeof(UA_ModifySubscriptionRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 7, /* .membersSize */ ModifySubscriptionRequest_members /* .members */ }, /* ModifySubscriptionResponse */ { UA_TYPENAME("ModifySubscriptionResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {794LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {796LU}}, /* .binaryEncodingId */ sizeof(UA_ModifySubscriptionResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ ModifySubscriptionResponse_members /* .members */ }, /* SetPublishingModeRequest */ { UA_TYPENAME("SetPublishingModeRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {797LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {799LU}}, /* .binaryEncodingId */ sizeof(UA_SetPublishingModeRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ SetPublishingModeRequest_members /* .members */ }, /* SetPublishingModeResponse */ { UA_TYPENAME("SetPublishingModeResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {800LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {802LU}}, /* .binaryEncodingId */ sizeof(UA_SetPublishingModeResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ SetPublishingModeResponse_members /* .members */ }, /* NotificationMessage */ { UA_TYPENAME("NotificationMessage") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {803LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {805LU}}, /* .binaryEncodingId */ sizeof(UA_NotificationMessage), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ NotificationMessage_members /* .members */ }, /* MonitoredItemNotification */ { UA_TYPENAME("MonitoredItemNotification") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {806LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {808LU}}, /* .binaryEncodingId */ sizeof(UA_MonitoredItemNotification), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ MonitoredItemNotification_members /* .members */ }, /* EventFieldList */ { UA_TYPENAME("EventFieldList") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {917LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {919LU}}, /* .binaryEncodingId */ sizeof(UA_EventFieldList), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ EventFieldList_members /* .members */ }, /* HistoryEventFieldList */ { UA_TYPENAME("HistoryEventFieldList") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {920LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {922LU}}, /* .binaryEncodingId */ sizeof(UA_HistoryEventFieldList), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ HistoryEventFieldList_members /* .members */ }, /* StatusChangeNotification */ { UA_TYPENAME("StatusChangeNotification") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {818LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {820LU}}, /* .binaryEncodingId */ sizeof(UA_StatusChangeNotification), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ StatusChangeNotification_members /* .members */ }, /* SubscriptionAcknowledgement */ { UA_TYPENAME("SubscriptionAcknowledgement") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {821LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {823LU}}, /* .binaryEncodingId */ sizeof(UA_SubscriptionAcknowledgement), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ SubscriptionAcknowledgement_members /* .members */ }, /* PublishRequest */ { UA_TYPENAME("PublishRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {824LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {826LU}}, /* .binaryEncodingId */ sizeof(UA_PublishRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ PublishRequest_members /* .members */ }, /* PublishResponse */ { UA_TYPENAME("PublishResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {827LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {829LU}}, /* .binaryEncodingId */ sizeof(UA_PublishResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 7, /* .membersSize */ PublishResponse_members /* .members */ }, /* RepublishRequest */ { UA_TYPENAME("RepublishRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {830LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {832LU}}, /* .binaryEncodingId */ sizeof(UA_RepublishRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ RepublishRequest_members /* .members */ }, /* RepublishResponse */ { UA_TYPENAME("RepublishResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {833LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {835LU}}, /* .binaryEncodingId */ sizeof(UA_RepublishResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ RepublishResponse_members /* .members */ }, /* TransferResult */ { UA_TYPENAME("TransferResult") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {836LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {838LU}}, /* .binaryEncodingId */ sizeof(UA_TransferResult), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ TransferResult_members /* .members */ }, /* TransferSubscriptionsRequest */ { UA_TYPENAME("TransferSubscriptionsRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {839LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {841LU}}, /* .binaryEncodingId */ sizeof(UA_TransferSubscriptionsRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ TransferSubscriptionsRequest_members /* .members */ }, /* TransferSubscriptionsResponse */ { UA_TYPENAME("TransferSubscriptionsResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {842LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {844LU}}, /* .binaryEncodingId */ sizeof(UA_TransferSubscriptionsResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ TransferSubscriptionsResponse_members /* .members */ }, /* DeleteSubscriptionsRequest */ { UA_TYPENAME("DeleteSubscriptionsRequest") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {845LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {847LU}}, /* .binaryEncodingId */ sizeof(UA_DeleteSubscriptionsRequest), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ DeleteSubscriptionsRequest_members /* .members */ }, /* DeleteSubscriptionsResponse */ { UA_TYPENAME("DeleteSubscriptionsResponse") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {848LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {850LU}}, /* .binaryEncodingId */ sizeof(UA_DeleteSubscriptionsResponse), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ DeleteSubscriptionsResponse_members /* .members */ }, /* BuildInfo */ { UA_TYPENAME("BuildInfo") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {338LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {340LU}}, /* .binaryEncodingId */ sizeof(UA_BuildInfo), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 6, /* .membersSize */ BuildInfo_members /* .members */ }, /* RedundancySupport */ { UA_TYPENAME("RedundancySupport") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {851LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_RedundancySupport), /* .memSize */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ RedundancySupport_members /* .members */ }, /* ServerState */ { UA_TYPENAME("ServerState") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {852LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_ServerState), /* .memSize */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ ServerState_members /* .members */ }, /* ServerDiagnosticsSummaryDataType */ { UA_TYPENAME("ServerDiagnosticsSummaryDataType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {859LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {861LU}}, /* .binaryEncodingId */ sizeof(UA_ServerDiagnosticsSummaryDataType), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ false, /* .overlayable */ 12, /* .membersSize */ ServerDiagnosticsSummaryDataType_members /* .members */ }, /* ServerStatusDataType */ { UA_TYPENAME("ServerStatusDataType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {862LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {864LU}}, /* .binaryEncodingId */ sizeof(UA_ServerStatusDataType), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 6, /* .membersSize */ ServerStatusDataType_members /* .members */ }, /* Range */ { UA_TYPENAME("Range") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {884LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {886LU}}, /* .binaryEncodingId */ sizeof(UA_Range), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ Range_members /* .members */ }, /* EUInformation */ { UA_TYPENAME("EUInformation") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {887LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {889LU}}, /* .binaryEncodingId */ sizeof(UA_EUInformation), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ EUInformation_members /* .members */ }, /* AxisScaleEnumeration */ { UA_TYPENAME("AxisScaleEnumeration") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {12077LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_AxisScaleEnumeration), /* .memSize */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ AxisScaleEnumeration_members /* .members */ }, /* ComplexNumberType */ { UA_TYPENAME("ComplexNumberType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {12171LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {12181LU}}, /* .binaryEncodingId */ sizeof(UA_ComplexNumberType), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ ComplexNumberType_members /* .members */ }, /* DoubleComplexNumberType */ { UA_TYPENAME("DoubleComplexNumberType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {12172LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {12182LU}}, /* .binaryEncodingId */ sizeof(UA_DoubleComplexNumberType), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ DoubleComplexNumberType_members /* .members */ }, /* AxisInformation */ { UA_TYPENAME("AxisInformation") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {12079LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {12089LU}}, /* .binaryEncodingId */ sizeof(UA_AxisInformation), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ AxisInformation_members /* .members */ }, /* XVType */ { UA_TYPENAME("XVType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {12080LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {12090LU}}, /* .binaryEncodingId */ sizeof(UA_XVType), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ XVType_members /* .members */ }, /* EnumDefinition */ { UA_TYPENAME("EnumDefinition") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {100LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {123LU}}, /* .binaryEncodingId */ sizeof(UA_EnumDefinition), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ EnumDefinition_members /* .members */ }, /* ReadEventDetails */ { UA_TYPENAME("ReadEventDetails") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {644LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {646LU}}, /* .binaryEncodingId */ sizeof(UA_ReadEventDetails), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 4, /* .membersSize */ ReadEventDetails_members /* .members */ }, /* ReadProcessedDetails */ { UA_TYPENAME("ReadProcessedDetails") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {650LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {652LU}}, /* .binaryEncodingId */ sizeof(UA_ReadProcessedDetails), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ ReadProcessedDetails_members /* .members */ }, /* ModificationInfo */ { UA_TYPENAME("ModificationInfo") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {11216LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {11226LU}}, /* .binaryEncodingId */ sizeof(UA_ModificationInfo), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ ModificationInfo_members /* .members */ }, /* HistoryModifiedData */ { UA_TYPENAME("HistoryModifiedData") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {11217LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {11227LU}}, /* .binaryEncodingId */ sizeof(UA_HistoryModifiedData), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ HistoryModifiedData_members /* .members */ }, /* HistoryEvent */ { UA_TYPENAME("HistoryEvent") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {659LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {661LU}}, /* .binaryEncodingId */ sizeof(UA_HistoryEvent), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ HistoryEvent_members /* .members */ }, /* DataChangeNotification */ { UA_TYPENAME("DataChangeNotification") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {809LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {811LU}}, /* .binaryEncodingId */ sizeof(UA_DataChangeNotification), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ DataChangeNotification_members /* .members */ }, /* EventNotificationList */ { UA_TYPENAME("EventNotificationList") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {914LU}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {916LU}}, /* .binaryEncodingId */ sizeof(UA_EventNotificationList), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 1, /* .membersSize */ EventNotificationList_members /* .members */ }, }; /**** amalgamated original file "/build/src_generated/open62541/transport_generated.c" ****/ /********************************** * Autogenerated -- do not modify * **********************************/ /* MessageType */ #define MessageType_members NULL /* ChunkType */ #define ChunkType_members NULL /* TcpMessageHeader */ static UA_DataTypeMember TcpMessageHeader_members[2] = { { UA_TYPENAME("MessageTypeAndChunkType") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("MessageSize") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_TcpMessageHeader, messageSize) - offsetof(UA_TcpMessageHeader, messageTypeAndChunkType) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* TcpHelloMessage */ static UA_DataTypeMember TcpHelloMessage_members[6] = { { UA_TYPENAME("ProtocolVersion") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ReceiveBufferSize") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_TcpHelloMessage, receiveBufferSize) - offsetof(UA_TcpHelloMessage, protocolVersion) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SendBufferSize") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_TcpHelloMessage, sendBufferSize) - offsetof(UA_TcpHelloMessage, receiveBufferSize) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("MaxMessageSize") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_TcpHelloMessage, maxMessageSize) - offsetof(UA_TcpHelloMessage, sendBufferSize) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("MaxChunkCount") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_TcpHelloMessage, maxChunkCount) - offsetof(UA_TcpHelloMessage, maxMessageSize) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("EndpointUrl") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_TcpHelloMessage, endpointUrl) - offsetof(UA_TcpHelloMessage, maxChunkCount) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* TcpAcknowledgeMessage */ static UA_DataTypeMember TcpAcknowledgeMessage_members[5] = { { UA_TYPENAME("ProtocolVersion") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ReceiveBufferSize") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_TcpAcknowledgeMessage, receiveBufferSize) - offsetof(UA_TcpAcknowledgeMessage, protocolVersion) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SendBufferSize") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_TcpAcknowledgeMessage, sendBufferSize) - offsetof(UA_TcpAcknowledgeMessage, receiveBufferSize) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("MaxMessageSize") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_TcpAcknowledgeMessage, maxMessageSize) - offsetof(UA_TcpAcknowledgeMessage, sendBufferSize) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("MaxChunkCount") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_TcpAcknowledgeMessage, maxChunkCount) - offsetof(UA_TcpAcknowledgeMessage, maxMessageSize) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* TcpErrorMessage */ static UA_DataTypeMember TcpErrorMessage_members[2] = { { UA_TYPENAME("Error") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("Reason") /* .memberName */ &UA_TYPES[UA_TYPES_STRING], /* .memberType */ offsetof(UA_TcpErrorMessage, reason) - offsetof(UA_TcpErrorMessage, error) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* AsymmetricAlgorithmSecurityHeader */ static UA_DataTypeMember AsymmetricAlgorithmSecurityHeader_members[3] = { { UA_TYPENAME("SecurityPolicyUri") /* .memberName */ &UA_TYPES[UA_TYPES_BYTESTRING], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("SenderCertificate") /* .memberName */ &UA_TYPES[UA_TYPES_BYTESTRING], /* .memberType */ offsetof(UA_AsymmetricAlgorithmSecurityHeader, senderCertificate) - offsetof(UA_AsymmetricAlgorithmSecurityHeader, securityPolicyUri) - sizeof(UA_ByteString), /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("ReceiverCertificateThumbprint") /* .memberName */ &UA_TYPES[UA_TYPES_BYTESTRING], /* .memberType */ offsetof(UA_AsymmetricAlgorithmSecurityHeader, receiverCertificateThumbprint) - offsetof(UA_AsymmetricAlgorithmSecurityHeader, senderCertificate) - sizeof(UA_ByteString), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; /* SequenceHeader */ static UA_DataTypeMember SequenceHeader_members[2] = { { UA_TYPENAME("SequenceNumber") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ 0, /* .padding */ false, /* .isArray */ false /* .isOptional */ }, { UA_TYPENAME("RequestId") /* .memberName */ &UA_TYPES[UA_TYPES_UINT32], /* .memberType */ offsetof(UA_SequenceHeader, requestId) - offsetof(UA_SequenceHeader, sequenceNumber) - sizeof(UA_UInt32), /* .padding */ false, /* .isArray */ false /* .isOptional */ },}; const UA_DataType UA_TRANSPORT[UA_TRANSPORT_COUNT] = { /* MessageType */ { UA_TYPENAME("MessageType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_MessageType), /* .memSize */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ MessageType_members /* .members */ }, /* ChunkType */ { UA_TYPENAME("ChunkType") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_ChunkType), /* .memSize */ UA_DATATYPEKIND_ENUM, /* .typeKind */ true, /* .pointerFree */ UA_BINARY_OVERLAYABLE_INTEGER, /* .overlayable */ 0, /* .membersSize */ ChunkType_members /* .members */ }, /* TcpMessageHeader */ { UA_TYPENAME("TcpMessageHeader") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_TcpMessageHeader), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ TcpMessageHeader_members /* .members */ }, /* TcpHelloMessage */ { UA_TYPENAME("TcpHelloMessage") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_TcpHelloMessage), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 6, /* .membersSize */ TcpHelloMessage_members /* .members */ }, /* TcpAcknowledgeMessage */ { UA_TYPENAME("TcpAcknowledgeMessage") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_TcpAcknowledgeMessage), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ false, /* .overlayable */ 5, /* .membersSize */ TcpAcknowledgeMessage_members /* .members */ }, /* TcpErrorMessage */ { UA_TYPENAME("TcpErrorMessage") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_TcpErrorMessage), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ TcpErrorMessage_members /* .members */ }, /* AsymmetricAlgorithmSecurityHeader */ { UA_TYPENAME("AsymmetricAlgorithmSecurityHeader") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_AsymmetricAlgorithmSecurityHeader), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ false, /* .pointerFree */ false, /* .overlayable */ 3, /* .membersSize */ AsymmetricAlgorithmSecurityHeader_members /* .members */ }, /* SequenceHeader */ { UA_TYPENAME("SequenceHeader") /* .typeName */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .typeId */ {0, UA_NODEIDTYPE_NUMERIC, {0}}, /* .binaryEncodingId */ sizeof(UA_SequenceHeader), /* .memSize */ UA_DATATYPEKIND_STRUCTURE, /* .typeKind */ true, /* .pointerFree */ false, /* .overlayable */ 2, /* .membersSize */ SequenceHeader_members /* .members */ }, }; /**** amalgamated original file "/build/src_generated/open62541/statuscodes.c" ****/ /********************************** * Autogenerated -- do not modify * **********************************/ 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 = 252; static const UA_StatusCodeName statusCodeDescriptions[252] = { {UA_STATUSCODE_GOOD, "Good"}, {UA_STATUSCODE_GOOD, "Good"}, {UA_STATUSCODE_UNCERTAIN, "Uncertain"}, {UA_STATUSCODE_BAD, "Bad"}, {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_GOODRETRANSMISSIONQUEUENOTSUPPORTED, "GoodRetransmissionQueueNotSupported"}, {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_BADTICKETREQUIRED, "BadTicketRequired"}, {UA_STATUSCODE_BADTICKETINVALID, "BadTicketInvalid"}, {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_GOODEDITED_DEPENDENTVALUECHANGED, "GoodEdited_DependentValueChanged"}, {UA_STATUSCODE_GOODEDITED_DOMINANTVALUECHANGED, "GoodEdited_DominantValueChanged"}, {UA_STATUSCODE_GOODEDITED_DOMINANTVALUECHANGED_DEPENDENTVALUECHANGED, "GoodEdited_DominantValueChanged_DependentValueChanged"}, {UA_STATUSCODE_BADEDITED_OUTOFRANGE, "BadEdited_OutOfRange"}, {UA_STATUSCODE_BADINITIALVALUE_OUTOFRANGE, "BadInitialValue_OutOfRange"}, {UA_STATUSCODE_BADOUTOFRANGE_DOMINANTVALUECHANGED, "BadOutOfRange_DominantValueChanged"}, {UA_STATUSCODE_BADEDITED_OUTOFRANGE_DOMINANTVALUECHANGED, "BadEdited_OutOfRange_DominantValueChanged"}, {UA_STATUSCODE_BADOUTOFRANGE_DOMINANTVALUECHANGED_DEPENDENTVALUECHANGED, "BadOutOfRange_DominantValueChanged_DependentValueChanged"}, {UA_STATUSCODE_BADEDITED_OUTOFRANGE_DOMINANTVALUECHANGED_DEPENDENTVALUECHANGED, "BadEdited_OutOfRange_DominantValueChanged_DependentValueChanged"}, {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 (UA_StatusCode_isEqualTop(statusCodeDescriptions[i].code,code)) return statusCodeDescriptions[i].name; } return statusCodeDescriptions[statusCodeDescriptionsSize-1].name; } #endif /**** amalgamated original file "/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(const 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) { UA_Boolean ipv6 = false; /* 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++; ipv6 = true; } 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 */ if(ipv6) { /* Skip the ipv6 '[]' container for getaddrinfo() later */ outHostname->data = &endpointUrl->data[11]; outHostname->length = curr - 12; } else { outHostname->data = &endpointUrl->data[10]; outHostname->length = curr - 10; } /* Empty string? */ if(outHostname->length == 0) outHostname->data = NULL; 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--; /* Empty string? */ if(outPath->length == 0) outPath->data = NULL; 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_toBase64(const UA_ByteString *byteString, UA_String *str) { UA_String_init(str); if(!byteString || !byteString->data) return UA_STATUSCODE_GOOD; str->data = (UA_Byte*) UA_base64(byteString->data, byteString->length, &str->length); if(!str->data) return UA_STATUSCODE_BADOUTOFMEMORY; return UA_STATUSCODE_GOOD; } UA_StatusCode UA_EXPORT UA_ByteString_fromBase64(UA_ByteString *bs, const UA_String *input) { UA_ByteString_init(bs); if(input->length == 0) return UA_STATUSCODE_GOOD; bs->data = UA_unbase64((const unsigned char*)input->data, input->length, &bs->length); /* TODO: Differentiate between encoding and memory errors */ if(!bs->data) return UA_STATUSCODE_BADINTERNALERROR; return UA_STATUSCODE_GOOD; } /* Key Value Map */ UA_StatusCode UA_KeyValueMap_setQualified(UA_KeyValuePair **map, size_t *mapSize, const UA_QualifiedName *key, const UA_Variant *value) { /* Parameter exists already */ const UA_Variant *v = UA_KeyValueMap_getQualified(*map, *mapSize, key); if(v) { UA_Variant copyV; UA_StatusCode res = UA_Variant_copy(v, ©V); if(res != UA_STATUSCODE_GOOD) return res; UA_Variant *target = (UA_Variant*)(uintptr_t)v; UA_Variant_clear(target); *target = copyV; return UA_STATUSCODE_GOOD; } /* Append to the array */ UA_KeyValuePair pair; pair.key = *key; pair.value = *value; return UA_Array_appendCopy((void**)map, mapSize, &pair, &UA_TYPES[UA_TYPES_KEYVALUEPAIR]); } UA_StatusCode UA_KeyValueMap_set(UA_KeyValuePair **map, size_t *mapSize, const char *key, const UA_Variant *value) { UA_QualifiedName qnKey; qnKey.namespaceIndex = 0; qnKey.name = UA_STRING((char*)(uintptr_t)key); return UA_KeyValueMap_setQualified(map, mapSize, &qnKey, value); } const UA_Variant * UA_KeyValueMap_getQualified(UA_KeyValuePair *map, size_t mapSize, const UA_QualifiedName *key) { for(size_t i = 0; i < mapSize; i++) { if(map[i].key.namespaceIndex == key->namespaceIndex && UA_String_equal(&map[i].key.name, &key->name)) return &map[i].value; } return NULL; } const UA_Variant * UA_KeyValueMap_get(UA_KeyValuePair *map, size_t mapSize, const char *key) { UA_QualifiedName qnKey; qnKey.namespaceIndex = 0; qnKey.name = UA_STRING((char*)(uintptr_t)key); return UA_KeyValueMap_getQualified(map, mapSize, &qnKey); } /* Returns NULL if the parameter is not defined or not of the right datatype */ const UA_Variant * UA_KeyValueMap_getScalar(UA_KeyValuePair *map, size_t mapSize, const char *key, const UA_DataType *type) { const UA_Variant *v = UA_KeyValueMap_get(map, mapSize, key); if(!v || !UA_Variant_hasScalarType(v, type)) return NULL; return v; } const UA_Variant * UA_KeyValueMap_getArray(UA_KeyValuePair *map, size_t mapSize, const char *key, const UA_DataType *type) { const UA_Variant *v = UA_KeyValueMap_get(map, mapSize, key); if(!v || !UA_Variant_hasArrayType(v, type)) return NULL; return v; } void UA_KeyValueMap_deleteQualified(UA_KeyValuePair **map, size_t *mapSize, const UA_QualifiedName *key) { UA_KeyValuePair *m = *map; size_t s = *mapSize; for(size_t i = 0; i < s; i++) { if(m[i].key.namespaceIndex != key->namespaceIndex || !UA_String_equal(&m[i].key.name, &key->name)) continue; /* Clean the pair */ UA_KeyValuePair_clear(&m[i]); /* Move the last pair to fill the empty slot */ if(s > 1 && i < s - 1) { m[i] = m[s-1]; UA_KeyValuePair_init(&m[s-1]); } UA_StatusCode res = UA_Array_resize((void**)map, mapSize, *mapSize-1, &UA_TYPES[UA_TYPES_KEYVALUEPAIR]); (void)res; *mapSize = s - 1; /* In case resize fails, keep the longer original * array around. Resize never fails when reducing * the size to zero. Reduce the size integer in * any case. */ return; } } void UA_KeyValueMap_delete(UA_KeyValuePair **map, size_t *mapSize, const char *key) { UA_QualifiedName qnKey; qnKey.namespaceIndex = 0; qnKey.name = UA_STRING((char*)(uintptr_t)key); UA_KeyValueMap_deleteQualified(map, mapSize, &qnKey); } /**** amalgamated original file "/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, 2021 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2017 (c) Stefan Profanter, fortiss GmbH */ /* 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 aa_cmp cmpDateTime(const UA_DateTime *a, const UA_DateTime *b) { if(*a < *b) return AA_CMP_LESS; if(*a > *b) return AA_CMP_MORE; if(a == b) return AA_CMP_EQ; if(a < b) return AA_CMP_LESS; return AA_CMP_MORE; } /* The identifiers of entries are unique */ static enum aa_cmp cmpId(const UA_UInt64 *a, const UA_UInt64 *b) { if(*a < *b) return AA_CMP_LESS; if(*a == *b) return AA_CMP_EQ; return AA_CMP_MORE; } static UA_DateTime calculateNextTime(UA_DateTime currentTime, UA_DateTime baseTime, UA_DateTime interval) { /* Take the difference between current and base time */ UA_DateTime diffCurrentTimeBaseTime = currentTime - baseTime; /* Take modulo of the diff time with the interval. This is the duration we * are already "into" the current interval. Subtract it from (current + * interval) to get the next execution time. */ UA_DateTime cycleDelay = diffCurrentTimeBaseTime % interval; /* Handle the special case where the baseTime is in the future */ if(UA_UNLIKELY(cycleDelay < 0)) cycleDelay += interval; return currentTime + interval - cycleDelay; } void UA_Timer_init(UA_Timer *t) { memset(t, 0, sizeof(UA_Timer)); aa_init(&t->root, (enum aa_cmp (*)(const void*, const void*))cmpDateTime, offsetof(UA_TimerEntry, treeEntry), offsetof(UA_TimerEntry, nextTime)); aa_init(&t->idRoot, (enum aa_cmp (*)(const void*, const void*))cmpId, offsetof(UA_TimerEntry, idTreeEntry), offsetof(UA_TimerEntry, id)); UA_LOCK_INIT(&t->timerMutex); } void UA_Timer_addTimerEntry(UA_Timer *t, UA_TimerEntry *te, UA_UInt64 *callbackId) { UA_LOCK(&t->timerMutex); te->id = ++t->idCounter; if(callbackId) *callbackId = te->id; aa_insert(&t->root, te); aa_insert(&t->idRoot, te); UA_UNLOCK(&t->timerMutex); } static UA_StatusCode addCallback(UA_Timer *t, UA_ApplicationCallback callback, void *application, void *data, UA_DateTime nextTime, UA_UInt64 interval, UA_TimerPolicy timerPolicy, 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->nextTime = nextTime; te->timerPolicy = timerPolicy; /* Set the output identifier */ if(callbackId) *callbackId = te->id; aa_insert(&t->root, te); aa_insert(&t->idRoot, te); 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) { UA_LOCK(&t->timerMutex); UA_StatusCode res = addCallback(t, callback, application, data, date, 0, UA_TIMER_HANDLE_CYCLEMISS_WITH_CURRENTTIME, callbackId); UA_UNLOCK(&t->timerMutex); return res; } /* 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_DateTime *baseTime, UA_TimerPolicy timerPolicy, 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); if(interval == 0) return UA_STATUSCODE_BADINTERNALERROR; /* Compute the first time for execution */ UA_DateTime currentTime = UA_DateTime_nowMonotonic(); UA_DateTime nextTime; if(baseTime == NULL) { /* Use "now" as the basetime */ nextTime = currentTime + (UA_DateTime)interval; } else { nextTime = calculateNextTime(currentTime, *baseTime, (UA_DateTime)interval); } UA_LOCK(&t->timerMutex); UA_StatusCode res = addCallback(t, callback, application, data, nextTime, interval, timerPolicy, callbackId); UA_UNLOCK(&t->timerMutex); return res; } UA_StatusCode UA_Timer_changeRepeatedCallback(UA_Timer *t, UA_UInt64 callbackId, UA_Double interval_ms, UA_DateTime *baseTime, UA_TimerPolicy timerPolicy) { /* 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); if(interval == 0) return UA_STATUSCODE_BADINTERNALERROR; UA_LOCK(&t->timerMutex); /* Remove from the sorted tree */ UA_TimerEntry *te = (UA_TimerEntry*)aa_find(&t->idRoot, &callbackId); if(!te) { UA_UNLOCK(&t->timerMutex); return UA_STATUSCODE_BADNOTFOUND; } aa_remove(&t->root, te); /* Compute the next time for execution. The logic is identical to the * creation of a new repeated callback. */ UA_DateTime currentTime = UA_DateTime_nowMonotonic(); if(baseTime == NULL) { /* Use "now" as the basetime */ te->nextTime = currentTime + (UA_DateTime)interval; } else { te->nextTime = calculateNextTime(currentTime, *baseTime, (UA_DateTime)interval); } /* Update the remaining parameters and re-insert */ te->interval = interval; te->timerPolicy = timerPolicy; aa_insert(&t->root, te); UA_UNLOCK(&t->timerMutex); return UA_STATUSCODE_GOOD; } void UA_Timer_removeCallback(UA_Timer *t, UA_UInt64 callbackId) { UA_LOCK(&t->timerMutex); UA_TimerEntry *te = (UA_TimerEntry*)aa_find(&t->idRoot, &callbackId); if(UA_LIKELY(te != NULL)) { aa_remove(&t->root, te); aa_remove(&t->idRoot, te); UA_free(te); } UA_UNLOCK(&t->timerMutex); } UA_DateTime UA_Timer_process(UA_Timer *t, UA_DateTime nowMonotonic, UA_TimerExecutionCallback executionCallback, void *executionApplication) { UA_LOCK(&t->timerMutex); UA_TimerEntry *first; while((first = (UA_TimerEntry*)aa_min(&t->root)) && first->nextTime <= nowMonotonic) { aa_remove(&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->interval == 0) { aa_remove(&t->idRoot, first); if(first->callback) { UA_UNLOCK(&t->timerMutex); executionCallback(executionApplication, first->callback, first->application, first->data); UA_LOCK(&t->timerMutex); } UA_free(first); continue; } /* Set the time for the next execution. Prevent an infinite loop by * forcing the execution time in the next iteration. * * If the timer policy is "CurrentTime", then there is at least the * interval between executions. This is used for Monitoreditems, for * which the spec says: The sampling interval indicates the fastest rate * at which the Server should sample its underlying source for data * changes. (Part 4, 5.12.1.2) */ first->nextTime += (UA_DateTime)first->interval; if(first->nextTime < nowMonotonic) { if(first->timerPolicy == UA_TIMER_HANDLE_CYCLEMISS_WITH_BASETIME) first->nextTime = calculateNextTime(nowMonotonic, first->nextTime, (UA_DateTime)first->interval); else first->nextTime = nowMonotonic + (UA_DateTime)first->interval; } aa_insert(&t->root, first); if(!first->callback) continue; /* Unlock the mutes before dropping into the callback. So that the timer * itself can be edited within the callback. When we return, only the * pointer to t must still exist. */ UA_ApplicationCallback cb = first->callback; void *app = first->application; void *data = first->data; UA_UNLOCK(&t->timerMutex); executionCallback(executionApplication, cb, app, data); UA_LOCK(&t->timerMutex); } /* Return the timestamp of the earliest next callback */ first = (UA_TimerEntry*)aa_min(&t->root); UA_DateTime next = (first) ? first->nextTime : UA_INT64_MAX; if(next < nowMonotonic) next = nowMonotonic; UA_UNLOCK(&t->timerMutex); return next; } void UA_Timer_clear(UA_Timer *t) { UA_LOCK(&t->timerMutex); /* Free all entries */ UA_TimerEntry *top; while((top = (UA_TimerEntry*)aa_min(&t->idRoot))) { aa_remove(&t->idRoot, top); UA_free(top); } /* Reset the trees to avoid future access */ t->root.root = NULL; t->idRoot.root = NULL; UA_UNLOCK(&t->timerMutex); #if UA_MULTITHREADING >= 100 UA_LOCK_DESTROY(&t->timerMutex); #endif } /**** amalgamated original file "/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 */ /* 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]; retval |= UA_encodeBinaryInternal(&header, &UA_TRANSPORT[UA_TRANSPORT_TCPMESSAGEHEADER], &bufPos, &bufEnd, NULL, NULL); retval |= UA_encodeBinaryInternal(error, &UA_TRANSPORT[UA_TRANSPORT_TCPERRORMESSAGE], &bufPos, &bufEnd, NULL, NULL); (void)retval; /* Encoding of these cannot fail */ msg.length = header.messageSize; connection->send(connection, &msg); } 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 "/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-2020 (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 * Copyright 2018-2019 (c) HMS Industrial Networks AB (Author: Jonas Green) */ #define UA_BITMASK_MESSAGETYPE 0x00ffffffu #define UA_BITMASK_CHUNKTYPE 0xff000000u const UA_String 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, const UA_ConnectionConfig *config) { /* Linked lists are also initialized by zeroing out */ memset(channel, 0, sizeof(UA_SecureChannel)); channel->state = UA_SECURECHANNELSTATE_FRESH; SIMPLEQ_INIT(&channel->completeChunks); SIMPLEQ_INIT(&channel->decryptedChunks); SLIST_INIT(&channel->sessions); channel->config = *config; } UA_StatusCode UA_SecureChannel_setSecurityPolicy(UA_SecureChannel *channel, const UA_SecurityPolicy *securityPolicy, const UA_ByteString *remoteCertificate) { /* Is a policy already configured? */ UA_CHECK_ERROR(!channel->securityPolicy, return UA_STATUSCODE_BADINTERNALERROR, securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Security policy already configured"); /* Create the context */ UA_StatusCode res = securityPolicy->channelModule. newContext(securityPolicy, remoteCertificate, &channel->channelContext); res |= UA_ByteString_copy(remoteCertificate, &channel->remoteCertificate); UA_CHECK_STATUS_WARN(res, return res, securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Could not set up the SecureChannel context"); /* Compute the certificate thumbprint */ UA_ByteString remoteCertificateThumbprint = {20, channel->remoteCertificateThumbprint}; res = securityPolicy->asymmetricModule. makeCertificateThumbprint(securityPolicy, &channel->remoteCertificate, &remoteCertificateThumbprint); UA_CHECK_STATUS_WARN(res, return res, securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Could not create the certificate thumbprint"); /* Set the policy */ channel->securityPolicy = securityPolicy; return UA_STATUSCODE_GOOD; } static void UA_Chunk_delete(UA_Chunk *chunk) { if(chunk->copied) UA_ByteString_clear(&chunk->bytes); UA_free(chunk); } static void deleteChunks(UA_ChunkQueue *queue) { UA_Chunk *chunk; while((chunk = SIMPLEQ_FIRST(queue))) { SIMPLEQ_REMOVE_HEAD(queue, pointers); UA_Chunk_delete(chunk); } } void UA_SecureChannel_deleteBuffered(UA_SecureChannel *channel) { deleteChunks(&channel->completeChunks); deleteChunks(&channel->decryptedChunks); UA_ByteString_clear(&channel->incompleteChunk); } 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_CONNECTIONSTATE_CLOSED) channel->connection->close(channel->connection); UA_Connection_detachSecureChannel(channel->connection); } /* Detach Sessions from the SecureChannel. This also removes outstanding * Publish requests whose RequestId is valid only for the SecureChannel. */ UA_SessionHeader *sh; while((sh = SLIST_FIRST(&channel->sessions))) { if(sh->serverSession) { UA_Session_detachFromSecureChannel((UA_Session *)sh); } else { sh->channel = NULL; SLIST_REMOVE_HEAD(&channel->sessions, next); } } /* Delete the channel context for the security policy */ if(channel->securityPolicy) { channel->securityPolicy->channelModule.deleteContext(channel->channelContext); channel->securityPolicy = NULL; channel->channelContext = NULL; } /* Delete members */ UA_ByteString_clear(&channel->remoteCertificate); UA_ByteString_clear(&channel->localNonce); UA_ByteString_clear(&channel->remoteNonce); UA_ChannelSecurityToken_clear(&channel->securityToken); UA_ChannelSecurityToken_clear(&channel->altSecurityToken); UA_SecureChannel_deleteBuffered(channel); } UA_StatusCode UA_SecureChannel_processHELACK(UA_SecureChannel *channel, const UA_TcpAcknowledgeMessage *remoteConfig) { /* The lowest common version is used by both sides */ if(channel->config.protocolVersion > remoteConfig->protocolVersion) channel->config.protocolVersion = remoteConfig->protocolVersion; /* Can we receive the max send size? */ if(channel->config.sendBufferSize > remoteConfig->receiveBufferSize) channel->config.sendBufferSize = remoteConfig->receiveBufferSize; /* Can we send the max receive size? */ if(channel->config.recvBufferSize > remoteConfig->sendBufferSize) channel->config.recvBufferSize = remoteConfig->sendBufferSize; channel->config.remoteMaxMessageSize = remoteConfig->maxMessageSize; channel->config.remoteMaxChunkCount = remoteConfig->maxChunkCount; /* Chunks of at least 8192 bytes must be permissible. * See Part 6, Clause 6.7.1 */ if(channel->config.recvBufferSize < 8192 || channel->config.sendBufferSize < 8192 || (channel->config.remoteMaxMessageSize != 0 && channel->config.remoteMaxMessageSize < 8192)) return UA_STATUSCODE_BADINTERNALERROR; channel->connection->state = UA_CONNECTIONSTATE_ESTABLISHED; return UA_STATUSCODE_GOOD; } /* 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) { UA_CHECK(channel->securityMode != UA_MESSAGESECURITYMODE_INVALID, return UA_STATUSCODE_BADSECURITYMODEREJECTED); const UA_SecurityPolicy *sp = channel->securityPolicy; UA_Connection *conn = channel->connection; UA_CHECK_MEM(sp, return UA_STATUSCODE_BADINTERNALERROR); UA_CHECK_MEM(conn, return UA_STATUSCODE_BADINTERNALERROR); /* Allocate the message buffer */ UA_ByteString buf = UA_BYTESTRING_NULL; UA_StatusCode res = conn->getSendBuffer(conn, channel->config.sendBufferSize, &buf); UA_CHECK_STATUS(res, return res); /* 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 */ res |= UA_NodeId_encodeBinary(&contentType->binaryEncodingId, &buf_pos, buf_end); res |= UA_encodeBinaryInternal(content, contentType, &buf_pos, &buf_end, NULL, NULL); UA_CHECK_STATUS(res, conn->releaseSendBuffer(conn, &buf); return res); const size_t securityHeaderLength = calculateAsymAlgSecurityHeaderLength(channel); #ifdef UA_ENABLE_ENCRYPTION /* Add padding to the chunk. 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_NONE) padChunk(channel, &channel->securityPolicy->asymmetricModule.cryptoModule, &buf.data[UA_SECURECHANNEL_CHANNELHEADER_LENGTH + 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 += sp->asymmetricModule.cryptoModule.signatureAlgorithm. getLocalSignatureSize(channel->channelContext); /* The total message length is known here which is why we encode the headers * at this step and not earlier. */ size_t encryptedLength = 0; res = prependHeadersAsym(channel, buf.data, buf_end, total_length, securityHeaderLength, requestId, &encryptedLength); UA_CHECK_STATUS(res, conn->releaseSendBuffer(conn, &buf); return res); #ifdef UA_ENABLE_ENCRYPTION res = signAndEncryptAsym(channel, pre_sig_length, &buf, securityHeaderLength, total_length); UA_CHECK_STATUS(res, conn->releaseSendBuffer(conn, &buf); return res); #endif /* Send the message, the buffer is freed in the network layer */ buf.length = encryptedLength; res = conn->send(conn, &buf); #ifdef UA_ENABLE_UNIT_TEST_FAILURE_HOOKS res |= sendAsym_sendFailure; #endif return res; } /* Will this chunk surpass the capacity of the SecureChannel for the message? */ static UA_StatusCode adjustCheckMessageLimitsSym(UA_MessageContext *mc, size_t bodyLength) { mc->messageSizeSoFar += bodyLength; mc->chunksSoFar++; UA_SecureChannel *channel = mc->channel; if(mc->messageSizeSoFar > channel->config.localMaxMessageSize && channel->config.localMaxMessageSize != 0) return UA_STATUSCODE_BADRESPONSETOOLARGE; if(mc->chunksSoFar > channel->config.localMaxChunkCount && channel->config.localMaxChunkCount != 0) return UA_STATUSCODE_BADRESPONSETOOLARGE; return UA_STATUSCODE_GOOD; } static UA_StatusCode encodeHeadersSym(UA_MessageContext *mc, size_t totalLength) { UA_SecureChannel *channel = mc->channel; UA_Byte *header_pos = mc->messageBuffer.data; UA_TcpMessageHeader header; header.messageTypeAndChunkType = mc->messageType; header.messageSize = (UA_UInt32)totalLength; if(mc->final) header.messageTypeAndChunkType += UA_CHUNKTYPE_FINAL; else header.messageTypeAndChunkType += UA_CHUNKTYPE_INTERMEDIATE; UA_SequenceHeader seqHeader; seqHeader.requestId = mc->requestId; seqHeader.sequenceNumber = UA_atomic_addUInt32(&channel->sendSequenceNumber, 1); UA_StatusCode res = UA_STATUSCODE_GOOD; res |= UA_encodeBinaryInternal(&header, &UA_TRANSPORT[UA_TRANSPORT_TCPMESSAGEHEADER], &header_pos, &mc->buf_end, NULL, NULL); res |= UA_UInt32_encodeBinary(&channel->securityToken.channelId, &header_pos, mc->buf_end); res |= UA_UInt32_encodeBinary(&channel->securityToken.tokenId, &header_pos, mc->buf_end); res |= UA_encodeBinaryInternal(&seqHeader, &UA_TRANSPORT[UA_TRANSPORT_SEQUENCEHEADER], &header_pos, &mc->buf_end, NULL, NULL); return res; } static UA_StatusCode sendSymmetricChunk(UA_MessageContext *mc) { UA_SecureChannel *channel = mc->channel; const UA_SecurityPolicy *sp = channel->securityPolicy; UA_Connection *connection = channel->connection; UA_CHECK_MEM(connection, return UA_STATUSCODE_BADINTERNALERROR); /* The size of the message payload */ size_t bodyLength = (uintptr_t)mc->buf_pos - (uintptr_t)&mc->messageBuffer.data[UA_SECURECHANNEL_SYMMETRIC_HEADER_TOTALLENGTH]; /* Early-declare variables so we can use a goto in the error case */ size_t total_length = 0; size_t pre_sig_length = 0; /* Check if chunk exceeds the limits for the overall message */ UA_StatusCode res = adjustCheckMessageLimitsSym(mc, bodyLength); UA_CHECK_STATUS(res, goto error); UA_LOG_TRACE_CHANNEL(sp->logger, channel, "Send from a symmetric message buffer of length %lu " "a message of header+payload length of %lu", (long unsigned int)mc->messageBuffer.length, (long unsigned int) ((uintptr_t)mc->buf_pos - (uintptr_t)mc->messageBuffer.data)); #ifdef UA_ENABLE_ENCRYPTION /* Add padding if the message is encrypted */ if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) padChunk(channel, &sp->symmetricModule.cryptoModule, &mc->messageBuffer.data[UA_SECURECHANNEL_SYMMETRIC_HEADER_UNENCRYPTEDLENGTH], &mc->buf_pos); #endif /* Compute the total message length */ pre_sig_length = (uintptr_t)mc->buf_pos - (uintptr_t)mc->messageBuffer.data; total_length = pre_sig_length; if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGN || channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) total_length += sp->symmetricModule.cryptoModule.signatureAlgorithm. getLocalSignatureSize(channel->channelContext); UA_LOG_TRACE_CHANNEL(sp->logger, channel, "Send from a symmetric message buffer of length %lu " "a message of length %lu", (long unsigned int)mc->messageBuffer.length, (long unsigned int)total_length); /* Space for the padding and the signature have been reserved in setBufPos() */ UA_assert(total_length <= channel->config.sendBufferSize); /* Adjust the buffer size of the network layer */ mc->messageBuffer.length = total_length; /* Generate and encode the header for symmetric messages */ res = encodeHeadersSym(mc, total_length); UA_CHECK_STATUS(res, goto error); #ifdef UA_ENABLE_ENCRYPTION /* Sign and encrypt the messge */ res = signAndEncryptSym(mc, pre_sig_length, total_length); UA_CHECK_STATUS(res, goto error); #endif /* Send the chunk, the buffer is freed in the network layer */ return connection->send(channel->connection, &mc->messageBuffer); error: /* Free the unused message buffer */ connection->releaseSendBuffer(channel->connection, &mc->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 res = sendSymmetricChunk(mc); UA_CHECK_STATUS(res, return res); /* Set a new buffer for the next chunk */ UA_Connection *c = mc->channel->connection; UA_CHECK_MEM(c, return UA_STATUSCODE_BADINTERNALERROR); res = c->getSendBuffer(c, mc->channel->config.sendBufferSize, &mc->messageBuffer); UA_CHECK_STATUS(res, return res); /* 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_CHECK(messageType == UA_MESSAGETYPE_MSG || messageType == UA_MESSAGETYPE_CLO, return UA_STATUSCODE_BADINTERNALERROR); UA_Connection *c = channel->connection; UA_CHECK_MEM(c, 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 res = c->getSendBuffer(c, channel->config.sendBufferSize, &mc->messageBuffer); UA_CHECK_STATUS(res, return res); /* 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 res = UA_encodeBinaryInternal(content, contentType, &mc->buf_pos, &mc->buf_end, sendSymmetricEncodingCallback, mc); if(res != UA_STATUSCODE_GOOD && mc->messageBuffer.length > 0) UA_MessageContext_abort(mc); return res; } 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->state != UA_SECURECHANNELSTATE_OPEN) return UA_STATUSCODE_BADCONNECTIONCLOSED; if(channel->connection->state != UA_CONNECTIONSTATE_ESTABLISHED) return UA_STATUSCODE_BADCONNECTIONCLOSED; UA_MessageContext mc; UA_StatusCode res = UA_MessageContext_begin(&mc, channel, requestId, messageType); UA_CHECK_STATUS(res, return res); /* Assert's required for clang-analyzer */ UA_assert(mc.buf_pos == &mc.messageBuffer.data[UA_SECURECHANNEL_SYMMETRIC_HEADER_TOTALLENGTH]); UA_assert(mc.buf_end <= &mc.messageBuffer.data[mc.messageBuffer.length]); res = UA_MessageContext_encode(&mc, &payloadType->binaryEncodingId, &UA_TYPES[UA_TYPES_NODEID]); UA_CHECK_STATUS(res, return res); res = UA_MessageContext_encode(&mc, payload, payloadType); UA_CHECK_STATUS(res, return res); return UA_MessageContext_finish(&mc); } /********************************/ /* Receive and Process Messages */ /********************************/ /* Does the sequence number match? Otherwise try to rollover. See Part 6, * Section 6.7.2.4 of the standard. */ #define UA_SEQUENCENUMBER_ROLLOVER 4294966271 #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION static UA_StatusCode processSequenceNumberSym(UA_SecureChannel *channel, UA_UInt32 sequenceNumber) { #ifdef UA_ENABLE_UNIT_TEST_FAILURE_HOOKS /* Failure mode hook for unit tests */ if(processSym_seqNumberFailure != UA_STATUSCODE_GOOD) return processSym_seqNumberFailure; #endif if(sequenceNumber != channel->receiveSequenceNumber + 1) { if(channel->receiveSequenceNumber + 1 <= UA_SEQUENCENUMBER_ROLLOVER || sequenceNumber >= 1024) return UA_STATUSCODE_BADSECURITYCHECKSFAILED; channel->receiveSequenceNumber = sequenceNumber - 1; /* Roll over */ } ++channel->receiveSequenceNumber; return UA_STATUSCODE_GOOD; } #endif static UA_StatusCode unpackPayloadOPN(UA_SecureChannel *channel, UA_Chunk *chunk, void *application) { UA_assert(chunk->bytes.length >= UA_SECURECHANNEL_MESSAGE_MIN_LENGTH); size_t offset = UA_SECURECHANNEL_MESSAGEHEADER_LENGTH; /* Skip the message header */ UA_UInt32 secureChannelId; UA_StatusCode res = UA_UInt32_decodeBinary(&chunk->bytes, &offset, &secureChannelId); UA_assert(res == UA_STATUSCODE_GOOD); UA_AsymmetricAlgorithmSecurityHeader asymHeader; res = UA_decodeBinaryInternal(&chunk->bytes, &offset, &asymHeader, &UA_TRANSPORT[UA_TRANSPORT_ASYMMETRICALGORITHMSECURITYHEADER], NULL); UA_CHECK_STATUS(res, return res); if(asymHeader.senderCertificate.length > 0) { if(channel->certificateVerification) res = channel->certificateVerification-> verifyCertificate(channel->certificateVerification->context, &asymHeader.senderCertificate); else res = UA_STATUSCODE_BADINTERNALERROR; UA_CHECK_STATUS(res, goto error); } /* New channel, create a security policy context and attach */ if(!channel->securityPolicy) { if(channel->processOPNHeader) res = channel->processOPNHeader(application, channel, &asymHeader); if(!channel->securityPolicy) res = UA_STATUSCODE_BADINTERNALERROR; UA_CHECK_STATUS(res, goto error); } /* On the client side, take the SecureChannelId from the first response */ if(secureChannelId != 0 && channel->securityToken.channelId == 0) channel->securityToken.channelId = secureChannelId; #if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) /* Check the ChannelId. Non-opened channels have the id zero. */ if(secureChannelId != channel->securityToken.channelId) { res = UA_STATUSCODE_BADSECURECHANNELIDINVALID; goto error; } #endif /* Check the header */ res = checkAsymHeader(channel, &asymHeader); UA_AsymmetricAlgorithmSecurityHeader_clear(&asymHeader); UA_CHECK_STATUS(res, return res); /* Decrypt the chunk payload */ res = decryptAndVerifyChunk(channel, &channel->securityPolicy->asymmetricModule.cryptoModule, chunk->messageType, &chunk->bytes, offset); UA_CHECK_STATUS(res, return res); /* Decode the SequenceHeader */ UA_SequenceHeader sequenceHeader; res = UA_decodeBinaryInternal(&chunk->bytes, &offset, &sequenceHeader, &UA_TRANSPORT[UA_TRANSPORT_SEQUENCEHEADER], NULL); UA_CHECK_STATUS(res, return res); /* Set the sequence number for the channel from which to count up */ channel->receiveSequenceNumber = sequenceHeader.sequenceNumber; chunk->requestId = sequenceHeader.requestId; /* Set the RequestId of the chunk */ /* Use only the payload */ chunk->bytes.data += offset; chunk->bytes.length -= offset; return UA_STATUSCODE_GOOD; error: UA_AsymmetricAlgorithmSecurityHeader_clear(&asymHeader); return res; } static UA_StatusCode unpackPayloadMSG(UA_SecureChannel *channel, UA_Chunk *chunk) { UA_CHECK_MEM(channel->securityPolicy, return UA_STATUSCODE_BADINTERNALERROR); UA_assert(chunk->bytes.length >= UA_SECURECHANNEL_MESSAGE_MIN_LENGTH); size_t offset = UA_SECURECHANNEL_MESSAGEHEADER_LENGTH; /* Skip the message header */ UA_UInt32 secureChannelId; UA_UInt32 tokenId; /* SymmetricAlgorithmSecurityHeader */ UA_StatusCode res = UA_STATUSCODE_GOOD; res |= UA_UInt32_decodeBinary(&chunk->bytes, &offset, &secureChannelId); res |= UA_UInt32_decodeBinary(&chunk->bytes, &offset, &tokenId); UA_assert(offset == UA_SECURECHANNEL_MESSAGE_MIN_LENGTH); UA_assert(res == UA_STATUSCODE_GOOD); #if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) /* Check the ChannelId. Non-opened channels have the id zero. */ UA_CHECK(secureChannelId == channel->securityToken.channelId, return UA_STATUSCODE_BADSECURECHANNELIDINVALID); #endif /* Check (and revolve) the SecurityToken */ res = checkSymHeader(channel, tokenId); UA_CHECK_STATUS(res, return res); /* Decrypt the chunk payload */ res = decryptAndVerifyChunk(channel, &channel->securityPolicy->symmetricModule.cryptoModule, chunk->messageType, &chunk->bytes, offset); UA_CHECK_STATUS(res, return res); /* Check the sequence number. Skip sequence number checking for fuzzer to * improve coverage */ UA_SequenceHeader sequenceHeader; res = UA_decodeBinaryInternal(&chunk->bytes, &offset, &sequenceHeader, &UA_TRANSPORT[UA_TRANSPORT_SEQUENCEHEADER], NULL); #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION res |= processSequenceNumberSym(channel, sequenceHeader.sequenceNumber); #endif UA_CHECK_STATUS(res, return res); chunk->requestId = sequenceHeader.requestId; /* Set the RequestId of the chunk */ /* Use only the payload */ chunk->bytes.data += offset; chunk->bytes.length -= offset; return UA_STATUSCODE_GOOD; } static UA_StatusCode assembleProcessMessage(UA_SecureChannel *channel, void *application, UA_ProcessMessageCallback callback) { UA_Chunk *chunk = SIMPLEQ_FIRST(&channel->decryptedChunks); UA_assert(chunk != NULL); UA_StatusCode res = UA_STATUSCODE_GOOD; if(chunk->chunkType == UA_CHUNKTYPE_FINAL) { SIMPLEQ_REMOVE_HEAD(&channel->decryptedChunks, pointers); UA_assert(chunk->chunkType == UA_CHUNKTYPE_FINAL); res = callback(application, channel, chunk->messageType, chunk->requestId, &chunk->bytes); UA_Chunk_delete(chunk); return res; } UA_UInt32 requestId = chunk->requestId; UA_MessageType messageType = chunk->messageType; UA_ChunkType chunkType = chunk->chunkType; UA_assert(chunkType == UA_CHUNKTYPE_INTERMEDIATE); size_t messageSize = 0; SIMPLEQ_FOREACH(chunk, &channel->decryptedChunks, pointers) { /* Consistency check */ if(requestId != chunk->requestId) return UA_STATUSCODE_BADINTERNALERROR; if(chunkType != chunk->chunkType && chunk->chunkType != UA_CHUNKTYPE_FINAL) return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; if(chunk->messageType != messageType) return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; /* Sum up the lengths */ messageSize += chunk->bytes.length; if(chunk->chunkType == UA_CHUNKTYPE_FINAL) break; } /* Allocate memory for the full message */ UA_ByteString payload; res = UA_ByteString_allocBuffer(&payload, messageSize); UA_CHECK_STATUS(res, return res); /* Assemble the full message */ size_t offset = 0; while(true) { chunk = SIMPLEQ_FIRST(&channel->decryptedChunks); memcpy(&payload.data[offset], chunk->bytes.data, chunk->bytes.length); offset += chunk->bytes.length; SIMPLEQ_REMOVE_HEAD(&channel->decryptedChunks, pointers); UA_ChunkType ct = chunk->chunkType; UA_Chunk_delete(chunk); if(ct == UA_CHUNKTYPE_FINAL) break; } /* Process the assembled message */ res = callback(application, channel, messageType, requestId, &payload); UA_ByteString_clear(&payload); return res; } static UA_StatusCode persistCompleteChunks(UA_ChunkQueue *queue) { UA_Chunk *chunk; SIMPLEQ_FOREACH(chunk, queue, pointers) { if(chunk->copied) continue; UA_ByteString copy; UA_StatusCode res = UA_ByteString_copy(&chunk->bytes, ©); UA_CHECK_STATUS(res, return res); chunk->bytes = copy; chunk->copied = true; } return UA_STATUSCODE_GOOD; } static UA_StatusCode persistIncompleteChunk(UA_SecureChannel *channel, const UA_ByteString *buffer, size_t offset) { UA_assert(channel->incompleteChunk.length == 0); UA_assert(offset < buffer->length); size_t length = buffer->length - offset; UA_StatusCode res = UA_ByteString_allocBuffer(&channel->incompleteChunk, length); UA_CHECK_STATUS(res, return res); memcpy(channel->incompleteChunk.data, &buffer->data[offset], length); return UA_STATUSCODE_GOOD; } /* Processes chunks and puts them into the payloads queue. Once a final chunk is * put into the queue, the message is assembled and the callback is called. The * queue will be cleared for the next message. */ static UA_StatusCode processChunks(UA_SecureChannel *channel, void *application, UA_ProcessMessageCallback callback) { UA_Chunk *chunk; UA_StatusCode res = UA_STATUSCODE_GOOD; while((chunk = SIMPLEQ_FIRST(&channel->completeChunks))) { /* Remove from the complete-chunk queue */ SIMPLEQ_REMOVE_HEAD(&channel->completeChunks, pointers); /* Check, decrypt and unpack the payload */ if(chunk->messageType == UA_MESSAGETYPE_OPN) { if(channel->state != UA_SECURECHANNELSTATE_OPEN && channel->state != UA_SECURECHANNELSTATE_OPN_SENT && channel->state != UA_SECURECHANNELSTATE_ACK_SENT) res = UA_STATUSCODE_BADINVALIDSTATE; else res = unpackPayloadOPN(channel, chunk, application); } else if(chunk->messageType == UA_MESSAGETYPE_MSG || chunk->messageType == UA_MESSAGETYPE_CLO) { if(channel->state == UA_SECURECHANNELSTATE_CLOSED) res = UA_STATUSCODE_BADSECURECHANNELCLOSED; else res = unpackPayloadMSG(channel, chunk); } else { chunk->bytes.data += UA_SECURECHANNEL_MESSAGEHEADER_LENGTH; chunk->bytes.length -= UA_SECURECHANNEL_MESSAGEHEADER_LENGTH; } if(res != UA_STATUSCODE_GOOD) { UA_Chunk_delete(chunk); return res; } /* Add to the decrypted-chunk queue */ SIMPLEQ_INSERT_TAIL(&channel->decryptedChunks, chunk, pointers); /* Check the resource limits */ channel->decryptedChunksCount++; channel->decryptedChunksLength += chunk->bytes.length; if((channel->config.localMaxChunkCount != 0 && channel->decryptedChunksCount > channel->config.localMaxChunkCount) || (channel->config.localMaxMessageSize != 0 && channel->decryptedChunksLength > channel->config.localMaxMessageSize)) { return UA_STATUSCODE_BADTCPMESSAGETOOLARGE; } /* Waiting for additional chunks */ if(chunk->chunkType == UA_CHUNKTYPE_INTERMEDIATE) continue; /* Final chunk or abort. Reset the counters. */ channel->decryptedChunksCount = 0; channel->decryptedChunksLength = 0; /* Abort the message, remove all decrypted chunks * TODO: Log a warning with the error code */ if(chunk->chunkType == UA_CHUNKTYPE_ABORT) { while((chunk = SIMPLEQ_FIRST(&channel->decryptedChunks))) { SIMPLEQ_REMOVE_HEAD(&channel->decryptedChunks, pointers); UA_Chunk_delete(chunk); } continue; } /* The decrypted queue contains a full message. Process it. */ UA_assert(chunk->chunkType == UA_CHUNKTYPE_FINAL); res = assembleProcessMessage(channel, application, callback); UA_CHECK_STATUS(res, return res); } return UA_STATUSCODE_GOOD; } static UA_StatusCode extractCompleteChunk(UA_SecureChannel *channel, const UA_ByteString *buffer, size_t *offset, UA_Boolean *done) { /* At least 8 byte needed for the header. Wait for the next chunk. */ size_t initial_offset = *offset; size_t remaining = buffer->length - initial_offset; if(remaining < UA_SECURECHANNEL_MESSAGEHEADER_LENGTH) { *done = true; return UA_STATUSCODE_GOOD; } /* Decoding cannot fail */ UA_TcpMessageHeader hdr; UA_StatusCode res = UA_decodeBinaryInternal(buffer, &initial_offset, &hdr, &UA_TRANSPORT[UA_TRANSPORT_TCPMESSAGEHEADER], NULL); UA_assert(res == UA_STATUSCODE_GOOD); (void)res; /* pacify compilers if assert is ignored */ UA_MessageType msgType = (UA_MessageType) (hdr.messageTypeAndChunkType & UA_BITMASK_MESSAGETYPE); UA_ChunkType chunkType = (UA_ChunkType) (hdr.messageTypeAndChunkType & UA_BITMASK_CHUNKTYPE); /* The message size is not allowed */ if(hdr.messageSize < UA_SECURECHANNEL_MESSAGE_MIN_LENGTH) return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; if(hdr.messageSize > channel->config.recvBufferSize) return UA_STATUSCODE_BADTCPMESSAGETOOLARGE; /* Incomplete chunk */ if(hdr.messageSize > remaining) { *done = true; return UA_STATUSCODE_GOOD; } /* ByteString with only this chunk. */ UA_ByteString chunkPayload; chunkPayload.data = &buffer->data[*offset]; chunkPayload.length = hdr.messageSize; if(msgType == UA_MESSAGETYPE_HEL || msgType == UA_MESSAGETYPE_ACK || msgType == UA_MESSAGETYPE_ERR || msgType == UA_MESSAGETYPE_OPN) { if(chunkType != UA_CHUNKTYPE_FINAL) return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; } else { /* Only messages on SecureChannel-level with symmetric encryption afterwards */ if(msgType != UA_MESSAGETYPE_MSG && msgType != UA_MESSAGETYPE_CLO) return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; /* Check the chunk type before decrypting */ if(chunkType != UA_CHUNKTYPE_FINAL && chunkType != UA_CHUNKTYPE_INTERMEDIATE && chunkType != UA_CHUNKTYPE_ABORT) return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; } /* Add the chunk; forward the offset */ *offset += hdr.messageSize; UA_Chunk *chunk = (UA_Chunk*)UA_malloc(sizeof(UA_Chunk)); UA_CHECK_MEM(chunk, return UA_STATUSCODE_BADOUTOFMEMORY); chunk->bytes = chunkPayload; chunk->messageType = msgType; chunk->chunkType = chunkType; chunk->requestId = 0; chunk->copied = false; SIMPLEQ_INSERT_TAIL(&channel->completeChunks, chunk, pointers); return UA_STATUSCODE_GOOD; } UA_StatusCode UA_SecureChannel_processBuffer(UA_SecureChannel *channel, void *application, UA_ProcessMessageCallback callback, const UA_ByteString *buffer) { /* 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. */ UA_ByteString appended = channel->incompleteChunk; if(appended.length > 0) { channel->incompleteChunk = UA_BYTESTRING_NULL; UA_Byte *t = (UA_Byte*)UA_realloc(appended.data, appended.length + buffer->length); UA_CHECK_MEM(t, UA_ByteString_clear(&appended); return UA_STATUSCODE_BADOUTOFMEMORY); memcpy(&t[appended.length], buffer->data, buffer->length); appended.data = t; appended.length += buffer->length; buffer = &appended; } /* Loop over the received chunks */ size_t offset = 0; UA_Boolean done = false; UA_StatusCode res; while(!done) { res = extractCompleteChunk(channel, buffer, &offset, &done); UA_CHECK_STATUS(res, goto cleanup); } /* Buffer half-received chunk. Before processing the messages so that * processing is reentrant. */ if(offset < buffer->length) { res = persistIncompleteChunk(channel, buffer, offset); UA_CHECK_STATUS(res, goto cleanup); } /* Process whatever we can. Chunks of completed and processed messages are * removed. */ res = processChunks(channel, application, callback); UA_CHECK_STATUS(res, goto cleanup); /* Persist full chunks that still point to the buffer. Can only return * UA_STATUSCODE_BADOUTOFMEMORY as an error code. So merging res works. */ res |= persistCompleteChunks(&channel->completeChunks); res |= persistCompleteChunks(&channel->decryptedChunks); cleanup: UA_ByteString_clear(&appended); return res; } UA_StatusCode UA_SecureChannel_receive(UA_SecureChannel *channel, void *application, UA_ProcessMessageCallback callback, UA_UInt32 timeout) { UA_Connection *connection = channel->connection; UA_CHECK_MEM(connection, return UA_STATUSCODE_BADINTERNALERROR); /* Listen for messages to arrive */ UA_ByteString buffer = UA_BYTESTRING_NULL; UA_StatusCode res = connection->recv(connection, &buffer, timeout); UA_CHECK_STATUS(res, return res); /* Try to process one complete chunk */ res = UA_SecureChannel_processBuffer(channel, application, callback, &buffer); connection->releaseRecvBuffer(connection, &buffer); return res; } /**** amalgamated original file "/src/ua_securechannel_crypto.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-2020 (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 */ UA_StatusCode UA_SecureChannel_generateLocalNonce(UA_SecureChannel *channel) { const UA_SecurityPolicy *sp = channel->securityPolicy; UA_CHECK_MEM(sp, return UA_STATUSCODE_BADINTERNALERROR); UA_LOG_DEBUG_CHANNEL(sp->logger, channel, "Generating new local nonce"); /* Is the length of the previous nonce correct? */ size_t nonceLength = sp->symmetricModule.secureChannelNonceLength; if(channel->localNonce.length != nonceLength) { UA_ByteString_clear(&channel->localNonce); UA_StatusCode res = UA_ByteString_allocBuffer(&channel->localNonce, nonceLength); UA_CHECK_STATUS(res, return res); } /* Generate the nonce */ return sp->symmetricModule.generateNonce(sp->policyContext, &channel->localNonce); } UA_StatusCode UA_SecureChannel_generateLocalKeys(const UA_SecureChannel *channel) { const UA_SecurityPolicy *sp = channel->securityPolicy; UA_CHECK_MEM(sp, return UA_STATUSCODE_BADINTERNALERROR); UA_LOG_TRACE_CHANNEL(sp->logger, channel, "Generating new local keys"); void *cc = channel->channelContext; const UA_SecurityPolicyChannelModule *cm = &sp->channelModule; const UA_SecurityPolicySymmetricModule *sm = &sp->symmetricModule; const UA_SecurityPolicyCryptoModule *crm = &sm->cryptoModule; /* Generate symmetric key buffer of the required length. The block size is * identical for local/remote. */ UA_ByteString buf; size_t encrKL = crm->encryptionAlgorithm.getLocalKeyLength(cc); size_t encrBS = crm->encryptionAlgorithm.getRemoteBlockSize(cc); size_t signKL = crm->signatureAlgorithm.getLocalKeyLength(cc); if(encrBS + signKL + encrKL == 0) return UA_STATUSCODE_GOOD; /* No keys to generate */ UA_StatusCode retval = UA_ByteString_allocBuffer(&buf, encrBS + signKL + encrKL); UA_CHECK_STATUS(retval, return retval); UA_ByteString localSigningKey = {signKL, buf.data}; UA_ByteString localEncryptingKey = {encrKL, &buf.data[signKL]}; UA_ByteString localIv = {encrBS, &buf.data[signKL + encrKL]}; /* Generate key */ retval = sm->generateKey(sp->policyContext, &channel->remoteNonce, &channel->localNonce, &buf); UA_CHECK_STATUS(retval, goto error); /* Set the channel context */ retval |= cm->setLocalSymSigningKey(cc, &localSigningKey); retval |= cm->setLocalSymEncryptingKey(cc, &localEncryptingKey); retval |= cm->setLocalSymIv(cc, &localIv); error: UA_CHECK_STATUS(retval, UA_LOG_WARNING_CHANNEL(sp->logger, channel, "Could not generate local keys (statuscode: %s)", UA_StatusCode_name(retval))); UA_ByteString_clear(&buf); return retval; } UA_StatusCode generateRemoteKeys(const UA_SecureChannel *channel) { const UA_SecurityPolicy *sp = channel->securityPolicy; UA_CHECK_MEM(sp, return UA_STATUSCODE_BADINTERNALERROR); UA_LOG_TRACE_CHANNEL(sp->logger, channel, "Generating new remote keys"); void *cc = channel->channelContext; const UA_SecurityPolicyChannelModule *cm = &sp->channelModule; const UA_SecurityPolicySymmetricModule *sm = &sp->symmetricModule; const UA_SecurityPolicyCryptoModule *crm = &sm->cryptoModule; /* Generate symmetric key buffer of the required length */ UA_ByteString buf; size_t encrKL = crm->encryptionAlgorithm.getRemoteKeyLength(cc); size_t encrBS = crm->encryptionAlgorithm.getRemoteBlockSize(cc); size_t signKL = crm->signatureAlgorithm.getRemoteKeyLength(cc); if(encrBS + signKL + encrKL == 0) return UA_STATUSCODE_GOOD; /* No keys to generate */ UA_StatusCode retval = UA_ByteString_allocBuffer(&buf, encrBS + signKL + encrKL); UA_CHECK_STATUS(retval, return retval); UA_ByteString remoteSigningKey = {signKL, buf.data}; UA_ByteString remoteEncryptingKey = {encrKL, &buf.data[signKL]}; UA_ByteString remoteIv = {encrBS, &buf.data[signKL + encrKL]}; /* Generate key */ retval = sm->generateKey(sp->policyContext, &channel->localNonce, &channel->remoteNonce, &buf); UA_CHECK_STATUS(retval, goto error); /* Set the channel context */ retval |= cm->setRemoteSymSigningKey(cc, &remoteSigningKey); retval |= cm->setRemoteSymEncryptingKey(cc, &remoteEncryptingKey); retval |= cm->setRemoteSymIv(cc, &remoteIv); error: UA_CHECK_STATUS(retval, UA_LOG_WARNING_CHANNEL(sp->logger, channel, "Could not generate remote keys (statuscode: %s)", UA_StatusCode_name(retval))); UA_ByteString_clear(&buf); return retval; } /***************************/ /* Send Asymmetric Message */ /***************************/ /* The length of the static header content */ #define UA_SECURECHANNEL_ASYMMETRIC_SECURITYHEADER_FIXED_LENGTH 12 size_t calculateAsymAlgSecurityHeaderLength(const UA_SecureChannel *channel) { const UA_SecurityPolicy *sp = channel->securityPolicy; UA_CHECK_MEM(sp, return UA_STATUSCODE_BADINTERNALERROR); size_t asymHeaderLength = UA_SECURECHANNEL_ASYMMETRIC_SECURITYHEADER_FIXED_LENGTH + sp->policyUri.length; if(channel->securityMode == UA_MESSAGESECURITYMODE_NONE) return asymHeaderLength; /* OPN is always encrypted even if the mode is sign only */ asymHeaderLength += 20; /* Thumbprints are always 20 byte long */ asymHeaderLength += sp->localCertificate.length; return asymHeaderLength; } 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 encryptedLength) { const UA_SecurityPolicy *sp = channel->securityPolicy; UA_CHECK_MEM(sp, return UA_STATUSCODE_BADINTERNALERROR); if(channel->securityMode == UA_MESSAGESECURITYMODE_NONE) { *encryptedLength = totalLength; } else { size_t dataToEncryptLength = totalLength - (UA_SECURECHANNEL_CHANNELHEADER_LENGTH + securityHeaderLength); size_t plainTextBlockSize = sp->asymmetricModule.cryptoModule. encryptionAlgorithm.getRemotePlainTextBlockSize(channel->channelContext); size_t encryptedBlockSize = sp->asymmetricModule.cryptoModule. encryptionAlgorithm.getRemoteBlockSize(channel->channelContext); /* Padding always fills up the last block */ UA_assert(dataToEncryptLength % plainTextBlockSize == 0); size_t blocks = dataToEncryptLength / plainTextBlockSize; *encryptedLength = totalLength + blocks * (encryptedBlockSize - plainTextBlockSize); } UA_TcpMessageHeader messageHeader; messageHeader.messageTypeAndChunkType = UA_MESSAGETYPE_OPN + UA_CHUNKTYPE_FINAL; messageHeader.messageSize = (UA_UInt32)*encryptedLength; UA_UInt32 secureChannelId = channel->securityToken.channelId; UA_StatusCode retval = UA_STATUSCODE_GOOD; retval |= UA_encodeBinaryInternal(&messageHeader, &UA_TRANSPORT[UA_TRANSPORT_TCPMESSAGEHEADER], &header_pos, &buf_end, NULL, NULL); retval |= UA_UInt32_encodeBinary(&secureChannelId, &header_pos, buf_end); UA_CHECK_STATUS(retval, return retval); UA_AsymmetricAlgorithmSecurityHeader asymHeader; UA_AsymmetricAlgorithmSecurityHeader_init(&asymHeader); asymHeader.securityPolicyUri = sp->policyUri; if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGN || channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) { asymHeader.senderCertificate = sp->localCertificate; asymHeader.receiverCertificateThumbprint.length = 20; asymHeader.receiverCertificateThumbprint.data = channel->remoteCertificateThumbprint; } retval = UA_encodeBinaryInternal(&asymHeader, &UA_TRANSPORT[UA_TRANSPORT_ASYMMETRICALGORITHMSECURITYHEADER], &header_pos, &buf_end, NULL, NULL); UA_CHECK_STATUS(retval, return retval); UA_SequenceHeader seqHeader; seqHeader.requestId = requestId; seqHeader.sequenceNumber = UA_atomic_addUInt32(&channel->sendSequenceNumber, 1); retval = UA_encodeBinaryInternal(&seqHeader, &UA_TRANSPORT[UA_TRANSPORT_SEQUENCEHEADER], &header_pos, &buf_end, NULL, NULL); return retval; } void hideBytesAsym(const UA_SecureChannel *channel, UA_Byte **buf_start, const UA_Byte **buf_end) { /* Set buf_start to the beginning of the payload body */ *buf_start += UA_SECURECHANNEL_CHANNELHEADER_LENGTH; *buf_start += calculateAsymAlgSecurityHeaderLength(channel); *buf_start += UA_SECURECHANNEL_SEQUENCEHEADER_LENGTH; #ifdef UA_ENABLE_ENCRYPTION if(channel->securityMode == UA_MESSAGESECURITYMODE_NONE) return; /* Make space for the certificate */ const UA_SecurityPolicy *sp = channel->securityPolicy; *buf_end -= sp->asymmetricModule.cryptoModule.signatureAlgorithm. getLocalSignatureSize(channel->channelContext); /* Block sizes depend on the remote key (certificate) */ size_t plainTextBlockSize = sp->asymmetricModule.cryptoModule. encryptionAlgorithm.getRemotePlainTextBlockSize(channel->channelContext); size_t encryptedBlockSize = sp->asymmetricModule.cryptoModule. encryptionAlgorithm.getRemoteBlockSize(channel->channelContext); UA_Boolean extraPadding = (sp->asymmetricModule.cryptoModule.encryptionAlgorithm. getRemoteKeyLength(channel->channelContext) > 2048); /* Compute the maximum number of encrypted blocks that can fit entirely * before the signature. From that compute the maximum usable plaintext * size. */ size_t maxEncrypted = (size_t)(*buf_end - *buf_start) + UA_SECURECHANNEL_SEQUENCEHEADER_LENGTH; size_t max_blocks = maxEncrypted / encryptedBlockSize; size_t paddingBytes = (UA_LIKELY(!extraPadding)) ? 1u : 2u; *buf_end = *buf_start + (max_blocks * plainTextBlockSize) - UA_SECURECHANNEL_SEQUENCEHEADER_LENGTH - paddingBytes; #endif } #ifdef UA_ENABLE_ENCRYPTION /* Assumes that pos can be advanced to the end of the current block */ void padChunk(UA_SecureChannel *channel, const UA_SecurityPolicyCryptoModule *cm, const UA_Byte *start, UA_Byte **pos) { const size_t bytesToWrite = (uintptr_t)*pos - (uintptr_t)start; size_t signatureSize = cm->signatureAlgorithm. getLocalSignatureSize(channel->channelContext); size_t plainTextBlockSize = cm->encryptionAlgorithm. getRemotePlainTextBlockSize(channel->channelContext); UA_Boolean extraPadding = (cm->encryptionAlgorithm. getRemoteKeyLength(channel->channelContext) > 2048); size_t paddingBytes = (UA_LIKELY(!extraPadding)) ? 1u : 2u; size_t lastBlock = ((bytesToWrite + signatureSize + paddingBytes) % plainTextBlockSize); size_t paddingLength = (lastBlock != 0) ? plainTextBlockSize - lastBlock : 0; UA_LOG_TRACE_CHANNEL(channel->securityPolicy->logger, channel, "Add %lu bytes of padding plus %lu padding size bytes", (long unsigned int)paddingLength, (long unsigned int)paddingBytes); /* Write the padding. This is <= because the paddingSize byte also has to be * written */ UA_Byte paddingByte = (UA_Byte)paddingLength; for(UA_UInt16 i = 0; i <= paddingLength; ++i) { **pos = paddingByte; ++*pos; } /* Write the extra padding byte if required */ if(extraPadding) { **pos = (UA_Byte)(paddingLength >> 8u); ++*pos; } } UA_StatusCode signAndEncryptAsym(UA_SecureChannel *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; /* Sign message */ const UA_SecurityPolicy *sp = channel->securityPolicy; const UA_ByteString dataToSign = {preSignLength, buf->data}; size_t sigsize = sp->asymmetricModule.cryptoModule.signatureAlgorithm. getLocalSignatureSize(channel->channelContext); UA_ByteString signature = {sigsize, buf->data + preSignLength}; UA_StatusCode retval = sp->asymmetricModule.cryptoModule.signatureAlgorithm. sign(channel->channelContext, &dataToSign, &signature); UA_CHECK_STATUS(retval, 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_SECURECHANNEL_CHANNELHEADER_LENGTH + securityHeaderLength; UA_ByteString dataToEncrypt = {totalLength - unencrypted_length, &buf->data[unencrypted_length]}; return sp->asymmetricModule.cryptoModule.encryptionAlgorithm. encrypt(channel->channelContext, &dataToEncrypt); } /**************************/ /* Send Symmetric Message */ /**************************/ UA_StatusCode signAndEncryptSym(UA_MessageContext *messageContext, size_t preSigLength, size_t totalLength) { const UA_SecureChannel *channel = messageContext->channel; if(channel->securityMode == UA_MESSAGESECURITYMODE_NONE) return UA_STATUSCODE_GOOD; /* Sign */ const UA_SecurityPolicy *sp = channel->securityPolicy; UA_ByteString dataToSign = messageContext->messageBuffer; dataToSign.length = preSigLength; UA_ByteString signature; signature.length = sp->symmetricModule.cryptoModule.signatureAlgorithm. getLocalSignatureSize(channel->channelContext); signature.data = messageContext->buf_pos; UA_StatusCode res = sp->symmetricModule.cryptoModule.signatureAlgorithm. sign(channel->channelContext, &dataToSign, &signature); UA_CHECK_STATUS(res, return res); if(channel->securityMode != UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) return UA_STATUSCODE_GOOD; /* Encrypt */ UA_ByteString dataToEncrypt; dataToEncrypt.data = messageContext->messageBuffer.data + UA_SECURECHANNEL_CHANNELHEADER_LENGTH + UA_SECURECHANNEL_SYMMETRIC_SECURITYHEADER_LENGTH; dataToEncrypt.length = totalLength - (UA_SECURECHANNEL_CHANNELHEADER_LENGTH + UA_SECURECHANNEL_SYMMETRIC_SECURITYHEADER_LENGTH); return sp->symmetricModule.cryptoModule.encryptionAlgorithm. encrypt(channel->channelContext, &dataToEncrypt); } #endif /* UA_ENABLE_ENCRYPTION */ void setBufPos(UA_MessageContext *mc) { /* Forward the data pointer so that the payload is encoded after the message * header. This has to be a symmetric message because OPN (with asymmetric * encryption) does not support chunking. */ mc->buf_pos = &mc->messageBuffer.data[UA_SECURECHANNEL_SYMMETRIC_HEADER_TOTALLENGTH]; mc->buf_end = &mc->messageBuffer.data[mc->messageBuffer.length]; #ifdef UA_ENABLE_ENCRYPTION if(mc->channel->securityMode == UA_MESSAGESECURITYMODE_NONE) return; const UA_SecureChannel *channel = mc->channel; const UA_SecurityPolicy *sp = channel->securityPolicy; size_t sigsize = sp->symmetricModule.cryptoModule.signatureAlgorithm. getLocalSignatureSize(channel->channelContext); size_t plainBlockSize = sp->symmetricModule.cryptoModule. encryptionAlgorithm.getRemotePlainTextBlockSize(channel->channelContext); /* Assuming that for symmetric encryption the plainTextBlockSize == * cypherTextBlockSize. For symmetric encryption the remote/local block * sizes are identical. */ UA_assert(sp->symmetricModule.cryptoModule.encryptionAlgorithm. getRemoteBlockSize(channel->channelContext) == plainBlockSize); /* Leave enough space for the signature and padding */ mc->buf_end -= sigsize; mc->buf_end -= mc->messageBuffer.length % plainBlockSize; if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) { /* Reserve space for the padding bytes */ UA_Boolean extraPadding = (sp->symmetricModule.cryptoModule.encryptionAlgorithm. getRemoteKeyLength(channel->channelContext) > 2048); mc->buf_end -= (UA_LIKELY(!extraPadding)) ? 1 : 2; } UA_LOG_TRACE_CHANNEL(sp->logger, channel, "Prepare a symmetric message buffer of length %lu " "with a usable maximum payload length of %lu", (long unsigned)mc->messageBuffer.length, (long unsigned)((uintptr_t)mc->buf_end - (uintptr_t)mc->messageBuffer.data)); #endif } /****************************/ /* Process a received Chunk */ /****************************/ static size_t decodePadding(const UA_SecureChannel *channel, const UA_SecurityPolicyCryptoModule *cryptoModule, const UA_ByteString *chunk, size_t sigsize) { /* Read the byte with the padding size */ size_t paddingSize = chunk->data[chunk->length - sigsize - 1]; /* Extra padding size */ if(cryptoModule->encryptionAlgorithm. getLocalKeyLength(channel->channelContext) > 2048) { paddingSize <<= 8u; paddingSize += chunk->data[chunk->length - sigsize - 2]; paddingSize += 1; /* Extra padding byte itself */ } /* Add one since the paddingSize byte itself needs to be removed as well */ return paddingSize + 1; } static UA_StatusCode verifySignature(const UA_SecureChannel *channel, const UA_SecurityPolicyCryptoModule *cryptoModule, const UA_ByteString *chunk, size_t sigsize) { UA_LOG_TRACE_CHANNEL(channel->securityPolicy->logger, channel, "Verifying chunk signature"); UA_CHECK(sigsize < chunk->length, return UA_STATUSCODE_BADSECURITYCHECKSFAILED); const UA_ByteString content = {chunk->length - sigsize, chunk->data}; const UA_ByteString sig = {sigsize, chunk->data + chunk->length - sigsize}; UA_StatusCode retval = cryptoModule->signatureAlgorithm. verify(channel->channelContext, &content, &sig); #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 */ UA_StatusCode decryptAndVerifyChunk(const UA_SecureChannel *channel, const UA_SecurityPolicyCryptoModule *cryptoModule, UA_MessageType messageType, UA_ByteString *chunk, size_t offset) { /* Decrypt the chunk */ UA_StatusCode res = UA_STATUSCODE_GOOD; if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT || messageType == UA_MESSAGETYPE_OPN) { UA_ByteString cipher = {chunk->length - offset, chunk->data + offset}; res = cryptoModule->encryptionAlgorithm.decrypt(channel->channelContext, &cipher); UA_CHECK_STATUS(res, return res); chunk->length = cipher.length + offset; } /* Does the message have a signature? */ if(channel->securityMode != UA_MESSAGESECURITYMODE_SIGN && channel->securityMode != UA_MESSAGESECURITYMODE_SIGNANDENCRYPT && messageType != UA_MESSAGETYPE_OPN) return UA_STATUSCODE_GOOD; /* Verify the chunk signature */ size_t sigsize = cryptoModule->signatureAlgorithm. getRemoteSignatureSize(channel->channelContext); res = verifySignature(channel, cryptoModule, chunk, sigsize); UA_CHECK_STATUS(res, UA_LOG_WARNING_CHANNEL(channel->securityPolicy->logger, channel, "Could not verify the signature"); return res); /* Compute the padding if the payload as encrypted */ size_t padSize = 0; if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT || (messageType == UA_MESSAGETYPE_OPN && cryptoModule->encryptionAlgorithm.uri.length > 0)) { padSize = decodePadding(channel, cryptoModule, chunk, sigsize); UA_LOG_TRACE_CHANNEL(channel->securityPolicy->logger, channel, "Calculated padding size to be %lu", (long unsigned)padSize); } /* Verify the content length. The encrypted payload has to be at least 9 * bytes long: 8 byte for the SequenceHeader and one byte for the actual * message */ UA_CHECK(offset + padSize + sigsize + 9 < chunk->length, UA_LOG_WARNING_CHANNEL(channel->securityPolicy->logger, channel, "Impossible padding value"); return UA_STATUSCODE_BADSECURITYCHECKSFAILED); /* Hide the signature and padding */ chunk->length -= (sigsize + padSize); return UA_STATUSCODE_GOOD; } UA_StatusCode checkAsymHeader(UA_SecureChannel *channel, const UA_AsymmetricAlgorithmSecurityHeader *asymHeader) { const UA_SecurityPolicy *sp = channel->securityPolicy; if(!UA_ByteString_equal(&sp->policyUri, &asymHeader->securityPolicyUri)) return UA_STATUSCODE_BADSECURITYPOLICYREJECTED; return sp->asymmetricModule. compareCertificateThumbprint(sp, &asymHeader->receiverCertificateThumbprint); /* The certificate in the header is verified via the configured PKI plugin * as certificateVerification.verifyCertificate(...). We cannot do it here * because the client/server context is needed. */ } UA_StatusCode checkSymHeader(UA_SecureChannel *channel, const UA_UInt32 tokenId) { /* If no match, try to revolve to the next token after a * RenewSecureChannel */ UA_StatusCode retval = UA_STATUSCODE_GOOD; UA_ChannelSecurityToken *token = &channel->securityToken; switch(channel->renewState) { case UA_SECURECHANNELRENEWSTATE_NORMAL: case UA_SECURECHANNELRENEWSTATE_SENT: default: break; case UA_SECURECHANNELRENEWSTATE_NEWTOKEN_SERVER: /* Old token still in use */ if(tokenId == channel->securityToken.tokenId) break; /* Not the new token */ UA_CHECK(tokenId == channel->altSecurityToken.tokenId, UA_LOG_WARNING_CHANNEL(channel->securityPolicy->logger, channel, "Unknown SecurityToken"); return UA_STATUSCODE_BADSECURECHANNELTOKENUNKNOWN); /* Roll over to the new token, generate new local and remote keys */ channel->renewState = UA_SECURECHANNELRENEWSTATE_NORMAL; channel->securityToken = channel->altSecurityToken; UA_ChannelSecurityToken_init(&channel->altSecurityToken); retval |= UA_SecureChannel_generateLocalKeys(channel); retval |= generateRemoteKeys(channel); UA_CHECK_STATUS(retval, return retval); break; case UA_SECURECHANNELRENEWSTATE_NEWTOKEN_CLIENT: /* The server is still using the old token. That's okay. */ if(tokenId == channel->altSecurityToken.tokenId) { token = &channel->altSecurityToken; break; } /* Not the new token */ UA_CHECK(tokenId == channel->securityToken.tokenId, UA_LOG_WARNING_CHANNEL(channel->securityPolicy->logger, channel, "Unknown SecurityToken"); return UA_STATUSCODE_BADSECURECHANNELTOKENUNKNOWN); /* The remote server uses the new token for the first time. Delete the * old token and roll the remote key over. The local key already uses * the nonce pair from the last OPN exchange. */ channel->renewState = UA_SECURECHANNELRENEWSTATE_NORMAL; UA_ChannelSecurityToken_init(&channel->altSecurityToken); retval = generateRemoteKeys(channel); UA_CHECK_STATUS(retval, return retval); } UA_DateTime timeout = token->createdAt + (token->revisedLifetime * UA_DATETIME_MSEC); if(channel->state == UA_SECURECHANNELSTATE_OPEN && timeout < UA_DateTime_nowMonotonic()) { UA_LOG_WARNING_CHANNEL(channel->securityPolicy->logger, channel, "SecurityToken timed out"); UA_SecureChannel_close(channel); return UA_STATUSCODE_BADSECURECHANNELCLOSED; } return UA_STATUSCODE_GOOD; } /**** amalgamated original file "/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 * Copyright 2019 (c) HMS Industrial Networks AB (Author: Jonas Green) */ #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); TAILQ_INIT(&session->subscriptions); #endif } void UA_Session_clear(UA_Session *session, UA_Server* server) { UA_LOCK_ASSERT(&server->serviceMutex, 1); /* Remove all Subscriptions. This may send out remaining publish * responses. */ #ifdef UA_ENABLE_SUBSCRIPTIONS UA_Subscription *sub, *tempsub; TAILQ_FOREACH_SAFE(sub, &session->subscriptions, sessionListEntry, tempsub) { UA_Subscription_delete(server, sub); } #endif #ifdef UA_ENABLE_DIAGNOSTICS deleteNode(server, session->sessionId, true); #endif UA_Session_detachFromSecureChannel(session); UA_ApplicationDescription_clear(&session->clientDescription); UA_NodeId_clear(&session->header.authenticationToken); UA_NodeId_clear(&session->sessionId); UA_String_clear(&session->sessionName); UA_ByteString_clear(&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; UA_Array_delete(session->params, session->paramsSize, &UA_TYPES[UA_TYPES_KEYVALUEPAIR]); session->params = NULL; session->paramsSize = 0; UA_Array_delete(session->localeIds, session->localeIdsSize, &UA_TYPES[UA_TYPES_STRING]); session->localeIds = NULL; session->localeIdsSize = 0; #ifdef UA_ENABLE_DIAGNOSTICS UA_SessionDiagnosticsDataType_clear(&session->diagnostics); UA_SessionSecurityDiagnosticsDataType_clear(&session->securityDiagnostics); #endif } void UA_Session_attachToSecureChannel(UA_Session *session, UA_SecureChannel *channel) { UA_Session_detachFromSecureChannel(session); session->header.channel = channel; session->header.serverSession = true; SLIST_INSERT_HEAD(&channel->sessions, &session->header, next); } void UA_Session_detachFromSecureChannel(UA_Session *session) { UA_SecureChannel *channel = session->header.channel; if(!channel) return; session->header.channel = NULL; UA_SessionHeader *sh; SLIST_FOREACH(sh, &channel->sessions, next) { if((UA_Session*)sh != session) continue; SLIST_REMOVE(&channel->sessions, sh, UA_SessionHeader, next); break; } /* Clean up the response queue. Their RequestId is bound to the * SecureChannel so they cannot be reused. */ #ifdef UA_ENABLE_SUBSCRIPTIONS UA_PublishResponseEntry *pre; while((pre = UA_Session_dequeuePublishReq(session))) { UA_PublishResponse_clear(&pre->response); UA_free(pre); } #endif } 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_clear(&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->policyContext, &session->serverNonce); } void UA_Session_updateLifetime(UA_Session *session) { session->validTill = UA_DateTime_nowMonotonic() + (UA_DateTime)(session->timeout * UA_DATETIME_MSEC); #ifdef UA_ENABLE_DIAGNOSTICS session->diagnostics.clientLastContactTime = UA_DateTime_now(); #endif } #ifdef UA_ENABLE_SUBSCRIPTIONS void UA_Session_attachSubscription(UA_Session *session, UA_Subscription *sub) { /* Attach to the session */ sub->session = session; /* Increase the count */ session->subscriptionsSize++; /* Increase the number of outstanding retransmissions */ session->totalRetransmissionQueueSize += sub->retransmissionQueueSize; /* Insert at the end of the subscriptions of the same priority / just before * the subscriptions with the next lower priority. */ UA_Subscription *after = NULL; TAILQ_FOREACH(after, &session->subscriptions, sessionListEntry) { if(after->priority < sub->priority) { TAILQ_INSERT_BEFORE(after, sub, sessionListEntry); return; } } TAILQ_INSERT_TAIL(&session->subscriptions, sub, sessionListEntry); } void UA_Session_detachSubscription(UA_Server *server, UA_Session *session, UA_Subscription *sub, UA_Boolean releasePublishResponses) { /* Detach from the session */ sub->session = NULL; TAILQ_REMOVE(&session->subscriptions, sub, sessionListEntry); /* Reduce the count */ UA_assert(session->subscriptionsSize > 0); session->subscriptionsSize--; /* Reduce the number of outstanding retransmissions */ session->totalRetransmissionQueueSize -= sub->retransmissionQueueSize; /* Send remaining publish responses if the last subscription was removed */ if(!releasePublishResponses || !TAILQ_EMPTY(&session->subscriptions)) return; 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(); sendResponse(server, session, session->header.channel, pre->requestId, (UA_Response*)response, &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]); UA_PublishResponse_clear(response); UA_free(pre); } } UA_Subscription * UA_Session_getSubscriptionById(UA_Session *session, UA_UInt32 subscriptionId) { UA_Subscription *sub; TAILQ_FOREACH(sub, &session->subscriptions, sessionListEntry) { /* Prevent lookup of subscriptions that are to be deleted with a statuschange */ if(sub->statusChange != UA_STATUSCODE_GOOD) continue; if(sub->subscriptionId == subscriptionId) break; } return sub; } UA_Subscription * UA_Server_getSubscriptionById(UA_Server *server, UA_UInt32 subscriptionId) { UA_Subscription *sub; LIST_FOREACH(sub, &server->subscriptions, serverListEntry) { /* Prevent lookup of subscriptions that are to be deleted with a statuschange */ if(sub->statusChange != UA_STATUSCODE_GOOD) continue; 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->responseQueueSize--; } 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->responseQueueSize++; } #endif /* Session Handling */ UA_StatusCode UA_Server_closeSession(UA_Server *server, const UA_NodeId *sessionId) { UA_LOCK(&server->serviceMutex); session_list_entry *entry; UA_StatusCode res = UA_STATUSCODE_BADSESSIONIDINVALID; LIST_FOREACH(entry, &server->sessions, pointers) { if(UA_NodeId_equal(&entry->session.sessionId, sessionId)) { UA_Server_removeSession(server, entry, UA_DIAGNOSTICEVENT_CLOSE); res = UA_STATUSCODE_GOOD; break; } } UA_UNLOCK(&server->serviceMutex); return res; } UA_StatusCode UA_Server_setSessionParameter(UA_Server *server, const UA_NodeId *sessionId, const char *name, const UA_Variant *parameter) { UA_LOCK(&server->serviceMutex); UA_Session *session = UA_Server_getSessionById(server, sessionId); UA_StatusCode res = UA_STATUSCODE_BADSESSIONIDINVALID; if(session) res = UA_KeyValueMap_set(&session->params, &session->paramsSize, name, parameter); UA_UNLOCK(&server->serviceMutex); return res; } void UA_Server_deleteSessionParameter(UA_Server *server, const UA_NodeId *sessionId, const char *name) { UA_LOCK(&server->serviceMutex); UA_Session *session = UA_Server_getSessionById(server, sessionId); if(session) UA_KeyValueMap_delete(&session->params, &session->paramsSize, name); UA_UNLOCK(&server->serviceMutex); } UA_StatusCode UA_Server_getSessionParameter(UA_Server *server, const UA_NodeId *sessionId, const char *name, UA_Variant *outParameter) { UA_LOCK(&server->serviceMutex); if(!outParameter) { UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_BADINTERNALERROR; } UA_Session *session = UA_Server_getSessionById(server, sessionId); if(!session) { UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_BADSESSIONIDINVALID; } const UA_Variant *param = UA_KeyValueMap_get(session->params, session->paramsSize, name); if(!param) { UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_BADNOTFOUND; } UA_StatusCode res = UA_Variant_copy(param, outParameter); UA_UNLOCK(&server->serviceMutex); return res; } UA_StatusCode UA_Server_getSessionScalarParameter(UA_Server *server, const UA_NodeId *sessionId, const char *name, const UA_DataType *type, UA_Variant *outParameter) { UA_LOCK(&server->serviceMutex); if(!outParameter) { UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_BADINTERNALERROR; } UA_Session *session = UA_Server_getSessionById(server, sessionId); if(!session) { UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_BADSESSIONIDINVALID; } const UA_Variant *param = UA_KeyValueMap_get(session->params, session->paramsSize, name); if(!param || !UA_Variant_hasScalarType(param, type)) { UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_BADNOTFOUND; } UA_StatusCode res = UA_Variant_copy(param, outParameter); UA_UNLOCK(&server->serviceMutex); return res; } UA_StatusCode UA_Server_getSessionArrayParameter(UA_Server *server, const UA_NodeId *sessionId, const char *name, const UA_DataType *type, UA_Variant *outParameter) { UA_LOCK(&server->serviceMutex); if(!outParameter) { UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_BADINTERNALERROR; } UA_Session *session = UA_Server_getSessionById(server, sessionId); if(!session) { UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_BADSESSIONIDINVALID; } const UA_Variant *param = UA_KeyValueMap_get(session->params, session->paramsSize, name); if(!param || !UA_Variant_hasArrayType(param, type)) { UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_BADNOTFOUND; } UA_StatusCode res = UA_Variant_copy(param, outParameter); UA_UNLOCK(&server->serviceMutex); return res; } /**** amalgamated original file "/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, 2021 (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 */ /*****************/ /* Node Pointers */ /*****************/ #define UA_NODEPOINTER_MASK 0x03 #define UA_NODEPOINTER_TAG_IMMEDIATE 0x00 #define UA_NODEPOINTER_TAG_NODEID 0x01 #define UA_NODEPOINTER_TAG_EXPANDEDNODEID 0x02 #define UA_NODEPOINTER_TAG_NODE 0x03 void UA_NodePointer_clear(UA_NodePointer *np) { switch(np->immediate & UA_NODEPOINTER_MASK) { case UA_NODEPOINTER_TAG_NODEID: np->immediate &= ~(uintptr_t)UA_NODEPOINTER_MASK; UA_NodeId_delete((UA_NodeId*)(uintptr_t)np->id); break; case UA_NODEPOINTER_TAG_EXPANDEDNODEID: np->immediate &= ~(uintptr_t)UA_NODEPOINTER_MASK; UA_ExpandedNodeId_delete((UA_ExpandedNodeId*)(uintptr_t) np->expandedId); break; default: break; } UA_NodePointer_init(np); } UA_StatusCode UA_NodePointer_copy(UA_NodePointer in, UA_NodePointer *out) { UA_StatusCode res = UA_STATUSCODE_GOOD; UA_Byte tag = in.immediate & UA_NODEPOINTER_MASK; in.immediate &= ~(uintptr_t)UA_NODEPOINTER_MASK; switch(tag) { case UA_NODEPOINTER_TAG_NODE: in.id = &in.node->nodeId; goto nodeid; /* fallthrough */ case UA_NODEPOINTER_TAG_NODEID: nodeid: out->id = UA_NodeId_new(); if(!out->id) return UA_STATUSCODE_BADOUTOFMEMORY; res = UA_NodeId_copy(in.id, (UA_NodeId*)(uintptr_t)out->id); if(res != UA_STATUSCODE_GOOD) { UA_free((void*)out->immediate); out->immediate = 0; break; } out->immediate |= UA_NODEPOINTER_TAG_NODEID; break; case UA_NODEPOINTER_TAG_EXPANDEDNODEID: out->expandedId = UA_ExpandedNodeId_new(); if(!out->expandedId) return UA_STATUSCODE_BADOUTOFMEMORY; res = UA_ExpandedNodeId_copy(in.expandedId, (UA_ExpandedNodeId*)(uintptr_t) out->expandedId); if(res != UA_STATUSCODE_GOOD) { UA_free((void*)out->immediate); out->immediate = 0; break; } out->immediate |= UA_NODEPOINTER_TAG_EXPANDEDNODEID; break; default: case UA_NODEPOINTER_TAG_IMMEDIATE: *out = in; break; } return res; } UA_Boolean UA_NodePointer_isLocal(UA_NodePointer np) { UA_Byte tag = np.immediate & UA_NODEPOINTER_MASK; return (tag != UA_NODEPOINTER_TAG_EXPANDEDNODEID); } UA_Order UA_NodePointer_order(UA_NodePointer p1, UA_NodePointer p2) { if(p1.immediate == p2.immediate) return UA_ORDER_EQ; /* Extract the tag and resolve pointers to nodes */ UA_Byte tag1 = p1.immediate & UA_NODEPOINTER_MASK; if(tag1 == UA_NODEPOINTER_TAG_NODE) { p1 = UA_NodePointer_fromNodeId(&p1.node->nodeId); tag1 = p1.immediate & UA_NODEPOINTER_MASK; } UA_Byte tag2 = p2.immediate & UA_NODEPOINTER_MASK; if(tag2 == UA_NODEPOINTER_TAG_NODE) { p2 = UA_NodePointer_fromNodeId(&p2.node->nodeId); tag2 = p2.immediate & UA_NODEPOINTER_MASK; } /* Different tags, cannot be identical */ if(tag1 != tag2) return (tag1 > tag2) ? UA_ORDER_MORE : UA_ORDER_LESS; /* Immediate */ if(UA_LIKELY(tag1 == UA_NODEPOINTER_TAG_IMMEDIATE)) return (p1.immediate > p2.immediate) ? UA_ORDER_MORE : UA_ORDER_LESS; /* Compare from pointers */ p1.immediate &= ~(uintptr_t)UA_NODEPOINTER_MASK; p2.immediate &= ~(uintptr_t)UA_NODEPOINTER_MASK; if(tag1 == UA_NODEPOINTER_TAG_EXPANDEDNODEID) return UA_ExpandedNodeId_order(p1.expandedId, p2.expandedId); return UA_NodeId_order(p1.id, p2.id); } UA_NodePointer UA_NodePointer_fromNodeId(const UA_NodeId *id) { UA_NodePointer np; if(id->identifierType != UA_NODEIDTYPE_NUMERIC) { np.id = id; np.immediate |= UA_NODEPOINTER_TAG_NODEID; return np; } #if SIZE_MAX > UA_UINT32_MAX /* 64bit: 4 Byte for the numeric identifier + 2 Byte for the namespaceIndex * + 1 Byte for the tagging bit (zero) */ np.immediate = ((uintptr_t)id->identifier.numeric) << 32; np.immediate |= ((uintptr_t)id->namespaceIndex) << 8; #else /* 32bit: 3 Byte for the numeric identifier + 6 Bit for the namespaceIndex * + 2 Bit for the tagging bit (zero) */ if(id->namespaceIndex < (0x01 << 6) && id->identifier.numeric < (0x01 << 24)) { np.immediate = ((uintptr_t)id->identifier.numeric) << 8; np.immediate |= ((uintptr_t)id->namespaceIndex) << 2; } else { np.id = id; np.immediate |= UA_NODEPOINTER_TAG_NODEID; } #endif return np; } UA_NodeId UA_NodePointer_toNodeId(UA_NodePointer np) { UA_Byte tag = np.immediate & UA_NODEPOINTER_MASK; np.immediate &= ~(uintptr_t)UA_NODEPOINTER_MASK; switch(tag) { case UA_NODEPOINTER_TAG_NODE: return np.node->nodeId; case UA_NODEPOINTER_TAG_NODEID: return *np.id; case UA_NODEPOINTER_TAG_EXPANDEDNODEID: return np.expandedId->nodeId; default: case UA_NODEPOINTER_TAG_IMMEDIATE: break; } UA_NodeId id; id.identifierType = UA_NODEIDTYPE_NUMERIC; #if SIZE_MAX > UA_UINT32_MAX /* 64bit */ id.namespaceIndex = (UA_UInt16)(np.immediate >> 8); id.identifier.numeric = (UA_UInt32)(np.immediate >> 32); #else /* 32bit */ id.namespaceIndex = ((UA_Byte)np.immediate) >> 2; id.identifier.numeric = np.immediate >> 8; #endif return id; } UA_NodePointer UA_NodePointer_fromExpandedNodeId(const UA_ExpandedNodeId *id) { if(!UA_ExpandedNodeId_isLocal(id)) { UA_NodePointer np; np.expandedId = id; np.immediate |= UA_NODEPOINTER_TAG_EXPANDEDNODEID; return np; } return UA_NodePointer_fromNodeId(&id->nodeId); } UA_ExpandedNodeId UA_NodePointer_toExpandedNodeId(UA_NodePointer np) { /* Resolve node pointer to get the NodeId */ UA_Byte tag = np.immediate & UA_NODEPOINTER_MASK; if(tag == UA_NODEPOINTER_TAG_NODE) { np = UA_NodePointer_fromNodeId(&np.node->nodeId); tag = np.immediate & UA_NODEPOINTER_MASK; } /* ExpandedNodeId, make a shallow copy */ if(tag == UA_NODEPOINTER_TAG_EXPANDEDNODEID) { np.immediate &= ~(uintptr_t)UA_NODEPOINTER_MASK; return *np.expandedId; } /* NodeId, either immediate or via a pointer */ UA_ExpandedNodeId en; UA_ExpandedNodeId_init(&en); en.nodeId = UA_NodePointer_toNodeId(np); return en; } /**************/ /* References */ /**************/ static UA_StatusCode addReferenceTarget(UA_NodeReferenceKind *refs, UA_NodePointer target, UA_UInt32 targetNameHash); static enum aa_cmp cmpRefTargetId(const void *a, const void *b) { const UA_ReferenceTargetTreeElem *aa = (const UA_ReferenceTargetTreeElem*)a; const UA_ReferenceTargetTreeElem *bb = (const UA_ReferenceTargetTreeElem*)b; if(aa->targetIdHash < bb->targetIdHash) return AA_CMP_LESS; if(aa->targetIdHash > bb->targetIdHash) return AA_CMP_MORE; return (enum aa_cmp)UA_NodePointer_order(aa->target.targetId, bb->target.targetId); } static enum aa_cmp cmpRefTargetName(const void *a, const void *b) { const UA_UInt32 *nameHashA = (const UA_UInt32*)a; const UA_UInt32 *nameHashB = (const UA_UInt32*)b; if(*nameHashA < *nameHashB) return AA_CMP_LESS; if(*nameHashA > *nameHashB) return AA_CMP_MORE; return AA_CMP_EQ; } /* Reusable binary search tree "heads". Just switch out the root pointer. */ static const struct aa_head refIdTree = { NULL, cmpRefTargetId, offsetof(UA_ReferenceTargetTreeElem, idTreeEntry), 0 }; const struct aa_head refNameTree = { NULL, cmpRefTargetName, offsetof(UA_ReferenceTargetTreeElem, nameTreeEntry), offsetof(UA_ReferenceTarget, targetNameHash) }; const UA_ReferenceTarget * UA_NodeReferenceKind_iterate(const UA_NodeReferenceKind *rk, const UA_ReferenceTarget *prev) { /* Return from the tree */ if(rk->hasRefTree) { const struct aa_head _refIdTree = { rk->targets.tree.idTreeRoot, cmpRefTargetId, offsetof(UA_ReferenceTargetTreeElem, idTreeEntry), 0 }; if(prev == NULL) return (const UA_ReferenceTarget*)aa_min(&_refIdTree); return (const UA_ReferenceTarget*)aa_next(&_refIdTree, prev); } if(prev == NULL) /* Return start of the array */ return rk->targets.array; if(prev + 1 >= &rk->targets.array[rk->targetsSize]) return NULL; /* End of the array */ return prev + 1; /* Next element in the array */ } /* Also deletes the elements of the tree */ static void moveTreeToArray(UA_ReferenceTarget *array, size_t *pos, struct aa_entry *entry) { if(!entry) return; UA_ReferenceTargetTreeElem *elem = (UA_ReferenceTargetTreeElem*) ((uintptr_t)entry - offsetof(UA_ReferenceTargetTreeElem, idTreeEntry)); moveTreeToArray(array, pos, elem->idTreeEntry.left); moveTreeToArray(array, pos, elem->idTreeEntry.right); array[*pos] = elem->target; (*pos)++; UA_free(elem); } UA_StatusCode UA_NodeReferenceKind_switch(UA_NodeReferenceKind *rk) { if(rk->hasRefTree) { /* From tree to array */ UA_ReferenceTarget *array = (UA_ReferenceTarget*) UA_malloc(sizeof(UA_ReferenceTarget) * rk->targetsSize); if(!array) return UA_STATUSCODE_BADOUTOFMEMORY; size_t pos = 0; moveTreeToArray(array, &pos, rk->targets.tree.idTreeRoot); rk->targets.array = array; rk->hasRefTree = false; return UA_STATUSCODE_GOOD; } /* From array to tree */ UA_NodeReferenceKind newRk = *rk; newRk.hasRefTree = true; newRk.targets.tree.idTreeRoot = NULL; newRk.targets.tree.nameTreeRoot = NULL; for(size_t i = 0; i < rk->targetsSize; i++) { UA_StatusCode res = addReferenceTarget(&newRk, rk->targets.array[i].targetId, rk->targets.array[i].targetNameHash); if(res != UA_STATUSCODE_GOOD) { struct aa_head _refIdTree = refIdTree; _refIdTree.root = newRk.targets.tree.idTreeRoot; while(_refIdTree.root) { UA_ReferenceTargetTreeElem *elem = (UA_ReferenceTargetTreeElem*) ((uintptr_t)_refIdTree.root - offsetof(UA_ReferenceTargetTreeElem, idTreeEntry)); aa_remove(&_refIdTree, elem); UA_NodePointer_clear(&elem->target.targetId); UA_free(elem); } return res; } } for(size_t i = 0; i < rk->targetsSize; i++) UA_NodePointer_clear(&rk->targets.array[i].targetId); UA_free(rk->targets.array); *rk = newRk; return UA_STATUSCODE_GOOD; } const UA_ReferenceTarget * UA_NodeReferenceKind_findTarget(const UA_NodeReferenceKind *rk, const UA_ExpandedNodeId *targetId) { UA_NodePointer targetP = UA_NodePointer_fromExpandedNodeId(targetId); /* Return from the tree */ if(rk->hasRefTree) { UA_ReferenceTargetTreeElem tmpTarget; tmpTarget.target.targetId = targetP; tmpTarget.targetIdHash = UA_ExpandedNodeId_hash(targetId); const struct aa_head _refIdTree = { rk->targets.tree.idTreeRoot, cmpRefTargetId, offsetof(UA_ReferenceTargetTreeElem, idTreeEntry), 0 }; return (const UA_ReferenceTarget*)aa_find(&_refIdTree, &tmpTarget); } /* Return from the array */ for(size_t i = 0; i < rk->targetsSize; i++) { if(UA_NodePointer_equal(targetP, rk->targets.array[i].targetId)) return &rk->targets.array[i]; } return NULL; } const UA_Node * UA_NODESTORE_GETFROMREF(UA_Server *server, UA_NodePointer target) { if(!UA_NodePointer_isLocal(target)) return NULL; UA_NodeId id = UA_NodePointer_toNodeId(target); return UA_NODESTORE_GET(server, &id); } /* General node handling methods. There is no UA_Node_new() method here. * Creating nodes is part of the Nodestore layer */ void UA_Node_clear(UA_Node *node) { /* Delete references */ UA_Node_deleteReferences(node); /* Delete other head content */ UA_NodeHead *head = &node->head; UA_NodeId_clear(&head->nodeId); UA_QualifiedName_clear(&head->browseName); UA_LocalizedText_clear(&head->displayName); UA_LocalizedText_clear(&head->description); /* Delete unique content of the nodeclass */ switch(head->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 = &node->variableNode; UA_NodeId_clear(&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_clear(&p->value.data.value); break; } case UA_NODECLASS_REFERENCETYPE: { UA_ReferenceTypeNode *p = &node->referenceTypeNode; UA_LocalizedText_clear(&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) { dst->accessLevel = src->accessLevel; dst->minimumSamplingInterval = src->minimumSamplingInterval; dst->historizing = src->historizing; dst->isDynamic = src->isDynamic; return UA_CommonVariableNode_copy(src, dst); } static UA_StatusCode UA_VariableTypeNode_copy(const UA_VariableTypeNode *src, UA_VariableTypeNode *dst) { dst->isAbstract = src->isAbstract; return UA_CommonVariableNode_copy((const UA_VariableNode*)src, (UA_VariableNode*)dst); } static UA_StatusCode UA_MethodNode_copy(const UA_MethodNode *src, UA_MethodNode *dst) { dst->executable = src->executable; dst->method = src->method; #if UA_MULTITHREADING >= 100 dst->async = src->async; #endif 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) { dst->isAbstract = src->isAbstract; dst->symmetric = src->symmetric; dst->referenceTypeIndex = src->referenceTypeIndex; dst->subTypes = src->subTypes; return UA_LocalizedText_copy(&src->inverseName, &dst->inverseName); } 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) { const UA_NodeHead *srchead = &src->head; UA_NodeHead *dsthead = &dst->head; if(srchead->nodeClass != dsthead->nodeClass) return UA_STATUSCODE_BADINTERNALERROR; /* Copy standard content */ UA_StatusCode retval = UA_NodeId_copy(&srchead->nodeId, &dsthead->nodeId); retval |= UA_QualifiedName_copy(&srchead->browseName, &dsthead->browseName); retval |= UA_LocalizedText_copy(&srchead->displayName, &dsthead->displayName); retval |= UA_LocalizedText_copy(&srchead->description, &dsthead->description); dsthead->writeMask = srchead->writeMask; dsthead->context = srchead->context; dsthead->constructed = srchead->constructed; #ifdef UA_ENABLE_SUBSCRIPTIONS dsthead->monitoredItems = srchead->monitoredItems; #endif if(retval != UA_STATUSCODE_GOOD) { UA_Node_clear(dst); return retval; } /* Copy the references */ dsthead->references = NULL; if(srchead->referencesSize > 0) { dsthead->references = (UA_NodeReferenceKind*) UA_calloc(srchead->referencesSize, sizeof(UA_NodeReferenceKind)); if(!dsthead->references) { UA_Node_clear(dst); return UA_STATUSCODE_BADOUTOFMEMORY; } dsthead->referencesSize = srchead->referencesSize; for(size_t i = 0; i < srchead->referencesSize; ++i) { UA_NodeReferenceKind *srefs = &srchead->references[i]; UA_NodeReferenceKind *drefs = &dsthead->references[i]; drefs->referenceTypeIndex = srefs->referenceTypeIndex; drefs->isInverse = srefs->isInverse; drefs->hasRefTree = srefs->hasRefTree; /* initially empty */ /* Copy all the targets */ const UA_ReferenceTarget *t = NULL; while((t = UA_NodeReferenceKind_iterate(srefs, t))) { retval = addReferenceTarget(drefs, t->targetId, t->targetNameHash); if(retval != UA_STATUSCODE_GOOD) { UA_Node_clear(dst); return retval; } } } } /* Copy unique content of the nodeclass */ switch(src->head.nodeClass) { case UA_NODECLASS_OBJECT: retval = UA_ObjectNode_copy(&src->objectNode, &dst->objectNode); break; case UA_NODECLASS_VARIABLE: retval = UA_VariableNode_copy(&src->variableNode, &dst->variableNode); break; case UA_NODECLASS_METHOD: retval = UA_MethodNode_copy(&src->methodNode, &dst->methodNode); break; case UA_NODECLASS_OBJECTTYPE: retval = UA_ObjectTypeNode_copy(&src->objectTypeNode, &dst->objectTypeNode); break; case UA_NODECLASS_VARIABLETYPE: retval = UA_VariableTypeNode_copy(&src->variableTypeNode, &dst->variableTypeNode); break; case UA_NODECLASS_REFERENCETYPE: retval = UA_ReferenceTypeNode_copy(&src->referenceTypeNode, &dst->referenceTypeNode); break; case UA_NODECLASS_DATATYPE: retval = UA_DataTypeNode_copy(&src->dataTypeNode, &dst->dataTypeNode); break; case UA_NODECLASS_VIEW: retval = UA_ViewNode_copy(&src->viewNode, &dst->viewNode); break; default: break; } if(retval != UA_STATUSCODE_GOOD) UA_Node_clear(dst); return retval; } UA_Node * UA_Node_copy_alloc(const UA_Node *src) { size_t nodesize = 0; switch(src->head.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->head.nodeClass = src->head.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_NodeHead *head, const UA_NodeAttributes *attr) { /* UA_NodeId_copy(&item->requestedNewNodeId.nodeId, &node->nodeId); */ /* UA_QualifiedName_copy(&item->browseName, &node->browseName); */ head->writeMask = attr->writeMask; UA_StatusCode retval = UA_LocalizedText_copy(&attr->description, &head->description); /* The new nodeset format has optional display names: * https://github.com/open62541/open62541/issues/2627. If the display name * is NULL, take the name part of the browse name */ if(attr->displayName.text.length == 0) retval |= UA_String_copy(&head->browseName.name, &head->displayName.text); else retval |= UA_LocalizedText_copy(&attr->displayName, &head->displayName); 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->head.nodeClass) { case UA_NODECLASS_OBJECT: CHECK_ATTRIBUTES(OBJECTATTRIBUTES); retval = copyObjectNodeAttributes(&node->objectNode, (const UA_ObjectAttributes*)attributes); break; case UA_NODECLASS_VARIABLE: CHECK_ATTRIBUTES(VARIABLEATTRIBUTES); retval = copyVariableNodeAttributes(&node->variableNode, (const UA_VariableAttributes*)attributes); break; case UA_NODECLASS_OBJECTTYPE: CHECK_ATTRIBUTES(OBJECTTYPEATTRIBUTES); retval = copyObjectTypeNodeAttributes(&node->objectTypeNode, (const UA_ObjectTypeAttributes*)attributes); break; case UA_NODECLASS_VARIABLETYPE: CHECK_ATTRIBUTES(VARIABLETYPEATTRIBUTES); retval = copyVariableTypeNodeAttributes(&node->variableTypeNode, (const UA_VariableTypeAttributes*)attributes); break; case UA_NODECLASS_REFERENCETYPE: CHECK_ATTRIBUTES(REFERENCETYPEATTRIBUTES); retval = copyReferenceTypeNodeAttributes(&node->referenceTypeNode, (const UA_ReferenceTypeAttributes*)attributes); break; case UA_NODECLASS_DATATYPE: CHECK_ATTRIBUTES(DATATYPEATTRIBUTES); retval = copyDataTypeNodeAttributes(&node->dataTypeNode, (const UA_DataTypeAttributes*)attributes); break; case UA_NODECLASS_VIEW: CHECK_ATTRIBUTES(VIEWATTRIBUTES); retval = copyViewNodeAttributes(&node->viewNode, (const UA_ViewAttributes*)attributes); break; case UA_NODECLASS_METHOD: CHECK_ATTRIBUTES(METHODATTRIBUTES); retval = copyMethodNodeAttributes(&node->methodNode, (const UA_MethodAttributes*)attributes); break; case UA_NODECLASS_UNSPECIFIED: default: retval = UA_STATUSCODE_BADNODECLASSINVALID; } if(retval == UA_STATUSCODE_GOOD) retval = copyStandardAttributes(&node->head, (const UA_NodeAttributes*)attributes); if(retval != UA_STATUSCODE_GOOD) UA_Node_clear(node); return retval; } /*********************/ /* Manage References */ /*********************/ static UA_StatusCode addReferenceTarget(UA_NodeReferenceKind *rk, UA_NodePointer targetId, UA_UInt32 targetNameHash) { /* Insert into array */ if(!rk->hasRefTree) { UA_ReferenceTarget *newRefs = (UA_ReferenceTarget*) UA_realloc(rk->targets.array, sizeof(UA_ReferenceTarget) * (rk->targetsSize + 1)); if(!newRefs) return UA_STATUSCODE_BADOUTOFMEMORY; rk->targets.array = newRefs; UA_StatusCode retval = UA_NodePointer_copy(targetId, &rk->targets.array[rk->targetsSize].targetId); rk->targets.array[rk->targetsSize].targetNameHash = targetNameHash; if(retval != UA_STATUSCODE_GOOD) { if(rk->targetsSize == 0) { UA_free(rk->targets.array); rk->targets.array = NULL; } return retval; } rk->targetsSize++; return UA_STATUSCODE_GOOD; } /* Insert into tree */ UA_ReferenceTargetTreeElem *entry = (UA_ReferenceTargetTreeElem*) UA_malloc(sizeof(UA_ReferenceTargetTreeElem)); if(!entry) return UA_STATUSCODE_BADOUTOFMEMORY; UA_StatusCode retval = UA_NodePointer_copy(targetId, &entry->target.targetId); if(retval != UA_STATUSCODE_GOOD) { UA_free(entry); return retval; } /* <-- The point of no return --> */ UA_ExpandedNodeId en = UA_NodePointer_toExpandedNodeId(targetId); entry->targetIdHash = UA_ExpandedNodeId_hash(&en); entry->target.targetNameHash = targetNameHash; /* Insert to the id lookup binary search tree. Only the root is kept in refs * to save space. */ struct aa_head _refIdTree = refIdTree; _refIdTree.root = rk->targets.tree.idTreeRoot; aa_insert(&_refIdTree, entry); rk->targets.tree.idTreeRoot = _refIdTree.root; /* Insert to the name lookup binary search tree */ struct aa_head _refNameTree = refNameTree; _refNameTree.root = rk->targets.tree.nameTreeRoot; aa_insert(&_refNameTree, entry); rk->targets.tree.nameTreeRoot = _refNameTree.root; rk->targetsSize++; return UA_STATUSCODE_GOOD; } static UA_StatusCode addReferenceKind(UA_NodeHead *head, UA_Byte refTypeIndex, UA_Boolean isForward, const UA_NodePointer target, UA_UInt32 targetBrowseNameHash) { UA_NodeReferenceKind *refs = (UA_NodeReferenceKind*) UA_realloc(head->references, sizeof(UA_NodeReferenceKind) * (head->referencesSize+1)); if(!refs) return UA_STATUSCODE_BADOUTOFMEMORY; head->references = refs; UA_NodeReferenceKind *newRef = &refs[head->referencesSize]; memset(newRef, 0, sizeof(UA_NodeReferenceKind)); newRef->referenceTypeIndex = refTypeIndex; newRef->isInverse = !isForward; UA_StatusCode retval = addReferenceTarget(newRef, target, targetBrowseNameHash); if(retval != UA_STATUSCODE_GOOD) { if(head->referencesSize == 0) { UA_free(head->references); head->references = NULL; } return retval; } head->referencesSize++; return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Node_addReference(UA_Node *node, UA_Byte refTypeIndex, UA_Boolean isForward, const UA_ExpandedNodeId *targetNodeId, UA_UInt32 targetBrowseNameHash) { /* Find the matching reference kind */ for(size_t i = 0; i < node->head.referencesSize; ++i) { UA_NodeReferenceKind *refs = &node->head.references[i]; /* Reference direction does not match */ if(refs->isInverse == isForward) continue; /* Reference type does not match */ if(refs->referenceTypeIndex != refTypeIndex) continue; /* Does an identical reference already exist? */ const UA_ReferenceTarget *found = UA_NodeReferenceKind_findTarget(refs, targetNodeId); if(found) return UA_STATUSCODE_BADDUPLICATEREFERENCENOTALLOWED; /* Add to existing ReferenceKind */ return addReferenceTarget(refs, UA_NodePointer_fromExpandedNodeId(targetNodeId), targetBrowseNameHash); } /* Add new ReferenceKind for the target */ return addReferenceKind(&node->head, refTypeIndex, isForward, UA_NodePointer_fromExpandedNodeId(targetNodeId), targetBrowseNameHash); } UA_StatusCode UA_Node_deleteReference(UA_Node *node, UA_Byte refTypeIndex, UA_Boolean isForward, const UA_ExpandedNodeId *targetNodeId) { struct aa_head _refIdTree = refIdTree; struct aa_head _refNameTree = refNameTree; UA_NodeHead *head = &node->head; for(size_t i = 0; i < head->referencesSize; i++) { UA_NodeReferenceKind *refs = &head->references[i]; if(isForward == refs->isInverse) continue; if(refTypeIndex != refs->referenceTypeIndex) continue; /* Cast out the const qualifier (hack!) */ UA_ReferenceTarget *target = (UA_ReferenceTarget*)(uintptr_t) UA_NodeReferenceKind_findTarget(refs, targetNodeId); if(!target) continue; /* Ok, delete the reference. Cannot fail */ refs->targetsSize--; if(!refs->hasRefTree) { /* Remove from array */ UA_NodePointer_clear(&target->targetId); /* Elements remaining. Realloc. */ if(refs->targetsSize > 0) { if(target != &refs->targets.array[refs->targetsSize]) *target = refs->targets.array[refs->targetsSize]; UA_ReferenceTarget *newRefs = (UA_ReferenceTarget*) UA_realloc(refs->targets.array, sizeof(UA_ReferenceTarget) * refs->targetsSize); if(newRefs) refs->targets.array = newRefs; return UA_STATUSCODE_GOOD; /* Realloc allowed to fail */ } /* Remove the last target. Remove the ReferenceKind below */ UA_free(refs->targets.array); } else { /* Remove from the tree */ _refIdTree.root = refs->targets.tree.idTreeRoot; aa_remove(&_refIdTree, target); refs->targets.tree.idTreeRoot = _refIdTree.root; _refNameTree.root = refs->targets.tree.nameTreeRoot; aa_remove(&_refNameTree, target); refs->targets.tree.nameTreeRoot = _refNameTree.root; UA_NodePointer_clear(&target->targetId); UA_free(target); if(refs->targets.tree.idTreeRoot) return UA_STATUSCODE_GOOD; /* At least one target remains */ } /* No targets remaining. Remove the ReferenceKind. */ head->referencesSize--; if(head->referencesSize > 0) { /* No target for the ReferenceType remaining. Remove and shrink down * allocated buffer. Ignore errors in case memory buffer could not * be shrinked down. */ if(i != head->referencesSize) head->references[i] = head->references[node->head.referencesSize]; UA_NodeReferenceKind *newRefs = (UA_NodeReferenceKind*) UA_realloc(head->references, sizeof(UA_NodeReferenceKind) * head->referencesSize); if(newRefs) head->references = newRefs; } else { /* No remaining references of any ReferenceType */ UA_free(head->references); head->references = NULL; } return UA_STATUSCODE_GOOD; } return UA_STATUSCODE_UNCERTAINREFERENCENOTDELETED; } void UA_Node_deleteReferencesSubset(UA_Node *node, const UA_ReferenceTypeSet *keepSet) { UA_NodeHead *head = &node->head; struct aa_head _refIdTree = refIdTree; for(size_t i = 0; i < head->referencesSize; i++) { /* Keep the references of this type? */ UA_NodeReferenceKind *refs = &head->references[i]; if(UA_ReferenceTypeSet_contains(keepSet, refs->referenceTypeIndex)) continue; /* Remove all target entries. Don't remove entries from browseName tree. * The entire ReferenceKind will be removed anyway. */ if(!refs->hasRefTree) { for(size_t j = 0; j < refs->targetsSize; j++) UA_NodePointer_clear(&refs->targets.array[j].targetId); UA_free(refs->targets.array); } else { _refIdTree.root = refs->targets.tree.idTreeRoot; while(_refIdTree.root) { UA_ReferenceTargetTreeElem *elem = (UA_ReferenceTargetTreeElem*) ((uintptr_t)_refIdTree.root - offsetof(UA_ReferenceTargetTreeElem, idTreeEntry)); aa_remove(&_refIdTree, elem); UA_NodePointer_clear(&elem->target.targetId); UA_free(elem); } } /* Move last references-kind entry to this position. Don't memcpy over * the same position. Decrease i to repeat at this location. */ head->referencesSize--; if(i != head->referencesSize) { head->references[i] = head->references[head->referencesSize]; i--; } } if(head->referencesSize > 0) { /* Realloc to save memory. Ignore if realloc fails. */ UA_NodeReferenceKind *refs = (UA_NodeReferenceKind*) UA_realloc(head->references, sizeof(UA_NodeReferenceKind) * head->referencesSize); if(refs) head->references = refs; } else { /* The array is empty. Remove. */ UA_free(head->references); head->references = NULL; } } void UA_Node_deleteReferences(UA_Node *node) { UA_ReferenceTypeSet noRefs; UA_ReferenceTypeSet_init(&noRefs); UA_Node_deleteReferencesSubset(node, &noRefs); } /**** amalgamated original file "/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 * Copyright 2021 (c) Fraunhofer IOSB (Author: Jan Hermes) * Copyright 2022 (c) Fraunhofer IOSB (Author: Andreas Ebner) */ #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL #endif #ifdef UA_ENABLE_SUBSCRIPTIONS #endif #ifdef UA_ENABLE_VALGRIND_INTERACTIVE #include #endif #define STARTCHANNELID 1 #define STARTTOKENID 1 /**********************/ /* 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, or UA_Server_getNamespaceByIndex * 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)); UA_CHECK_MEM(newNS, return 0); server->namespaces = newNS; /* Copy the namespace string */ UA_StatusCode retval = UA_String_copy(&name, &server->namespaces[server->namespacesSize]); UA_CHECK_STATUS(retval, 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; UA_LOCK(&server->serviceMutex); UA_UInt16 retVal = addNamespace(server, nameString); UA_UNLOCK(&server->serviceMutex); return retVal; } UA_ServerConfig* UA_Server_getConfig(UA_Server *server) { UA_CHECK_MEM(server, return NULL); return &server->config; } UA_StatusCode 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); UA_StatusCode res = UA_STATUSCODE_BADNOTFOUND; for(size_t idx = 0; idx < server->namespacesSize; idx++) { if(UA_String_equal(&server->namespaces[idx], &namespaceUri)) { (*foundIndex) = idx; res = UA_STATUSCODE_GOOD; break; } } return res; } UA_StatusCode getNamespaceByIndex(UA_Server *server, const size_t namespaceIndex, UA_String *foundUri) { /* ensure that the uri for ns1 is set up from the app description */ setupNs1Uri(server); UA_StatusCode res = UA_STATUSCODE_BADNOTFOUND; if(namespaceIndex >= server->namespacesSize) return res; res = UA_String_copy(&server->namespaces[namespaceIndex], foundUri); return res; } UA_StatusCode UA_Server_getNamespaceByName(UA_Server *server, const UA_String namespaceUri, size_t *foundIndex) { UA_LOCK(&server->serviceMutex); UA_StatusCode res = getNamespaceByName(server, namespaceUri, foundIndex); UA_UNLOCK(&server->serviceMutex); return res; } UA_StatusCode UA_Server_getNamespaceByIndex(UA_Server *server, const size_t namespaceIndex, UA_String *foundUri) { UA_LOCK(&server->serviceMutex); UA_StatusCode res = getNamespaceByIndex(server, namespaceIndex, foundUri); UA_UNLOCK(&server->serviceMutex); return res; } UA_StatusCode UA_Server_forEachChildNodeCall(UA_Server *server, UA_NodeId parentNodeId, UA_NodeIteratorCallback callback, void *handle) { UA_BrowseDescription bd; UA_BrowseDescription_init(&bd); bd.nodeId = parentNodeId; bd.browseDirection = UA_BROWSEDIRECTION_BOTH; bd.resultMask = UA_BROWSERESULTMASK_REFERENCETYPEID | UA_BROWSERESULTMASK_ISFORWARD; UA_BrowseResult br = UA_Server_browse(server, 0, &bd); UA_StatusCode res = br.statusCode; UA_CHECK_STATUS(res, goto cleanup); for(size_t i = 0; i < br.referencesSize; i++) { if(!UA_ExpandedNodeId_isLocal(&br.references[i].nodeId)) continue; res = callback(br.references[i].nodeId.nodeId, !br.references[i].isForward, br.references[i].referenceTypeId, handle); UA_CHECK_STATUS(res, goto cleanup); } cleanup: UA_BrowseResult_clear(&br); return res; } /********************/ /* Server Lifecycle */ /********************/ static void serverExecuteRepeatedCallback(UA_Server *server, UA_ApplicationCallback cb, void *callbackApplication, void *data); /* The server needs to be stopped before it can be deleted */ void UA_Server_delete(UA_Server *server) { UA_LOCK(&server->serviceMutex); UA_Server_deleteSecureChannels(server); session_list_entry *current, *temp; LIST_FOREACH_SAFE(current, &server->sessions, pointers, temp) { UA_Server_removeSession(server, current, UA_DIAGNOSTICEVENT_CLOSE); } 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); } /* Remove subscriptions without a session */ UA_Subscription *sub, *sub_tmp; LIST_FOREACH_SAFE(sub, &server->subscriptions, serverListEntry, sub_tmp) { UA_Subscription_delete(server, sub); } UA_assert(server->monitoredItemsSize == 0); UA_assert(server->subscriptionsSize == 0); #ifdef UA_ENABLE_SUBSCRIPTIONS_ALARMS_CONDITIONS UA_ConditionList_delete(server); #endif #endif #ifdef UA_ENABLE_PUBSUB UA_PubSubManager_delete(server, &server->pubSubManager); #endif #ifdef UA_ENABLE_DISCOVERY UA_DiscoveryManager_clear(&server->discoveryManager, server); #endif #if UA_MULTITHREADING >= 100 UA_AsyncManager_clear(&server->asyncManager, server); #endif /* Clean up the Admin Session */ UA_Session_clear(&server->adminSession, server); UA_UNLOCK(&server->serviceMutex); /* The timer has its own mutex */ /* Execute all remaining delayed events and clean up the timer */ UA_Timer_process(&server->timer, UA_DateTime_nowMonotonic() + 1, (UA_TimerExecutionCallback)serverExecuteRepeatedCallback, server); UA_Timer_clear(&server->timer); /* Clean up the config */ UA_ServerConfig_clean(&server->config); #if UA_MULTITHREADING >= 100 UA_LOCK_DESTROY(&server->networkMutex); UA_LOCK_DESTROY(&server->serviceMutex); #endif /* 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_LOCK(&server->serviceMutex); UA_DateTime nowMonotonic = UA_DateTime_nowMonotonic(); UA_Server_cleanupSessions(server, nowMonotonic); UA_Server_cleanupTimedOutSecureChannels(server, nowMonotonic); #ifdef UA_ENABLE_DISCOVERY UA_Discovery_cleanupTimedOut(server, nowMonotonic); #endif UA_UNLOCK(&server->serviceMutex); } /********************/ /* Server Lifecycle */ /********************/ static UA_INLINE UA_Boolean UA_Server_NodestoreIsConfigured(UA_Server *server) { return server->config.nodestore.getNode != NULL; } static UA_Server * UA_Server_init(UA_Server *server) { UA_StatusCode res = UA_STATUSCODE_GOOD; UA_CHECK_FATAL(UA_Server_NodestoreIsConfigured(server), goto cleanup, &server->config.logger, UA_LOGCATEGORY_SERVER, "No Nodestore configured in the 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 #if UA_MULTITHREADING >= 100 UA_LOCK_INIT(&server->networkMutex); UA_LOCK_INIT(&server->serviceMutex); #endif /* Initialize the handling of repeated callbacks */ UA_Timer_init(&server->timer); /* 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; server->adminSession.sessionName = UA_STRING_ALLOC("Administrator"); /* 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]); UA_CHECK_MEM(server->namespaces, goto cleanup); server->namespaces[0] = UA_STRING_ALLOC("http://opcfoundation.org/UA/"); server->namespaces[1] = UA_STRING_NULL; server->namespacesSize = 2; /* Initialize SecureChannel */ TAILQ_INIT(&server->channels); /* TODO: use an ID that is likely to be unique after a restart */ server->lastChannelId = STARTCHANNELID; server->lastTokenId = STARTTOKENID; /* Initialize Session Management */ LIST_INIT(&server->sessions); server->sessionCount = 0; #if UA_MULTITHREADING >= 100 UA_AsyncManager_init(&server->asyncManager, server); #endif /* Initialized discovery */ #ifdef UA_ENABLE_DISCOVERY UA_DiscoveryManager_init(&server->discoveryManager, server); #endif /* 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*/ res = UA_Server_initNS0(server); UA_CHECK_STATUS(res, goto cleanup); #ifdef UA_ENABLE_PUBSUB /* Build PubSub information model */ #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL UA_Server_initPubSubNS0(server); #endif #ifdef UA_ENABLE_PUBSUB_MONITORING /* setup default PubSub monitoring callbacks */ res = UA_PubSubManager_setDefaultMonitoringCallbacks(&server->config.pubSubConfig.monitoringInterface); UA_CHECK_STATUS(res, goto cleanup); #endif /* UA_ENABLE_PUBSUB_MONITORING */ #endif /* UA_ENABLE_PUBSUB */ return server; cleanup: UA_Server_delete(server); return NULL; } UA_Server * UA_Server_newWithConfig(UA_ServerConfig *config) { UA_CHECK_MEM(config, return NULL); UA_Server *server = (UA_Server *)UA_calloc(1, sizeof(UA_Server)); UA_CHECK_MEM(server, UA_ServerConfig_clean(config); return NULL); server->config = *config; /* The config might have been "moved" into the server struct. Ensure that * the logger pointer is correct. */ for(size_t i = 0; i < server->config.securityPoliciesSize; i++) server->config.securityPolicies[i].logger = &server->config.logger; /* Reset the old config */ memset(config, 0, sizeof(UA_ServerConfig)); 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) { UA_LOCK(&server->serviceMutex); UA_StatusCode retval = UA_Timer_addTimedCallback(&server->timer, (UA_ApplicationCallback)callback, server, data, date, callbackId); UA_UNLOCK(&server->serviceMutex); return retval; } UA_StatusCode 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, NULL, UA_TIMER_HANDLE_CYCLEMISS_WITH_CURRENTTIME, callbackId); } UA_StatusCode UA_Server_addRepeatedCallback(UA_Server *server, UA_ServerCallback callback, void *data, UA_Double interval_ms, UA_UInt64 *callbackId) { UA_LOCK(&server->serviceMutex); UA_StatusCode retval = addRepeatedCallback(server, callback, data, interval_ms, callbackId); UA_UNLOCK(&server->serviceMutex); return retval; } UA_StatusCode changeRepeatedCallbackInterval(UA_Server *server, UA_UInt64 callbackId, UA_Double interval_ms) { return UA_Timer_changeRepeatedCallback(&server->timer, callbackId, interval_ms, NULL, UA_TIMER_HANDLE_CYCLEMISS_WITH_CURRENTTIME); } UA_StatusCode UA_Server_changeRepeatedCallbackInterval(UA_Server *server, UA_UInt64 callbackId, UA_Double interval_ms) { UA_LOCK(&server->serviceMutex); UA_StatusCode retval = changeRepeatedCallbackInterval(server, callbackId, interval_ms); UA_UNLOCK(&server->serviceMutex); return retval; } void removeCallback(UA_Server *server, UA_UInt64 callbackId) { UA_Timer_removeCallback(&server->timer, callbackId); } void UA_Server_removeCallback(UA_Server *server, UA_UInt64 callbackId) { UA_LOCK(&server->serviceMutex); removeCallback(server, callbackId); UA_UNLOCK(&server->serviceMutex); } UA_StatusCode UA_Server_updateCertificate(UA_Server *server, const UA_ByteString *oldCertificate, const UA_ByteString *newCertificate, const UA_ByteString *newPrivateKey, UA_Boolean closeSessions, UA_Boolean closeSecureChannels) { UA_CHECK(server && oldCertificate && newCertificate && newPrivateKey, return UA_STATUSCODE_BADINTERNALERROR); if(closeSessions) { session_list_entry *current; LIST_FOREACH(current, &server->sessions, pointers) { if(UA_ByteString_equal(oldCertificate, ¤t->session.header.channel->securityPolicy->localCertificate)) { UA_LOCK(&server->serviceMutex); UA_Server_removeSessionByToken(server, ¤t->session.header.authenticationToken, UA_DIAGNOSTICEVENT_CLOSE); UA_UNLOCK(&server->serviceMutex); } } } if(closeSecureChannels) { channel_entry *entry; TAILQ_FOREACH(entry, &server->channels, pointers) { if(UA_ByteString_equal(&entry->channel.securityPolicy->localCertificate, oldCertificate)) UA_Server_closeSecureChannel(server, &entry->channel, UA_DIAGNOSTICEVENT_CLOSE); } } 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_clear(&ed->serverCertificate); UA_String_copy(newCertificate, &ed->serverCertificate); UA_SecurityPolicy *sp = getSecurityPolicyByUri(server, &server->config.endpoints[i].securityPolicyUri); UA_CHECK_MEM(sp, return UA_STATUSCODE_BADINTERNALERROR); sp->updateCertificateAndPrivateKey(sp, *newCertificate, *newPrivateKey); } i++; } return UA_STATUSCODE_GOOD; } /***************************/ /* Server lookup functions */ /***************************/ 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 UA_StatusCode verifyServerApplicationURI(const UA_Server *server) { const UA_String securityPolicyNoneUri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None"); for(size_t i = 0; i < server->config.securityPoliciesSize; i++) { UA_SecurityPolicy *sp = &server->config.securityPolicies[i]; if(UA_String_equal(&sp->policyUri, &securityPolicyNoneUri) && (sp->localCertificate.length == 0)) continue; UA_StatusCode retval = server->config.certificateVerification. verifyApplicationURI(server->config.certificateVerification.context, &sp->localCertificate, &server->config.applicationDescription.applicationUri); UA_CHECK_STATUS_ERROR(retval, return retval, &server->config.logger, UA_LOGCATEGORY_SERVER, "The configured ApplicationURI \"%.*s\"does not match the " "ApplicationURI specified in the certificate for the " "SecurityPolicy %.*s", (int)server->config.applicationDescription.applicationUri.length, server->config.applicationDescription.applicationUri.data, (int)sp->policyUri.length, sp->policyUri.data); } return UA_STATUSCODE_GOOD; } #endif UA_ServerStatistics UA_Server_getStatistics(UA_Server *server) { UA_ServerStatistics stat; stat.ns = server->networkStatistics; stat.scs = server->secureChannelStatistics; stat.ss.currentSessionCount = server->activeSessionCount; stat.ss.cumulatedSessionCount = server->serverDiagnosticsSummary.cumulatedSessionCount; stat.ss.securityRejectedSessionCount = server->serverDiagnosticsSummary.securityRejectedSessionCount; stat.ss.rejectedSessionCount = server->serverDiagnosticsSummary.rejectedSessionCount; stat.ss.sessionTimeoutCount = server->serverDiagnosticsSummary.sessionTimeoutCount; stat.ss.sessionAbortCount = server->serverDiagnosticsSummary.sessionAbortCount; return stat; } /********************/ /* 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) { #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION /* Prominently warn user that fuzzing build is enabled. This will tamper with authentication tokens and other important variables * E.g. if fuzzing is enabled, and two clients are connected, subscriptions do not work properly, * since the tokens will be overridden to allow easier fuzzing. */ UA_LOG_FATAL(&server->config.logger, UA_LOGCATEGORY_SERVER, "Server was built with unsafe fuzzing mode. " "This should only be used for specific fuzzing builds."); #endif /* 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]); UA_CHECK_STATUS(retVal, 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."); } /* Does the ApplicationURI match the local certificates? */ #ifdef UA_ENABLE_ENCRYPTION retVal = verifyServerApplicationURI(server); UA_CHECK_STATUS(retVal, return retVal); #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]; nl->statistics = &server->networkStatistics; result |= nl->start(nl, &server->config.logger, &server->config.customHostname); } UA_CHECK_STATUS(result, return result); /* 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]); UA_CHECK_MEM(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]); } /* Start the multicast discovery server */ #ifdef UA_ENABLE_DISCOVERY_MULTICAST if(server->config.mdnsEnabled) startMulticastDiscoveryServer(server); #endif /* Update Endpoint description */ for(size_t i = 0; i < server->config.endpointsSize; ++i){ UA_ApplicationDescription_clear(&server->config.endpoints[i].server); UA_ApplicationDescription_copy(&server->config.applicationDescription, &server->config.endpoints[i].server); } server->state = UA_SERVERLIFECYCLE_FRESH; return result; } static void serverExecuteRepeatedCallback(UA_Server *server, UA_ApplicationCallback cb, void *callbackApplication, void *data) { /* Service mutex is not set inside the timer that triggers the callback */ /* The following check cannot be used since another thread can take the * serviceMutex during a server_iterate_call. */ //UA_LOCK_ASSERT(&server->serviceMutex, 0); cb(callbackApplication, data); } 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_PUBSUB_MQTT) /* Listen on the pubsublayer, but only if the yield function is set */ UA_PubSubConnection *connection; TAILQ_FOREACH(connection, &server->pubSubManager.connections, listEntry){ UA_PubSubConnection *ps = connection; if(ps && ps->channel->yield){ ps->channel->yield(ps->channel, timeout); } } #endif UA_LOCK(&server->serviceMutex); #if defined(UA_ENABLE_DISCOVERY_MULTICAST) && (UA_MULTITHREADING < 200) if(server->config.mdnsEnabled) { /* 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 UA_UNLOCK(&server->serviceMutex); 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_DISCOVERY_MULTICAST /* Stop multicast discovery */ if(server->config.mdnsEnabled) stopMulticastDiscoveryServer(server); #endif 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); UA_CHECK_STATUS(retval, 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); } /**** amalgamated original file "/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-2022 (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 * Copyright 2021 (c) Christian von Arnim, ISW University of Stuttgart (for VDW and umati) */ 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); UA_ExtensionObject_setValueNoDelete(&item.nodeAttributes, attributes, 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 ReferenceTypes. The order of these is important for the * ReferenceTypeIndex. The ReferenceTypeIndex is created with the raw node. * The ReferenceTypeSet of subtypes for every ReferenceType is created * during the call to AddNode_finish. */ 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("", "SubtypeOf"); 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 = true; 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, true, 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); ret |= addReferenceTypeNode(server, "HasInterface", "InterfaceOf", UA_NS0ID_HASINTERFACE, false, false, UA_NS0ID_NONHIERARCHICALREFERENCES); /**************/ /* 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); /* Add BaseEventType */ UA_ObjectTypeAttributes eventtype_attr = UA_ObjectTypeAttributes_default; eventtype_attr.displayName = UA_LOCALIZEDTEXT("", "BaseEventType"); ret |= addNode_raw(server, UA_NODECLASS_OBJECTTYPE, UA_NS0ID_BASEEVENTTYPE, "BaseEventType", &eventtype_attr, &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES]); ret |= addNode_finish(server, UA_NS0ID_BASEEVENTTYPE, UA_NS0ID_EVENTTYPESFOLDER, UA_NS0ID_ORGANIZES); 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; UA_Duration minInterval; #ifdef UA_ENABLE_SUBSCRIPTIONS minInterval = server->config.samplingIntervalLimits.min; #else minInterval = 0.0; #endif retval = UA_Variant_setScalarCopy(&value->value, &minInterval, &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) { /* Return two empty arrays by default */ 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]); /* Get the Session */ UA_LOCK(&server->serviceMutex); UA_Session *session = UA_Server_getSessionById(server, sessionId); if(!session) { UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_BADINTERNALERROR; } if(inputSize == 0 || !input[0].data) { UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; } /* Get the Subscription */ UA_UInt32 subscriptionId = *((UA_UInt32*)(input[0].data)); UA_Subscription *subscription = UA_Server_getSubscriptionById(server, subscriptionId); if(!subscription) { UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; } /* The Subscription is not attached to this Session */ if(subscription->session != session) { UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_BADUSERACCESSDENIED; } /* Count the MonitoredItems */ UA_UInt32 sizeOfOutput = 0; UA_MonitoredItem* monitoredItem; LIST_FOREACH(monitoredItem, &subscription->monitoredItems, listEntry) { ++sizeOfOutput; } if(sizeOfOutput == 0) { UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_GOOD; } /* Allocate the output arrays */ UA_UInt32 *clientHandles = (UA_UInt32*) UA_Array_new(sizeOfOutput, &UA_TYPES[UA_TYPES_UINT32]); if(!clientHandles) { UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_BADOUTOFMEMORY; } UA_UInt32 *serverHandles = (UA_UInt32*) UA_Array_new(sizeOfOutput, &UA_TYPES[UA_TYPES_UINT32]); if(!serverHandles) { UA_UNLOCK(&server->serviceMutex); UA_free(clientHandles); return UA_STATUSCODE_BADOUTOFMEMORY; } /* Fill the array */ UA_UInt32 i = 0; LIST_FOREACH(monitoredItem, &subscription->monitoredItems, listEntry) { clientHandles[i] = monitoredItem->parameters.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]); UA_UNLOCK(&server->serviceMutex); 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); #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 server->bootstrapNS0 = false; if(retVal != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Initialization of Namespace 0 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}; UA_NodeId currTime = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME); retVal |= UA_Server_setVariableNode_dataSource(server, currTime, currentTime); retVal |= UA_Server_writeMinimumSamplingInterval(server, currTime, 100.0); /* 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 - EnabledFlag */ #ifdef UA_ENABLE_DIAGNOSTICS UA_Boolean enabledFlag = true; #else UA_Boolean enabledFlag = false; #endif 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]); /* 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 configurations */ 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_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); #ifdef UA_ENABLE_DIAGNOSTICS /* ServerDiagnostics - ServerDiagnosticsSummary */ UA_DataSource serverDiagSummary = {readDiagnostics, NULL}; retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY), serverDiagSummary); /* ServerDiagnostics - ServerDiagnosticsSummary - ServerViewCount */ retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_SERVERVIEWCOUNT), serverDiagSummary); /* ServerDiagnostics - ServerDiagnosticsSummary - CurrentSessionCount */ retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_CURRENTSESSIONCOUNT), serverDiagSummary); /* ServerDiagnostics - ServerDiagnosticsSummary - CumulatedSessionCount */ retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_CUMULATEDSESSIONCOUNT), serverDiagSummary); /* ServerDiagnostics - ServerDiagnosticsSummary - SecurityRejectedSessionCount */ retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_SECURITYREJECTEDSESSIONCOUNT), serverDiagSummary); /* ServerDiagnostics - ServerDiagnosticsSummary - RejectedSessionCount */ retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_REJECTEDSESSIONCOUNT), serverDiagSummary); /* ServerDiagnostics - ServerDiagnosticsSummary - SessionTimeoutCount */ retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_SESSIONTIMEOUTCOUNT), serverDiagSummary); /* ServerDiagnostics - ServerDiagnosticsSummary - SessionAbortCount */ retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_SESSIONABORTCOUNT), serverDiagSummary); /* ServerDiagnostics - ServerDiagnosticsSummary - CurrentSubscriptionCount */ retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_CURRENTSUBSCRIPTIONCOUNT), serverDiagSummary); /* ServerDiagnostics - ServerDiagnosticsSummary - CumulatedSubscriptionCount */ retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_CUMULATEDSUBSCRIPTIONCOUNT), serverDiagSummary); /* ServerDiagnostics - ServerDiagnosticsSummary - PublishingIntervalCount */ retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_PUBLISHINGINTERVALCOUNT), serverDiagSummary); /* ServerDiagnostics - ServerDiagnosticsSummary - SecurityRejectedRequestsCount */ retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_SECURITYREJECTEDREQUESTSCOUNT), serverDiagSummary); /* ServerDiagnostics - ServerDiagnosticsSummary - RejectedRequestsCount */ retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_REJECTEDREQUESTSCOUNT), serverDiagSummary); /* ServerDiagnostics - SubscriptionDiagnosticsArray */ #ifdef UA_ENABLE_SUBSCRIPTIONS UA_DataSource serverSubDiagSummary = {readSubscriptionDiagnosticsArray, NULL}; retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SUBSCRIPTIONDIAGNOSTICSARRAY), serverSubDiagSummary); #endif /* ServerDiagnostics - SessionDiagnosticsSummary - SessionDiagnosticsArray */ UA_DataSource sessionDiagSummary = {readSessionDiagnosticsArray, NULL}; retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SESSIONSDIAGNOSTICSSUMMARY_SESSIONDIAGNOSTICSARRAY), sessionDiagSummary); /* ServerDiagnostics - SessionDiagnosticsSummary - SessionSecurityDiagnosticsArray */ UA_DataSource sessionSecDiagSummary = {readSessionSecurityDiagnostics, NULL}; retVal |= UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SESSIONSDIAGNOSTICSSUMMARY_SESSIONSECURITYDIAGNOSTICSARRAY), sessionSecDiagSummary); #else /* 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_SAMPLINGINTERVALDIAGNOSTICSARRAY), true); UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SESSIONSDIAGNOSTICSSUMMARY), true); 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_PUBSUB UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE), 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_setMethodNodeCallback(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 */ 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 "/src/server/ua_server_ns0_diagnostics.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 2022 (c) Fraunhofer IOSB (Author: Julius Pfrommer) */ #ifdef UA_ENABLE_DIAGNOSTICS static UA_Boolean equalBrowseName(UA_String *bn, char *n) { UA_String name = UA_STRING(n); return UA_String_equal(bn, &name); } #ifdef UA_ENABLE_SUBSCRIPTIONS /****************************/ /* Subscription Diagnostics */ /****************************/ static void fillSubscriptionDiagnostics(UA_Subscription *sub, UA_SubscriptionDiagnosticsDataType *diag) { UA_NodeId_copy(&sub->session->sessionId, &diag->sessionId); /* ignore status */ diag->subscriptionId = sub->subscriptionId; diag->priority = sub->priority; diag->publishingInterval = sub->publishingInterval; diag->maxKeepAliveCount = sub->maxKeepAliveCount; diag->maxLifetimeCount = sub->lifeTimeCount; diag->maxNotificationsPerPublish = sub->notificationsPerPublish; diag->publishingEnabled = sub->publishingEnabled; diag->modifyCount = sub->modifyCount; diag->enableCount = sub->enableCount; diag->disableCount = sub->disableCount; diag->republishRequestCount = sub->republishRequestCount; diag->republishMessageRequestCount = sub->republishRequestCount; /* Always equal to the previous republishRequestCount */ diag->republishMessageCount = sub->republishMessageCount; diag->transferRequestCount = sub->transferRequestCount; diag->transferredToAltClientCount = sub->transferredToAltClientCount; diag->transferredToSameClientCount = sub->transferredToSameClientCount; diag->publishRequestCount = sub->publishRequestCount; diag->dataChangeNotificationsCount = sub->dataChangeNotificationsCount; diag->eventNotificationsCount = sub->eventNotificationsCount; diag->notificationsCount = sub->notificationsCount; diag->latePublishRequestCount = sub->latePublishRequestCount; diag->currentKeepAliveCount = sub->currentKeepAliveCount; diag->currentLifetimeCount = sub->currentLifetimeCount; diag->unacknowledgedMessageCount = (UA_UInt32)sub->retransmissionQueueSize; diag->discardedMessageCount = sub->discardedMessageCount; diag->monitoredItemCount = sub->monitoredItemsSize; diag->monitoringQueueOverflowCount = sub->monitoringQueueOverflowCount; diag->nextSequenceNumber = sub->nextSequenceNumber; diag->eventQueueOverFlowCount = sub->eventQueueOverFlowCount; /* Count the disabled MonitoredItems */ UA_MonitoredItem *mon; LIST_FOREACH(mon, &sub->monitoredItems, listEntry) { if(mon->monitoringMode == UA_MONITORINGMODE_DISABLED) diag->disabledMonitoredItemCount++; } } /* The node context points to the subscription */ static UA_StatusCode readSubscriptionDiagnostics(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) { /* Check the Subscription pointer */ UA_Subscription *sub = (UA_Subscription*)nodeContext; if(!sub) return UA_STATUSCODE_BADINTERNALERROR; /* Read the BrowseName */ UA_QualifiedName bn; UA_StatusCode res = readWithReadValue(server, nodeId, UA_ATTRIBUTEID_BROWSENAME, &bn); if(res != UA_STATUSCODE_GOOD) return res; /* Set the value */ UA_SubscriptionDiagnosticsDataType sddt; UA_SubscriptionDiagnosticsDataType_init(&sddt); fillSubscriptionDiagnostics(sub, &sddt); char memberName[128]; memcpy(memberName, bn.name.data, bn.name.length); memberName[bn.name.length] = 0; size_t memberOffset; const UA_DataType *memberType; UA_Boolean isArray; UA_Boolean found = UA_DataType_getStructMember(&UA_TYPES[UA_TYPES_SUBSCRIPTIONDIAGNOSTICSDATATYPE], memberName, &memberOffset, &memberType, &isArray); if(!found) { /* Not the member, but the main subscription diagnostics variable... */ memberOffset = 0; memberType = &UA_TYPES[UA_TYPES_SUBSCRIPTIONDIAGNOSTICSDATATYPE]; } void *content = (void*)(((uintptr_t)&sddt) + memberOffset); res = UA_Variant_setScalarCopy(&value->value, content, memberType); if(UA_LIKELY(res == UA_STATUSCODE_GOOD)) value->hasValue = true; UA_SubscriptionDiagnosticsDataType_clear(&sddt); UA_QualifiedName_clear(&bn); return res; } /* If the nodeContext == NULL, return all subscriptions in the server. * Otherwise only for the current session. */ UA_StatusCode readSubscriptionDiagnosticsArray(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) { /* Get the current session */ size_t sdSize = 0; UA_Session *session = NULL; session_list_entry *sentry; if(nodeContext) { session = UA_Server_getSessionById(server, sessionId); if(!session) return UA_STATUSCODE_BADINTERNALERROR; sdSize = session->subscriptionsSize; } else { LIST_FOREACH(sentry, &server->sessions, pointers) { sdSize += sentry->session.subscriptionsSize; } } /* Allocate the output array */ UA_SubscriptionDiagnosticsDataType *sd = (UA_SubscriptionDiagnosticsDataType*) UA_Array_new(sdSize, &UA_TYPES[UA_TYPES_SUBSCRIPTIONDIAGNOSTICSDATATYPE]); if(!sd) return UA_STATUSCODE_BADOUTOFMEMORY; /* Collect the statistics */ size_t i = 0; UA_Subscription *sub; if(session) { TAILQ_FOREACH(sub, &session->subscriptions, sessionListEntry) { fillSubscriptionDiagnostics(sub, &sd[i]); i++; } } else { LIST_FOREACH(sentry, &server->sessions, pointers) { TAILQ_FOREACH(sub, &sentry->session.subscriptions, sessionListEntry) { fillSubscriptionDiagnostics(sub, &sd[i]); i++; } } } /* Set the output */ value->hasValue = true; UA_Variant_setArray(&value->value, sd, sdSize, &UA_TYPES[UA_TYPES_SUBSCRIPTIONDIAGNOSTICSDATATYPE]); return UA_STATUSCODE_GOOD; } void createSubscriptionObject(UA_Server *server, UA_Session *session, UA_Subscription *sub) { UA_ExpandedNodeId *children = NULL; size_t childrenSize = 0; UA_ReferenceTypeSet refTypes; UA_NodeId hasComponent = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT); char subIdStr[32]; snprintf(subIdStr, 32, "%u", sub->subscriptionId); /* Find the NodeId of the SubscriptionDiagnosticsArray */ UA_BrowsePath bp; UA_BrowsePath_init(&bp); bp.startingNode = sub->session->sessionId; UA_RelativePathElement rpe[1]; memset(rpe, 0, sizeof(UA_RelativePathElement) * 1); rpe[0].targetName = UA_QUALIFIEDNAME(0, "SubscriptionDiagnosticsArray"); bp.relativePath.elements = rpe; bp.relativePath.elementsSize = 1; UA_BrowsePathResult bpr = translateBrowsePathToNodeIds(server, &bp); if(bpr.targetsSize < 1) return; /* Create an object for the subscription. Instantiates all the mandatory * children. */ UA_VariableAttributes var_attr = UA_VariableAttributes_default; var_attr.displayName.text = UA_STRING(subIdStr); var_attr.dataType = UA_TYPES[UA_TYPES_SUBSCRIPTIONDIAGNOSTICSDATATYPE].typeId; UA_NodeId refId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT); UA_QualifiedName browseName = UA_QUALIFIEDNAME(0, subIdStr); UA_NodeId typeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SUBSCRIPTIONDIAGNOSTICSTYPE); UA_NodeId objId = UA_NODEID_NUMERIC(1, 0); /* 0 => assign a random free id */ UA_StatusCode res = addNode(server, UA_NODECLASS_VARIABLE, &objId, &bpr.targets[0].targetId.nodeId /* parent */, &refId, browseName, &typeId, (UA_NodeAttributes*)&var_attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES], NULL, &objId); UA_CHECK_STATUS(res, goto cleanup); /* Add a second reference from the overall SubscriptionDiagnosticsArray variable */ const UA_NodeId subDiagArray = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SUBSCRIPTIONDIAGNOSTICSARRAY); res = addRef(server, session, &subDiagArray, &refId, &objId, true); /* Get all children (including the variable itself) and set the contenxt + callback */ res = referenceTypeIndices(server, &hasComponent, &refTypes, false); if(res != UA_STATUSCODE_GOOD) goto cleanup; res = browseRecursive(server, 1, &objId, UA_BROWSEDIRECTION_FORWARD, &refTypes, UA_NODECLASS_VARIABLE, true, &childrenSize, &children); if(res != UA_STATUSCODE_GOOD) goto cleanup; /* Add the callback to all variables */ UA_DataSource subDiagSource = {readSubscriptionDiagnostics, NULL}; for(size_t i = 0; i < childrenSize; i++) { setVariableNode_dataSource(server, children[i].nodeId, subDiagSource); setNodeContext(server, children[i].nodeId, sub); } UA_Array_delete(children, childrenSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); cleanup: UA_BrowsePathResult_clear(&bpr); if(res != UA_STATUSCODE_GOOD) { UA_LOG_WARNING_SESSION(&server->config.logger, session, "Creating the subscription diagnostics object failed " "with StatusCode %s", UA_StatusCode_name(res)); } } #endif /* UA_ENABLE_SUBSCRIPTIONS */ /***********************/ /* Session Diagnostics */ /***********************/ static void setSessionDiagnostics(UA_Session *session, UA_SessionDiagnosticsDataType *sd) { UA_SessionDiagnosticsDataType_copy(&session->diagnostics, sd); UA_NodeId_copy(&session->sessionId, &sd->sessionId); UA_String_copy(&session->sessionName, &sd->sessionName); UA_ApplicationDescription_copy(&session->clientDescription, &sd->clientDescription); sd->maxResponseMessageSize = session->maxResponseMessageSize; #ifdef UA_ENABLE_SUBSCRIPTIONS sd->currentPublishRequestsInQueue = (UA_UInt32)session->responseQueueSize; #endif sd->actualSessionTimeout = session->timeout; /* Set LocaleIds */ UA_StatusCode res = UA_Array_copy(session->localeIds, session->localeIdsSize, (void **)&sd->localeIds, &UA_TYPES[UA_TYPES_STRING]); if(UA_LIKELY(res == UA_STATUSCODE_GOOD)) sd->localeIdsSize = session->localeIdsSize; /* Set Subscription diagnostics */ #ifdef UA_ENABLE_SUBSCRIPTIONS sd->currentSubscriptionsCount = (UA_UInt32)session->subscriptionsSize; UA_Subscription *sub; TAILQ_FOREACH(sub, &session->subscriptions, sessionListEntry) { sd->currentMonitoredItemsCount += (UA_UInt32)sub->monitoredItemsSize; } #endif } UA_StatusCode readSessionDiagnosticsArray(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) { /* Allocate the output array */ UA_SessionDiagnosticsDataType *sd = (UA_SessionDiagnosticsDataType*) UA_Array_new(server->sessionCount, &UA_TYPES[UA_TYPES_SESSIONDIAGNOSTICSDATATYPE]); if(!sd) return UA_STATUSCODE_BADOUTOFMEMORY; /* Collect the statistics */ size_t i = 0; session_list_entry *session; LIST_FOREACH(session, &server->sessions, pointers) { setSessionDiagnostics(&session->session, &sd[i]); i++; } /* Set the output */ value->hasValue = true; UA_Variant_setArray(&value->value, sd, server->sessionCount, &UA_TYPES[UA_TYPES_SESSIONDIAGNOSTICSDATATYPE]); return UA_STATUSCODE_GOOD; } static void setSessionSecurityDiagnostics(UA_Session *session, UA_SessionSecurityDiagnosticsDataType *sd) { UA_SessionSecurityDiagnosticsDataType_copy(&session->securityDiagnostics, sd); UA_NodeId_copy(&session->sessionId, &sd->sessionId); UA_SecureChannel *channel = session->header.channel; if(channel) { UA_ByteString_copy(&channel->remoteCertificate, &sd->clientCertificate); UA_String_copy(&channel->securityPolicy->policyUri, &sd->securityPolicyUri); sd->securityMode = channel->securityMode; sd->encoding = UA_STRING_ALLOC("UA Binary"); /* The only one atm */ sd->transportProtocol = UA_STRING_ALLOC("opc.tcp"); /* The only one atm */ } } static UA_StatusCode readSessionDiagnostics(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) { /* Get the Session */ UA_Session *session = UA_Server_getSessionById(server, sessionId); if(!session) return UA_STATUSCODE_BADINTERNALERROR; /* Read the BrowseName */ UA_QualifiedName bn; UA_StatusCode res = readWithReadValue(server, nodeId, UA_ATTRIBUTEID_BROWSENAME, &bn); if(res != UA_STATUSCODE_GOOD) return res; union { UA_SessionDiagnosticsDataType sddt; UA_SessionSecurityDiagnosticsDataType ssddt; } data; void *content; UA_Boolean isArray = false; const UA_DataType *type = NULL; UA_Boolean securityDiagnostics = false; char memberName[128]; size_t memberOffset; UA_Boolean found; if(equalBrowseName(&bn.name, "SubscriptionDiagnosticsArray")) { /* Reuse the datasource callback. Forward a non-null nodeContext to * indicate that we want to see only the subscriptions for the current * session. */ res = readSubscriptionDiagnosticsArray(server, sessionId, sessionContext, nodeId, (void*)0x01, sourceTimestamp, range, value); goto cleanup; } else if(equalBrowseName(&bn.name, "SessionDiagnostics")) { setSessionDiagnostics(session, &data.sddt); content = &data.sddt; type = &UA_TYPES[UA_TYPES_SESSIONDIAGNOSTICSDATATYPE]; goto set_value; } else if(equalBrowseName(&bn.name, "SessionSecurityDiagnostics")) { setSessionSecurityDiagnostics(session, &data.ssddt); securityDiagnostics = true; content = &data.ssddt; type = &UA_TYPES[UA_TYPES_SESSIONSECURITYDIAGNOSTICSDATATYPE]; goto set_value; } /* Try to find the member in SessionDiagnosticsDataType and * SessionSecurityDiagnosticsDataType */ memcpy(memberName, bn.name.data, bn.name.length); memberName[bn.name.length] = 0; found = UA_DataType_getStructMember(&UA_TYPES[UA_TYPES_SESSIONDIAGNOSTICSDATATYPE], memberName, &memberOffset, &type, &isArray); if(found) { setSessionDiagnostics(session, &data.sddt); content = (void*)(((uintptr_t)&data.sddt) + memberOffset); } else { found = UA_DataType_getStructMember(&UA_TYPES[UA_TYPES_SESSIONSECURITYDIAGNOSTICSDATATYPE], memberName, &memberOffset, &type, &isArray); if(!found) { res = UA_STATUSCODE_BADNOTIMPLEMENTED; goto cleanup; } setSessionSecurityDiagnostics(session, &data.ssddt); securityDiagnostics = true; content = (void*)(((uintptr_t)&data.ssddt) + memberOffset); } set_value: if(!isArray) { res = UA_Variant_setScalarCopy(&value->value, content, type); } else { size_t len = *(size_t*)content; content = (void*)(((uintptr_t)content) + sizeof(size_t)); res = UA_Variant_setArrayCopy(&value->value, content, len, type); } if(UA_LIKELY(res == UA_STATUSCODE_GOOD)) value->hasValue = true; if(securityDiagnostics) UA_SessionSecurityDiagnosticsDataType_clear(&data.ssddt); else UA_SessionDiagnosticsDataType_clear(&data.sddt); cleanup: UA_QualifiedName_clear(&bn); return res; } UA_StatusCode readSessionSecurityDiagnostics(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) { /* Allocate the output array */ UA_SessionSecurityDiagnosticsDataType *sd = (UA_SessionSecurityDiagnosticsDataType*) UA_Array_new(server->sessionCount, &UA_TYPES[UA_TYPES_SESSIONSECURITYDIAGNOSTICSDATATYPE]); if(!sd) return UA_STATUSCODE_BADOUTOFMEMORY; /* Collect the statistics */ size_t i = 0; session_list_entry *session; LIST_FOREACH(session, &server->sessions, pointers) { setSessionSecurityDiagnostics(&session->session, &sd[i]); i++; } /* Set the output */ value->hasValue = true; UA_Variant_setArray(&value->value, sd, server->sessionCount, &UA_TYPES[UA_TYPES_SESSIONSECURITYDIAGNOSTICSDATATYPE]); return UA_STATUSCODE_GOOD; } void createSessionObject(UA_Server *server, UA_Session *session) { UA_ExpandedNodeId *children = NULL; size_t childrenSize = 0; UA_ReferenceTypeSet refTypes; UA_NodeId hasComponent = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT); /* Create an object for the session. Instantiates all the mandatory children. */ UA_ObjectAttributes object_attr = UA_ObjectAttributes_default; object_attr.displayName.text = session->sessionName; UA_NodeId parentId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SESSIONSDIAGNOSTICSSUMMARY); UA_NodeId refId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT); UA_QualifiedName browseName = UA_QUALIFIEDNAME(0, ""); browseName.name = session->sessionName; /* shallow copy */ UA_NodeId typeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SESSIONDIAGNOSTICSOBJECTTYPE); UA_StatusCode res = addNode(server, UA_NODECLASS_OBJECT, &session->sessionId, &parentId, &refId, browseName, &typeId, (UA_NodeAttributes*)&object_attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES], NULL, NULL); if(res != UA_STATUSCODE_GOOD) goto cleanup; /* Recursively browse all children */ res = referenceTypeIndices(server, &hasComponent, &refTypes, false); if(res != UA_STATUSCODE_GOOD) goto cleanup; res = browseRecursive(server, 1, &session->sessionId, UA_BROWSEDIRECTION_FORWARD, &refTypes, UA_NODECLASS_VARIABLE, false, &childrenSize, &children); if(res != UA_STATUSCODE_GOOD) goto cleanup; /* Add the callback to all variables */ UA_DataSource sessionDiagSource = {readSessionDiagnostics, NULL}; for(size_t i = 0; i < childrenSize; i++) { setVariableNode_dataSource(server, children[i].nodeId, sessionDiagSource); } cleanup: if(res != UA_STATUSCODE_GOOD) { UA_LOG_WARNING_SESSION(&server->config.logger, session, "Creating the session diagnostics object failed " "with StatusCode %s", UA_StatusCode_name(res)); } UA_Array_delete(children, childrenSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); } /***************************/ /* Server-Wide Diagnostics */ /***************************/ UA_StatusCode readDiagnostics(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(); } UA_assert(nodeId->identifierType == UA_NODEIDTYPE_NUMERIC); void *data = NULL; const UA_DataType *type = &UA_TYPES[UA_TYPES_UINT32]; /* Default */ switch(nodeId->identifier.numeric) { case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY: server->serverDiagnosticsSummary.currentSessionCount = server->activeSessionCount; data = &server->serverDiagnosticsSummary; type = &UA_TYPES[UA_TYPES_SERVERDIAGNOSTICSSUMMARYDATATYPE]; break; case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_SERVERVIEWCOUNT: data = &server->serverDiagnosticsSummary.serverViewCount; break; case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_CURRENTSESSIONCOUNT: data = &server->activeSessionCount; break; case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_CUMULATEDSESSIONCOUNT: data = &server->serverDiagnosticsSummary.cumulatedSessionCount; break; case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_SECURITYREJECTEDSESSIONCOUNT: data = &server->serverDiagnosticsSummary.securityRejectedSessionCount; break; case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_REJECTEDSESSIONCOUNT: data = &server->serverDiagnosticsSummary.rejectedSessionCount; break; case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_SESSIONTIMEOUTCOUNT: data = &server->serverDiagnosticsSummary.sessionTimeoutCount; break; case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_SESSIONABORTCOUNT: data = &server->serverDiagnosticsSummary.sessionAbortCount; break; case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_CURRENTSUBSCRIPTIONCOUNT: data = &server->serverDiagnosticsSummary.currentSubscriptionCount; break; case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_CUMULATEDSUBSCRIPTIONCOUNT: data = &server->serverDiagnosticsSummary.cumulatedSubscriptionCount; break; case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_PUBLISHINGINTERVALCOUNT: data = &server->serverDiagnosticsSummary.publishingIntervalCount; break; case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_SECURITYREJECTEDREQUESTSCOUNT: data = &server->serverDiagnosticsSummary.securityRejectedRequestsCount; break; case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_REJECTEDREQUESTSCOUNT: data = &server->serverDiagnosticsSummary.rejectedRequestsCount; break; default: return UA_STATUSCODE_BADINTERNALERROR; } UA_StatusCode res = UA_Variant_setScalarCopy(&value->value, data, type); if(res == UA_STATUSCODE_GOOD) value->hasValue = true; return res; } #endif /* UA_ENABLE_DIAGNOSTICS */ /**** amalgamated original file "/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) * Copyright 2019 (c) HMS Industrial Networks AB (Author: Jonas Green) */ void UA_ServerConfig_clean(UA_ServerConfig *config) { if(!config) return; /* Server Description */ UA_BuildInfo_clear(&config->buildInfo); UA_ApplicationDescription_clear(&config->applicationDescription); #ifdef UA_ENABLE_DISCOVERY_MULTICAST UA_MdnsDiscoveryConfiguration_clear(&config->mdnsConfig); UA_String_clear(&config->mdnsInterfaceIP); # if !defined(UA_HAS_GETIFADDR) if (config->mdnsIpAddressListSize) { UA_free(config->mdnsIpAddressList); } # endif #endif /* Custom DataTypes */ /* nothing to do */ /* Networking */ for(size_t i = 0; i < config->networkLayersSize; ++i) config->networkLayers[i].clear(&config->networkLayers[i]); UA_free(config->networkLayers); config->networkLayers = NULL; config->networkLayersSize = 0; UA_String_clear(&config->customHostname); config->customHostname = UA_STRING_NULL; for(size_t i = 0; i < config->securityPoliciesSize; ++i) { UA_SecurityPolicy *policy = &config->securityPolicies[i]; policy->clear(policy); } UA_free(config->securityPolicies); config->securityPolicies = NULL; config->securityPoliciesSize = 0; for(size_t i = 0; i < config->endpointsSize; ++i) UA_EndpointDescription_clear(&config->endpoints[i]); UA_free(config->endpoints); config->endpoints = NULL; config->endpointsSize = 0; /* Nodestore */ if(config->nodestore.context && config->nodestore.clear) { config->nodestore.clear(config->nodestore.context); config->nodestore.context = NULL; } /* Certificate Validation */ if(config->certificateVerification.clear) config->certificateVerification.clear(&config->certificateVerification); /* Access Control */ if(config->accessControl.clear) config->accessControl.clear(&config->accessControl); /* Historical data */ #ifdef UA_ENABLE_HISTORIZING if(config->historyDatabase.clear) config->historyDatabase.clear(&config->historyDatabase); #endif /* Logger */ if(config->logger.clear) config->logger.clear(config->logger.context); config->logger.log = NULL; config->logger.clear = NULL; #ifdef UA_ENABLE_PUBSUB #ifdef UA_ENABLE_PUBSUB_ENCRYPTION if(config->pubSubConfig.securityPolicies != NULL) { for(size_t i = 0; i < config->pubSubConfig.securityPoliciesSize; i++) { config->pubSubConfig.securityPolicies[i].clear(&config->pubSubConfig.securityPolicies[i]); } UA_free(config->pubSubConfig.securityPolicies); config->pubSubConfig.securityPolicies = NULL; config->pubSubConfig.securityPoliciesSize = 0; } #endif #endif /* UA_ENABLE_PUBSUB */ } #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) { UA_PubSubTransportLayer *tmpLayers = (UA_PubSubTransportLayer*) UA_realloc(config->pubSubConfig.transportLayers, sizeof(UA_PubSubTransportLayer) * (config->pubSubConfig.transportLayersSize + 1)); if(tmpLayers == NULL) return UA_STATUSCODE_BADOUTOFMEMORY; config->pubSubConfig.transportLayers = tmpLayers; config->pubSubConfig.transportLayers[config->pubSubConfig.transportLayersSize] = pubsubTransportLayer; config->pubSubConfig.transportLayersSize++; return UA_STATUSCODE_GOOD; } #endif /* UA_ENABLE_PUBSUB */ /**** amalgamated original file "/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-2022 (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 */ /********************/ UA_StatusCode sendServiceFault(UA_SecureChannel *channel, UA_UInt32 requestId, UA_UInt32 requestHandle, UA_StatusCode statusCode) { UA_ServiceFault response; UA_ServiceFault_init(&response); UA_ResponseHeader *responseHeader = &response.responseHeader; responseHeader->requestHandle = requestHandle; responseHeader->timestamp = UA_DateTime_now(); responseHeader->serviceResult = statusCode; UA_LOG_DEBUG(channel->securityPolicy->logger, UA_LOGCATEGORY_SERVER, "Sending response for RequestId %u with ServiceResult %s", (unsigned)requestId, UA_StatusCode_name(statusCode)); /* Send error message. Message type is MSG and not ERR, since we are on a * SecureChannel! */ return UA_SecureChannel_sendSymmetricMessage(channel, requestId, UA_MESSAGETYPE_MSG, &response, &UA_TYPES[UA_TYPES_SERVICEFAULT]); } /* This is not an ERR message, the connection is not closed afterwards */ static UA_StatusCode decodeHeaderSendServiceFault(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_decodeBinaryInternal(msg, &offset, &requestHeader, &UA_TYPES[UA_TYPES_REQUESTHEADER], NULL); if(retval != UA_STATUSCODE_GOOD) return retval; retval = sendServiceFault(channel, requestId, requestHeader.requestHandle, error); UA_RequestHeader_clear(&requestHeader); return retval; } /* The counterOffset is the offset of the UA_ServiceCounterDataType for the * service in the UA_ SessionDiagnosticsDataType. */ #ifdef UA_ENABLE_DIAGNOSTICS #define UA_SERVICECOUNTER_OFFSET(X) \ *counterOffset = offsetof(UA_SessionDiagnosticsDataType, X) #else #define UA_SERVICECOUNTER_OFFSET(X) #endif static void getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType, const UA_DataType **responseType, UA_Service *service, UA_Boolean *requiresSession, size_t *counterOffset) { 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 = (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 = (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]; UA_SERVICECOUNTER_OFFSET(readCount); break; case UA_NS0ID_WRITEREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_Write; *requestType = &UA_TYPES[UA_TYPES_WRITEREQUEST]; *responseType = &UA_TYPES[UA_TYPES_WRITERESPONSE]; UA_SERVICECOUNTER_OFFSET(writeCount); break; case UA_NS0ID_BROWSEREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_Browse; *requestType = &UA_TYPES[UA_TYPES_BROWSEREQUEST]; *responseType = &UA_TYPES[UA_TYPES_BROWSERESPONSE]; UA_SERVICECOUNTER_OFFSET(browseCount); break; case UA_NS0ID_BROWSENEXTREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_BrowseNext; *requestType = &UA_TYPES[UA_TYPES_BROWSENEXTREQUEST]; *responseType = &UA_TYPES[UA_TYPES_BROWSENEXTRESPONSE]; UA_SERVICECOUNTER_OFFSET(browseNextCount); break; case UA_NS0ID_REGISTERNODESREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_RegisterNodes; *requestType = &UA_TYPES[UA_TYPES_REGISTERNODESREQUEST]; *responseType = &UA_TYPES[UA_TYPES_REGISTERNODESRESPONSE]; UA_SERVICECOUNTER_OFFSET(registerNodesCount); break; case UA_NS0ID_UNREGISTERNODESREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_UnregisterNodes; *requestType = &UA_TYPES[UA_TYPES_UNREGISTERNODESREQUEST]; *responseType = &UA_TYPES[UA_TYPES_UNREGISTERNODESRESPONSE]; UA_SERVICECOUNTER_OFFSET(unregisterNodesCount); break; case UA_NS0ID_TRANSLATEBROWSEPATHSTONODEIDSREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_TranslateBrowsePathsToNodeIds; *requestType = &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSREQUEST]; *responseType = &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE]; UA_SERVICECOUNTER_OFFSET(translateBrowsePathsToNodeIdsCount); 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]; UA_SERVICECOUNTER_OFFSET(createSubscriptionCount); break; case UA_NS0ID_PUBLISHREQUEST_ENCODING_DEFAULTBINARY: *requestType = &UA_TYPES[UA_TYPES_PUBLISHREQUEST]; *responseType = &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]; UA_SERVICECOUNTER_OFFSET(publishCount); break; case UA_NS0ID_REPUBLISHREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_Republish; *requestType = &UA_TYPES[UA_TYPES_REPUBLISHREQUEST]; *responseType = &UA_TYPES[UA_TYPES_REPUBLISHRESPONSE]; UA_SERVICECOUNTER_OFFSET(republishCount); break; case UA_NS0ID_MODIFYSUBSCRIPTIONREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_ModifySubscription; *requestType = &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONREQUEST]; *responseType = &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONRESPONSE]; UA_SERVICECOUNTER_OFFSET(modifySubscriptionCount); break; case UA_NS0ID_SETPUBLISHINGMODEREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_SetPublishingMode; *requestType = &UA_TYPES[UA_TYPES_SETPUBLISHINGMODEREQUEST]; *responseType = &UA_TYPES[UA_TYPES_SETPUBLISHINGMODERESPONSE]; UA_SERVICECOUNTER_OFFSET(setPublishingModeCount); break; case UA_NS0ID_DELETESUBSCRIPTIONSREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_DeleteSubscriptions; *requestType = &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSREQUEST]; *responseType = &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSRESPONSE]; UA_SERVICECOUNTER_OFFSET(deleteSubscriptionsCount); break; case UA_NS0ID_TRANSFERSUBSCRIPTIONSREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_TransferSubscriptions; *requestType = &UA_TYPES[UA_TYPES_TRANSFERSUBSCRIPTIONSREQUEST]; *responseType = &UA_TYPES[UA_TYPES_TRANSFERSUBSCRIPTIONSRESPONSE]; UA_SERVICECOUNTER_OFFSET(transferSubscriptionsCount); break; case UA_NS0ID_CREATEMONITOREDITEMSREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_CreateMonitoredItems; *requestType = &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSREQUEST]; *responseType = &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSRESPONSE]; UA_SERVICECOUNTER_OFFSET(createMonitoredItemsCount); break; case UA_NS0ID_DELETEMONITOREDITEMSREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_DeleteMonitoredItems; *requestType = &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSREQUEST]; *responseType = &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSRESPONSE]; UA_SERVICECOUNTER_OFFSET(deleteMonitoredItemsCount); break; case UA_NS0ID_MODIFYMONITOREDITEMSREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_ModifyMonitoredItems; *requestType = &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSREQUEST]; *responseType = &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSRESPONSE]; UA_SERVICECOUNTER_OFFSET(modifyMonitoredItemsCount); break; case UA_NS0ID_SETMONITORINGMODEREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_SetMonitoringMode; *requestType = &UA_TYPES[UA_TYPES_SETMONITORINGMODEREQUEST]; *responseType = &UA_TYPES[UA_TYPES_SETMONITORINGMODERESPONSE]; UA_SERVICECOUNTER_OFFSET(setMonitoringModeCount); break; case UA_NS0ID_SETTRIGGERINGREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_SetTriggering; *requestType = &UA_TYPES[UA_TYPES_SETTRIGGERINGREQUEST]; *responseType = &UA_TYPES[UA_TYPES_SETTRIGGERINGRESPONSE]; UA_SERVICECOUNTER_OFFSET(setTriggeringCount); 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]; UA_SERVICECOUNTER_OFFSET(historyReadCount); 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]; UA_SERVICECOUNTER_OFFSET(historyUpdateCount); 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]; UA_SERVICECOUNTER_OFFSET(callCount); 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]; UA_SERVICECOUNTER_OFFSET(addNodesCount); break; case UA_NS0ID_ADDREFERENCESREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_AddReferences; *requestType = &UA_TYPES[UA_TYPES_ADDREFERENCESREQUEST]; *responseType = &UA_TYPES[UA_TYPES_ADDREFERENCESRESPONSE]; UA_SERVICECOUNTER_OFFSET(addReferencesCount); break; case UA_NS0ID_DELETENODESREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_DeleteNodes; *requestType = &UA_TYPES[UA_TYPES_DELETENODESREQUEST]; *responseType = &UA_TYPES[UA_TYPES_DELETENODESRESPONSE]; UA_SERVICECOUNTER_OFFSET(deleteNodesCount); break; case UA_NS0ID_DELETEREFERENCESREQUEST_ENCODING_DEFAULTBINARY: *service = (UA_Service)Service_DeleteReferences; *requestType = &UA_TYPES[UA_TYPES_DELETEREFERENCESREQUEST]; *responseType = &UA_TYPES[UA_TYPES_DELETEREFERENCESRESPONSE]; UA_SERVICECOUNTER_OFFSET(deleteReferencesCount); break; #endif default: break; } } /*************************/ /* Process Message Types */ /*************************/ /* HEL -> Open up the connection */ static UA_StatusCode processHEL(UA_Server *server, UA_SecureChannel *channel, const UA_ByteString *msg) { if(channel->state != UA_SECURECHANNELSTATE_FRESH) return UA_STATUSCODE_BADINTERNALERROR; size_t offset = 0; /* Go to the beginning of the TcpHelloMessage */ UA_TcpHelloMessage helloMessage; UA_StatusCode retval = UA_decodeBinaryInternal(msg, &offset, &helloMessage, &UA_TRANSPORT[UA_TRANSPORT_TCPHELLOMESSAGE], NULL); if(retval != UA_STATUSCODE_GOOD) return retval; /* Currently not checked */ UA_String_clear(&helloMessage.endpointUrl); /* Parameterize the connection. The TcpHelloMessage casts to a * TcpAcknowledgeMessage. */ retval = UA_SecureChannel_processHELACK(channel, (UA_TcpAcknowledgeMessage*)&helloMessage); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_NETWORK, "Connection %i | Error during the HEL/ACK handshake", (int)(channel->connection->sockfd)); return retval; } /* Get the send buffer from the network layer */ UA_Connection *connection = channel->connection; UA_ByteString ack_msg; UA_ByteString_init(&ack_msg); retval = connection->getSendBuffer(connection, channel->config.sendBufferSize, &ack_msg); if(retval != UA_STATUSCODE_GOOD) return retval; /* Build acknowledge response */ UA_TcpAcknowledgeMessage ackMessage; ackMessage.protocolVersion = 0; ackMessage.receiveBufferSize = channel->config.recvBufferSize; ackMessage.sendBufferSize = channel->config.sendBufferSize; ackMessage.maxMessageSize = channel->config.localMaxMessageSize; ackMessage.maxChunkCount = channel->config.localMaxChunkCount; UA_TcpMessageHeader ackHeader; ackHeader.messageTypeAndChunkType = UA_MESSAGETYPE_ACK + UA_CHUNKTYPE_FINAL; ackHeader.messageSize = 8 + 20; /* ackHeader + ackMessage */ /* Encode and send the response */ UA_Byte *bufPos = ack_msg.data; const UA_Byte *bufEnd = &ack_msg.data[ack_msg.length]; retval |= UA_encodeBinaryInternal(&ackHeader, &UA_TRANSPORT[UA_TRANSPORT_TCPMESSAGEHEADER], &bufPos, &bufEnd, NULL, NULL); retval |= UA_encodeBinaryInternal(&ackMessage, &UA_TRANSPORT[UA_TRANSPORT_TCPACKNOWLEDGEMESSAGE], &bufPos, &bufEnd, NULL, NULL); if(retval != UA_STATUSCODE_GOOD) { connection->releaseSendBuffer(connection, &ack_msg); return retval; } ack_msg.length = ackHeader.messageSize; retval = connection->send(connection, &ack_msg); if(retval == UA_STATUSCODE_GOOD) channel->state = UA_SECURECHANNELSTATE_ACK_SENT; return retval; } /* OPN -> Open up/renew the securechannel */ static UA_StatusCode processOPN(UA_Server *server, UA_SecureChannel *channel, const UA_UInt32 requestId, const UA_ByteString *msg) { if(channel->state != UA_SECURECHANNELSTATE_ACK_SENT && channel->state != UA_SECURECHANNELSTATE_OPEN) return UA_STATUSCODE_BADINTERNALERROR; /* Decode the request */ UA_NodeId requestType; UA_OpenSecureChannelRequest openSecureChannelRequest; size_t offset = 0; UA_StatusCode retval = UA_NodeId_decodeBinary(msg, &offset, &requestType); if(retval != UA_STATUSCODE_GOOD) { UA_NodeId_clear(&requestType); UA_LOG_WARNING_CHANNEL(&server->config.logger, channel, "Could not decode the NodeId. Closing the connection"); UA_Server_closeSecureChannel(server, channel, UA_DIAGNOSTICEVENT_REJECT); return retval; } retval = UA_decodeBinaryInternal(msg, &offset, &openSecureChannelRequest, &UA_TYPES[UA_TYPES_OPENSECURECHANNELREQUEST], NULL); /* Error occurred */ if(retval != UA_STATUSCODE_GOOD || !UA_NodeId_equal(&requestType, &UA_TYPES[UA_TYPES_OPENSECURECHANNELREQUEST].binaryEncodingId)) { UA_NodeId_clear(&requestType); UA_OpenSecureChannelRequest_clear(&openSecureChannelRequest); UA_LOG_WARNING_CHANNEL(&server->config.logger, channel, "Could not decode the OPN message. Closing the connection."); UA_Server_closeSecureChannel(server, channel, UA_DIAGNOSTICEVENT_REJECT); return retval; } UA_NodeId_clear(&requestType); /* Call the service */ UA_OpenSecureChannelResponse openScResponse; UA_OpenSecureChannelResponse_init(&openScResponse); Service_OpenSecureChannel(server, channel, &openSecureChannelRequest, &openScResponse); UA_OpenSecureChannelRequest_clear(&openSecureChannelRequest); if(openScResponse.responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_LOG_WARNING_CHANNEL(&server->config.logger, channel, "Could not open a SecureChannel. " "Closing the connection."); UA_Server_closeSecureChannel(server, channel, UA_DIAGNOSTICEVENT_REJECT); return openScResponse.responseHeader.serviceResult; } /* Send the response */ retval = UA_SecureChannel_sendAsymmetricOPNMessage(channel, requestId, &openScResponse, &UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE]); UA_OpenSecureChannelResponse_clear(&openScResponse); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING_CHANNEL(&server->config.logger, channel, "Could not send the OPN answer with error code %s", UA_StatusCode_name(retval)); UA_Server_closeSecureChannel(server, channel, UA_DIAGNOSTICEVENT_REJECT); } return retval; } /* The responseHeader must have the requestHandle already set */ UA_StatusCode sendResponse(UA_Server *server, UA_Session *session, UA_SecureChannel *channel, UA_UInt32 requestId, UA_Response *response, const UA_DataType *responseType) { if(!channel) return UA_STATUSCODE_BADINTERNALERROR; /* If the overall service call failed, answer with a ServiceFault */ if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) return sendServiceFault(channel, requestId, response->responseHeader.requestHandle, response->responseHeader.serviceResult); /* Prepare the ResponseHeader */ response->responseHeader.timestamp = UA_DateTime_now(); if(session) { #ifdef UA_ENABLE_TYPEDESCRIPTION UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Sending response for RequestId %u of type %s", (unsigned)requestId, responseType->typeName); #else UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Sending reponse for RequestId %u of type %" PRIu32, (unsigned)requestId, responseType->binaryEncodingId.identifier.numeric); #endif } else { #ifdef UA_ENABLE_TYPEDESCRIPTION UA_LOG_DEBUG_CHANNEL(&server->config.logger, channel, "Sending response for RequestId %u of type %s", (unsigned)requestId, responseType->typeName); #else UA_LOG_DEBUG_CHANNEL(&server->config.logger, channel, "Sending reponse for RequestId %u of type %" PRIu32, (unsigned)requestId, responseType->binaryEncodingId.identifier.numeric); #endif } /* 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_SECURECHANNEL_SYMMETRIC_HEADER_TOTALLENGTH]); UA_assert(mc.buf_end <= &mc.messageBuffer.data[mc.messageBuffer.length]); /* Encode the response type */ retval = UA_MessageContext_encode(&mc, &responseType->binaryEncodingId, &UA_TYPES[UA_TYPES_NODEID]); if(retval != UA_STATUSCODE_GOOD) return retval; /* Encode the response */ retval = UA_MessageContext_encode(&mc, response, responseType); if(retval != UA_STATUSCODE_GOOD) return retval; /* Finish / send out */ return UA_MessageContext_finish(&mc); } /* A Session is "bound" to a SecureChannel if it was created by the * SecureChannel or if it was activated on it. A Session can only be bound to * one SecureChannel. A Session can only be closed from the SecureChannel to * which it is bound. * * Returns Good if the AuthenticationToken exists nowhere (for CTT). */ UA_StatusCode getBoundSession(UA_Server *server, const UA_SecureChannel *channel, const UA_NodeId *token, UA_Session **session) { UA_DateTime now = UA_DateTime_nowMonotonic(); UA_SessionHeader *sh; SLIST_FOREACH(sh, &channel->sessions, next) { if(!UA_NodeId_equal(token, &sh->authenticationToken)) continue; UA_Session *current = (UA_Session*)sh; /* Has the session timed out? */ if(current->validTill < now) { server->serverDiagnosticsSummary.rejectedSessionCount++; return UA_STATUSCODE_BADSESSIONCLOSED; } *session = current; return UA_STATUSCODE_GOOD; } server->serverDiagnosticsSummary.rejectedSessionCount++; /* Session exists on another SecureChannel. The CTT expect this error. */ UA_Session *tmpSession = getSessionByToken(server, token); if(tmpSession) { #ifdef UA_ENABLE_DIAGNOSTICS tmpSession->diagnostics.unauthorizedRequestCount++; #endif return UA_STATUSCODE_BADSECURECHANNELIDINVALID; } return UA_STATUSCODE_GOOD; } static const UA_String securityPolicyNone = UA_STRING_STATIC("http://opcfoundation.org/UA/SecurityPolicy#None"); /* Returns a status of the SecureChannel. The detailed service status (usually * part of the response) is set in the serviceResult argument. */ static UA_StatusCode processMSGDecoded(UA_Server *server, UA_SecureChannel *channel, UA_UInt32 requestId, UA_Service service, const UA_Request *request, const UA_DataType *requestType, UA_Response *response, const UA_DataType *responseType, UA_Boolean sessionRequired, size_t counterOffset) { UA_Session *session = NULL; UA_StatusCode channelRes = UA_STATUSCODE_GOOD; UA_StatusCode serviceRes = UA_STATUSCODE_GOOD; const UA_RequestHeader *requestHeader = &request->requestHeader; /* If it is an unencrypted (#None) channel, only allow the discovery services */ if(server->config.securityPolicyNoneDiscoveryOnly && UA_String_equal(&channel->securityPolicy->policyUri, &securityPolicyNone ) && requestType != &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST] && requestType != &UA_TYPES[UA_TYPES_FINDSERVERSREQUEST] #if defined(UA_ENABLE_DISCOVERY) && defined(UA_ENABLE_DISCOVERY_MULTICAST) && requestType != &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKREQUEST] #endif ) { serviceRes = UA_STATUSCODE_BADSECURITYPOLICYREJECTED; channelRes = sendServiceFault(channel, requestId, requestHeader->requestHandle, UA_STATUSCODE_BADSECURITYPOLICYREJECTED); goto update_statistics; } /* Session lifecycle services. */ if(requestType == &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST] || requestType == &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST] || requestType == &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST]) { UA_LOCK(&server->serviceMutex); ((UA_ChannelService)service)(server, channel, request, response); UA_UNLOCK(&server->serviceMutex); #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION /* Store the authentication token so we can help fuzzing by setting * these values in the next request automatically */ if(requestType == &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST]) { UA_CreateSessionResponse *res = &response->createSessionResponse; UA_NodeId_copy(&res->authenticationToken, &unsafe_fuzz_authenticationToken); } #endif serviceRes = response->responseHeader.serviceResult; channelRes = sendResponse(server, NULL, channel, requestId, response, responseType); goto update_statistics; } /* Get the Session bound to the SecureChannel (not necessarily activated) */ if(!UA_NodeId_isNull(&requestHeader->authenticationToken)) { UA_LOCK(&server->serviceMutex); UA_StatusCode retval = getBoundSession(server, channel, &requestHeader->authenticationToken, &session); UA_UNLOCK(&server->serviceMutex); if(retval != UA_STATUSCODE_GOOD) { serviceRes = response->responseHeader.serviceResult; channelRes = sendServiceFault(channel, requestId, requestHeader->requestHandle, retval); goto update_statistics; } } /* Set an anonymous, inactive session for services that need no session */ UA_Session anonymousSession; if(!session) { if(sessionRequired) { #ifdef UA_ENABLE_TYPEDESCRIPTION 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 %" PRIu32 " refused without a valid session", requestType->binaryEncodingId.identifier.numeric); #endif serviceRes = UA_STATUSCODE_BADSESSIONIDINVALID; channelRes = sendServiceFault(channel, requestId, requestHeader->requestHandle, UA_STATUSCODE_BADSESSIONIDINVALID); goto update_statistics; } UA_Session_init(&anonymousSession); anonymousSession.sessionId = UA_NODEID_GUID(0, UA_GUID_NULL); anonymousSession.header.channel = channel; session = &anonymousSession; } UA_assert(session != NULL); /* Trying to use a non-activated session? */ if(sessionRequired && !session->activated) { #ifdef UA_ENABLE_TYPEDESCRIPTION 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 %" PRIu32 " refused on a non-activated session", requestType->binaryEncodingId.identifier.numeric); #endif if(session != &anonymousSession) { UA_LOCK(&server->serviceMutex); UA_Server_removeSessionByToken(server, &session->header.authenticationToken, UA_DIAGNOSTICEVENT_ABORT); UA_UNLOCK(&server->serviceMutex); } serviceRes = UA_STATUSCODE_BADSESSIONNOTACTIVATED; channelRes = sendServiceFault(channel, requestId, requestHeader->requestHandle, UA_STATUSCODE_BADSESSIONNOTACTIVATED); goto update_statistics; } /* 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]) { UA_LOCK(&server->serviceMutex); serviceRes = Service_Publish(server, session, &request->publishRequest, requestId); /* No channelRes due to the async response */ UA_UNLOCK(&server->serviceMutex); goto update_statistics; } #endif #if UA_MULTITHREADING >= 100 /* The call request might not be answered immediately */ if(requestType == &UA_TYPES[UA_TYPES_CALLREQUEST]) { UA_Boolean finished = true; UA_LOCK(&server->serviceMutex); Service_CallAsync(server, session, requestId, &request->callRequest, &response->callResponse, &finished); UA_UNLOCK(&server->serviceMutex); /* Async method calls remain. Don't send a response now. In case we have * an async call, count as a "good" request for the diagnostics * statistic. */ if(UA_LIKELY(finished)) { serviceRes = response->responseHeader.serviceResult; channelRes = sendResponse(server, session, channel, requestId, response, responseType); } goto update_statistics; } #endif /* Execute the synchronous service call */ UA_LOCK(&server->serviceMutex); service(server, session, request, response); UA_UNLOCK(&server->serviceMutex); /* Send the response */ serviceRes = response->responseHeader.serviceResult; channelRes = sendResponse(server, session, channel, requestId, response, responseType); /* Update the diagnostics statistics */ update_statistics: #ifdef UA_ENABLE_DIAGNOSTICS if(session && session != &server->adminSession) { session->diagnostics.totalRequestCount.totalCount++; if(serviceRes != UA_STATUSCODE_GOOD) session->diagnostics.totalRequestCount.errorCount++; if(counterOffset != 0) { UA_ServiceCounterDataType *serviceCounter = (UA_ServiceCounterDataType*) (((uintptr_t)&session->diagnostics) + counterOffset); serviceCounter->totalCount++; if(serviceRes != UA_STATUSCODE_GOOD) serviceCounter->errorCount++; } } #else (void)serviceRes; /* Pacify compiler warnings */ #endif return channelRes; } static UA_StatusCode processMSG(UA_Server *server, UA_SecureChannel *channel, UA_UInt32 requestId, const UA_ByteString *msg) { if(channel->state != UA_SECURECHANNELSTATE_OPEN) return UA_STATUSCODE_BADINTERNALERROR; /* 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_clear(&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; size_t counterOffset = 0; getServicePointers(requestTypeId.identifier.numeric, &requestType, &responseType, &service, &sessionRequired, &counterOffset); if(!requestType) { if(requestTypeId.identifier.numeric == UA_NS0ID_CREATESUBSCRIPTIONREQUEST_ENCODING_DEFAULTBINARY) { 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 %" PRIi32, requestTypeId.identifier.numeric); } return decodeHeaderSendServiceFault(channel, msg, requestPos, &UA_TYPES[UA_TYPES_SERVICEFAULT], requestId, UA_STATUSCODE_BADSERVICEUNSUPPORTED); } UA_assert(responseType); /* Decode the request */ UA_Request request; retval = UA_decodeBinaryInternal(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 with StatusCode %s", UA_StatusCode_name(retval)); return decodeHeaderSendServiceFault(channel, msg, requestPos, responseType, requestId, retval); } /* Check timestamp in the request header */ UA_RequestHeader *requestHeader = &request.requestHeader; 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 = sendServiceFault(channel, requestId, requestHeader->requestHandle, UA_STATUSCODE_BADINVALIDTIMESTAMP); UA_clear(&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 */ if(!UA_NodeId_isNull(&unsafe_fuzz_authenticationToken) && !UA_NodeId_isNull(&requestHeader->authenticationToken)) { UA_NodeId_clear(&requestHeader->authenticationToken); UA_NodeId_copy(&unsafe_fuzz_authenticationToken, &requestHeader->authenticationToken); } #endif /* Prepare the respone and process the request */ UA_Response response; UA_init(&response, responseType); response.responseHeader.requestHandle = requestHeader->requestHandle; retval = processMSGDecoded(server, channel, requestId, service, &request, requestType, &response, responseType, sessionRequired, counterOffset); /* Clean up */ UA_clear(&request, requestType); UA_clear(&response, responseType); return retval; } /* Takes decoded messages starting at the nodeid of the content type. */ static UA_StatusCode processSecureChannelMessage(void *application, UA_SecureChannel *channel, UA_MessageType messagetype, UA_UInt32 requestId, UA_ByteString *message) { UA_Server *server = (UA_Server*)application; UA_StatusCode retval = UA_STATUSCODE_GOOD; switch(messagetype) { case UA_MESSAGETYPE_HEL: UA_LOG_TRACE_CHANNEL(&server->config.logger, channel, "Process a HEL message"); retval = processHEL(server, channel, message); break; case UA_MESSAGETYPE_OPN: UA_LOG_TRACE_CHANNEL(&server->config.logger, channel, "Process an OPN message"); 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); /* Regular close */ break; default: UA_LOG_TRACE_CHANNEL(&server->config.logger, channel, "Invalid message type"); retval = UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; break; } if(retval != UA_STATUSCODE_GOOD) { if(!channel->connection) { UA_LOG_INFO_CHANNEL(&server->config.logger, channel, "Processing the message failed. Channel already closed " "with StatusCode %s. ", UA_StatusCode_name(retval)); return retval; } UA_LOG_INFO_CHANNEL(&server->config.logger, channel, "Processing the message failed with StatusCode %s. " "Closing the channel.", UA_StatusCode_name(retval)); UA_TcpErrorMessage errMsg; UA_TcpErrorMessage_init(&errMsg); errMsg.error = retval; UA_Connection_sendError(channel->connection, &errMsg); switch(retval) { case UA_STATUSCODE_BADSECURITYMODEREJECTED: case UA_STATUSCODE_BADSECURITYCHECKSFAILED: case UA_STATUSCODE_BADSECURECHANNELIDINVALID: case UA_STATUSCODE_BADSECURECHANNELTOKENUNKNOWN: case UA_STATUSCODE_BADSECURITYPOLICYREJECTED: case UA_STATUSCODE_BADCERTIFICATEUSENOTALLOWED: UA_Server_closeSecureChannel(server, channel, UA_DIAGNOSTICEVENT_SECURITYREJECT); break; default: UA_Server_closeSecureChannel(server, channel, UA_DIAGNOSTICEVENT_CLOSE); break; } } return retval; } 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)); UA_TcpErrorMessage error; UA_StatusCode retval = UA_STATUSCODE_GOOD; UA_SecureChannel *channel = connection->channel; /* Add a SecureChannel to a new connection */ if(!channel) { retval = UA_Server_createSecureChannel(server, connection); if(retval != UA_STATUSCODE_GOOD) goto error; channel = connection->channel; UA_assert(channel); } #ifdef UA_DEBUG_DUMP_PKGS UA_dump_hex_pkg(message->data, message->length); #endif #ifdef UA_DEBUG_DUMP_PKGS_FILE UA_debug_dumpCompleteChunk(server, channel->connection, message); #endif retval = UA_SecureChannel_processBuffer(channel, server, processSecureChannelMessage, 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)); goto error; } return; error: /* Send an ERR message and close the connection */ error.error = retval; error.reason = UA_STRING_NULL; UA_Connection_sendError(connection, &error); connection->close(connection); } void UA_Server_removeConnection(UA_Server *server, UA_Connection *connection) { UA_Connection_detachSecureChannel(connection); connection->free(connection); } /**** amalgamated original file "/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 */ const UA_DataType * UA_Server_findDataType(UA_Server *server, const UA_NodeId *typeId) { return UA_findDataTypeWithCustom(typeId, server->config.customDataTypes); } /********************************/ /* Information Model Operations */ /********************************/ const UA_Node * getNodeType(UA_Server *server, const UA_NodeHead *head) { /* The reference to the parent is different for variable and variabletype */ UA_Byte parentRefIndex; UA_Boolean inverse; UA_NodeClass typeNodeClass; switch(head->nodeClass) { case UA_NODECLASS_OBJECT: parentRefIndex = UA_REFERENCETYPEINDEX_HASTYPEDEFINITION; inverse = false; typeNodeClass = UA_NODECLASS_OBJECTTYPE; break; case UA_NODECLASS_VARIABLE: parentRefIndex = UA_REFERENCETYPEINDEX_HASTYPEDEFINITION; inverse = false; typeNodeClass = UA_NODECLASS_VARIABLETYPE; break; case UA_NODECLASS_OBJECTTYPE: case UA_NODECLASS_VARIABLETYPE: case UA_NODECLASS_REFERENCETYPE: case UA_NODECLASS_DATATYPE: parentRefIndex = UA_REFERENCETYPEINDEX_HASSUBTYPE; inverse = true; typeNodeClass = head->nodeClass; break; default: return NULL; } /* Return the first matching candidate */ for(size_t i = 0; i < head->referencesSize; ++i) { UA_NodeReferenceKind *rk = &head->references[i]; if(rk->isInverse != inverse) continue; if(rk->referenceTypeIndex != parentRefIndex) continue; const UA_ReferenceTarget *t = NULL; while((t = UA_NodeReferenceKind_iterate(rk, t))) { const UA_Node *type = UA_NODESTORE_GETFROMREF(server, t->targetId); if(!type) continue; if(type->head.nodeClass == typeNodeClass) return type; /* Don't release the node that is returned */ UA_NODESTORE_RELEASE(server, type); } } return NULL; } UA_Boolean UA_Node_hasSubTypeOrInstances(const UA_NodeHead *head) { for(size_t i = 0; i < head->referencesSize; ++i) { if(head->references[i].isInverse == false && head->references[i].referenceTypeIndex == UA_REFERENCETYPEINDEX_HASSUBTYPE) return true; if(head->references[i].isInverse == true && head->references[i].referenceTypeIndex == UA_REFERENCETYPEINDEX_HASTYPEDEFINITION) return true; } return false; } UA_StatusCode getParentTypeAndInterfaceHierarchy(UA_Server *server, const UA_NodeId *typeNode, UA_NodeId **typeHierarchy, size_t *typeHierarchySize) { UA_ReferenceTypeSet reftypes_subtype = UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASSUBTYPE); UA_ExpandedNodeId *subTypes = NULL; size_t subTypesSize = 0; UA_StatusCode retval = browseRecursive(server, 1, typeNode, UA_BROWSEDIRECTION_INVERSE, &reftypes_subtype, UA_NODECLASS_UNSPECIFIED, false, &subTypesSize, &subTypes); if(retval != UA_STATUSCODE_GOOD) return retval; UA_assert(subTypesSize < 1000); UA_ReferenceTypeSet reftypes_interface = UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASINTERFACE); UA_ExpandedNodeId *interfaces = NULL; size_t interfacesSize = 0; retval = browseRecursive(server, 1, typeNode, UA_BROWSEDIRECTION_FORWARD, &reftypes_interface, UA_NODECLASS_UNSPECIFIED, 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; } UA_StatusCode getAllInterfaceChildNodeIds(UA_Server *server, const UA_NodeId *objectNode, const UA_NodeId *objectTypeNode, UA_NodeId **interfaceChildNodes, size_t *interfaceChildNodesSize) { if(interfaceChildNodesSize == NULL || interfaceChildNodes == NULL) return UA_STATUSCODE_BADINTERNALERROR; *interfaceChildNodesSize = 0; *interfaceChildNodes = NULL; UA_ExpandedNodeId *hasInterfaceCandidates = NULL; size_t hasInterfaceCandidatesSize = 0; UA_ReferenceTypeSet reftypes_subtype = UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASSUBTYPE); UA_StatusCode retval = browseRecursive(server, 1, objectTypeNode, UA_BROWSEDIRECTION_INVERSE, &reftypes_subtype, UA_NODECLASS_OBJECTTYPE, true, &hasInterfaceCandidatesSize, &hasInterfaceCandidates); if(retval != UA_STATUSCODE_GOOD) return retval; /* The interface could also have been added manually before calling UA_Server_addNode_finish * This can be handled by adding the object node as a start node for the HasInterface lookup */ UA_ExpandedNodeId *resizedHasInterfaceCandidates = (UA_ExpandedNodeId*) UA_realloc(hasInterfaceCandidates, (hasInterfaceCandidatesSize + 1) * sizeof(UA_ExpandedNodeId)); if(!resizedHasInterfaceCandidates) { if(hasInterfaceCandidates) UA_Array_delete(hasInterfaceCandidates, hasInterfaceCandidatesSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); return UA_STATUSCODE_BADOUTOFMEMORY; } hasInterfaceCandidates = resizedHasInterfaceCandidates; hasInterfaceCandidatesSize += 1; UA_ExpandedNodeId_init(&hasInterfaceCandidates[hasInterfaceCandidatesSize - 1]); UA_ExpandedNodeId_init(&hasInterfaceCandidates[hasInterfaceCandidatesSize - 1]); UA_NodeId_copy(objectNode, &hasInterfaceCandidates[hasInterfaceCandidatesSize - 1].nodeId); size_t outputIndex = 0; for(size_t i = 0; i < hasInterfaceCandidatesSize; ++i) { UA_ReferenceTypeSet reftypes_interface = UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASINTERFACE); UA_ExpandedNodeId *interfaceChildren = NULL; size_t interfacesChildrenSize = 0; retval = browseRecursive(server, 1, &hasInterfaceCandidates[i].nodeId, UA_BROWSEDIRECTION_FORWARD, &reftypes_interface, UA_NODECLASS_OBJECTTYPE, false, &interfacesChildrenSize, &interfaceChildren); if(retval != UA_STATUSCODE_GOOD) { UA_Array_delete(hasInterfaceCandidates, hasInterfaceCandidatesSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); if(*interfaceChildNodesSize) { UA_Array_delete(*interfaceChildNodes, *interfaceChildNodesSize, &UA_TYPES[UA_TYPES_NODEID]); *interfaceChildNodesSize = 0; } return retval; } UA_assert(interfacesChildrenSize < 1000); if(interfacesChildrenSize == 0) { continue; } if(!*interfaceChildNodes) { *interfaceChildNodes = (UA_NodeId*) UA_calloc(interfacesChildrenSize, sizeof(UA_NodeId)); *interfaceChildNodesSize = interfacesChildrenSize; if(!*interfaceChildNodes) { UA_Array_delete(interfaceChildren, interfacesChildrenSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); UA_Array_delete(hasInterfaceCandidates, hasInterfaceCandidatesSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); return UA_STATUSCODE_BADOUTOFMEMORY; } } else { UA_NodeId *resizedInterfaceChildNodes = (UA_NodeId*) UA_realloc(*interfaceChildNodes, ((*interfaceChildNodesSize + interfacesChildrenSize) * sizeof(UA_NodeId))); if(!resizedInterfaceChildNodes) { UA_Array_delete(hasInterfaceCandidates, hasInterfaceCandidatesSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); UA_Array_delete(interfaceChildren, interfacesChildrenSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); return UA_STATUSCODE_BADOUTOFMEMORY; } const size_t oldSize = *interfaceChildNodesSize; *interfaceChildNodesSize += interfacesChildrenSize; *interfaceChildNodes = resizedInterfaceChildNodes; for(size_t j = oldSize; j < *interfaceChildNodesSize; ++j) UA_NodeId_init(&(*interfaceChildNodes)[j]); } for(size_t j = 0; j < interfacesChildrenSize; j++) { (*interfaceChildNodes)[outputIndex++] = interfaceChildren[j].nodeId; } UA_assert(*interfaceChildNodesSize < 1000); UA_Array_delete(interfaceChildren, interfacesChildrenSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); } UA_Array_delete(hasInterfaceCandidates, hasInterfaceCandidatesSize, &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_GET(server, nodeId); if(!node) return UA_STATUSCODE_BADNODEIDUNKNOWN; UA_StatusCode retval = callback(server, session, (UA_Node*)(uintptr_t)node, data); UA_NODESTORE_RELEASE(server, node); return retval; #else UA_StatusCode retval; do { /* Get an editable copy of the node */ UA_Node *node; retval = UA_NODESTORE_GETCOPY(server, 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_DELETE(server, node); return retval; } /* Replace the node */ retval = UA_NODESTORE_REPLACE(server, 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 "/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 * Copyright 2017 (c) HMS Industrial Networks AB (Author: Jonas Green) */ #ifdef UA_ENABLE_DISCOVERY UA_StatusCode register_server_with_discovery_server(UA_Server *server, void *pClient, const UA_Boolean isUnregister, const char* semaphoreFilePath) { UA_Client *client = (UA_Client *) pClient; /* 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; /* Mirror 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; request.server.discoveryUrls = (UA_String*) UA_Array_new(total_discurls, &UA_TYPES[UA_TYPES_STRING]); if(!request.server.discoveryUrls) return UA_STATUSCODE_BADOUTOFMEMORY; 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; } request.server.discoveryUrlsSize = total_discurls; #ifdef UA_ENABLE_DISCOVERY_MULTICAST request.discoveryConfigurationSize = 1; request.discoveryConfiguration = UA_ExtensionObject_new(); // Set to NODELETE so that we can just use a pointer to the mdns config UA_ExtensionObject_setValueNoDelete(request.discoveryConfiguration, &server->config.mdnsConfig, &UA_TYPES[UA_TYPES_MDNSDISCOVERYCONFIGURATION]); #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_clear(&response); UA_Array_delete(request.discoveryConfiguration, request.discoveryConfigurationSize, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]); if(total_discurls > 0) UA_free(request.server.discoveryUrls); 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_clear(&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) { UA_LOCK(&server->serviceMutex); UA_StatusCode retval = register_server_with_discovery_server(server, client, false, semaphoreFilePath); UA_UNLOCK(&server->serviceMutex); return retval; } UA_StatusCode UA_Server_unregister_discovery(UA_Server *server, UA_Client *client) { UA_LOCK(&server->serviceMutex); UA_StatusCode retval = register_server_with_discovery_server(server, client, true, NULL); UA_UNLOCK(&server->serviceMutex); return retval; } #endif /* UA_ENABLE_DISCOVERY */ /**** amalgamated original file "/src/server/ua_server_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/. * * Copyright 2019 (c) Fraunhofer IOSB (Author: Klaus Schick) * Copyright 2019 (c) Fraunhofer IOSB (Author: Julius Pfrommer) */ #if UA_MULTITHREADING >= 100 static void UA_AsyncOperation_delete(UA_AsyncOperation *ar) { UA_CallMethodRequest_clear(&ar->request); UA_CallMethodResult_clear(&ar->response); UA_free(ar); } static UA_StatusCode UA_AsyncManager_sendAsyncResponse(UA_AsyncManager *am, UA_Server *server, UA_AsyncResponse *ar) { /* Get the session */ UA_StatusCode res = UA_STATUSCODE_GOOD; UA_LOCK(&server->serviceMutex); UA_Session* session = UA_Server_getSessionById(server, &ar->sessionId); UA_UNLOCK(&server->serviceMutex); UA_SecureChannel* channel = NULL; UA_ResponseHeader *responseHeader = NULL; if(!session) { res = UA_STATUSCODE_BADSESSIONIDINVALID; UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_Server_InsertMethodResponse: Session is gone"); goto clean_up; } /* Check the channel */ channel = session->header.channel; if(!channel) { res = UA_STATUSCODE_BADSECURECHANNELCLOSED; UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_Server_InsertMethodResponse: Channel is gone"); goto clean_up; } /* Okay, here we go, send the UA_CallResponse */ responseHeader = (UA_ResponseHeader*) &ar->response.callResponse.responseHeader; responseHeader->requestHandle = ar->requestHandle; res = sendResponse(server, session, channel, ar->requestId, (UA_Response*)&ar->response, &UA_TYPES[UA_TYPES_CALLRESPONSE]); UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_Server_SendResponse: Response for Req# %" PRIu32 " sent", ar->requestId); clean_up: /* Remove from the AsyncManager */ UA_AsyncManager_removeAsyncResponse(&server->asyncManager, ar); return res; } /* Integrate operation result in the AsyncResponse and send out the response if * it is ready. */ static void integrateOperationResult(UA_AsyncManager *am, UA_Server *server, UA_AsyncOperation *ao) { /* Grab the open request, so we can continue to construct the response */ UA_AsyncResponse *ar = ao->parent; /* Reduce the number of open results */ ar->opCountdown -= 1; UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "Return result in the server thread with %" PRIu32 " remaining", ar->opCountdown); /* Move the UA_CallMethodResult to UA_CallResponse */ ar->response.callResponse.results[ao->index] = ao->response; UA_CallMethodResult_init(&ao->response); /* Are we done with all operations? */ if(ar->opCountdown == 0) UA_AsyncManager_sendAsyncResponse(am, server, ar); } /* Process all operations in the result queue -> move content over to the * AsyncResponse. This is only done by the server thread. */ static void processAsyncResults(UA_Server *server, void *data) { UA_AsyncManager *am = &server->asyncManager; while(true) { UA_LOCK(&am->queueLock); UA_AsyncOperation *ao = TAILQ_FIRST(&am->resultQueue); if(ao) TAILQ_REMOVE(&am->resultQueue, ao, pointers); UA_UNLOCK(&am->queueLock); if(!ao) break; UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_Server_CallMethodResponse: Got Response: OKAY"); integrateOperationResult(am, server, ao); UA_AsyncOperation_delete(ao); am->opsCount--; } } /* Check if any operations have timed out */ static void checkTimeouts(UA_Server *server, void *_) { /* Timeouts are not configured */ if(server->config.asyncOperationTimeout <= 0.0) return; UA_AsyncManager *am = &server->asyncManager; const UA_DateTime tNow = UA_DateTime_now(); UA_LOCK(&am->queueLock); /* Loop over the queue of dispatched ops */ UA_AsyncOperation *op = NULL, *op_tmp = NULL; TAILQ_FOREACH_SAFE(op, &am->dispatchedQueue, pointers, op_tmp) { /* The timeout has not passed. Also for all elements following in the queue. */ if(tNow <= op->parent->timeout) break; /* Mark as timed out and put it into the result queue */ op->response.statusCode = UA_STATUSCODE_BADTIMEOUT; TAILQ_REMOVE(&am->dispatchedQueue, op, pointers); TAILQ_INSERT_TAIL(&am->resultQueue, op, pointers); UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Operation was removed due to a timeout"); } /* Loop over the queue of new ops */ TAILQ_FOREACH_SAFE(op, &am->newQueue, pointers, op_tmp) { /* The timeout has not passed. Also for all elements following in the queue. */ if(tNow <= op->parent->timeout) break; /* Mark as timed out and put it into the result queue */ op->response.statusCode = UA_STATUSCODE_BADTIMEOUT; TAILQ_REMOVE(&am->newQueue, op, pointers); TAILQ_INSERT_TAIL(&am->resultQueue, op, pointers); UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Operation was removed due to a timeout"); } UA_UNLOCK(&am->queueLock); /* Integrate async results and send out complete responses */ processAsyncResults(server, NULL); } void UA_AsyncManager_init(UA_AsyncManager *am, UA_Server *server) { memset(am, 0, sizeof(UA_AsyncManager)); TAILQ_INIT(&am->asyncResponses); TAILQ_INIT(&am->newQueue); TAILQ_INIT(&am->dispatchedQueue); TAILQ_INIT(&am->resultQueue); UA_LOCK_INIT(&am->queueLock); /* Add a regular callback for cleanup and sending finished responses at a * 100s interval. */ UA_Server_addRepeatedCallback(server, (UA_ServerCallback)checkTimeouts, NULL, 100.0, &am->checkTimeoutCallbackId); } void UA_AsyncManager_clear(UA_AsyncManager *am, UA_Server *server) { removeCallback(server, am->checkTimeoutCallbackId); UA_AsyncOperation *ar, *ar_tmp; /* Clean up queues */ UA_LOCK(&am->queueLock); TAILQ_FOREACH_SAFE(ar, &am->newQueue, pointers, ar_tmp) { TAILQ_REMOVE(&am->newQueue, ar, pointers); UA_AsyncOperation_delete(ar); } TAILQ_FOREACH_SAFE(ar, &am->dispatchedQueue, pointers, ar_tmp) { TAILQ_REMOVE(&am->dispatchedQueue, ar, pointers); UA_AsyncOperation_delete(ar); } TAILQ_FOREACH_SAFE(ar, &am->resultQueue, pointers, ar_tmp) { TAILQ_REMOVE(&am->resultQueue, ar, pointers); UA_AsyncOperation_delete(ar); } UA_UNLOCK(&am->queueLock); /* Remove responses */ UA_AsyncResponse *current, *temp; TAILQ_FOREACH_SAFE(current, &am->asyncResponses, pointers, temp) { UA_AsyncManager_removeAsyncResponse(am, current); } /* Delete all locks */ UA_LOCK_DESTROY(&am->queueLock); } UA_StatusCode UA_AsyncManager_createAsyncResponse(UA_AsyncManager *am, UA_Server *server, const UA_NodeId *sessionId, const UA_UInt32 requestId, const UA_UInt32 requestHandle, const UA_AsyncOperationType operationType, UA_AsyncResponse **outAr) { UA_AsyncResponse *newentry = (UA_AsyncResponse*)UA_calloc(1, sizeof(UA_AsyncResponse)); if(!newentry) return UA_STATUSCODE_BADOUTOFMEMORY; UA_StatusCode res = UA_NodeId_copy(sessionId, &newentry->sessionId); if(res != UA_STATUSCODE_GOOD) { UA_free(newentry); return res; } am->asyncResponsesCount += 1; newentry->requestId = requestId; newentry->requestHandle = requestHandle; newentry->timeout = UA_DateTime_now(); if(server->config.asyncOperationTimeout > 0.0) newentry->timeout += (UA_DateTime) (server->config.asyncOperationTimeout * (UA_DateTime)UA_DATETIME_MSEC); TAILQ_INSERT_TAIL(&am->asyncResponses, newentry, pointers); *outAr = newentry; return UA_STATUSCODE_GOOD; } /* Remove entry and free all allocated data */ void UA_AsyncManager_removeAsyncResponse(UA_AsyncManager *am, UA_AsyncResponse *ar) { TAILQ_REMOVE(&am->asyncResponses, ar, pointers); am->asyncResponsesCount -= 1; UA_CallResponse_clear(&ar->response.callResponse); UA_NodeId_clear(&ar->sessionId); UA_free(ar); } /* Enqueue next MethodRequest */ UA_StatusCode UA_AsyncManager_createAsyncOp(UA_AsyncManager *am, UA_Server *server, UA_AsyncResponse *ar, size_t opIndex, const UA_CallMethodRequest *opRequest) { if(server->config.maxAsyncOperationQueueSize != 0 && am->opsCount >= server->config.maxAsyncOperationQueueSize) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_Server_SetNextAsyncMethod: Queue exceeds limit (%d).", (int unsigned)server->config.maxAsyncOperationQueueSize); return UA_STATUSCODE_BADUNEXPECTEDERROR; } UA_AsyncOperation *ao = (UA_AsyncOperation*)UA_calloc(1, sizeof(UA_AsyncOperation)); if(!ao) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_Server_SetNextAsyncMethod: Mem alloc failed."); return UA_STATUSCODE_BADOUTOFMEMORY; } UA_StatusCode result = UA_CallMethodRequest_copy(opRequest, &ao->request); if(result != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_Server_SetAsyncMethodResult: UA_CallMethodRequest_copy failed."); UA_free(ao); return result; } UA_CallMethodResult_init(&ao->response); ao->index = opIndex; ao->parent = ar; UA_LOCK(&am->queueLock); TAILQ_INSERT_TAIL(&am->newQueue, ao, pointers); am->opsCount++; ar->opCountdown++; UA_UNLOCK(&am->queueLock); if(server->config.asyncOperationNotifyCallback) server->config.asyncOperationNotifyCallback(server); return UA_STATUSCODE_GOOD; } /* Get and remove next Method Call Request */ UA_Boolean UA_Server_getAsyncOperationNonBlocking(UA_Server *server, UA_AsyncOperationType *type, const UA_AsyncOperationRequest **request, void **context, UA_DateTime *timeout) { UA_AsyncManager *am = &server->asyncManager; UA_Boolean bRV = false; *type = UA_ASYNCOPERATIONTYPE_INVALID; UA_LOCK(&am->queueLock); UA_AsyncOperation *ao = TAILQ_FIRST(&am->newQueue); if(ao) { TAILQ_REMOVE(&am->newQueue, ao, pointers); TAILQ_INSERT_TAIL(&am->dispatchedQueue, ao, pointers); *type = UA_ASYNCOPERATIONTYPE_CALL; *request = (UA_AsyncOperationRequest*)&ao->request; *context = (void*)ao; if(timeout) *timeout = ao->parent->timeout; bRV = true; } UA_UNLOCK(&am->queueLock); return bRV; } /* Worker submits Method Call Response */ void UA_Server_setAsyncOperationResult(UA_Server *server, const UA_AsyncOperationResponse *response, void *context) { UA_AsyncManager *am = &server->asyncManager; UA_AsyncOperation *ao = (UA_AsyncOperation*)context; if(!ao) { /* Something went wrong. Not a good AsyncOp. */ UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_Server_SetAsyncMethodResult: Invalid context"); return; } UA_LOCK(&am->queueLock); /* See if the operation is still in the dispatched queue. Otherwise it has * been removed due to a timeout. * * TODO: Add a tree-structure for the dispatch queue. The linear lookup does * not scale. */ UA_Boolean found = false; UA_AsyncOperation *op = NULL; TAILQ_FOREACH(op, &am->dispatchedQueue, pointers) { if(op == ao) { found = true; break; } } if(!found) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_Server_SetAsyncMethodResult: The operation has timed out"); UA_UNLOCK(&am->queueLock); return; } /* Copy the result into the internal AsyncOperation */ UA_StatusCode result = UA_CallMethodResult_copy(&response->callMethodResult, &ao->response); if(result != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_Server_SetAsyncMethodResult: UA_CallMethodResult_copy failed."); ao->response.statusCode = UA_STATUSCODE_BADOUTOFMEMORY; } /* Move to the result queue */ TAILQ_REMOVE(&am->dispatchedQueue, ao, pointers); TAILQ_INSERT_TAIL(&am->resultQueue, ao, pointers); UA_UNLOCK(&am->queueLock); UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "Set the result from the worker thread"); } /******************/ /* Server Methods */ /******************/ static UA_StatusCode setMethodNodeAsync(UA_Server *server, UA_Session *session, UA_Node *node, UA_Boolean *isAsync) { if(node->head.nodeClass != UA_NODECLASS_METHOD) return UA_STATUSCODE_BADNODECLASSINVALID; node->methodNode.async = *isAsync; return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Server_setMethodNodeAsync(UA_Server *server, const UA_NodeId id, UA_Boolean isAsync) { return UA_Server_editNode(server, &server->adminSession, &id, (UA_EditNodeCallback)setMethodNodeAsync, &isAsync); } UA_StatusCode UA_Server_processServiceOperationsAsync(UA_Server *server, UA_Session *session, UA_UInt32 requestId, UA_UInt32 requestHandle, UA_AsyncServiceOperation operationCallback, const size_t *requestOperations, const UA_DataType *requestOperationsType, size_t *responseOperations, const UA_DataType *responseOperationsType, UA_AsyncResponse **ar) { size_t ops = *requestOperations; if(ops == 0) return UA_STATUSCODE_BADNOTHINGTODO; /* Allocate the response array. 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; /* Finish / dispatch the operations. This may allocate a new AsyncResponse internally */ uintptr_t respOp = (uintptr_t)*respPos; uintptr_t reqOp = *(uintptr_t*)((uintptr_t)requestOperations + sizeof(size_t)); for(size_t i = 0; i < ops; i++) { operationCallback(server, session, requestId, requestHandle, i, (void*)reqOp, (void*)respOp, ar); reqOp += requestOperationsType->memSize; respOp += responseOperationsType->memSize; } return UA_STATUSCODE_GOOD; } #endif /**** amalgamated original file "/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) * Copyright (c) 2019 Fraunhofer IOSB (Author: Andreas Ebner) */ #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_updateBufferedMessage(UA_NetworkMessageOffsetBuffer *buffer){ UA_StatusCode rv = UA_STATUSCODE_GOOD; for(size_t i = 0; i < buffer->offsetsSize; ++i) { UA_NetworkMessageOffset *nmo = &buffer->offsets[i]; const UA_Byte *bufEnd = &buffer->buffer.data[buffer->buffer.length]; UA_Byte *bufPos = &buffer->buffer.data[nmo->offset]; switch(nmo->contentType) { case UA_PUBSUB_OFFSETTYPE_DATASETMESSAGE_SEQUENCENUMBER: case UA_PUBSUB_OFFSETTYPE_NETWORKMESSAGE_SEQUENCENUMBER: rv = UA_UInt16_encodeBinary((UA_UInt16 *)nmo->offsetData.value.value->value.data, &bufPos, bufEnd); if(*((UA_UInt16 *)nmo->offsetData.value.value->value.data) < UA_UINT16_MAX){ (*((UA_UInt16 *)nmo->offsetData.value.value->value.data))++; } else { (*((UA_UInt16 *)nmo->offsetData.value.value->value.data)) = 0; } break; case UA_PUBSUB_OFFSETTYPE_PAYLOAD_DATAVALUE: rv = UA_DataValue_encodeBinary(nmo->offsetData.value.value, &bufPos, bufEnd); break; case UA_PUBSUB_OFFSETTYPE_PAYLOAD_VARIANT: rv = UA_Variant_encodeBinary(&nmo->offsetData.value.value->value, &bufPos, bufEnd); break; case UA_PUBSUB_OFFSETTYPE_PAYLOAD_RAW: rv = UA_encodeBinaryInternal(nmo->offsetData.value.value->value.data, nmo->offsetData.value.value->value.type, &bufPos, &bufEnd, NULL, NULL); break; case UA_PUBSUB_OFFSETTYPE_NETWORKMESSAGE_FIELDENCDODING: break; default: return UA_STATUSCODE_BADNOTSUPPORTED; } } return rv; } UA_StatusCode UA_NetworkMessage_updateBufferedNwMessage(UA_NetworkMessageOffsetBuffer *buffer, const UA_ByteString *src, size_t *bufferPosition){ UA_StatusCode rv = UA_STATUSCODE_GOOD; size_t payloadCounter = 0; size_t offset = 0; UA_DataSetMessage* dsm = buffer->nm->payload.dataSetPayload.dataSetMessages; //Considering one DSM in RT TODO: Clarify multiple DSM UA_DataSetMessageHeader header; size_t smallestRawOffset = UA_UINT32_MAX; for (size_t i = 0; i < buffer->offsetsSize; ++i) { offset = buffer->offsets[i].offset + *bufferPosition; switch (buffer->offsets[i].contentType) { case UA_PUBSUB_OFFSETTYPE_NETWORKMESSAGE_FIELDENCDODING: rv = UA_DataSetMessageHeader_decodeBinary(src, &offset, &header); if(rv != UA_STATUSCODE_GOOD) return rv; break; case UA_PUBSUB_OFFSETTYPE_PUBLISHERID: switch (buffer->nm->publisherIdType) { case UA_PUBLISHERDATATYPE_BYTE: rv = UA_Byte_decodeBinary(src, &offset, &(buffer->nm->publisherId.publisherIdByte)); break; case UA_PUBLISHERDATATYPE_UINT16: rv = UA_UInt16_decodeBinary(src, &offset, &(buffer->nm->publisherId.publisherIdUInt16)); break; case UA_PUBLISHERDATATYPE_UINT32: rv = UA_UInt32_decodeBinary(src, &offset, &(buffer->nm->publisherId.publisherIdUInt32)); break; case UA_PUBLISHERDATATYPE_UINT64: rv = UA_UInt64_decodeBinary(src, &offset, &(buffer->nm->publisherId.publisherIdUInt64)); break; default: return UA_STATUSCODE_BADNOTSUPPORTED; } break; case UA_PUBSUB_OFFSETTYPE_WRITERGROUPID: rv = UA_UInt16_decodeBinary(src, &offset, &buffer->nm->groupHeader.writerGroupId); UA_CHECK_STATUS(rv, return rv); break; case UA_PUBSUB_OFFSETTYPE_DATASETWRITERID: rv = UA_UInt16_decodeBinary(src, &offset, &buffer->nm->payloadHeader.dataSetPayloadHeader.dataSetWriterIds[0]); /* TODO */ UA_CHECK_STATUS(rv, return rv); break; case UA_PUBSUB_OFFSETTYPE_NETWORKMESSAGE_SEQUENCENUMBER: rv = UA_UInt16_decodeBinary(src, &offset, &buffer->nm->groupHeader.sequenceNumber); UA_CHECK_STATUS(rv, return rv); break; case UA_PUBSUB_OFFSETTYPE_DATASETMESSAGE_SEQUENCENUMBER: rv = UA_UInt16_decodeBinary(src, &offset, &(dsm->header.dataSetMessageSequenceNr)); UA_CHECK_STATUS(rv, return rv); break; case UA_PUBSUB_OFFSETTYPE_PAYLOAD_DATAVALUE: rv = UA_DataValue_decodeBinary(src, &offset, &(dsm->data.keyFrameData.dataSetFields[payloadCounter])); UA_CHECK_STATUS(rv, return rv); payloadCounter++; break; case UA_PUBSUB_OFFSETTYPE_PAYLOAD_VARIANT: rv = UA_Variant_decodeBinary(src, &offset, &dsm->data.keyFrameData.dataSetFields[payloadCounter].value); UA_CHECK_STATUS(rv, return rv); dsm->data.keyFrameData.dataSetFields[payloadCounter].hasValue = true; payloadCounter++; break; case UA_PUBSUB_OFFSETTYPE_PAYLOAD_RAW: /* We need only the start address of the raw fields */ if (smallestRawOffset > offset){ smallestRawOffset = offset; dsm->data.keyFrameData.rawFields.data = &src->data[offset]; dsm->data.keyFrameData.rawFields.length = buffer->rawMessageLength; } payloadCounter++; break; default: return UA_STATUSCODE_BADNOTSUPPORTED; } } //check if the frame is of type "raw" payload if(smallestRawOffset != UA_UINT32_MAX){ *bufferPosition = smallestRawOffset + buffer->rawMessageLength; } else { *bufferPosition = offset; } return rv; } static UA_StatusCode UA_NetworkMessageHeader_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); UA_CHECK_STATUS(rv, 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); UA_CHECK_STATUS(rv, 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); UA_CHECK_STATUS(rv, 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; } UA_CHECK_STATUS(rv, return rv); } // DataSetClassId if(src->dataSetClassIdEnabled) { rv = UA_Guid_encodeBinary(&(src->dataSetClassId), bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); } return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_GroupHeader_encodeBinary(const UA_NetworkMessage* src, UA_Byte **bufPos, const UA_Byte *bufEnd) { UA_Byte 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; UA_StatusCode rv = UA_Byte_encodeBinary(&v, bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); if(src->groupHeader.writerGroupIdEnabled) { rv = UA_UInt16_encodeBinary(&(src->groupHeader.writerGroupId), bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); } if(src->groupHeader.groupVersionEnabled) { rv = UA_UInt32_encodeBinary(&(src->groupHeader.groupVersion), bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); } if(src->groupHeader.networkMessageNumberEnabled) { rv = UA_UInt16_encodeBinary(&(src->groupHeader.networkMessageNumber), bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); } if(src->groupHeader.sequenceNumberEnabled) { rv = UA_UInt16_encodeBinary(&(src->groupHeader.sequenceNumber), bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); } return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_PayloadHeader_encodeBinary(const UA_NetworkMessage* src, UA_Byte **bufPos, const UA_Byte *bufEnd) { if(src->networkMessageType != UA_NETWORKMESSAGE_DATASET) return UA_STATUSCODE_BADNOTIMPLEMENTED; UA_StatusCode rv = UA_Byte_encodeBinary(&(src->payloadHeader.dataSetPayloadHeader.count), bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); 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); UA_CHECK_STATUS(rv, return rv); } return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_ExtendedNetworkMessageHeader_encodeBinary(const UA_NetworkMessage* src, UA_Byte **bufPos, const UA_Byte *bufEnd) { UA_StatusCode rv = UA_STATUSCODE_GOOD; // Timestamp if(src->timestampEnabled) rv = UA_DateTime_encodeBinary(&(src->timestamp), bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); // Picoseconds if(src->picosecondsEnabled) rv = UA_UInt16_encodeBinary(&(src->picoseconds), bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); // 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); UA_CHECK_STATUS(rv, return rv); for (UA_UInt16 i = 0; i < src->promotedFieldsSize; i++) rv |= UA_Variant_encodeBinary(&(src->promotedFields[i]), bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); } return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_SecurityHeader_encodeBinary(const UA_NetworkMessage* src, UA_Byte **bufPos, const UA_Byte *bufEnd) { // SecurityFlags UA_Byte 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; UA_StatusCode rv = UA_Byte_encodeBinary(&v, bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); // SecurityTokenId rv = UA_UInt32_encodeBinary(&src->securityHeader.securityTokenId, bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); // NonceLength UA_Byte nonceLength = (UA_Byte)src->securityHeader.messageNonce.length; rv = UA_Byte_encodeBinary(&nonceLength, bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); // MessageNonce for (size_t i = 0; i < src->securityHeader.messageNonce.length; i++) { rv = UA_Byte_encodeBinary(&src->securityHeader.messageNonce.data[i], bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); } // SecurityFooterSize if(src->securityHeader.securityFooterEnabled) { rv = UA_UInt16_encodeBinary(&src->securityHeader.securityFooterSize, bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); } return UA_STATUSCODE_GOOD; } UA_StatusCode UA_NetworkMessage_encodeHeaders(const UA_NetworkMessage* src, UA_Byte **bufPos, const UA_Byte *bufEnd) { UA_StatusCode rv = UA_NetworkMessageHeader_encodeBinary(src, bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); // Group Header if(src->groupHeaderEnabled) { rv = UA_GroupHeader_encodeBinary(src, bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); } // Payload Header if(src->payloadHeaderEnabled) { rv = UA_PayloadHeader_encodeBinary(src, bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); } // Extended Network Message Header rv = UA_ExtendedNetworkMessageHeader_encodeBinary(src, bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); // SecurityHeader if(src->securityEnabled) { rv = UA_SecurityHeader_encodeBinary(src, bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); } return UA_STATUSCODE_GOOD; } UA_StatusCode UA_NetworkMessage_encodePayload(const UA_NetworkMessage* src, UA_Byte **bufPos, const UA_Byte *bufEnd) { UA_StatusCode 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], NULL, 0); } rv = UA_UInt16_encodeBinary(&sz, bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); } } } for(UA_Byte i = 0; i < count; i++) { rv = UA_DataSetMessage_encodeBinary(&(src->payload.dataSetPayload.dataSetMessages[i]), bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); } return UA_STATUSCODE_GOOD; } UA_StatusCode UA_NetworkMessage_encodeFooters(const UA_NetworkMessage* src, UA_Byte **bufPos, const UA_Byte *bufEnd) { if(src->securityEnabled) { // SecurityFooter if(src->securityHeader.securityFooterEnabled) { for(size_t i = 0; i < src->securityHeader.securityFooterSize; i++) { UA_StatusCode rv = UA_Byte_encodeBinary(&(src->securityFooter.data[i]), bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); } } } return UA_STATUSCODE_GOOD; } UA_StatusCode UA_NetworkMessage_encodeBinary(const UA_NetworkMessage* src, UA_Byte **bufPos, const UA_Byte *bufEnd, UA_Byte **dataToEncryptStart) { UA_StatusCode rv = UA_NetworkMessage_encodeHeaders(src, bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); if(dataToEncryptStart) { *dataToEncryptStart = *bufPos; } rv = UA_NetworkMessage_encodePayload(src, bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); rv = UA_NetworkMessage_encodeFooters(src, bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); return UA_STATUSCODE_GOOD; } UA_StatusCode UA_NetworkMessageHeader_decodeBinary(const UA_ByteString *src, size_t *offset, UA_NetworkMessage *dst) { UA_Byte decoded = 0; UA_StatusCode rv = UA_Byte_decodeBinary(src, offset, &decoded); UA_CHECK_STATUS(rv, return rv); dst->version = decoded & NM_VERSION_MASK; if((decoded & NM_PUBLISHER_ID_ENABLED_MASK) != 0) dst->publisherIdEnabled = true; if((decoded & NM_GROUP_HEADER_ENABLED_MASK) != 0) dst->groupHeaderEnabled = true; if((decoded & NM_PAYLOAD_HEADER_ENABLED_MASK) != 0) dst->payloadHeaderEnabled = true; if((decoded & NM_EXTENDEDFLAGS1_ENABLED_MASK) != 0) { decoded = 0; rv = UA_Byte_decodeBinary(src, offset, &decoded); UA_CHECK_STATUS(rv, return rv); dst->publisherIdType = (UA_PublisherIdDatatype)(decoded & NM_PUBLISHER_ID_MASK); if((decoded & NM_DATASET_CLASSID_ENABLED_MASK) != 0) dst->dataSetClassIdEnabled = true; if((decoded & NM_SECURITY_ENABLED_MASK) != 0) dst->securityEnabled = true; if((decoded & NM_TIMESTAMP_ENABLED_MASK) != 0) dst->timestampEnabled = true; if((decoded & NM_PICOSECONDS_ENABLED_MASK) != 0) dst->picosecondsEnabled = true; if((decoded & NM_EXTENDEDFLAGS2_ENABLED_MASK) != 0) { decoded = 0; rv = UA_Byte_decodeBinary(src, offset, &decoded); UA_CHECK_STATUS(rv, return rv); if((decoded & NM_CHUNK_MESSAGE_MASK) != 0) dst->chunkMessage = true; if((decoded & NM_PROMOTEDFIELDS_ENABLED_MASK) != 0) dst->promotedFieldsEnabled = true; decoded = decoded & NM_NETWORK_MSG_TYPE_MASK; decoded = (UA_Byte) (decoded >> NM_SHIFT_LEN); dst->networkMessageType = (UA_NetworkMessageType)decoded; } } 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; } UA_CHECK_STATUS(rv, return rv); } if(dst->dataSetClassIdEnabled) { rv = UA_Guid_decodeBinary(src, offset, &(dst->dataSetClassId)); UA_CHECK_STATUS(rv, return rv); } return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_GroupHeader_decodeBinary(const UA_ByteString *src, size_t *offset, UA_NetworkMessage* dst) { UA_Byte decoded = 0; UA_StatusCode rv = UA_Byte_decodeBinary(src, offset, &decoded); UA_CHECK_STATUS(rv, return rv); if((decoded & GROUP_HEADER_WRITER_GROUPID_ENABLED) != 0) dst->groupHeader.writerGroupIdEnabled = true; if((decoded & GROUP_HEADER_GROUP_VERSION_ENABLED) != 0) dst->groupHeader.groupVersionEnabled = true; if((decoded & GROUP_HEADER_NM_NUMBER_ENABLED) != 0) dst->groupHeader.networkMessageNumberEnabled = true; if((decoded & GROUP_HEADER_SEQUENCE_NUMBER_ENABLED) != 0) dst->groupHeader.sequenceNumberEnabled = true; if(dst->groupHeader.writerGroupIdEnabled) { rv = UA_UInt16_decodeBinary(src, offset, &dst->groupHeader.writerGroupId); UA_CHECK_STATUS(rv, return rv); } if(dst->groupHeader.groupVersionEnabled) { rv = UA_UInt32_decodeBinary(src, offset, &dst->groupHeader.groupVersion); UA_CHECK_STATUS(rv, return rv); } if(dst->groupHeader.networkMessageNumberEnabled) { rv = UA_UInt16_decodeBinary(src, offset, &dst->groupHeader.networkMessageNumber); UA_CHECK_STATUS(rv, return rv); } if(dst->groupHeader.sequenceNumberEnabled) { rv = UA_UInt16_decodeBinary(src, offset, &dst->groupHeader.sequenceNumber); UA_CHECK_STATUS(rv, return rv); } return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_PayloadHeader_decodeBinary(const UA_ByteString *src, size_t *offset, UA_NetworkMessage* dst) { if(dst->networkMessageType != UA_NETWORKMESSAGE_DATASET) return UA_STATUSCODE_BADNOTIMPLEMENTED; UA_StatusCode rv = UA_Byte_decodeBinary(src, offset, &dst->payloadHeader.dataSetPayloadHeader.count); UA_CHECK_STATUS(rv, 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]); UA_CHECK_STATUS(rv, return rv); } return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_ExtendedNetworkMessageHeader_decodeBinary(const UA_ByteString *src, size_t *offset, UA_NetworkMessage* dst) { UA_StatusCode rv; // Timestamp if(dst->timestampEnabled) { rv = UA_DateTime_decodeBinary(src, offset, &(dst->timestamp)); UA_CHECK_STATUS(rv, goto error); } // Picoseconds if(dst->picosecondsEnabled) { rv = UA_UInt16_decodeBinary(src, offset, &(dst->picoseconds)); UA_CHECK_STATUS(rv, goto error); } // PromotedFields if(dst->promotedFieldsEnabled) { // Size UA_UInt16 promotedFieldsSize = 0; rv = UA_UInt16_decodeBinary(src, offset, &promotedFieldsSize); UA_CHECK_STATUS(rv, goto error); // 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); UA_CHECK_MEM(dst->promotedFields, return UA_STATUSCODE_BADOUTOFMEMORY); // set promotedFieldsSize to the number of objects dst->promotedFieldsSize = (UA_UInt16) (counter + 1); } else { dst->promotedFields = (UA_Variant*) UA_realloc(dst->promotedFields, (size_t) UA_TYPES[UA_TYPES_VARIANT].memSize * (counter + 1)); UA_CHECK_MEM(dst->promotedFields, return UA_STATUSCODE_BADOUTOFMEMORY); // 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]); UA_CHECK_STATUS(rv, goto error); counter++; } while ((*offset) < offsetEnd); } } return UA_STATUSCODE_GOOD; error: if (dst->promotedFields) { UA_free(dst->promotedFields); } return rv; } static UA_StatusCode UA_SecurityHeader_decodeBinary(const UA_ByteString *src, size_t *offset, UA_NetworkMessage* dst) { UA_Byte decoded = 0; // SecurityFlags decoded = 0; UA_StatusCode rv = UA_Byte_decodeBinary(src, offset, &decoded); UA_CHECK_STATUS(rv, return rv); if((decoded & SECURITY_HEADER_NM_SIGNED) != 0) dst->securityHeader.networkMessageSigned = true; if((decoded & SECURITY_HEADER_NM_ENCRYPTED) != 0) dst->securityHeader.networkMessageEncrypted = true; if((decoded & SECURITY_HEADER_SEC_FOOTER_ENABLED) != 0) dst->securityHeader.securityFooterEnabled = true; if((decoded & SECURITY_HEADER_FORCE_KEY_RESET) != 0) dst->securityHeader.forceKeyReset = true; // SecurityTokenId rv = UA_UInt32_decodeBinary(src, offset, &dst->securityHeader.securityTokenId); UA_CHECK_STATUS(rv, return rv); // NonceLength UA_Byte nonceLength; rv = UA_Byte_decodeBinary(src, offset, &nonceLength); UA_CHECK_STATUS(rv, return rv); // MessageNonce if(nonceLength > 0) { //TODO: check for memory leaks rv = UA_ByteString_allocBuffer(&dst->securityHeader.messageNonce, nonceLength); UA_CHECK_STATUS(rv, return rv); for (UA_Byte i = 0; i < nonceLength; i++) { rv = UA_Byte_decodeBinary(src, offset, &dst->securityHeader.messageNonce.data[i]); UA_CHECK_STATUS(rv, return rv); } } // SecurityFooterSize if(dst->securityHeader.securityFooterEnabled) { rv = UA_UInt16_decodeBinary(src, offset, &dst->securityHeader.securityFooterSize); UA_CHECK_STATUS(rv, return rv); } return UA_STATUSCODE_GOOD; } UA_StatusCode UA_NetworkMessage_decodeHeaders(const UA_ByteString *src, size_t *offset, UA_NetworkMessage *dst) { UA_StatusCode rv = UA_NetworkMessageHeader_decodeBinary(src, offset, dst); UA_CHECK_STATUS(rv, return rv); if (dst->groupHeaderEnabled) { rv = UA_GroupHeader_decodeBinary(src, offset, dst); UA_CHECK_STATUS(rv, return rv); } if (dst->payloadHeaderEnabled) { rv = UA_PayloadHeader_decodeBinary(src, offset, dst); UA_CHECK_STATUS(rv, return rv); } rv = UA_ExtendedNetworkMessageHeader_decodeBinary(src, offset, dst); UA_CHECK_STATUS(rv, return rv); if (dst->securityEnabled) { rv = UA_SecurityHeader_decodeBinary(src, offset, dst); UA_CHECK_STATUS(rv, return rv); } return UA_STATUSCODE_GOOD; } UA_StatusCode UA_NetworkMessage_decodePayload(const UA_ByteString *src, size_t *offset, UA_NetworkMessage *dst) { // Payload if(dst->networkMessageType != UA_NETWORKMESSAGE_DATASET) return UA_STATUSCODE_BADNOTIMPLEMENTED; UA_StatusCode rv; 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])); UA_CHECK_STATUS(rv, return rv); } } } dst->payload.dataSetPayload.dataSetMessages = (UA_DataSetMessage*) UA_calloc(count, sizeof(UA_DataSetMessage)); UA_CHECK_MEM(dst->payload.dataSetPayload.dataSetMessages, return UA_STATUSCODE_BADOUTOFMEMORY); if(count == 1) rv = UA_DataSetMessage_decodeBinary(src, offset, &(dst->payload.dataSetPayload.dataSetMessages[0]), 0); else { for(UA_Byte i = 0; i < count; i++) { rv = UA_DataSetMessage_decodeBinary(src, offset, &(dst->payload.dataSetPayload.dataSetMessages[i]), dst->payload.dataSetPayload.sizes[i]); } } UA_CHECK_STATUS(rv, return rv); return UA_STATUSCODE_GOOD; /** * TODO: check if making the cleanup to free its own allocated memory is better, * currently the free happens in a parent context */ // error: // if (dst->payload.dataSetPayload.dataSetMessages) { // UA_free(dst->payload.dataSetPayload.dataSetMessages); // } // return rv; } UA_StatusCode UA_NetworkMessage_decodeFooters(const UA_ByteString *src, size_t *offset, UA_NetworkMessage *dst) { if (dst->securityEnabled) { // SecurityFooter if(dst->securityHeader.securityFooterEnabled && (dst->securityHeader.securityFooterSize > 0)) { UA_StatusCode rv = UA_ByteString_allocBuffer(&dst->securityFooter, dst->securityHeader.securityFooterSize); UA_CHECK_STATUS(rv, return rv); for(UA_UInt16 i = 0; i < dst->securityHeader.securityFooterSize; i++) { rv = UA_Byte_decodeBinary(src, offset, &(dst->securityFooter.data[i])); UA_CHECK_STATUS(rv, return rv); } } } return UA_STATUSCODE_GOOD; } UA_StatusCode UA_NetworkMessage_decodeBinary(const UA_ByteString *src, size_t *offset, UA_NetworkMessage* dst) { UA_StatusCode rv = UA_STATUSCODE_GOOD; /* headers only need to be decoded when not in encryption mode * because headers are already decoded when encryption mode is enabled * to check for security parameters and decrypt/verify * * TODO: check if there is a workaround to use this function * also when encryption is enabled */ // #ifndef UA_ENABLE_PUBSUB_ENCRYPTION // if (*offset == 0) { // rv = UA_NetworkMessage_decodeHeaders(src, offset, dst); // UA_CHECK_STATUS(rv, return rv); // } // #endif rv = UA_NetworkMessage_decodeHeaders(src, offset, dst); UA_CHECK_STATUS(rv, return rv); rv = UA_NetworkMessage_decodePayload(src, offset, dst); UA_CHECK_STATUS(rv, return rv); rv = UA_NetworkMessage_decodeFooters(src, offset, dst); UA_CHECK_STATUS(rv, return rv); return UA_STATUSCODE_GOOD; } static UA_Boolean increaseOffsetArray(UA_NetworkMessageOffsetBuffer *offsetBuffer) { UA_NetworkMessageOffset *tmpOffsets = (UA_NetworkMessageOffset *) UA_realloc(offsetBuffer->offsets, sizeof(UA_NetworkMessageOffset) * (offsetBuffer->offsetsSize + (size_t)1)); UA_CHECK_MEM(tmpOffsets, return false); offsetBuffer->offsets = tmpOffsets; offsetBuffer->offsetsSize++; return true; } size_t UA_NetworkMessage_calcSizeBinary(UA_NetworkMessage *p, UA_NetworkMessageOffsetBuffer *offsetBuffer) { size_t retval = 0; UA_Byte byte = 0; 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) { if(offsetBuffer && offsetBuffer->RTsubscriberEnabled){ size_t pos = offsetBuffer->offsetsSize; if(!increaseOffsetArray(offsetBuffer)) return 0; offsetBuffer->offsets[pos].offset = size; offsetBuffer->offsets[pos].contentType = UA_PUBSUB_OFFSETTYPE_PUBLISHERID; } 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) { if(offsetBuffer && offsetBuffer->RTsubscriberEnabled){ size_t pos = offsetBuffer->offsetsSize; if(!increaseOffsetArray(offsetBuffer)) return 0; offsetBuffer->offsets[pos].offset = size; offsetBuffer->offsets[pos].contentType = UA_PUBSUB_OFFSETTYPE_WRITERGROUPID; } 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){ if(offsetBuffer){ size_t pos = offsetBuffer->offsetsSize; if(!increaseOffsetArray(offsetBuffer)) return 0; offsetBuffer->offsets[pos].offset = size; offsetBuffer->offsets[pos].offsetData.value.value = UA_DataValue_new(); UA_Variant_setScalarCopy(&offsetBuffer->offsets[pos].offsetData.value.value->value, &p->groupHeader.sequenceNumber, &UA_TYPES[UA_TYPES_UINT16]); offsetBuffer->offsets[pos].contentType = UA_PUBSUB_OFFSETTYPE_NETWORKMESSAGE_SEQUENCENUMBER; } 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) { if(offsetBuffer && offsetBuffer->RTsubscriberEnabled){ size_t pos = offsetBuffer->offsetsSize; if(!increaseOffsetArray(offsetBuffer)) return 0; offsetBuffer->offsets[pos].offset = size; offsetBuffer->offsets[pos].contentType = UA_PUBSUB_OFFSETTYPE_DATASETWRITERID; } 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) { if(offsetBuffer){ size_t pos = offsetBuffer->offsetsSize; if(!increaseOffsetArray(offsetBuffer)) return 0; offsetBuffer->offsets[pos].offset = size; offsetBuffer->offsets[pos].contentType = UA_PUBSUB_OFFSETTYPE_TIMESTAMP; } size += UA_DateTime_calcSizeBinary(&p->timestamp); } if(p->picosecondsEnabled){ if (offsetBuffer) { size_t pos = offsetBuffer->offsetsSize; if(!increaseOffsetArray(offsetBuffer)) return 0; offsetBuffer->offsets[pos].offset = size; offsetBuffer->offsets[pos].contentType = UA_PUBSUB_OFFSETTYPE_TIMESTAMP_PICOSECONDS; } 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 += 1; /* UA_Byte_calcSizeBinary(&p->securityHeader.nonceLength); */ size += p->securityHeader.messageNonce.length; 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++) { if (offsetBuffer) UA_DataSetMessage_calcSizeBinary(&(p->payload.dataSetPayload.dataSetMessages[i]), offsetBuffer, size); size += UA_DataSetMessage_calcSizeBinary(&(p->payload.dataSetPayload.dataSetMessages[i]), NULL, 0); } } if(p->securityEnabled) { if(p->securityHeader.securityFooterEnabled) size += p->securityHeader.securityFooterSize; } retval = size; return retval; } void UA_NetworkMessage_clear(UA_NetworkMessage* p) { if(p->promotedFieldsEnabled) UA_Array_delete(p->promotedFields, p->promotedFieldsSize, &UA_TYPES[UA_TYPES_VARIANT]); UA_ByteString_clear(&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) { UA_Byte count = 1; if(p->payloadHeaderEnabled) count = p->payloadHeader.dataSetPayloadHeader.count; for(size_t i = 0; i < count; i++) UA_DataSetMessage_clear(&(p->payload.dataSetPayload.dataSetMessages[i])); UA_free(p->payload.dataSetPayload.dataSetMessages); } } if(p->securityHeader.securityFooterEnabled && (p->securityHeader.securityFooterSize > 0)) UA_ByteString_clear(&p->securityFooter); if(p->messageIdEnabled){ UA_String_clear(&p->messageId); } if(p->publisherIdEnabled && p->publisherIdType == UA_PUBLISHERDATATYPE_STRING){ UA_String_clear(&p->publisherId.publisherIdString); } memset(p, 0, sizeof(UA_NetworkMessage)); } 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); UA_CHECK_STATUS(rv, 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); UA_CHECK_STATUS(rv, return rv); } // DataSetMessageSequenceNr if(src->dataSetMessageSequenceNrEnabled) { rv = UA_UInt16_encodeBinary(&src->dataSetMessageSequenceNr, bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); } // Timestamp if(src->timestampEnabled) { rv = UA_DateTime_encodeBinary(&(src->timestamp), bufPos, bufEnd); /* UtcTime */ UA_CHECK_STATUS(rv, return rv); } // PicoSeconds if(src->picoSecondsIncluded) { rv = UA_UInt16_encodeBinary(&(src->picoSeconds), bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); } // Status if(src->statusEnabled) { rv = UA_UInt16_encodeBinary(&(src->status), bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); } // ConfigVersionMajorVersion if(src->configVersionMajorVersionEnabled) { rv = UA_UInt32_encodeBinary(&(src->configVersionMajorVersion), bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); } // ConfigVersionMinorVersion if(src->configVersionMinorVersionEnabled) { rv = UA_UInt32_encodeBinary(&(src->configVersionMinorVersion), bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); } return UA_STATUSCODE_GOOD; } #ifdef UA_ENABLE_PUBSUB_ENCRYPTION UA_StatusCode UA_NetworkMessage_signEncrypt(UA_NetworkMessage *nm, UA_MessageSecurityMode securityMode, UA_PubSubSecurityPolicy *policy, void *policyContext, UA_Byte *messageStart, UA_Byte *encryptStart, UA_Byte *sigStart) { UA_StatusCode res = UA_STATUSCODE_GOOD; /* Encrypt the payload */ if(securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) { /* Set the temporary MessageNonce in the SecurityPolicy */ res = policy->setMessageNonce(policyContext, &nm->securityHeader.messageNonce); UA_CHECK_STATUS(res, return res); /* The encryption is done in-place, no need to encode again */ UA_ByteString encryptBuf; encryptBuf.data = encryptStart; encryptBuf.length = (uintptr_t)sigStart - (uintptr_t)encryptStart; res = policy->symmetricModule.cryptoModule.encryptionAlgorithm. encrypt(policyContext, &encryptBuf); UA_CHECK_STATUS(res, return res); } /* Sign the entire message */ if(securityMode == UA_MESSAGESECURITYMODE_SIGN || securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) { UA_ByteString sigBuf; sigBuf.length = (uintptr_t)sigStart - (uintptr_t)messageStart; sigBuf.data = messageStart; size_t sigSize = policy->symmetricModule.cryptoModule. signatureAlgorithm.getLocalSignatureSize(policyContext); UA_ByteString sig = {sigSize, sigStart}; res = policy->symmetricModule.cryptoModule. signatureAlgorithm.sign(policyContext, &sigBuf, &sig); } return res; } #endif 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); UA_CHECK_STATUS(rv, 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); UA_CHECK_STATUS(rv, 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); UA_CHECK_STATUS(rv, return rv); } else { dst->dataSetMessageSequenceNr = 0; } if(dst->timestampEnabled) { rv = UA_DateTime_decodeBinary(src, offset, &dst->timestamp); /* UtcTime */ UA_CHECK_STATUS(rv, return rv); } else { dst->timestamp = 0; } if(dst->picoSecondsIncluded) { rv = UA_UInt16_decodeBinary(src, offset, &dst->picoSeconds); UA_CHECK_STATUS(rv, return rv); } else { dst->picoSeconds = 0; } if(dst->statusEnabled) { rv = UA_UInt16_decodeBinary(src, offset, &dst->status); UA_CHECK_STATUS(rv, return rv); } else { dst->status = 0; } if(dst->configVersionMajorVersionEnabled) { rv = UA_UInt32_decodeBinary(src, offset, &dst->configVersionMajorVersion); UA_CHECK_STATUS(rv, return rv); } else { dst->configVersionMajorVersion = 0; } if(dst->configVersionMinorVersionEnabled) { rv = UA_UInt32_decodeBinary(src, offset, &dst->configVersionMinorVersion); UA_CHECK_STATUS(rv, return rv); } else { dst->configVersionMinorVersion = 0; } return UA_STATUSCODE_GOOD; } size_t UA_DataSetMessageHeader_calcSizeBinary(const UA_DataSetMessageHeader* p) { UA_Byte byte = 0; 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); UA_CHECK_STATUS(rv, 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); UA_CHECK_STATUS(rv, 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); UA_CHECK_STATUS(rv, return rv); } } else if(src->header.fieldEncoding == UA_FIELDENCODING_RAWDATA) { for (UA_UInt16 i = 0; i < src->data.keyFrameData.fieldCount; i++) { rv = UA_encodeBinaryInternal(src->data.keyFrameData.dataSetFields[i].value.data, src->data.keyFrameData.dataSetFields[i].value.type, bufPos, &bufEnd, NULL, NULL); UA_CHECK_STATUS(rv, return rv); } } 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); UA_CHECK_STATUS(rv, 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); UA_CHECK_STATUS(rv, 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); UA_CHECK_STATUS(rv, return rv); rv = UA_Variant_encodeBinary(&(src->data.deltaFrameData.deltaFrameFields[i].fieldValue.value), bufPos, bufEnd); UA_CHECK_STATUS(rv, 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); UA_CHECK_STATUS(rv, return rv); rv = UA_DataValue_encodeBinary(&(src->data.deltaFrameData.deltaFrameFields[i].fieldValue), bufPos, bufEnd); UA_CHECK_STATUS(rv, 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, UA_UInt16 dsmSize) { size_t initialOffset = *offset; memset(dst, 0, sizeof(UA_DataSetMessage)); UA_StatusCode rv = UA_DataSetMessageHeader_decodeBinary(src, offset, &dst->header); UA_CHECK_STATUS(rv, return rv); if(dst->header.dataSetMessageType == UA_DATASETMESSAGE_DATAKEYFRAME) { switch(dst->header.fieldEncoding) { case UA_FIELDENCODING_VARIANT: { rv = UA_UInt16_decodeBinary(src, offset, &dst->data.keyFrameData.fieldCount); UA_CHECK_STATUS(rv, return rv); 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); UA_CHECK_STATUS(rv, return rv); dst->data.keyFrameData.dataSetFields[i].hasValue = true; } break; } case UA_FIELDENCODING_DATAVALUE: { rv = UA_UInt16_decodeBinary(src, offset, &dst->data.keyFrameData.fieldCount); UA_CHECK_STATUS(rv, return rv); 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])); UA_CHECK_STATUS(rv, return rv); } break; } case UA_FIELDENCODING_RAWDATA: { dst->data.keyFrameData.rawFields.data = &src->data[*offset]; dst->data.keyFrameData.rawFields.length = dsmSize; if(dsmSize == 0){ //TODO calculate the length of the DSM-Payload for a single DSM //Problem: Size is not set and MetaData information are needed. //Increase offset to avoid endless chunk loop. Needs to be fixed when //pubsub security footer and signatur is enabled. *offset += 1500; } else { *offset += (dsmSize - (*offset - initialOffset)); } break; } default: return UA_STATUSCODE_BADINTERNALERROR; } } else if(dst->header.dataSetMessageType == UA_DATASETMESSAGE_DATADELTAFRAME) { switch(dst->header.fieldEncoding) { case UA_FIELDENCODING_VARIANT: { rv = UA_UInt16_decodeBinary(src, offset, &dst->data.deltaFrameData.fieldCount); UA_CHECK_STATUS(rv, return rv); 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); UA_CHECK_STATUS(rv, return rv); UA_DataValue_init(&dst->data.deltaFrameData.deltaFrameFields[i].fieldValue); rv = UA_Variant_decodeBinary(src, offset, &dst->data.deltaFrameData.deltaFrameFields[i].fieldValue.value); UA_CHECK_STATUS(rv, return rv); dst->data.deltaFrameData.deltaFrameFields[i].fieldValue.hasValue = true; } break; } case UA_FIELDENCODING_DATAVALUE: { rv = UA_UInt16_decodeBinary(src, offset, &dst->data.deltaFrameData.fieldCount); UA_CHECK_STATUS(rv, return rv); 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); UA_CHECK_STATUS(rv, return rv); rv = UA_DataValue_decodeBinary(src, offset, &(dst->data.deltaFrameData.deltaFrameFields[i].fieldValue)); UA_CHECK_STATUS(rv, return rv); } break; } case UA_FIELDENCODING_RAWDATA: { return UA_STATUSCODE_BADNOTIMPLEMENTED; } default: return UA_STATUSCODE_BADINTERNALERROR; } } 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(UA_DataSetMessage* p, UA_NetworkMessageOffsetBuffer *offsetBuffer, size_t currentOffset) { size_t size = currentOffset; if (offsetBuffer) { size_t pos = offsetBuffer->offsetsSize; if(!increaseOffsetArray(offsetBuffer)) return 0; offsetBuffer->offsets[pos].offset = size; offsetBuffer->offsets[pos].offsetData.value.value = UA_DataValue_new(); UA_Variant_setScalar(&offsetBuffer->offsets[pos].offsetData.value.value->value, &p->header.fieldEncoding, &UA_TYPES[UA_TYPES_UINT32]); offsetBuffer->offsets[pos].contentType = UA_PUBSUB_OFFSETTYPE_NETWORKMESSAGE_FIELDENCDODING; } UA_Byte byte = 0; size += UA_Byte_calcSizeBinary(&byte); // DataSetMessage Type + Flags if(UA_DataSetMessageHeader_DataSetFlags2Enabled(&p->header)) size += UA_Byte_calcSizeBinary(&byte); if(p->header.dataSetMessageSequenceNrEnabled) { if (offsetBuffer) { size_t pos = offsetBuffer->offsetsSize; if(!increaseOffsetArray(offsetBuffer)) return 0; offsetBuffer->offsets[pos].offset = size; offsetBuffer->offsets[pos].offsetData.value.value = UA_DataValue_new(); UA_Variant_setScalarCopy(&offsetBuffer->offsets[pos].offsetData.value.value->value, &p->header.dataSetMessageSequenceNr, &UA_TYPES[UA_TYPES_UINT16]); offsetBuffer->offsets[pos].contentType = UA_PUBSUB_OFFSETTYPE_DATASETMESSAGE_SEQUENCENUMBER; } size += UA_UInt16_calcSizeBinary(&p->header.dataSetMessageSequenceNr); } if(p->header.timestampEnabled) size += UA_DateTime_calcSizeBinary(&p->header.timestamp); /* UtcTime */ if(p->header.picoSecondsIncluded) size += UA_UInt16_calcSizeBinary(&p->header.picoSeconds); if(p->header.statusEnabled) size += UA_UInt16_calcSizeBinary(&p->header.status); if(p->header.configVersionMajorVersionEnabled) size += UA_UInt32_calcSizeBinary(&p->header.configVersionMajorVersion); if(p->header.configVersionMinorVersionEnabled) size += UA_UInt32_calcSizeBinary(&p->header.configVersionMinorVersion); 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++){ if (offsetBuffer) { size_t pos = offsetBuffer->offsetsSize; if(!increaseOffsetArray(offsetBuffer)) return 0; offsetBuffer->offsets[pos].offset = size; offsetBuffer->offsets[pos].contentType = UA_PUBSUB_OFFSETTYPE_PAYLOAD_VARIANT; //TODO check value source and alloc! //offsetBuffer->offsets[pos].offsetData.value.value = p->data.keyFrameData.dataSetFields; offsetBuffer->offsets[pos].offsetData.value.value = UA_DataValue_new(); UA_Variant_setScalar(&offsetBuffer->offsets[pos].offsetData.value.value->value, p->data.keyFrameData.dataSetFields[i].value.data, p->data.keyFrameData.dataSetFields[i].value.type); offsetBuffer->offsets[pos].offsetData.value.value->value.storageType = UA_VARIANT_DATA_NODELETE; } size += UA_calcSizeBinary(&p->data.keyFrameData.dataSetFields[i].value, &UA_TYPES[UA_TYPES_VARIANT]); } } else if(p->header.fieldEncoding == UA_FIELDENCODING_RAWDATA) { for (UA_UInt16 i = 0; i < p->data.keyFrameData.fieldCount; i++){ if (offsetBuffer) { size_t pos = offsetBuffer->offsetsSize; if(!increaseOffsetArray(offsetBuffer)) return 0; offsetBuffer->offsets[pos].offset = size; offsetBuffer->offsets[pos].contentType = UA_PUBSUB_OFFSETTYPE_PAYLOAD_RAW; offsetBuffer->offsets[pos].offsetData.value.value = UA_DataValue_new(); //init offset buffer with the latest value UA_Variant_setScalar(&offsetBuffer->offsets[pos].offsetData.value.value->value, p->data.keyFrameData.dataSetFields[i].value.data, p->data.keyFrameData.dataSetFields[i].value.type); offsetBuffer->offsets[pos].offsetData.value.value->value.storageType = UA_VARIANT_DATA_NODELETE; //count the memory size of the specific field offsetBuffer->rawMessageLength += p->data.keyFrameData.dataSetFields[i].value.type->memSize; } size += UA_calcSizeBinary(p->data.keyFrameData.dataSetFields[i].value.data, p->data.keyFrameData.dataSetFields[i].value.type); } } else if(p->header.fieldEncoding == UA_FIELDENCODING_DATAVALUE) { for (UA_UInt16 i = 0; i < p->data.keyFrameData.fieldCount; i++) { if (offsetBuffer) { size_t pos = offsetBuffer->offsetsSize; if(!increaseOffsetArray(offsetBuffer)) return 0; offsetBuffer->offsets[pos].offset = size; offsetBuffer->offsets[pos].contentType = UA_PUBSUB_OFFSETTYPE_PAYLOAD_DATAVALUE; //TODO check value source, change implementation to 'variant' offsetBuffer->offsets[pos].offsetData.value.value = p->data.keyFrameData.dataSetFields; } size += UA_calcSizeBinary(&p->data.keyFrameData.dataSetFields[i], &UA_TYPES[UA_TYPES_DATAVALUE]); } } } else if(p->header.dataSetMessageType == UA_DATASETMESSAGE_DATADELTAFRAME) { //TODO clarify how to handle DATADELTAFRAME messages with RT 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_clear(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_clear(&p->data.deltaFrameData.deltaFrameFields[i].fieldValue); } else if(p->header.fieldEncoding == UA_FIELDENCODING_VARIANT) { UA_Variant_clear(&p->data.deltaFrameData.deltaFrameFields[i].fieldValue.value); } } UA_free(p->data.deltaFrameData.deltaFrameFields); } } } void UA_NetworkMessageOffsetBuffer_clear(UA_NetworkMessageOffsetBuffer *ob) { UA_ByteString_clear(&ob->buffer); if(ob->nm) { UA_NetworkMessage_clear(ob->nm); UA_free(ob->nm); } ob->nm = NULL; ob->rawMessageLength = 0; for(size_t i = 0; i < ob->offsetsSize; i++) { UA_NetworkMessageOffset *nmo = &ob->offsets[i]; if(nmo->contentType == UA_PUBSUB_OFFSETTYPE_PAYLOAD_VARIANT || nmo->contentType == UA_PUBSUB_OFFSETTYPE_PAYLOAD_RAW || nmo->contentType == UA_PUBSUB_OFFSETTYPE_PAYLOAD_DATAVALUE) { UA_DataValue_delete(nmo->offsetData.value.value); } else if(nmo->contentType == UA_PUBSUB_OFFSETTYPE_DATASETMESSAGE_SEQUENCENUMBER || nmo->contentType == UA_PUBSUB_OFFSETTYPE_NETWORKMESSAGE_SEQUENCENUMBER) { UA_DataValue_delete(nmo->offsetData.value.value); } else if(nmo->contentType == UA_PUBSUB_OFFSETTYPE_NETWORKMESSAGE_FIELDENCDODING) { nmo->offsetData.value.value->value.data = NULL; UA_DataValue_delete(nmo->offsetData.value.value); } } UA_free(ob->offsets); ob->offsets = NULL; ob->offsetsSize = 0; ob->RTsubscriberEnabled = false; } #endif /* UA_ENABLE_PUBSUB */ /**** amalgamated original file "/src/pubsub/ua_pubsub_writer.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-2019 Fraunhofer IOSB (Author: Andreas Ebner) * Copyright (c) 2019 Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright (c) 2019-2021 Kalycito Infotech Private Limited * Copyright (c) 2020 Yannick Wallerer, Siemens AG * Copyright (c) 2020 Thomas Fischer, Siemens AG * Copyright (c) 2021 Fraunhofer IOSB (Author: Jan Hermes) */ #ifdef UA_ENABLE_PUBSUB /* conditional compilation */ #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL #endif #ifdef UA_ENABLE_PUBSUB_DELTAFRAMES #endif /* Forward declaration */ static void UA_DataSetField_clear(UA_DataSetField *field); /**********************************************/ /* Connection */ /**********************************************/ UA_StatusCode UA_PubSubConnectionConfig_copy(const UA_PubSubConnectionConfig *src, UA_PubSubConnectionConfig *dst) { UA_StatusCode res = UA_STATUSCODE_GOOD; memcpy(dst, src, sizeof(UA_PubSubConnectionConfig)); res |= UA_String_copy(&src->name, &dst->name); res |= UA_Variant_copy(&src->address, &dst->address); res |= UA_String_copy(&src->transportProfileUri, &dst->transportProfileUri); res |= 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) { UA_PubSubConnectionConfig_clear(dst); return UA_STATUSCODE_BADOUTOFMEMORY; } for(size_t i = 0; i < src->connectionPropertiesSize; i++){ res |= UA_QualifiedName_copy(&src->connectionProperties[i].key, &dst->connectionProperties[i].key); res |= UA_Variant_copy(&src->connectionProperties[i].value, &dst->connectionProperties[i].value); } } if(res != UA_STATUSCODE_GOOD) UA_PubSubConnectionConfig_clear(dst); return res; } 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; return UA_PubSubConnectionConfig_copy(currentPubSubConnection->config, config); } UA_PubSubConnection * UA_PubSubConnection_findConnectionbyId(UA_Server *server, UA_NodeId connectionIdentifier) { UA_PubSubConnection *pubSubConnection; TAILQ_FOREACH(pubSubConnection, &server->pubSubManager.connections, listEntry){ if(UA_NodeId_equal(&connectionIdentifier, &pubSubConnection->identifier)) break; } return pubSubConnection; } void UA_PubSubConnectionConfig_clear(UA_PubSubConnectionConfig *connectionConfig) { UA_String_clear(&connectionConfig->name); UA_String_clear(&connectionConfig->transportProfileUri); UA_Variant_clear(&connectionConfig->connectionTransportSettings); UA_Variant_clear(&connectionConfig->address); for(size_t i = 0; i < connectionConfig->connectionPropertiesSize; i++){ UA_QualifiedName_clear(&connectionConfig->connectionProperties[i].key); UA_Variant_clear(&connectionConfig->connectionProperties[i].value); } UA_free(connectionConfig->connectionProperties); } void UA_PubSubConnection_clear(UA_Server *server, UA_PubSubConnection *connection) { /* Remove WriterGroups */ UA_WriterGroup *writerGroup, *tmpWriterGroup; LIST_FOREACH_SAFE(writerGroup, &connection->writerGroups, listEntry, tmpWriterGroup) UA_Server_removeWriterGroup(server, writerGroup->identifier); /* Remove ReaderGroups */ UA_ReaderGroup *readerGroups, *tmpReaderGroup; LIST_FOREACH_SAFE(readerGroups, &connection->readerGroups, listEntry, tmpReaderGroup) UA_Server_removeReaderGroup(server, readerGroups->identifier); UA_NodeId_clear(&connection->identifier); if(connection->channel) connection->channel->close(connection->channel); UA_PubSubConnectionConfig_clear(connection->config); UA_free(connection->config); } /**********************************************/ /* PublishedDataSet */ /**********************************************/ UA_StatusCode UA_PublishedDataSetConfig_copy(const UA_PublishedDataSetConfig *src, UA_PublishedDataSetConfig *dst) { UA_StatusCode res = UA_STATUSCODE_GOOD; memcpy(dst, src, sizeof(UA_PublishedDataSetConfig)); res |= 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)); if(!dst->config.itemsTemplate.variablesToAdd) { res = UA_STATUSCODE_BADOUTOFMEMORY; break; } dst->config.itemsTemplate.variablesToAddSize = src->config.itemsTemplate.variablesToAddSize; } for(size_t i = 0; i < src->config.itemsTemplate.variablesToAddSize; i++) { res |= UA_PublishedVariableDataType_copy(&src->config.itemsTemplate.variablesToAdd[i], &dst->config.itemsTemplate.variablesToAdd[i]); } res |= UA_DataSetMetaDataType_copy(&src->config.itemsTemplate.metaData, &dst->config.itemsTemplate.metaData); break; default: res = UA_STATUSCODE_BADINVALIDARGUMENT; break; } if(res != UA_STATUSCODE_GOOD) UA_PublishedDataSetConfig_clear(dst); return res; } UA_StatusCode UA_Server_getPublishedDataSetConfig(UA_Server *server, const UA_NodeId pds, UA_PublishedDataSetConfig *config) { if(!config) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_PublishedDataSet *currentPDS = UA_PublishedDataSet_findPDSbyId(server, pds); if(!currentPDS) return UA_STATUSCODE_BADNOTFOUND; return UA_PublishedDataSetConfig_copy(¤tPDS->config, config); } UA_StatusCode UA_Server_getPublishedDataSetMetaData(UA_Server *server, const UA_NodeId pds, UA_DataSetMetaDataType *metaData) { if(!metaData) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_PublishedDataSet *currentPDS = UA_PublishedDataSet_findPDSbyId(server, pds); if(!currentPDS) return UA_STATUSCODE_BADNOTFOUND; return UA_DataSetMetaDataType_copy(¤tPDS->dataSetMetaData, metaData); } UA_PublishedDataSet * UA_PublishedDataSet_findPDSbyId(UA_Server *server, UA_NodeId identifier) { UA_PublishedDataSet *tmpPDS = NULL; TAILQ_FOREACH(tmpPDS, &server->pubSubManager.publishedDataSets, listEntry) { if(UA_NodeId_equal(&tmpPDS->identifier, &identifier)) break; } return tmpPDS; } void UA_PublishedDataSetConfig_clear(UA_PublishedDataSetConfig *pdsConfig) { //delete pds config UA_String_clear(&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_clear(&pdsConfig->config.itemsTemplate.variablesToAdd[i]); } UA_free(pdsConfig->config.itemsTemplate.variablesToAdd); } UA_DataSetMetaDataType_clear(&pdsConfig->config.itemsTemplate.metaData); break; default: break; } } void UA_PublishedDataSet_clear(UA_Server *server, UA_PublishedDataSet *publishedDataSet) { UA_DataSetField *field, *tmpField; TAILQ_FOREACH_SAFE(field, &publishedDataSet->fields, listEntry, tmpField) { UA_Server_removeDataSetField(server, field->identifier); } UA_PublishedDataSetConfig_clear(&publishedDataSet->config); UA_DataSetMetaDataType_clear(&publishedDataSet->dataSetMetaData); UA_NodeId_clear(&publishedDataSet->identifier); } /* The fieldMetaData variable has to be cleaned up external in case of an error */ static UA_StatusCode generateFieldMetaData(UA_Server *server, UA_DataSetField *field, UA_FieldMetaData *fieldMetaData) { if(field->config.dataSetFieldType != UA_PUBSUB_DATASETFIELD_VARIABLE) return UA_STATUSCODE_BADNOTSUPPORTED; /* Set the field identifier */ fieldMetaData->dataSetFieldId = UA_PubSubManager_generateUniqueGuid(server); /* Set the description */ fieldMetaData->description = UA_LOCALIZEDTEXT_ALLOC("", ""); /* Set the name */ const UA_DataSetVariableConfig *var = &field->config.field.variable; UA_StatusCode res = UA_String_copy(&var->fieldNameAlias, &fieldMetaData->name); UA_CHECK_STATUS(res, return res); /* Static value source. ToDo after freeze PR, the value source must be * checked (other behavior for static value source) */ if(var->rtValueSource.rtFieldSourceEnabled && !var->rtValueSource.rtInformationModelNode) { const UA_DataValue *svs = *var->rtValueSource.staticValueSource; if(svs->value.arrayDimensionsSize > 0) { fieldMetaData->arrayDimensions = (UA_UInt32 *) UA_calloc(svs->value.arrayDimensionsSize, sizeof(UA_UInt32)); if(fieldMetaData->arrayDimensions == NULL) return UA_STATUSCODE_BADOUTOFMEMORY; memcpy(fieldMetaData->arrayDimensions, svs->value.arrayDimensions, sizeof(UA_UInt32) * svs->value.arrayDimensionsSize); } fieldMetaData->arrayDimensionsSize = svs->value.arrayDimensionsSize; res = UA_NodeId_copy(&svs->value.type->typeId, &fieldMetaData->dataType); UA_CHECK_STATUS(res, return res); //TODO collect value rank for the static field source fieldMetaData->properties = NULL; fieldMetaData->propertiesSize = 0; fieldMetaData->fieldFlags = UA_DATASETFIELDFLAGS_NONE; return UA_STATUSCODE_GOOD; } /* Set the Array Dimensions */ const UA_PublishedVariableDataType *pp = &var->publishParameters; UA_Variant value; UA_Variant_init(&value); res = UA_Server_readArrayDimensions(server, pp->publishedVariable, &value); UA_CHECK_STATUS_LOG(res, return res, WARNING, &server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub meta data generation. Reading the array dimensions failed."); if(value.arrayDimensionsSize > 0) { fieldMetaData->arrayDimensions = (UA_UInt32 *) UA_calloc(value.arrayDimensionsSize, sizeof(UA_UInt32)); if(!fieldMetaData->arrayDimensions) return UA_STATUSCODE_BADOUTOFMEMORY; memcpy(fieldMetaData->arrayDimensions, value.arrayDimensions, sizeof(UA_UInt32)*value.arrayDimensionsSize); } fieldMetaData->arrayDimensionsSize = value.arrayDimensionsSize; UA_Variant_clear(&value); /* Set the DataType */ res = UA_Server_readDataType(server, pp->publishedVariable, &fieldMetaData->dataType); UA_CHECK_STATUS_LOG(res, return res, WARNING, &server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub meta data generation. Reading the datatype failed."); if(!UA_NodeId_isNull(&fieldMetaData->dataType)) { const UA_DataType *currentDataType = UA_findDataTypeWithCustom(&fieldMetaData->dataType, server->config.customDataTypes); #ifdef UA_ENABLE_TYPEDESCRIPTION UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "MetaData creation. Found DataType %s.", currentDataType->typeName); #endif /* Check if the datatype is a builtInType, if yes set the builtinType. * TODO: Remove the magic number */ if(currentDataType->typeKind <= UA_DATATYPEKIND_ENUM) fieldMetaData->builtInType = (UA_Byte)currentDataType->typeKind; } else { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub meta data generation. DataType is UA_NODEID_NULL."); } /* Set the ValueRank */ UA_Int32 valueRank; res = UA_Server_readValueRank(server, pp->publishedVariable, &valueRank); UA_CHECK_STATUS_LOG(res, return res, WARNING, &server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub meta data generation. Reading the value rank failed."); fieldMetaData->valueRank = valueRank; /* PromotedField? */ if(var->promotedField) fieldMetaData->fieldFlags = UA_DATASETFIELDFLAGS_PROMOTEDFIELD; else fieldMetaData->fieldFlags = UA_DATASETFIELDFLAGS_NONE; /* Properties */ fieldMetaData->properties = NULL; fieldMetaData->propertiesSize = 0; //TODO collect the following fields*/ //fieldMetaData.builtInType //fieldMetaData.maxStringLength return UA_STATUSCODE_GOOD; } UA_DataSetFieldResult UA_Server_addDataSetField(UA_Server *server, const UA_NodeId publishedDataSet, const UA_DataSetFieldConfig *fieldConfig, UA_NodeId *fieldIdentifier) { UA_DataSetFieldResult result = {0}; if(!fieldConfig) { result.result = UA_STATUSCODE_BADINVALIDARGUMENT; return result; } UA_PublishedDataSet *currDS = UA_PublishedDataSet_findPDSbyId(server, publishedDataSet); if(!currDS) { result.result = UA_STATUSCODE_BADNOTFOUND; return result; } if(currDS->configurationFrozen) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Adding DataSetField failed. PublishedDataSet is frozen."); result.result = UA_STATUSCODE_BADCONFIGURATIONERROR; return result; } if(currDS->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_StatusCode retVal = UA_DataSetFieldConfig_copy(fieldConfig, &newField->config); if(retVal != UA_STATUSCODE_GOOD) { UA_free(newField); result.result = retVal; return result; } newField->publishedDataSet = currDS->identifier; /* Initialize the field metadata. Also generates a FieldId */ UA_FieldMetaData fmd; UA_FieldMetaData_init(&fmd); result.result = generateFieldMetaData(server, newField, &fmd); if(result.result != UA_STATUSCODE_GOOD) { UA_FieldMetaData_clear(&fmd); UA_DataSetFieldConfig_clear(&newField->config); UA_free(newField); return result; } /* Append to the metadata fields array. Point of last return. */ result.result = UA_Array_append((void**)&currDS->dataSetMetaData.fields, &currDS->dataSetMetaData.fieldsSize, &fmd, &UA_TYPES[UA_TYPES_FIELDMETADATA]); if(result.result != UA_STATUSCODE_GOOD) { UA_FieldMetaData_clear(&fmd); UA_DataSetFieldConfig_clear(&newField->config); UA_free(newField); return result; } /* Copy the identifier from the metadata. Cannot fail with a guid NodeId. */ newField->identifier = UA_NODEID_GUID(1, fmd.dataSetFieldId); if(fieldIdentifier) UA_NodeId_copy(&newField->identifier, fieldIdentifier); /* Register the field. The order of DataSetFields should be the same in both * creating and publishing. So adding DataSetFields at the the end of the * DataSets using the TAILQ structure. */ TAILQ_INSERT_TAIL(&currDS->fields, newField, listEntry); currDS->fieldSize++; if(newField->config.field.variable.promotedField) currDS->promotedFieldsCount++; /* The values of the metadata are "borrowed" in a mirrored structure in the * pds. Reset them after resizing the array. */ size_t counter = 0; UA_DataSetField *dsf; TAILQ_FOREACH(dsf, &currDS->fields, listEntry) { dsf->fieldMetaData = currDS->dataSetMetaData.fields[counter++]; } /* Update major version of parent published data set */ currDS->dataSetMetaData.configurationVersion.majorVersion = UA_PubSubConfigurationVersionTimeDifference(); result.configurationVersion.majorVersion = currDS->dataSetMetaData.configurationVersion.majorVersion; result.configurationVersion.minorVersion = currDS->dataSetMetaData.configurationVersion.minorVersion; return result; } UA_DataSetFieldResult UA_Server_removeDataSetField(UA_Server *server, const UA_NodeId dsf) { UA_DataSetFieldResult result = {0}; UA_DataSetField *currentField = UA_DataSetField_findDSFbyId(server, dsf); if(!currentField) { result.result = UA_STATUSCODE_BADNOTFOUND; return result; } if(currentField->configurationFrozen) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Remove DataSetField failed. DataSetField is frozen."); result.result = UA_STATUSCODE_BADCONFIGURATIONERROR; return result; } UA_PublishedDataSet *pds = UA_PublishedDataSet_findPDSbyId(server, currentField->publishedDataSet); if(!pds) { result.result = UA_STATUSCODE_BADNOTFOUND; return result; } if(pds->configurationFrozen) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Remove DataSetField failed. PublishedDataSet is frozen."); result.result = UA_STATUSCODE_BADCONFIGURATIONERROR; return result; } /* Reduce the counters before the config is cleaned up */ if(currentField->config.field.variable.promotedField) pds->promotedFieldsCount--; pds->fieldSize--; /* Update major version of PublishedDataSet */ pds->dataSetMetaData.configurationVersion.majorVersion = UA_PubSubConfigurationVersionTimeDifference(); /* Clean up */ currentField->fieldMetaData.arrayDimensions = NULL; currentField->fieldMetaData.properties = NULL; currentField->fieldMetaData.name = UA_STRING_NULL; currentField->fieldMetaData.description.locale = UA_STRING_NULL; currentField->fieldMetaData.description.text = UA_STRING_NULL; UA_DataSetField_clear(currentField); /* Remove */ TAILQ_REMOVE(&pds->fields, currentField, listEntry); UA_free(currentField); /* Regenerate DataSetMetaData */ pds->dataSetMetaData.fieldsSize--; if(pds->dataSetMetaData.fieldsSize > 0) { for(size_t i = 0; i < pds->dataSetMetaData.fieldsSize+1; i++) { UA_FieldMetaData_clear(&pds->dataSetMetaData.fields[i]); } UA_free(pds->dataSetMetaData.fields); UA_FieldMetaData *fieldMetaData = (UA_FieldMetaData *) UA_calloc(pds->dataSetMetaData.fieldsSize, sizeof(UA_FieldMetaData)); if(!fieldMetaData) { result.result = UA_STATUSCODE_BADOUTOFMEMORY; return result; } UA_DataSetField *tmpDSF; size_t counter = 0; TAILQ_FOREACH(tmpDSF, &pds->fields, listEntry){ result.result = generateFieldMetaData(server, tmpDSF, &fieldMetaData[counter]); if(result.result != UA_STATUSCODE_GOOD) { UA_FieldMetaData_clear(&fieldMetaData[counter]); UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub MetaData generation failed!"); break; } counter++; } pds->dataSetMetaData.fields = fieldMetaData; } else { UA_FieldMetaData_delete(pds->dataSetMetaData.fields); pds->dataSetMetaData.fields = NULL; } result.configurationVersion.majorVersion = pds->dataSetMetaData.configurationVersion.majorVersion; result.configurationVersion.minorVersion = pds->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); if(src->dataSetWriterPropertiesSize > 0) { 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) { if(!config) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_DataSetWriter *currentDataSetWriter = UA_DataSetWriter_findDSWbyId(server, dsw); if(!currentDataSetWriter) return UA_STATUSCODE_BADNOTFOUND; return UA_DataSetWriterConfig_copy(¤tDataSetWriter->config, config); } UA_StatusCode UA_Server_DataSetWriter_getState(UA_Server *server, UA_NodeId dataSetWriterIdentifier, UA_PubSubState *state) { if((server == NULL) || (state == NULL)) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_DataSetWriter *currentDataSetWriter = UA_DataSetWriter_findDSWbyId(server, dataSetWriterIdentifier); if(currentDataSetWriter == NULL) return UA_STATUSCODE_BADNOTFOUND; *state = currentDataSetWriter->state; return UA_STATUSCODE_GOOD; } UA_DataSetWriter * UA_DataSetWriter_findDSWbyId(UA_Server *server, UA_NodeId identifier) { UA_PubSubConnection *pubSubConnection; TAILQ_FOREACH(pubSubConnection, &server->pubSubManager.connections, listEntry){ UA_WriterGroup *tmpWriterGroup; LIST_FOREACH(tmpWriterGroup, &pubSubConnection->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_clear(UA_DataSetWriterConfig *pdsConfig) { UA_String_clear(&pdsConfig->name); UA_String_clear(&pdsConfig->dataSetName); for(size_t i = 0; i < pdsConfig->dataSetWriterPropertiesSize; i++) { UA_KeyValuePair_clear(&pdsConfig->dataSetWriterProperties[i]); } UA_free(pdsConfig->dataSetWriterProperties); UA_ExtensionObject_clear(&pdsConfig->messageSettings); } static void UA_DataSetWriter_clear(UA_Server *server, UA_DataSetWriter *dataSetWriter) { UA_DataSetWriterConfig_clear(&dataSetWriter->config); UA_NodeId_clear(&dataSetWriter->identifier); UA_NodeId_clear(&dataSetWriter->linkedWriterGroup); UA_NodeId_clear(&dataSetWriter->connectedDataSet); /* Delete lastSamples store */ #ifdef UA_ENABLE_PUBSUB_DELTAFRAMES for(size_t i = 0; i < dataSetWriter->lastSamplesCount; i++) { UA_DataValue_clear(&dataSetWriter->lastSamples[i].value); } UA_free(dataSetWriter->lastSamples); dataSetWriter->lastSamples = NULL; dataSetWriter->lastSamplesCount = 0; #endif } //state machine methods not part of the open62541 state machine API UA_StatusCode UA_DataSetWriter_setPubSubState(UA_Server *server, UA_PubSubState state, UA_DataSetWriter *dataSetWriter) { switch(state){ case UA_PUBSUBSTATE_DISABLED: switch (dataSetWriter->state){ case UA_PUBSUBSTATE_DISABLED: return UA_STATUSCODE_GOOD; case UA_PUBSUBSTATE_PAUSED: dataSetWriter->state = UA_PUBSUBSTATE_DISABLED; //no further action is required break; case UA_PUBSUBSTATE_OPERATIONAL: dataSetWriter->state = UA_PUBSUBSTATE_DISABLED; break; case UA_PUBSUBSTATE_ERROR: break; default: UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Received unknown PubSub state!"); } break; case UA_PUBSUBSTATE_PAUSED: switch (dataSetWriter->state){ case UA_PUBSUBSTATE_DISABLED: break; case UA_PUBSUBSTATE_PAUSED: return UA_STATUSCODE_GOOD; case UA_PUBSUBSTATE_OPERATIONAL: break; case UA_PUBSUBSTATE_ERROR: break; default: UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Received unknown PubSub state!"); } break; case UA_PUBSUBSTATE_OPERATIONAL: switch (dataSetWriter->state){ case UA_PUBSUBSTATE_DISABLED: dataSetWriter->state = UA_PUBSUBSTATE_OPERATIONAL; break; case UA_PUBSUBSTATE_PAUSED: break; case UA_PUBSUBSTATE_OPERATIONAL: return UA_STATUSCODE_GOOD; case UA_PUBSUBSTATE_ERROR: break; default: UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Received unknown PubSub state!"); } break; case UA_PUBSUBSTATE_ERROR: switch (dataSetWriter->state){ case UA_PUBSUBSTATE_DISABLED: break; case UA_PUBSUBSTATE_PAUSED: break; case UA_PUBSUBSTATE_OPERATIONAL: break; case UA_PUBSUBSTATE_ERROR: return UA_STATUSCODE_GOOD; default: UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Received unknown PubSub state!"); } break; default: UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Received unknown PubSub state!"); } return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Server_addDataSetWriter(UA_Server *server, const UA_NodeId writerGroup, const UA_NodeId dataSet, const UA_DataSetWriterConfig *dataSetWriterConfig, UA_NodeId *writerIdentifier) { if(!dataSetWriterConfig) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_PublishedDataSet *currentDataSetContext = UA_PublishedDataSet_findPDSbyId(server, dataSet); if(!currentDataSetContext) return UA_STATUSCODE_BADNOTFOUND; if(currentDataSetContext->configurationFrozen){ UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Adding DataSetWriter failed. PublishedDataSet is frozen."); return UA_STATUSCODE_BADCONFIGURATIONERROR; } UA_WriterGroup *wg = UA_WriterGroup_findWGbyId(server, writerGroup); if(!wg) return UA_STATUSCODE_BADNOTFOUND; if(wg->configurationFrozen) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Adding DataSetWriter failed. WriterGroup is frozen."); return UA_STATUSCODE_BADCONFIGURATIONERROR; } if(wg->config.rtLevel != UA_PUBSUB_RT_NONE) { UA_DataSetField *tmpDSF; TAILQ_FOREACH(tmpDSF, ¤tDataSetContext->fields, listEntry) { if(!tmpDSF->config.field.variable.rtValueSource.rtFieldSourceEnabled && !tmpDSF->config.field.variable.rtValueSource.rtInformationModelNode) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Adding DataSetWriter failed. Fields in PDS are not RT capable."); return UA_STATUSCODE_BADCONFIGURATIONERROR; } } } UA_DataSetWriter *newDataSetWriter = (UA_DataSetWriter *) UA_calloc(1, sizeof(UA_DataSetWriter)); if(!newDataSetWriter) return UA_STATUSCODE_BADOUTOFMEMORY; newDataSetWriter->componentType = UA_PUBSUB_COMPONENT_DATASETWRITER; UA_StatusCode res = UA_STATUSCODE_GOOD; if(wg->state == UA_PUBSUBSTATE_OPERATIONAL) { res = UA_DataSetWriter_setPubSubState(server, UA_PUBSUBSTATE_OPERATIONAL, newDataSetWriter); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Add DataSetWriter failed. setPubSubState failed."); UA_free(newDataSetWriter); return res; } } /* Copy the config into the new dataSetWriter */ res = UA_DataSetWriterConfig_copy(dataSetWriterConfig, &newDataSetWriter->config); UA_CHECK_STATUS(res, UA_free(newDataSetWriter); return res); /* 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 */ if(currentDataSetContext->fieldSize > 0) { newDataSetWriter->lastSamples = (UA_DataSetWriterSample*) UA_calloc(currentDataSetContext->fieldSize, sizeof(UA_DataSetWriterSample)); if(!newDataSetWriter->lastSamples) { UA_DataSetWriterConfig_clear(&newDataSetWriter->config); UA_free(newDataSetWriter); return UA_STATUSCODE_BADOUTOFMEMORY; } newDataSetWriter->lastSamplesCount = currentDataSetContext->fieldSize; for(size_t i = 0; i < newDataSetWriter->lastSamplesCount; i++) { UA_DataValue_init(&newDataSetWriter->lastSamples[i].value); newDataSetWriter->lastSamples[i].valueChanged = false; } } #endif /* Connect PublishedDataSet with DataSetWriter */ newDataSetWriter->connectedDataSet = currentDataSetContext->identifier; newDataSetWriter->linkedWriterGroup = wg->identifier; /* Add the new writer to the group */ LIST_INSERT_HEAD(&wg->writers, newDataSetWriter, listEntry); wg->writersCount++; #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL res |= addDataSetWriterRepresentation(server, newDataSetWriter); #else UA_PubSubManager_generateUniqueNodeId(&server->pubSubManager, &newDataSetWriter->identifier); #endif if(writerIdentifier) UA_NodeId_copy(&newDataSetWriter->identifier, writerIdentifier); return res; } UA_StatusCode UA_DataSetWriter_remove(UA_Server *server, UA_WriterGroup *linkedWriterGroup, UA_DataSetWriter *dataSetWriter) { /* Frozen? */ if(linkedWriterGroup->configurationFrozen) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Remove DataSetWriter failed. WriterGroup is frozen."); return UA_STATUSCODE_BADCONFIGURATIONERROR; } /* Remove from information model */ #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL removeDataSetWriterRepresentation(server, dataSetWriter); #endif /* Remove DataSetWriter from group */ UA_DataSetWriter_clear(server, dataSetWriter); LIST_REMOVE(dataSetWriter, listEntry); linkedWriterGroup->writersCount--; UA_free(dataSetWriter); return UA_STATUSCODE_GOOD; } 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; if(dataSetWriter->configurationFrozen) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Remove DataSetWriter failed. DataSetWriter is frozen."); return UA_STATUSCODE_BADCONFIGURATIONERROR; } UA_WriterGroup *linkedWriterGroup = UA_WriterGroup_findWGbyId(server, dataSetWriter->linkedWriterGroup); if(!linkedWriterGroup) return UA_STATUSCODE_BADNOTFOUND; return UA_DataSetWriter_remove(server, linkedWriterGroup, dataSetWriter); } /**********************************************/ /* DataSetField */ /**********************************************/ static void UA_DataSetField_clear(UA_DataSetField *field) { UA_DataSetFieldConfig_clear(&field->config); UA_NodeId_clear(&field->identifier); UA_NodeId_clear(&field->publishedDataSet); UA_FieldMetaData_clear(&field->fieldMetaData); } UA_StatusCode UA_DataSetFieldConfig_copy(const UA_DataSetFieldConfig *src, UA_DataSetFieldConfig *dst) { if(src->dataSetFieldType != UA_PUBSUB_DATASETFIELD_VARIABLE) return UA_STATUSCODE_BADNOTSUPPORTED; memcpy(dst, src, sizeof(UA_DataSetFieldConfig)); UA_StatusCode res = UA_STATUSCODE_GOOD; res |= UA_String_copy(&src->field.variable.fieldNameAlias, &dst->field.variable.fieldNameAlias); res |= UA_PublishedVariableDataType_copy(&src->field.variable.publishParameters, &dst->field.variable.publishParameters); if(res != UA_STATUSCODE_GOOD) UA_DataSetFieldConfig_clear(dst); return res; } UA_StatusCode UA_Server_getDataSetFieldConfig(UA_Server *server, const UA_NodeId dsf, UA_DataSetFieldConfig *config) { if(!config) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_DataSetField *currentDataSetField = UA_DataSetField_findDSFbyId(server, dsf); if(!currentDataSetField) return UA_STATUSCODE_BADNOTFOUND; return UA_DataSetFieldConfig_copy(¤tDataSetField->config, config); } UA_DataSetField * UA_DataSetField_findDSFbyId(UA_Server *server, UA_NodeId identifier) { UA_PublishedDataSet *tmpPDS; TAILQ_FOREACH(tmpPDS, &server->pubSubManager.publishedDataSets, listEntry) { UA_DataSetField *tmpField; TAILQ_FOREACH(tmpField, &tmpPDS->fields, listEntry) { if(UA_NodeId_equal(&tmpField->identifier, &identifier)) return tmpField; } } return NULL; } void UA_DataSetFieldConfig_clear(UA_DataSetFieldConfig *dataSetFieldConfig) { if(dataSetFieldConfig->dataSetFieldType == UA_PUBSUB_DATASETFIELD_VARIABLE) { UA_String_clear(&dataSetFieldConfig->field.variable.fieldNameAlias); UA_PublishedVariableDataType_clear(&dataSetFieldConfig->field.variable.publishParameters); } } /*********************************************************/ /* PublishValues handling */ /*********************************************************/ /* Compare two variants. Internally used for value change detection. */ #ifdef UA_ENABLE_PUBSUB_DELTAFRAMES static UA_Boolean valueChangedVariant(UA_Variant *oldValue, UA_Variant *newValue) { if(!oldValue || !newValue) return false; size_t oldValueEncodingSize = UA_calcSizeBinary(oldValue, &UA_TYPES[UA_TYPES_VARIANT]); size_t newValueEncodingSize = UA_calcSizeBinary(newValue, &UA_TYPES[UA_TYPES_VARIANT]); if(oldValueEncodingSize == 0 || newValueEncodingSize == 0) return false; if(oldValueEncodingSize != newValueEncodingSize) return true; UA_ByteString oldValueEncoding = {0}; UA_StatusCode res = UA_ByteString_allocBuffer(&oldValueEncoding, oldValueEncodingSize); if(res != UA_STATUSCODE_GOOD) return false; UA_ByteString newValueEncoding = {0}; res = UA_ByteString_allocBuffer(&newValueEncoding, newValueEncodingSize); if(res != UA_STATUSCODE_GOOD) { UA_ByteString_clear(&oldValueEncoding); 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]; UA_Boolean compareResult = false; /* default */ res = UA_encodeBinaryInternal(oldValue, &UA_TYPES[UA_TYPES_VARIANT], &bufPosOldValue, &bufEndOldValue, NULL, NULL); if(res != UA_STATUSCODE_GOOD) goto cleanup; res = UA_encodeBinaryInternal(newValue, &UA_TYPES[UA_TYPES_VARIANT], &bufPosNewValue, &bufEndNewValue, NULL, NULL); if(res != UA_STATUSCODE_GOOD) goto cleanup; oldValueEncoding.length = (uintptr_t)bufPosOldValue - (uintptr_t)oldValueEncoding.data; newValueEncoding.length = (uintptr_t)bufPosNewValue - (uintptr_t)newValueEncoding.data; compareResult = !UA_ByteString_equal(&oldValueEncoding, &newValueEncoding); cleanup: UA_ByteString_clear(&oldValueEncoding); UA_ByteString_clear(&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) { UA_PublishedVariableDataType *params = &field->config.field.variable.publishParameters; /* Read the value */ if(field->config.field.variable.rtValueSource.rtInformationModelNode) { const UA_VariableNode *rtNode = (const UA_VariableNode *) UA_NODESTORE_GET(server, ¶ms->publishedVariable); *value = **rtNode->valueBackend.backend.external.value; value->value.storageType = UA_VARIANT_DATA_NODELETE; UA_NODESTORE_RELEASE(server, (const UA_Node *) rtNode); } else if(field->config.field.variable.rtValueSource.rtFieldSourceEnabled == UA_FALSE){ UA_ReadValueId rvid; UA_ReadValueId_init(&rvid); rvid.nodeId = params->publishedVariable; rvid.attributeId = params->attributeId; rvid.indexRange = params->indexRange; *value = UA_Server_read(server, &rvid, UA_TIMESTAMPSTORETURN_BOTH); } else { *value = **field->config.field.variable.rtValueSource.staticValueSource; value->value.storageType = UA_VARIANT_DATA_NODELETE; } } 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 dataSetMessage->data.keyFrameData.fieldNames = (UA_String *) UA_Array_new(currentDataSet->fieldSize, &UA_TYPES[UA_TYPES_STRING]); if(!dataSetMessage->data.keyFrameData.fieldNames) { UA_DataSetMessage_clear(dataSetMessage); return UA_STATUSCODE_BADOUTOFMEMORY; } #endif /* Loop over the fields */ size_t counter = 0; UA_DataSetField *dsf; TAILQ_FOREACH(dsf, ¤tDataSet->fields, listEntry) { #ifdef UA_ENABLE_JSON_ENCODING /* Set the field name alias */ 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_clear(&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; if(currentDataSet->fieldSize == 0) return UA_STATUSCODE_GOOD; UA_DataSetField *dsf; size_t counter = 0; TAILQ_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 */ UA_DataSetWriterSample *ls = &dataSetWriter->lastSamples[counter]; if(valueChangedVariant(&ls->value.value, &value.value)) { /* increase fieldCount for current delta message */ dataSetMessage->data.deltaFrameData.fieldCount++; ls->valueChanged = true; /* Update last stored sample */ UA_DataValue_clear(&ls->value); ls->value = value; } else { UA_DataValue_clear(&value); ls->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. */ 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)); /* The configuration Flags are included * inside the std. defined UA_UadpDataSetWriterMessageDataType */ UA_UadpDataSetWriterMessageDataType defaultUadpConfiguration; UA_UadpDataSetWriterMessageDataType *dsm = NULL; UA_JsonDataSetWriterMessageDataType *jsonDsm = NULL; const UA_ExtensionObject *ms = &dataSetWriter->config.messageSettings; if((ms->encoding == UA_EXTENSIONOBJECT_DECODED || ms->encoding == UA_EXTENSIONOBJECT_DECODED_NODELETE) && ms->content.decoded.type == &UA_TYPES[UA_TYPES_UADPDATASETWRITERMESSAGEDATATYPE]) { dsm = (UA_UadpDataSetWriterMessageDataType*)ms->content.decoded.data; /* type is UADP */ } else if((ms->encoding == UA_EXTENSIONOBJECT_DECODED || ms->encoding == UA_EXTENSIONOBJECT_DECODED_NODELETE) && ms->content.decoded.type == &UA_TYPES[UA_TYPES_JSONDATASETWRITERMESSAGEDATATYPE]) { jsonDsm = (UA_JsonDataSetWriterMessageDataType*)ms->content.decoded.data; /* type is JSON */ } 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); dsm = &defaultUadpConfiguration; /* type is UADP */ } /* The field encoding depends on the flags inside the writer config. */ 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(dsm) { /* Sanity-test the configuration */ if(dsm->networkMessageNumber != 0 || dsm->dataSetOffset != 0 || dsm->configuredSize != 0) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Static DSM configuration not supported. Using defaults"); dsm->networkMessageNumber = 0; dsm->dataSetOffset = 0; dsm->configuredSize = 0; } /* Std: 'The DataSetMessageContentMask defines the flags for the content * of the DataSetMessage header.' */ if((u64)dsm->dataSetMessageContentMask & (u64)UA_UADPDATASETMESSAGECONTENTMASK_MAJORVERSION) { dataSetMessage->header.configVersionMajorVersionEnabled = true; dataSetMessage->header.configVersionMajorVersion = currentDataSet->dataSetMetaData.configurationVersion.majorVersion; } if((u64)dsm->dataSetMessageContentMask & (u64)UA_UADPDATASETMESSAGECONTENTMASK_MINORVERSION) { dataSetMessage->header.configVersionMinorVersionEnabled = true; dataSetMessage->header.configVersionMinorVersion = currentDataSet->dataSetMetaData.configurationVersion.minorVersion; } if((u64)dsm->dataSetMessageContentMask & (u64)UA_UADPDATASETMESSAGECONTENTMASK_SEQUENCENUMBER) { dataSetMessage->header.dataSetMessageSequenceNrEnabled = true; dataSetMessage->header.dataSetMessageSequenceNr = dataSetWriter->actualDataSetMessageSequenceCount; } if((u64)dsm->dataSetMessageContentMask & (u64)UA_UADPDATASETMESSAGECONTENTMASK_TIMESTAMP) { dataSetMessage->header.timestampEnabled = true; dataSetMessage->header.timestamp = UA_DateTime_now(); } /* TODO: Picoseconds resolution not supported atm */ if((u64)dsm->dataSetMessageContentMask & (u64)UA_UADPDATASETMESSAGECONTENTMASK_PICOSECONDS) { dataSetMessage->header.picoSecondsIncluded = false; } /* TODO: Statuscode not supported yet */ if((u64)dsm->dataSetMessageContentMask & (u64)UA_UADPDATASETMESSAGECONTENTMASK_STATUS) { dataSetMessage->header.statusEnabled = true; } } else if(jsonDsm) { if((u64)jsonDsm->dataSetMessageContentMask & (u64)UA_JSONDATASETMESSAGECONTENTMASK_METADATAVERSION) { dataSetMessage->header.configVersionMajorVersionEnabled = true; dataSetMessage->header.configVersionMajorVersion = currentDataSet->dataSetMetaData.configurationVersion.majorVersion; } if((u64)jsonDsm->dataSetMessageContentMask & (u64)UA_JSONDATASETMESSAGECONTENTMASK_METADATAVERSION) { dataSetMessage->header.configVersionMinorVersionEnabled = true; dataSetMessage->header.configVersionMinorVersion = currentDataSet->dataSetMetaData.configurationVersion.minorVersion; } if((u64)jsonDsm->dataSetMessageContentMask & (u64)UA_JSONDATASETMESSAGECONTENTMASK_SEQUENCENUMBER) { dataSetMessage->header.dataSetMessageSequenceNrEnabled = true; dataSetMessage->header.dataSetMessageSequenceNr = dataSetWriter->actualDataSetMessageSequenceCount; } if((u64)jsonDsm->dataSetMessageContentMask & (u64)UA_JSONDATASETMESSAGECONTENTMASK_TIMESTAMP) { dataSetMessage->header.timestampEnabled = true; dataSetMessage->header.timestamp = UA_DateTime_now(); } /* TODO: Statuscode not supported yet */ if((u64)jsonDsm->dataSetMessageContentMask & (u64)UA_JSONDATASETMESSAGECONTENTMASK_STATUS) { dataSetMessage->header.statusEnabled = true; } } /* 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(dsm) { #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_clear(&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 } return UA_PubSubDataSetWriter_generateKeyFrameMessage(server, dataSetMessage, dataSetWriter); } #endif /* UA_ENABLE_PUBSUB */ /**** amalgamated original file "/src/pubsub/ua_pubsub_writergroup.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-2019 Fraunhofer IOSB (Author: Andreas Ebner) * Copyright (c) 2019 Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright (c) 2019 Kalycito Infotech Private Limited * Copyright (c) 2020 Yannick Wallerer, Siemens AG * Copyright (c) 2020 Thomas Fischer, Siemens AG * Copyright (c) 2021 Fraunhofer IOSB (Author: Jan Hermes) */ #ifdef UA_ENABLE_PUBSUB /* conditional compilation */ #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL #endif #define UA_MAX_STACKBUF 128 /* Max size of network messages on the stack */ static void UA_WriterGroup_clear(UA_Server *server, UA_WriterGroup *writerGroup); static UA_StatusCode generateNetworkMessage(UA_PubSubConnection *connection, UA_WriterGroup *wg, UA_DataSetMessage *dsm, UA_UInt16 *writerIds, UA_Byte dsmCount, UA_ExtensionObject *messageSettings, UA_ExtensionObject *transportSettings, UA_NetworkMessage *networkMessage); UA_StatusCode UA_Server_addWriterGroup(UA_Server *server, const UA_NodeId connection, const UA_WriterGroupConfig *writerGroupConfig, UA_NodeId *writerGroupIdentifier) { 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; if(currentConnectionContext->configurationFrozen){ UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Adding WriterGroup failed. PubSubConnection is frozen."); return UA_STATUSCODE_BADCONFIGURATIONERROR; } /* Validate messageSettings type */ const UA_ExtensionObject *ms = &writerGroupConfig->messageSettings; if(ms->content.decoded.type) { if(writerGroupConfig->encodingMimeType == UA_PUBSUB_ENCODING_JSON && (ms->encoding != UA_EXTENSIONOBJECT_DECODED || ms->content.decoded.type != &UA_TYPES[UA_TYPES_JSONWRITERGROUPMESSAGEDATATYPE])) { return UA_STATUSCODE_BADTYPEMISMATCH; } if(writerGroupConfig->encodingMimeType == UA_PUBSUB_ENCODING_UADP && (ms->encoding != UA_EXTENSIONOBJECT_DECODED || ms->content.decoded.type != &UA_TYPES[UA_TYPES_UADPWRITERGROUPMESSAGEDATATYPE])) { return UA_STATUSCODE_BADTYPEMISMATCH; } } /* Allocate new WriterGroup */ UA_WriterGroup *newWriterGroup = (UA_WriterGroup*)UA_calloc(1, sizeof(UA_WriterGroup)); if(!newWriterGroup) return UA_STATUSCODE_BADOUTOFMEMORY; newWriterGroup->componentType = UA_PUBSUB_COMPONENT_WRITERGROUP; newWriterGroup->linkedConnection = currentConnectionContext; /* Deep copy of the config */ UA_WriterGroupConfig *newConfig = &newWriterGroup->config; UA_StatusCode res = UA_WriterGroupConfig_copy(writerGroupConfig, newConfig); if(res != UA_STATUSCODE_GOOD) { UA_free(newWriterGroup); return res; } /* Create the datatype value if not present */ if(!newConfig->messageSettings.content.decoded.type) { UA_UadpWriterGroupMessageDataType *wgm = UA_UadpWriterGroupMessageDataType_new(); newConfig->messageSettings.content.decoded.data = wgm; newConfig->messageSettings.content.decoded.type = &UA_TYPES[UA_TYPES_UADPWRITERGROUPMESSAGEDATATYPE]; newConfig->messageSettings.encoding = UA_EXTENSIONOBJECT_DECODED; } /* Attach to the connection */ LIST_INSERT_HEAD(¤tConnectionContext->writerGroups, newWriterGroup, listEntry); currentConnectionContext->writerGroupsSize++; /* Add representation / create unique identifier */ #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL res = addWriterGroupRepresentation(server, newWriterGroup); #else UA_PubSubManager_generateUniqueNodeId(&server->pubSubManager, &newWriterGroup->identifier); #endif if(writerGroupIdentifier) UA_NodeId_copy(&newWriterGroup->identifier, writerGroupIdentifier); return res; } 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; if(wg->configurationFrozen) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Delete WriterGroup failed. WriterGroup is frozen."); return UA_STATUSCODE_BADCONFIGURATIONERROR; } UA_PubSubConnection *connection = wg->linkedConnection; if(!connection) return UA_STATUSCODE_BADNOTFOUND; if(connection->configurationFrozen) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Delete WriterGroup failed. PubSubConnection is frozen."); return UA_STATUSCODE_BADCONFIGURATIONERROR; } if(wg->state == UA_PUBSUBSTATE_OPERATIONAL) { /* Unregister the publish callback */ if(wg->config.pubsubManagerCallback.removeCustomCallback) wg->config.pubsubManagerCallback. removeCustomCallback(server, wg->identifier, wg->publishCallbackId); else UA_PubSubManager_removeRepeatedPubSubCallback(server, wg->publishCallbackId); } UA_DataSetWriter *dsw, *dsw_tmp; LIST_FOREACH_SAFE(dsw, &wg->writers, listEntry, dsw_tmp) { UA_DataSetWriter_remove(server, wg, dsw); } connection->writerGroupsSize--; #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL removeGroupRepresentation(server, wg); #endif UA_WriterGroup_clear(server, wg); LIST_REMOVE(wg, listEntry); UA_free(wg); return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Server_freezeWriterGroupConfiguration(UA_Server *server, const UA_NodeId writerGroup) { UA_WriterGroup *wg = UA_WriterGroup_findWGbyId(server, writerGroup); if(!wg) return UA_STATUSCODE_BADNOTFOUND; /* PubSubConnection freezeCounter++ */ UA_PubSubConnection *pubSubConnection = wg->linkedConnection; pubSubConnection->configurationFreezeCounter++; pubSubConnection->configurationFrozen = true; /* WriterGroup freeze */ wg->configurationFrozen = true; /* DataSetWriter freeze */ UA_DataSetWriter *dataSetWriter; LIST_FOREACH(dataSetWriter, &wg->writers, listEntry) { dataSetWriter->configurationFrozen = true; /* PublishedDataSet freezeCounter++ */ UA_PublishedDataSet *publishedDataSet = UA_PublishedDataSet_findPDSbyId(server, dataSetWriter->connectedDataSet); publishedDataSet->configurationFreezeCounter++; publishedDataSet->configurationFrozen = true; /* DataSetFields freeze */ UA_DataSetField *dataSetField; TAILQ_FOREACH(dataSetField, &publishedDataSet->fields, listEntry) { dataSetField->configurationFrozen = true; } } if(wg->config.rtLevel != UA_PUBSUB_RT_FIXED_SIZE) return UA_STATUSCODE_GOOD; /* Freeze the RT writer configuration */ size_t dsmCount = 0; if(wg->config.encodingMimeType != UA_PUBSUB_ENCODING_UADP) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub-RT configuration fail: Non-RT capable encoding."); return UA_STATUSCODE_BADNOTSUPPORTED; } //TODO Clarify: should we only allow = maxEncapsulatedDataSetMessageCount == 1 with RT? //TODO Clarify: Behaviour if the finale size is more than MTU /* Generate data set messages */ UA_STACKARRAY(UA_UInt16, dsWriterIds, wg->writersCount); UA_STACKARRAY(UA_DataSetMessage, dsmStore, wg->writersCount); UA_StatusCode res = UA_STATUSCODE_GOOD; UA_DataSetWriter *dsw; LIST_FOREACH(dsw, &wg->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; } if(pds->promotedFieldsCount > 0) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub-RT configuration fail: PDS contains promoted fields."); return UA_STATUSCODE_BADNOTSUPPORTED; } /* Test the DataSetFields */ UA_DataSetField *dsf; TAILQ_FOREACH(dsf, &pds->fields, listEntry) { const UA_VariableNode *rtNode = (const UA_VariableNode *) UA_NODESTORE_GET(server, &dsf->config.field.variable.publishParameters.publishedVariable); if(rtNode != NULL && rtNode->valueBackend.backendType != UA_VALUEBACKENDTYPE_EXTERNAL) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub-RT configuration fail: PDS contains field without external data source."); UA_NODESTORE_RELEASE(server, (const UA_Node *) rtNode); return UA_STATUSCODE_BADNOTSUPPORTED; } UA_NODESTORE_RELEASE(server, (const UA_Node *) rtNode); if((UA_NodeId_equal(&dsf->fieldMetaData.dataType, &UA_TYPES[UA_TYPES_STRING].typeId) || UA_NodeId_equal(&dsf->fieldMetaData.dataType, &UA_TYPES[UA_TYPES_BYTESTRING].typeId)) && dsf->fieldMetaData.maxStringLength == 0) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub-RT configuration fail: " "PDS contains String/ByteString with dynamic length."); return UA_STATUSCODE_BADNOTSUPPORTED; } else if(!UA_DataType_isNumeric(UA_findDataType(&dsf->fieldMetaData.dataType)) && !UA_NodeId_equal(&dsf->fieldMetaData.dataType, &UA_TYPES[UA_TYPES_BOOLEAN].typeId)) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub-RT configuration fail: " "PDS contains variable with dynamic size."); return UA_STATUSCODE_BADNOTSUPPORTED; } } /* Generate the DSM */ res = UA_DataSetWriter_generateDataSetMessage(server, &dsmStore[dsmCount], dsw); if(res != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub RT Offset calculation: DataSetMessage buffering failed"); continue; } dsWriterIds[dsmCount] = dsw->config.dataSetWriterId; dsmCount++; } /* Define variables here for goto */ size_t msgSize; UA_ByteString buf; UA_NetworkMessage networkMessage; const UA_Byte *bufEnd; UA_Byte *bufPos; if(res != UA_STATUSCODE_GOOD) goto cleanup_dsm; memset(&networkMessage, 0, sizeof(networkMessage)); res = generateNetworkMessage(pubSubConnection, wg, dsmStore, dsWriterIds, (UA_Byte) dsmCount, &wg->config.messageSettings, &wg->config.transportSettings, &networkMessage); if(res != UA_STATUSCODE_GOOD) goto cleanup_dsm; /* Generate the offset-buffer (done inside calcSizeBinary) */ memset(&wg->bufferedMessage, 0, sizeof(UA_NetworkMessageOffsetBuffer)); UA_NetworkMessage_calcSizeBinary(&networkMessage, &wg->bufferedMessage); /* Allocate the buffer. Allocate on the stack if the buffer is small. */ msgSize = UA_NetworkMessage_calcSizeBinary(&networkMessage, NULL); res = UA_ByteString_allocBuffer(&buf, msgSize); if(res != UA_STATUSCODE_GOOD) goto cleanup; wg->bufferedMessage.buffer = buf; /* Encode the NetworkMessage */ bufEnd = &wg->bufferedMessage.buffer.data[wg->bufferedMessage.buffer.length]; bufPos = wg->bufferedMessage.buffer.data; UA_NetworkMessage_encodeBinary(&networkMessage, &bufPos, bufEnd, NULL); cleanup: UA_free(networkMessage.payload.dataSetPayload.sizes); /* Clean up DSM */ cleanup_dsm: for(size_t i = 0; i < dsmCount; i++){ UA_free(dsmStore[i].data.keyFrameData.dataSetFields); #ifdef UA_ENABLE_JSON_ENCODING UA_Array_delete(dsmStore[i].data.keyFrameData.fieldNames, dsmStore[i].data.keyFrameData.fieldCount, &UA_TYPES[UA_TYPES_STRING]); #endif } return res; } UA_StatusCode UA_Server_unfreezeWriterGroupConfiguration(UA_Server *server, const UA_NodeId writerGroup) { UA_WriterGroup *wg = UA_WriterGroup_findWGbyId(server, writerGroup); if(!wg) return UA_STATUSCODE_BADNOTFOUND; //if(wg->config.rtLevel == UA_PUBSUB_RT_NONE){ // UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, // "PubSub configuration freeze without RT configuration has no effect."); // return UA_STATUSCODE_BADCONFIGURATIONERROR; //} //PubSubConnection freezeCounter-- UA_PubSubConnection *pubSubConnection = wg->linkedConnection; pubSubConnection->configurationFreezeCounter--; if(pubSubConnection->configurationFreezeCounter == 0){ pubSubConnection->configurationFrozen = UA_FALSE; } //WriterGroup unfreeze wg->configurationFrozen = UA_FALSE; //DataSetWriter unfreeze UA_DataSetWriter *dataSetWriter; LIST_FOREACH(dataSetWriter, &wg->writers, listEntry) { UA_PublishedDataSet *publishedDataSet = UA_PublishedDataSet_findPDSbyId(server, dataSetWriter->connectedDataSet); //PublishedDataSet freezeCounter-- publishedDataSet->configurationFreezeCounter--; if(publishedDataSet->configurationFreezeCounter == 0){ publishedDataSet->configurationFrozen = UA_FALSE; UA_DataSetField *dataSetField; TAILQ_FOREACH(dataSetField, &publishedDataSet->fields, listEntry){ dataSetField->configurationFrozen = UA_FALSE; } } dataSetWriter->configurationFrozen = UA_FALSE; } UA_NetworkMessageOffsetBuffer_clear(&wg->bufferedMessage); return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Server_setWriterGroupOperational(UA_Server *server, const UA_NodeId writerGroup) { UA_WriterGroup *wg = UA_WriterGroup_findWGbyId(server, writerGroup); if(!wg) return UA_STATUSCODE_BADNOTFOUND; return UA_WriterGroup_setPubSubState(server, UA_PUBSUBSTATE_OPERATIONAL, wg); } UA_StatusCode UA_Server_setWriterGroupDisabled(UA_Server *server, const UA_NodeId writerGroup) { UA_WriterGroup *wg = UA_WriterGroup_findWGbyId(server, writerGroup); if(!wg) return UA_STATUSCODE_BADNOTFOUND; return UA_WriterGroup_setPubSubState(server, UA_PUBSUBSTATE_DISABLED, wg); } UA_StatusCode UA_WriterGroupConfig_copy(const UA_WriterGroupConfig *src, UA_WriterGroupConfig *dst){ UA_StatusCode res = UA_STATUSCODE_GOOD; memcpy(dst, src, sizeof(UA_WriterGroupConfig)); res |= UA_String_copy(&src->name, &dst->name); res |= UA_ExtensionObject_copy(&src->transportSettings, &dst->transportSettings); res |= UA_ExtensionObject_copy(&src->messageSettings, &dst->messageSettings); if(src->groupPropertiesSize > 0) { 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++) { res |= UA_KeyValuePair_copy(&src->groupProperties[i], &dst->groupProperties[i]); } } if(res != UA_STATUSCODE_GOOD) UA_WriterGroupConfig_clear(dst); return res; } UA_StatusCode UA_Server_getWriterGroupConfig(UA_Server *server, const UA_NodeId writerGroup, UA_WriterGroupConfig *config) { if(!config) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_WriterGroup *currentWG = UA_WriterGroup_findWGbyId(server, writerGroup); if(!currentWG) return UA_STATUSCODE_BADNOTFOUND; return UA_WriterGroupConfig_copy(¤tWG->config, config); } 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; if(currentWriterGroup->configurationFrozen){ UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Modify WriterGroup failed. WriterGroup is frozen."); return UA_STATUSCODE_BADCONFIGURATIONERROR; } //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) { if(currentWriterGroup->config.rtLevel == UA_PUBSUB_RT_NONE && currentWriterGroup->state == UA_PUBSUBSTATE_OPERATIONAL) { if(currentWriterGroup->config.pubsubManagerCallback.removeCustomCallback) currentWriterGroup->config.pubsubManagerCallback. removeCustomCallback(server, currentWriterGroup->identifier, currentWriterGroup->publishCallbackId); else UA_PubSubManager_removeRepeatedPubSubCallback(server, currentWriterGroup->publishCallbackId); currentWriterGroup->config.publishingInterval = config->publishingInterval; UA_WriterGroup_addPublishCallback(server, currentWriterGroup); } else { currentWriterGroup->config.publishingInterval = config->publishingInterval; } } 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_StatusCode UA_Server_WriterGroup_getState(UA_Server *server, UA_NodeId writerGroupIdentifier, UA_PubSubState *state) { if((server == NULL) || (state == NULL)) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_WriterGroup *currentWriterGroup = UA_WriterGroup_findWGbyId(server, writerGroupIdentifier); if(currentWriterGroup == NULL) return UA_STATUSCODE_BADNOTFOUND; *state = currentWriterGroup->state; return UA_STATUSCODE_GOOD; } UA_WriterGroup * UA_WriterGroup_findWGbyId(UA_Server *server, UA_NodeId identifier) { UA_PubSubConnection *tmpConnection; TAILQ_FOREACH(tmpConnection, &server->pubSubManager.connections, listEntry) { UA_WriterGroup *tmpWriterGroup; LIST_FOREACH(tmpWriterGroup, &tmpConnection->writerGroups, listEntry) { if(UA_NodeId_equal(&identifier, &tmpWriterGroup->identifier)) return tmpWriterGroup; } } return NULL; } #ifdef UA_ENABLE_PUBSUB_ENCRYPTION UA_StatusCode UA_Server_setWriterGroupEncryptionKeys(UA_Server *server, const UA_NodeId writerGroup, UA_UInt32 securityTokenId, const UA_ByteString signingKey, const UA_ByteString encryptingKey, const UA_ByteString keyNonce) { UA_WriterGroup *wg = UA_WriterGroup_findWGbyId(server, writerGroup); if(!wg) return UA_STATUSCODE_BADNOTFOUND; if(!wg->config.securityPolicy) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "No SecurityPolicy configured for the WriterGroup"); return UA_STATUSCODE_BADINTERNALERROR; } if(securityTokenId != wg->securityTokenId) { wg->securityTokenId = securityTokenId; wg->nonceSequenceNumber = 1; } /* Create a new context */ if(!wg->securityPolicyContext) { return wg->config.securityPolicy-> newContext(wg->config.securityPolicy->policyContext, &signingKey, &encryptingKey, &keyNonce, &wg->securityPolicyContext); } /* Update the context */ return wg->config.securityPolicy-> setSecurityKeys(wg->securityPolicyContext, &signingKey, &encryptingKey, &keyNonce); } #endif void UA_WriterGroupConfig_clear(UA_WriterGroupConfig *writerGroupConfig){ UA_String_clear(&writerGroupConfig->name); UA_ExtensionObject_clear(&writerGroupConfig->transportSettings); UA_ExtensionObject_clear(&writerGroupConfig->messageSettings); UA_Array_delete(writerGroupConfig->groupProperties, writerGroupConfig->groupPropertiesSize, &UA_TYPES[UA_TYPES_KEYVALUEPAIR]); writerGroupConfig->groupProperties = NULL; } static void UA_WriterGroup_clear(UA_Server *server, UA_WriterGroup *writerGroup) { UA_WriterGroupConfig_clear(&writerGroup->config); UA_NodeId_clear(&writerGroup->identifier); /* Delete all writers */ UA_DataSetWriter *dataSetWriter, *tmpDataSetWriter; LIST_FOREACH_SAFE(dataSetWriter, &writerGroup->writers, listEntry, tmpDataSetWriter){ UA_Server_removeDataSetWriter(server, dataSetWriter->identifier); } UA_NetworkMessageOffsetBuffer_clear(&writerGroup->bufferedMessage); #ifdef UA_ENABLE_PUBSUB_ENCRYPTION if(writerGroup->config.securityPolicy && writerGroup->securityPolicyContext) { writerGroup->config.securityPolicy->deleteContext(writerGroup->securityPolicyContext); writerGroup->securityPolicyContext = NULL; } #endif } UA_StatusCode UA_WriterGroup_setPubSubState(UA_Server *server, UA_PubSubState state, UA_WriterGroup *writerGroup) { UA_DataSetWriter *dataSetWriter; switch(state) { case UA_PUBSUBSTATE_DISABLED: switch (writerGroup->state){ case UA_PUBSUBSTATE_DISABLED: return UA_STATUSCODE_GOOD; case UA_PUBSUBSTATE_PAUSED: break; case UA_PUBSUBSTATE_OPERATIONAL: if(writerGroup->config.pubsubManagerCallback.removeCustomCallback) writerGroup->config.pubsubManagerCallback. removeCustomCallback(server, writerGroup->identifier, writerGroup->publishCallbackId); else UA_PubSubManager_removeRepeatedPubSubCallback(server, writerGroup->publishCallbackId); LIST_FOREACH(dataSetWriter, &writerGroup->writers, listEntry){ UA_DataSetWriter_setPubSubState(server, UA_PUBSUBSTATE_DISABLED, dataSetWriter); } writerGroup->state = UA_PUBSUBSTATE_DISABLED; break; case UA_PUBSUBSTATE_ERROR: break; default: UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Received unknown PubSub state!"); } break; case UA_PUBSUBSTATE_PAUSED: switch (writerGroup->state) { case UA_PUBSUBSTATE_DISABLED: break; case UA_PUBSUBSTATE_PAUSED: return UA_STATUSCODE_GOOD; case UA_PUBSUBSTATE_OPERATIONAL: break; case UA_PUBSUBSTATE_ERROR: break; default: UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Received unknown PubSub state!"); } break; case UA_PUBSUBSTATE_OPERATIONAL: switch (writerGroup->state) { case UA_PUBSUBSTATE_DISABLED: writerGroup->state = UA_PUBSUBSTATE_OPERATIONAL; if(writerGroup->config.pubsubManagerCallback.removeCustomCallback) writerGroup->config.pubsubManagerCallback. removeCustomCallback(server, writerGroup->identifier, writerGroup->publishCallbackId); else UA_PubSubManager_removeRepeatedPubSubCallback(server, writerGroup->publishCallbackId); LIST_FOREACH(dataSetWriter, &writerGroup->writers, listEntry){ UA_DataSetWriter_setPubSubState(server, UA_PUBSUBSTATE_OPERATIONAL, dataSetWriter); } UA_WriterGroup_addPublishCallback(server, writerGroup); break; case UA_PUBSUBSTATE_PAUSED: break; case UA_PUBSUBSTATE_OPERATIONAL: return UA_STATUSCODE_GOOD; case UA_PUBSUBSTATE_ERROR: break; default: UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Received unknown PubSub state!"); } break; case UA_PUBSUBSTATE_ERROR: { switch (writerGroup->state){ case UA_PUBSUBSTATE_DISABLED: break; case UA_PUBSUBSTATE_PAUSED: break; case UA_PUBSUBSTATE_OPERATIONAL: UA_PubSubManager_removeRepeatedPubSubCallback(server, writerGroup->publishCallbackId); LIST_FOREACH(dataSetWriter, &writerGroup->writers, listEntry){ UA_DataSetWriter_setPubSubState(server, UA_PUBSUBSTATE_ERROR, dataSetWriter); } break; case UA_PUBSUBSTATE_ERROR: return UA_STATUSCODE_GOOD; default: UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Received unknown PubSub state!"); } writerGroup->state = UA_PUBSUBSTATE_ERROR; /* TODO: WIP - example usage of pubsubStateChangeCallback -> inform * application about error state, reason param necessary */ UA_ServerConfig *pConfig = UA_Server_getConfig(server); if(pConfig->pubSubConfig.stateChangeCallback != 0) { pConfig->pubSubConfig. stateChangeCallback(&writerGroup->identifier, UA_PUBSUBSTATE_ERROR, UA_STATUSCODE_BADINTERNALERROR); } break; } default: UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Received unknown PubSub state!"); } return UA_STATUSCODE_GOOD; } #ifdef UA_ENABLE_PUBSUB_ENCRYPTION static UA_StatusCode encryptAndSign(UA_WriterGroup *wg, const UA_NetworkMessage *nm, UA_Byte *signStart, UA_Byte *encryptStart, UA_Byte *msgEnd) { UA_StatusCode rv; void *channelContext = wg->securityPolicyContext; if(nm->securityHeader.networkMessageEncrypted) { /* Set the temporary MessageNonce in the SecurityPolicy */ rv = wg->config.securityPolicy->setMessageNonce(channelContext, &nm->securityHeader.messageNonce); UA_CHECK_STATUS(rv, return rv); /* The encryption is done in-place, no need to encode again */ UA_ByteString toBeEncrypted = {(uintptr_t)msgEnd - (uintptr_t)encryptStart, encryptStart}; rv = wg->config.securityPolicy->symmetricModule.cryptoModule.encryptionAlgorithm. encrypt(channelContext, &toBeEncrypted); UA_CHECK_STATUS(rv, return rv); } if(nm->securityHeader.networkMessageSigned) { UA_ByteString toBeSigned = {(uintptr_t)msgEnd - (uintptr_t)signStart, signStart}; size_t sigSize = wg->config.securityPolicy->symmetricModule.cryptoModule. signatureAlgorithm.getLocalSignatureSize(channelContext); UA_ByteString signature = {sigSize, msgEnd}; rv = wg->config.securityPolicy->symmetricModule.cryptoModule. signatureAlgorithm.sign(channelContext, &toBeSigned, &signature); UA_CHECK_STATUS(rv, return rv); } return UA_STATUSCODE_GOOD; } #endif static UA_StatusCode encodeNetworkMessage(UA_WriterGroup *wg, UA_NetworkMessage *nm, UA_ByteString *buf) { UA_Byte *bufPos = buf->data; UA_Byte *bufEnd = &buf->data[buf->length]; #ifdef UA_ENABLE_PUBSUB_ENCRYPTION UA_Byte *networkMessageStart = bufPos; #endif UA_StatusCode rv = UA_NetworkMessage_encodeHeaders(nm, &bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); #ifdef UA_ENABLE_PUBSUB_ENCRYPTION UA_Byte *payloadStart = bufPos; #endif rv = UA_NetworkMessage_encodePayload(nm, &bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); rv = UA_NetworkMessage_encodeFooters(nm, &bufPos, bufEnd); UA_CHECK_STATUS(rv, return rv); #ifdef UA_ENABLE_PUBSUB_ENCRYPTION /* Encrypt and Sign the message */ UA_Byte *footerEnd = bufPos; rv = encryptAndSign(wg, nm, networkMessageStart, payloadStart, footerEnd); UA_CHECK_STATUS(rv, return rv); #endif return UA_STATUSCODE_GOOD; } #ifdef UA_ENABLE_JSON_ENCODING static UA_StatusCode sendNetworkMessageJson(UA_PubSubConnection *connection, UA_WriterGroup *wg, UA_DataSetMessage *dsm, UA_UInt16 *writerIds, UA_Byte dsmCount) { /* Prepare the NetworkMessage */ 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; /* Compute the message length */ size_t msgSize = UA_NetworkMessage_calcSizeJson(&nm, NULL, 0, NULL, 0, true); /* Allocate the buffer. Allocate on the stack if the buffer is small. */ UA_ByteString buf; UA_Byte stackBuf[UA_MAX_STACKBUF]; buf.data = stackBuf; buf.length = msgSize; UA_StatusCode res = UA_STATUSCODE_GOOD; if(msgSize > UA_MAX_STACKBUF) { res = UA_ByteString_allocBuffer(&buf, msgSize); if(res != UA_STATUSCODE_GOOD) return res; } /* Encode the message */ UA_Byte *bufPos = buf.data; const UA_Byte *bufEnd = &buf.data[msgSize]; res = UA_NetworkMessage_encodeJson(&nm, &bufPos, &bufEnd, NULL, 0, NULL, 0, true); if(res != UA_STATUSCODE_GOOD) goto cleanup; UA_assert(bufPos == bufEnd); /* Send the prepared messages */ res = connection->channel->send(connection->channel, &wg->config.transportSettings, &buf); cleanup: if(msgSize > UA_MAX_STACKBUF) UA_ByteString_clear(&buf); return res; } #endif static UA_StatusCode generateNetworkMessage(UA_PubSubConnection *connection, UA_WriterGroup *wg, UA_DataSetMessage *dsm, UA_UInt16 *writerIds, UA_Byte dsmCount, UA_ExtensionObject *messageSettings, UA_ExtensionObject *transportSettings, UA_NetworkMessage *networkMessage) { if(messageSettings->content.decoded.type != &UA_TYPES[UA_TYPES_UADPWRITERGROUPMESSAGEDATATYPE]) return UA_STATUSCODE_BADINTERNALERROR; UA_UadpWriterGroupMessageDataType *wgm = (UA_UadpWriterGroupMessageDataType*) messageSettings->content.decoded.data; networkMessage->publisherIdEnabled = ((u64)wgm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_PUBLISHERID) != 0; networkMessage->groupHeaderEnabled = ((u64)wgm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_GROUPHEADER) != 0; networkMessage->groupHeader.writerGroupIdEnabled = ((u64)wgm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_WRITERGROUPID) != 0; networkMessage->groupHeader.groupVersionEnabled = ((u64)wgm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_GROUPVERSION) != 0; networkMessage->groupHeader.networkMessageNumberEnabled = ((u64)wgm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_NETWORKMESSAGENUMBER) != 0; networkMessage->groupHeader.sequenceNumberEnabled = ((u64)wgm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_SEQUENCENUMBER) != 0; networkMessage->payloadHeaderEnabled = ((u64)wgm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_PAYLOADHEADER) != 0; networkMessage->timestampEnabled = ((u64)wgm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_TIMESTAMP) != 0; networkMessage->picosecondsEnabled = ((u64)wgm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_PICOSECONDS) != 0; networkMessage->dataSetClassIdEnabled = ((u64)wgm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_DATASETCLASSID) != 0; networkMessage->promotedFieldsEnabled = ((u64)wgm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_PROMOTEDFIELDS) != 0; /* Set the SecurityHeader */ #ifdef UA_ENABLE_PUBSUB_ENCRYPTION if(wg->config.securityMode > UA_MESSAGESECURITYMODE_NONE) { networkMessage->securityEnabled = true; networkMessage->securityHeader.networkMessageSigned = true; if(wg->config.securityMode >= UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) networkMessage->securityHeader.networkMessageEncrypted = true; networkMessage->securityHeader.securityTokenId = wg->securityTokenId; /* Generate the MessageNonce */ UA_ByteString_allocBuffer(&networkMessage->securityHeader.messageNonce, 8); if(networkMessage->securityHeader.messageNonce.length == 0) return UA_STATUSCODE_BADOUTOFMEMORY; networkMessage->securityHeader.messageNonce.length = 4; /* Generate 4 random bytes */ UA_StatusCode rv = wg->config.securityPolicy->symmetricModule. generateNonce(wg->config.securityPolicy->policyContext, &networkMessage->securityHeader.messageNonce); if(rv != UA_STATUSCODE_GOOD) return rv; networkMessage->securityHeader.messageNonce.length = 8; UA_Byte *pos = &networkMessage->securityHeader.messageNonce.data[4]; const UA_Byte *end = &networkMessage->securityHeader.messageNonce.data[8]; UA_UInt32_encodeBinary(&wg->nonceSequenceNumber, &pos, end); } #endif networkMessage->version = 1; networkMessage->networkMessageType = UA_NETWORKMESSAGE_DATASET; if(connection->config->publisherIdType == UA_PUBSUB_PUBLISHERID_NUMERIC) { networkMessage->publisherIdType = UA_PUBLISHERDATATYPE_UINT16; networkMessage->publisherId.publisherIdUInt32 = connection->config->publisherId.numeric; } else if(connection->config->publisherIdType == UA_PUBSUB_PUBLISHERID_STRING) { networkMessage->publisherIdType = UA_PUBLISHERDATATYPE_STRING; networkMessage->publisherId.publisherIdString = connection->config->publisherId.string; } if(networkMessage->groupHeader.sequenceNumberEnabled) networkMessage->groupHeader.sequenceNumber = wg->sequenceNumber; if(networkMessage->groupHeader.groupVersionEnabled) networkMessage->groupHeader.groupVersion = wgm->groupVersion; /* Compute the length of the dsm separately for the header */ UA_UInt16 *dsmLengths = (UA_UInt16 *) UA_calloc(dsmCount, sizeof(UA_UInt16)); if(!dsmLengths) return UA_STATUSCODE_BADOUTOFMEMORY; for(UA_Byte i = 0; i < dsmCount; i++) dsmLengths[i] = (UA_UInt16) UA_DataSetMessage_calcSizeBinary(&dsm[i], NULL, 0); networkMessage->payloadHeader.dataSetPayloadHeader.count = dsmCount; networkMessage->payloadHeader.dataSetPayloadHeader.dataSetWriterIds = writerIds; networkMessage->groupHeader.writerGroupId = wg->config.writerGroupId; /* number of the NetworkMessage inside a PublishingInterval */ networkMessage->groupHeader.networkMessageNumber = 1; networkMessage->payload.dataSetPayload.sizes = dsmLengths; networkMessage->payload.dataSetPayload.dataSetMessages = dsm; return UA_STATUSCODE_GOOD; } static UA_StatusCode sendNetworkMessageBinary(UA_PubSubConnection *connection, UA_WriterGroup *wg, UA_DataSetMessage *dsm, UA_UInt16 *writerIds, UA_Byte dsmCount) { UA_NetworkMessage nm; memset(&nm, 0, sizeof(UA_NetworkMessage)); /* Fill the message structure */ UA_StatusCode rv = generateNetworkMessage(connection, wg, dsm, writerIds, dsmCount, &wg->config.messageSettings, &wg->config.transportSettings, &nm); UA_CHECK_STATUS(rv, return rv); /* Compute the message size. Add the overhead for the security signature. * There is no padding and the encryption incurs no size overhead. */ size_t msgSize = UA_NetworkMessage_calcSizeBinary(&nm, NULL); #ifdef UA_ENABLE_PUBSUB_ENCRYPTION if(wg->config.securityMode > UA_MESSAGESECURITYMODE_NONE) { UA_PubSubSecurityPolicy *sp = wg->config.securityPolicy; msgSize += sp->symmetricModule.cryptoModule. signatureAlgorithm.getLocalSignatureSize(sp->policyContext); } #endif /* Allocate the buffer. Allocate on the stack if the buffer is small. */ UA_ByteString buf; UA_Byte stackBuf[UA_MAX_STACKBUF]; buf.data = stackBuf; buf.length = msgSize; if(msgSize > UA_MAX_STACKBUF) { rv = UA_ByteString_allocBuffer(&buf, msgSize); UA_CHECK_STATUS(rv, goto cleanup); } /* Encode and encrypt the message */ rv = encodeNetworkMessage(wg, &nm, &buf); UA_CHECK_STATUS(rv, goto cleanup_with_msg_size); /* Send out the message */ rv = connection->channel->send(connection->channel, &wg->config.transportSettings, &buf); UA_CHECK_STATUS(rv, goto cleanup_with_msg_size); cleanup_with_msg_size: if(msgSize > UA_MAX_STACKBUF) { UA_ByteString_clear(&buf); } cleanup: UA_ByteString_clear(&nm.securityHeader.messageNonce); UA_free(nm.payload.dataSetPayload.sizes); return rv; } static void sendNetworkMessage(UA_Server *server, UA_WriterGroup *wg, UA_PubSubConnection *connection, UA_DataSetMessage *dsm, UA_UInt16 *writerIds, UA_Byte dsmCount) { UA_StatusCode res = UA_STATUSCODE_GOOD; switch(wg->config.encodingMimeType) { case UA_PUBSUB_ENCODING_UADP: res = sendNetworkMessageBinary(connection, wg, dsm, writerIds, dsmCount); break; #ifdef UA_ENABLE_JSON_ENCODING case UA_PUBSUB_ENCODING_JSON: res = sendNetworkMessageJson(connection, wg, dsm, writerIds, dsmCount); break; #endif default: res = UA_STATUSCODE_BADNOTSUPPORTED; break; } /* If sending failed, disable all writer of the writergroup */ if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub Publish: Could not send a NetworkMessage " "with status code %s", UA_StatusCode_name(res)); UA_DataSetWriter *dsw; LIST_FOREACH(dsw, &wg->writers, listEntry) { UA_DataSetWriter_setPubSubState(server, UA_PUBSUBSTATE_ERROR, dsw); } return; } /* Increase the sequence number */ wg->sequenceNumber++; } static void sendBufferedNetworkMessage(UA_Server *server, UA_WriterGroup *wg, UA_PubSubConnection *connection) { UA_NetworkMessageOffsetBuffer *buf = &wg->bufferedMessage; UA_StatusCode res = UA_NetworkMessage_updateBufferedMessage(buf); if(res != UA_STATUSCODE_GOOD) { UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub sending failed - could not update the message"); UA_WriterGroup_setPubSubState(server, UA_PUBSUBSTATE_ERROR, wg); return; } res = connection->channel->send(connection->channel, &wg->config.transportSettings, &buf->buffer); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Publish failed. RT fixed size. sendBufferedNetworkMessage failed"); UA_WriterGroup_setPubSubState(server, UA_PUBSUBSTATE_ERROR, wg); return; } /* Sending successful - increase the sequence number */ wg->sequenceNumber++; } /* 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"); // TODO: review if its okay to force correct value from caller side instead // UA_assert(writerGroup != NULL); // UA_assert(server != NULL); if(!writerGroup) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Publish failed. WriterGroup not found"); return; } /* Nothing to do? */ if(writerGroup->writersCount == 0) return; /* Find the connection associated with the writer */ UA_PubSubConnection *connection = writerGroup->linkedConnection; if(!connection) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Publish failed. PubSubConnection invalid."); UA_WriterGroup_setPubSubState(server, UA_PUBSUBSTATE_ERROR, writerGroup); return; } /* Realtime path - update the buffer message and send directly */ if(writerGroup->config.rtLevel == UA_PUBSUB_RT_FIXED_SIZE) { sendBufferedNetworkMessage(server, writerGroup, connection); 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 promoted fields * are sent out right away. The others are kept in a buffer for * "batching". */ size_t dsmCount = 0; UA_STACKARRAY(UA_UInt16, dsWriterIds, writerGroup->writersCount); UA_STACKARRAY(UA_DataSetMessage, dsmStore, writerGroup->writersCount); UA_DataSetWriter *dsw; LIST_FOREACH(dsw, &writerGroup->writers, listEntry) { if(dsw->state != UA_PUBSUBSTATE_OPERATIONAL) continue; /* Find the dataset */ UA_PublishedDataSet *pds = UA_PublishedDataSet_findPDSbyId(server, dsw->connectedDataSet); if(!pds) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub Publish: PublishedDataSet not found"); UA_DataSetWriter_setPubSubState(server, UA_PUBSUBSTATE_ERROR, dsw); continue; } /* Generate the DSM */ dsWriterIds[dsmCount] = dsw->config.dataSetWriterId; UA_StatusCode res = UA_DataSetWriter_generateDataSetMessage(server, &dsmStore[dsmCount], dsw); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub Publish: DataSetMessage creation failed"); UA_DataSetWriter_setPubSubState(server, UA_PUBSUBSTATE_ERROR, dsw); continue; } /* There is no promoted field -> send right away */ if(pds->promotedFieldsCount > 0) { sendNetworkMessage(server, writerGroup, connection, &dsmStore[dsmCount], &dsWriterIds[dsmCount], 1); /* Clean up the current store entry */ if(writerGroup->config.rtLevel == UA_PUBSUB_RT_DIRECT_VALUE_ACCESS) { for(size_t i = 0; i < dsmStore[dsmCount].data.keyFrameData.fieldCount; ++i) { dsmStore[dsmCount].data.keyFrameData.dataSetFields[i].value.data = NULL; } } UA_DataSetMessage_clear(&dsmStore[dsmCount]); continue; /* Don't increase the dsmCount, reuse the slot */ } dsmCount++; } /* Send the NetworkMessages with batched DataSetMessages */ UA_Byte nmDsmCount = 0; for(size_t i = 0; i < dsmCount; i += nmDsmCount) { /* How many dsm are batched in this iteration? */ nmDsmCount = (i + maxDSM > dsmCount) ? (UA_Byte)(dsmCount - i) : maxDSM; /* Send the batched messages */ sendNetworkMessage(server, writerGroup, connection, &dsmStore[i], &dsWriterIds[i], nmDsmCount); } /* Clean up DSM */ for(size_t i = 0; i < dsmCount; i++) { if(writerGroup->config.rtLevel == UA_PUBSUB_RT_DIRECT_VALUE_ACCESS) { for(size_t j = 0; j < dsmStore[i].data.keyFrameData.fieldCount; ++j) { dsmStore[i].data.keyFrameData.dataSetFields[j].value.data = NULL; } } UA_DataSetMessage_clear(&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_STATUSCODE_GOOD; if(writerGroup->config.pubsubManagerCallback.addCustomCallback) retval |= writerGroup->config.pubsubManagerCallback. addCustomCallback(server, writerGroup->identifier, (UA_ServerCallback) UA_WriterGroup_publishCallback, writerGroup, writerGroup->config.publishingInterval, NULL, // TODO: Send base time from writer group config UA_TIMER_HANDLE_CYCLEMISS_WITH_CURRENTTIME, // TODO: Send timer policy from writer group config &writerGroup->publishCallbackId); else retval |= UA_PubSubManager_addRepeatedCallback(server, (UA_ServerCallback) UA_WriterGroup_publishCallback, writerGroup, writerGroup->config.publishingInterval, NULL, // TODO: Send base time from writer group config UA_TIMER_HANDLE_CYCLEMISS_WITH_CURRENTTIME, // TODO: Send timer policy from writer group config &writerGroup->publishCallbackId); if(retval == UA_STATUSCODE_GOOD) writerGroup->publishCallbackIsRegistered = true; /* Run once after creation */ UA_WriterGroup_publishCallback(server, writerGroup); return retval; } #endif /* UA_ENABLE_PUBSUB */ /**** amalgamated original file "/src/pubsub/ua_pubsub_reader.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 * Copyright (c) 2021 Fraunhofer IOSB (Author: Jan Hermes) */ #ifdef UA_ENABLE_PUBSUB /* conditional compilation */ #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL #endif #ifdef UA_ENABLE_PUBSUB_DELTAFRAMES #endif #ifdef UA_ENABLE_PUBSUB_BUFMALLOC #endif /* This functionality of this API will be used in future to create mirror Variables - TODO */ /* #define UA_MAX_SIZENAME 64 */ /* Max size of Qualified Name of Subscribed Variable */ /* Clear DataSetReader */ static void UA_DataSetReader_clear(UA_Server *server, UA_DataSetReader *dataSetReader); static void UA_PubSubDSRDataSetField_sampleValue(UA_Server *server, UA_DataSetReader *dataSetReader, UA_DataValue *value, UA_FieldTargetVariable *ftv) { /* TODO: Static value source without RT information model * This API supports only to external datasource in RT configutation * TODO: Extend to support other configuration if required */ /* Get the Node */ const UA_VariableNode *rtNode = (const UA_VariableNode *) UA_NODESTORE_GET(server, &ftv->targetVariable.targetNodeId); if(!rtNode) return; if(rtNode->valueBackend.backendType == UA_VALUEBACKENDTYPE_EXTERNAL) { /* Set the external source in the dataset reader config */ ftv->externalDataValue = rtNode->valueBackend.backend.external.value; /* Get the value to compute the offsets */ *value = **rtNode->valueBackend.backend.external.value; value->value.storageType = UA_VARIANT_DATA_NODELETE; } UA_NODESTORE_RELEASE(server, (const UA_Node *) rtNode); } static UA_StatusCode UA_PubSubDataSetReader_generateKeyFrameMessage(UA_Server *server, UA_DataSetMessage *dataSetMessage, UA_DataSetReader *dataSetReader) { /* Prepare DataSetMessageContent */ UA_TargetVariables *tv = &dataSetReader->config.subscribedDataSet.subscribedDataSetTarget; dataSetMessage->header.dataSetMessageValid = true; dataSetMessage->header.dataSetMessageType = UA_DATASETMESSAGE_DATAKEYFRAME; dataSetMessage->data.keyFrameData.fieldCount = (UA_UInt16) tv->targetVariablesSize; dataSetMessage->data.keyFrameData.dataSetFields = (UA_DataValue *) UA_Array_new(tv->targetVariablesSize, &UA_TYPES[UA_TYPES_DATAVALUE]); if(!dataSetMessage->data.keyFrameData.dataSetFields) return UA_STATUSCODE_BADOUTOFMEMORY; for(size_t counter = 0; counter < tv->targetVariablesSize; counter++) { /* Sample the value and set the source in the reader config */ UA_DataValue *dfv = &dataSetMessage->data.keyFrameData.dataSetFields[counter]; UA_FieldTargetVariable *ftv = &tv->targetVariables[counter]; UA_PubSubDSRDataSetField_sampleValue(server, dataSetReader, dfv, ftv); /* Deactivate statuscode? */ if(((u64)dataSetReader->config.dataSetFieldContentMask & (u64)UA_DATASETFIELDCONTENTMASK_STATUSCODE) == 0) dfv->hasStatus = false; /* Deactivate timestamps */ if(((u64)dataSetReader->config.dataSetFieldContentMask & (u64)UA_DATASETFIELDCONTENTMASK_SOURCETIMESTAMP) == 0) dfv->hasSourceTimestamp = false; if(((u64)dataSetReader->config.dataSetFieldContentMask & (u64)UA_DATASETFIELDCONTENTMASK_SOURCEPICOSECONDS) == 0) dfv->hasSourcePicoseconds = false; if(((u64)dataSetReader->config.dataSetFieldContentMask & (u64)UA_DATASETFIELDCONTENTMASK_SERVERTIMESTAMP) == 0) dfv->hasServerTimestamp = false; if(((u64)dataSetReader->config.dataSetFieldContentMask & (u64)UA_DATASETFIELDCONTENTMASK_SERVERPICOSECONDS) == 0) dfv->hasServerPicoseconds = false; } return UA_STATUSCODE_GOOD; } /* Generate a DataSetMessage for the given reader. */ UA_StatusCode UA_DataSetReader_generateDataSetMessage(UA_Server *server, UA_DataSetMessage *dataSetMessage, UA_DataSetReader *dataSetReader) { /* Reset the message */ memset(dataSetMessage, 0, sizeof(UA_DataSetMessage)); /* Support only for UADP configuration * TODO: JSON encoding if UA_DataSetReader_generateDataSetMessage used other * that RT configuration */ UA_ExtensionObject *settings = &dataSetReader->config.messageSettings; if(settings->content.decoded.type != &UA_TYPES[UA_TYPES_UADPDATASETREADERMESSAGEDATATYPE]) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Only UADP encoding is supported."); return UA_STATUSCODE_BADNOTSUPPORTED; } /* The configuration Flags are included inside the std. defined UA_UadpDataSetReaderMessageDataType */ UA_UadpDataSetReaderMessageDataType defaultUadpConfiguration; UA_UadpDataSetReaderMessageDataType *dataSetReaderMessageDataType = (UA_UadpDataSetReaderMessageDataType*) settings->content.decoded.data; if(!(settings->encoding == UA_EXTENSIONOBJECT_DECODED || settings->encoding == UA_EXTENSIONOBJECT_DECODED_NODELETE) || !dataSetReaderMessageDataType->dataSetMessageContentMask) { /* create default flag configuration if no dataSetMessageContentMask or even messageSettings in * UadpDataSetWriterMessageDataType was passed in */ memset(&defaultUadpConfiguration, 0, sizeof(UA_UadpDataSetReaderMessageDataType)); defaultUadpConfiguration.dataSetMessageContentMask = (UA_UadpDataSetMessageContentMask) ((u64)UA_UADPDATASETMESSAGECONTENTMASK_TIMESTAMP | (u64)UA_UADPDATASETMESSAGECONTENTMASK_MAJORVERSION | (u64)UA_UADPDATASETMESSAGECONTENTMASK_MINORVERSION); dataSetReaderMessageDataType = &defaultUadpConfiguration; } /* Sanity-test the configuration */ if(dataSetReaderMessageDataType && (dataSetReaderMessageDataType->networkMessageNumber != 0 || dataSetReaderMessageDataType->dataSetOffset != 0)) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Static DSM configuration not supported. Using defaults"); dataSetReaderMessageDataType->networkMessageNumber = 0; dataSetReaderMessageDataType->dataSetOffset = 0; } /* The field encoding depends on the flags inside the reader config. */ if(dataSetReader->config.dataSetFieldContentMask & (u64)UA_DATASETFIELDCONTENTMASK_RAWDATA) { dataSetMessage->header.fieldEncoding = UA_FIELDENCODING_RAWDATA; } else if((u64)dataSetReader->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; } /* Std: 'The DataSetMessageContentMask defines the flags for the content * of the DataSetMessage header.' */ if((u64)dataSetReaderMessageDataType->dataSetMessageContentMask & (u64)UA_UADPDATASETMESSAGECONTENTMASK_MAJORVERSION) { dataSetMessage->header.configVersionMajorVersionEnabled = true; dataSetMessage->header.configVersionMajorVersion = dataSetReader->config.dataSetMetaData.configurationVersion.majorVersion; } if((u64)dataSetReaderMessageDataType->dataSetMessageContentMask & (u64)UA_UADPDATASETMESSAGECONTENTMASK_MINORVERSION) { dataSetMessage->header.configVersionMinorVersionEnabled = true; dataSetMessage->header.configVersionMinorVersion = dataSetReader->config.dataSetMetaData.configurationVersion.minorVersion; } if((u64)dataSetReaderMessageDataType->dataSetMessageContentMask & (u64)UA_UADPDATASETMESSAGECONTENTMASK_SEQUENCENUMBER) { /* Will be modified when subscriber receives new nw msg */ dataSetMessage->header.dataSetMessageSequenceNrEnabled = true; dataSetMessage->header.dataSetMessageSequenceNr = 1; } if((u64)dataSetReaderMessageDataType->dataSetMessageContentMask & (u64)UA_UADPDATASETMESSAGECONTENTMASK_TIMESTAMP) { dataSetMessage->header.timestampEnabled = true; dataSetMessage->header.timestamp = UA_DateTime_now(); } /* TODO: Picoseconds resolution not supported atm */ if((u64)dataSetReaderMessageDataType->dataSetMessageContentMask & (u64)UA_UADPDATASETMESSAGECONTENTMASK_PICOSECONDS) { dataSetMessage->header.picoSecondsIncluded = false; } if((u64)dataSetReaderMessageDataType->dataSetMessageContentMask & (u64)UA_UADPDATASETMESSAGECONTENTMASK_STATUS) { dataSetMessage->header.statusEnabled = true; } /* Not supported for Delta frames atm */ return UA_PubSubDataSetReader_generateKeyFrameMessage(server, dataSetMessage, dataSetReader); } UA_StatusCode UA_DataSetReader_generateNetworkMessage(UA_PubSubConnection *pubSubConnection, UA_DataSetReader *dataSetReader, UA_DataSetMessage *dsm, UA_UInt16 *writerId, UA_Byte dsmCount, UA_NetworkMessage *nm) { UA_ExtensionObject *settings = &dataSetReader->config.messageSettings; if(settings->content.decoded.type != &UA_TYPES[UA_TYPES_UADPDATASETREADERMESSAGEDATATYPE]) return UA_STATUSCODE_BADNOTSUPPORTED; UA_UadpDataSetReaderMessageDataType *dsrm = (UA_UadpDataSetReaderMessageDataType*)settings->content.decoded.data; nm->publisherIdEnabled = ((u64)dsrm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_PUBLISHERID) != 0; nm->groupHeaderEnabled = ((u64)dsrm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_GROUPHEADER) != 0; nm->groupHeader.writerGroupIdEnabled = ((u64)dsrm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_WRITERGROUPID) != 0; nm->groupHeader.groupVersionEnabled = ((u64)dsrm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_GROUPVERSION) != 0; nm->groupHeader.networkMessageNumberEnabled = ((u64)dsrm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_NETWORKMESSAGENUMBER) != 0; nm->groupHeader.sequenceNumberEnabled = ((u64)dsrm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_SEQUENCENUMBER) != 0; nm->payloadHeaderEnabled = ((u64)dsrm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_PAYLOADHEADER) != 0; nm->timestampEnabled = ((u64)dsrm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_TIMESTAMP) != 0; nm->picosecondsEnabled = ((u64)dsrm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_PICOSECONDS) != 0; nm->dataSetClassIdEnabled = ((u64)dsrm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_DATASETCLASSID) != 0; nm->promotedFieldsEnabled = ((u64)dsrm->networkMessageContentMask & (u64)UA_UADPNETWORKMESSAGECONTENTMASK_PROMOTEDFIELDS) != 0; nm->version = 1; nm->networkMessageType = UA_NETWORKMESSAGE_DATASET; if(!UA_DataType_isNumeric(dataSetReader->config.publisherId.type)) return UA_STATUSCODE_BADNOTSUPPORTED; switch(dataSetReader->config.publisherId.type->typeKind) { case UA_DATATYPEKIND_BYTE: nm->publisherIdType = UA_PUBLISHERDATATYPE_BYTE; nm->publisherId.publisherIdByte = *(UA_Byte *) dataSetReader->config.publisherId.data; break; case UA_DATATYPEKIND_UINT16: nm->publisherIdType = UA_PUBLISHERDATATYPE_UINT16; nm->publisherId.publisherIdUInt16 = *(UA_UInt16 *) dataSetReader->config.publisherId.data; break; case UA_DATATYPEKIND_UINT32: nm->publisherIdType = UA_PUBLISHERDATATYPE_UINT32; nm->publisherId.publisherIdUInt32 = *(UA_UInt32 *) dataSetReader->config.publisherId.data; break; case UA_DATATYPEKIND_UINT64: nm->publisherIdType = UA_PUBLISHERDATATYPE_UINT64; nm->publisherId.publisherIdUInt64 = *(UA_UInt64 *) dataSetReader->config.publisherId.data; break; default: return UA_STATUSCODE_BADNOTSUPPORTED; } if(nm->groupHeader.sequenceNumberEnabled) nm->groupHeader.sequenceNumber = 1; /* Will be modified when subscriber receives new nw msg. */ if(nm->groupHeader.groupVersionEnabled) nm->groupHeader.groupVersion = dsrm->groupVersion; /* Compute the length of the dsm separately for the header */ UA_UInt16 *dsmLengths = (UA_UInt16 *) UA_calloc(dsmCount, sizeof(UA_UInt16)); if(!dsmLengths) return UA_STATUSCODE_BADOUTOFMEMORY; for(UA_Byte i = 0; i < dsmCount; i++){ dsmLengths[i] = (UA_UInt16) UA_DataSetMessage_calcSizeBinary(&dsm[i], NULL, 0); switch(dataSetReader->config.expectedEncoding) { case UA_PUBSUB_RT_UNKNOWN: break; case UA_PUBSUB_RT_VARIANT: dsm[i].header.fieldEncoding = UA_FIELDENCODING_VARIANT; break; case UA_PUBSUB_RT_DATA_VALUE: dsm[i].header.fieldEncoding = UA_FIELDENCODING_DATAVALUE; break; case UA_PUBSUB_RT_RAW: dsm[i].header.fieldEncoding = UA_FIELDENCODING_RAWDATA; break; } } nm->payloadHeader.dataSetPayloadHeader.count = dsmCount; nm->payloadHeader.dataSetPayloadHeader.dataSetWriterIds = writerId; nm->groupHeader.writerGroupId = dataSetReader->config.writerGroupId; nm->groupHeader.networkMessageNumber = 1; /* number of the NetworkMessage inside a PublishingInterval */ nm->payload.dataSetPayload.sizes = dsmLengths; nm->payload.dataSetPayload.dataSetMessages = dsm; return UA_STATUSCODE_GOOD; } static UA_StatusCode checkReaderIdentifier(UA_Server *server, UA_NetworkMessage *msg, UA_DataSetReader *reader) { if(!msg->groupHeaderEnabled || !msg->groupHeader.writerGroupIdEnabled || !msg->payloadHeaderEnabled) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "Cannot process DataSetReader without WriterGroup" "and DataSetWriter identifiers"); return UA_STATUSCODE_BADNOTIMPLEMENTED; } switch(msg->publisherIdType) { case UA_PUBLISHERDATATYPE_BYTE: if(reader->config.publisherId.type == &UA_TYPES[UA_TYPES_BYTE] && msg->publisherIdType == UA_PUBLISHERDATATYPE_BYTE && msg->publisherId.publisherIdByte == *(UA_Byte*)reader->config.publisherId.data) break; return UA_STATUSCODE_BADNOTFOUND; case UA_PUBLISHERDATATYPE_UINT16: if(reader->config.publisherId.type == &UA_TYPES[UA_TYPES_UINT16] && msg->publisherIdType == UA_PUBLISHERDATATYPE_UINT16 && msg->publisherId.publisherIdUInt16 == *(UA_UInt16*)reader->config.publisherId.data) break; return UA_STATUSCODE_BADNOTFOUND; case UA_PUBLISHERDATATYPE_UINT32: if(reader->config.publisherId.type == &UA_TYPES[UA_TYPES_UINT32] && msg->publisherIdType == UA_PUBLISHERDATATYPE_UINT32 && msg->publisherId.publisherIdUInt32 == *(UA_UInt32*)reader->config.publisherId.data) break; return UA_STATUSCODE_BADNOTFOUND; case UA_PUBLISHERDATATYPE_UINT64: if(reader->config.publisherId.type == &UA_TYPES[UA_TYPES_UINT64] && msg->publisherIdType == UA_PUBLISHERDATATYPE_UINT64 && msg->publisherId.publisherIdUInt64 == *(UA_UInt64*)reader->config.publisherId.data) break; return UA_STATUSCODE_BADNOTFOUND; case UA_PUBLISHERDATATYPE_STRING: if(reader->config.publisherId.type == &UA_TYPES[UA_TYPES_STRING] && msg->publisherIdType == UA_PUBLISHERDATATYPE_STRING && UA_String_equal(&msg->publisherId.publisherIdString, (UA_String*)reader->config.publisherId.data)) break; return UA_STATUSCODE_BADNOTFOUND; default: return UA_STATUSCODE_BADNOTFOUND; } if(reader->config.writerGroupId == msg->groupHeader.writerGroupId && reader->config.dataSetWriterId == *msg->payloadHeader.dataSetPayloadHeader.dataSetWriterIds) { UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "DataSetReader found. Process NetworkMessage"); return UA_STATUSCODE_GOOD; } return UA_STATUSCODE_BADNOTFOUND; } 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(readerGroup == NULL) return UA_STATUSCODE_BADNOTFOUND; if(!dataSetReaderConfig) return UA_STATUSCODE_BADNOTFOUND; if(readerGroup->configurationFrozen){ UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Add DataSetReader failed. Subscriber configuration is frozen."); return UA_STATUSCODE_BADCONFIGURATIONERROR; } /* Allocate memory for new DataSetReader */ UA_DataSetReader *newDataSetReader = (UA_DataSetReader *) UA_calloc(1, sizeof(UA_DataSetReader)); if(!newDataSetReader) return UA_STATUSCODE_BADOUTOFMEMORY; UA_StatusCode retVal = UA_STATUSCODE_GOOD; newDataSetReader->componentType = UA_PUBSUB_COMPONENT_DATASETREADER; if(readerGroup->state == UA_PUBSUBSTATE_OPERATIONAL) { retVal = UA_DataSetReader_setPubSubState(server, UA_PUBSUBSTATE_OPERATIONAL, newDataSetReader); if(retVal != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Add DataSetReader failed. setPubSubState failed."); UA_free(newDataSetReader); newDataSetReader = 0; return retVal; } } /* Copy the config into the new dataSetReader */ UA_DataSetReaderConfig_copy(dataSetReaderConfig, &newDataSetReader->config); newDataSetReader->linkedReaderGroup = readerGroup->identifier; #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL retVal = addDataSetReaderRepresentation(server, newDataSetReader); if(retVal != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Add DataSetReader failed. addDataSetReaderRepresentation failed."); UA_DataSetReaderConfig_clear(&newDataSetReader->config); UA_free(newDataSetReader); newDataSetReader = 0; return retVal; } #else UA_PubSubManager_generateUniqueNodeId(&server->pubSubManager, &newDataSetReader->identifier); #endif #ifdef UA_ENABLE_PUBSUB_MONITORING /* create message receive timeout timer */ retVal = server->config.pubSubConfig.monitoringInterface.createMonitoring( server, newDataSetReader->identifier, UA_PUBSUB_COMPONENT_DATASETREADER, UA_PUBSUB_MONITORING_MESSAGE_RECEIVE_TIMEOUT, newDataSetReader, UA_DataSetReader_handleMessageReceiveTimeout); if(retVal != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Add DataSetReader failed. Create message receive timeout timer failed."); UA_DataSetReaderConfig_clear(&newDataSetReader->config); UA_free(newDataSetReader); newDataSetReader = 0; return retVal; } #endif /* UA_ENABLE_PUBSUB_MONITORING */ /* Add the new reader to the group */ LIST_INSERT_HEAD(&readerGroup->readers, newDataSetReader, listEntry); readerGroup->readersCount++; if(readerIdentifier) UA_NodeId_copy(&newDataSetReader->identifier, readerIdentifier); return retVal; } UA_StatusCode UA_Server_removeDataSetReader(UA_Server *server, UA_NodeId readerIdentifier) { /* Remove datasetreader given by the identifier */ UA_DataSetReader *dsr = UA_ReaderGroup_findDSRbyId(server, readerIdentifier); if(!dsr) return UA_STATUSCODE_BADNOTFOUND; if(dsr->configurationFrozen) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Remove DataSetReader failed. " "Subscriber configuration is frozen."); return UA_STATUSCODE_BADCONFIGURATIONERROR; } #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL removeDataSetReaderRepresentation(server, dsr); #endif UA_StatusCode res = UA_STATUSCODE_GOOD; #ifdef UA_ENABLE_PUBSUB_MONITORING /* Stop and remove message receive timeout timer */ if(dsr->msgRcvTimeoutTimerRunning) { res = server->config.pubSubConfig.monitoringInterface. stopMonitoring(server, dsr->identifier, UA_PUBSUB_COMPONENT_DATASETREADER, UA_PUBSUB_MONITORING_MESSAGE_RECEIVE_TIMEOUT, dsr); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Remove DataSetReader failed. Stop message " "receive timeout timer of DataSetReader '%.*s' failed.", (int) dsr->config.name.length, dsr->config.name.data); } } res |= server->config.pubSubConfig.monitoringInterface. deleteMonitoring(server, dsr->identifier, UA_PUBSUB_COMPONENT_DATASETREADER, UA_PUBSUB_MONITORING_MESSAGE_RECEIVE_TIMEOUT, dsr); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Remove DataSetReader failed. Delete message receive " "timeout timer of DataSetReader '%.*s' failed.", (int) dsr->config.name.length, dsr->config.name.data); } #endif /* UA_ENABLE_PUBSUB_MONITORING */ UA_DataSetReader_clear(server, dsr); return res; } 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); if(!currentDataSetReader) return UA_STATUSCODE_BADNOTFOUND; if(currentDataSetReader->configurationFrozen){ UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Update DataSetReader config failed. " "Subscriber configuration is frozen."); return UA_STATUSCODE_BADCONFIGURATIONERROR; } UA_ReaderGroup *currentReaderGroup = UA_ReaderGroup_findRGbyId(server, readerGroupIdentifier); if(currentReaderGroup->configurationFrozen) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Update DataSetReader config failed. " "Subscriber configuration is frozen."); return UA_STATUSCODE_BADCONFIGURATIONERROR; } /* The update functionality will be extended during the next PubSub batches. * Currently changes for writerGroupId, dataSetWriterId and TargetVariables are possible. */ if(currentDataSetReader->config.writerGroupId != config->writerGroupId) currentDataSetReader->config.writerGroupId = config->writerGroupId; if(currentDataSetReader->config.dataSetWriterId != config->dataSetWriterId) currentDataSetReader->config.dataSetWriterId = config->dataSetWriterId; if(currentDataSetReader->config.subscribedDataSetType != UA_PUBSUB_SDS_TARGET) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Unsupported SubscribedDataSetType."); return UA_STATUSCODE_BADCONFIGURATIONERROR; } UA_TargetVariables *oldTV = ¤tDataSetReader->config.subscribedDataSet.subscribedDataSetTarget; const UA_TargetVariables *newTV = &config->subscribedDataSet.subscribedDataSetTarget; if(oldTV->targetVariablesSize == newTV->targetVariablesSize) { for(size_t i = 0; i < config->subscribedDataSet.subscribedDataSetTarget.targetVariablesSize; i++) { if(!UA_NodeId_equal(&oldTV->targetVariables[i].targetVariable.targetNodeId, &newTV->targetVariables[i].targetVariable.targetNodeId)) { UA_Server_DataSetReader_createTargetVariables(server, currentDataSetReader->identifier, newTV->targetVariablesSize, newTV->targetVariables); } } } else { UA_Server_DataSetReader_createTargetVariables(server, currentDataSetReader->identifier, newTV->targetVariablesSize, newTV->targetVariables); } UA_StatusCode res = UA_STATUSCODE_GOOD; #ifdef UA_ENABLE_PUBSUB_MONITORING if(currentDataSetReader->config.messageReceiveTimeout != config->messageReceiveTimeout) { /* Update message receive timeout timer interval */ currentDataSetReader->config.messageReceiveTimeout = config->messageReceiveTimeout; res = server->config.pubSubConfig.monitoringInterface. updateMonitoringInterval(server, currentDataSetReader->identifier, UA_PUBSUB_COMPONENT_DATASETREADER, UA_PUBSUB_MONITORING_MESSAGE_RECEIVE_TIMEOUT, currentDataSetReader); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Update DataSetReader message receive timeout timer failed."); } } #endif /* UA_ENABLE_PUBSUB_MONITORING */ return res; } 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; } 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; dst->expectedEncoding = src->expectedEncoding; 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_ExtensionObject_copy(&src->messageSettings, &dst->messageSettings); if(retVal != UA_STATUSCODE_GOOD) return retVal; retVal = UA_ExtensionObject_copy(&src->transportSettings, &dst->transportSettings); if(retVal != UA_STATUSCODE_GOOD) return retVal; if(src->subscribedDataSetType == UA_PUBSUB_SDS_TARGET) { retVal = UA_TargetVariables_copy(&src->subscribedDataSet.subscribedDataSetTarget, &dst->subscribedDataSet.subscribedDataSetTarget); } return retVal; } void UA_DataSetReaderConfig_clear(UA_DataSetReaderConfig *cfg) { UA_String_clear(&cfg->name); UA_Variant_clear(&cfg->publisherId); UA_DataSetMetaDataType_clear(&cfg->dataSetMetaData); UA_ExtensionObject_clear(&cfg->messageSettings); UA_ExtensionObject_clear(&cfg->transportSettings); if(cfg->subscribedDataSetType == UA_PUBSUB_SDS_TARGET) { UA_TargetVariables_clear(&cfg->subscribedDataSet.subscribedDataSetTarget); } } UA_StatusCode UA_Server_DataSetReader_getState(UA_Server *server, UA_NodeId dataSetReaderIdentifier, UA_PubSubState *state) { if((server == NULL) || (state == NULL)) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_DataSetReader *currentDataSetReader = UA_ReaderGroup_findDSRbyId(server, dataSetReaderIdentifier); if(currentDataSetReader == NULL) return UA_STATUSCODE_BADNOTFOUND; *state = currentDataSetReader->state; return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_DataSetReader_setState_disabled(UA_Server *server, UA_DataSetReader *dsr) { UA_StatusCode ret = UA_STATUSCODE_GOOD; switch(dsr->state) { case UA_PUBSUBSTATE_DISABLED: return UA_STATUSCODE_GOOD; case UA_PUBSUBSTATE_PAUSED: dsr->state = UA_PUBSUBSTATE_DISABLED; return UA_STATUSCODE_GOOD; case UA_PUBSUBSTATE_OPERATIONAL: #ifdef UA_ENABLE_PUBSUB_MONITORING /* Stop MessageReceiveTimeout timer */ if(dsr->msgRcvTimeoutTimerRunning == UA_TRUE) { ret = server->config.pubSubConfig.monitoringInterface. stopMonitoring(server, dsr->identifier, UA_PUBSUB_COMPONENT_DATASETREADER, UA_PUBSUB_MONITORING_MESSAGE_RECEIVE_TIMEOUT, dsr); if(ret == UA_STATUSCODE_GOOD) { dsr->msgRcvTimeoutTimerRunning = UA_FALSE; } else { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Disable ReaderGroup failed. Stop message receive " "timeout timer of DataSetReader '%.*s' failed.", (int) dsr->config.name.length, dsr->config.name.data); } } #endif /* UA_ENABLE_PUBSUB_MONITORING */ if(ret == UA_STATUSCODE_GOOD) dsr->state = UA_PUBSUBSTATE_DISABLED; return ret; case UA_PUBSUBSTATE_ERROR: break; default: UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Received unknown PubSub state!"); } return UA_STATUSCODE_BADINVALIDARGUMENT; } /* State machine methods not part of the open62541 state machine API */ UA_StatusCode UA_DataSetReader_setPubSubState(UA_Server *server, UA_PubSubState state, UA_DataSetReader *dataSetReader) { switch(state) { case UA_PUBSUBSTATE_DISABLED: return UA_DataSetReader_setState_disabled(server, dataSetReader); case UA_PUBSUBSTATE_PAUSED: return UA_STATUSCODE_BADNOTSUPPORTED; case UA_PUBSUBSTATE_OPERATIONAL: dataSetReader->state = UA_PUBSUBSTATE_OPERATIONAL; break; case UA_PUBSUBSTATE_ERROR: dataSetReader->state = UA_PUBSUBSTATE_ERROR; break; default: UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Received unknown PubSub state!"); return UA_STATUSCODE_BADINVALIDARGUMENT; } return UA_STATUSCODE_GOOD; } UA_StatusCode UA_FieldTargetVariable_copy(const UA_FieldTargetVariable *src, UA_FieldTargetVariable *dst) { /* Do a simple memcpy */ memcpy(dst, src, sizeof(UA_FieldTargetVariable)); return UA_FieldTargetDataType_copy(&src->targetVariable, &dst->targetVariable); } UA_StatusCode UA_TargetVariables_copy(const UA_TargetVariables *src, UA_TargetVariables *dst) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; memcpy(dst, src, sizeof(UA_TargetVariables)); if(src->targetVariablesSize > 0) { dst->targetVariables = (UA_FieldTargetVariable*) UA_calloc(src->targetVariablesSize, sizeof(UA_FieldTargetVariable)); if(!dst->targetVariables) return UA_STATUSCODE_BADOUTOFMEMORY; for(size_t i = 0; i < src->targetVariablesSize; i++) retVal |= UA_FieldTargetVariable_copy(&src->targetVariables[i], &dst->targetVariables[i]); } return retVal; } void UA_TargetVariables_clear(UA_TargetVariables *subscribedDataSetTarget) { for(size_t i = 0; i < subscribedDataSetTarget->targetVariablesSize; i++) { UA_FieldTargetDataType_clear(&subscribedDataSetTarget->targetVariables[i].targetVariable); } if(subscribedDataSetTarget->targetVariablesSize > 0) UA_free(subscribedDataSetTarget->targetVariables); memset(subscribedDataSetTarget, 0, sizeof(UA_TargetVariables)); } /* This Method is used to initially set the SubscribedDataSet to * TargetVariablesType and to create the list of target Variables of a * SubscribedDataSetType. */ UA_StatusCode UA_Server_DataSetReader_createTargetVariables(UA_Server *server, UA_NodeId dataSetReaderIdentifier, size_t targetVariablesSize, const UA_FieldTargetVariable *targetVariables) { UA_DataSetReader *dataSetReader = UA_ReaderGroup_findDSRbyId(server, dataSetReaderIdentifier); if(!dataSetReader) return UA_STATUSCODE_BADINVALIDARGUMENT; if(dataSetReader->configurationFrozen) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Create Target Variables failed. Subscriber configuration is frozen."); return UA_STATUSCODE_BADCONFIGURATIONERROR; } if(dataSetReader->config.subscribedDataSet.subscribedDataSetTarget.targetVariablesSize > 0) UA_TargetVariables_clear(&dataSetReader->config.subscribedDataSet.subscribedDataSetTarget); /* Set subscribed dataset to TargetVariableType */ dataSetReader->config.subscribedDataSetType = UA_PUBSUB_SDS_TARGET; UA_TargetVariables tmp; tmp.targetVariablesSize = targetVariablesSize; tmp.targetVariables = (UA_FieldTargetVariable*)(uintptr_t)targetVariables; return UA_TargetVariables_copy(&tmp, &dataSetReader->config.subscribedDataSet.subscribedDataSetTarget); } /* This functionality of this API will be used in future to create mirror Variables - TODO */ /* UA_StatusCode UA_Server_DataSetReader_createDataSetMirror(UA_Server *server, UA_String *parentObjectNodeName, UA_NodeId dataSetReaderIdentifier) { 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; } if(pDataSetReader->configurationFrozen) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Add Target Variables failed. Subscriber configuration is frozen."); return UA_STATUSCODE_BADCONFIGURATIONERROR; } // TODO: Frozen configuration variable in TargetVariable structure UA_TargetVariables targetVars; targetVars.targetVariablesSize = pDataSetReader->config.dataSetMetaData.fieldsSize; targetVars.targetVariables = (UA_FieldTargetVariable *) UA_calloc(targetVars.targetVariablesSize, sizeof(UA_FieldTargetVariable)); for(size_t i = 0; i < pDataSetReader->config.dataSetMetaData.fieldsSize; i++) { UA_VariableAttributes vAttr = UA_VariableAttributes_default; vAttr.valueRank = pDataSetReader->config.dataSetMetaData.fields[i].valueRank; if(pDataSetReader->config.dataSetMetaData.fields[i].arrayDimensionsSize > 0) { retval = UA_Array_copy(pDataSetReader->config.dataSetMetaData.fields[i].arrayDimensions, pDataSetReader->config.dataSetMetaData.fields[i].arrayDimensionsSize, (void**)&vAttr.arrayDimensions, &UA_TYPES[UA_TYPES_UINT32]); if(retval == UA_STATUSCODE_GOOD) { vAttr.arrayDimensionsSize = pDataSetReader->config.dataSetMetaData.fields[i].arrayDimensionsSize; } } vAttr.dataType = pDataSetReader->config.dataSetMetaData.fields[i].dataType; vAttr.accessLevel = UA_ACCESSLEVELMASK_READ; UA_LocalizedText_copy(&pDataSetReader->config.dataSetMetaData.fields[i].description, &vAttr.description); UA_QualifiedName qn; UA_QualifiedName_init(&qn); char szTmpName[UA_MAX_SIZENAME]; if(pDataSetReader->config.dataSetMetaData.fields[i].name.length > 0) { UA_UInt16 slen = UA_MAX_SIZENAME -1; vAttr.displayName.locale = UA_STRING("en-US"); vAttr.displayName.text = pDataSetReader->config.dataSetMetaData.fields[i].name; if(pDataSetReader->config.dataSetMetaData.fields[i].name.length < slen) { slen = (UA_UInt16)pDataSetReader->config.dataSetMetaData.fields[i].name.length; UA_snprintf(szTmpName, sizeof(szTmpName), "%.*s", (int)slen, (const char*)pDataSetReader->config.dataSetMetaData.fields[i].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%" PRIx32, retval); } targetVars.targetVariables[i].targetVariable.attributeId = UA_ATTRIBUTEID_VALUE; UA_NodeId_copy(&newNode, &targetVars.targetVariables[i].targetVariable.targetNodeId); UA_NodeId_clear(&newNode); if(vAttr.arrayDimensionsSize > 0) { UA_Array_delete(vAttr.arrayDimensions, vAttr.arrayDimensionsSize, &UA_TYPES[UA_TYPES_UINT32]); } } UA_TargetVariables_clear(&targetVars); return retval; }*/ static void DataSetReader_processRaw(UA_Server *server, UA_ReaderGroup *rg, UA_DataSetReader *dsr, UA_DataSetMessage* msg) { UA_LOG_TRACE(&server->config.logger, UA_LOGCATEGORY_SERVER, "Received RAW Frame"); msg->data.keyFrameData.fieldCount = (UA_UInt16) dsr->config.dataSetMetaData.fieldsSize; size_t offset = 0; for(size_t i = 0; i < dsr->config.dataSetMetaData.fieldsSize; i++) { /* TODO The datatype reference should be part of the internal * pubsub configuration to avoid the time-expensive lookup */ const UA_DataType *type = UA_findDataTypeWithCustom(&dsr->config.dataSetMetaData.fields[i].dataType, server->config.customDataTypes); msg->data.keyFrameData.rawFields.length += type->memSize; UA_STACKARRAY(UA_Byte, value, type->memSize); UA_StatusCode res = UA_decodeBinaryInternal(&msg->data.keyFrameData.rawFields, &offset, value, type, NULL); if(res != UA_STATUSCODE_GOOD) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "Error during Raw-decode KeyFrame field %u: %s", (unsigned)i, UA_StatusCode_name(res)); return; } UA_FieldTargetVariable *tv = &dsr->config.subscribedDataSet.subscribedDataSetTarget.targetVariables[i]; if(rg->config.rtLevel == UA_PUBSUB_RT_FIXED_SIZE) { if (tv->beforeWrite) { void *pData = (**tv->externalDataValue).value.data; (**tv->externalDataValue).value.data = value; // set raw data as "preview" tv->beforeWrite(server, &dsr->identifier, &dsr->linkedReaderGroup, &dsr->config.subscribedDataSet.subscribedDataSetTarget.targetVariables[i].targetVariable.targetNodeId, dsr->config.subscribedDataSet.subscribedDataSetTarget.targetVariables[i].targetVariableContext, tv->externalDataValue); (**tv->externalDataValue).value.data = pData; // restore previous data pointer } memcpy((**tv->externalDataValue).value.data, value, type->memSize); if(tv->afterWrite) tv->afterWrite(server, &dsr->identifier, &dsr->linkedReaderGroup, &tv->targetVariable.targetNodeId, tv->targetVariableContext, tv->externalDataValue); continue; /* No dynamic allocation for fixed-size msg, no need to _clear */ } UA_WriteValue writeVal; UA_WriteValue_init(&writeVal); writeVal.attributeId = tv->targetVariable.attributeId; writeVal.indexRange = tv->targetVariable.receiverIndexRange; writeVal.nodeId = tv->targetVariable.targetNodeId; UA_Variant_setScalar(&writeVal.value.value, value, type); writeVal.value.hasValue = true; res = UA_Server_write(server, &writeVal); UA_clear(value, type); if(res != UA_STATUSCODE_GOOD) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "Error writing KeyFrame field %u: %s", (unsigned)i, UA_StatusCode_name(res)); } } } static void DataSetReader_processFixedSize(UA_Server *server, UA_ReaderGroup *rg, UA_DataSetReader *dsr, UA_DataSetMessage *msg, size_t fieldCount) { for(size_t i = 0; i < fieldCount; i++) { if(!msg->data.keyFrameData.dataSetFields[i].hasValue) continue; UA_FieldTargetVariable *tv = &dsr->config.subscribedDataSet.subscribedDataSetTarget.targetVariables[i]; if(tv->targetVariable.attributeId != UA_ATTRIBUTEID_VALUE) continue; if (tv->beforeWrite) { UA_DataValue *tmp = &msg->data.keyFrameData.dataSetFields[i]; tv->beforeWrite(server, &dsr->identifier, &dsr->linkedReaderGroup, &dsr->config.subscribedDataSet.subscribedDataSetTarget.targetVariables[i].targetVariable.targetNodeId, dsr->config.subscribedDataSet.subscribedDataSetTarget.targetVariables[i].targetVariableContext, &tmp); } memcpy((**tv->externalDataValue).value.data, msg->data.keyFrameData.dataSetFields[i].value.data, msg->data.keyFrameData.dataSetFields[i].value.type->memSize); if(tv->afterWrite) tv->afterWrite(server, &dsr->identifier, &dsr->linkedReaderGroup, &tv->targetVariable.targetNodeId, tv->targetVariableContext, tv->externalDataValue); } } void UA_DataSetReader_process(UA_Server *server, UA_ReaderGroup *rg, UA_DataSetReader *dsr, UA_DataSetMessage *msg) { if(!dsr || !rg || !msg || !server) return; UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "DataSetReader '%.*s': received a network message", (int) dsr->config.name.length, dsr->config.name.data); if(!msg->header.dataSetMessageValid) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "DataSetMessage is discarded: message is not valid"); /* To Do check ConfigurationVersion */ /* if(msg->header.configVersionMajorVersionEnabled) { * if(msg->header.configVersionMajorVersion != * dsr->config.dataSetMetaData.configurationVersion.majorVersion) { * UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER, * "DataSetMessage is discarded: ConfigurationVersion " * "MajorVersion does not match"); * return; * } * } */ return; } if(msg->header.dataSetMessageType != UA_DATASETMESSAGE_DATAKEYFRAME) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "DataSetMessage is discarded: Only keyframes are supported"); return; } /* Process message with raw encoding (realtime and non-realtime) */ if(msg->header.fieldEncoding == UA_FIELDENCODING_RAWDATA) { DataSetReader_processRaw(server, rg, dsr, msg); #ifdef UA_ENABLE_PUBSUB_MONITORING UA_DataSetReader_checkMessageReceiveTimeout(server, dsr); #endif return; } /* Check and adjust the field count * TODO Throw an error if non-matching? */ size_t fieldCount = msg->data.keyFrameData.fieldCount; if(dsr->config.dataSetMetaData.fieldsSize < fieldCount) fieldCount = dsr->config.dataSetMetaData.fieldsSize; if(dsr->config.subscribedDataSet.subscribedDataSetTarget.targetVariablesSize < fieldCount) fieldCount = dsr->config.subscribedDataSet.subscribedDataSetTarget.targetVariablesSize; /* Process message with fixed size fields (realtime capable) */ if(rg->config.rtLevel == UA_PUBSUB_RT_FIXED_SIZE) { DataSetReader_processFixedSize(server, rg, dsr, msg, fieldCount); #ifdef UA_ENABLE_PUBSUB_MONITORING UA_DataSetReader_checkMessageReceiveTimeout(server, dsr); #endif return; } /* Write the message fields via the write service (non realtime) */ UA_StatusCode res = UA_STATUSCODE_GOOD; for(size_t i = 0; i < fieldCount; i++) { if(!msg->data.keyFrameData.dataSetFields[i].hasValue) continue; UA_FieldTargetVariable *tv = &dsr->config.subscribedDataSet.subscribedDataSetTarget.targetVariables[i]; UA_WriteValue writeVal; UA_WriteValue_init(&writeVal); writeVal.attributeId = tv->targetVariable.attributeId; writeVal.indexRange = tv->targetVariable.receiverIndexRange; writeVal.nodeId = tv->targetVariable.targetNodeId; writeVal.value = msg->data.keyFrameData.dataSetFields[i]; res = UA_Server_write(server, &writeVal); if(res != UA_STATUSCODE_GOOD) UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "Error writing KeyFrame field %u: %s", (unsigned)i, UA_StatusCode_name(res)); } #ifdef UA_ENABLE_PUBSUB_MONITORING UA_DataSetReader_checkMessageReceiveTimeout(server, dsr); #endif } #ifdef UA_ENABLE_PUBSUB_MONITORING void UA_DataSetReader_checkMessageReceiveTimeout(UA_Server *server, UA_DataSetReader *dsr) { UA_assert(server != 0); UA_assert(dsr != 0); /* If previous reader state was error (because we haven't received messages * and ran into timeout) we should set the state back to operational */ if(dsr->state == UA_PUBSUBSTATE_ERROR) { UA_DataSetReader_setPubSubState(server, UA_PUBSUBSTATE_OPERATIONAL, dsr); if(server->config.pubSubConfig.stateChangeCallback != 0) { server->config.pubSubConfig.stateChangeCallback(&dsr->identifier, UA_PUBSUBSTATE_OPERATIONAL, UA_STATUSCODE_GOOD); } } /* Stop message receive timeout timer */ UA_StatusCode res; if(dsr->msgRcvTimeoutTimerRunning) { res = server->config.pubSubConfig.monitoringInterface. stopMonitoring(server, dsr->identifier, UA_PUBSUB_COMPONENT_DATASETREADER, UA_PUBSUB_MONITORING_MESSAGE_RECEIVE_TIMEOUT, dsr); if(res == UA_STATUSCODE_GOOD) { dsr->msgRcvTimeoutTimerRunning = false; } else { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "DataSetReader '%.*s': stop receive timeout timer failed", (int)dsr->config.name.length, dsr->config.name.data); UA_DataSetReader_setPubSubState(server, UA_PUBSUBSTATE_ERROR, dsr); } } /* Start message receive timeout timer */ res = server->config.pubSubConfig.monitoringInterface. startMonitoring(server, dsr->identifier, UA_PUBSUB_COMPONENT_DATASETREADER, UA_PUBSUB_MONITORING_MESSAGE_RECEIVE_TIMEOUT, dsr); if(res == UA_STATUSCODE_GOOD) { UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "Info: DataSetReader '%.*s': start receive timeout timer", (int)dsr->config.name.length, dsr->config.name.data); dsr->msgRcvTimeoutTimerRunning = true; } else { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Starting Message Receive Timeout timer failed."); UA_DataSetReader_setPubSubState(server, UA_PUBSUBSTATE_ERROR, dsr); } } /* Timeout callback for DataSetReader MessageReceiveTimeout handling */ void UA_DataSetReader_handleMessageReceiveTimeout(UA_Server *server, void *dataSetReader) { if(!server || !dataSetReader) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_DataSetReader_handleMessageReceiveTimeout(): " "null pointer param"); return; } UA_DataSetReader *dsr = (UA_DataSetReader*) dataSetReader; if(dsr->componentType != UA_PUBSUB_COMPONENT_DATASETREADER) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_DataSetReader_handleMessageReceiveTimeout(): " "input param is not of type DataSetReader"); return; } UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_DataSetReader_handleMessageReceiveTimeout(): " "MessageReceiveTimeout occurred at DataSetReader " "'%.*s': MessageReceiveTimeout = %f Timer Id = %u ", (int)dsr->config.name.length, dsr->config.name.data, dsr->config.messageReceiveTimeout, (UA_UInt32) dsr->msgRcvTimeoutTimerId); UA_ServerConfig *pConfig = UA_Server_getConfig(server); if(pConfig->pubSubConfig.stateChangeCallback != 0) { pConfig->pubSubConfig.stateChangeCallback(&dsr->identifier, UA_PUBSUBSTATE_ERROR, UA_STATUSCODE_BADTIMEOUT); } UA_StatusCode res = UA_DataSetReader_setPubSubState(server, UA_PUBSUBSTATE_ERROR, dsr); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_DataSetReader_handleMessageReceiveTimeout(): " "setting pubsub state failed"); } } #endif /* UA_ENABLE_PUBSUB_MONITORING */ static void UA_DataSetReader_clear(UA_Server *server, UA_DataSetReader *dsr) { /* Delete DataSetReader config */ UA_DataSetReaderConfig_clear(&dsr->config); /* Delete DataSetReader */ UA_ReaderGroup *rg = UA_ReaderGroup_findRGbyId(server, dsr->linkedReaderGroup); if(rg) rg->readersCount--; UA_NodeId_clear(&dsr->identifier); UA_NodeId_clear(&dsr->linkedReaderGroup); if(dsr->config.subscribedDataSetType == UA_PUBSUB_SDS_TARGET) { UA_TargetVariables_clear(&dsr->config.subscribedDataSet.subscribedDataSetTarget); } else { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_DataSetReader_clear(): unsupported subscribed dataset enum type"); } /* Remove DataSetReader from group */ LIST_REMOVE(dsr, listEntry); /* Free memory allocated for DataSetReader */ UA_free(dsr); } static void processMessageWithReader(UA_Server *server, UA_ReaderGroup *readerGroup, UA_DataSetReader *reader, UA_NetworkMessage *msg) { UA_Byte totalDataSets = 1; if(msg->payloadHeaderEnabled) totalDataSets = msg->payloadHeader.dataSetPayloadHeader.count; for(UA_Byte i = 0; i < totalDataSets; i++) { UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "Process Msg with DataSetReader!"); UA_DataSetReader_process(server, readerGroup, reader, &msg->payload.dataSetPayload.dataSetMessages[i]); } } UA_StatusCode UA_Server_processNetworkMessage(UA_Server *server, UA_PubSubConnection *connection, UA_NetworkMessage* msg) { if(!msg || !connection) return UA_STATUSCODE_BADINVALIDARGUMENT; /* To Do The condition pMsg->dataSetClassIdEnabled * Here some filtering is possible */ if(!msg->publisherIdEnabled) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "Cannot process DataSetReader without PublisherId"); return UA_STATUSCODE_BADNOTIMPLEMENTED; /* TODO: Handle DSR without PublisherId */ } /* There can be several readers listening for the same network message */ UA_Boolean processed = false; UA_ReaderGroup *readerGroup; UA_DataSetReader *reader; LIST_FOREACH(readerGroup, &connection->readerGroups, listEntry) { LIST_FOREACH(reader, &readerGroup->readers, listEntry) { UA_StatusCode retval = checkReaderIdentifier(server, msg, reader); if(retval == UA_STATUSCODE_GOOD) { processed = true; processMessageWithReader(server, readerGroup, reader, msg); } } } if(!processed) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "Dataset reader not found. Check PublisherID, WriterGroupID " "and DatasetWriterID"); } return UA_STATUSCODE_GOOD; } /******************************************************************************** * Functionality related to decoding, decrypting and processing network messages * as a subscriber ********************************************************************************/ #define MIN_PAYLOAD_SIZE_ETHERNET 46 /* Delete the payload value of every decoded DataSet field */ static void UA_DataSetMessage_freeDecodedPayload(UA_DataSetMessage *dsm) { if(dsm->header.fieldEncoding == UA_FIELDENCODING_VARIANT) { for(size_t i = 0; i < dsm->data.keyFrameData.fieldCount; i++) { #ifdef UA_ENABLE_PUBSUB_BUFMALLOC UA_Variant_init(&dsm->data.keyFrameData.dataSetFields[i].value); #else UA_Variant_clear(&dsm->data.keyFrameData.dataSetFields[i].value); #endif } } else if(dsm->header.fieldEncoding == UA_FIELDENCODING_DATAVALUE) { for(size_t i = 0; i < dsm->data.keyFrameData.fieldCount; i++) { #ifdef UA_ENABLE_PUBSUB_BUFMALLOC UA_DataValue_init(&dsm->data.keyFrameData.dataSetFields[i]); #else UA_DataValue_clear(&dsm->data.keyFrameData.dataSetFields[i]); #endif } } } UA_StatusCode decodeNetworkMessage(UA_Server *server, UA_ByteString *buffer, size_t *pos, UA_NetworkMessage *nm, UA_PubSubConnection *connection) { #ifdef UA_DEBUG_DUMP_PKGS UA_dump_hex_pkg(buffer->data, buffer->length); #endif UA_StatusCode rv = UA_NetworkMessage_decodeHeaders(buffer, pos, nm); UA_CHECK_STATUS_ERROR(rv, return rv, &server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub receive. decoding headers failed"); #ifdef UA_ENABLE_PUBSUB_ENCRYPTION UA_Boolean processed = false; UA_ReaderGroup *readerGroup; UA_DataSetReader *reader; /* Choose a correct readergroup for decrypt/verify this message * (there could be multiple) */ LIST_FOREACH(readerGroup, &connection->readerGroups, listEntry) { LIST_FOREACH(reader, &readerGroup->readers, listEntry) { UA_StatusCode retval = checkReaderIdentifier(server, nm, reader); if(retval == UA_STATUSCODE_GOOD) { processed = true; rv = verifyAndDecryptNetworkMessage(&server->config.logger, buffer, pos, nm, readerGroup); UA_CHECK_STATUS_WARN(rv, return rv, &server->config.logger, UA_LOGCATEGORY_SERVER, "Subscribe failed. verify and decrypt network message failed."); #ifdef UA_DEBUG_DUMP_PKGS UA_dump_hex_pkg(buffer->data, buffer->length); #endif /* break out of all loops when first verify & decrypt was successful */ goto loops_exit; } } } loops_exit: if(!processed) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "Dataset reader not found. Check PublisherId, " "WriterGroupId and DatasetWriterId"); /* Possible multicast scenario: there are multiple connections (with one * or more ReaderGroups) within a multicast group every connection * receives all network messages, even if some of them are not meant for * the connection currently processed -> therefore it is ok if the * connection does not have a DataSetReader for every received network * message. We must not return an error here, but continue with the * buffer decoding and see if we have a matching DataSetReader for the * next network message. */ } #endif rv = UA_NetworkMessage_decodePayload(buffer, pos, nm); UA_CHECK_STATUS(rv, return rv); rv = UA_NetworkMessage_decodeFooters(buffer, pos, nm); UA_CHECK_STATUS(rv, return rv); return UA_STATUSCODE_GOOD; } static UA_StatusCode decodeAndProcessNetworkMessage(UA_Server *server, UA_ReaderGroup *readerGroup, UA_PubSubConnection *connection, UA_ByteString *buffer) { UA_NetworkMessage nm; memset(&nm, 0, sizeof(UA_NetworkMessage)); size_t currentPosition = 0; UA_StatusCode rv = UA_STATUSCODE_GOOD; rv = decodeNetworkMessage(server, buffer, ¤tPosition, &nm, connection); UA_CHECK_STATUS_WARN(rv, goto cleanup, &server->config.logger, UA_LOGCATEGORY_SERVER, "Subscribe failed. verify, decrypt and decode network message failed."); rv = UA_Server_processNetworkMessage(server, connection, &nm); // TODO: check what action to perform on error (nothing?) UA_CHECK_STATUS_WARN(rv, (void)0, &server->config.logger, UA_LOGCATEGORY_SERVER, "Subscribe failed. process network message failed."); cleanup: UA_NetworkMessage_clear(&nm); return rv; } static UA_StatusCode decodeAndProcessNetworkMessageRT(UA_Server *server, UA_ReaderGroup *readerGroup, UA_PubSubConnection *connection, UA_ByteString *buffer) { #ifdef UA_ENABLE_PUBSUB_BUFMALLOC useMembufAlloc(); #endif /* Considering max DSM as 1 * TODO: Process with the static value source */ size_t currentPosition = 0; UA_DataSetReader *dataSetReader = LIST_FIRST(&readerGroup->readers); UA_NetworkMessage *nm = dataSetReader->bufferedMessage.nm; /* Decode only the necessary offset and update the networkMessage */ UA_StatusCode res = UA_NetworkMessage_updateBufferedNwMessage(&dataSetReader->bufferedMessage, buffer, ¤tPosition); if(res != UA_STATUSCODE_GOOD) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub receive. Unknown field type."); res = UA_STATUSCODE_UNCERTAIN; goto cleanup; } /* Check the decoded message is the expected one * TODO: PublisherID check after modification in NM to support all datatypes */ if(nm->groupHeader.writerGroupId != dataSetReader->config.writerGroupId || *nm->payloadHeader.dataSetPayloadHeader.dataSetWriterIds != dataSetReader->config.dataSetWriterId) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub receive. Unknown message received. Will not be processed."); res = UA_STATUSCODE_UNCERTAIN; goto cleanup; } UA_DataSetReader_process(server, readerGroup, dataSetReader, nm->payload.dataSetPayload.dataSetMessages); cleanup: UA_DataSetMessage_freeDecodedPayload(nm->payload.dataSetPayload.dataSetMessages); #ifdef UA_ENABLE_PUBSUB_BUFMALLOC useNormalAlloc(); #endif return res; } typedef struct { UA_Server *server; UA_PubSubConnection *connection; UA_ReaderGroup *readerGroup; } UA_RGContext; static UA_StatusCode decodeAndProcessFun(UA_PubSubChannel *channel, void *cbContext, const UA_ByteString *buffer) { UA_ByteString mutableBuffer = {buffer->length, buffer->data}; UA_RGContext *ctx = (UA_RGContext*) cbContext; return decodeAndProcessNetworkMessage(ctx->server, ctx->readerGroup, ctx->connection, &mutableBuffer); } static UA_StatusCode decodeAndProcessFunRT(UA_PubSubChannel *channel, void *cbContext, const UA_ByteString *buffer) { UA_ByteString mutableBuffer = {buffer->length, buffer->data}; UA_RGContext *ctx = (UA_RGContext*) cbContext; return decodeAndProcessNetworkMessageRT(ctx->server, ctx->readerGroup, ctx->connection, &mutableBuffer); } UA_StatusCode receiveBufferedNetworkMessage(UA_Server *server, UA_ReaderGroup *readerGroup, UA_PubSubConnection *connection) { UA_RGContext ctx = {server, connection, readerGroup}; UA_PubSubReceiveCallback receiveCB; if(readerGroup->config.rtLevel == UA_PUBSUB_RT_FIXED_SIZE) receiveCB = decodeAndProcessFunRT; else receiveCB = decodeAndProcessFun; /* TODO: Move the TransportSettings to to the readerGroupConfig. So we can * use it here instead of a NULL pointer. */ UA_StatusCode rv = connection->channel->receive(connection->channel, NULL, receiveCB, &ctx, readerGroup->config.timeout); // TODO attention: here rv is ok if UA_STATUSCODE_GOOD != rv UA_CHECK_WARN(!UA_StatusCode_isBad(rv), return rv, &server->config.logger, UA_LOGCATEGORY_SERVER, "SubscribeCallback(): Connection receive failed!"); return UA_STATUSCODE_GOOD; } #endif /* UA_ENABLE_PUBSUB */ /**** amalgamated original file "/src/pubsub/ua_pubsub_readergroup.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 * Copyright (c) 2021 Fraunhofer IOSB (Author: Jan Hermes) */ #ifdef UA_ENABLE_PUBSUB /* conditional compilation */ #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL #endif UA_ReaderGroup * UA_ReaderGroup_findRGbyId(UA_Server *server, UA_NodeId identifier) { UA_PubSubConnection *pubSubConnection; TAILQ_FOREACH(pubSubConnection, &server->pubSubManager.connections, listEntry){ UA_ReaderGroup* readerGroup = NULL; LIST_FOREACH(readerGroup, &pubSubConnection->readerGroups, listEntry) { if(UA_NodeId_equal(&identifier, &readerGroup->identifier)) return readerGroup; } } return NULL; } UA_DataSetReader *UA_ReaderGroup_findDSRbyId(UA_Server *server, UA_NodeId identifier) { UA_PubSubConnection *pubSubConnection; TAILQ_FOREACH(pubSubConnection, &server->pubSubManager.connections, listEntry){ UA_ReaderGroup* readerGroup = NULL; LIST_FOREACH(readerGroup, &pubSubConnection->readerGroups, listEntry) { UA_DataSetReader *tmpReader; LIST_FOREACH(tmpReader, &readerGroup->readers, listEntry) { if(UA_NodeId_equal(&tmpReader->identifier, &identifier)) return tmpReader; } } } return NULL; } /* Clear ReaderGroup */ static void UA_Server_ReaderGroup_clear(UA_Server* server, UA_ReaderGroup *readerGroup); /* ReaderGroup Config Handling */ UA_StatusCode UA_ReaderGroupConfig_copy(const UA_ReaderGroupConfig *src, UA_ReaderGroupConfig *dst) { memcpy(dst, src, sizeof(UA_ReaderGroupConfig)); UA_StatusCode res = UA_String_copy(&src->name, &dst->name); if(res != UA_STATUSCODE_GOOD) return res; res = UA_Array_copy(src->groupProperties, src->groupPropertiesSize, (void**)&dst->groupProperties, &UA_TYPES[UA_TYPES_KEYVALUEPAIR]); if(res != UA_STATUSCODE_GOOD) { UA_String_clear(&dst->name); return res; } dst->groupPropertiesSize = src->groupPropertiesSize; return UA_STATUSCODE_GOOD; } void UA_ReaderGroupConfig_clear(UA_ReaderGroupConfig *readerGroupConfig) { UA_String_clear(&readerGroupConfig->name); UA_Array_delete(readerGroupConfig->groupProperties, readerGroupConfig->groupPropertiesSize, &UA_TYPES[UA_TYPES_KEYVALUEPAIR]); readerGroupConfig->groupProperties = NULL; readerGroupConfig->groupPropertiesSize = 0; } /* ReaderGroup Lifecycle */ UA_StatusCode UA_Server_addReaderGroup(UA_Server *server, UA_NodeId connectionIdentifier, const UA_ReaderGroupConfig *readerGroupConfig, UA_NodeId *readerGroupIdentifier) { UA_StatusCode retval = UA_STATUSCODE_GOOD; /* Check for valid readergroup configuration */ if(!readerGroupConfig) return UA_STATUSCODE_BADINVALIDARGUMENT; if(!readerGroupConfig->pubsubManagerCallback.addCustomCallback && readerGroupConfig->enableBlockingSocket) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Adding ReaderGroup failed, blocking socket functionality " "only supported in customcallback"); return UA_STATUSCODE_BADNOTSUPPORTED; } /* Search the connection by the given connectionIdentifier */ UA_PubSubConnection *currentConnectionContext = UA_PubSubConnection_findConnectionbyId(server, connectionIdentifier); if(!currentConnectionContext) return UA_STATUSCODE_BADNOTFOUND; if(currentConnectionContext->configurationFrozen){ UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Adding ReaderGroup failed. Subscriber configuration is frozen."); return UA_STATUSCODE_BADCONFIGURATIONERROR; } /* Regist (bind) the connection channel if it is not already registered */ if(!currentConnectionContext->isRegistered) { retval |= UA_PubSubConnection_regist(server, &connectionIdentifier); if(retval != UA_STATUSCODE_GOOD) return retval; } /* Allocate memory for new reader group */ UA_ReaderGroup *newGroup = (UA_ReaderGroup *)UA_calloc(1, sizeof(UA_ReaderGroup)); if(!newGroup) return UA_STATUSCODE_BADOUTOFMEMORY; newGroup->componentType = UA_PUBSUB_COMPONENT_READERGROUP; /* Generate nodeid for the readergroup identifier */ newGroup->linkedConnection = currentConnectionContext->identifier; /* Deep copy of the config */ retval |= UA_ReaderGroupConfig_copy(readerGroupConfig, &newGroup->config); /* Check user configured params and define it accordingly */ if(newGroup->config.subscribingInterval <= 0.0) newGroup->config.subscribingInterval = 5; // Set default to 5 ms if(newGroup->config.enableBlockingSocket) newGroup->config.timeout = 0; // Set timeout to 0 for blocking socket if((!newGroup->config.enableBlockingSocket) && (!newGroup->config.timeout)) newGroup->config.timeout = 1000; /* Set default to 1ms socket timeout when non-blocking socket allows with zero timeout */ LIST_INSERT_HEAD(¤tConnectionContext->readerGroups, newGroup, listEntry); currentConnectionContext->readerGroupsSize++; #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL retval |= addReaderGroupRepresentation(server, newGroup); #else UA_PubSubManager_generateUniqueNodeId(&server->pubSubManager, &newGroup->identifier); #endif if(readerGroupIdentifier) UA_NodeId_copy(&newGroup->identifier, readerGroupIdentifier); return retval; } 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; if(readerGroup->configurationFrozen){ UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Remove ReaderGroup failed. Subscriber configuration is frozen."); return UA_STATUSCODE_BADCONFIGURATIONERROR; } /* 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 */ if(readerGroup->state == UA_PUBSUBSTATE_OPERATIONAL) UA_ReaderGroup_removeSubscribeCallback(server, readerGroup); #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL removeReaderGroupRepresentation(server, readerGroup); #endif /* UA_Server_ReaderGroup_clear also removes itself from the list */ UA_Server_ReaderGroup_clear(server, readerGroup); /* Remove readerGroup from Connection */ LIST_REMOVE(readerGroup, listEntry); UA_free(readerGroup); return UA_STATUSCODE_GOOD; } /* TODO: Implement UA_StatusCode UA_Server_ReaderGroup_updateConfig(UA_Server *server, UA_NodeId readerGroupIdentifier, const UA_ReaderGroupConfig *config) { return UA_STATUSCODE_BADNOTIMPLEMENTED; } */ 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; } static void UA_Server_ReaderGroup_clear(UA_Server* server, UA_ReaderGroup *readerGroup) { UA_ReaderGroupConfig_clear(&readerGroup->config); UA_DataSetReader *dataSetReader; UA_DataSetReader *tmpDataSetReader; LIST_FOREACH_SAFE(dataSetReader, &readerGroup->readers, listEntry, tmpDataSetReader) { UA_Server_removeDataSetReader(server, dataSetReader->identifier); } UA_PubSubConnection* pConn = UA_PubSubConnection_findConnectionbyId(server, readerGroup->linkedConnection); if(pConn != NULL) pConn->readerGroupsSize--; /* Delete ReaderGroup and its members */ UA_NodeId_clear(&readerGroup->linkedConnection); UA_NodeId_clear(&readerGroup->identifier); #ifdef UA_ENABLE_PUBSUB_ENCRYPTION if(readerGroup->config.securityPolicy && readerGroup->securityPolicyContext) { readerGroup->config.securityPolicy->deleteContext(readerGroup->securityPolicyContext); readerGroup->securityPolicyContext = NULL; } #endif UA_ReaderGroupConfig_clear(&readerGroup->config); } UA_StatusCode UA_Server_ReaderGroup_getState(UA_Server *server, UA_NodeId readerGroupIdentifier, UA_PubSubState *state) { if((server == NULL) || (state == NULL)) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_ReaderGroup *currentReaderGroup = UA_ReaderGroup_findRGbyId(server, readerGroupIdentifier); if(!currentReaderGroup) return UA_STATUSCODE_BADNOTFOUND; *state = currentReaderGroup->state; return UA_STATUSCODE_GOOD; } /* ReaderGroup State */ static UA_StatusCode UA_ReaderGroup_setPubSubState_disable(UA_Server *server, UA_ReaderGroup *rg) { UA_DataSetReader *dataSetReader; switch(rg->state) { case UA_PUBSUBSTATE_DISABLED: return UA_STATUSCODE_GOOD; case UA_PUBSUBSTATE_PAUSED: break; case UA_PUBSUBSTATE_OPERATIONAL: UA_ReaderGroup_removeSubscribeCallback(server, rg); LIST_FOREACH(dataSetReader, &rg->readers, listEntry) { UA_DataSetReader_setPubSubState(server, UA_PUBSUBSTATE_DISABLED, dataSetReader); } rg->state = UA_PUBSUBSTATE_DISABLED; break; case UA_PUBSUBSTATE_ERROR: break; default: UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Unknown PubSub state!"); return UA_STATUSCODE_BADINTERNALERROR; } return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_ReaderGroup_setPubSubState_paused(UA_Server *server, UA_ReaderGroup *rg) { UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub state paused is unsupported at the moment!"); switch(rg->state) { case UA_PUBSUBSTATE_DISABLED: break; case UA_PUBSUBSTATE_PAUSED: return UA_STATUSCODE_GOOD; case UA_PUBSUBSTATE_OPERATIONAL: break; case UA_PUBSUBSTATE_ERROR: break; default: UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Unknown PubSub state!"); return UA_STATUSCODE_BADINTERNALERROR; } return UA_STATUSCODE_BADNOTSUPPORTED; } static UA_StatusCode UA_ReaderGroup_setPubSubState_operational(UA_Server *server, UA_ReaderGroup *rg) { UA_DataSetReader *dataSetReader; switch(rg->state) { case UA_PUBSUBSTATE_DISABLED: LIST_FOREACH(dataSetReader, &rg->readers, listEntry) { UA_DataSetReader_setPubSubState(server, UA_PUBSUBSTATE_OPERATIONAL, dataSetReader); } UA_ReaderGroup_addSubscribeCallback(server, rg); rg->state = UA_PUBSUBSTATE_OPERATIONAL; return UA_STATUSCODE_GOOD; case UA_PUBSUBSTATE_PAUSED: break; case UA_PUBSUBSTATE_OPERATIONAL: return UA_STATUSCODE_GOOD; case UA_PUBSUBSTATE_ERROR: break; default: UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Unknown PubSub state!"); return UA_STATUSCODE_BADINTERNALERROR; } return UA_STATUSCODE_BADNOTSUPPORTED; } static UA_StatusCode UA_ReaderGroup_setPubSubState_error(UA_Server *server, UA_ReaderGroup *rg) { UA_DataSetReader *dataSetReader; switch(rg->state) { case UA_PUBSUBSTATE_DISABLED: break; case UA_PUBSUBSTATE_PAUSED: break; case UA_PUBSUBSTATE_OPERATIONAL: UA_ReaderGroup_removeSubscribeCallback(server, rg); LIST_FOREACH(dataSetReader, &rg->readers, listEntry){ UA_DataSetReader_setPubSubState(server, UA_PUBSUBSTATE_ERROR, dataSetReader); } break; case UA_PUBSUBSTATE_ERROR: return UA_STATUSCODE_GOOD; default: UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Unknown PubSub state!"); return UA_STATUSCODE_BADINTERNALERROR; } rg->state = UA_PUBSUBSTATE_ERROR; return UA_STATUSCODE_GOOD; } UA_StatusCode UA_ReaderGroup_setPubSubState(UA_Server *server, UA_PubSubState state, UA_ReaderGroup *readerGroup) { switch(state) { case UA_PUBSUBSTATE_DISABLED: return UA_ReaderGroup_setPubSubState_disable(server, readerGroup); case UA_PUBSUBSTATE_PAUSED: return UA_ReaderGroup_setPubSubState_paused(server, readerGroup); case UA_PUBSUBSTATE_OPERATIONAL: return UA_ReaderGroup_setPubSubState_operational(server, readerGroup); case UA_PUBSUBSTATE_ERROR: return UA_ReaderGroup_setPubSubState_error(server, readerGroup); default: UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Received unknown PubSub state!"); break; } return UA_STATUSCODE_BADINVALIDARGUMENT; } UA_StatusCode UA_Server_setReaderGroupOperational(UA_Server *server, const UA_NodeId readerGroupId){ UA_ReaderGroup *rg = UA_ReaderGroup_findRGbyId(server, readerGroupId); if(!rg) return UA_STATUSCODE_BADNOTFOUND; return UA_ReaderGroup_setPubSubState(server, UA_PUBSUBSTATE_OPERATIONAL, rg); } UA_StatusCode UA_Server_setReaderGroupDisabled(UA_Server *server, const UA_NodeId readerGroupId){ UA_ReaderGroup *rg = UA_ReaderGroup_findRGbyId(server, readerGroupId); if(!rg) return UA_STATUSCODE_BADNOTFOUND; return UA_ReaderGroup_setPubSubState(server, UA_PUBSUBSTATE_DISABLED, rg); } #ifdef UA_ENABLE_PUBSUB_ENCRYPTION UA_StatusCode UA_Server_setReaderGroupEncryptionKeys(UA_Server *server, const UA_NodeId readerGroup, UA_UInt32 securityTokenId, const UA_ByteString signingKey, const UA_ByteString encryptingKey, const UA_ByteString keyNonce) { UA_ReaderGroup *rg = UA_ReaderGroup_findRGbyId(server, readerGroup); UA_CHECK_MEM(rg, return UA_STATUSCODE_BADNOTFOUND); UA_CHECK_MEM_WARN(rg->config.securityPolicy, return UA_STATUSCODE_BADINTERNALERROR, &server->config.logger, UA_LOGCATEGORY_SERVER, "No SecurityPolicy configured for the ReaderGroup"); if(securityTokenId != rg->securityTokenId) { rg->securityTokenId = securityTokenId; rg->nonceSequenceNumber = 1; } /* Create a new context */ if(!rg->securityPolicyContext) { return rg->config.securityPolicy-> newContext(rg->config.securityPolicy->policyContext, &signingKey, &encryptingKey, &keyNonce, &rg->securityPolicyContext); } /* Update the context */ return rg->config.securityPolicy-> setSecurityKeys(rg->securityPolicyContext, &signingKey, &encryptingKey, &keyNonce); } #endif /* Freezing of the configuration */ UA_StatusCode UA_Server_freezeReaderGroupConfiguration(UA_Server *server, const UA_NodeId readerGroupId) { UA_ReaderGroup *rg = UA_ReaderGroup_findRGbyId(server, readerGroupId); if(!rg) return UA_STATUSCODE_BADNOTFOUND; /* PubSubConnection freezeCounter++ */ UA_NodeId pubSubConnectionId = rg->linkedConnection; UA_PubSubConnection *pubSubConnection = UA_PubSubConnection_findConnectionbyId(server, pubSubConnectionId); pubSubConnection->configurationFreezeCounter++; pubSubConnection->configurationFrozen = UA_TRUE; /* ReaderGroup freeze */ /* TODO: Clarify on the freeze functionality in multiple DSR, multiple * networkMessage conf in a RG */ rg->configurationFrozen = UA_TRUE; /* DataSetReader freeze */ UA_DataSetReader *dataSetReader; UA_UInt16 dsrCount = 0; LIST_FOREACH(dataSetReader, &rg->readers, listEntry){ dataSetReader->configurationFrozen = UA_TRUE; dsrCount++; /* TODO: Configuration frozen for subscribedDataSet once * UA_Server_DataSetReader_addTargetVariables API modified to support * adding target variable one by one or in a group stored in a list. */ } /* Not rt, we don't have to adjust anything */ if(rg->config.rtLevel != UA_PUBSUB_RT_FIXED_SIZE) return UA_STATUSCODE_GOOD; if(dsrCount > 1) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Mutiple DSR in a readerGroup not supported in RT " "fixed size configuration"); return UA_STATUSCODE_BADNOTIMPLEMENTED; } dataSetReader = LIST_FIRST(&rg->readers); /* Support only to UADP encoding */ if(dataSetReader->config.messageSettings.content.decoded.type != &UA_TYPES[UA_TYPES_UADPDATASETREADERMESSAGEDATATYPE]) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub-RT configuration fail: Non-RT capable encoding."); return UA_STATUSCODE_BADNOTSUPPORTED; } size_t fieldsSize = dataSetReader->config.dataSetMetaData.fieldsSize; for(size_t i = 0; i < fieldsSize; i++) { UA_FieldTargetVariable *tv = &dataSetReader->config.subscribedDataSet.subscribedDataSetTarget.targetVariables[i]; const UA_VariableNode *rtNode = (const UA_VariableNode *) UA_NODESTORE_GET(server, &tv->targetVariable.targetNodeId); if(rtNode != NULL && rtNode->valueBackend.backendType != UA_VALUEBACKENDTYPE_EXTERNAL) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub-RT configuration fail: PDS contains field " "without external data source."); UA_NODESTORE_RELEASE(server, (const UA_Node *) rtNode); return UA_STATUSCODE_BADNOTSUPPORTED; } UA_NODESTORE_RELEASE(server, (const UA_Node *) rtNode); UA_FieldMetaData *field = &dataSetReader->config.dataSetMetaData.fields[i]; if((UA_NodeId_equal(&field->dataType, &UA_TYPES[UA_TYPES_STRING].typeId) || UA_NodeId_equal(&field->dataType, &UA_TYPES[UA_TYPES_BYTESTRING].typeId)) && field->maxStringLength == 0) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub-RT configuration fail: " "PDS contains String/ByteString with dynamic length."); return UA_STATUSCODE_BADNOTSUPPORTED; } else if(!UA_DataType_isNumeric(UA_findDataType(&field->dataType)) && !UA_NodeId_equal(&field->dataType, &UA_TYPES[UA_TYPES_BOOLEAN].typeId)) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub-RT configuration fail: " "PDS contains variable with dynamic size."); return UA_STATUSCODE_BADNOTSUPPORTED; } } UA_DataSetMessage *dsm = (UA_DataSetMessage *) UA_calloc(1, sizeof(UA_DataSetMessage)); if(!dsm) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub RT Offset calculation: DSM creation failed"); return UA_STATUSCODE_BADOUTOFMEMORY; } /* Generate the DSM */ UA_StatusCode res = UA_DataSetReader_generateDataSetMessage(server, dsm, dataSetReader); if(res != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub RT Offset calculation: DataSetMessage generation failed"); UA_DataSetMessage_clear(dsm); UA_free(dsm); return UA_STATUSCODE_BADINTERNALERROR; } /* Generate data set messages - Considering 1 DSM as max */ UA_UInt16 *dsWriterIds = (UA_UInt16 *)UA_calloc(1, sizeof(UA_UInt16)); if(!dsWriterIds) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub RT Offset calculation: DataSetWriterId creation failed"); UA_DataSetMessage_clear(dsm); UA_free(dsm); return UA_STATUSCODE_BADOUTOFMEMORY; } *dsWriterIds = dataSetReader->config.dataSetWriterId; UA_NetworkMessage *networkMessage = (UA_NetworkMessage *)UA_calloc(1, sizeof(UA_NetworkMessage)); if(!networkMessage) { UA_free(dsWriterIds); UA_DataSetMessage_clear(dsm); UA_free(dsm); UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub RT Offset calculation: Network message creation failed"); return UA_STATUSCODE_BADOUTOFMEMORY; } res = UA_DataSetReader_generateNetworkMessage(pubSubConnection, dataSetReader, dsm, dsWriterIds, 1, networkMessage); if(res != UA_STATUSCODE_GOOD) { UA_free(networkMessage->payload.dataSetPayload.sizes); UA_free(networkMessage); UA_free(dsWriterIds); UA_DataSetMessage_clear(dsm); UA_free(dsm); UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub RT Offset calculation: NetworkMessage generation failed"); return UA_STATUSCODE_BADINTERNALERROR; } /* Fix the offsets necessary to decode */ memset(&dataSetReader->bufferedMessage, 0, sizeof(UA_NetworkMessageOffsetBuffer)); UA_NetworkMessage_calcSizeBinary(networkMessage, &dataSetReader->bufferedMessage); dataSetReader->bufferedMessage.RTsubscriberEnabled = true; dataSetReader->bufferedMessage.nm = networkMessage; return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Server_unfreezeReaderGroupConfiguration(UA_Server *server, const UA_NodeId readerGroupId) { UA_ReaderGroup *rg = UA_ReaderGroup_findRGbyId(server, readerGroupId); if(!rg) return UA_STATUSCODE_BADNOTFOUND; /* PubSubConnection freezeCounter-- */ UA_NodeId pubSubConnectionId = rg->linkedConnection; UA_PubSubConnection *pubSubConnection = UA_PubSubConnection_findConnectionbyId(server, pubSubConnectionId); pubSubConnection->configurationFreezeCounter--; if(pubSubConnection->configurationFreezeCounter == 0){ pubSubConnection->configurationFrozen = UA_FALSE; } /* ReaderGroup unfreeze */ rg->configurationFrozen = UA_FALSE; /* DataSetReader unfreeze */ UA_DataSetReader *dataSetReader; LIST_FOREACH(dataSetReader, &rg->readers, listEntry) { UA_NetworkMessageOffsetBuffer_clear(&dataSetReader->bufferedMessage); dataSetReader->configurationFrozen = false; } return UA_STATUSCODE_GOOD; } /* This triggers the collection and reception of NetworkMessages and the * contained DataSetMessages. */ void UA_ReaderGroup_subscribeCallback(UA_Server *server, UA_ReaderGroup *readerGroup) { // TODO: feedback for debug-assert vs runtime-check UA_assert(server); UA_assert(readerGroup); UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub subscribe callback"); UA_PubSubConnection *connection = UA_PubSubConnection_findConnectionbyId(server, readerGroup->linkedConnection); if(!connection) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "SubscribeCallback(): Find linked connection failed"); UA_ReaderGroup_setPubSubState(server, UA_PUBSUBSTATE_ERROR, readerGroup); return; } receiveBufferedNetworkMessage(server, readerGroup, connection); } /* 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; if(readerGroup->config.pubsubManagerCallback.addCustomCallback) retval = readerGroup->config.pubsubManagerCallback. addCustomCallback(server, readerGroup->identifier, (UA_ServerCallback)UA_ReaderGroup_subscribeCallback, readerGroup, readerGroup->config.subscribingInterval, NULL, // TODO: Send base time from reader group config // TODO: Send timer policy from reader group config UA_TIMER_HANDLE_CYCLEMISS_WITH_CURRENTTIME, &readerGroup->subscribeCallbackId); else { if(readerGroup->config.enableBlockingSocket == UA_TRUE) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "addSubscribeCallback() failed, blocking socket " "functionality only supported in customcallback"); return UA_STATUSCODE_BADNOTSUPPORTED; } retval = UA_PubSubManager_addRepeatedCallback(server, (UA_ServerCallback)UA_ReaderGroup_subscribeCallback, readerGroup, readerGroup->config.subscribingInterval, NULL, // TODO: Send base time from reader group config // TODO: Send timer policy from reader group config UA_TIMER_HANDLE_CYCLEMISS_WITH_CURRENTTIME, &readerGroup->subscribeCallbackId); } /* Run once after creation */ /* When using blocking socket functionality, the server mechanism might get * blocked. It is highly recommended to use custom callback when using * blockingsocket. */ if(readerGroup->config.enableBlockingSocket != UA_TRUE) UA_ReaderGroup_subscribeCallback(server, readerGroup); return retval; } void UA_ReaderGroup_removeSubscribeCallback(UA_Server *server, UA_ReaderGroup *readerGroup) { if(readerGroup->config.pubsubManagerCallback.removeCustomCallback) readerGroup->config.pubsubManagerCallback. removeCustomCallback(server, readerGroup->identifier, readerGroup->subscribeCallbackId); else UA_PubSubManager_removeRepeatedPubSubCallback(server, readerGroup->subscribeCallbackId); } #endif /* UA_ENABLE_PUBSUB */ /**** amalgamated original file "/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-2019 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.pubSubConfig.transportLayersSize; i++) { if(connectionConfig && UA_String_equal(&server->config.pubSubConfig.transportLayers[i].transportProfileUri, &connectionConfig->transportProfileUri)) { tl = &server->config.pubSubConfig.transportLayers[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_calloc(1, sizeof(UA_PubSubConnection)); if(!newConnectionsField) { UA_PubSubConnectionConfig_clear(tmpConnectionConfig); UA_free(tmpConnectionConfig); UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub Connection creation failed. Out of Memory."); return UA_STATUSCODE_BADOUTOFMEMORY; } newConnectionsField->componentType = UA_PUBSUB_COMPONENT_CONNECTION; if (server->pubSubManager.connectionsSize != 0) TAILQ_INSERT_TAIL(&server->pubSubManager.connections, newConnectionsField, listEntry); else { TAILQ_INIT(&server->pubSubManager.connections); TAILQ_INSERT_HEAD(&server->pubSubManager.connections, newConnectionsField, listEntry); } server->pubSubManager.connectionsSize++; LIST_INIT(&newConnectionsField->writerGroups); newConnectionsField->config = tmpConnectionConfig; /* Open the channel */ newConnectionsField->channel = tl->createPubSubChannel(newConnectionsField->config); if(!newConnectionsField->channel) { UA_PubSubConnection_clear(server, newConnectionsField); TAILQ_REMOVE(&server->pubSubManager.connections, newConnectionsField, listEntry); server->pubSubManager.connectionsSize--; UA_free(newConnectionsField); UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "PubSub Connection creation failed. Transport layer creation problem."); return UA_STATUSCODE_BADINTERNALERROR; } #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL /* Internally createa a unique id */ addPubSubConnectionRepresentation(server, newConnectionsField); #else /* Create a unique NodeId that does not correspond to a Node */ UA_PubSubManager_generateUniqueNodeId(&server->pubSubManager, &newConnectionsField->identifier); #endif if(connectionIdentifier) UA_NodeId_copy(&newConnectionsField->identifier, connectionIdentifier); 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 UA_PubSubConnection *currentConnection = UA_PubSubConnection_findConnectionbyId(server, connection); if(!currentConnection) return UA_STATUSCODE_BADNOTFOUND; UA_WriterGroup *wg, *wg_tmp; LIST_FOREACH_SAFE(wg, ¤tConnection->writerGroups, listEntry, wg_tmp) { UA_Server_unfreezeWriterGroupConfiguration(server, wg->identifier); UA_Server_removeWriterGroup(server, wg->identifier); } UA_ReaderGroup *rg, *rg_tmp; LIST_FOREACH_SAFE(rg, ¤tConnection->readerGroups, listEntry, rg_tmp) { UA_Server_unfreezeReaderGroupConfiguration(server, rg->identifier); UA_Server_removeReaderGroup(server, rg->identifier); } #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL removePubSubConnectionRepresentation(server, currentConnection); #endif server->pubSubManager.connectionsSize--; UA_PubSubConnection_clear(server, currentConnection); TAILQ_REMOVE(&server->pubSubManager.connections, currentConnection, listEntry); UA_free(currentConnection); return UA_STATUSCODE_GOOD; } UA_StatusCode UA_PubSubConnection_regist(UA_Server *server, UA_NodeId *connectionIdentifier) { UA_PubSubConnection *connection = UA_PubSubConnection_findConnectionbyId(server, *connectionIdentifier); if(!connection) return UA_STATUSCODE_BADNOTFOUND; if(connection->isRegistered) { UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "Connection already registered"); return UA_STATUSCODE_GOOD; } UA_StatusCode retval = connection->channel->regist(connection->channel, NULL, NULL); if(retval != UA_STATUSCODE_GOOD) UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "register channel failed: 0x%" PRIx32 "!", retval); connection->isRegistered = UA_TRUE; return retval; } 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; } /* Create new PDS and add to UA_PubSubManager */ UA_PublishedDataSet *newPDS = (UA_PublishedDataSet *) UA_calloc(1, sizeof(UA_PublishedDataSet)); if(!newPDS) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "PublishedDataSet creation failed. Out of Memory."); result.addResult = UA_STATUSCODE_BADOUTOFMEMORY; return result; } TAILQ_INIT(&newPDS->fields); UA_PublishedDataSetConfig *newConfig = &newPDS->config; /* Deep copy the given connection config */ UA_StatusCode res = UA_PublishedDataSetConfig_copy(publishedDataSetConfig, newConfig); if(res != UA_STATUSCODE_GOOD){ UA_free(newPDS); UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "PublishedDataSet creation failed. Configuration copy failed."); result.addResult = UA_STATUSCODE_BADINTERNALERROR; return result; } /* TODO: Parse template config and add fields (later PubSub batch) */ if(newConfig->publishedDataSetType == UA_PUBSUB_DATASET_PUBLISHEDITEMS_TEMPLATE) { } /* Fill the DataSetMetaData */ result.configurationVersion.majorVersion = UA_PubSubConfigurationVersionTimeDifference(); result.configurationVersion.minorVersion = UA_PubSubConfigurationVersionTimeDifference(); switch(newConfig->publishedDataSetType) { case UA_PUBSUB_DATASET_PUBLISHEDEVENTS_TEMPLATE: res = UA_STATUSCODE_BADNOTSUPPORTED; break; case UA_PUBSUB_DATASET_PUBLISHEDEVENTS: res = UA_STATUSCODE_BADNOTSUPPORTED; break; case UA_PUBSUB_DATASET_PUBLISHEDITEMS: newPDS->dataSetMetaData.configurationVersion.majorVersion = UA_PubSubConfigurationVersionTimeDifference(); newPDS->dataSetMetaData.configurationVersion.minorVersion = UA_PubSubConfigurationVersionTimeDifference(); newPDS->dataSetMetaData.description = UA_LOCALIZEDTEXT_ALLOC("", ""); newPDS->dataSetMetaData.dataSetClassId = UA_GUID_NULL; res = UA_String_copy(&newConfig->name, &newPDS->dataSetMetaData.name); break; case UA_PUBSUB_DATASET_PUBLISHEDITEMS_TEMPLATE: res = UA_DataSetMetaDataType_copy(&newConfig->config.itemsTemplate.metaData, &newPDS->dataSetMetaData); break; default: res = UA_STATUSCODE_BADINTERNALERROR; } /* Abort? */ result.addResult = res; if(result.addResult != UA_STATUSCODE_GOOD) { UA_PublishedDataSetConfig_clear(newConfig); UA_free(newPDS); return result; } /* Insert into the queue of the manager */ if(server->pubSubManager.publishedDataSetsSize != 0) { TAILQ_INSERT_TAIL(&server->pubSubManager.publishedDataSets, newPDS, listEntry); } else { TAILQ_INIT(&server->pubSubManager.publishedDataSets); TAILQ_INSERT_HEAD(&server->pubSubManager.publishedDataSets, newPDS, listEntry); } server->pubSubManager.publishedDataSetsSize++; #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL /* Create representation and unique id */ addPublishedDataItemsRepresentation(server, newPDS); #else /* Generate unique nodeId */ UA_PubSubManager_generateUniqueNodeId(&server->pubSubManager, &newPDS->identifier); #endif if(pdsIdentifier) UA_NodeId_copy(&newPDS->identifier, pdsIdentifier); 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 = UA_PublishedDataSet_findPDSbyId(server, pds); if(!publishedDataSet){ return UA_STATUSCODE_BADNOTFOUND; } if(publishedDataSet->configurationFrozen){ UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Remove PublishedDataSet failed. PublishedDataSet is frozen."); return UA_STATUSCODE_BADCONFIGURATIONERROR; } //search for referenced writers -> delete this writers. (Standard: writer must be connected with PDS) UA_PubSubConnection *tmpConnectoin; TAILQ_FOREACH(tmpConnectoin, &server->pubSubManager.connections, listEntry){ UA_WriterGroup *writerGroup; LIST_FOREACH(writerGroup, &tmpConnectoin->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_clear(server, publishedDataSet); server->pubSubManager.publishedDataSetsSize--; TAILQ_REMOVE(&server->pubSubManager.publishedDataSets, publishedDataSet, listEntry); UA_free(publishedDataSet); return UA_STATUSCODE_GOOD; } /* Calculate the time difference between current time and UTC (00:00) on January * 1, 2000. */ UA_UInt32 UA_PubSubConfigurationVersionTimeDifference(void) { 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. */ #ifndef UA_ENABLE_PUBSUB_INFORMATIONMODEL void UA_PubSubManager_generateUniqueNodeId(UA_PubSubManager *psm, UA_NodeId *nodeId) { *nodeId = UA_NODEID_NUMERIC(1, ++psm->uniqueIdCount); } #endif UA_Guid UA_PubSubManager_generateUniqueGuid(UA_Server *server) { while(true) { UA_NodeId testId = UA_NODEID_GUID(1, UA_Guid_random()); const UA_Node *testNode = UA_NODESTORE_GET(server, &testId); if(!testNode) return testId.identifier.guid; UA_NODESTORE_RELEASE(server, testNode); } } /* 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."); /* Stop and unfreeze all WriterGroups */ UA_PubSubConnection *tmpConnection; TAILQ_FOREACH(tmpConnection, &server->pubSubManager.connections, listEntry){ for(size_t i = 0; i < pubSubManager->connectionsSize; i++) { UA_WriterGroup *writerGroup; LIST_FOREACH(writerGroup, &tmpConnection->writerGroups, listEntry) { UA_WriterGroup_setPubSubState(server, UA_PUBSUBSTATE_DISABLED, writerGroup); UA_Server_unfreezeWriterGroupConfiguration(server, writerGroup->identifier); } } } //free the currently configured transport layers if(server->config.pubSubConfig.transportLayersSize > 0) { UA_free(server->config.pubSubConfig.transportLayers); server->config.pubSubConfig.transportLayersSize = 0; } //remove Connections and WriterGroups UA_PubSubConnection *tmpConnection1, *tmpConnection2; TAILQ_FOREACH_SAFE(tmpConnection1, &server->pubSubManager.connections, listEntry, tmpConnection2){ UA_Server_removePubSubConnection(server, tmpConnection1->identifier); } UA_PublishedDataSet *tmpPDS1, *tmpPDS2; TAILQ_FOREACH_SAFE(tmpPDS1, &server->pubSubManager.publishedDataSets, listEntry, tmpPDS2){ UA_Server_removePublishedDataSet(server, tmpPDS1->identifier); } } /***********************************/ /* PubSub Jobs abstraction */ /***********************************/ /* Default Timer based PubSub Callbacks */ UA_StatusCode UA_PubSubManager_addRepeatedCallback(UA_Server *server, UA_ServerCallback callback, void *data, UA_Double interval_ms, UA_DateTime *baseTime, UA_TimerPolicy timerPolicy, UA_UInt64 *callbackId) { return UA_Timer_addRepeatedCallback(&server->timer, (UA_ApplicationCallback)callback, server, data, interval_ms, baseTime, timerPolicy, callbackId); } UA_StatusCode UA_PubSubManager_changeRepeatedCallback(UA_Server *server, UA_UInt64 callbackId, UA_Double interval_ms, UA_DateTime *baseTime, UA_TimerPolicy timerPolicy) { return UA_Timer_changeRepeatedCallback(&server->timer, callbackId, interval_ms, baseTime, timerPolicy); } void UA_PubSubManager_removeRepeatedPubSubCallback(UA_Server *server, UA_UInt64 callbackId) { UA_Timer_removeCallback(&server->timer, callbackId); } #ifdef UA_ENABLE_PUBSUB_MONITORING static UA_StatusCode UA_PubSubComponent_createMonitoring(UA_Server *server, UA_NodeId Id, UA_PubSubComponentEnumType eComponentType, UA_PubSubMonitoringType eMonitoringType, void *data, UA_ServerCallback callback) { if ((!server) || (!data)) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Error UA_PubSubComponent_createMonitoring(): " "null pointer param"); return UA_STATUSCODE_BADINVALIDARGUMENT; } UA_StatusCode ret = UA_STATUSCODE_GOOD; switch (eComponentType) { case UA_PUBSUB_COMPONENT_DATASETREADER: { UA_DataSetReader *reader = (UA_DataSetReader*) data; switch (eMonitoringType) { case UA_PUBSUB_MONITORING_MESSAGE_RECEIVE_TIMEOUT: UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_PubSubComponent_createMonitoring(): DataSetReader '%.*s' " "- MessageReceiveTimeout", (UA_Int32) reader->config.name.length, reader->config.name.data); reader->msgRcvTimeoutTimerCallback = callback; break; default: UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_PubSubComponent_createMonitoring(): DataSetReader '%.*s' " "DataSetReader does not support timeout type '%i'", (UA_Int32) reader->config.name.length, reader->config.name.data, eMonitoringType); ret = UA_STATUSCODE_BADNOTSUPPORTED; break; } break; } default: UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Error UA_PubSubComponent_createMonitoring(): PubSub component type '%i' is not supported", eComponentType); ret = UA_STATUSCODE_BADNOTSUPPORTED; break; } return ret; } static UA_StatusCode UA_PubSubComponent_startMonitoring(UA_Server *server, UA_NodeId Id, UA_PubSubComponentEnumType eComponentType, UA_PubSubMonitoringType eMonitoringType, void *data) { if ((!server) || (!data)) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Error UA_PubSubComponent_startMonitoring(): " "null pointer param"); return UA_STATUSCODE_BADINVALIDARGUMENT; } UA_StatusCode ret = UA_STATUSCODE_GOOD; switch (eComponentType) { case UA_PUBSUB_COMPONENT_DATASETREADER: { UA_DataSetReader *reader = (UA_DataSetReader*) data; switch (eMonitoringType) { case UA_PUBSUB_MONITORING_MESSAGE_RECEIVE_TIMEOUT: { /* use a timed callback, because one notification is enough, we assume that MessageReceiveTimeout configuration is in [ms], we do not handle or check fractions */ UA_UInt64 interval = (UA_UInt64)(reader->config.messageReceiveTimeout * UA_DATETIME_MSEC); ret = UA_Timer_addTimedCallback(&server->timer, (UA_ApplicationCallback) reader->msgRcvTimeoutTimerCallback, server, reader, UA_DateTime_nowMonotonic() + (UA_DateTime) interval, &(reader->msgRcvTimeoutTimerId)); if (ret == UA_STATUSCODE_GOOD) { UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_PubSubComponent_startMonitoring(): DataSetReader '%.*s'- MessageReceiveTimeout: MessageReceiveTimeout = '%f' " "Timer Id = '%u'", (UA_Int32) reader->config.name.length, reader->config.name.data, reader->config.messageReceiveTimeout, (UA_UInt32) reader->msgRcvTimeoutTimerId); } else { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Error UA_PubSubComponent_startMonitoring(): DataSetReader '%.*s' - MessageReceiveTimeout: start timer failed", (UA_Int32) reader->config.name.length, reader->config.name.data); } break; } default: UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_PubSubComponent_startMonitoring(): DataSetReader '%.*s' " "DataSetReader does not support timeout type '%i'", (UA_Int32) reader->config.name.length, reader->config.name.data, eMonitoringType); ret = UA_STATUSCODE_BADNOTSUPPORTED; break; } break; } default: UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Error UA_PubSubComponent_startMonitoring(): PubSub component type '%i' is not supported", eComponentType); ret = UA_STATUSCODE_BADNOTSUPPORTED; break; } return ret; } static UA_StatusCode UA_PubSubComponent_stopMonitoring(UA_Server *server, UA_NodeId Id, UA_PubSubComponentEnumType eComponentType, UA_PubSubMonitoringType eMonitoringType, void *data) { if ((!server) || (!data)) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Error UA_PubSubComponent_stopMonitoring(): " "null pointer param"); return UA_STATUSCODE_BADINVALIDARGUMENT; } UA_StatusCode ret = UA_STATUSCODE_GOOD; switch (eComponentType) { case UA_PUBSUB_COMPONENT_DATASETREADER: { UA_DataSetReader *reader = (UA_DataSetReader*) data; switch (eMonitoringType) { case UA_PUBSUB_MONITORING_MESSAGE_RECEIVE_TIMEOUT: { UA_Timer_removeCallback(&server->timer, reader->msgRcvTimeoutTimerId); UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_PubSubComponent_stopMonitoring(): DataSetReader '%.*s' - MessageReceiveTimeout: MessageReceiveTimeout = '%f' " "Timer Id = '%u'", (UA_Int32) reader->config.name.length, reader->config.name.data, reader->config.messageReceiveTimeout, (UA_UInt32) reader->msgRcvTimeoutTimerId); break; } default: UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_PubSubComponent_stopMonitoring(): DataSetReader '%.*s' " "DataSetReader does not support timeout type '%i'", (UA_Int32) reader->config.name.length, reader->config.name.data, eMonitoringType); ret = UA_STATUSCODE_BADNOTSUPPORTED; break; } break; } default: UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Error UA_PubSubComponent_stopMonitoring(): PubSub component type '%i' is not supported", eComponentType); ret = UA_STATUSCODE_BADNOTSUPPORTED; break; } return ret; } static UA_StatusCode UA_PubSubComponent_updateMonitoringInterval(UA_Server *server, UA_NodeId Id, UA_PubSubComponentEnumType eComponentType, UA_PubSubMonitoringType eMonitoringType, void *data) { if ((!server) || (!data)) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Error UA_PubSubComponent_updateMonitoringInterval(): " "null pointer param"); return UA_STATUSCODE_BADINVALIDARGUMENT; } UA_StatusCode ret = UA_STATUSCODE_GOOD; switch (eComponentType) { case UA_PUBSUB_COMPONENT_DATASETREADER: { UA_DataSetReader *reader = (UA_DataSetReader*) data; switch (eMonitoringType) { case UA_PUBSUB_MONITORING_MESSAGE_RECEIVE_TIMEOUT: { ret = UA_Timer_changeRepeatedCallback(&server->timer, reader->msgRcvTimeoutTimerId, reader->config.messageReceiveTimeout, NULL, UA_TIMER_HANDLE_CYCLEMISS_WITH_CURRENTTIME); if (ret == UA_STATUSCODE_GOOD) { UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_PubSubComponent_updateMonitoringInterval(): DataSetReader '%.*s' - MessageReceiveTimeout: new MessageReceiveTimeout = '%f' " "Timer Id = '%u'", (UA_Int32) reader->config.name.length, reader->config.name.data, reader->config.messageReceiveTimeout, (UA_UInt32) reader->msgRcvTimeoutTimerId); } else { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Error UA_PubSubComponent_updateMonitoringInterval(): DataSetReader '%.*s': update timer interval failed", (UA_Int32) reader->config.name.length, reader->config.name.data); } break; } default: UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_PubSubComponent_createMonitoring(): DataSetReader '%.*s' " "DataSetReader does not support timeout type '%i'", (UA_Int32) reader->config.name.length, reader->config.name.data, eMonitoringType); ret = UA_STATUSCODE_BADNOTSUPPORTED; break; } break; } default: UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Error UA_PubSubComponent_updateMonitoringInterval(): PubSub component type '%i' is not supported", eComponentType); ret = UA_STATUSCODE_BADNOTSUPPORTED; break; } return ret; } static UA_StatusCode UA_PubSubComponent_deleteMonitoring(UA_Server *server, UA_NodeId Id, UA_PubSubComponentEnumType eComponentType, UA_PubSubMonitoringType eMonitoringType, void *data) { if ((!server) || (!data)) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Error UA_PubSubComponent_deleteMonitoring(): " "null pointer param"); return UA_STATUSCODE_BADINVALIDARGUMENT; } UA_StatusCode ret = UA_STATUSCODE_GOOD; switch (eComponentType) { case UA_PUBSUB_COMPONENT_DATASETREADER: { UA_DataSetReader *reader = (UA_DataSetReader*) data; switch (eMonitoringType) { case UA_PUBSUB_MONITORING_MESSAGE_RECEIVE_TIMEOUT: UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_PubSubComponent_deleteMonitoring(): DataSetReader '%.*s' - MessageReceiveTimeout: Timer Id = '%u'", (UA_Int32) reader->config.name.length, reader->config.name.data, (UA_UInt32) reader->msgRcvTimeoutTimerId); break; default: UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "UA_PubSubComponent_deleteMonitoring(): DataSetReader '%.*s' " "DataSetReader does not support timeout type '%i'", (UA_Int32) reader->config.name.length, reader->config.name.data, eMonitoringType); ret = UA_STATUSCODE_BADNOTSUPPORTED; break; } break; } default: UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Error UA_PubSubComponent_deleteMonitoring(): PubSub component type '%i' is not supported", eComponentType); ret = UA_STATUSCODE_BADNOTSUPPORTED; break; } return ret; } UA_StatusCode UA_PubSubManager_setDefaultMonitoringCallbacks(UA_PubSubMonitoringInterface *monitoringInterface) { if (monitoringInterface == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } monitoringInterface->createMonitoring = UA_PubSubComponent_createMonitoring; monitoringInterface->startMonitoring = UA_PubSubComponent_startMonitoring; monitoringInterface->stopMonitoring = UA_PubSubComponent_stopMonitoring; monitoringInterface->updateMonitoringInterval = UA_PubSubComponent_updateMonitoringInterval; monitoringInterface->deleteMonitoring = UA_PubSubComponent_deleteMonitoring; return UA_STATUSCODE_GOOD; } #endif /* UA_ENABLE_PUBSUB_MONITORING */ #endif /* UA_ENABLE_PUBSUB */ /**** amalgamated original file "/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-2021 Kalycito Infotech Private Limited * Copyright (c) 2020 Yannick Wallerer, Siemens AG * Copyright (c) 2020 Thomas Fischer, Siemens AG */ #ifdef UA_ENABLE_PUBSUB_FILE_CONFIG #endif #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL /* conditional compilation */ typedef struct { UA_NodeId parentNodeId; UA_UInt32 parentClassifier; UA_UInt32 elementClassiefier; } UA_NodePropertyContext; 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; UA_StatusCode res = UA_NodeId_copy(&bpr.targets[0].targetId.nodeId, &resultNodeId); if(res != UA_STATUSCODE_GOOD){ UA_BrowsePathResult_clear(&bpr); return UA_NODEID_NULL; } UA_BrowsePathResult_clear(&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_setScalarCopy(&value, &pubSubConnection->config->publisherId.numeric, &UA_TYPES[UA_TYPES_STRING]); } else { UA_Variant_setScalarCopy(&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_DATASETREADERTYPE: { UA_DataSetReader *dataSetReader = UA_ReaderGroup_findDSRbyId(server, *myNodeId); if(!dataSetReader) return; switch(nodeContext->elementClassiefier) { case UA_NS0ID_DATASETREADERTYPE_PUBLISHERID: UA_Variant_setScalarCopy(&value, dataSetReader->config.publisherId.data, dataSetReader->config.publisherId.type); 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_setScalarCopy(&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_DATASETWRITERTYPE: { UA_DataSetWriter *dataSetWriter = UA_DataSetWriter_findDSWbyId(server, *myNodeId); if(!dataSetWriter) return; switch(nodeContext->elementClassiefier) { case UA_NS0ID_DATASETWRITERTYPE_DATASETWRITERID: UA_Variant_setScalarCopy(&value, &dataSetWriter->config.dataSetWriterId, &UA_TYPES[UA_TYPES_UINT16]); 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; TAILQ_FOREACH(field, &publishedDataSet->fields, listEntry) { pvd[counter].attributeId = UA_ATTRIBUTEID_VALUE; 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); UA_Variant_clear(&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_clear(&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); } #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS static UA_StatusCode addPubSubConnectionConfig(UA_Server *server, UA_PubSubConnectionDataType *pubsubConnectionDataType, UA_NodeId *connectionId){ UA_StatusCode retVal = UA_STATUSCODE_GOOD; 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; } retVal |= UA_Server_addPubSubConnection(server, &connectionConfig, connectionId); UA_NetworkAddressUrlDataType_clear(&networkAddressUrlDataType); return retVal; } /** * **WriterGroup handling** * * The WriterGroup (WG) is part of the connection and contains the primary * configuration parameters for the message creation. */ static UA_StatusCode addWriterGroupConfig(UA_Server *server, UA_NodeId connectionId, UA_WriterGroupDataType *writerGroupDataType, UA_NodeId *writerGroupId){ /* Now we create a new WriterGroupConfig and add the group to the existing * PubSubConnection. */ UA_WriterGroupConfig writerGroupConfig; memset(&writerGroupConfig, 0, sizeof(UA_WriterGroupConfig)); writerGroupConfig.name = writerGroupDataType->name; writerGroupConfig.publishingInterval = writerGroupDataType->publishingInterval; writerGroupConfig.enabled = writerGroupDataType->enabled; writerGroupConfig.writerGroupId = writerGroupDataType->writerGroupId; //TODO remove hard coded UADP writerGroupConfig.encodingMimeType = UA_PUBSUB_ENCODING_UADP; writerGroupConfig.priority = writerGroupDataType->priority; UA_UadpWriterGroupMessageDataType writerGroupMessage; UA_ExtensionObject *eoWG = &writerGroupDataType->messageSettings; if(eoWG->encoding == UA_EXTENSIONOBJECT_DECODED){ writerGroupConfig.messageSettings.encoding = UA_EXTENSIONOBJECT_DECODED; if(eoWG->content.decoded.type == &UA_TYPES[UA_TYPES_UADPWRITERGROUPMESSAGEDATATYPE]){ if(UA_UadpWriterGroupMessageDataType_copy((UA_UadpWriterGroupMessageDataType *) eoWG->content.decoded.data, &writerGroupMessage) != UA_STATUSCODE_GOOD){ return UA_STATUSCODE_BADOUTOFMEMORY; } writerGroupConfig.messageSettings.content.decoded.type = &UA_TYPES[UA_TYPES_UADPWRITERGROUPMESSAGEDATATYPE]; writerGroupConfig.messageSettings.content.decoded.data = &writerGroupMessage; } } return UA_Server_addWriterGroup(server, connectionId, &writerGroupConfig, writerGroupId); } /** * **DataSetWriter handling** * * A DataSetWriter (DSW) is the glue between the WG and the PDS. The DSW is * linked to exactly one PDS and contains additional informations for the * message generation. */ static UA_StatusCode addDataSetWriterConfig(UA_Server *server, const UA_NodeId *writerGroupId, UA_DataSetWriterDataType *dataSetWriterDataType, UA_NodeId *dataSetWriterId) { UA_NodeId publishedDataSetId = UA_NODEID_NULL; UA_PublishedDataSet *tmpPDS; TAILQ_FOREACH(tmpPDS, &server->pubSubManager.publishedDataSets, listEntry){ if(UA_String_equal(&dataSetWriterDataType->dataSetName, &tmpPDS->config.name)) { publishedDataSetId = tmpPDS->identifier; break; } } if(UA_NodeId_isNull(&publishedDataSetId)) return UA_STATUSCODE_BADPARENTNODEIDINVALID; /* We need now a DataSetWriter within the WriterGroup. This means we must * create a new DataSetWriterConfig and add call the addWriterGroup function. */ UA_DataSetWriterConfig dataSetWriterConfig; memset(&dataSetWriterConfig, 0, sizeof(UA_DataSetWriterConfig)); dataSetWriterConfig.name = dataSetWriterDataType->name; dataSetWriterConfig.dataSetWriterId = dataSetWriterDataType->dataSetWriterId; dataSetWriterConfig.keyFrameCount = dataSetWriterDataType->keyFrameCount; dataSetWriterConfig.dataSetFieldContentMask = dataSetWriterDataType->dataSetFieldContentMask; return UA_Server_addDataSetWriter(server, *writerGroupId, publishedDataSetId, &dataSetWriterConfig, dataSetWriterId); } /** * **ReaderGroup** * * ReaderGroup is used to group a list of DataSetReaders. All ReaderGroups are * created within a PubSubConnection and automatically deleted if the connection * is removed. All network message related filters are only available in the DataSetReader. */ /* Add ReaderGroup to the created connection */ static UA_StatusCode addReaderGroupConfig(UA_Server *server, UA_NodeId connectionId, UA_ReaderGroupDataType *readerGroupDataType, UA_NodeId *readerGroupId) { UA_ReaderGroupConfig readerGroupConfig; memset(&readerGroupConfig, 0, sizeof(UA_ReaderGroupConfig)); readerGroupConfig.name = readerGroupDataType->name; return UA_Server_addReaderGroup(server, connectionId, &readerGroupConfig, readerGroupId); } /** * **SubscribedDataSet** * * Set SubscribedDataSet type to TargetVariables data type. * Add subscribedvariables to the DataSetReader */ static UA_StatusCode addSubscribedVariables(UA_Server *server, UA_NodeId dataSetReaderId, UA_DataSetReaderDataType *dataSetReaderDataType, UA_DataSetMetaDataType *pMetaData) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ExtensionObject *eoTargetVar = &dataSetReaderDataType->subscribedDataSet; if(eoTargetVar->encoding != UA_EXTENSIONOBJECT_DECODED || eoTargetVar->content.decoded.type != &UA_TYPES[UA_TYPES_TARGETVARIABLESDATATYPE]) return UA_STATUSCODE_BADINTERNALERROR; const UA_TargetVariablesDataType *targetVars = (UA_TargetVariablesDataType *) eoTargetVar->content.decoded.data; UA_NodeId folderId; UA_String folderName = pMetaData->name; UA_ObjectAttributes oAttr = UA_ObjectAttributes_default; UA_QualifiedName folderBrowseName; if(folderName.length > 0) { oAttr.displayName.locale = UA_STRING(""); oAttr.displayName.text = folderName; folderBrowseName.namespaceIndex = 1; folderBrowseName.name = folderName; } else { oAttr.displayName = UA_LOCALIZEDTEXT("", "Subscribed Variables"); folderBrowseName = UA_QUALIFIEDNAME(1, "Subscribed Variables"); } UA_Server_addObjectNode(server, UA_NODEID_NULL, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), folderBrowseName, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE), oAttr, NULL, &folderId); /** * **TargetVariables** * * The SubscribedDataSet option TargetVariables defines a list of Variable * mappings between received DataSet fields and target Variables in the Subscriber * AddressSpace. The values subscribed from the Publisher are updated in the value * field of these variables */ /* Create the TargetVariables with respect to DataSetMetaData fields */ UA_FieldTargetVariable *targetVarsData = (UA_FieldTargetVariable *) UA_calloc(targetVars->targetVariablesSize, sizeof(UA_FieldTargetVariable)); for(size_t i = 0; i < targetVars->targetVariablesSize; i++) { /* Variable to subscribe data */ UA_VariableAttributes vAttr = UA_VariableAttributes_default; UA_LocalizedText_copy(&pMetaData->fields[i].description, &vAttr.description); vAttr.displayName.locale = UA_STRING(""); vAttr.displayName.text = pMetaData->fields[i].name; vAttr.dataType = pMetaData->fields[i].dataType; UA_QualifiedName browseName; browseName.namespaceIndex = 1; browseName.name = pMetaData->fields[i].name; UA_NodeId newNode; retVal |= UA_Server_addVariableNode( server, targetVars->targetVariables[i].targetNodeId, folderId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), browseName, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), vAttr, NULL, &newNode); /* For creating Targetvariables */ targetVarsData[i].targetVariable.attributeId = targetVars->targetVariables[i].attributeId; targetVarsData[i].targetVariable.targetNodeId = newNode; } retVal = UA_Server_DataSetReader_createTargetVariables(server, dataSetReaderId, targetVars->targetVariablesSize, targetVarsData); for(size_t j = 0; j < targetVars->targetVariablesSize; j++) UA_FieldTargetDataType_clear(&targetVarsData[j].targetVariable); UA_free(targetVarsData); return retVal; } /** * **DataSetReader** * * DataSetReader can receive NetworkMessages with the DataSetMessage * of interest sent by the Publisher. DataSetReader provides * the configuration necessary to receive and process DataSetMessages * on the Subscriber side. DataSetReader must be linked with a * SubscribedDataSet and be contained within a ReaderGroup. */ /* Add DataSetReader to the ReaderGroup */ static UA_StatusCode addDataSetReaderConfig(UA_Server *server, UA_NodeId readerGroupId, UA_DataSetReaderDataType *dataSetReaderDataType, UA_NodeId *dataSetReaderId) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataSetReaderConfig readerConfig; memset (&readerConfig, 0, sizeof(UA_DataSetReaderConfig)); readerConfig.name = dataSetReaderDataType->name; /* Parameters to filter which DataSetMessage has to be processed * by the DataSetReader */ if(dataSetReaderDataType->publisherId.type == &UA_TYPES[UA_TYPES_STRING]){ UA_String publisherIdentifier; readerConfig.publisherId.type = &UA_TYPES[UA_TYPES_STRING]; UA_String_copy((UA_String *) dataSetReaderDataType->publisherId.data, &publisherIdentifier); readerConfig.publisherId.data = &publisherIdentifier; } else { UA_UInt16 publisherIdentifier = *(UA_UInt16*)dataSetReaderDataType->publisherId.data; readerConfig.publisherId.type = &UA_TYPES[UA_TYPES_UINT16]; readerConfig.publisherId.data = &publisherIdentifier; } readerConfig.writerGroupId = dataSetReaderDataType->writerGroupId; readerConfig.dataSetWriterId = dataSetReaderDataType->dataSetWriterId; /* Setting up Meta data configuration in DataSetReader */ UA_DataSetMetaDataType *pMetaData; pMetaData = &readerConfig.dataSetMetaData; UA_DataSetMetaDataType_init (pMetaData); pMetaData->name = dataSetReaderDataType->dataSetMetaData.name; pMetaData->fieldsSize = dataSetReaderDataType->dataSetMetaData.fieldsSize; pMetaData->fields = (UA_FieldMetaData*)UA_Array_new (pMetaData->fieldsSize, &UA_TYPES[UA_TYPES_FIELDMETADATA]); for(size_t i = 0; i < pMetaData->fieldsSize; i++){ UA_FieldMetaData_init (&pMetaData->fields[i]); UA_NodeId_copy (&dataSetReaderDataType->dataSetMetaData.fields[i].dataType, &pMetaData->fields[i].dataType); pMetaData->fields[i].builtInType = dataSetReaderDataType->dataSetMetaData.fields[i].builtInType; pMetaData->fields[i].name = dataSetReaderDataType->dataSetMetaData.fields[i].name; pMetaData->fields[i].valueRank = dataSetReaderDataType->dataSetMetaData.fields[i].valueRank; } retVal |= UA_Server_addDataSetReader(server, readerGroupId, &readerConfig, dataSetReaderId); if(retVal != UA_STATUSCODE_GOOD) { UA_free(pMetaData->fields); return retVal; } retVal |= addSubscribedVariables(server, *dataSetReaderId, dataSetReaderDataType, pMetaData); UA_free(pMetaData->fields); return retVal; } #endif /*************************************************/ /* 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; char connectionName[513]; memcpy(connectionName, connection->config->name.data, connection->config->name.length); connectionName[connection->config->name.length] = '\0'; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", connectionName); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(1, 0), /* Generate a new id */ 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, &connection->identifier); attr.displayName = UA_LOCALIZEDTEXT("", "Address"); retVal |= UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 0), connection->identifier, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(0, "Address"), UA_NODEID_NUMERIC(0, UA_NS0ID_NETWORKADDRESSURLTYPE), attr, NULL, NULL); UA_Server_addNode_finish(server, connection->identifier); UA_NodeId addressNode, urlNode, interfaceNode, publisherIdNode, connectionPropertieNode, transportProfileUri; addressNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "Address"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), connection->identifier); 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), connection->identifier); connectionPropertieNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "ConnectionProperties"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), connection->identifier); transportProfileUri = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "TransportProfileUri"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), connection->identifier); if(UA_NodeId_isNull(&addressNode) || UA_NodeId_isNull(&urlNode) || UA_NodeId_isNull(&interfaceNode) || UA_NodeId_isNull(&publisherIdNode) || UA_NodeId_isNull(&connectionPropertieNode) || UA_NodeId_isNull(&transportProfileUri)) { 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); //call API function and create the connection UA_NodeId connectionId; retVal |= addPubSubConnectionConfig(server, &pubSubConnectionDataType, &connectionId); if(retVal != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "addPubSubConnection failed"); return retVal; } for(size_t i = 0; i < pubSubConnectionDataType.writerGroupsSize; i++){ UA_NodeId writerGroupId; UA_WriterGroupDataType *writerGroupDataType = &pubSubConnectionDataType.writerGroups[i]; retVal |= addWriterGroupConfig(server, connectionId, writerGroupDataType, &writerGroupId); if(retVal != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "addWriterGroup failed"); return retVal; } for (size_t j = 0; j < pubSubConnectionDataType.writerGroups[i].dataSetWritersSize; j++){ UA_DataSetWriterDataType *dataSetWriterDataType = &writerGroupDataType->dataSetWriters[j]; retVal |= addDataSetWriterConfig(server, &writerGroupId, dataSetWriterDataType, NULL); if(retVal != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "addDataSetWriter failed"); return retVal; } } //TODO: Need to handle the UA_Server_setWriterGroupOperational based on the status variable in information model if(pubSubConnectionDataType.enabled) { UA_Server_freezeWriterGroupConfiguration(server, writerGroupId); UA_Server_setWriterGroupOperational(server, writerGroupId); } else UA_Server_setWriterGroupDisabled(server, writerGroupId); } for(size_t i = 0; i < pubSubConnectionDataType.readerGroupsSize; i++){ UA_NodeId readerGroupId; UA_ReaderGroupDataType *rg = &pubSubConnectionDataType.readerGroups[i]; retVal |= addReaderGroupConfig(server, connectionId, rg, &readerGroupId); if(retVal != UA_STATUSCODE_GOOD) { UA_NodeId_clear(&readerGroupId); UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "addReaderGroup failed"); return retVal; } for(size_t j = 0; j < rg->dataSetReadersSize; j++) { UA_NodeId dataSetReaderId; UA_DataSetReaderDataType *dataSetReaderDataType = &rg->dataSetReaders[j]; retVal |= addDataSetReaderConfig(server, readerGroupId, dataSetReaderDataType, &dataSetReaderId); if(retVal != UA_STATUSCODE_GOOD) { UA_NodeId_clear(&readerGroupId); UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "addDataSetReader failed"); return retVal; } } //TODO: Need to handle the UA_Server_setReaderGroupOperational based on the status variable in information model if(pubSubConnectionDataType.enabled) { UA_Server_freezeReaderGroupConfiguration(server, readerGroupId); UA_Server_setReaderGroupOperational(server, readerGroupId); } else { UA_Server_setReaderGroupDisabled(server, readerGroupId); } UA_NodeId_clear(&readerGroupId); } //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) { return UA_Server_deleteNode(server, connection->identifier, true); } #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){ if(dataSetReader->config.name.length > 512) return UA_STATUSCODE_BADCONFIGURATIONERROR; char dsrName[513]; memcpy(dsrName, dataSetReader->config.name.data, dataSetReader->config.name.length); dsrName[dataSetReader->config.name.length] = '\0'; UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_NodeId publisherIdNode, writerGroupIdNode, dataSetwriterIdNode; UA_ObjectAttributes object_attr = UA_ObjectAttributes_default; object_attr.displayName = UA_LOCALIZEDTEXT("", dsrName); retVal = UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 0), /* create an id */ dataSetReader->linkedReaderGroup, UA_NODEID_NUMERIC(0, UA_NS0ID_HASDATASETREADER), UA_QUALIFIEDNAME(0, dsrName), UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETREADERTYPE), object_attr, NULL, &dataSetReader->identifier); /* Add childNodes such as PublisherId, WriterGroupId and DataSetWriterId in * DataSetReader object */ publisherIdNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublisherId"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), dataSetReader->identifier); writerGroupIdNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "WriterGroupId"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), dataSetReader->identifier); dataSetwriterIdNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "DataSetWriterId"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), dataSetReader->identifier); if(UA_NodeId_isNull(&publisherIdNode) || UA_NodeId_isNull(&writerGroupIdNode) || UA_NodeId_isNull(&dataSetwriterIdNode)) { return UA_STATUSCODE_BADNOTFOUND; } UA_NodePropertyContext *dataSetReaderPublisherIdContext = (UA_NodePropertyContext *) UA_malloc(sizeof(UA_NodePropertyContext)); dataSetReaderPublisherIdContext->parentNodeId = dataSetReader->identifier; dataSetReaderPublisherIdContext->parentClassifier = UA_NS0ID_DATASETREADERTYPE; dataSetReaderPublisherIdContext->elementClassiefier = UA_NS0ID_DATASETREADERTYPE_PUBLISHERID; UA_ValueCallback valueCallback; valueCallback.onRead = onRead; valueCallback.onWrite = NULL; retVal |= addVariableValueSource(server, valueCallback, publisherIdNode, dataSetReaderPublisherIdContext); /* Update childNode with values from Publisher */ UA_Variant value; UA_Variant_init(&value); UA_Variant_setScalar(&value, &dataSetReader->config.writerGroupId, &UA_TYPES[UA_TYPES_UINT16]); UA_Server_writeValue(server, writerGroupIdNode, value); UA_Variant_setScalar(&value, &dataSetReader->config.dataSetWriterId, &UA_TYPES[UA_TYPES_UINT16]); UA_Server_writeValue(server, dataSetwriterIdNode, value); return retVal; } #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_GOOD; UA_ReaderGroup *rg = UA_ReaderGroup_findRGbyId(server, *objectId); if(rg->configurationFrozen) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "addDataSetReader cannot be done because ReaderGroup config frozen"); return UA_STATUSCODE_BAD; } UA_NodeId dataSetReaderId; UA_DataSetReaderDataType *dataSetReaderDataType = (UA_DataSetReaderDataType *) input[0].data; retVal |= addDataSetReaderConfig(server, *objectId, dataSetReaderDataType, &dataSetReaderId); if(retVal != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "addDataSetReader failed"); return retVal; } UA_Variant_setScalarCopy(output, &dataSetReaderId, &UA_TYPES[UA_TYPES_NODEID]); return retVal; } #endif UA_StatusCode removeDataSetReaderRepresentation(UA_Server *server, UA_DataSetReader* dataSetReader) { return UA_Server_deleteNode(server, dataSetReader->identifier, true); } #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_NodeId nodeToRemove = *((UA_NodeId *)input[0].data); return UA_Server_removeDataSetReader(server, nodeToRemove); } #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(""), 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_NodeId nodeToRemove = *((UA_NodeId *) input[0].data); return UA_Server_deleteNode(server, nodeToRemove, true); } #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; char pdsName[513]; memcpy(pdsName, publishedDataSet->config.name.data, publishedDataSet->config.name.length); pdsName[publishedDataSet->config.name.length] = '\0'; UA_ObjectAttributes object_attr = UA_ObjectAttributes_default; object_attr.displayName = UA_LOCALIZEDTEXT("", pdsName); retVal = UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 0), /* Create a new id */ UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE_PUBLISHEDDATASETS), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(0, pdsName), UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHEDDATAITEMSTYPE), object_attr, NULL, &publishedDataSet->identifier); UA_CHECK_STATUS(retVal, return retVal); UA_ValueCallback valueCallback; valueCallback.onRead = onRead; valueCallback.onWrite = NULL; //ToDo: Need to move the browse name from namespaceindex 0 to 1 UA_NodeId configurationVersionNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "ConfigurationVersion"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), publishedDataSet->identifier); if(UA_NodeId_isNull(&configurationVersionNode)) 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), publishedDataSet->identifier); if(UA_NodeId_isNull(&publishedDataNode)) 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), publishedDataSet->identifier); if(UA_NodeId_isNull(&dataSetMetaDataNode)) 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 *variablesToAdd = NULL; UA_ExtensionObject *eoAddVar = NULL; if(input[3].type == &UA_TYPES[UA_TYPES_PUBLISHEDVARIABLEDATATYPE]) variablesToAdd = (UA_PublishedVariableDataType*)input[3].data; else if(input[3].type == &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]) eoAddVar = (UA_ExtensionObject *)input[3].data; else return UA_STATUSCODE_BADINTERNALERROR; 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; if(retVal != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "addPublishedDataset failed"); return retVal; } UA_DataSetFieldConfig dataSetFieldConfig; for(size_t j = 0; j < variablesToAddSize; ++j) { /* Prepare the config */ memset(&dataSetFieldConfig, 0, sizeof(UA_DataSetFieldConfig)); dataSetFieldConfig.dataSetFieldType = UA_PUBSUB_DATASETFIELD_VARIABLE; dataSetFieldConfig.field.variable.fieldNameAlias = fieldNameAliases[j]; if(fieldFlags[j] == UA_DATASETFIELDFLAGS_PROMOTEDFIELD) dataSetFieldConfig.field.variable.promotedField = true; UA_PublishedVariableDataType *variableToAdd; if(variablesToAdd) { variableToAdd = &variablesToAdd[j]; } else { if(eoAddVar[j].content.decoded.type != &UA_TYPES[UA_TYPES_PUBLISHEDVARIABLEDATATYPE]) return UA_STATUSCODE_BADINTERNALERROR; variableToAdd = (UA_PublishedVariableDataType*) eoAddVar[j].content.decoded.data; } dataSetFieldConfig.field.variable.publishParameters = *variableToAdd; /* Add the dataset field */ retVal |= UA_Server_addDataSetField(server, dataSetItemsNodeId, &dataSetFieldConfig, NULL).result; if(retVal != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "addDataSetField failed"); return retVal; } } UA_Variant_setScalarCopy(output, &dataSetItemsNodeId, &UA_TYPES[UA_TYPES_NODEID]); 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){ return UA_STATUSCODE_GOOD; } 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){ return UA_STATUSCODE_GOOD; } #endif UA_StatusCode removePublishedDataSetRepresentation(UA_Server *server, UA_PublishedDataSet *publishedDataSet) { return UA_Server_deleteNode(server, publishedDataSet->identifier, true); } #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_NodeId nodeToRemove = *((UA_NodeId *) input[0].data); return UA_Server_removePublishedDataSet(server, nodeToRemove); } #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; char wgName[513]; memcpy(wgName, writerGroup->config.name.data, writerGroup->config.name.length); wgName[writerGroup->config.name.length] = '\0'; UA_ObjectAttributes object_attr = UA_ObjectAttributes_default; object_attr.displayName = UA_LOCALIZEDTEXT("", wgName); retVal = UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 0), /* create a new id */ writerGroup->linkedConnection->identifier, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(0, wgName), UA_NODEID_NUMERIC(0, UA_NS0ID_WRITERGROUPTYPE), object_attr, NULL, &writerGroup->identifier); UA_NodeId keepAliveNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "KeepAliveTime"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), writerGroup->identifier); UA_NodeId publishingIntervalNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublishingInterval"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), writerGroup->identifier); if(UA_NodeId_isNull(&keepAliveNode) || UA_NodeId_isNull(&publishingIntervalNode)) 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), writerGroup->identifier); UA_NodeId writerGroupIdNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "WriterGroupId"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), writerGroup->identifier); 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); object_attr.displayName = UA_LOCALIZEDTEXT("", "MessageSettings"); retVal |= UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 0), writerGroup->identifier, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(0, "MessageSettings"), UA_NODEID_NUMERIC(0, UA_NS0ID_UADPWRITERGROUPMESSAGETYPE), object_attr, NULL, NULL); /* Find the variable with the content mask */ UA_NodeId messageSettingsId = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "MessageSettings"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), writerGroup->identifier); UA_NodeId contentMaskId = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "NetworkMessageContentMask"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), messageSettingsId); if(!UA_NodeId_isNull(&contentMaskId)) { /* 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); } /* Add reference to methods */ #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS retVal |= UA_Server_addReference(server, writerGroup->identifier, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_WRITERGROUPTYPE_ADDDATASETWRITER), true); retVal |= UA_Server_addReference(server, writerGroup->identifier, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_WRITERGROUPTYPE_REMOVEDATASETWRITER), true); #endif 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 writerGroupId; retVal |= addWriterGroupConfig(server, *objectId, writerGroupDataType, &writerGroupId); if(retVal != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "addWriterGroup failed"); return retVal; } //TODO: Need to handle the UA_Server_setWriterGroupOperational based on the status variable in information model UA_Variant_setScalarCopy(output, &writerGroupId, &UA_TYPES[UA_TYPES_NODEID]); return retVal; } #endif UA_StatusCode removeGroupRepresentation(UA_Server *server, UA_WriterGroup *writerGroup) { return UA_Server_deleteNode(server, writerGroup->identifier, true); } UA_StatusCode removeReaderGroupRepresentation(UA_Server *server, UA_ReaderGroup *readerGroup) { return UA_Server_deleteNode(server, readerGroup->identifier, true); } #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_NodeId nodeToRemove = *((UA_NodeId *)input->data); if(UA_WriterGroup_findWGbyId(server, nodeToRemove)) { UA_WriterGroup *wg = UA_WriterGroup_findWGbyId(server, nodeToRemove); if(wg->configurationFrozen) UA_Server_unfreezeWriterGroupConfiguration(server, nodeToRemove); return UA_Server_removeWriterGroup(server, nodeToRemove); } else { UA_ReaderGroup *rg = UA_ReaderGroup_findRGbyId(server, nodeToRemove); if(rg->configurationFrozen) UA_Server_unfreezeReaderGroupConfiguration(server, nodeToRemove); return UA_Server_removeReaderGroup(server, nodeToRemove); } } #endif /**********************************************/ /* ReaderGroup */ /**********************************************/ UA_StatusCode addReaderGroupRepresentation(UA_Server *server, UA_ReaderGroup *readerGroup) { if(readerGroup->config.name.length > 512) return UA_STATUSCODE_BADCONFIGURATIONERROR; char rgName[513]; memcpy(rgName, readerGroup->config.name.data, readerGroup->config.name.length); rgName[readerGroup->config.name.length] = '\0'; UA_ObjectAttributes object_attr = UA_ObjectAttributes_default; object_attr.displayName = UA_LOCALIZEDTEXT("", rgName); UA_StatusCode retVal = UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 0), /* create an id */ readerGroup->linkedConnection, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(0, rgName), UA_NODEID_NUMERIC(0, UA_NS0ID_READERGROUPTYPE), object_attr, NULL, &readerGroup->identifier); #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS retVal |= UA_Server_addReference(server, readerGroup->identifier, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_READERGROUPTYPE_ADDDATASETREADER), true); retVal |= UA_Server_addReference(server, readerGroup->identifier, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_READERGROUPTYPE_REMOVEDATASETREADER), true); #endif return retVal; } #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; UA_ReaderGroupDataType *readerGroupDataType = ((UA_ReaderGroupDataType *) input->data); UA_NodeId readerGroupId; retVal |= addReaderGroupConfig(server, *objectId, readerGroupDataType, &readerGroupId); if(retVal != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "addReaderGroup failed"); return retVal; } //TODO: Need to handle the UA_Server_setReaderGroupOperational based on the status variable in information model UA_Variant_setScalarCopy(output, &readerGroupId, &UA_TYPES[UA_TYPES_NODEID]); 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; char dswName[513]; memcpy(dswName, dataSetWriter->config.name.data, dataSetWriter->config.name.length); dswName[dataSetWriter->config.name.length] = '\0'; UA_ObjectAttributes object_attr = UA_ObjectAttributes_default; object_attr.displayName = UA_LOCALIZEDTEXT("", dswName); retVal = UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 0), /* create an id */ dataSetWriter->linkedWriterGroup, UA_NODEID_NUMERIC(0, UA_NS0ID_HASDATASETWRITER), UA_QUALIFIEDNAME(0, dswName), UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETWRITERTYPE), object_attr, NULL, &dataSetWriter->identifier); retVal |= UA_Server_addReference(server, dataSetWriter->connectedDataSet, UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETTOWRITER), UA_EXPANDEDNODEID_NODEID(dataSetWriter->identifier), true); UA_NodeId dataSetWriterIdNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "DataSetWriterId"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), dataSetWriter->identifier); UA_NodeId keyFrameNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "KeyFrameCount"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), dataSetWriter->identifier); UA_NodeId dataSetFieldContentMaskNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "DataSetFieldContentMask"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), dataSetWriter->identifier); UA_NodePropertyContext *dataSetWriterIdContext = (UA_NodePropertyContext *) UA_malloc(sizeof(UA_NodePropertyContext)); dataSetWriterIdContext->parentNodeId = dataSetWriter->identifier; dataSetWriterIdContext->parentClassifier = UA_NS0ID_DATASETWRITERTYPE; dataSetWriterIdContext->elementClassiefier = UA_NS0ID_DATASETWRITERTYPE_DATASETWRITERID; UA_ValueCallback valueCallback; valueCallback.onRead = onRead; valueCallback.onWrite = NULL; retVal |= addVariableValueSource(server, valueCallback, dataSetWriterIdNode, dataSetWriterIdContext); UA_Variant value; UA_Variant_init(&value); UA_Variant_setScalar(&value, &dataSetWriter->config.dataSetWriterId, &UA_TYPES[UA_TYPES_UINT16]); UA_Server_writeValue(server, dataSetWriterIdNode, value); UA_Variant_setScalar(&value, &dataSetWriter->config.keyFrameCount, &UA_TYPES[UA_TYPES_UINT32]); UA_Server_writeValue(server, keyFrameNode, value); UA_Variant_setScalar(&value, &dataSetWriter->config.dataSetFieldContentMask, &UA_TYPES[UA_TYPES_DATASETFIELDCONTENTMASK]); UA_Server_writeValue(server, dataSetFieldContentMaskNode, value); object_attr.displayName = UA_LOCALIZEDTEXT("", "MessageSettings"); retVal |= UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 0), dataSetWriter->identifier, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(0, "MessageSettings"), UA_NODEID_NUMERIC(0, UA_NS0ID_UADPDATASETWRITERMESSAGETYPE), object_attr, NULL, NULL); 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_StatusCode retVal = UA_STATUSCODE_GOOD; UA_WriterGroup *wg = UA_WriterGroup_findWGbyId(server, *objectId); if(!wg) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Not a WriterGroup"); return UA_STATUSCODE_BAD; } if(wg->configurationFrozen) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "addDataSetWriter cannot be done because writergroup config frozen"); return UA_STATUSCODE_BAD; } UA_NodeId dataSetWriterId; UA_DataSetWriterDataType *dataSetWriterData = (UA_DataSetWriterDataType *)input->data; retVal |= addDataSetWriterConfig(server, objectId, dataSetWriterData, &dataSetWriterId); if(retVal != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "addDataSetWriter failed"); return retVal; } UA_Variant_setScalarCopy(output, &dataSetWriterId, &UA_TYPES[UA_TYPES_NODEID]); return UA_STATUSCODE_GOOD; } #endif UA_StatusCode removeDataSetWriterRepresentation(UA_Server *server, UA_DataSetWriter *dataSetWriter) { return UA_Server_deleteNode(server, dataSetWriter->identifier, true); } #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_NodeId nodeToRemove = *((UA_NodeId *) input[0].data); return UA_Server_removeDataSetWriter(server, nodeToRemove); } #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 = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublisherId"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), *nodeId); UA_NodePropertyContext *ctx; UA_Server_getNodeContext(server, publisherIdNode, (void **)&ctx); if(!UA_NodeId_isNull(&publisherIdNode)) UA_free(ctx); } 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 = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublishingInterval"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), *nodeId); UA_NodePropertyContext *ctx; UA_Server_getNodeContext(server, intervalNode, (void **)&ctx); if(!UA_NodeId_isNull(&intervalNode)) UA_free(ctx); } 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!"); UA_NodeId dataSetWriterIdNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "DataSetWriterId"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), *nodeId); UA_NodePropertyContext *ctx; UA_Server_getNodeContext(server, dataSetWriterIdNode, (void **)&ctx); if(!UA_NodeId_isNull(&dataSetWriterIdNode)) UA_free(ctx); } 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!"); /* Deallocate the memory allocated for publisherId */ UA_NodeId publisherIdNode = findSingleChildNode(server, UA_QUALIFIEDNAME(0, "PublisherId"), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), *nodeId); UA_NodePropertyContext *ctx; UA_Server_getNodeContext(server, publisherIdNode, (void **)&ctx); if(!UA_NodeId_isNull(&publisherIdNode)) UA_free(ctx); } 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_isNull(&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_isNull(&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_isNull(&node)) UA_free(childContext); } /*************************************/ /* PubSub configurator */ /*************************************/ #if defined(UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS) && defined(UA_ENABLE_PUBSUB_FILE_CONFIG) /* Callback function that will be executed when the method "PubSub configurator * (replace config)" is called. */ static UA_StatusCode UA_loadPubSubConfigMethodCallback(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) { if(inputSize == 1) { UA_ByteString *inputStr = (UA_ByteString*)input->data; return UA_PubSubManager_loadPubSubConfigFromByteString(server, *inputStr); } else if(inputSize > 1) { return UA_STATUSCODE_BADTOOMANYARGUMENTS; } else { return UA_STATUSCODE_BADARGUMENTSMISSING; } } /* Adds method node to server. This method is used to load binary files for * PubSub configuration and delete / replace old PubSub configurations. */ static UA_StatusCode UA_addLoadPubSubConfigMethod(UA_Server *server) { UA_Argument inputArgument; UA_Argument_init(&inputArgument); inputArgument.description = UA_LOCALIZEDTEXT("", "PubSub config binfile"); inputArgument.name = UA_STRING("BinFile"); inputArgument.dataType = UA_TYPES[UA_TYPES_BYTESTRING].typeId; inputArgument.valueRank = UA_VALUERANK_SCALAR; UA_MethodAttributes configAttr = UA_MethodAttributes_default; configAttr.description = UA_LOCALIZEDTEXT("","Load binary configuration file"); configAttr.displayName = UA_LOCALIZEDTEXT("","LoadPubSubConfigurationFile"); configAttr.executable = true; configAttr.userExecutable = true; return UA_Server_addMethodNode(server, UA_NODEID_NULL, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE), UA_NODEID_NUMERIC(0, UA_NS0ID_HASORDEREDCOMPONENT), UA_QUALIFIEDNAME(1, "PubSub configuration"), configAttr, &UA_loadPubSubConfigMethodCallback, 1, &inputArgument, 0, NULL, NULL, NULL); } /* Callback function that will be executed when the method "PubSub configurator * (delete config)" is called. */ static UA_StatusCode UA_deletePubSubConfigMethodCallback(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_PubSubManager_delete(server, &(server->pubSubManager)); return UA_STATUSCODE_GOOD; } /* Adds method node to server. This method is used to delete the current PubSub * configuration. */ static UA_StatusCode UA_addDeletePubSubConfigMethod(UA_Server *server) { UA_MethodAttributes configAttr = UA_MethodAttributes_default; configAttr.description = UA_LOCALIZEDTEXT("","Delete current PubSub configuration"); configAttr.displayName = UA_LOCALIZEDTEXT("","DeletePubSubConfiguration"); configAttr.executable = true; configAttr.userExecutable = true; return UA_Server_addMethodNode(server, UA_NODEID_NULL, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE), UA_NODEID_NUMERIC(0, UA_NS0ID_HASORDEREDCOMPONENT), UA_QUALIFIEDNAME(1, "Delete PubSub config"), configAttr, &UA_deletePubSubConfigMethodCallback, 0, NULL, 0, NULL, NULL, NULL); } #endif /* defined(UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS) && defined(UA_ENABLE_PUBSUB_FILE_CONFIG) */ 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 /* Add missing references */ 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); /* Set method callbacks */ retVal |= UA_Server_setMethodNodeCallback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE_ADDCONNECTION), addPubSubConnectionAction); retVal |= UA_Server_setMethodNodeCallback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE_REMOVECONNECTION), removeConnectionAction); retVal |= UA_Server_setMethodNodeCallback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_ADDDATASETFOLDER), addDataSetFolderAction); retVal |= UA_Server_setMethodNodeCallback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_REMOVEDATASETFOLDER), removeDataSetFolderAction); retVal |= UA_Server_setMethodNodeCallback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_ADDPUBLISHEDDATAITEMS), addPublishedDataItemsAction); retVal |= UA_Server_setMethodNodeCallback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETFOLDERTYPE_REMOVEPUBLISHEDDATASET), removePublishedDataSetAction); retVal |= UA_Server_setMethodNodeCallback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHEDDATAITEMSTYPE_ADDVARIABLES), addVariablesAction); retVal |= UA_Server_setMethodNodeCallback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHEDDATAITEMSTYPE_REMOVEVARIABLES), removeVariablesAction); retVal |= UA_Server_setMethodNodeCallback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE_ADDWRITERGROUP), addWriterGroupAction); retVal |= UA_Server_setMethodNodeCallback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE_ADDREADERGROUP), addReaderGroupAction); retVal |= UA_Server_setMethodNodeCallback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE_REMOVEGROUP), removeGroupAction); retVal |= UA_Server_setMethodNodeCallback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_WRITERGROUPTYPE_ADDDATASETWRITER), addDataSetWriterAction); retVal |= UA_Server_setMethodNodeCallback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_WRITERGROUPTYPE_REMOVEDATASETWRITER), removeDataSetWriterAction); retVal |= UA_Server_setMethodNodeCallback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_READERGROUPTYPE_ADDDATASETREADER), addDataSetReaderAction); retVal |= UA_Server_setMethodNodeCallback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_READERGROUPTYPE_REMOVEDATASETREADER), removeDataSetReaderAction); #ifdef UA_ENABLE_PUBSUB_FILE_CONFIG retVal |= UA_addLoadPubSubConfigMethod(server); retVal |= UA_addDeletePubSubConfigMethod(server); #endif #else /* Remove methods */ 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 /* Set the object-type destructors */ UA_NodeTypeLifecycle lifeCycle; lifeCycle.constructor = NULL; lifeCycle.destructor = connectionTypeDestructor; retVal |= UA_Server_setNodeTypeLifecycle(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBSUBCONNECTIONTYPE), lifeCycle); lifeCycle.destructor = writerGroupTypeDestructor; retVal |= UA_Server_setNodeTypeLifecycle(server, UA_NODEID_NUMERIC(0, UA_NS0ID_WRITERGROUPTYPE), lifeCycle); lifeCycle.destructor = readerGroupTypeDestructor; retVal |= UA_Server_setNodeTypeLifecycle(server, UA_NODEID_NUMERIC(0, UA_NS0ID_READERGROUPTYPE), lifeCycle); lifeCycle.destructor = dataSetWriterTypeDestructor; retVal |= UA_Server_setNodeTypeLifecycle(server, UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETWRITERTYPE), lifeCycle); lifeCycle.destructor = publishedDataItemsTypeDestructor; retVal |= UA_Server_setNodeTypeLifecycle(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHEDDATAITEMSTYPE), lifeCycle); lifeCycle.destructor = dataSetReaderTypeDestructor; retVal |= UA_Server_setNodeTypeLifecycle(server, UA_NODEID_NUMERIC(0, UA_NS0ID_DATASETREADERTYPE), lifeCycle); return retVal; } #endif /* UA_ENABLE_PUBSUB_INFORMATIONMODEL */ /**** amalgamated original file "/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 */ #define UA_MAX_TREE_RECURSE 50 /* How deep up/down the tree do we recurse at most? */ UA_StatusCode referenceTypeIndices(UA_Server *server, const UA_NodeId *refType, UA_ReferenceTypeSet *indices, UA_Boolean includeSubtypes) { if(UA_NodeId_isNull(refType)) { UA_ReferenceTypeSet_any(indices); return UA_STATUSCODE_GOOD; } UA_ReferenceTypeSet_init(indices); const UA_Node *refNode = UA_NODESTORE_GET(server, refType); if(!refNode) return UA_STATUSCODE_BADREFERENCETYPEIDINVALID; if(refNode->head.nodeClass != UA_NODECLASS_REFERENCETYPE) { UA_NODESTORE_RELEASE(server, refNode); return UA_STATUSCODE_BADREFERENCETYPEIDINVALID; } if(!includeSubtypes) *indices = UA_REFTYPESET(refNode->referenceTypeNode.referenceTypeIndex); else *indices = refNode->referenceTypeNode.subTypes; UA_NODESTORE_RELEASE(server, refNode); return UA_STATUSCODE_GOOD; } static UA_Boolean matchClassMask(const UA_Node *node, UA_UInt32 nodeClassMask) { if(nodeClassMask != UA_NODECLASS_UNSPECIFIED && (node->head.nodeClass & nodeClassMask) == 0) return false; return true; } /****************/ /* IsNodeInTree */ /****************/ /* Internal method to check if a node is already upwards from a leaf node */ /* Keeps track of visited nodes to detect circular references */ struct ref_history { struct ref_history *parent; /* the previous element */ const UA_NodePointer id; /* the id of the node at this depth */ UA_UInt16 depth; }; static UA_Boolean isNodeInTreeNoCircular(UA_Server *server, UA_NodePointer leafNode, UA_NodePointer nodeToFind, struct ref_history *visitedRefs, const UA_ReferenceTypeSet *relevantRefs) { if(UA_NodePointer_equal(nodeToFind, leafNode)) return true; if(visitedRefs->depth >= UA_MAX_TREE_RECURSE) return false; const UA_Node *node = UA_NODESTORE_GETFROMREF(server, leafNode); if(!node) return false; for(size_t i = 0; i < node->head.referencesSize; ++i) { UA_NodeReferenceKind *rk = &node->head.references[i]; /* Search upwards in the tree */ if(!rk->isInverse) continue; /* Consider only the indicated reference types */ if(!UA_ReferenceTypeSet_contains(relevantRefs, rk->referenceTypeIndex)) continue; /* Match the targets or recurse */ const UA_ReferenceTarget *t = NULL; while((t = UA_NodeReferenceKind_iterate(rk, t))) { /* Don't follow remote targets */ if(!UA_NodePointer_isLocal(t->targetId)) continue; /* 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(last) { if(UA_NodePointer_equal(last->id, t->targetId)) { skip = true; break; } last = last->parent; } if(skip) continue; } /* Stack-allocate the visitedRefs structure for the next depth */ struct ref_history nextVisitedRefs = {visitedRefs, t->targetId, (UA_UInt16)(visitedRefs->depth+1)}; /* Recurse */ UA_Boolean foundRecursive = isNodeInTreeNoCircular(server, t->targetId, nodeToFind, &nextVisitedRefs, relevantRefs); if(foundRecursive) { UA_NODESTORE_RELEASE(server, node); return true; } } } UA_NODESTORE_RELEASE(server, node); return false; } UA_Boolean isNodeInTree(UA_Server *server, const UA_NodeId *leafNode, const UA_NodeId *nodeToFind, const UA_ReferenceTypeSet *relevantRefs) { UA_NodePointer leafP = UA_NodePointer_fromNodeId(leafNode); UA_NodePointer targetP = UA_NodePointer_fromNodeId(nodeToFind); struct ref_history visitedRefs = {NULL, leafP, 0}; return isNodeInTreeNoCircular(server, leafP, targetP, &visitedRefs, relevantRefs); } UA_Boolean isNodeInTree_singleRef(UA_Server *server, const UA_NodeId *leafNode, const UA_NodeId *nodeToFind, const UA_Byte relevantRefTypeIndex) { UA_ReferenceTypeSet reftypes = UA_REFTYPESET(relevantRefTypeIndex); return isNodeInTree(server, leafNode, nodeToFind, &reftypes); } 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_FUNCTIONS(RefHead, RefEntry, zipfields, RefEntry, zipfields, cmpTarget) UA_StatusCode RefTree_init(RefTree *rt) { rt->size = 0; rt->capacity = 0; ZIP_INIT(&rt->head); size_t space = (sizeof(UA_ExpandedNodeId) + sizeof(RefEntry)) * UA_REFTREE_INITIAL_SIZE; rt->targets = (UA_ExpandedNodeId*)UA_malloc(space); if(!rt->targets) return UA_STATUSCODE_BADOUTOFMEMORY; rt->capacity = UA_REFTREE_INITIAL_SIZE; return UA_STATUSCODE_GOOD; } void RefTree_clear(RefTree *rt) { for(size_t i = 0; i < rt->size; i++) UA_ExpandedNodeId_clear(&rt->targets[i]); if(rt->targets) 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 */ // What is this sorcery? // FIXME: This needs some cleaning up or explanation. // IMO uintptr could be completely avoided here. 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++) { uintptr_t *left = (uintptr_t*)&ZIP_LEFT(&reArray[i], zipfields); uintptr_t *right = (uintptr_t*)&ZIP_RIGHT(&reArray[i], zipfields); if(*left != 0) *left += entrydiff; if(*right != 0) *right += entrydiff; reArray[i].target = (UA_ExpandedNodeId*)((uintptr_t)reArray[i].target + arraydiff); } ZIP_ROOT(&rt->head) = (RefEntry*)((uintptr_t)ZIP_ROOT(&rt->head) + entrydiff); rt->capacity = capacity; rt->targets = newTargets; return UA_STATUSCODE_GOOD; } static UA_StatusCode RefTree_add(RefTree *rt, UA_NodePointer target, UA_Boolean *duplicate) { UA_ExpandedNodeId en = UA_NodePointer_toExpandedNodeId(target); /* Is the target already in the tree? */ RefEntry dummy; memset(&dummy, 0, sizeof(RefEntry)); dummy.target = &en; dummy.targetHash = UA_ExpandedNodeId_hash(&en); if(ZIP_FIND(RefHead, &rt->head, &dummy)) { if(duplicate) *duplicate = true; return UA_STATUSCODE_GOOD; } 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(&en, &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 = dummy.targetHash; ZIP_INSERT(RefHead, &rt->head, re, UA_UInt32_random()); rt->size++; return UA_STATUSCODE_GOOD; } UA_StatusCode RefTree_addNodeId(RefTree *rt, const UA_NodeId *target, UA_Boolean *duplicate) { return RefTree_add(rt, UA_NodePointer_fromNodeId(target), duplicate); } UA_Boolean RefTree_contains(RefTree *rt, const UA_ExpandedNodeId *target) { RefEntry dummy; dummy.target = target; dummy.targetHash = UA_ExpandedNodeId_hash(target); return !!ZIP_FIND(RefHead, &rt->head, &dummy); } UA_Boolean RefTree_containsNodeId(RefTree *rt, const UA_NodeId *target) { UA_ExpandedNodeId en; en.nodeId = *target; en.namespaceUri = UA_STRING_NULL; en.serverIndex = 0; return RefTree_contains(rt, &en); } /********************/ /* Browse Recursive */ /********************/ static UA_StatusCode browseRecursiveInner(UA_Server *server, RefTree *rt, UA_UInt16 depth, UA_Boolean skip, UA_NodePointer nodeP, UA_BrowseDirection browseDirection, const UA_ReferenceTypeSet *refTypes, UA_UInt32 nodeClassMask) { /* Have we reached the max recursion depth? */ if(depth >= UA_MAX_TREE_RECURSE) return UA_STATUSCODE_GOOD; const UA_Node *node = UA_NODESTORE_GETFROMREF(server, nodeP); if(!node) return UA_STATUSCODE_BADNODEIDUNKNOWN; UA_StatusCode retval = UA_STATUSCODE_GOOD; const UA_NodeHead *head = &node->head; /* Add the current node to the results if we don't want to skip it (i.e. for * includeStartNodes) and it matches the nodeClassMask filter. Process the * children also if the nodeClassMask does not match. */ if(!skip && matchClassMask(node, nodeClassMask)) { UA_Boolean duplicate = false; retval = RefTree_addNodeId(rt, &head->nodeId, &duplicate); if(duplicate || retval != UA_STATUSCODE_GOOD) goto cleanup; } for(size_t i = 0; i < head->referencesSize; i++) { UA_NodeReferenceKind *rk = &head->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(!UA_ReferenceTypeSet_contains(refTypes, rk->referenceTypeIndex)) continue; const UA_ReferenceTarget *target = NULL; while((target = UA_NodeReferenceKind_iterate(rk, target))) { if(UA_NodePointer_isLocal(target->targetId)) { retval = browseRecursiveInner(server, rt, (UA_UInt16)(depth+1), false, target->targetId, browseDirection, refTypes, nodeClassMask); } else { retval = RefTree_add(rt, target->targetId, NULL); } if(retval != UA_STATUSCODE_GOOD) goto cleanup; } } cleanup: UA_NODESTORE_RELEASE(server, node); return retval; } UA_StatusCode browseRecursive(UA_Server *server, size_t startNodesSize, const UA_NodeId *startNodes, UA_BrowseDirection browseDirection, const UA_ReferenceTypeSet *refTypes, UA_UInt32 nodeClassMask, UA_Boolean includeStartNodes, size_t *resultsSize, UA_ExpandedNodeId **results) { RefTree rt; UA_StatusCode retval = RefTree_init(&rt); if(retval != UA_STATUSCODE_GOOD) return retval; for(size_t i = 0; i < startNodesSize; i++) { /* Call the inner recursive browse separately for the search direction. * Otherwise we might take one step up and another step down in the * search tree. */ if(browseDirection == UA_BROWSEDIRECTION_FORWARD || browseDirection == UA_BROWSEDIRECTION_BOTH) retval |= browseRecursiveInner(server, &rt, 0, !includeStartNodes, UA_NodePointer_fromNodeId(&startNodes[i]), UA_BROWSEDIRECTION_FORWARD, refTypes, nodeClassMask); if(browseDirection == UA_BROWSEDIRECTION_INVERSE || browseDirection == UA_BROWSEDIRECTION_BOTH) retval |= browseRecursiveInner(server, &rt, 0, !includeStartNodes, UA_NodePointer_fromNodeId(&startNodes[i]), UA_BROWSEDIRECTION_INVERSE, refTypes, nodeClassMask); if(retval != UA_STATUSCODE_GOOD) break; } if(rt.size > 0 && retval == UA_STATUSCODE_GOOD) { *results = rt.targets; *resultsSize = rt.size; } else { RefTree_clear(&rt); } return retval; } UA_StatusCode UA_Server_browseRecursive(UA_Server *server, const UA_BrowseDescription *bd, size_t *resultsSize, UA_ExpandedNodeId **results) { UA_LOCK(&server->serviceMutex); /* Set the list of relevant reference types */ UA_ReferenceTypeSet refTypes; UA_StatusCode retval = referenceTypeIndices(server, &bd->referenceTypeId, &refTypes, bd->includeSubtypes); if(retval != UA_STATUSCODE_GOOD) { UA_UNLOCK(&server->serviceMutex); return retval; } /* Browse */ retval = browseRecursive(server, 1, &bd->nodeId, bd->browseDirection, &refTypes, bd->nodeClassMask, false, resultsSize, results); UA_UNLOCK(&server->serviceMutex); 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_REFTREE_INITIAL_SIZE, &UA_TYPES[UA_TYPES_REFERENCEDESCRIPTION]); if(!rr->descr) return UA_STATUSCODE_BADOUTOFMEMORY; rr->capacity = UA_REFTREE_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; /* Parameters of the Browse Request */ UA_BrowseDescription browseDescription; UA_UInt32 maxReferences; UA_ReferenceTypeSet relevantReferences; /* The next target to be transmitted to the client */ UA_ExpandedNodeId nextTarget; UA_Byte nextRefKindIndex; }; ContinuationPoint * ContinuationPoint_clear(ContinuationPoint *cp) { UA_ByteString_clear(&cp->identifier); UA_BrowseDescription_clear(&cp->browseDescription); UA_ExpandedNodeId_clear(&cp->nextTarget); 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, UA_NodePointer nodeP, 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 */ UA_ExpandedNodeId en = UA_NodePointer_toExpandedNodeId(nodeP); retval = UA_ExpandedNodeId_copy(&en, &descr->nodeId); if(mask & UA_BROWSERESULTMASK_REFERENCETYPEID) { const UA_NodeId *refTypeId = UA_NODESTORE_GETREFERENCETYPEID(server, ref->referenceTypeIndex); retval |= UA_NodeId_copy(refTypeId, &descr->referenceTypeId); } if(mask & UA_BROWSERESULTMASK_ISFORWARD) descr->isForward = !ref->isInverse; /* Remote references (ExpandedNodeId) are not further looked up here */ if(!curr) { UA_ReferenceDescription_clear(descr); return retval; } /* Fields that require the actual node */ if(mask & UA_BROWSERESULTMASK_NODECLASS) descr->nodeClass = curr->head.nodeClass; if(mask & UA_BROWSERESULTMASK_BROWSENAME) retval |= UA_QualifiedName_copy(&curr->head.browseName, &descr->browseName); if(mask & UA_BROWSERESULTMASK_DISPLAYNAME) retval |= UA_LocalizedText_copy(&curr->head.displayName, &descr->displayName); if(mask & UA_BROWSERESULTMASK_TYPEDEFINITION) { if(curr->head.nodeClass == UA_NODECLASS_OBJECT || curr->head.nodeClass == UA_NODECLASS_VARIABLE) { const UA_Node *type = getNodeType(server, &curr->head); if(type) { retval |= UA_NodeId_copy(&type->head.nodeId, &descr->typeDefinition.nodeId); UA_NODESTORE_RELEASE(server, type); } } } if(retval == UA_STATUSCODE_GOOD) rr->size++; /* Increase the counter */ else UA_ReferenceDescription_clear(descr); return retval; } /* Returns whether the node / continuationpoint is done */ static UA_StatusCode browseReferences(UA_Server *server, const UA_NodeHead *head, ContinuationPoint *cp, RefResult *rr, UA_Boolean *done) { UA_assert(cp); const UA_BrowseDescription *bd = &cp->browseDescription; size_t i = 0; const UA_ReferenceTarget *ref = NULL; /* If the cp was previously used, skip forward to the next ReferenceType to * be transmitted. */ if(cp->identifier.length > 0) { for(; i < head->referencesSize; ++i) { UA_NodeReferenceKind *rk = &head->references[i]; /* Was this the last transmitted ReferenceType? */ if(head->references[i].referenceTypeIndex != cp->nextRefKindIndex) continue; /* Reference in the right direction? */ if(rk->isInverse && bd->browseDirection == UA_BROWSEDIRECTION_FORWARD) continue; if(!rk->isInverse && bd->browseDirection == UA_BROWSEDIRECTION_INVERSE) continue; /* Get the next target */ ref = UA_NodeReferenceKind_findTarget(rk, &cp->nextTarget); if(ref) break; /* The target no longer exists for this ReferenceType (and * direction). Continue to iterate for the case that a nodestore has * a duplicate UA_NodeReferenceKind (should not happen though). */ } /* Fail with an error if the reference no longer exists. */ if(!ref) return UA_STATUSCODE_BADINTERNALERROR; } /* Loop over the ReferenceTypes */ UA_StatusCode retval = UA_STATUSCODE_GOOD; for(; i < head->referencesSize; ++i) { UA_NodeReferenceKind *rk = &head->references[i]; /* 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(!UA_ReferenceTypeSet_contains(&cp->relevantReferences, rk->referenceTypeIndex)) continue; /* We have a matching ReferenceType! */ /* Loop over the targets for the ReferenceType. Start with the first * entry if we don't have a known entry-point from the cp. */ if(!ref) ref = UA_NodeReferenceKind_iterate(rk, ref); for(;ref; ref = UA_NodeReferenceKind_iterate(rk, ref)) { /* Get the node if it is not a remote reference */ const UA_Node *target = UA_NODESTORE_GETFROMREF(server, ref->targetId); /* Test if the node class matches */ if(target && !matchClassMask(target, bd->nodeClassMask)) { UA_NODESTORE_RELEASE(server, target); continue; } /* We have a matching target! */ /* Reached maxrefs. Update the cp and bail. */ if(rr->size >= cp->maxReferences) { if(target) UA_NODESTORE_RELEASE(server, target); cp->nextRefKindIndex = rk->referenceTypeIndex; /* Make a deep copy */ UA_ExpandedNodeId tmpEn = UA_NodePointer_toExpandedNodeId(ref->targetId); UA_ExpandedNodeId_clear(&cp->nextTarget); return UA_ExpandedNodeId_copy(&tmpEn, &cp->nextTarget); } /* Copy the node description. Target is on top of the stack */ retval = addReferenceDescription(server, rr, rk, bd->resultMask, ref->targetId, target); if(target) UA_NODESTORE_RELEASE(server, target); if(retval != UA_STATUSCODE_GOOD) return retval; } } /* 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) { UA_assert(cp); 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_GET(server, &descr->referenceTypeId); if(!reftype) { result->statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID; return true; } UA_Boolean isRef = (reftype->head.nodeClass == UA_NODECLASS_REFERENCETYPE); UA_NODESTORE_RELEASE(server, reftype); if(!isRef) { result->statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID; return true; } } const UA_Node *node = UA_NODESTORE_GET(server, &descr->nodeId); if(!node) { result->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN; return true; } if(session != &server->adminSession && !server->config.accessControl. allowBrowseNode(server, &server->config.accessControl, &session->sessionId, session->sessionHandle, &descr->nodeId, node->head.context)) { result->statusCode = UA_STATUSCODE_BADUSERACCESSDENIED; UA_NODESTORE_RELEASE(server, node); return true; } RefResult rr; result->statusCode = RefResult_init(&rr); if(result->statusCode != UA_STATUSCODE_GOOD) { UA_NODESTORE_RELEASE(server, node); return true; } /* Browse the references */ UA_Boolean done = false; result->statusCode = browseReferences(server, &node->head, cp, &rr, &done); UA_NODESTORE_RELEASE(server, 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 */ ContinuationPoint cp; 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 */ result->statusCode = referenceTypeIndices(server, &descr->referenceTypeId, &cp.relevantReferences, descr->includeSubtypes); 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) 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)); /* The BrowseDescription is only a shallow copy in cp */ retval = UA_BrowseDescription_copy(descr, &cp2->browseDescription); if(retval != UA_STATUSCODE_GOOD) goto cleanup; cp2->maxReferences = cp.maxReferences; cp2->relevantReferences = cp.relevantReferences; cp2->nextTarget = cp.nextTarget; cp2->nextRefKindIndex = cp.nextRefKindIndex; /* 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_clear(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"); UA_LOCK_ASSERT(&server->serviceMutex, 1); /* 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); UA_LOCK(&server->serviceMutex); Operation_Browse(server, &server->adminSession, &maxReferences, bd, &result); UA_UNLOCK(&server->serviceMutex); 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; ContinuationPoint *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_clear(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_LOCK_ASSERT(&server->serviceMutex, 1); 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); UA_LOCK(&server->serviceMutex); Operation_BrowseNext(server, &server->adminSession, &releaseContinuationPoint, continuationPoint, &result); UA_UNLOCK(&server->serviceMutex); return result; } /***********************/ /* TranslateBrowsePath */ /***********************/ /* Find all entries for that hash. There are duplicate for the possible hash * collisions. The exact browsename is checked afterwards. */ static UA_StatusCode recursiveAddBrowseHashTarget(RefTree *results, struct aa_head *head, const UA_ReferenceTarget *rt) { UA_assert(rt); UA_StatusCode res = RefTree_add(results, rt->targetId, NULL); UA_ReferenceTarget *prev = (UA_ReferenceTarget*)aa_prev(head, rt); while(prev && prev->targetNameHash == rt->targetNameHash) { res |= RefTree_add(results, prev->targetId, NULL); prev = (UA_ReferenceTarget*)aa_prev(head, prev); } UA_ReferenceTarget *next= (UA_ReferenceTarget*)aa_next(head, rt); while(next && next->targetNameHash == rt->targetNameHash) { res |= RefTree_add(results, next->targetId, NULL); next = (UA_ReferenceTarget*)aa_next(head, next); } return res; } static UA_StatusCode walkBrowsePathElement(UA_Server *server, UA_Session *session, const UA_RelativePath *path, const size_t pathIndex, UA_UInt32 nodeClassMask, const UA_QualifiedName *lastBrowseName, UA_BrowsePathResult *result, RefTree *current, RefTree *next) { /* For the next level. Note the difference from lastBrowseName */ const UA_RelativePathElement *elem = &path->elements[pathIndex]; UA_UInt32 browseNameHash = UA_QualifiedName_hash(&elem->targetName); /* Get the relevant ReferenceTypes */ UA_ReferenceTypeSet refTypes; UA_StatusCode res = referenceTypeIndices(server, &elem->referenceTypeId, &refTypes, elem->includeSubtypes); if(res != UA_STATUSCODE_GOOD) return UA_STATUSCODE_BADNOMATCH; struct aa_head _refNameTree = refNameTree; /* Loop over all Nodes in the current depth level */ for(size_t i = 0; i < current->size; i++) { /* Remote Node. Immediately add to the results with the * RemainingPathIndex set. */ if(!UA_ExpandedNodeId_isLocal(¤t->targets[i])) { /* Increase the size of the results array */ UA_BrowsePathTarget *tmpResults = (UA_BrowsePathTarget*) UA_realloc(result->targets, sizeof(UA_BrowsePathTarget) * (result->targetsSize + 1)); if(!tmpResults) return UA_STATUSCODE_BADOUTOFMEMORY; result->targets = tmpResults; /* Copy over the result */ res = UA_ExpandedNodeId_copy(¤t->targets[i], &result->targets[result->targetsSize].targetId); result->targets[result->targetsSize].remainingPathIndex = (UA_UInt32)pathIndex; result->targetsSize++; if(res != UA_STATUSCODE_GOOD) break; continue; } /* Local Node. Add to the tree of results at the next depth. */ const UA_Node *node = UA_NODESTORE_GET(server, ¤t->targets[i].nodeId); if(!node) continue; /* Test whether the node fits the class mask */ UA_Boolean skip = !matchClassMask(node, nodeClassMask); /* Does the BrowseName match for the current node (not the references * going out here) */ skip |= (lastBrowseName && !UA_QualifiedName_equal(lastBrowseName, &node->head.browseName)); if(skip) { UA_NODESTORE_RELEASE(server, node); continue; } /* Loop over the ReferenceKinds */ for(size_t j = 0; j < node->head.referencesSize; j++) { UA_NodeReferenceKind *rk = &node->head.references[j]; /* Does the direction of the reference match? */ if(rk->isInverse != elem->isInverse) continue; /* Does the reference type match? */ if(!UA_ReferenceTypeSet_contains(&refTypes, rk->referenceTypeIndex)) continue; if(rk->hasRefTree) { /* Retrieve by BrowseName hash. We might have several nodes where * the hash matches. The exact BrowseName will be verified in the * next iteration of the outer loop. So we only have to retrieve * every node just once. */ _refNameTree.root = rk->targets.tree.nameTreeRoot; UA_ReferenceTarget *rt = (UA_ReferenceTarget*) aa_find(&_refNameTree, &browseNameHash); if(!rt) continue; res = recursiveAddBrowseHashTarget(next, &_refNameTree, rt); if(res != UA_STATUSCODE_GOOD) break; } else { /* The array entries don't have a BrowseName hash. Add all of * them at this level to be checked with a full string * comparison. */ for(size_t k = 0; k < rk->targetsSize; k++) { if(rk->targets.array[k].targetNameHash != browseNameHash) continue; res = RefTree_add(next, rk->targets.array[k].targetId, NULL); if(res != UA_STATUSCODE_GOOD) break; } if(res != UA_STATUSCODE_GOOD) break; } } UA_NODESTORE_RELEASE(server, node); } return res; } static void Operation_TranslateBrowsePathToNodeIds(UA_Server *server, UA_Session *session, const UA_UInt32 *nodeClassMask, const UA_BrowsePath *path, UA_BrowsePathResult *result) { UA_LOCK_ASSERT(&server->serviceMutex, 1); 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; } } /* Check if the starting node exists */ const UA_Node *startingNode = UA_NODESTORE_GET(server, &path->startingNode); if(!startingNode) { result->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN; return; } UA_NODESTORE_RELEASE(server, startingNode); /* Create two RefTrees that are alternated between path elements */ RefTree rt1; RefTree rt2; RefTree *current = &rt1; RefTree *next = &rt2; RefTree *tmp; result->statusCode |= RefTree_init(&rt1); result->statusCode |= RefTree_init(&rt2); UA_BrowsePathTarget *tmpResults = NULL; UA_QualifiedName *browseNameFilter = NULL; if(result->statusCode != UA_STATUSCODE_GOOD) goto cleanup; /* Copy the starting node into next */ result->statusCode = RefTree_addNodeId(next, &path->startingNode, NULL); if(result->statusCode != UA_STATUSCODE_GOOD) goto cleanup; /* Walk the path elements. Retrieve the nodes only once from the NodeStore. * Hence the BrowseName is checked with one element "delay". */ for(size_t i = 0; i < path->relativePath.elementsSize; i++) { /* Switch the trees */ tmp = current; current = next; next = tmp; /* Clear up current, keep the capacity */ for(size_t j = 0; j < next->size; j++) UA_ExpandedNodeId_clear(&next->targets[j]); next->size = 0; ZIP_INIT(&next->head); /* Do this check after next->size has been set to zero */ if(current->size == 0) break; /* Walk element for all NodeIds in the "current" tree. * Puts new results in the "next" tree. */ result->statusCode = walkBrowsePathElement(server, session, &path->relativePath, i, *nodeClassMask, browseNameFilter, result, current, next); if(result->statusCode != UA_STATUSCODE_GOOD) goto cleanup; browseNameFilter = &path->relativePath.elements[i].targetName; } /* Allocate space for the results array */ tmpResults = (UA_BrowsePathTarget*) UA_realloc(result->targets, sizeof(UA_BrowsePathTarget) * (result->targetsSize + next->size)); if(!tmpResults && next->size > 0) { result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY; goto cleanup; } result->targets = tmpResults; for(size_t k = 0; k < next->size; k++) { /* Check the BrowseName. It has been filtered only via its hash so far. */ const UA_Node *node = UA_NODESTORE_GET(server, &next->targets[k].nodeId); if(!node) continue; UA_Boolean match = UA_QualifiedName_equal(browseNameFilter, &node->head.browseName); UA_NODESTORE_RELEASE(server, node); if(!match) continue; /* Move to the target to the results array */ result->targets[result->targetsSize].targetId = next->targets[k]; result->targets[result->targetsSize].remainingPathIndex = UA_UINT32_MAX; UA_ExpandedNodeId_init(&next->targets[k]); result->targetsSize++; } /* 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 */ cleanup: RefTree_clear(&rt1); RefTree_clear(&rt2); if(result->statusCode != UA_STATUSCODE_GOOD) { for(size_t i = 0; i < result->targetsSize; ++i) UA_BrowsePathTarget_clear(&result->targets[i]); if(result->targets) UA_free(result->targets); result->targets = NULL; result->targetsSize = 0; } } UA_BrowsePathResult translateBrowsePathToNodeIds(UA_Server *server, const UA_BrowsePath *browsePath) { UA_LOCK_ASSERT(&server->serviceMutex, 1); UA_BrowsePathResult result; UA_BrowsePathResult_init(&result); UA_UInt32 nodeClassMask = 0; /* All node classes */ Operation_TranslateBrowsePathToNodeIds(server, &server->adminSession, &nodeClassMask, browsePath, &result); return result; } UA_BrowsePathResult UA_Server_translateBrowsePathToNodeIds(UA_Server *server, const UA_BrowsePath *browsePath) { UA_LOCK(&server->serviceMutex); UA_BrowsePathResult result = translateBrowsePathToNodeIds(server, browsePath); UA_UNLOCK(&server->serviceMutex); 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"); UA_LOCK_ASSERT(&server->serviceMutex, 1); /* 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 browseSimplifiedBrowsePath(UA_Server *server, const UA_NodeId origin, size_t browsePathSize, const UA_QualifiedName *browsePath) { UA_LOCK_ASSERT(&server->serviceMutex, 1); UA_BrowsePathResult bpr; UA_BrowsePathResult_init(&bpr); if(browsePathSize > UA_MAX_TREE_RECURSE) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Simplified Browse Path too long"); bpr.statusCode = UA_STATUSCODE_BADINTERNALERROR; return bpr; } /* Construct the BrowsePath */ UA_BrowsePath bp; UA_BrowsePath_init(&bp); bp.startingNode = origin; UA_RelativePathElement rpe[UA_MAX_TREE_RECURSE]; 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_UInt32 nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE | UA_NODECLASS_OBJECTTYPE; Operation_TranslateBrowsePathToNodeIds(server, &server->adminSession, &nodeClassMask, &bp, &bpr); return bpr; } UA_BrowsePathResult UA_Server_browseSimplifiedBrowsePath(UA_Server *server, const UA_NodeId origin, size_t browsePathSize, const UA_QualifiedName *browsePath) { UA_LOCK(&server->serviceMutex); UA_BrowsePathResult bpr = browseSimplifiedBrowsePath(server, origin, browsePathSize, browsePath); UA_UNLOCK(&server->serviceMutex); 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"); UA_LOCK_ASSERT(&server->serviceMutex, 1); //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"); UA_LOCK_ASSERT(&server->serviceMutex, 1); //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 "/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 * Copyright 2020 (c) Hilscher Gesellschaft für Systemautomation mbH (Author: Martin Lang) */ #ifdef UA_ENABLE_METHODCALLS /* conditional compilation */ static const UA_VariableNode * getArgumentsVariableNode(UA_Server *server, const UA_NodeHead *head, UA_String withBrowseName) { for(size_t i = 0; i < head->referencesSize; ++i) { const UA_NodeReferenceKind *rk = &head->references[i]; if(rk->isInverse != false) continue; if(rk->referenceTypeIndex != UA_REFERENCETYPEINDEX_HASPROPERTY) continue; const UA_ReferenceTarget *t = NULL; while((t = UA_NodeReferenceKind_iterate(rk, t))) { const UA_Node *refTarget = UA_NODESTORE_GETFROMREF(server, t->targetId); if(!refTarget) continue; if(refTarget->head.nodeClass == UA_NODECLASS_VARIABLE && refTarget->head.browseName.namespaceIndex == 0 && UA_String_equal(&withBrowseName, &refTarget->head.browseName.name)) { return &refTarget->variableNode; } UA_NODESTORE_RELEASE(server, 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; const char *reason; 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, &reason)) continue; /* Incompatible value. Try to correct the type if possible. */ adjustValueType(server, &args[i], &argReqs[i].dataType); /* Recheck */ if(!compatibleValue(server, session, &argReqs[i].dataType, argReqs[i].valueRank, argReqs[i].arrayDimensionsSize, argReqs[i].arrayDimensions, &args[i], NULL, &reason)) { 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->head, 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_RELEASE(server, (const UA_Node*)inputArguments); return retval; } static const UA_NodeId hasComponentNodeId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASCOMPONENT}}; static const UA_NodeId organizedByNodeId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_ORGANIZES}}; static const UA_String namespaceDiModel = UA_STRING_STATIC("http://opcfoundation.org/UA/DI/"); static const UA_NodeId hasTypeDefinitionNodeId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASTYPEDEFINITION}}; // ns=0 will be replace dynamically. DI-Spec. 1.01: static UA_NodeId functionGroupNodeId = {0, UA_NODEIDTYPE_NUMERIC, {1005}}; 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->head.nodeClass != UA_NODECLASS_OBJECT && object->head.nodeClass != UA_NODECLASS_OBJECTTYPE) { result->statusCode = UA_STATUSCODE_BADNODECLASSINVALID; return; } /* Verify the method's NodeClass */ if(method->head.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; } UA_NodePointer methodP = UA_NodePointer_fromNodeId(&request->methodId); /* 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_ReferenceTypeSet hasComponentRefs; result->statusCode = referenceTypeIndices(server, &hasComponentNodeId, &hasComponentRefs, true); if(result->statusCode != UA_STATUSCODE_GOOD) return; UA_Boolean found = false; for(size_t i = 0; i < object->head.referencesSize && !found; ++i) { const UA_NodeReferenceKind *rk = &object->head.references[i]; if(rk->isInverse) continue; if(!UA_ReferenceTypeSet_contains(&hasComponentRefs, rk->referenceTypeIndex)) continue; const UA_ReferenceTarget *t = NULL; while((t = UA_NodeReferenceKind_iterate(rk, t))) { if(UA_NodePointer_equal(t->targetId, methodP)) { found = true; break; } } } if(!found) { /* The following ParentObject evaluation is a workaround only to fulfill * the OPC UA Spec. Part 100 - Devices requirements regarding functional * groups. Compare OPC UA Spec. Part 100 - Devices, Release 1.02 * - 5.4 FunctionalGroupType * - B.1 Functional Group Usages * A functional group is a sub-type of the FolderType and is used to * organize the Parameters and Methods from the complete set (named * ParameterSet and MethodSet) in (Functional) groups for instance * Configuration or Identification. The same Property, Parameter or * Method can be referenced from more than one FunctionalGroup. */ /* Check whether the DI namespace is available */ size_t foundNamespace = 0; UA_StatusCode res = getNamespaceByName(server, namespaceDiModel, &foundNamespace); if(res != UA_STATUSCODE_GOOD) { result->statusCode = UA_STATUSCODE_BADMETHODINVALID; return; } functionGroupNodeId.namespaceIndex = (UA_UInt16)foundNamespace; UA_ReferenceTypeSet hasTypeDefinitionRefs; result->statusCode = referenceTypeIndices(server, &hasTypeDefinitionNodeId, &hasTypeDefinitionRefs, true); if(result->statusCode != UA_STATUSCODE_GOOD) return; /* Search for a HasTypeDefinition (or sub-) reference in the parent object */ for(size_t i = 0; i < object->head.referencesSize && !found; ++i) { const UA_NodeReferenceKind *rk = &object->head.references[i]; if(rk->isInverse) continue; if(!UA_ReferenceTypeSet_contains(&hasTypeDefinitionRefs, rk->referenceTypeIndex)) continue; /* Verify that the HasTypeDefinition is equal to FunctionGroupType * (or sub-type) from the DI model */ const UA_ReferenceTarget *t = NULL; while((t = UA_NodeReferenceKind_iterate(rk, t))) { if(!UA_NodePointer_isLocal(t->targetId)) continue; UA_NodeId tmpId = UA_NodePointer_toNodeId(t->targetId); if(!isNodeInTree_singleRef(server, &tmpId, &functionGroupNodeId, UA_REFERENCETYPEINDEX_HASSUBTYPE)) continue; /* Search for the called method with reference Organize (or * sub-type) from the parent object */ for(size_t k = 0; k < object->head.referencesSize && !found; ++k) { const UA_NodeReferenceKind *rkInner = &object->head.references[k]; if(rkInner->isInverse) continue; const UA_NodeId * refId = UA_NODESTORE_GETREFERENCETYPEID(server, rkInner->referenceTypeIndex); if(!isNodeInTree_singleRef(server, refId, &organizedByNodeId, UA_REFERENCETYPEINDEX_HASSUBTYPE)) continue; const UA_ReferenceTarget *t2 = NULL; while((t2 = UA_NodeReferenceKind_iterate(rkInner, t2))) { if(UA_NodePointer_equal(t2->targetId, methodP)) { found = true; break; } } } } } if(!found) { result->statusCode = UA_STATUSCODE_BADMETHODINVALID; return; } } /* Verify access rights */ UA_Boolean executable = method->executable; if(session != &server->adminSession) { UA_UNLOCK(&server->serviceMutex); executable = executable && server->config.accessControl. getUserExecutableOnObject(server, &server->config.accessControl, &session->sessionId, session->sessionHandle, &request->methodId, method->head.context, &request->objectId, object->head.context); UA_LOCK(&server->serviceMutex); } 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->head, 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_RELEASE(server, (const UA_Node*)outputArguments); /* Call the method */ UA_UNLOCK(&server->serviceMutex); result->statusCode = method->method(server, &session->sessionId, session->sessionHandle, &method->head.nodeId, method->head.context, &object->head.nodeId, object->head.context, request->inputArgumentsSize, request->inputArguments, result->outputArgumentsSize, result->outputArguments); UA_LOCK(&server->serviceMutex); /* TODO: Verify Output matches the argument definition */ } #if UA_MULTITHREADING >= 100 static void Operation_CallMethodAsync(UA_Server *server, UA_Session *session, UA_UInt32 requestId, UA_UInt32 requestHandle, size_t opIndex, UA_CallMethodRequest *opRequest, UA_CallMethodResult *opResult, UA_AsyncResponse **ar) { /* Get the method node */ const UA_Node *method = UA_NODESTORE_GET(server, &opRequest->methodId); if(!method) { opResult->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN; return; } /* Get the object node */ const UA_Node *object = UA_NODESTORE_GET(server, &opRequest->objectId); if(!object) { opResult->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN; UA_NODESTORE_RELEASE(server, method); return; } /* Synchronous execution */ if(!method->methodNode.async) { callWithMethodAndObject(server, session, opRequest, opResult, &method->methodNode, &object->objectNode); goto cleanup; } /* <-- Async method call --> */ /* No AsyncResponse allocated so far */ if(!*ar) { opResult->statusCode = UA_AsyncManager_createAsyncResponse(&server->asyncManager, server, &session->sessionId, requestId, requestHandle, UA_ASYNCOPERATIONTYPE_CALL, ar); if(opResult->statusCode != UA_STATUSCODE_GOOD) goto cleanup; } /* Create the Async Request to be taken by workers */ opResult->statusCode = UA_AsyncManager_createAsyncOp(&server->asyncManager, server, *ar, opIndex, opRequest); cleanup: /* Release the method and object node */ UA_NODESTORE_RELEASE(server, method); UA_NODESTORE_RELEASE(server, object); } void Service_CallAsync(UA_Server *server, UA_Session *session, UA_UInt32 requestId, const UA_CallRequest *request, UA_CallResponse *response, UA_Boolean *finished) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing CallRequestAsync"); if(server->config.maxNodesPerMethodCall != 0 && request->methodsToCallSize > server->config.maxNodesPerMethodCall) { response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS; return; } UA_AsyncResponse *ar = NULL; response->responseHeader.serviceResult = UA_Server_processServiceOperationsAsync(server, session, requestId, request->requestHeader.requestHandle, (UA_AsyncServiceOperation)Operation_CallMethodAsync, &request->methodsToCallSize, &UA_TYPES[UA_TYPES_CALLMETHODREQUEST], &response->resultsSize, &UA_TYPES[UA_TYPES_CALLMETHODRESULT], &ar); if(ar) { if(ar->opCountdown > 0) { /* Move all results to the AsyncResponse. The async operation * results will be overwritten when the workers return results. */ ar->response.callResponse = *response; UA_CallResponse_init(response); *finished = false; } else { /* If there is a new AsyncResponse, ensure it has at least one * pending operation */ UA_AsyncManager_removeAsyncResponse(&server->asyncManager, ar); } } } #endif 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_Node *method = UA_NODESTORE_GET(server, &request->methodId); if(!method) { result->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN; return; } /* Get the object node */ const UA_Node *object = UA_NODESTORE_GET(server, &request->objectId); if(!object) { result->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN; UA_NODESTORE_RELEASE(server, method); return; } /* Continue with method and object as context */ callWithMethodAndObject(server, session, request, result, &method->methodNode, &object->objectNode); /* Release the method and object node */ UA_NODESTORE_RELEASE(server, method); UA_NODESTORE_RELEASE(server, 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"); UA_LOCK_ASSERT(&server->serviceMutex, 1); 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_Server_call(UA_Server *server, const UA_CallMethodRequest *request) { UA_CallMethodResult result; UA_CallMethodResult_init(&result); UA_LOCK(&server->serviceMutex); Operation_CallMethod(server, &server->adminSession, NULL, request, &result); UA_UNLOCK(&server->serviceMutex); return result; } #endif /* UA_ENABLE_METHODCALLS */ /**** amalgamated original file "/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-2020 (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 * Copyright 2018-2020 (c) HMS Industrial Networks AB (Author: Jonas Green) */ /* Delayed callback to free the session memory */ static void removeSessionCallback(UA_Server *server, session_list_entry *entry) { UA_LOCK(&server->serviceMutex); UA_Session_clear(&entry->session, server); UA_UNLOCK(&server->serviceMutex); } void UA_Server_removeSession(UA_Server *server, session_list_entry *sentry, UA_DiagnosticEvent event) { UA_Session *session = &sentry->session; UA_LOCK_ASSERT(&server->serviceMutex, 1); /* Remove the Subscriptions */ #ifdef UA_ENABLE_SUBSCRIPTIONS UA_Subscription *sub, *tempsub; TAILQ_FOREACH_SAFE(sub, &session->subscriptions, sessionListEntry, tempsub) { UA_Subscription_delete(server, sub); } UA_PublishResponseEntry *entry; while((entry = UA_Session_dequeuePublishReq(session))) { UA_PublishResponse_clear(&entry->response); UA_free(entry); } #endif /* Callback into userland access control */ if(server->config.accessControl.closeSession) { UA_UNLOCK(&server->serviceMutex); server->config.accessControl.closeSession(server, &server->config.accessControl, &session->sessionId, session->sessionHandle); UA_LOCK(&server->serviceMutex); } /* Detach the Session from the SecureChannel */ UA_Session_detachFromSecureChannel(session); /* Deactivate the session */ if(sentry->session.activated) { sentry->session.activated = false; server->activeSessionCount--; } /* Detach the session from the session manager and make the capacity * available */ LIST_REMOVE(sentry, pointers); server->sessionCount--; switch(event) { case UA_DIAGNOSTICEVENT_CLOSE: case UA_DIAGNOSTICEVENT_PURGE: break; case UA_DIAGNOSTICEVENT_TIMEOUT: server->serverDiagnosticsSummary.sessionTimeoutCount++; break; case UA_DIAGNOSTICEVENT_REJECT: server->serverDiagnosticsSummary.rejectedSessionCount++; break; case UA_DIAGNOSTICEVENT_SECURITYREJECT: server->serverDiagnosticsSummary.securityRejectedSessionCount++; break; case UA_DIAGNOSTICEVENT_ABORT: server->serverDiagnosticsSummary.sessionAbortCount++; break; default: UA_assert(false); break; } /* Add a delayed callback to remove the session when the currently * scheduled jobs have completed */ sentry->cleanupCallback.callback = (UA_ApplicationCallback)removeSessionCallback; sentry->cleanupCallback.application = server; sentry->cleanupCallback.data = sentry; sentry->cleanupCallback.nextTime = UA_DateTime_nowMonotonic() + 1; sentry->cleanupCallback.interval = 0; /* Remove the structure */ UA_Timer_addTimerEntry(&server->timer, &sentry->cleanupCallback, NULL); } UA_StatusCode UA_Server_removeSessionByToken(UA_Server *server, const UA_NodeId *token, UA_DiagnosticEvent event) { UA_LOCK_ASSERT(&server->serviceMutex, 1); session_list_entry *entry; LIST_FOREACH(entry, &server->sessions, pointers) { if(UA_NodeId_equal(&entry->session.header.authenticationToken, token)) { UA_Server_removeSession(server, entry, event); return UA_STATUSCODE_GOOD; } } return UA_STATUSCODE_BADSESSIONIDINVALID; } void UA_Server_cleanupSessions(UA_Server *server, UA_DateTime nowMonotonic) { UA_LOCK_ASSERT(&server->serviceMutex, 1); session_list_entry *sentry, *temp; LIST_FOREACH_SAFE(sentry, &server->sessions, pointers, temp) { /* Session has timed out? */ if(sentry->session.validTill >= nowMonotonic) continue; UA_LOG_INFO_SESSION(&server->config.logger, &sentry->session, "Session has timed out"); UA_Server_removeSession(server, sentry, UA_DIAGNOSTICEVENT_TIMEOUT); } } /************/ /* Services */ /************/ UA_Session * getSessionByToken(UA_Server *server, const UA_NodeId *token) { UA_LOCK_ASSERT(&server->serviceMutex, 1); session_list_entry *current = NULL; LIST_FOREACH(current, &server->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(&server->config.logger, ¤t->session, "Client tries to use a session that has timed out"); return NULL; } return ¤t->session; } return NULL; } UA_Session * UA_Server_getSessionById(UA_Server *server, const UA_NodeId *sessionId) { UA_LOCK_ASSERT(&server->serviceMutex, 1); session_list_entry *current = NULL; LIST_FOREACH(current, &server->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(&server->config.logger, ¤t->session, "Client tries to use a session that has timed out"); return NULL; } return ¤t->session; } return NULL; } 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 *securityPolicy = channel->securityPolicy; UA_SignatureData *signatureData = &response->serverSignature; /* Prepare the signature */ size_t signatureSize = securityPolicy->certificateSigningAlgorithm. getLocalSignatureSize(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(channel->channelContext, &dataToSign, &signatureData->signature); /* Clean up */ UA_ByteString_clear(&dataToSign); return retval; } /* Creates and adds a session. But it is not yet attached to a secure channel. */ UA_StatusCode UA_Server_createSession(UA_Server *server, UA_SecureChannel *channel, const UA_CreateSessionRequest *request, UA_Session **session) { UA_LOCK_ASSERT(&server->serviceMutex, 1); if(server->sessionCount >= server->config.maxSessions) { UA_LOG_WARNING_CHANNEL(&server->config.logger, channel, "Could not create a Session - Server limits reached"); return UA_STATUSCODE_BADTOOMANYSESSIONS; } session_list_entry *newentry = (session_list_entry*) UA_malloc(sizeof(session_list_entry)); if(!newentry) return UA_STATUSCODE_BADOUTOFMEMORY; /* Initialize the Session */ 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()); newentry->session.timeout = server->config.maxSessionTimeout; if(request->requestedSessionTimeout <= server->config.maxSessionTimeout && request->requestedSessionTimeout > 0) newentry->session.timeout = request->requestedSessionTimeout; /* Attach the session to the channel. But don't activate for now. */ if(channel) UA_Session_attachToSecureChannel(&newentry->session, channel); UA_Session_updateLifetime(&newentry->session); /* Add to the server */ LIST_INSERT_HEAD(&server->sessions, newentry, pointers); server->sessionCount++; *session = &newentry->session; return UA_STATUSCODE_GOOD; } void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel, const UA_CreateSessionRequest *request, UA_CreateSessionResponse *response) { UA_LOCK_ASSERT(&server->serviceMutex, 1); 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; } } UA_assert(channel->securityToken.channelId != 0); if(!UA_ByteString_equal(&channel->securityPolicy->policyUri, &UA_SECURITY_POLICY_NONE_URI) && request->clientNonce.length < 32) { response->responseHeader.serviceResult = UA_STATUSCODE_BADNONCEINVALID; return; } if(request->clientCertificate.length > 0) { UA_CertificateVerification *cv = &server->config.certificateVerification; 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"); server->serverDiagnosticsSummary.securityRejectedSessionCount++; server->serverDiagnosticsSummary.rejectedSessionCount++; return; } } UA_Session *newSession = NULL; response->responseHeader.serviceResult = UA_Server_createSession(server, channel, request, &newSession); if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_LOG_WARNING_CHANNEL(&server->config.logger, channel, "Processing CreateSessionRequest failed"); server->serverDiagnosticsSummary.rejectedSessionCount++; 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_Server_removeSessionByToken(server, &newSession->header.authenticationToken, UA_DIAGNOSTICEVENT_REJECT); 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_Server_removeSessionByToken(server, &newSession->header.authenticationToken, UA_DIAGNOSTICEVENT_REJECT); return; } /* Mirror back the endpointUrl */ for(size_t i = 0; i < response->serverEndpointsSize; ++i) { UA_String_clear(&response->serverEndpoints[i].endpointUrl); response->responseHeader.serviceResult |= UA_String_copy(&request->endpointUrl, &response->serverEndpoints[i].endpointUrl); } /* Fill the session information */ newSession->maxResponseMessageSize = request->maxResponseMessageSize; newSession->maxRequestMessageSize = channel->config.localMaxMessageSize; 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); #ifdef UA_ENABLE_DIAGNOSTICS response->responseHeader.serviceResult |= UA_String_copy(&request->serverUri, &newSession->diagnostics.serverUri); response->responseHeader.serviceResult |= UA_String_copy(&request->endpointUrl, &newSession->diagnostics.endpointUrl); #endif 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_Server_removeSessionByToken(server, &newSession->header.authenticationToken, UA_DIAGNOSTICEVENT_REJECT); return; } #ifdef UA_ENABLE_DIAGNOSTICS newSession->diagnostics.clientConnectionTime = UA_DateTime_now(); newSession->diagnostics.clientLastContactTime = newSession->diagnostics.clientConnectionTime; /* Create the object in the information model */ createSessionObject(server, newSession); #endif UA_LOG_INFO_SESSION(&server->config.logger, newSession, "Session created"); } static UA_StatusCode checkSignature(const UA_Server *server, const UA_SecurityPolicy *securityPolicy, void *channelContext, const UA_ByteString *serverNonce, const UA_SignatureData *signature) { /* Check for zero signature length */ if(signature->signature.length == 0) return UA_STATUSCODE_BADAPPLICATIONSIGNATUREINVALID; if(!securityPolicy) return UA_STATUSCODE_BADINTERNALERROR; const UA_ByteString *localCertificate = &securityPolicy->localCertificate; /* Data to verify is calculated by appending the serverNonce to the local certificate */ UA_ByteString dataToVerify; size_t dataToVerifySize = localCertificate->length + serverNonce->length; 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, serverNonce->data, serverNonce->length); retval = securityPolicy->certificateSigningAlgorithm. verify(channelContext, &dataToVerify, &signature->signature); UA_ByteString_clear(&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; size_t tokenpos = 0; size_t offset = 0; if(UA_ByteString_copy(&userToken->password, &decryptedTokenSecret) != UA_STATUSCODE_GOOD) return UA_STATUSCODE_BADIDENTITYTOKENINVALID; UA_StatusCode retval = UA_STATUSCODE_BADIDENTITYTOKENINVALID; if(asymEnc->decrypt(tempChannelContext, &decryptedTokenSecret) != UA_STATUSCODE_GOOD) goto cleanup; UA_UInt32_decodeBinary(&decryptedTokenSecret, &offset, &tokenSecretLength); /* 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. */ tokenpos = sizeof(UA_UInt32) + tokenSecretLength - serverNonce->length; tokenServerNonce.length = serverNonce->length; tokenServerNonce.data = &decryptedTokenSecret.data[tokenpos]; 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_clear(&decryptedTokenSecret); return retval; } #endif static void selectEndpointAndTokenPolicy(UA_Server *server, UA_SecureChannel *channel, const UA_ExtensionObject *identityToken, const UA_EndpointDescription **ed, const UA_UserTokenPolicy **utp) { for(size_t i = 0; i < server->config.endpointsSize; ++i) { const UA_EndpointDescription *desc = &server->config.endpoints[i]; /* Match the Security Mode */ if(desc->securityMode != channel->securityMode) continue; /* Match the SecurityPolicy of the endpoint with the current channel */ if(!UA_String_equal(&desc->securityPolicyUri, &channel->securityPolicy->policyUri)) continue; /* Match the UserTokenType */ const UA_DataType *tokenDataType = identityToken->content.decoded.type; for(size_t j = 0; j < desc->userIdentityTokensSize; j++) { const UA_UserTokenPolicy *pol = &desc->userIdentityTokens[j]; /* Part 4, Section 5.6.3.2, Table 17: A NULL or empty * UserIdentityToken should be treated as Anonymous */ if(identityToken->encoding == UA_EXTENSIONOBJECT_ENCODED_NOBODY && pol->tokenType == UA_USERTOKENTYPE_ANONYMOUS) { *ed = desc; *utp = pol; return; } /* Expect decoded content */ if(!tokenDataType) continue; if(pol->tokenType == UA_USERTOKENTYPE_ANONYMOUS) { if(tokenDataType != &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN]) continue; } else if(pol->tokenType == UA_USERTOKENTYPE_USERNAME) { if(tokenDataType != &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]) continue; } else if(pol->tokenType == UA_USERTOKENTYPE_CERTIFICATE) { if(tokenDataType != &UA_TYPES[UA_TYPES_X509IDENTITYTOKEN]) continue; } else if(pol->tokenType == UA_USERTOKENTYPE_ISSUEDTOKEN) { if(tokenDataType != &UA_TYPES[UA_TYPES_ISSUEDIDENTITYTOKEN]) continue; } else { continue; } /* All valid token data types start with a string policyId */ UA_AnonymousIdentityToken *token = (UA_AnonymousIdentityToken*) identityToken->content.decoded.data; if(!UA_String_equal(&pol->policyId, &token->policyId)) continue; /* Match found */ *ed = desc; *utp = pol; return; } } } #ifdef UA_ENABLE_DIAGNOSTICS static UA_StatusCode saveClientUserId(const UA_ExtensionObject *userIdentityToken, UA_SessionSecurityDiagnosticsDataType *diag) { UA_StatusCode res = UA_STATUSCODE_GOOD; UA_String_clear(&diag->clientUserIdOfSession); if(userIdentityToken->encoding != UA_EXTENSIONOBJECT_DECODED) return UA_STATUSCODE_GOOD; if(userIdentityToken->content.decoded.type == &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN]) { /* String of length 0 */ } else if(userIdentityToken->content.decoded.type == &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]) { const UA_UserNameIdentityToken *userToken = (UA_UserNameIdentityToken*) userIdentityToken->content.decoded.data; res = UA_String_copy(&userToken->userName, &diag->clientUserIdOfSession); } else if(userIdentityToken->content.decoded.type == &UA_TYPES[UA_TYPES_X509IDENTITYTOKEN]) { /* TODO: return the X509 Subject Name of the certificate */ } else { return UA_STATUSCODE_BADIDENTITYTOKENINVALID; } if(res != UA_STATUSCODE_GOOD) return res; return UA_Array_appendCopy((void**)&diag->clientUserIdHistory, &diag->clientUserIdHistorySize, &diag->clientUserIdOfSession, &UA_TYPES[UA_TYPES_STRING]); } #endif /* TODO: Check all of the following: 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, const UA_ActivateSessionRequest *request, UA_ActivateSessionResponse *response) { const UA_EndpointDescription *ed = NULL; const UA_UserTokenPolicy *utp = NULL; UA_String *tmpLocaleIds; UA_LOCK_ASSERT(&server->serviceMutex, 1); UA_Session *session = getSessionByToken(server, &request->requestHeader.authenticationToken); if(!session) { UA_LOG_WARNING_CHANNEL(&server->config.logger, channel, "ActivateSession: Session not found"); response->responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID; goto rejected; } /* 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(!session->activated && session->header.channel != channel) { UA_LOG_WARNING_CHANNEL(&server->config.logger, channel, "ActivateSession: The Session has to be initially activated " "on the SecureChannel that created it"); response->responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID; goto rejected; } /* Has the session timed out? */ if(session->validTill < UA_DateTime_nowMonotonic()) { UA_LOG_WARNING_SESSION(&server->config.logger, session, "ActivateSession: The Session has timed out"); response->responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID; goto rejected; } /* Check the client signature */ if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGN || channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) { response->responseHeader.serviceResult = checkSignature(server, channel->securityPolicy, channel->channelContext, &session->serverNonce, &request->clientSignature); if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_LOG_WARNING_SESSION(&server->config.logger, session, "ActivateSession: Client signature check failed with StatusCode %s", UA_StatusCode_name(response->responseHeader.serviceResult)); goto securityRejected; } } /* Find the matching Endpoint with UserTokenPolicy */ selectEndpointAndTokenPolicy(server, channel, &request->userIdentityToken, &ed, &utp); if(!ed) { response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID; goto rejected; } /* If it is a UserNameIdentityToken, the password may be encrypted */ if(utp->tokenType == UA_USERTOKENTYPE_USERNAME) { UA_UserNameIdentityToken *userToken = (UA_UserNameIdentityToken *) request->userIdentityToken.content.decoded.data; /* If the userTokenPolicy doesn't specify a security policy the security * policy of the secure channel is used. */ UA_SecurityPolicy* securityPolicy; if(!utp->securityPolicyUri.data) securityPolicy = getSecurityPolicyByUri(server, &ed->securityPolicyUri); else securityPolicy = getSecurityPolicyByUri(server, &utp->securityPolicyUri); if(!securityPolicy) { response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR; goto securityRejected; } /* 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; goto securityRejected; } #ifdef UA_ENABLE_ENCRYPTION /* Encrypted password? */ if(!UA_String_equal(&securityPolicy->policyUri, &UA_SECURITY_POLICY_NONE_URI)) { /* 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) { /* We use our own certificate to create a temporary 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. */ UA_UNLOCK(&server->serviceMutex); response->responseHeader.serviceResult = securityPolicy->channelModule. newContext(securityPolicy, &securityPolicy->localCertificate, &tempChannelContext); UA_LOCK(&server->serviceMutex); 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); goto securityRejected; } } /* Decrypt */ response->responseHeader.serviceResult = decryptPassword(securityPolicy, tempChannelContext, &session->serverNonce, userToken); /* Remove the temporary channel context */ if(securityPolicy != channel->securityPolicy) { UA_UNLOCK(&server->serviceMutex); securityPolicy->channelModule.deleteContext(tempChannelContext); UA_LOCK(&server->serviceMutex); } } else if(userToken->encryptionAlgorithm.length != 0) { /* If SecurityPolicy is None there shall be no EncryptionAlgorithm */ response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID; goto securityRejected; } if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_LOG_WARNING_SESSION(&server->config.logger, session, "ActivateSession: " "Failed to decrypt the password with the StatusCode %s", UA_StatusCode_name(response->responseHeader.serviceResult)); goto securityRejected; } #endif } #ifdef UA_ENABLE_ENCRYPTION /* If it is a X509IdentityToken, check the userTokenSignature. Note this * only validates that the user has the corresponding private key for the * given user cetificate. Checking whether the user certificate is trusted * has to be implemented in the access control plugin. The entire token is * forwarded in the call to ActivateSession. */ if(utp->tokenType == UA_USERTOKENTYPE_CERTIFICATE) { UA_X509IdentityToken* userCertToken = (UA_X509IdentityToken*) request->userIdentityToken.content.decoded.data; /* If the userTokenPolicy doesn't specify a security policy the security * policy of the secure channel is used. */ UA_SecurityPolicy* securityPolicy; if(!utp->securityPolicyUri.data) securityPolicy = getSecurityPolicyByUri(server, &ed->securityPolicyUri); else securityPolicy = getSecurityPolicyByUri(server, &utp->securityPolicyUri); if(!securityPolicy) { response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR; goto securityRejected; } /* We need a channel context with the user certificate in order to reuse * the signature checking code. */ void *tempChannelContext; UA_UNLOCK(&server->serviceMutex); response->responseHeader.serviceResult = securityPolicy->channelModule. newContext(securityPolicy, &userCertToken->certificateData, &tempChannelContext); UA_LOCK(&server->serviceMutex); 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); goto securityRejected; } /* Check the user token signature */ response->responseHeader.serviceResult = checkSignature(server, channel->securityPolicy, tempChannelContext, &session->serverNonce, &request->userTokenSignature); /* Delete the temporary channel context */ UA_UNLOCK(&server->serviceMutex); securityPolicy->channelModule.deleteContext(tempChannelContext); UA_LOCK(&server->serviceMutex); if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_LOG_WARNING_SESSION(&server->config.logger, session, "ActivateSession: User token signature check failed with StatusCode %s", UA_StatusCode_name(response->responseHeader.serviceResult)); goto securityRejected; } } #endif /* Callback into userland access control */ UA_UNLOCK(&server->serviceMutex); response->responseHeader.serviceResult = server->config.accessControl. activateSession(server, &server->config.accessControl, ed, &channel->remoteCertificate, &session->sessionId, &request->userIdentityToken, &session->sessionHandle); UA_LOCK(&server->serviceMutex); if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_LOG_WARNING_SESSION(&server->config.logger, session, "ActivateSession: The AccessControl " "plugin denied the activation with the StatusCode %s", UA_StatusCode_name(response->responseHeader.serviceResult)); goto securityRejected; } /* Attach the session to the currently used channel if the session isn't * attached to a channel or if the session is activated on a different * channel than it is attached to. */ if(!session->header.channel || session->header.channel != channel) { /* Attach the new SecureChannel, the old channel will be detached if present */ UA_Session_attachToSecureChannel(session, channel); UA_LOG_INFO_SESSION(&server->config.logger, session, "ActivateSession: Session attached to new channel"); } /* 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); UA_LOG_WARNING_SESSION(&server->config.logger, session, "ActivateSession: Could not generate the server nonce"); goto rejected; } /* Set the locale */ response->responseHeader.serviceResult |= UA_Array_copy(request->localeIds, request->localeIdsSize, (void**)&tmpLocaleIds, &UA_TYPES[UA_TYPES_STRING]); if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_Session_detachFromSecureChannel(session); UA_LOG_WARNING_SESSION(&server->config.logger, session, "ActivateSession: Could not store the Session LocaleIds"); goto rejected; } UA_Array_delete(session->localeIds, session->localeIdsSize, &UA_TYPES[UA_TYPES_STRING]); session->localeIds = tmpLocaleIds; session->localeIdsSize = request->localeIdsSize; UA_Session_updateLifetime(session); /* Activate the session */ if(!session->activated) { session->activated = true; server->activeSessionCount++; server->serverDiagnosticsSummary.cumulatedSessionCount++; } #ifdef UA_ENABLE_DIAGNOSTICS saveClientUserId(&request->userIdentityToken, &session->securityDiagnostics); UA_String_clear(&session->securityDiagnostics.authenticationMechanism); switch(utp->tokenType) { case UA_USERTOKENTYPE_ANONYMOUS: session->securityDiagnostics.authenticationMechanism = UA_STRING_ALLOC("Anonymous"); break; case UA_USERTOKENTYPE_USERNAME: session->securityDiagnostics.authenticationMechanism = UA_STRING_ALLOC("UserName"); break; case UA_USERTOKENTYPE_CERTIFICATE: session->securityDiagnostics.authenticationMechanism = UA_STRING_ALLOC("Certificate"); break; case UA_USERTOKENTYPE_ISSUEDTOKEN: session->securityDiagnostics.authenticationMechanism = UA_STRING_ALLOC("IssuedToken"); break; default: break; } #endif UA_LOG_INFO_SESSION(&server->config.logger, session, "ActivateSession: Session activated"); return; securityRejected: server->serverDiagnosticsSummary.securityRejectedSessionCount++; rejected: server->serverDiagnosticsSummary.rejectedSessionCount++; } void Service_CloseSession(UA_Server *server, UA_SecureChannel *channel, const UA_CloseSessionRequest *request, UA_CloseSessionResponse *response) { UA_LOCK_ASSERT(&server->serviceMutex, 1); /* Part 4, 5.6.4: When the CloseSession Service is called before the Session * is successfully activated, the Server shall reject the request if the * SecureChannel is not the same as the one associated with the * CreateSession request. * * A non-activated Session is already bound to the SecureChannel that * created the Session. */ UA_Session *session = NULL; response->responseHeader.serviceResult = getBoundSession(server, channel, &request->requestHeader.authenticationToken, &session); if(!session && response->responseHeader.serviceResult == UA_STATUSCODE_GOOD) response->responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID; if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_LOG_WARNING_CHANNEL(&server->config.logger, channel, "CloseSession: No Session activated to the SecureChannel"); return; } UA_assert(session); /* Assured by the previous section */ UA_LOG_INFO_SESSION(&server->config.logger, session, "Closing the Session"); #ifdef UA_ENABLE_SUBSCRIPTIONS /* If Subscriptions are not deleted, detach them from the Session */ if(!request->deleteSubscriptions) { UA_Subscription *sub, *sub_tmp; TAILQ_FOREACH_SAFE(sub, &session->subscriptions, sessionListEntry, sub_tmp) { UA_LOG_INFO_SUBSCRIPTION(&server->config.logger, sub, "Detaching the Subscription from the Session"); UA_Session_detachSubscription(server, session, sub, true); } } #endif /* Remove the sesison */ response->responseHeader.serviceResult = UA_Server_removeSessionByToken(server, &session->header.authenticationToken, UA_DIAGNOSTICEVENT_CLOSE); } /**** amalgamated original file "/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-2020 (c) HMS Industrial Networks AB (Author: Jonas Green) * Copyright 2017 (c) Henrik Norrman * Copyright 2020 (c) Christian von Arnim, ISW University of Stuttgart (for VDW and umati) */ #ifdef UA_ENABLE_HISTORIZING #endif /******************/ /* Access Control */ /******************/ /* Session for read operations can be NULL. For example for a MonitoredItem * where the underlying Subscription was detached during CloseSession. */ static UA_UInt32 getUserWriteMask(UA_Server *server, const UA_Session *session, const UA_NodeHead *head) { if(session == &server->adminSession) return 0xFFFFFFFF; /* the local admin user has all rights */ UA_UInt32 mask = head->writeMask; UA_UNLOCK(&server->serviceMutex); mask &= server->config.accessControl. getUserRightsMask(server, &server->config.accessControl, session ? &session->sessionId : NULL, session ? session->sessionHandle : NULL, &head->nodeId, head->context); UA_LOCK(&server->serviceMutex); return mask; } 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 */ UA_Byte retval = node->accessLevel; UA_UNLOCK(&server->serviceMutex); retval &= server->config.accessControl. getUserAccessLevel(server, &server->config.accessControl, session ? &session->sessionId : NULL, session ? session->sessionHandle : NULL, &node->head.nodeId, node->head.context); UA_LOCK(&server->serviceMutex); return retval; } 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 */ UA_UNLOCK(&server->serviceMutex); UA_Boolean userExecutable = node->executable; userExecutable &= server->config.accessControl. getUserExecutable(server, &server->config.accessControl, session ? &session->sessionId : NULL, session ? session->sessionHandle : NULL, &node->head.nodeId, node->head.context); UA_LOCK(&server->serviceMutex); return userExecutable; } /****************/ /* Read Service */ /****************/ static UA_StatusCode readIsAbstractAttribute(const UA_Node *node, UA_Variant *v) { const UA_Boolean *isAbstract; switch(node->head.nodeClass) { case UA_NODECLASS_REFERENCETYPE: isAbstract = &node->referenceTypeNode.isAbstract; break; case UA_NODECLASS_OBJECTTYPE: isAbstract = &node->objectTypeNode.isAbstract; break; case UA_NODECLASS_VARIABLETYPE: isAbstract = &node->variableTypeNode.isAbstract; break; case UA_NODECLASS_DATATYPE: isAbstract = &node->dataTypeNode.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) { UA_UNLOCK(&server->serviceMutex); vn->value.data.callback.onRead(server, session ? &session->sessionId : NULL, session ? session->sessionHandle : NULL, &vn->head.nodeId, vn->head.context, rangeptr, &vn->value.data.value); UA_LOCK(&server->serviceMutex); vn = (const UA_VariableNode*)UA_NODESTORE_GET(server, &vn->head.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_RELEASE(server, (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_UNLOCK(&server->serviceMutex); UA_StatusCode retval = vn->value.dataSource. read(server, session ? &session->sessionId : NULL, session ? session->sessionHandle : NULL, &vn->head.nodeId, vn->head.context, sourceTimeStamp, rangeptr, &v2); UA_LOCK(&server->serviceMutex); if(v2.hasValue && v2.value.storageType == UA_VARIANT_DATA_NODELETE) { retval = UA_DataValue_copy(&v2, v); UA_DataValue_clear(&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_parse(&range, *indexRange); if(retval != UA_STATUSCODE_GOOD) return retval; rangeptr = ⦥ } switch(vn->valueBackend.backendType) { case UA_VALUEBACKENDTYPE_INTERNAL: retval = readValueAttributeFromNode(server, session, vn, v, rangeptr); //TODO change old structure to value backend break; case UA_VALUEBACKENDTYPE_DATA_SOURCE_CALLBACK: retval = readValueAttributeFromDataSource(server, session, vn, v, timestamps, rangeptr); //TODO change old structure to value backend break; case UA_VALUEBACKENDTYPE_EXTERNAL: if(vn->valueBackend.backend.external.callback.notificationRead){ retval = vn->valueBackend.backend.external.callback. notificationRead(server, session ? &session->sessionId : NULL, session ? session->sessionHandle : NULL, &vn->head.nodeId, vn->head.context, rangeptr); } else { retval = UA_STATUSCODE_BADNOTREADABLE; } if(retval != UA_STATUSCODE_GOOD){ break; } /* Set the result */ if(rangeptr) retval = UA_DataValue_copyVariantRange( *vn->valueBackend.backend.external.value, v, *rangeptr); else retval = UA_DataValue_copy(*vn->valueBackend.backend.external.value, v); break; case UA_VALUEBACKENDTYPE_NONE: /* 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); /* end lagacy */ break; } /* Static Variables and VariableTypes have timestamps of "now". Will be set * below in the absence of predefined timestamps. */ if(vn->head.nodeClass == UA_NODECLASS_VARIABLE) { if(!vn->isDynamic) { v->hasServerTimestamp = false; v->hasSourceTimestamp = false; } } else { v->hasServerTimestamp = false; v->hasSourceTimestamp = false; } /* 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->head.nodeClass & (CLASS))) { \ retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID; \ break; \ } #ifdef UA_ENABLE_TYPEDESCRIPTION static const UA_DataType * findDataType(const UA_Node *node, const UA_DataTypeArray *customTypes) { for(size_t i = 0; i < UA_TYPES_COUNT; ++i) { if(UA_NodeId_equal(&UA_TYPES[i].typeId, &node->head.nodeId)) { return &UA_TYPES[i]; } } // lookup custom type while(customTypes) { for(size_t i = 0; i < customTypes->typesSize; ++i) { if(UA_NodeId_equal(&customTypes->types[i].typeId, &node->head.nodeId)) return &customTypes->types[i]; } customTypes = customTypes->next; } return NULL; } static UA_StatusCode getStructureDefinition(const UA_DataType *type, UA_StructureDefinition *def) { UA_StatusCode retval = UA_NodeId_copy(&type->binaryEncodingId, &def->defaultEncodingId); if(retval != UA_STATUSCODE_GOOD) return retval; switch(type->typeKind) { case UA_DATATYPEKIND_STRUCTURE: def->structureType = UA_STRUCTURETYPE_STRUCTURE; def->baseDataType = UA_NODEID_NUMERIC(0, UA_NS0ID_STRUCTURE); break; case UA_DATATYPEKIND_OPTSTRUCT: def->structureType = UA_STRUCTURETYPE_STRUCTUREWITHOPTIONALFIELDS; def->baseDataType = UA_NODEID_NUMERIC(0, UA_NS0ID_STRUCTURE); break; case UA_DATATYPEKIND_UNION: def->structureType = UA_STRUCTURETYPE_UNION; def->baseDataType = UA_NODEID_NUMERIC(0, UA_NS0ID_UNION); break; default: return UA_STATUSCODE_BADENCODINGERROR; } def->fieldsSize = type->membersSize; def->fields = (UA_StructureField *) UA_calloc(def->fieldsSize, sizeof(UA_StructureField)); if(!def->fields) { UA_NodeId_clear(&def->defaultEncodingId); return UA_STATUSCODE_BADOUTOFMEMORY; } for(size_t cnt = 0; cnt < def->fieldsSize; cnt++) { const UA_DataTypeMember *m = &type->members[cnt]; def->fields[cnt].valueRank = UA_TRUE == m->isArray ? 1 : -1; def->fields[cnt].arrayDimensions = NULL; def->fields[cnt].arrayDimensionsSize = 0; def->fields[cnt].name = UA_STRING((char *)(uintptr_t)m->memberName); def->fields[cnt].description.locale = UA_STRING_NULL; def->fields[cnt].description.text = UA_STRING_NULL; def->fields[cnt].dataType = m->memberType->typeId; def->fields[cnt].maxStringLength = 0; def->fields[cnt].isOptional = m->isOptional; } return UA_STATUSCODE_GOOD; } #endif /* 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_NODEID_DEBUG(&node->head.nodeId, UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Read attribute %"PRIi32 " of Node %.*s", id->attributeId, (int)nodeIdStr.length, nodeIdStr.data)); /* 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->head.nodeId, &UA_TYPES[UA_TYPES_NODEID]); break; case UA_ATTRIBUTEID_NODECLASS: retval = UA_Variant_setScalarCopy(&v->value, &node->head.nodeClass, &UA_TYPES[UA_TYPES_NODECLASS]); break; case UA_ATTRIBUTEID_BROWSENAME: retval = UA_Variant_setScalarCopy(&v->value, &node->head.browseName, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]); break; case UA_ATTRIBUTEID_DISPLAYNAME: retval = UA_Variant_setScalarCopy(&v->value, &node->head.displayName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]); break; case UA_ATTRIBUTEID_DESCRIPTION: retval = UA_Variant_setScalarCopy(&v->value, &node->head.description, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]); break; case UA_ATTRIBUTEID_WRITEMASK: retval = UA_Variant_setScalarCopy(&v->value, &node->head.writeMask, &UA_TYPES[UA_TYPES_UINT32]); break; case UA_ATTRIBUTEID_USERWRITEMASK: { UA_UInt32 userWriteMask = getUserWriteMask(server, session, &node->head); 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, &node->referenceTypeNode.symmetric, &UA_TYPES[UA_TYPES_BOOLEAN]); break; case UA_ATTRIBUTEID_INVERSENAME: CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE); retval = UA_Variant_setScalarCopy(&v->value, &node->referenceTypeNode.inverseName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]); break; case UA_ATTRIBUTEID_CONTAINSNOLOOPS: CHECK_NODECLASS(UA_NODECLASS_VIEW); retval = UA_Variant_setScalarCopy(&v->value, &node->viewNode.containsNoLoops, &UA_TYPES[UA_TYPES_BOOLEAN]); break; case UA_ATTRIBUTEID_EVENTNOTIFIER: CHECK_NODECLASS(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT); if(node->head.nodeClass == UA_NODECLASS_VIEW) { retval = UA_Variant_setScalarCopy(&v->value, &node->viewNode.eventNotifier, &UA_TYPES[UA_TYPES_BYTE]); } else { retval = UA_Variant_setScalarCopy(&v->value, &node->objectNode.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->head.nodeClass == UA_NODECLASS_VARIABLE) { /* The access to a value variable is granted via the AccessLevel * and UserAccessLevel attributes */ UA_Byte accessLevel = getAccessLevel(server, session, &node->variableNode); if(!(accessLevel & (UA_ACCESSLEVELMASK_READ))) { retval = UA_STATUSCODE_BADNOTREADABLE; break; } accessLevel = getUserAccessLevel(server, session, &node->variableNode); if(!(accessLevel & (UA_ACCESSLEVELMASK_READ))) { retval = UA_STATUSCODE_BADUSERACCESSDENIED; break; } } retval = readValueAttributeComplete(server, session, &node->variableNode, timestampsToReturn, &id->indexRange, v); break; } case UA_ATTRIBUTEID_DATATYPE: CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); retval = UA_Variant_setScalarCopy(&v->value, &node->variableTypeNode.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, &node->variableTypeNode.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, node->variableTypeNode.arrayDimensions, node->variableTypeNode.arrayDimensionsSize, &UA_TYPES[UA_TYPES_UINT32]); break; case UA_ATTRIBUTEID_ACCESSLEVEL: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); retval = UA_Variant_setScalarCopy(&v->value, &node->variableNode.accessLevel, &UA_TYPES[UA_TYPES_BYTE]); break; case UA_ATTRIBUTEID_USERACCESSLEVEL: { CHECK_NODECLASS(UA_NODECLASS_VARIABLE); UA_Byte userAccessLevel = getUserAccessLevel(server, session, &node->variableNode); 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, &node->variableNode.minimumSamplingInterval, &UA_TYPES[UA_TYPES_DOUBLE]); break; case UA_ATTRIBUTEID_HISTORIZING: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); retval = UA_Variant_setScalarCopy(&v->value, &node->variableNode.historizing, &UA_TYPES[UA_TYPES_BOOLEAN]); break; case UA_ATTRIBUTEID_EXECUTABLE: CHECK_NODECLASS(UA_NODECLASS_METHOD); retval = UA_Variant_setScalarCopy(&v->value, &node->methodNode.executable, &UA_TYPES[UA_TYPES_BOOLEAN]); break; case UA_ATTRIBUTEID_USEREXECUTABLE: { CHECK_NODECLASS(UA_NODECLASS_METHOD); UA_Boolean userExecutable = getUserExecutable(server, session, &node->methodNode); retval = UA_Variant_setScalarCopy(&v->value, &userExecutable, &UA_TYPES[UA_TYPES_BOOLEAN]); break; } case UA_ATTRIBUTEID_DATATYPEDEFINITION: { CHECK_NODECLASS(UA_NODECLASS_DATATYPE); #ifdef UA_ENABLE_TYPEDESCRIPTION const UA_DataType *type = findDataType(node, server->config.customDataTypes); if(!type) { retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID; break; } if(UA_DATATYPEKIND_STRUCTURE == type->typeKind || UA_DATATYPEKIND_OPTSTRUCT == type->typeKind || UA_DATATYPEKIND_UNION == type->typeKind) { UA_StructureDefinition def; retval = getStructureDefinition(type, &def); if(UA_STATUSCODE_GOOD!=retval) break; retval = UA_Variant_setScalarCopy(&v->value, &def, &UA_TYPES[UA_TYPES_STRUCTUREDEFINITION]); UA_free(def.fields); break; } #endif retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID; break; } default: retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID; } if(retval != UA_STATUSCODE_GOOD) { /* Reading has failed but can not return because we may need to add timestamp */ v->hasStatus = true; v->status = retval; } else { 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_GET(server, &rvi->nodeId); /* Perform the read operation */ if(node) { ReadWithNode(node, server, session, request->timestampsToReturn, rvi, result); UA_NODESTORE_RELEASE(server, 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"); UA_LOCK_ASSERT(&server->serviceMutex, 1); /* 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; } UA_LOCK_ASSERT(&server->serviceMutex, 1); 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_LOCK_ASSERT(&server->serviceMutex, 1); UA_DataValue dv; UA_DataValue_init(&dv); /* Get the node */ const UA_Node *node = UA_NODESTORE_GET(server, &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_RELEASE(server, node); return dv; } UA_DataValue readAttribute(UA_Server *server, const UA_ReadValueId *item, UA_TimestampsToReturn timestamps) { UA_LOCK_ASSERT(&server->serviceMutex, 1); return UA_Server_readWithSession(server, &server->adminSession, item, timestamps); } UA_StatusCode readWithReadValue(UA_Server *server, const UA_NodeId *nodeId, const UA_AttributeId attributeId, void *v) { UA_LOCK_ASSERT(&server->serviceMutex, 1); /* Call the read service */ UA_ReadValueId item; UA_ReadValueId_init(&item); item.nodeId = *nodeId; item.attributeId = attributeId; UA_DataValue dv = readAttribute(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_clear(&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; } /* Exposes the Read service to local users */ UA_DataValue UA_Server_read(UA_Server *server, const UA_ReadValueId *item, UA_TimestampsToReturn timestamps) { UA_LOCK(&server->serviceMutex); UA_DataValue dv = readAttribute(server, item, timestamps); UA_UNLOCK(&server->serviceMutex); return dv; } /* 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) { UA_LOCK(&server->serviceMutex); UA_StatusCode retval = readWithReadValue(server, nodeId, attributeId, v); UA_UNLOCK(&server->serviceMutex); return retval; } UA_StatusCode readObjectProperty(UA_Server *server, const UA_NodeId objectId, const UA_QualifiedName propertyName, UA_Variant *value) { UA_LOCK_ASSERT(&server->serviceMutex, 1); /* Create a BrowsePath to get the target NodeId */ 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 = translateBrowsePathToNodeIds(server, &bp); if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) { retval = bpr.statusCode; UA_BrowsePathResult_clear(&bpr); return retval; } /* Use the first result from the BrowsePath */ retval = readWithReadValue(server, &bpr.targets[0].targetId.nodeId, UA_ATTRIBUTEID_VALUE, value); UA_BrowsePathResult_clear(&bpr); return retval; } UA_StatusCode UA_Server_readObjectProperty(UA_Server *server, const UA_NodeId objectId, const UA_QualifiedName propertyName, UA_Variant *value) { UA_LOCK(&server->serviceMutex); UA_StatusCode retval = readObjectProperty(server, objectId, propertyName, value); UA_UNLOCK(&server->serviceMutex); 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 compatibleValueDataType(UA_Server *server, const UA_DataType *dataType, const UA_NodeId *constraintDataType) { if(compatibleDataTypes(server, &dataType->typeId, constraintDataType)) return true; /* For actual values, the constraint DataType may be a subtype of the * DataType of the value. E.g. UtcTime is subtype of DateTime. But it still * is a DateTime value when transferred over the wire. */ if(isNodeInTree_singleRef(server, constraintDataType, &dataType->typeId, UA_REFERENCETYPEINDEX_HASSUBTYPE)) return true; return false; } UA_Boolean compatibleDataTypes(UA_Server *server, const UA_NodeId *dataType, const UA_NodeId *constraintDataType) { /* Do not allow empty datatypes */ if(UA_NodeId_isNull(dataType)) return false; /* No constraint or Variant / BaseDataType which allows any content */ if(UA_NodeId_isNull(constraintDataType) || UA_NodeId_equal(constraintDataType, &UA_TYPES[UA_TYPES_VARIANT].typeId)) return true; /* Same datatypes */ if(UA_NodeId_equal(dataType, constraintDataType)) return true; /* Is the DataType a subtype of the constraint type? */ if(isNodeInTree_singleRef(server, dataType, constraintDataType, UA_REFERENCETYPEINDEX_HASSUBTYPE)) return true; /* The constraint is an enum -> allow writing Int32 */ if(UA_NodeId_equal(dataType, &UA_TYPES[UA_TYPES_INT32].typeId) && isNodeInTree_singleRef(server, constraintDataType, &enumNodeId, UA_REFERENCETYPEINDEX_HASSUBTYPE)) 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); case UA_VALUERANK_ONE_OR_MORE_DIMENSIONS: return (arrayDims >= 1); 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); } const char *reason_EmptyType = "Empty value only allowed for BaseDataType"; const char *reason_ValueDataType = "DataType of the value is incompatible"; const char *reason_ValueArrayDimensions = "ArrayDimensions of the value are incompatible"; const char *reason_ValueValueRank = "ValueRank of the value is incompatible"; 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, const char **reason) { /* 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; /* Ignore if that is configured */ if(server->bootstrapNS0 || server->config.allowEmptyVariables == UA_RULEHANDLING_ACCEPT) return true; UA_LOG_INFO_SESSION(&server->config.logger, session, "Only Variables with data type BaseDataType " "can contain an empty value"); /* Ignore if that is configured */ if(server->config.allowEmptyVariables == UA_RULEHANDLING_WARN) return true; /* Default handling is to abort */ *reason = reason_EmptyType; return false; } /* Is the datatype compatible? */ if(!compatibleValueDataType(server, value->type, targetDataTypeId)) { *reason = reason_ValueDataType; 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)) { *reason = reason_ValueArrayDimensions; return false; } /* Check if the valuerank allows for the value dimension */ if(!compatibleValueRankValue(targetValueRank, value)) { *reason = reason_ValueValueRank; return false; } return true; } /*****************/ /* Write Service */ /*****************/ void adjustValueType(UA_Server *server, UA_Variant *value, const UA_NodeId *targetDataTypeId) { /* If the value is empty, there is nothing we can do here */ if(!value->type) return; 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 typeKind 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->head.nodeClass == UA_NODECLASS_VARIABLETYPE && UA_Node_hasSubTypeOrInstances(&node->head)) { 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_clear(&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->head.nodeClass == UA_NODECLASS_VARIABLETYPE && UA_Node_hasSubTypeOrInstances(&node->head)) 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_clear(&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->head.nodeClass == UA_NODECLASS_VARIABLETYPE && UA_Node_hasSubTypeOrInstances(&node->head)) return UA_STATUSCODE_BADINTERNALERROR; /* Does the new type match the constraints of the variabletype? */ if(!compatibleDataTypes(server, dataType, &type->dataType)) 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) { const char *reason; /* temp value */ if(!compatibleValue(server, session, dataType, node->valueRank, node->arrayDimensionsSize, node->arrayDimensions, &value.value, NULL, &reason)) retval = UA_STATUSCODE_BADTYPEMISMATCH; UA_DataValue_clear(&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_clear(&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_clear(&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 writeNodeValueAttribute(UA_Server *server, UA_Session *session, UA_VariableNode *node, const UA_DataValue *value, const UA_String *indexRange) { UA_assert(node != NULL); UA_assert(session != NULL); /* Parse the range */ UA_NumericRange range; range.dimensions = NULL; UA_NumericRange *rangeptr = NULL; UA_StatusCode retval = UA_STATUSCODE_GOOD; if(indexRange && indexRange->length > 0) { retval = UA_NumericRange_parse(&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) { adjustValueType(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; const char *reason; if(!compatibleValue(server, session, nodeDataTypePtr, node->valueRank, node->arrayDimensionsSize, node->arrayDimensions, &adjustedValue.value, rangeptr, &reason)) { UA_LOG_NODEID_WARNING(&node->head.nodeId, if(session == &server->adminSession) { /* If the value is written via the local API, log a warning */ UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Writing the value of Node %.*s failed with the " "following reason: %s", (int)nodeIdStr.length, nodeIdStr.data, reason); } else { /* Don't spam the logs if writing from remote failed */ UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Writing the value of Node %.*s failed with the " "following reason: %s", (int)nodeIdStr.length, nodeIdStr.data, reason); }); if(rangeptr && rangeptr->dimensions != NULL) UA_free(rangeptr->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; } /* Update the timestamp when the value was last updated in the server */ adjustedValue.serverTimestamp = now; adjustedValue.hasServerTimestamp = true; switch(node->valueBackend.backendType) { case UA_VALUEBACKENDTYPE_NONE: /* 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->head.nodeClass == UA_NODECLASS_VARIABLE && server->config.historyDatabase.setValue) { UA_UNLOCK(&server->serviceMutex); server->config.historyDatabase. setValue(server, server->config.historyDatabase.context, &session->sessionId, session->sessionHandle, &node->head.nodeId, node->historizing, &adjustedValue); UA_LOCK(&server->serviceMutex); } #endif /* Callback after writing */ if(retval == UA_STATUSCODE_GOOD && node->value.data.callback.onWrite) { UA_UNLOCK(&server->serviceMutex); node->value.data.callback. onWrite(server, &session->sessionId, session->sessionHandle, &node->head.nodeId, node->head.context, rangeptr, &adjustedValue); UA_LOCK(&server->serviceMutex); } } else { if(node->value.dataSource.write) { UA_UNLOCK(&server->serviceMutex); retval = node->value.dataSource. write(server, &session->sessionId, session->sessionHandle, &node->head.nodeId, node->head.context, rangeptr, &adjustedValue); UA_LOCK(&server->serviceMutex); } else { retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; } } break; case UA_VALUEBACKENDTYPE_INTERNAL: break; case UA_VALUEBACKENDTYPE_DATA_SOURCE_CALLBACK: break; case UA_VALUEBACKENDTYPE_EXTERNAL: if(node->valueBackend.backend.external.callback.userWrite == NULL){ if(rangeptr && rangeptr->dimensions != NULL) UA_free(rangeptr->dimensions); return UA_STATUSCODE_BADWRITENOTSUPPORTED; } retval = node->valueBackend.backend.external.callback. userWrite(server, &session->sessionId, session->sessionHandle, &node->head.nodeId, node->head.context, rangeptr, &adjustedValue); break; } /* Clean up */ if(rangeptr && rangeptr->dimensions != NULL) UA_free(rangeptr->dimensions); return retval; } static UA_StatusCode writeIsAbstractAttribute(UA_Node *node, UA_Boolean value) { switch(node->head.nodeClass) { case UA_NODECLASS_OBJECTTYPE: node->objectTypeNode.isAbstract = value; break; case UA_NODECLASS_REFERENCETYPE: node->referenceTypeNode.isAbstract = value; break; case UA_NODECLASS_VARIABLETYPE: node->variableTypeNode.isAbstract = value; break; case UA_NODECLASS_DATATYPE: node->dataTypeNode.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->head.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->head); \ if(!type) { \ retval = UA_STATUSCODE_BADTYPEMISMATCH; \ break; \ } /* Update a localized text. Don't touch the target if copying fails * (maybe due to BadOutOfMemory). */ static UA_StatusCode updateLocalizedText(const UA_LocalizedText *source, UA_LocalizedText *target) { UA_LocalizedText tmp; UA_StatusCode retval = UA_LocalizedText_copy(source, &tmp); if(retval != UA_STATUSCODE_GOOD) return retval; UA_LocalizedText_clear(target); *target = tmp; return UA_STATUSCODE_GOOD; } /* Trigger sampling if a MonitoredItem surveils the attribute with no sampling * interval */ #ifdef UA_ENABLE_SUBSCRIPTIONS static void triggerImmediateDataChange(UA_Server *server, UA_Session *session, UA_Node *node, const UA_WriteValue *wvalue) { UA_MonitoredItem *mon = node->head.monitoredItems; for(; mon != NULL; mon = mon->sampling.nodeListNext) { if(mon->itemToMonitor.attributeId != wvalue->attributeId) continue; UA_DataValue value; UA_DataValue_init(&value); ReadWithNode(node, server, session, mon->timestampsToReturn, &mon->itemToMonitor, &value); UA_Subscription *sub = mon->subscription; UA_StatusCode res = sampleCallbackWithValue(server, sub, mon, &value); if(res != UA_STATUSCODE_GOOD) { UA_DataValue_clear(&value); UA_LOG_WARNING_SUBSCRIPTION(&server->config.logger, sub, "MonitoredItem %" PRIi32 " | " "Sampling returned the statuscode %s", mon->monitoredItemId, UA_StatusCode_name(res)); } } } #endif /* 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) { UA_assert(session != NULL); const void *value = wvalue->value.value.data; UA_UInt32 userWriteMask = getUserWriteMask(server, session, &node->head); UA_StatusCode retval = UA_STATUSCODE_GOOD; UA_LOG_NODEID_DEBUG(&node->head.nodeId, UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Write attribute %"PRIi32 " of Node %.*s", wvalue->attributeId, (int)nodeIdStr.length, nodeIdStr.data)); 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: case UA_ATTRIBUTEID_BROWSENAME: /* BrowseName is tracked in a binary tree for fast lookup */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_DISPLAYNAME: CHECK_USERWRITEMASK(UA_WRITEMASK_DISPLAYNAME); CHECK_DATATYPE_SCALAR(LOCALIZEDTEXT); retval = updateLocalizedText((const UA_LocalizedText *)value, &node->head.displayName); break; case UA_ATTRIBUTEID_DESCRIPTION: CHECK_USERWRITEMASK(UA_WRITEMASK_DESCRIPTION); CHECK_DATATYPE_SCALAR(LOCALIZEDTEXT); retval = updateLocalizedText((const UA_LocalizedText *)value, &node->head.description); break; case UA_ATTRIBUTEID_WRITEMASK: CHECK_USERWRITEMASK(UA_WRITEMASK_WRITEMASK); CHECK_DATATYPE_SCALAR(UINT32); node->head.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); node->referenceTypeNode.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); retval = updateLocalizedText((const UA_LocalizedText *)value, &node->referenceTypeNode.inverseName); break; case UA_ATTRIBUTEID_CONTAINSNOLOOPS: CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW); CHECK_USERWRITEMASK(UA_WRITEMASK_CONTAINSNOLOOPS); CHECK_DATATYPE_SCALAR(BOOLEAN); node->viewNode.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->head.nodeClass == UA_NODECLASS_VIEW) { node->viewNode.eventNotifier = *(const UA_Byte*)value; } else { node->objectNode.eventNotifier = *(const UA_Byte*)value; } break; case UA_ATTRIBUTEID_VALUE: CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); if(node->head.nodeClass == UA_NODECLASS_VARIABLE) { /* The access to a value variable is granted via the AccessLevel * and UserAccessLevel attributes */ UA_Byte accessLevel = getAccessLevel(server, session, &node->variableNode); if(!(accessLevel & (UA_ACCESSLEVELMASK_WRITE))) { retval = UA_STATUSCODE_BADNOTWRITABLE; break; } accessLevel = getUserAccessLevel(server, session, &node->variableNode); if(!(accessLevel & (UA_ACCESSLEVELMASK_WRITE))) { retval = UA_STATUSCODE_BADUSERACCESSDENIED; break; } } else { /* UA_NODECLASS_VARIABLETYPE */ CHECK_USERWRITEMASK(UA_WRITEMASK_VALUEFORVARIABLETYPE); } retval = writeNodeValueAttribute(server, session, &node->variableNode, &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, &node->variableNode, type, (const UA_NodeId*)value); UA_NODESTORE_RELEASE(server, (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, &node->variableNode, type, *(const UA_Int32*)value); UA_NODESTORE_RELEASE(server, (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, &node->variableNode, type, wvalue->value.value.arrayLength, (UA_UInt32 *)wvalue->value.value.data); UA_NODESTORE_RELEASE(server, (const UA_Node*)type); break; case UA_ATTRIBUTEID_ACCESSLEVEL: CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE); CHECK_USERWRITEMASK(UA_WRITEMASK_ACCESSLEVEL); CHECK_DATATYPE_SCALAR(BYTE); node->variableNode.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); node->variableNode.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); node->variableNode.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); node->methodNode.executable = *(const UA_Boolean*)value; break; default: retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID; break; } /* Check if writing succeeded */ if(retval != UA_STATUSCODE_GOOD) { UA_LOG_INFO_SESSION(&server->config.logger, session, "WriteRequest returned status code %s", UA_StatusCode_name(retval)); return retval; } /* Trigger MonitoredItems with no SamplingInterval */ #ifdef UA_ENABLE_SUBSCRIPTIONS triggerImmediateDataChange(server, session, node, wvalue); #endif return UA_STATUSCODE_GOOD; } static void Operation_Write(UA_Server *server, UA_Session *session, void *context, const UA_WriteValue *wv, UA_StatusCode *result) { UA_assert(session != NULL); *result = UA_Server_editNode(server, session, &wv->nodeId, (UA_EditNodeCallback)copyAttributeIntoNode, (void*)(uintptr_t)wv); } void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request, UA_WriteResponse *response) { UA_assert(session != NULL); UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing WriteRequest"); UA_LOCK_ASSERT(&server->serviceMutex, 1); if(server->config.maxNodesPerWrite != 0 && request->nodesToWriteSize > server->config.maxNodesPerWrite) { response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS; return; } UA_LOCK_ASSERT(&server->serviceMutex, 1); 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_write(UA_Server *server, const UA_WriteValue *value) { UA_StatusCode res = UA_STATUSCODE_GOOD; UA_LOCK(&server->serviceMutex); Operation_Write(server, &server->adminSession, NULL, value, &res); UA_UNLOCK(&server->serviceMutex); return res; } /* 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_LOCK(&server->serviceMutex); UA_StatusCode res = writeAttribute(server, &server->adminSession, nodeId, attributeId, attr, attr_type); UA_UNLOCK(&server->serviceMutex); return res; } /* Internal convenience function */ UA_StatusCode writeAttribute(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId, const UA_AttributeId attributeId, const void *attr, const UA_DataType *attr_type) { UA_LOCK_ASSERT(&server->serviceMutex, 1); 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]) { wvalue.value.value = *(const UA_Variant*)attr; } else if(attr_type == &UA_TYPES[UA_TYPES_DATAVALUE]) { wvalue.value = *(const UA_DataValue*)attr; } else { /* hacked cast. the target WriteValue is used as const anyway */ UA_Variant_setScalar(&wvalue.value.value, (void*)(uintptr_t)attr, attr_type); } UA_StatusCode res = UA_STATUSCODE_GOOD; Operation_Write(server, session, NULL, &wvalue, &res); return res; } #ifdef UA_ENABLE_HISTORIZING typedef void (*UA_HistoryDatabase_readFunc)(UA_Server *server, void *hdbContext, const UA_NodeId *sessionId, void *sessionContext, const UA_RequestHeader *requestHeader, const void *historyReadDetails, UA_TimestampsToReturn timestampsToReturn, UA_Boolean releaseContinuationPoints, size_t nodesToReadSize, const UA_HistoryReadValueId *nodesToRead, UA_HistoryReadResponse *response, void * const * const historyData); void Service_HistoryRead(UA_Server *server, UA_Session *session, const UA_HistoryReadRequest *request, UA_HistoryReadResponse *response) { UA_assert(session != NULL); UA_LOCK_ASSERT(&server->serviceMutex, 1); if(server->config.historyDatabase.context == NULL) { response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTSUPPORTED; return; } if(request->historyReadDetails.encoding != UA_EXTENSIONOBJECT_DECODED) { response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTSUPPORTED; return; } const UA_DataType *historyDataType = &UA_TYPES[UA_TYPES_HISTORYDATA]; UA_HistoryDatabase_readFunc readHistory = NULL; if(request->historyReadDetails.content.decoded.type == &UA_TYPES[UA_TYPES_READRAWMODIFIEDDETAILS]) { UA_ReadRawModifiedDetails *details = (UA_ReadRawModifiedDetails*) request->historyReadDetails.content.decoded.data; if(!details->isReadModified) { readHistory = (UA_HistoryDatabase_readFunc) server->config.historyDatabase.readRaw; } else { historyDataType = &UA_TYPES[UA_TYPES_HISTORYMODIFIEDDATA]; readHistory = (UA_HistoryDatabase_readFunc) server->config.historyDatabase.readModified; } } else if(request->historyReadDetails.content.decoded.type == &UA_TYPES[UA_TYPES_READEVENTDETAILS]) { historyDataType = &UA_TYPES[UA_TYPES_HISTORYEVENT]; readHistory = (UA_HistoryDatabase_readFunc) server->config.historyDatabase.readEvent; } else if(request->historyReadDetails.content.decoded.type == &UA_TYPES[UA_TYPES_READPROCESSEDDETAILS]) { readHistory = (UA_HistoryDatabase_readFunc) server->config.historyDatabase.readProcessed; } else if(request->historyReadDetails.content.decoded.type == &UA_TYPES[UA_TYPES_READATTIMEDETAILS]) { readHistory = (UA_HistoryDatabase_readFunc) server->config.historyDatabase.readAtTime; } else { /* TODO handle more request->historyReadDetails.content.decoded.type types */ 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; } /* Allocate a temporary array to forward the result pointers to the * backend */ void **historyData = (void **) UA_calloc(request->nodesToReadSize, sizeof(void*)); 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) { void * data = UA_new(historyDataType); UA_ExtensionObject_setValue(&response->results[i].historyData, data, historyDataType); historyData[i] = data; } UA_UNLOCK(&server->serviceMutex); readHistory(server, server->config.historyDatabase.context, &session->sessionId, session->sessionHandle, &request->requestHeader, request->historyReadDetails.content.decoded.data, request->timestampsToReturn, request->releaseContinuationPoints, request->nodesToReadSize, request->nodesToRead, response, historyData); UA_LOCK(&server->serviceMutex); UA_free(historyData); } void Service_HistoryUpdate(UA_Server *server, UA_Session *session, const UA_HistoryUpdateRequest *request, UA_HistoryUpdateResponse *response) { UA_assert(session != NULL); UA_LOCK_ASSERT(&server->serviceMutex, 1); 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; } const UA_DataType *updateDetailsType = request->historyUpdateDetails[i].content.decoded.type; void *updateDetailsData = request->historyUpdateDetails[i].content.decoded.data; if(updateDetailsType == &UA_TYPES[UA_TYPES_UPDATEDATADETAILS]) { if(!server->config.historyDatabase.updateData) { response->results[i].statusCode = UA_STATUSCODE_BADNOTSUPPORTED; continue; } UA_UNLOCK(&server->serviceMutex); server->config.historyDatabase. updateData(server, server->config.historyDatabase.context, &session->sessionId, session->sessionHandle, &request->requestHeader, (UA_UpdateDataDetails*)updateDetailsData, &response->results[i]); UA_LOCK(&server->serviceMutex); continue; } if(updateDetailsType == &UA_TYPES[UA_TYPES_DELETERAWMODIFIEDDETAILS]) { if(!server->config.historyDatabase.deleteRawModified) { response->results[i].statusCode = UA_STATUSCODE_BADNOTSUPPORTED; continue; } UA_UNLOCK(&server->serviceMutex); server->config.historyDatabase. deleteRawModified(server, server->config.historyDatabase.context, &session->sessionId, session->sessionHandle, &request->requestHeader, (UA_DeleteRawModifiedDetails*)updateDetailsData, &response->results[i]); UA_LOCK(&server->serviceMutex); continue; } response->results[i].statusCode = UA_STATUSCODE_BADNOTSUPPORTED; } } #endif UA_StatusCode UA_Server_writeObjectProperty(UA_Server *server, const UA_NodeId objectId, const UA_QualifiedName propertyName, const UA_Variant value) { UA_LOCK(&server->serviceMutex); UA_StatusCode retVal = writeObjectProperty(server, objectId, propertyName, value); UA_UNLOCK(&server->serviceMutex); return retVal; } UA_StatusCode writeObjectProperty(UA_Server *server, const UA_NodeId objectId, const UA_QualifiedName propertyName, const UA_Variant value) { UA_LOCK_ASSERT(&server->serviceMutex, 1); 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 = translateBrowsePathToNodeIds(server, &bp); if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) { retval = bpr.statusCode; UA_BrowsePathResult_clear(&bpr); return retval; } retval = writeValueAttribute(server, &server->adminSession, &bpr.targets[0].targetId.nodeId, &value); UA_BrowsePathResult_clear(&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); UA_LOCK(&server->serviceMutex); UA_StatusCode retval = writeObjectProperty(server, objectId, propertyName, var); UA_UNLOCK(&server->serviceMutex); return retval; } /**** amalgamated original file "/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"); UA_LOCK_ASSERT(&server->serviceMutex, 1); /* 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 /* Allocate enough memory, including memory for the "self" response */ size_t maxResults = server->discoveryManager.registeredServersSize + 1; response->servers = (UA_ApplicationDescription*)UA_Array_new(maxResults, &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]); if(!response->servers) { response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY; return; } /* Copy into the response. TODO: Evaluate return codes */ size_t pos = 0; if(foundSelf) setApplicationDescriptionFromServer(&response->servers[pos++], server); registeredServer_list_entry* current; LIST_FOREACH(current, &server->discoveryManager.registeredServers, pointers) { UA_Boolean usable = (request->serverUrisSize == 0); if(!usable) { /* 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])) { usable = true; break; } } } if(usable) setApplicationDescriptionFromRegisteredServer(request, &response->servers[pos++], ¤t->registeredServer); } /* Set the final size */ if(pos > 0) { response->serversSize = pos; } else { UA_free(response->servers); response->servers = NULL; } #endif } void Service_GetEndpoints(UA_Server *server, UA_Session *session, const UA_GetEndpointsRequest *request, UA_GetEndpointsResponse *response) { UA_LOCK_ASSERT(&server->serviceMutex, 1); /* 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"); } /* 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; } /* Allocate enough memory */ response->endpoints = (UA_EndpointDescription*) UA_Array_new(server->config.endpointsSize * clone_times, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]); if(!response->endpoints) { response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY; return; } size_t pos = 0; UA_StatusCode retval = UA_STATUSCODE_GOOD; for(size_t j = 0; j < server->config.endpointsSize; ++j) { /* Test if the supported binary profile shall be returned */ UA_Boolean usable = (request->profileUrisSize == 0); if(!usable) { for(size_t i = 0; i < request->profileUrisSize; ++i) { if(!UA_String_equal(&request->profileUris[i], &server->config.endpoints[j].transportProfileUri)) continue; usable = true; break; } } if(!usable) continue; /* Copy into the results */ for(size_t i = 0; i < clone_times; ++i) { retval |= UA_EndpointDescription_copy(&server->config.endpoints[j], &response->endpoints[pos]); UA_String_clear(&response->endpoints[pos].endpointUrl); UA_Array_delete(response->endpoints[pos].server.discoveryUrls, response->endpoints[pos].server.discoveryUrlsSize, &UA_TYPES[UA_TYPES_STRING]); response->endpoints[pos].server.discoveryUrls = NULL; response->endpoints[pos].server.discoveryUrlsSize = 0; if(nl_endpointurl) endpointUrl = &server->config.networkLayers[i].discoveryUrl; retval |= UA_String_copy(endpointUrl, &response->endpoints[pos].endpointUrl); retval |= UA_Array_copy(endpointUrl, 1, (void**)&response->endpoints[pos].server.discoveryUrls, &UA_TYPES[UA_TYPES_STRING]); if(retval != UA_STATUSCODE_GOOD) goto error; response->endpoints[pos].server.discoveryUrlsSize = 1; pos++; } } UA_assert(pos <= server->config.endpointsSize * clone_times); response->endpointsSize = pos; /* Clean up the memory of there are no usable results */ if(pos > 0) 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) { UA_LOCK_ASSERT(&server->serviceMutex, 1); /* 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.mdnsEnabled) { 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) { UA_UNLOCK(&server->serviceMutex); server->discoveryManager. registerServerCallback(requestServer, server->discoveryManager.registerServerCallbackData); UA_LOCK(&server->serviceMutex); } // server found, remove from list LIST_REMOVE(registeredServer_entry, pointers); UA_RegisteredServer_clear(®isteredServer_entry->registeredServer); UA_free(registeredServer_entry); server->discoveryManager.registeredServersSize--; 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); UA_atomic_addSize(&server->discoveryManager.registeredServersSize, 1); } else { UA_RegisteredServer_clear(®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) { UA_UNLOCK(&server->serviceMutex); server->discoveryManager. registerServerCallback(requestServer, server->discoveryManager.registerServerCallbackData); UA_LOCK(&server->serviceMutex); } // 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"); UA_LOCK_ASSERT(&server->serviceMutex, 1); 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"); UA_LOCK_ASSERT(&server->serviceMutex, 1); 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.discoveryCleanupTimeout) timedOut -= server->config.discoveryCleanupTimeout * 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.discoveryCleanupTimeout && 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_clear(¤t->registeredServer); UA_free(current); server->discoveryManager.registeredServersSize--; } } } /* 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); UA_LOCK(&server->serviceMutex); struct PeriodicServerRegisterCallback *cb = (struct PeriodicServerRegisterCallback *)data; UA_StatusCode retval = UA_Client_connectSecureChannel(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 = register_server_with_discovery_server(server, cb->client, false, NULL); if (retval == UA_STATUSCODE_BADCONNECTIONCLOSED) { /* If the periodic interval is higher than the maximum lifetime of * the session, the server will close the connection. In this case * we should try to reconnect */ UA_Client_disconnect(cb->client); retval = UA_Client_connectSecureChannel(cb->client, cb->discovery_server_url); if (retval == UA_STATUSCODE_GOOD) { retval = register_server_with_discovery_server(server, cb->client, false, NULL); } } } /* 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; changeRepeatedCallbackInterval(server, cb->id, nextInterval); UA_UNLOCK(&server->serviceMutex); 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 = 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_UNLOCK(&server->serviceMutex); } UA_StatusCode UA_Server_addPeriodicServerRegisterCallback(UA_Server *server, struct UA_Client *client, const char* discoveryServerUrl, UA_Double intervalMs, UA_Double delayFirstRegisterMs, UA_UInt64 *periodicCallbackId) { UA_LOCK(&server->serviceMutex); /* No valid server URL */ if(!discoveryServerUrl) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "No discovery server URL provided"); UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_BADINTERNALERROR; } if (client->connection.state != UA_CONNECTIONSTATE_CLOSED) { UA_UNLOCK(&server->serviceMutex); 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); removeCallback(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) { UA_UNLOCK(&server->serviceMutex); 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); UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_BADOUTOFMEMORY; } memcpy(cb->discovery_server_url, discoveryServerUrl, len+1); /* Add the callback */ UA_StatusCode retval = 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); UA_UNLOCK(&server->serviceMutex); 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) { removeCallback(server, cb->id); UA_free(cb); UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_BADOUTOFMEMORY; } newEntry->callback = cb; LIST_INSERT_HEAD(&server->discoveryManager.periodicServerRegisterCallbacks, newEntry, pointers); #endif if(periodicCallbackId) *periodicCallbackId = cb->id; UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_GOOD; } void UA_Server_setRegisterServerCallback(UA_Server *server, UA_Server_registerServerCallback cb, void* data) { UA_LOCK(&server->serviceMutex); server->discoveryManager.registerServerCallback = cb; server->discoveryManager.registerServerCallbackData = data; UA_UNLOCK(&server->serviceMutex); } #endif /* UA_ENABLE_DISCOVERY */ /**** amalgamated original file "/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, 2022 (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 * Copyright 2017-2019 (c) HMS Industrial Networks AB (Author: Jonas Green) */ #ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */ static void setPublishingEnabled(UA_Subscription *sub, UA_Boolean publishingEnabled) { if(sub->publishingEnabled == publishingEnabled) return; sub->publishingEnabled = publishingEnabled; #ifdef UA_ENABLE_DIAGNOSTICS if(publishingEnabled) sub->enableCount++; else sub->disableCount++; #endif } static void setSubscriptionSettings(UA_Server *server, UA_Subscription *subscription, UA_Double requestedPublishingInterval, UA_UInt32 requestedLifetimeCount, UA_UInt32 requestedMaxKeepAliveCount, UA_UInt32 maxNotificationsPerPublish, UA_Byte priority) { UA_LOCK_ASSERT(&server->serviceMutex, 1); /* 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; } void Service_CreateSubscription(UA_Server *server, UA_Session *session, const UA_CreateSubscriptionRequest *request, UA_CreateSubscriptionResponse *response) { UA_LOCK_ASSERT(&server->serviceMutex, 1); /* Check limits for the number of subscriptions */ if(((server->config.maxSubscriptions != 0) && (server->subscriptionsSize >= server->config.maxSubscriptions)) || ((server->config.maxSubscriptionsPerSession != 0) && (session->subscriptionsSize >= server->config.maxSubscriptionsPerSession))) { response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYSUBSCRIPTIONS; return; } /* Create the subscription */ UA_Subscription *sub= UA_Subscription_new(); if(!sub) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing CreateSubscriptionRequest failed"); response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY; return; } /* Set the subscription parameters */ setSubscriptionSettings(server, sub, request->requestedPublishingInterval, request->requestedLifetimeCount, request->requestedMaxKeepAliveCount, request->maxNotificationsPerPublish, request->priority); setPublishingEnabled(sub, request->publishingEnabled); sub->currentKeepAliveCount = sub->maxKeepAliveCount; /* set settings first */ /* Assign the SubscriptionId */ sub->subscriptionId = ++server->lastSubscriptionId; /* Register the cyclic callback */ UA_StatusCode retval = Subscription_registerPublishCallback(server, sub); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_DEBUG_SESSION(&server->config.logger, sub->session, "Subscription %" PRIu32 " | " "Could not register publish callback with error code %s", sub->subscriptionId, UA_StatusCode_name(retval)); response->responseHeader.serviceResult = retval; UA_Subscription_delete(server, sub); return; } /* Register the subscription in the server */ LIST_INSERT_HEAD(&server->subscriptions, sub, serverListEntry); server->subscriptionsSize++; /* Update the server statistics */ server->serverDiagnosticsSummary.currentSubscriptionCount++; server->serverDiagnosticsSummary.cumulatedSubscriptionCount++; /* Attach the Subscription to the session */ UA_Session_attachSubscription(session, sub); /* Prepare the response */ response->subscriptionId = sub->subscriptionId; response->revisedPublishingInterval = sub->publishingInterval; response->revisedLifetimeCount = sub->lifeTimeCount; response->revisedMaxKeepAliveCount = sub->maxKeepAliveCount; #ifdef UA_ENABLE_DIAGNOSTICS createSubscriptionObject(server, session, sub); #endif UA_LOG_INFO_SUBSCRIPTION(&server->config.logger, sub, "Subscription created (Publishing interval %.2fms, " "max %lu notifications per publish)", sub->publishingInterval, (long unsigned)sub->notificationsPerPublish);} 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_LOCK_ASSERT(&server->serviceMutex, 1); UA_Subscription *sub = UA_Session_getSubscriptionById(session, request->subscriptionId); if(!sub) { response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; return; } /* Store the old publishing interval */ UA_Double oldPublishingInterval = sub->publishingInterval; UA_Byte oldPriority = sub->priority; /* Change the Subscription settings */ setSubscriptionSettings(server, sub, request->requestedPublishingInterval, request->requestedLifetimeCount, request->requestedMaxKeepAliveCount, request->maxNotificationsPerPublish, request->priority); /* Reset the subscription lifetime */ sub->currentLifetimeCount = 0; /* Change the repeated callback to the new interval. This cannot fail as the * CallbackId must exist. */ if(sub->publishCallbackId > 0 && sub->publishingInterval != oldPublishingInterval) changeRepeatedCallbackInterval(server, sub->publishCallbackId, sub->publishingInterval); /* If the priority has changed, re-enter the subscription to the * priority-ordered queue in the session. */ if(oldPriority != sub->priority) { UA_Session_detachSubscription(server, session, sub, false); UA_Session_attachSubscription(session, sub); } /* Set the response */ response->revisedPublishingInterval = sub->publishingInterval; response->revisedLifetimeCount = sub->lifeTimeCount; response->revisedMaxKeepAliveCount = sub->maxKeepAliveCount; /* Update the diagnostics statistics */ #ifdef UA_ENABLE_DIAGNOSTICS sub->modifyCount++; #endif } static void Operation_SetPublishingMode(UA_Server *server, UA_Session *session, const UA_Boolean *publishingEnabled, const UA_UInt32 *subscriptionId, UA_StatusCode *result) { UA_LOCK_ASSERT(&server->serviceMutex, 1); UA_Subscription *sub = UA_Session_getSubscriptionById(session, *subscriptionId); if(!sub) { *result = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; return; } sub->currentLifetimeCount = 0; /* Reset the subscription lifetime */ setPublishingEnabled(sub, *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_LOCK_ASSERT(&server->serviceMutex, 1); 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]); } UA_StatusCode 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"); UA_LOCK_ASSERT(&server->serviceMutex, 1); /* Return an error if the session has no subscription */ if(TAILQ_EMPTY(&session->subscriptions)) { sendServiceFault(session->header.channel, requestId, request->requestHeader.requestHandle, UA_STATUSCODE_BADNOSUBSCRIPTION); return UA_STATUSCODE_BADNOSUBSCRIPTION; } /* 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->responseQueueSize >= server->config.maxPublishReqPerSession)) { if(!UA_Session_reachedPublishReqLimit(server, session)) { sendServiceFault(session->header.channel, requestId, request->requestHeader.requestHandle, UA_STATUSCODE_BADINTERNALERROR); return UA_STATUSCODE_BADINTERNALERROR; } } /* Allocate the response to store it in the retransmission queue */ UA_PublishResponseEntry *entry = (UA_PublishResponseEntry *) UA_malloc(sizeof(UA_PublishResponseEntry)); if(!entry) { sendServiceFault(session->header.channel, requestId, request->requestHeader.requestHandle, UA_STATUSCODE_BADOUTOFMEMORY); return UA_STATUSCODE_BADOUTOFMEMORY; } /* 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); sendServiceFault(session->header.channel, requestId, request->requestHeader.requestHandle, UA_STATUSCODE_BADOUTOFMEMORY); return UA_STATUSCODE_BADOUTOFMEMORY; } response->resultsSize = request->subscriptionAcknowledgementsSize; } /* <--- A good StatusCode is returned from here on ---> */ /* 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" PRIu32, 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 we still had publish requests in the queue, none of the subscriptions * is late. So we don't have to search for one. */ if(session->responseQueueSize > 1) { return UA_STATUSCODE_GOOD; } /* If there are late subscriptions, the new publish request is used to * answer them immediately. Late subscriptions with higher priority are * considered earlier. However, a single subscription that generates many * notifications must not "starve" other late subscriptions. Hence we move * it to the end of the queue for the subscriptions of that priority. */ UA_Subscription *late, *late_tmp; TAILQ_FOREACH_SAFE(late, &session->subscriptions, sessionListEntry, late_tmp) { if(late->state != UA_SUBSCRIPTIONSTATE_LATE) continue; UA_LOG_DEBUG_SUBSCRIPTION(&server->config.logger, late, "Send PublishResponse on a late subscription"); UA_Subscription_publishOnce(server, late); /* Skip re-insert if the subscription was deleted during publishOnce */ if(late->session) { /* Find the first element with smaller priority and insert before * that. If there is none, insert at the end of the queue. */ UA_Subscription *after = TAILQ_NEXT(late, sessionListEntry); while(after && after->priority >= late->priority) after = TAILQ_NEXT(after, sessionListEntry); TAILQ_REMOVE(&session->subscriptions, late, sessionListEntry); if(after) TAILQ_INSERT_BEFORE(after, late, sessionListEntry); else TAILQ_INSERT_TAIL(&session->subscriptions, late, sessionListEntry); } /* In case of an error we might not have used the publish request that * was just enqueued. Continue to find late subscriptions in that * case. */ if(session->responseQueueSize == 0) break; } return UA_STATUSCODE_GOOD; } static void Operation_DeleteSubscription(UA_Server *server, UA_Session *session, void *_, const UA_UInt32 *subscriptionId, UA_StatusCode *result) { UA_Subscription *sub = UA_Session_getSubscriptionById(session, *subscriptionId); if(!sub) { *result = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Deleting Subscription with Id %" PRIu32 " failed with error code %s", *subscriptionId, UA_StatusCode_name(*result)); return; } UA_Subscription_delete(server, sub); *result = UA_STATUSCODE_GOOD; UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Subscription %" PRIu32 " | Subscription deleted", *subscriptionId); } 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"); UA_LOCK_ASSERT(&server->serviceMutex, 1); 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]); } 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"); UA_LOCK_ASSERT(&server->serviceMutex, 1); /* 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; /* Update the subscription statistics */ #ifdef UA_ENABLE_DIAGNOSTICS sub->republishRequestCount++; #endif /* 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); /* Update the subscription statistics for the case where we return a message */ #ifdef UA_ENABLE_DIAGNOSTICS sub->republishMessageCount++; #endif } static UA_StatusCode setTransferredSequenceNumbers(const UA_Subscription *sub, UA_TransferResult *result) { /* Allocate memory */ result->availableSequenceNumbers = (UA_UInt32*) UA_Array_new(sub->retransmissionQueueSize, &UA_TYPES[UA_TYPES_UINT32]); if(!result->availableSequenceNumbers) return UA_STATUSCODE_BADOUTOFMEMORY; result->availableSequenceNumbersSize = sub->retransmissionQueueSize; /* Copy over the sequence numbers */ UA_NotificationMessageEntry *entry; size_t i = 0; TAILQ_FOREACH(entry, &sub->retransmissionQueue, listEntry) { result->availableSequenceNumbers[i] = entry->message.sequenceNumber; i++; } UA_assert(i == result->availableSequenceNumbersSize); return UA_STATUSCODE_GOOD; } static void Operation_TransferSubscription(UA_Server *server, UA_Session *session, const UA_Boolean *sendInitialValues, const UA_UInt32 *subscriptionId, UA_TransferResult *result) { /* Get the subscription. This requires a server-wide lookup instead of the * usual session-wide lookup. */ UA_Subscription *sub = UA_Server_getSubscriptionById(server, *subscriptionId); if(!sub) { result->statusCode = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; return; } /* Update the diagnostics statistics */ #ifdef UA_ENABLE_DIAGNOSTICS sub->transferRequestCount++; #endif /* Is this the same session? Return the sequence numbers and do nothing else. */ UA_Session *oldSession = sub->session; if(oldSession == session) { result->statusCode = setTransferredSequenceNumbers(sub, result); #ifdef UA_ENABLE_DIAGNOSTICS sub->transferredToSameClientCount++; #endif return; } /* Check with AccessControl if the transfer is allowed */ if(!server->config.accessControl.allowTransferSubscription || !server->config.accessControl. allowTransferSubscription(server, &server->config.accessControl, oldSession ? &oldSession->sessionId : NULL, oldSession ? oldSession->sessionHandle : NULL, &session->sessionId, session->sessionHandle)) { result->statusCode = UA_STATUSCODE_BADUSERACCESSDENIED; return; } /* Check limits for the number of subscriptions for this Session */ if((server->config.maxSubscriptionsPerSession != 0) && (session->subscriptionsSize >= server->config.maxSubscriptionsPerSession)) { result->statusCode = UA_STATUSCODE_BADTOOMANYSUBSCRIPTIONS; return; } /* Allocate memory for the new subscription */ UA_Subscription *newSub = (UA_Subscription*)UA_malloc(sizeof(UA_Subscription)); if(!newSub) { result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY; return; } /* Set the available sequence numbers */ result->statusCode = setTransferredSequenceNumbers(sub, result); if(result->statusCode != UA_STATUSCODE_GOOD) { UA_free(newSub); return; } /* Create an identical copy of the Subscription struct. The original * subscription remains in place until a StatusChange notification has been * sent. The elements for lists and queues are moved over manually to ensure * that all backpointers are set correctly. */ memcpy(newSub, sub, sizeof(UA_Subscription)); /* Register cyclic publish callback */ result->statusCode = Subscription_registerPublishCallback(server, newSub); if(result->statusCode != UA_STATUSCODE_GOOD) { UA_Array_delete(result->availableSequenceNumbers, sub->retransmissionQueueSize, &UA_TYPES[UA_TYPES_UINT32]); result->availableSequenceNumbers = NULL; result->availableSequenceNumbersSize = 0; UA_free(newSub); return; } /* <-- The point of no return --> */ /* Move over the MonitoredItems and adjust the backpointers */ LIST_INIT(&newSub->monitoredItems); UA_MonitoredItem *mon, *mon_tmp; LIST_FOREACH_SAFE(mon, &sub->monitoredItems, listEntry, mon_tmp) { LIST_REMOVE(mon, listEntry); mon->subscription = newSub; LIST_INSERT_HEAD(&newSub->monitoredItems, mon, listEntry); } sub->monitoredItemsSize = 0; /* Move over the notification queue */ TAILQ_INIT(&newSub->notificationQueue); UA_Notification *nn, *nn_tmp; TAILQ_FOREACH_SAFE(nn, &sub->notificationQueue, globalEntry, nn_tmp) { TAILQ_REMOVE(&sub->notificationQueue, nn, globalEntry); TAILQ_INSERT_TAIL(&newSub->notificationQueue, nn, globalEntry); } sub->notificationQueueSize = 0; sub->dataChangeNotifications = 0; sub->eventNotifications = 0; TAILQ_INIT(&newSub->retransmissionQueue); UA_NotificationMessageEntry *nme, *nme_tmp; TAILQ_FOREACH_SAFE(nme, &sub->retransmissionQueue, listEntry, nme_tmp) { TAILQ_REMOVE(&sub->retransmissionQueue, nme, listEntry); TAILQ_INSERT_TAIL(&newSub->retransmissionQueue, nme, listEntry); if(oldSession) oldSession->totalRetransmissionQueueSize -= 1; sub->retransmissionQueueSize -= 1; } UA_assert(sub->retransmissionQueueSize == 0); sub->retransmissionQueueSize = 0; /* Add to the server */ UA_assert(newSub->subscriptionId == sub->subscriptionId); LIST_INSERT_HEAD(&server->subscriptions, newSub, serverListEntry); server->subscriptionsSize++; /* Attach to the session */ UA_Session_attachSubscription(session, newSub); UA_LOG_INFO_SUBSCRIPTION(&server->config.logger, newSub, "Transferred to this Session"); /* Set StatusChange in the original subscription and force publish. This * also removes the Subscription, even if there was no PublishResponse * queued to send a StatusChangeNotification. */ sub->statusChange = UA_STATUSCODE_GOODSUBSCRIPTIONTRANSFERRED; UA_Subscription_publish(server, sub); /* The original subscription has been deactivated */ UA_assert(sub->publishCallbackId == 0); /* Re-create notifications with the current values for the new subscription */ if(*sendInitialValues) { LIST_FOREACH(mon, &newSub->monitoredItems, listEntry) { /* Create only DataChange notifications */ if(mon->itemToMonitor.attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) continue; /* Only if the mode is monitoring */ if(mon->monitoringMode != UA_MONITORINGMODE_REPORTING) continue; /* If a value is queued for a data MonitoredItem, the next value in * the queue is sent in the Publish response. */ if(mon->queueSize > 0) continue; /* Create a notification with the last sampled value */ UA_MonitoredItem_createDataChangeNotification(server, newSub, mon, &mon->lastValue); } } /* Do not update the statistics for the number of Subscriptions here. The * fact that we duplicate the subscription and move over the content is just * an implementtion detail. * server->serverDiagnosticsSummary.currentSubscriptionCount++; * server->serverDiagnosticsSummary.cumulatedSubscriptionCount++; * * Update the diagnostics statistics: */ #ifdef UA_ENABLE_DIAGNOSTICS if(oldSession && UA_order(&oldSession->clientDescription, &session->clientDescription, &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]) == UA_ORDER_EQ) sub->transferredToSameClientCount++; else sub->transferredToAltClientCount++; #endif /* Immediately try to publish on the new Subscription. This might put it * into the "late subscription" mode. */ UA_Subscription_publish(server, newSub); } void Service_TransferSubscriptions(UA_Server *server, UA_Session *session, const UA_TransferSubscriptionsRequest *request, UA_TransferSubscriptionsResponse *response) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing TransferSubscriptionsRequest"); UA_LOCK_ASSERT(&server->serviceMutex, 1); response->responseHeader.serviceResult = UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_TransferSubscription, &request->sendInitialValues, &request->subscriptionIdsSize, &UA_TYPES[UA_TYPES_UINT32], &response->resultsSize, &UA_TYPES[UA_TYPES_TRANSFERRESULT]); } #endif /* UA_ENABLE_SUBSCRIPTIONS */ /**** amalgamated original file "/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-2023 (c) Thomas Stalder, Blue Time Concept SA * Copyright 2018 (c) Fabian Arndt, Root-Core * Copyright 2020 (c) Kalycito Infotech Private Limited * Copyright 2021 (c) Uranik, Berisha * Copyright 2021 (c) Ammar, Morshed * Copyright 2021 (c) Fraunhofer IOSB (Author: Andreas Ebner) */ #ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */ #ifdef UA_ENABLE_DA /* Translate a percentage deadband into an absolute deadband based on the * UARange property of the variable */ static UA_StatusCode setAbsoluteFromPercentageDeadband(UA_Server *server, UA_Session *session, const UA_MonitoredItem *mon, UA_DataChangeFilter *filter) { /* A valid deadband? */ if(filter->deadbandValue < 0.0 || filter->deadbandValue > 100.0) return UA_STATUSCODE_BADDEADBANDFILTERINVALID; /* Browse for the percent range */ UA_QualifiedName qn = UA_QUALIFIEDNAME(0, "EURange"); UA_BrowsePathResult bpr = browseSimplifiedBrowsePath(server, mon->itemToMonitor.nodeId, 1, &qn); if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) { UA_BrowsePathResult_clear(&bpr); return UA_STATUSCODE_BADFILTERNOTALLOWED; } /* Read the range */ UA_ReadValueId rvi; UA_ReadValueId_init(&rvi); rvi.nodeId = bpr.targets->targetId.nodeId; rvi.attributeId = UA_ATTRIBUTEID_VALUE; UA_DataValue rangeVal = UA_Server_readWithSession(server, session, &rvi, UA_TIMESTAMPSTORETURN_NEITHER); UA_BrowsePathResult_clear(&bpr); if(!UA_Variant_isScalar(&rangeVal.value) || rangeVal.value.type != &UA_TYPES[UA_TYPES_RANGE]) { UA_DataValue_clear(&rangeVal); return UA_STATUSCODE_BADFILTERNOTALLOWED; } /* Compute the abs deadband */ UA_Range *euRange = (UA_Range*)rangeVal.value.data; UA_Double absDeadband = (filter->deadbandValue/100.0) * (euRange->high - euRange->low); UA_DataValue_clear(&rangeVal); /* EURange invalid or NaN? */ if(absDeadband < 0.0 || absDeadband != absDeadband) { UA_DataValue_clear(&rangeVal); return UA_STATUSCODE_BADFILTERNOTALLOWED; } /* Adjust the original filter */ filter->deadbandType = UA_DEADBANDTYPE_ABSOLUTE; filter->deadbandValue = absDeadband; return UA_STATUSCODE_GOOD; } #endif /* UA_ENABLE_DA */ void Service_SetTriggering(UA_Server *server, UA_Session *session, const UA_SetTriggeringRequest *request, UA_SetTriggeringResponse *response) { /* Nothing to do? */ if(request->linksToRemoveSize == 0 && request->linksToAddSize == 0) { response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO; return; } /* Get the Subscription */ UA_Subscription *sub = UA_Session_getSubscriptionById(session, request->subscriptionId); if(!sub) { response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; return; } /* Get the MonitoredItem */ UA_MonitoredItem *mon = UA_Subscription_getMonitoredItem(sub, request->triggeringItemId); if(!mon) { response->responseHeader.serviceResult = UA_STATUSCODE_BADMONITOREDITEMIDINVALID; return; } /* Allocate the results arrays */ if(request->linksToRemoveSize > 0) { response->removeResults = (UA_StatusCode*) UA_Array_new(request->linksToRemoveSize, &UA_TYPES[UA_TYPES_STATUSCODE]); if(!response->removeResults) { response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY; return; } response->removeResultsSize = request->linksToRemoveSize; } if(request->linksToAddSize> 0) { response->addResults = (UA_StatusCode*) UA_Array_new(request->linksToAddSize, &UA_TYPES[UA_TYPES_STATUSCODE]); if(!response->addResults) { UA_Array_delete(response->removeResults, request->linksToAddSize, &UA_TYPES[UA_TYPES_STATUSCODE]); response->removeResults = NULL; response->removeResultsSize = 0; response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY; return; } response->addResultsSize = request->linksToAddSize; } /* Apply the changes */ for(size_t i = 0; i < request->linksToRemoveSize; i++) response->removeResults[i] = UA_MonitoredItem_removeLink(sub, mon, request->linksToRemove[i]); for(size_t i = 0; i < request->linksToAddSize; i++) response->addResults[i] = UA_MonitoredItem_addLink(sub, mon, request->linksToAdd[i]); } /* Verify and adjust the parameters of a MonitoredItem */ static UA_StatusCode checkAdjustMonitoredItemParams(UA_Server *server, UA_Session *session, const UA_MonitoredItem *mon, const UA_DataType* valueType, UA_MonitoringParameters *params) { UA_LOCK_ASSERT(&server->serviceMutex, 1); /* Check the filter */ if(mon->itemToMonitor.attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) { /* Event MonitoredItems need a filter */ #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; #endif } else { /* DataChange MonitoredItem. Can be "no filter" which defaults to * triggering on Status and Value. */ if(params->filter.encoding != UA_EXTENSIONOBJECT_DECODED && params->filter.encoding != UA_EXTENSIONOBJECT_DECODED_NODELETE && params->filter.encoding != UA_EXTENSIONOBJECT_ENCODED_NOBODY) return UA_STATUSCODE_BADMONITOREDITEMFILTERUNSUPPORTED; /* If the filter ExtensionObject has a body, then it must be a * DataChangeFilter */ if(params->filter.encoding != UA_EXTENSIONOBJECT_ENCODED_NOBODY && params->filter.content.decoded.type != &UA_TYPES[UA_TYPES_DATACHANGEFILTER]) return UA_STATUSCODE_BADFILTERNOTALLOWED; /* Check the deadband and adjust if necessary. */ if(params->filter.content.decoded.type == &UA_TYPES[UA_TYPES_DATACHANGEFILTER]) { UA_DataChangeFilter *filter = (UA_DataChangeFilter *) params->filter.content.decoded.data; switch(filter->deadbandType) { case UA_DEADBANDTYPE_NONE: break; case UA_DEADBANDTYPE_ABSOLUTE: if(mon->itemToMonitor.attributeId != UA_ATTRIBUTEID_VALUE || !valueType || !UA_DataType_isNumeric(valueType)) return UA_STATUSCODE_BADFILTERNOTALLOWED; break; #ifdef UA_ENABLE_DA case UA_DEADBANDTYPE_PERCENT: { if(mon->itemToMonitor.attributeId != UA_ATTRIBUTEID_VALUE || !valueType || !UA_DataType_isNumeric(valueType)) return UA_STATUSCODE_BADFILTERNOTALLOWED; /* If percentage deadband is supported, look up the range values * and precompute as if it was an absolute deadband. */ UA_StatusCode res = setAbsoluteFromPercentageDeadband(server, session, mon, filter); if(res != UA_STATUSCODE_GOOD) return res; break; } #endif default: return UA_STATUSCODE_BADMONITOREDITEMFILTERUNSUPPORTED; } } } /* Read the minimum sampling interval for the variable. The sampling * interval of the MonitoredItem must not be less than that. */ if(mon->itemToMonitor.attributeId == UA_ATTRIBUTEID_VALUE) { const UA_Node *node = UA_NODESTORE_GET(server, &mon->itemToMonitor.nodeId); if(node) { const UA_VariableNode *vn = &node->variableNode; if(node->head.nodeClass == UA_NODECLASS_VARIABLE) { /* Take into account if the publishing interval is used for sampling */ UA_Double samplingInterval = params->samplingInterval; if(samplingInterval < 0 && mon->subscription) samplingInterval = mon->subscription->publishingInterval; /* Adjust if smaller than the allowed minimum for the variable */ if(samplingInterval < vn->minimumSamplingInterval) params->samplingInterval = vn->minimumSamplingInterval; } UA_NODESTORE_RELEASE(server, node); } } /* Adjust sampling interval */ if(params->samplingInterval < 0.0) { /* A negative number indicates that the sampling interval is the * publishing interval of the Subscription. */ if(!mon->subscription) { /* Not possible for local MonitoredItems */ params->samplingInterval = server->config.samplingIntervalLimits.min; } else { /* Test if the publishing interval is a valid sampling interval. If * not, adjust to lie within the limits. */ UA_BOUNDEDVALUE_SETWBOUNDS(server->config.samplingIntervalLimits, mon->subscription->publishingInterval, params->samplingInterval); if(params->samplingInterval == mon->subscription->publishingInterval) { /* The publishing interval is valid also for sampling. Set sampling * interval to -1 to sample the monitored item in the publish * callback. The revised sampling interval of the response will be * set to the publishing interval.*/ params->samplingInterval = -1.0; } } } else { /* Adjust positive sampling interval to lie within the limits */ UA_BOUNDEDVALUE_SETWBOUNDS(server->config.samplingIntervalLimits, params->samplingInterval, params->samplingInterval); /* Check for NaN */ if(mon->parameters.samplingInterval != mon->parameters.samplingInterval) params->samplingInterval = server->config.samplingIntervalLimits.min; } /* Adjust the maximum queue size */ #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS if(mon->itemToMonitor.attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) { /* 0 => Set to the configured maximum. Otherwise adjust with configured limits */ if(params->queueSize == 0) { params->queueSize = server->config.queueSizeLimits.max; } else { UA_BOUNDEDVALUE_SETWBOUNDS(server->config.queueSizeLimits, params->queueSize, params->queueSize); } } else #endif { /* 0 or 1 => queue-size 1. Otherwise adjust with configured limits */ if(params->queueSize == 0) params->queueSize = 1; if(params->queueSize != 1) UA_BOUNDEDVALUE_SETWBOUNDS(server->config.queueSizeLimits, params->queueSize, params->queueSize); } return UA_STATUSCODE_GOOD; } #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS static UA_StatusCode checkEventFilterParam(UA_Server *server, UA_Session *session, const UA_MonitoredItem *mon, UA_MonitoringParameters *params, UA_MonitoredItemCreateResult *result) { if(mon->itemToMonitor.attributeId != UA_ATTRIBUTEID_EVENTNOTIFIER) return UA_STATUSCODE_GOOD; UA_EventFilter *eventFilter = (UA_EventFilter *) params->filter.content.decoded.data; if(eventFilter == NULL) return UA_STATUSCODE_BADEVENTFILTERINVALID; //TODO make the maximum select clause size param an server-config parameter if(eventFilter->selectClausesSize > 128) return UA_STATUSCODE_BADCONFIGURATIONERROR; UA_ContentFilterResult contentFilterResult; UA_Event_staticWhereClauseValidation(server, &eventFilter->whereClause, &contentFilterResult); UA_StatusCode selectClauseValidationResult[128]; UA_Event_staticSelectClauseValidation(server,eventFilter, selectClauseValidationResult); //check the where clause for logical consistency UA_StatusCode res = UA_STATUSCODE_GOOD; for(size_t i = 0; i < contentFilterResult.elementResultsSize; ++i) { if(contentFilterResult.elementResults[i].statusCode != UA_STATUSCODE_GOOD){ //ToDo currently we return the first non good status code, check if //we can use the detailed contentFilterResult later res = contentFilterResult.elementResults[i].statusCode; break; } } //check the select clause for logical consistency for(size_t i = 0; i < eventFilter->selectClausesSize; ++i){ //ToDo currently we return the first non good status code, check if //we can use the detailed status code list later if(selectClauseValidationResult[i] != UA_STATUSCODE_GOOD){ res = selectClauseValidationResult[i]; break; } } if(res == UA_STATUSCODE_GOOD) { UA_ContentFilterResult_clear(&contentFilterResult); return res; } /* Create the EventFilterResult output */ UA_EventFilterResult *efr = UA_EventFilterResult_new(); if(!efr) { UA_ContentFilterResult_clear(&contentFilterResult); return UA_STATUSCODE_BADOUTOFMEMORY; } efr->whereClauseResult = contentFilterResult; /* UA_ContentFilterResult_init(&contentFilterResult); */ efr->selectClauseResults = (UA_StatusCode*) UA_Array_new(eventFilter->selectClausesSize, &UA_TYPES[UA_TYPES_STATUSCODE]); if(!efr->selectClauseResults) { UA_EventFilterResult_delete(efr); return UA_STATUSCODE_BADOUTOFMEMORY; } efr->selectClauseResultsSize = eventFilter->selectClausesSize; memcpy(efr->selectClauseResults, selectClauseValidationResult, sizeof(UA_StatusCode) * efr->selectClauseResultsSize); UA_ExtensionObject_setValue(&result->filterResult, efr, &UA_TYPES[UA_TYPES_EVENTFILTERRESULT]); return res; } #endif static const UA_String binaryEncoding = {sizeof("Default Binary") - 1, (UA_Byte *)"Default Binary"}; /* Structure 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) { UA_LOCK_ASSERT(&server->serviceMutex, 1); /* Check available capacity */ if(cmc->sub && (((server->config.maxMonitoredItems != 0) && (server->monitoredItemsSize >= server->config.maxMonitoredItems)) || ((server->config.maxMonitoredItemsPerSubscription != 0) && (cmc->sub->monitoredItemsSize >= server->config.maxMonitoredItemsPerSubscription)))) { result->statusCode = UA_STATUSCODE_BADTOOMANYMONITOREDITEMS; 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; 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; return; } /* Make an example read to check the itemToMonitor. The DataSource itself * could return a (temporary) error. This should still result in a valid * MonitoredItem. Only a few StatusCodes are considered unrecoverable and * lead to an abort: * - The Node does not exist * - The AttributeId does not match the NodeClass * - The Session does not have sufficient access rights * - The indicated encoding is not supported or not valid */ UA_DataValue v = UA_Server_readWithSession(server, session, &request->itemToMonitor, cmc->timestampsToReturn); if(v.hasStatus && (v.status == UA_STATUSCODE_BADNODEIDUNKNOWN || v.status == UA_STATUSCODE_BADATTRIBUTEIDINVALID || v.status == UA_STATUSCODE_BADDATAENCODINGUNSUPPORTED || v.status == UA_STATUSCODE_BADDATAENCODINGINVALID || v.status == UA_STATUSCODE_BADINDEXRANGEINVALID /* Part 4, 5.12.2 CreateMonitoredItems: When a user adds a monitored * item that the user is denied read access to, the add operation for * the item shall succeed and the bad status Bad_NotReadable or * Bad_UserAccessDenied shall be returned in the Publish response. * v.status == UA_STATUSCODE_BADNOTREADABLE * v.status == UA_STATUSCODE_BADUSERACCESSDENIED * * The IndexRange error can change depending on the value. * v.status == UA_STATUSCODE_BADINDEXRANGENODATA */ )) { result->statusCode = v.status; UA_DataValue_clear(&v); return; } /* Adding an Event MonitoredItem */ #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS if(request->itemToMonitor.attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) { /* TODO: Only remote clients can add Event-MonitoredItems at the moment */ if(!cmc->sub) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Only remote clients can add Event-MonitoredItems"); result->statusCode = UA_STATUSCODE_BADNOTSUPPORTED; UA_DataValue_clear(&v); return; } /* If the 'SubscribeToEvents' bit of EventNotifier attribute is * zero, then the object cannot be subscribed to monitor events */ if(!v.hasValue || !v.value.data) { result->statusCode = UA_STATUSCODE_BADINTERNALERROR; UA_DataValue_clear(&v); return; } UA_Byte eventNotifierValue = *((UA_Byte *)v.value.data); if((eventNotifierValue & 0x01) != 1) { result->statusCode = UA_STATUSCODE_BADNOTSUPPORTED; UA_LOG_INFO_SUBSCRIPTION(&server->config.logger, cmc->sub, "Could not create a MonitoredItem as the " "'SubscribeToEvents' bit of the EventNotifier " "attribute is not set"); UA_DataValue_clear(&v); return; } } #endif const UA_DataType *valueType = v.value.type; UA_DataValue_clear(&v); /* Allocate the MonitoredItem */ UA_MonitoredItem *newMon = NULL; if(cmc->sub) { newMon = (UA_MonitoredItem*)UA_malloc(sizeof(UA_MonitoredItem)); } else { UA_LocalMonitoredItem *localMon = (UA_LocalMonitoredItem*) UA_malloc(sizeof(UA_LocalMonitoredItem)); if(localMon) { /* Set special values only for the LocalMonitoredItem */ localMon->context = cmc->context; localMon->callback.dataChangeCallback = cmc->dataChangeCallback; } newMon = &localMon->monitoredItem; } if(!newMon) { result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY; return; } /* Initialize the MonitoredItem */ UA_MonitoredItem_init(newMon); newMon->subscription = cmc->sub; /* Can be NULL for local MonitoredItems */ newMon->timestampsToReturn = cmc->timestampsToReturn; result->statusCode |= UA_ReadValueId_copy(&request->itemToMonitor, &newMon->itemToMonitor); result->statusCode |= UA_MonitoringParameters_copy(&request->requestedParameters, &newMon->parameters); result->statusCode |= checkAdjustMonitoredItemParams(server, session, newMon, valueType, &newMon->parameters); #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS result->statusCode |= checkEventFilterParam(server, session, newMon, &newMon->parameters, result); #endif if(result->statusCode != UA_STATUSCODE_GOOD) { UA_LOG_INFO_SUBSCRIPTION(&server->config.logger, cmc->sub, "Could not create a MonitoredItem " "with StatusCode %s", UA_StatusCode_name(result->statusCode)); UA_MonitoredItem_delete(server, newMon); return; } /* Initialize the value status so the first sample always passes the filter */ newMon->lastValue.hasStatus = true; newMon->lastValue.status = ~(UA_StatusCode)0; /* Register the Monitoreditem in the server and subscription */ UA_Server_registerMonitoredItem(server, newMon); /* Activate the MonitoredItem */ result->statusCode |= UA_MonitoredItem_setMonitoringMode(server, newMon, request->monitoringMode); if(result->statusCode != UA_STATUSCODE_GOOD) { UA_MonitoredItem_delete(server, newMon); return; } /* Prepare the response */ result->revisedSamplingInterval = newMon->parameters.samplingInterval; result->revisedQueueSize = newMon->parameters.queueSize; result->monitoredItemId = newMon->monitoredItemId; /* If the sampling interval is negative (the sampling callback is called * from within the publishing callback), return the publishing interval of * the Subscription. Note that we only use the cyclic callback of the * Subscription. So if the Subscription publishing interval is modified, * this also impacts this MonitoredItem. */ if(result->revisedSamplingInterval < 0.0 && cmc->sub) result->revisedSamplingInterval = cmc->sub->publishingInterval; UA_LOG_INFO_SUBSCRIPTION(&server->config.logger, cmc->sub, "MonitoredItem %" PRIi32 " | " "Created the MonitoredItem " "(Sampling Interval: %.2fms, Queue Size: %lu)", newMon->monitoredItemId, newMon->parameters.samplingInterval, (unsigned long)newMon->parameters.queueSize); } 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"); UA_LOCK_ASSERT(&server->serviceMutex, 1); 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); UA_LOCK(&server->serviceMutex); Operation_CreateMonitoredItem(server, &server->adminSession, &cmc, &item, &result); UA_UNLOCK(&server->serviceMutex); 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; } /* Make local copy of the settings */ UA_MonitoringParameters params; result->statusCode = UA_MonitoringParameters_copy(&request->requestedParameters, ¶ms); if(result->statusCode != UA_STATUSCODE_GOOD) return; /* Read the current value to test if filters are possible. * Can return an empty value (v.value.type == NULL). */ UA_DataValue v = UA_Server_readWithSession(server, session, &mon->itemToMonitor, mon->timestampsToReturn); /* Verify and adjust the new parameters. This still leaves the original * MonitoredItem untouched. */ result->statusCode = checkAdjustMonitoredItemParams(server, session, mon, v.value.type, ¶ms); UA_DataValue_clear(&v); if(result->statusCode != UA_STATUSCODE_GOOD) { UA_MonitoringParameters_clear(¶ms); return; } /* Store the old sampling interval */ UA_Double oldSamplingInterval = mon->parameters.samplingInterval; /* Move over the new settings */ UA_MonitoringParameters_clear(&mon->parameters); mon->parameters = params; /* Re-register the callback if necessary */ if(oldSamplingInterval != mon->parameters.samplingInterval) { UA_MonitoredItem_unregisterSampling(server, mon); result->statusCode = UA_MonitoredItem_setMonitoringMode(server, mon, mon->monitoringMode); } result->revisedSamplingInterval = mon->parameters.samplingInterval; result->revisedQueueSize = mon->parameters.queueSize; /* Remove some notifications if the queue is now too small */ UA_MonitoredItem_ensureQueueSpace(server, mon); /* Remove the overflow bits if the queue has now a size of 1 */ UA_MonitoredItem_removeOverflowInfoBits(mon); /* If the sampling interval is negative (the sampling callback is called * from within the publishing callback), return the publishing interval of * the Subscription. Note that we only use the cyclic callback of the * Subscription. So if the Subscription publishing interval is modified, * this also impacts this MonitoredItem. */ if(result->revisedSamplingInterval < 0.0 && mon->subscription) result->revisedSamplingInterval = mon->subscription->publishingInterval; UA_LOG_INFO_SUBSCRIPTION(&server->config.logger, sub, "MonitoredItem %" PRIi32 " | " "Modified the MonitoredItem " "(Sampling Interval: %fms, Queue Size: %lu)", mon->monitoredItemId, mon->parameters.samplingInterval, (unsigned long)mon->queueSize); } 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"); UA_LOCK_ASSERT(&server->serviceMutex, 1); 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; } *result = UA_MonitoredItem_setMonitoringMode(server, mon, smc->monitoringMode); } 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"); UA_LOCK_ASSERT(&server->serviceMutex, 1); 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) { UA_LOCK_ASSERT(&server->serviceMutex, 1); UA_MonitoredItem *mon = UA_Subscription_getMonitoredItem(sub, *monitoredItemId); if(!mon) { *result = UA_STATUSCODE_BADMONITOREDITEMIDINVALID; return; } UA_MonitoredItem_delete(server, mon); } 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"); UA_LOCK_ASSERT(&server->serviceMutex, 1); 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_LOCK(&server->serviceMutex); UA_MonitoredItem *mon, *mon_tmp; LIST_FOREACH_SAFE(mon, &server->localMonitoredItems, listEntry, mon_tmp) { if(mon->monitoredItemId != monitoredItemId) continue; UA_MonitoredItem_delete(server, mon); UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_GOOD; } UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_BADMONITOREDITEMIDINVALID; } #endif /* UA_ENABLE_SUBSCRIPTIONS */ /**** amalgamated original file "/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 * Copyright 2023 (c) Hilscher Gesellschaft für Systemautomation mbH (Author: Phuong Nguyen) */ #ifndef container_of #define container_of(ptr, type, member) \ (type *)((uintptr_t)ptr - offsetof(type,member)) #endif static void removeSecureChannelCallback(void *_, channel_entry *entry) { UA_SecureChannel_close(&entry->channel); } /* Half-closes the channel. Will be completely closed / deleted in a deferred * callback. Deferring is necessary so we don't remove lists that are still * processed upwards the call stack. */ static void removeSecureChannel(UA_Server *server, channel_entry *entry, UA_DiagnosticEvent event) { if(entry->channel.state == UA_SECURECHANNELSTATE_CLOSING) return; entry->channel.state = UA_SECURECHANNELSTATE_CLOSING; /* Detach from the connection and close the connection */ if(entry->channel.connection) { if(entry->channel.connection->state != UA_CONNECTIONSTATE_CLOSED) entry->channel.connection->close(entry->channel.connection); UA_Connection_detachSecureChannel(entry->channel.connection); } /* Detach the channel */ TAILQ_REMOVE(&server->channels, entry, pointers); /* Update the statistics */ UA_SecureChannelStatistics *scs = &server->secureChannelStatistics; UA_atomic_subSize(&scs->currentChannelCount, 1); switch(event) { case UA_DIAGNOSTICEVENT_CLOSE: break; case UA_DIAGNOSTICEVENT_TIMEOUT: UA_atomic_addSize(&scs->channelTimeoutCount, 1); break; case UA_DIAGNOSTICEVENT_PURGE: UA_atomic_addSize(&scs->channelPurgeCount, 1); break; case UA_DIAGNOSTICEVENT_REJECT: case UA_DIAGNOSTICEVENT_SECURITYREJECT: UA_atomic_addSize(&scs->rejectedChannelCount, 1); break; case UA_DIAGNOSTICEVENT_ABORT: UA_atomic_addSize(&scs->channelAbortCount, 1); break; default: UA_assert(false); break; } /* 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; entry->cleanupCallback.nextTime = UA_DateTime_nowMonotonic() + 1; entry->cleanupCallback.interval = 0; /* Remove the structure */ UA_Timer_addTimerEntry(&server->timer, &entry->cleanupCallback, NULL); } void UA_Server_deleteSecureChannels(UA_Server *server) { channel_entry *entry, *temp; TAILQ_FOREACH_SAFE(entry, &server->channels, pointers, temp) removeSecureChannel(server, entry, UA_DIAGNOSTICEVENT_CLOSE); } /* remove channels that were not renewed or who have no connection attached */ void UA_Server_cleanupTimedOutSecureChannels(UA_Server *server, UA_DateTime nowMonotonic) { channel_entry *entry, *temp; TAILQ_FOREACH_SAFE(entry, &server->channels, pointers, temp) { /* The channel was closed internally */ if(entry->channel.state == UA_SECURECHANNELSTATE_CLOSED || !entry->channel.connection) { removeSecureChannel(server, entry, UA_DIAGNOSTICEVENT_CLOSE); continue; } /* Is the SecurityToken already created? */ if(entry->channel.securityToken.createdAt == 0) { /* No -> channel is still in progress of being opened, do not remove */ continue; } /* Has the SecurityToken timed out? */ UA_DateTime timeout = entry->channel.securityToken.createdAt + (UA_DateTime)(entry->channel.securityToken.revisedLifetime * UA_DATETIME_MSEC); /* There is a new token. But it has not been used by the client so far. * Do the rollover now instead of shutting the channel down. * * Part 4, 5.5.2 says: Servers shall use the existing SecurityToken to * secure outgoing Messages until the SecurityToken expires or the * Server receives a Message secured with a new SecurityToken.*/ if(timeout < nowMonotonic && entry->channel.renewState == UA_SECURECHANNELRENEWSTATE_NEWTOKEN_SERVER) { /* Compare with the rollover in checkSymHeader */ entry->channel.renewState = UA_SECURECHANNELRENEWSTATE_NORMAL; entry->channel.securityToken = entry->channel.altSecurityToken; UA_ChannelSecurityToken_init(&entry->channel.altSecurityToken); UA_SecureChannel_generateLocalKeys(&entry->channel); generateRemoteKeys(&entry->channel); /* Use the timeout of the new SecurityToken */ timeout = entry->channel.securityToken.createdAt + (UA_DateTime)(entry->channel.securityToken.revisedLifetime * UA_DATETIME_MSEC); } if(timeout < nowMonotonic) { UA_LOG_INFO_CHANNEL(&server->config.logger, &entry->channel, "SecureChannel has timed out"); removeSecureChannel(server, entry, UA_DIAGNOSTICEVENT_TIMEOUT); } } } /* remove the first channel that has no session attached */ static UA_Boolean purgeFirstChannelWithoutSession(UA_Server *server) { channel_entry *entry; TAILQ_FOREACH(entry, &server->channels, pointers) { if(SLIST_FIRST(&entry->channel.sessions)) continue; UA_LOG_INFO_CHANNEL(&server->config.logger, &entry->channel, "Channel was purged since maxSecureChannels was " "reached and channel had no session attached"); removeSecureChannel(server, entry, UA_DIAGNOSTICEVENT_PURGE); return true; } return false; } UA_StatusCode UA_Server_createSecureChannel(UA_Server *server, UA_Connection *connection) { /* 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((server->secureChannelStatistics.currentChannelCount >= server->config.maxSecureChannels) && !purgeFirstChannelWithoutSession(server)) return UA_STATUSCODE_BADOUTOFMEMORY; channel_entry *entry = (channel_entry *)UA_malloc(sizeof(channel_entry)); if(!entry) return UA_STATUSCODE_BADOUTOFMEMORY; /* Channel state is closed (0) */ /* TODO: Use the connection config from the correct network layer */ UA_SecureChannel_init(&entry->channel, &server->config.networkLayers[0].localConnectionConfig); entry->channel.certificateVerification = &server->config.certificateVerification; entry->channel.processOPNHeader = UA_Server_configSecureChannel; TAILQ_INSERT_TAIL(&server->channels, entry, pointers); UA_Connection_attachSecureChannel(connection, &entry->channel); server->secureChannelStatistics.currentChannelCount++; server->secureChannelStatistics.cumulatedChannelCount++; return UA_STATUSCODE_GOOD; } /* Get pointer to leaf certificate of a specified valid chain of DER encoded * certificates */ static void getLeafCertificate(const UA_ByteString *chain, UA_ByteString *leaf) { /* Detect DER encoded X.509 v3 certificate. If the DER detection fails, * return the entire chain. * * The OPC UA standard requires this to be DER. But we also allow other * formats like PEM. Afterwards it depends on the crypto backend to parse * it. mbedTLS and OpenSSL detect the format automatically. */ if(chain->length < 4 || chain->data[0] != 0x30 || chain->data[1] != 0x82) { *leaf = *chain; return; } /* The certificate length is encoded in the next 2 bytes. */ size_t leafLen = 4; /* Magic numbers + length bytes */ leafLen += (size_t)(((uint16_t)chain->data[2]) << 8); leafLen += chain->data[3]; /* Length consistency check */ if(leafLen > chain->length) return; leaf->data = chain->data; leaf->length = leafLen; } UA_StatusCode UA_Server_configSecureChannel(void *application, UA_SecureChannel *channel, const UA_AsymmetricAlgorithmSecurityHeader *asymHeader) { /* Iterate over available endpoints and choose the correct one */ UA_SecurityPolicy *securityPolicy = NULL; UA_Server *const server = (UA_Server *const) application; 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; /* If the sender provides a chain of certificates then we shall extract the * ApplicationInstanceCertificate. and ignore the extra bytes. See also: OPC * UA Part 6, V1.04, 6.7.2.3 Security Header, Table 42 - Asymmetric * algorithm Security header */ UA_ByteString appInstanceCertificate = UA_BYTESTRING_NULL; getLeafCertificate(&asymHeader->senderCertificate, &appInstanceCertificate); /* Create the channel context and parse the sender (remote) certificate used for the * secureChannel. */ UA_StatusCode retval = UA_SecureChannel_setSecurityPolicy(channel, securityPolicy, &appInstanceCertificate); if(retval != UA_STATUSCODE_GOOD) return retval; channel->securityToken.tokenId = server->lastTokenId++; return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_SecureChannelManager_open(UA_Server *server, UA_SecureChannel *channel, const UA_OpenSecureChannelRequest *request, UA_OpenSecureChannelResponse *response) { if(channel->state != UA_SECURECHANNELSTATE_ACK_SENT) { UA_LOG_ERROR_CHANNEL(&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.channelId = server->lastChannelId++; channel->securityToken.createdAt = UA_DateTime_nowMonotonic(); /* Set the lifetime. Lifetime 0 -> set the maximum possible */ channel->securityToken.revisedLifetime = (request->requestedLifetime > server->config.maxSecurityTokenLifetime) ? server->config.maxSecurityTokenLifetime : request->requestedLifetime; if(channel->securityToken.revisedLifetime == 0) channel->securityToken.revisedLifetime = server->config.maxSecurityTokenLifetime; UA_StatusCode retval = UA_ByteString_copy(&request->clientNonce, &channel->remoteNonce); if(retval != UA_STATUSCODE_GOOD) return retval; /* Generate the nonce. The syymmetric encryption keys are generated when the * first symmetric message is received */ retval = UA_SecureChannel_generateLocalNonce(channel); if(retval != UA_STATUSCODE_GOOD) return retval; /* Set the response. The token will be revolved to the new token when the * first symmetric messages is received. */ response->securityToken = channel->securityToken; response->securityToken.createdAt = UA_DateTime_now(); /* Only for sending */ response->responseHeader.timestamp = response->securityToken.createdAt; response->responseHeader.requestHandle = request->requestHeader.requestHandle; retval = UA_ByteString_copy(&channel->localNonce, &response->serverNonce); if(retval != UA_STATUSCODE_GOOD) return retval; /* The channel is open */ channel->state = UA_SECURECHANNELSTATE_OPEN; /* For the first revolve */ channel->renewState = UA_SECURECHANNELRENEWSTATE_NEWTOKEN_SERVER; channel->altSecurityToken = channel->securityToken; channel->securityToken.tokenId = 0; return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_SecureChannelManager_renew(UA_Server *server, UA_SecureChannel *channel, const UA_OpenSecureChannelRequest *request, UA_OpenSecureChannelResponse *response) { if(channel->state != UA_SECURECHANNELSTATE_OPEN) { UA_LOG_ERROR_CHANNEL(&server->config.logger, channel, "Called renew on channel which is not open"); return UA_STATUSCODE_BADINTERNALERROR; } /* Check whether the nonce was reused */ if(channel->securityMode != UA_MESSAGESECURITYMODE_NONE && UA_ByteString_equal(&channel->remoteNonce, &request->clientNonce)) { UA_LOG_ERROR_CHANNEL(&server->config.logger, channel, "The client reused the last nonce"); return UA_STATUSCODE_BADSECURITYCHECKSFAILED; } /* Create a new SecurityToken. Will be switched over when the first message * is received. */ channel->altSecurityToken = channel->securityToken; channel->altSecurityToken.tokenId = server->lastTokenId++; channel->altSecurityToken.createdAt = UA_DateTime_nowMonotonic(); channel->altSecurityToken.revisedLifetime = (request->requestedLifetime > server->config.maxSecurityTokenLifetime) ? server->config.maxSecurityTokenLifetime : request->requestedLifetime; if(channel->altSecurityToken.revisedLifetime == 0) /* lifetime 0 -> return the max lifetime */ channel->altSecurityToken.revisedLifetime = server->config.maxSecurityTokenLifetime; /* Replace the nonces */ UA_ByteString_clear(&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->securityToken = channel->altSecurityToken; response->securityToken.createdAt = UA_DateTime_now(); /* Only for sending */ response->responseHeader.requestHandle = request->requestHeader.requestHandle; retval = UA_ByteString_copy(&channel->localNonce, &response->serverNonce); if(retval != UA_STATUSCODE_GOOD) return retval; channel->renewState = UA_SECURECHANNELRENEWSTATE_NEWTOKEN_SERVER; return UA_STATUSCODE_GOOD; } void UA_Server_closeSecureChannel(UA_Server *server, UA_SecureChannel *channel, UA_DiagnosticEvent event) { removeSecureChannel(server, container_of(channel, channel_entry, channel), event); } 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, channel, request, response); /* Logging */ if(response->responseHeader.serviceResult == UA_STATUSCODE_GOOD) { UA_Float lifetime = (UA_Float)response->securityToken.revisedLifetime / 1000; UA_LOG_INFO_CHANNEL(&server->config.logger, channel, "SecureChannel renewed " "with a revised lifetime of %.2fs", lifetime); } 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, channel, request, response); /* Logging */ if(response->responseHeader.serviceResult == UA_STATUSCODE_GOOD) { UA_Float lifetime = (UA_Float)response->securityToken.revisedLifetime / 1000; UA_LOG_INFO_CHANNEL(&server->config.logger, channel, "SecureChannel opened with SecurityPolicy %.*s " "and a revised lifetime of %.2fs", (int)channel->securityPolicy->policyUri.length, channel->securityPolicy->policyUri.data, lifetime); } 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"); removeSecureChannel(server, container_of(channel, channel_entry, channel), UA_DIAGNOSTICEVENT_CLOSE); } /**** amalgamated original file "/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 2021 (c) Christian von Arnim, ISW University of Stuttgart (for VDW and umati) * Copyright 2017 (c) Henrik Norrman * Copyright 2021 (c) Fraunhofer IOSB (Author: Andreas Ebner) */ static UA_StatusCode setMethodNode_callback(UA_Server *server, const UA_NodeId methodNodeId, UA_MethodCallback methodCallback); /*********************/ /* Edit Node Context */ /*********************/ UA_StatusCode UA_Server_getNodeContext(UA_Server *server, UA_NodeId nodeId, void **nodeContext) { UA_LOCK(&server->serviceMutex); UA_StatusCode retval = getNodeContext(server, nodeId, nodeContext); UA_UNLOCK(&server->serviceMutex); return retval; } UA_StatusCode getNodeContext(UA_Server *server, UA_NodeId nodeId, void **nodeContext) { const UA_Node *node = UA_NODESTORE_GET(server, &nodeId); if(!node) return UA_STATUSCODE_BADNODEIDUNKNOWN; *nodeContext = node->head.context; UA_NODESTORE_RELEASE(server, node); return UA_STATUSCODE_GOOD; } static UA_StatusCode setDeconstructedNode(UA_Server *server, UA_Session *session, UA_NodeHead *head, void *context) { head->constructed = false; return UA_STATUSCODE_GOOD; } static UA_StatusCode setConstructedNodeContext(UA_Server *server, UA_Session *session, UA_NodeHead *head, void *context) { head->context = context; head->constructed = true; return UA_STATUSCODE_GOOD; } static UA_StatusCode editNodeContext(UA_Server *server, UA_Session* session, UA_NodeHead *head, void *context) { head->context = context; return UA_STATUSCODE_GOOD; } UA_StatusCode setNodeContext(UA_Server *server, UA_NodeId nodeId, void *nodeContext) { return UA_Server_editNode(server, &server->adminSession, &nodeId, (UA_EditNodeCallback)editNodeContext, nodeContext); } UA_StatusCode UA_Server_setNodeContext(UA_Server *server, UA_NodeId nodeId, void *nodeContext) { UA_LOCK(&server->serviceMutex); UA_StatusCode retval = setNodeContext(server, nodeId, nodeContext); UA_UNLOCK(&server->serviceMutex); return retval; } static UA_StatusCode checkSetIsDynamicVariable(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId); /**********************/ /* 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}} }; static void logAddNode(const UA_Logger *logger, UA_Session *session, const UA_NodeId *nodeId, const char *msg) { UA_LOG_NODEID_INFO(nodeId, UA_LOG_INFO_SESSION(logger, session, "AddNode (%.*s): %s", (int)nodeIdStr.length, nodeIdStr.data, msg)); } /* 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, const UA_NodeHead *head, 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((head->nodeClass == UA_NODECLASS_OBJECT || head->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_GET(server, parentNodeId); if(!parent) { logAddNode(&server->config.logger, session, &head->nodeId, "Parent node not found"); return UA_STATUSCODE_BADPARENTNODEIDINVALID; } UA_NodeClass parentNodeClass = parent->head.nodeClass; UA_NODESTORE_RELEASE(server, parent); /* Check the referencetype exists */ const UA_Node *referenceType = UA_NODESTORE_GET(server, referenceTypeId); if(!referenceType) { logAddNode(&server->config.logger, session, &head->nodeId, "Reference type to the parent not found"); return UA_STATUSCODE_BADREFERENCETYPEIDINVALID; } /* Check if the referencetype is a reference type node */ if(referenceType->head.nodeClass != UA_NODECLASS_REFERENCETYPE) { logAddNode(&server->config.logger, session, &head->nodeId, "Reference type to the parent is not a ReferenceTypeNode"); UA_NODESTORE_RELEASE(server, referenceType); return UA_STATUSCODE_BADREFERENCETYPEIDINVALID; } /* Check that the reference type is not abstract */ UA_Boolean referenceTypeIsAbstract = referenceType->referenceTypeNode.isAbstract; UA_NODESTORE_RELEASE(server, referenceType); if(referenceTypeIsAbstract == true) { logAddNode(&server->config.logger, session, &head->nodeId, "Abstract reference type to the parent not allowed"); return UA_STATUSCODE_BADREFERENCENOTALLOWED; } /* Check hassubtype relation for type nodes */ if(head->nodeClass == UA_NODECLASS_DATATYPE || head->nodeClass == UA_NODECLASS_VARIABLETYPE || head->nodeClass == UA_NODECLASS_OBJECTTYPE || head->nodeClass == UA_NODECLASS_REFERENCETYPE) { /* Type needs hassubtype reference to the supertype */ if(referenceType->referenceTypeNode.referenceTypeIndex != UA_REFERENCETYPEINDEX_HASSUBTYPE) { logAddNode(&server->config.logger, session, &head->nodeId, "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 != head->nodeClass) { logAddNode(&server->config.logger, session, &head->nodeId, "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 */ const UA_NodeId hierarchRefs = UA_NODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES); if(!isNodeInTree_singleRef(server, referenceTypeId, &hierarchRefs, UA_REFERENCETYPEINDEX_HASSUBTYPE)) { logAddNode(&server->config.logger, session, &head->nodeId, "Reference type to the parent is not hierarchical"); return UA_STATUSCODE_BADREFERENCETYPEIDINVALID; } return UA_STATUSCODE_GOOD; } static UA_StatusCode setDefaultValue(UA_Server *server, const UA_VariableNode *node) { /* Get the DataType */ UA_StatusCode res = UA_STATUSCODE_GOOD; const UA_DataType *type = UA_Server_findDataType(server, &node->dataType); if(!type) { /* No description for the DataType found. It is possible that an * abstract DataType is used, e.g. UInteger. Browse to see if there is a * non-abstract subtype that can be used for the default value. * * Look up and downwards in the hierarchy. Some data types (e.g. * UtcTime) are derived from a non-abstract data type. This is then used * for the actual value, Use the first match. */ UA_ReferenceTypeSet refs = UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASSUBTYPE); UA_ExpandedNodeId *typeCandidates = NULL; size_t typeCandidatesSize = 0; res = browseRecursive(server, 1, &node->dataType, UA_BROWSEDIRECTION_BOTH, &refs, UA_NODECLASS_DATATYPE, false, &typeCandidatesSize, &typeCandidates); if(res != UA_STATUSCODE_GOOD) return res; for(size_t i = 0; i < typeCandidatesSize; i++) { /* Skip BaseDataType (Variant). This is the root of the DataType * hierarchy. Variables of BaseDataType can be empty. */ if(UA_NodeId_equal(&UA_TYPES[UA_TYPES_VARIANT].typeId, &typeCandidates[i].nodeId)) continue; type = UA_Server_findDataType(server, &typeCandidates[i].nodeId); if(type) break; } UA_Array_delete(typeCandidates, typeCandidatesSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); if(!type) return UA_STATUSCODE_BADTYPEMISMATCH; } /* Set up the value with the default content */ UA_Variant val; UA_Variant_init(&val); if(node->valueRank < 0) { /* Set a scalar */ void *data = UA_new(type); if(!data) return UA_STATUSCODE_BADOUTOFMEMORY; UA_Variant_setScalar(&val, data, type); } else if(node->valueRank == 0) { /* Use an empty array of one dimension */ UA_Variant_setArray(&val, NULL, 0, type); } else { /* Write an array that matches the ArrayDimensions */ res = UA_Array_copy(node->arrayDimensions, node->arrayDimensionsSize, (void**)&val.arrayDimensions, &UA_TYPES[UA_TYPES_UINT32]); if(res != UA_STATUSCODE_GOOD) return res; val.arrayDimensionsSize = node->arrayDimensionsSize; /* No length restriction in the ArrayDimension -> use length 1 */ size_t size = 1; for(size_t i = 0; i < val.arrayDimensionsSize; i++) { if(val.arrayDimensions[i] == 0) val.arrayDimensions[i] = 1; size *= val.arrayDimensions[i]; } /* Create the content array */ void *data = UA_Array_new(size, type); if(!data) { UA_Variant_clear(&val); return UA_STATUSCODE_BADOUTOFMEMORY; } val.data = data; val.arrayLength = size; val.type = type; } /* Write the value */ res = writeAttribute(server, &server->adminSession, &node->head.nodeId, UA_ATTRIBUTEID_VALUE, &val, &UA_TYPES[UA_TYPES_VARIANT]); /* Clean up */ UA_Variant_clear(&val); return res; } static UA_StatusCode typeCheckVariableNode(UA_Server *server, UA_Session *session, const UA_VariableNode *node, const UA_VariableTypeNode *vt) { /* Check the datatype against the vt */ if(!compatibleDataTypes(server, &node->dataType, &vt->dataType)) { logAddNode(&server->config.logger, session, &node->head.nodeId, "The value of is incompatible with " "the datatype of the VariableType"); return UA_STATUSCODE_BADTYPEMISMATCH; } /* Check valueRank against array dimensions */ if(!compatibleValueRankArrayDimensions(server, session, node->valueRank, node->arrayDimensionsSize)) { logAddNode(&server->config.logger, session, &node->head.nodeId, "The value rank of is incompatible with its array dimensions"); return UA_STATUSCODE_BADTYPEMISMATCH; } /* Check valueRank against the vt */ if(!compatibleValueRanks(node->valueRank, vt->valueRank)) { logAddNode(&server->config.logger, session, &node->head.nodeId, "The value rank is incompatible " "with the value rank of the VariableType"); return UA_STATUSCODE_BADTYPEMISMATCH; } /* Check array dimensions against the vt */ if(!compatibleArrayDimensions(vt->arrayDimensionsSize, vt->arrayDimensions, node->arrayDimensionsSize, node->arrayDimensions)) { logAddNode(&server->config.logger, session, &node->head.nodeId, "The array dimensions are incompatible with the " "array dimensions of the VariableType"); return UA_STATUSCODE_BADTYPEMISMATCH; } /* Typecheck the value */ /* 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; /* Only BaseDataType (Variant) can have empty values. Create default content * otherwise that matches the constraints. */ if(!value.hasValue || !value.value.type) { if(!UA_NodeId_equal(&node->dataType, &UA_TYPES[UA_TYPES_VARIANT].typeId)) { /* Warn if that is configured */ if(!server->bootstrapNS0 && server->config.allowEmptyVariables != UA_RULEHANDLING_ACCEPT) logAddNode(&server->config.logger, session, &node->head.nodeId, "The value is empty. But this is only allowed for BaseDataType. " "Create a matching default value."); /* Abort if that is configured */ if(server->config.allowEmptyVariables == UA_RULEHANDLING_ABORT) retval = UA_STATUSCODE_BADTYPEMISMATCH; /* Try to generate a default value if that is configured */ if(server->config.allowEmptyVariables == UA_RULEHANDLING_DEFAULT) { retval = setDefaultValue(server, node); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_NODEID_INFO(&node->head.nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNode (%.*s): Could not create a default value " "with StatusCode %s", (int)nodeIdStr.length, nodeIdStr.data, UA_StatusCode_name(retval))); } /* Reread the current value for compat tests below */ UA_DataValue_clear(&value); retval = readValueAttribute(server, session, node, &value); } } if(retval != UA_STATUSCODE_GOOD) { UA_DataValue_clear(&value); return retval; } } /* Perform the value typecheck. If this fails, write the current value * again. The write-service tries to convert to the correct type... */ const char *reason; if(!compatibleValue(server, session, &node->dataType, node->valueRank, node->arrayDimensionsSize, node->arrayDimensions, &value.value, NULL, &reason)) { retval = writeValueAttribute(server, session, &node->head.nodeId, &value.value); if(retval != UA_STATUSCODE_GOOD) { logAddNode(&server->config.logger, session, &node->head.nodeId, "The value is incompatible with the variable definition"); } } UA_DataValue_clear(&value); 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, const UA_VariableTypeNode *vt) { /* 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_Variant orig; UA_StatusCode retval = readWithReadValue(server, &node->head.nodeId, UA_ATTRIBUTEID_VALUE, &orig); if(retval != UA_STATUSCODE_GOOD) return retval; if(orig.type) { /* A value is present */ UA_Variant_clear(&orig); } else { UA_DataValue v; UA_DataValue_init(&v); retval = readValueAttribute(server, session, (const UA_VariableNode*)vt, &v); if(retval == UA_STATUSCODE_GOOD && v.hasValue) { retval = writeValueAttribute(server, session, &node->head.nodeId, &v.value); } UA_DataValue_clear(&v); if(retval != UA_STATUSCODE_GOOD) { logAddNode(&server->config.logger, session, &node->head.nodeId, "The default content of the VariableType could " "not be used. This may happen if the VariableNode " "makes additional restrictions."); retval = UA_STATUSCODE_GOOD; } } /* If no datatype is given, use the datatype of the vt */ if(UA_NodeId_isNull(&node->dataType)) { logAddNode(&server->config.logger, session, &node->head.nodeId, "No datatype given; Copy the datatype attribute " "from the TypeDefinition"); retval = writeAttribute(server, session, &node->head.nodeId, UA_ATTRIBUTEID_DATATYPE, &vt->dataType, &UA_TYPES[UA_TYPES_NODEID]); if(retval != UA_STATUSCODE_GOOD) return retval; } /* Use the ArrayDimensions of the vt */ if(node->arrayDimensionsSize == 0 && vt->arrayDimensionsSize > 0) { UA_Variant v; UA_Variant_init(&v); UA_Variant_setArray(&v, vt->arrayDimensions, vt->arrayDimensionsSize, &UA_TYPES[UA_TYPES_UINT32]); retval = writeAttribute(server, session, &node->head.nodeId, UA_ATTRIBUTEID_ARRAYDIMENSIONS, &v, &UA_TYPES[UA_TYPES_VARIANT]); } return retval; } /* 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_clear(&br); return retval; } static const UA_NodeId mandatoryId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_MODELLINGRULE_MANDATORY}}; static UA_Boolean isMandatoryChild(UA_Server *server, UA_Session *session, const UA_NodeId *childNodeId) { /* Get the child */ const UA_Node *child = UA_NODESTORE_GET(server, childNodeId); if(!child) return false; /* Look for the reference making the child mandatory */ UA_NodePointer mandatoryP = UA_NodePointer_fromNodeId(&mandatoryId); for(size_t i = 0; i < child->head.referencesSize; ++i) { UA_NodeReferenceKind *rk = &child->head.references[i]; if(rk->referenceTypeIndex != UA_REFERENCETYPEINDEX_HASMODELLINGRULE) continue; if(rk->isInverse) continue; const UA_ReferenceTarget *t = NULL; while((t = UA_NodeReferenceKind_iterate(rk, t))) { if(UA_NodePointer_equal(mandatoryP, t->targetId)) { UA_NODESTORE_RELEASE(server, child); return true; } } } UA_NODESTORE_RELEASE(server, child); return false; } static UA_StatusCode copyAllChildren(UA_Server *server, UA_Session *session, const UA_NodeId *source, const UA_NodeId *destination); static void Operation_addReference(UA_Server *server, UA_Session *session, void *context, const UA_AddReferencesItem *item, UA_StatusCode *retval); UA_StatusCode addRef(UA_Server *server, UA_Session *session, const UA_NodeId *sourceId, const UA_NodeId *referenceTypeId, const UA_NodeId *targetId, UA_Boolean forward) { UA_AddReferencesItem ref_item; UA_AddReferencesItem_init(&ref_item); ref_item.sourceNodeId = *sourceId; ref_item.referenceTypeId = *referenceTypeId; ref_item.isForward = forward; ref_item.targetNodeId.nodeId = *targetId; UA_StatusCode retval = UA_STATUSCODE_GOOD; Operation_addReference(server, session, NULL, &ref_item, &retval); return retval; } static UA_StatusCode addInterfaceChildren(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId, const UA_NodeId *typeId) { /* Get the hierarchy of the type and all its supertypes */ UA_NodeId *hierarchy = NULL; size_t hierarchySize = 0; UA_StatusCode retval = getAllInterfaceChildNodeIds(server, nodeId, typeId, &hierarchy, &hierarchySize); if(retval != UA_STATUSCODE_GOOD) return retval; /* Copy members of the type and supertypes (and instantiate them) */ for(size_t i = 0; i < hierarchySize; ++i) { retval = copyAllChildren(server, session, &hierarchy[i], nodeId); if(retval != UA_STATUSCODE_GOOD) { UA_Array_delete(hierarchy, hierarchySize, &UA_TYPES[UA_TYPES_NODEID]); return retval; } } for(size_t i = 0; i < hierarchySize; ++i) { UA_NodeId refId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASINTERFACE); retval = addRef(server, &server->adminSession, nodeId, &refId, &hierarchy[i], true); /* Don't add the original HasInterface reference to ObjectType sub nodes */ if(retval == UA_STATUSCODE_BADDUPLICATEREFERENCENOTALLOWED) { retval = UA_STATUSCODE_GOOD; } else if(retval != UA_STATUSCODE_GOOD) { break; } } UA_Array_delete(hierarchy, hierarchySize, &UA_TYPES[UA_TYPES_NODEID]); return retval; } static UA_StatusCode copyChild(UA_Server *server, UA_Session *session, const UA_NodeId *destinationNodeId, const UA_ReferenceDescription *rd) { UA_assert(session); /* 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_clear(&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; UA_UNLOCK(&server->serviceMutex); UA_Boolean createChild = server->config.nodeLifecycle. createOptionalChild(server, &session->sessionId, session->sessionHandle, &rd->nodeId.nodeId, destinationNodeId, &rd->referenceTypeId); UA_LOCK(&server->serviceMutex); if(!createChild) 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_GETCOPY(server, &rd->nodeId.nodeId, &node); if(retval != UA_STATUSCODE_GOOD) return retval; /* Remove the context of the copied node */ node->head.context = NULL; node->head.constructed = false; #ifdef UA_ENABLE_SUBSCRIPTIONS node->head.monitoredItems = NULL; #endif /* Reset the NodeId (random numeric id will be assigned in the nodestore) */ UA_NodeId_clear(&node->head.nodeId); node->head.nodeId.namespaceIndex = destinationNodeId->namespaceIndex; if(server->config.nodeLifecycle.generateChildNodeId) { UA_UNLOCK(&server->serviceMutex); retval = server->config.nodeLifecycle. generateChildNodeId(server, &session->sessionId, session->sessionHandle, &rd->nodeId.nodeId, destinationNodeId, &rd->referenceTypeId, &node->head.nodeId); UA_LOCK(&server->serviceMutex); if(retval != UA_STATUSCODE_GOOD) { UA_NODESTORE_DELETE(server, 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 */ const UA_NodeId nodeId_typesFolder= UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER); const UA_ReferenceTypeSet reftypes_aggregates = UA_REFTYPESET(UA_REFERENCETYPEINDEX_AGGREGATES); UA_ReferenceTypeSet reftypes_skipped; /* Check if the hasModellingRule-reference is required (configured or node in an instance declaration) */ if(server->config.modellingRulesOnInstances || isNodeInTree(server, destinationNodeId, &nodeId_typesFolder, &reftypes_aggregates)) { reftypes_skipped = UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASMODELLINGRULE); } else { UA_ReferenceTypeSet_init(&reftypes_skipped); } reftypes_skipped = UA_ReferenceTypeSet_union(reftypes_skipped, UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASINTERFACE)); UA_Node_deleteReferencesSubset(node, &reftypes_skipped); /* Add the node to the nodestore */ UA_NodeId newNodeId; retval = UA_NODESTORE_INSERT(server, node, &newNodeId); /* node = NULL; The pointer is no longer valid */ 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_REMOVE(server, &newNodeId); UA_NodeId_clear(&newNodeId); return retval; } if (rd->nodeClass == UA_NODECLASS_VARIABLE) { retval = checkSetIsDynamicVariable(server, session, &newNodeId); if(retval != UA_STATUSCODE_GOOD) { UA_NODESTORE_REMOVE(server, &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); if(retval != UA_STATUSCODE_GOOD) { deleteNode(server, newNodeId, true); return retval; } /* Check if its a dynamic variable, add all type and/or interface * children and call the constructor */ retval = AddNode_finish(server, session, &newNodeId); if(retval != UA_STATUSCODE_GOOD) { deleteNode(server, newNodeId, true); return retval; } /* Clean up. Because it can happen that a string is assigned as ID at * generateChildNodeId. */ UA_NodeId_clear(&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) break; } UA_BrowseResult_clear(&br); return retval; } static UA_StatusCode addTypeChildren(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId, const UA_NodeId *typeId) { /* Get the hierarchy of the type and all its supertypes */ UA_NodeId *hierarchy = NULL; size_t hierarchySize = 0; UA_StatusCode retval = getParentTypeAndInterfaceHierarchy(server, typeId, &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], nodeId); if(retval != UA_STATUSCODE_GOOD) break; } UA_Array_delete(hierarchy, hierarchySize, &UA_TYPES[UA_TYPES_NODEID]); 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_GET(server, nodeId); if(!node) return UA_STATUSCODE_BADNODEIDUNKNOWN; /* Use the typeDefinition as parent for type-nodes */ const UA_NodeHead *head = &node->head; if(head->nodeClass == UA_NODECLASS_VARIABLETYPE || head->nodeClass == UA_NODECLASS_OBJECTTYPE || head->nodeClass == UA_NODECLASS_REFERENCETYPE || head->nodeClass == UA_NODECLASS_DATATYPE) { if(UA_NodeId_equal(referenceTypeId, &UA_NODEID_NULL)) referenceTypeId = &hasSubtype; const UA_Node *parentNode = UA_NODESTORE_GET(server, parentNodeId); if(parentNode) { if(parentNode->head.nodeClass == head->nodeClass) typeDefinitionId = parentNodeId; UA_NODESTORE_RELEASE(server, parentNode); } } UA_StatusCode retval; /* Make sure newly created node does not have itself as parent */ if(UA_NodeId_equal(nodeId, parentNodeId)) { logAddNode(&server->config.logger, session, nodeId, "A node cannot have itself as parent"); retval = UA_STATUSCODE_BADINVALIDARGUMENT; goto cleanup; } /* Check parent reference. Objects may have no parent. */ retval = checkParentReference(server, session, head, parentNodeId, referenceTypeId); if(retval != UA_STATUSCODE_GOOD) { logAddNode(&server->config.logger, session, nodeId, "The parent reference for is invalid"); goto cleanup; } /* Replace empty typeDefinition with the most permissive default */ if((head->nodeClass == UA_NODECLASS_VARIABLE || head->nodeClass == UA_NODECLASS_OBJECT) && UA_NodeId_isNull(typeDefinitionId)) { logAddNode(&server->config.logger, session, nodeId, "No TypeDefinition. Use the default " "TypeDefinition for the Variable/Object"); if(head->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_GET(server, typeDefinitionId); if(!type) { logAddNode(&server->config.logger, session, nodeId, "Node type not found"); retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID; goto cleanup; } UA_Boolean typeOk = false; const UA_NodeHead *typeHead = &type->head; switch(head->nodeClass) { case UA_NODECLASS_DATATYPE: typeOk = typeHead->nodeClass == UA_NODECLASS_DATATYPE; break; case UA_NODECLASS_METHOD: typeOk = typeHead->nodeClass == UA_NODECLASS_METHOD; break; case UA_NODECLASS_OBJECT: case UA_NODECLASS_OBJECTTYPE: typeOk = typeHead->nodeClass == UA_NODECLASS_OBJECTTYPE; break; case UA_NODECLASS_REFERENCETYPE: typeOk = typeHead->nodeClass == UA_NODECLASS_REFERENCETYPE; break; case UA_NODECLASS_VARIABLE: case UA_NODECLASS_VARIABLETYPE: typeOk = typeHead->nodeClass == UA_NODECLASS_VARIABLETYPE; break; case UA_NODECLASS_VIEW: typeOk = typeHead->nodeClass == UA_NODECLASS_VIEW; break; default: typeOk = false; } if(!typeOk) { logAddNode(&server->config.logger, session, nodeId, "Type does not match the NodeClass"); 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(head->nodeClass == UA_NODECLASS_VARIABLE && type->variableTypeNode.isAbstract) { /* Get subtypes of the parent reference types */ UA_ReferenceTypeSet refTypes1, refTypes2; retval |= referenceTypeIndices(server, &parentReferences[0], &refTypes1, true); retval |= referenceTypeIndices(server, &parentReferences[1], &refTypes2, true); UA_ReferenceTypeSet refTypes = UA_ReferenceTypeSet_union(refTypes1, refTypes2); if(retval != UA_STATUSCODE_GOOD) 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, parentNodeId, &variableTypes, &refTypes) && !isNodeInTree(server, parentNodeId, &objectTypes, &refTypes)) { logAddNode(&server->config.logger, session, nodeId, "Type of variable node must be a " "VariableType and not cannot be abstract"); retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID; goto cleanup; } } if(head->nodeClass == UA_NODECLASS_OBJECT && type->objectTypeNode.isAbstract) { /* Get subtypes of the parent reference types */ UA_ReferenceTypeSet refTypes1, refTypes2; retval |= referenceTypeIndices(server, &parentReferences[0], &refTypes1, true); retval |= referenceTypeIndices(server, &parentReferences[1], &refTypes2, true); UA_ReferenceTypeSet refTypes = UA_ReferenceTypeSet_union(refTypes1, refTypes2); if(retval != UA_STATUSCODE_GOOD) 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, parentNodeId, &objectTypes, &refTypes); const UA_NodeId eventTypes = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE); UA_Boolean isInBaseEventType = isNodeInTree_singleRef(server, &type->head.nodeId, &eventTypes, UA_REFERENCETYPEINDEX_HASSUBTYPE); if(!isInBaseObjectType && !(isInBaseEventType && UA_NodeId_isNull(parentNodeId))) { logAddNode(&server->config.logger, session, nodeId, "Type of ObjectNode must be ObjectType and not be abstract"); retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID; goto cleanup; } } } /* Add reference to the parent */ if(!UA_NodeId_isNull(parentNodeId)) { if(UA_NodeId_isNull(referenceTypeId)) { logAddNode(&server->config.logger, session, nodeId, "Reference to parent cannot be null"); retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID; goto cleanup; } retval = addRef(server, session, &head->nodeId, referenceTypeId, parentNodeId, false); if(retval != UA_STATUSCODE_GOOD) { logAddNode(&server->config.logger, session, nodeId, "Adding reference to parent failed"); goto cleanup; } } /* Add a hasTypeDefinition reference */ if(head->nodeClass == UA_NODECLASS_VARIABLE || head->nodeClass == UA_NODECLASS_OBJECT) { UA_assert(type != NULL); /* see above */ retval = addRef(server, session, &head->nodeId, &hasTypeDefinition, &type->head.nodeId, true); if(retval != UA_STATUSCODE_GOOD) { logAddNode(&server->config.logger, session, nodeId, "Adding a reference to the type definition failed"); } } cleanup: UA_NODESTORE_RELEASE(server, node); if(type) UA_NODESTORE_RELEASE(server, 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) { UA_UNLOCK(&server->serviceMutex); if(!server->config.accessControl. allowAddNode(server, &server->config.accessControl, &session->sessionId, session->sessionHandle, item)) { UA_LOCK(&server->serviceMutex); return UA_STATUSCODE_BADUSERACCESSDENIED; } UA_LOCK(&server->serviceMutex); } /* Check the namespaceindex */ if(item->requestedNewNodeId.nodeId.namespaceIndex >= server->namespacesSize) { UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNode: 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, "AddNode: Node attributes invalid"); return UA_STATUSCODE_BADINTERNALERROR; } /* Create a node */ UA_Node *node = UA_NODESTORE_NEW(server, item->nodeClass); if(!node) { UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNode: Node could not create a node " "in the nodestore"); return UA_STATUSCODE_BADOUTOFMEMORY; } UA_NodeId tmpOutId = UA_NODEID_NULL; /* Fill the node attributes */ node->head.context = nodeContext; UA_StatusCode retval = UA_NodeId_copy(&item->requestedNewNodeId.nodeId, &node->head.nodeId); if(retval != UA_STATUSCODE_GOOD) goto create_error; retval = UA_QualifiedName_copy(&item->browseName, &node->head.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 */ if(!outNewNodeId) outNewNodeId = &tmpOutId; retval = UA_NODESTORE_INSERT(server, node, outNewNodeId); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNode: Node could not add the new node " "to the nodestore with error code %s", UA_StatusCode_name(retval)); return retval; } if(outNewNodeId == &tmpOutId) UA_NodeId_clear(&tmpOutId); return UA_STATUSCODE_GOOD; create_error: UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNode: Node could not create a node " "with error code %s", UA_StatusCode_name(retval)); UA_NODESTORE_DELETE(server, node); return retval; } 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.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 = 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_clear(&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 checkSetBrowseName(UA_Server *server, UA_Session *session, UA_AddNodesItem *item) { /* If the object node already has a browse name we are done here. */ if(!UA_QualifiedName_isNull(&item->browseName)) return UA_STATUSCODE_GOOD; /* Nodes other than Objects must have a BrowseName */ if(item->nodeClass != UA_NODECLASS_OBJECT) return UA_STATUSCODE_BADBROWSENAMEINVALID; /* 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; UA_StatusCode retval = findDefaultInstanceBrowseNameNode(server, item->typeDefinition.nodeId, &defaultBrowseNameNode); if(retval != UA_STATUSCODE_GOOD) return UA_STATUSCODE_BADBROWSENAMEINVALID; UA_Variant defaultBrowseName; retval = readWithReadValue(server, &defaultBrowseNameNode, UA_ATTRIBUTEID_VALUE, &defaultBrowseName); UA_NodeId_clear(&defaultBrowseNameNode); if(retval != UA_STATUSCODE_GOOD) return UA_STATUSCODE_BADBROWSENAMEINVALID; if(UA_Variant_hasScalarType(&defaultBrowseName, &UA_TYPES[UA_TYPES_QUALIFIEDNAME])) { item->browseName = *(UA_QualifiedName*)defaultBrowseName.data; UA_QualifiedName_init((UA_QualifiedName*)defaultBrowseName.data); } else { retval = UA_STATUSCODE_BADBROWSENAMEINVALID; } UA_Variant_clear(&defaultBrowseName); 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; } /* Set the BrowsenName before adding to the Nodestore. The BrowseName is * immutable afterwards. */ UA_Boolean noBrowseName = UA_QualifiedName_isNull(&item->browseName); UA_StatusCode retval = checkSetBrowseName(server, session, (UA_AddNodesItem*)(uintptr_t)item); if(retval != UA_STATUSCODE_GOOD) return retval; /* Create the node and add it to the nodestore */ retval = AddNode_raw(server, session, nodeContext, item, outNewNodeId); if(retval != UA_STATUSCODE_GOOD) goto cleanup; /* 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) deleteNode(server, *outNewNodeId, true); if(outNewNodeId == &newId) UA_NodeId_clear(&newId); cleanup: if(noBrowseName) UA_QualifiedName_clear((UA_QualifiedName*)(uintptr_t)&item->browseName); return retval; } /* Construct children first */ static UA_StatusCode recursiveCallConstructors(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId, const UA_Node *type) { /* Browse the children */ UA_BrowseDescription bd; UA_BrowseDescription_init(&bd); bd.nodeId = *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 child node */ UA_StatusCode retval = UA_STATUSCODE_GOOD; for(size_t i = 0; i < br.referencesSize; ++i) { UA_ReferenceDescription *rd = &br.references[i]; if(!UA_ExpandedNodeId_isLocal(&rd->nodeId)) continue; const UA_Node *target = UA_NODESTORE_GET(server, &rd->nodeId.nodeId); if(!target) continue; if(target->head.constructed) { UA_NODESTORE_RELEASE(server, target); continue; } const UA_Node *targetType = NULL; if(target->head.nodeClass == UA_NODECLASS_VARIABLE || target->head.nodeClass == UA_NODECLASS_OBJECT) { targetType = getNodeType(server, &target->head); if(!targetType) { UA_NODESTORE_RELEASE(server, target); retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID; break; } } UA_NODESTORE_RELEASE(server, target); retval = recursiveCallConstructors(server, session, &rd->nodeId.nodeId, targetType); if(targetType) UA_NODESTORE_RELEASE(server, targetType); if(retval != UA_STATUSCODE_GOOD) break; } UA_BrowseResult_clear(&br); /* If a child could not be constructed or the node is already constructed */ if(retval != UA_STATUSCODE_GOOD) return retval; /* Get the node context */ const UA_Node *node = UA_NODESTORE_GET(server, nodeId); if(!node) return UA_STATUSCODE_BADNODEIDUNKNOWN; void *context = node->head.context; UA_NODESTORE_RELEASE(server, node); /* Call the global constructor */ if(server->config.nodeLifecycle.constructor) { UA_UNLOCK(&server->serviceMutex); retval = server->config.nodeLifecycle. constructor(server, &session->sessionId, session->sessionHandle, nodeId, &context); UA_LOCK(&server->serviceMutex); if(retval != UA_STATUSCODE_GOOD) return retval; } /* Call the local (per-type) constructor */ const UA_NodeTypeLifecycle *lifecycle = NULL; if(type && node->head.nodeClass == UA_NODECLASS_OBJECT) lifecycle = &type->objectTypeNode.lifecycle; else if(type && node->head.nodeClass == UA_NODECLASS_VARIABLE) lifecycle = &type->variableTypeNode.lifecycle; if(lifecycle && lifecycle->constructor) { UA_UNLOCK(&server->serviceMutex); retval = lifecycle->constructor(server, &session->sessionId, session->sessionHandle, &type->head.nodeId, type->head.context, nodeId, &context); UA_LOCK(&server->serviceMutex); if(retval != UA_STATUSCODE_GOOD) goto global_destructor; } /* Set the context *and* mark the node as constructed */ retval = UA_Server_editNode(server, &server->adminSession, nodeId, (UA_EditNodeCallback)setConstructedNodeContext, context); if(retval != UA_STATUSCODE_GOOD) goto local_destructor; /* All good, return */ return retval; /* Fail. Call the destructors. */ local_destructor: if(lifecycle && lifecycle->destructor) { UA_UNLOCK(&server->serviceMutex); lifecycle->destructor(server, &session->sessionId, session->sessionHandle, &type->head.nodeId, type->head.context, nodeId, &context); UA_LOCK(&server->serviceMutex); } global_destructor: if(server->config.nodeLifecycle.destructor) { UA_UNLOCK(&server->serviceMutex); server->config.nodeLifecycle.destructor(server, &session->sessionId, session->sessionHandle, nodeId, context); UA_LOCK(&server->serviceMutex); } return retval; } /* Add new ReferenceType to the subtypes bitfield */ static UA_StatusCode addReferenceTypeSubtype(UA_Server *server, UA_Session *session, UA_Node *node, void *context) { node->referenceTypeNode.subTypes = UA_ReferenceTypeSet_union(node->referenceTypeNode.subTypes, *(UA_ReferenceTypeSet*)context); return UA_STATUSCODE_GOOD; } static UA_StatusCode setReferenceTypeSubtypes(UA_Server *server, const UA_ReferenceTypeNode *node) { /* Get the ReferenceTypes upwards in the hierarchy */ size_t parentsSize = 0; UA_ExpandedNodeId *parents = NULL; UA_ReferenceTypeSet reftypes_subtype = UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASSUBTYPE); UA_StatusCode res = browseRecursive(server, 1, &node->head.nodeId, UA_BROWSEDIRECTION_INVERSE, &reftypes_subtype, UA_NODECLASS_UNSPECIFIED, false, &parentsSize, &parents); if(res != UA_STATUSCODE_GOOD) return res; /* Add the ReferenceTypeIndex of this node */ const UA_ReferenceTypeSet *newRefSet = &node->subTypes; for(size_t i = 0; i < parentsSize; i++) { UA_Server_editNode(server, &server->adminSession, &parents[i].nodeId, addReferenceTypeSubtype, (void*)(uintptr_t)newRefSet); } UA_Array_delete(parents, parentsSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); return UA_STATUSCODE_GOOD; } static UA_StatusCode setVariableNodeDynamic(UA_Server *server, UA_Session *session, UA_Node *node, const void *_) { (void)_; /* unused */ if(node->head.nodeClass == UA_NODECLASS_VARIABLE) ((UA_VariableNode*)node)->isDynamic = true; return UA_STATUSCODE_GOOD; } static UA_StatusCode checkSetIsDynamicVariable(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId) { /* Get all hierarchical reference types */ UA_ReferenceTypeSet reftypes_hierarchical; UA_ReferenceTypeSet_init(&reftypes_hierarchical); UA_NodeId hierarchicalRefs = UA_NODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES); UA_StatusCode res = referenceTypeIndices(server, &hierarchicalRefs, &reftypes_hierarchical, true); if(res != UA_STATUSCODE_GOOD) return res; /* Is the variable under the server object? */ UA_NodeId serverNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER); if(isNodeInTree(server, nodeId, &serverNodeId, &reftypes_hierarchical)) return UA_STATUSCODE_GOOD; /* Is the variable in the type hierarchy? */ UA_NodeId typesNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER); if(isNodeInTree(server, nodeId, &typesNodeId, &reftypes_hierarchical)) return UA_STATUSCODE_GOOD; /* Is the variable a property of a method node (InputArguments / * OutputArguments)? */ UA_BrowseDescription bd; UA_BrowseDescription_init(&bd); bd.nodeId = *nodeId; bd.browseDirection = UA_BROWSEDIRECTION_INVERSE; bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY); bd.includeSubtypes = false; bd.nodeClassMask = UA_NODECLASS_METHOD; UA_BrowseResult br; UA_BrowseResult_init(&br); UA_UInt32 maxrefs = 0; Operation_Browse(server, session, &maxrefs, &bd, &br); UA_Boolean hasParentMethod = (br.referencesSize > 0); UA_BrowseResult_clear(&br); if(hasParentMethod) return UA_STATUSCODE_GOOD; /* Set the variable to "dynamic" */ UA_Server_editNode(server, session, nodeId, (UA_EditNodeCallback)setVariableNodeDynamic, NULL); return UA_STATUSCODE_GOOD; } /* Children, references, type-checking, constructors. */ UA_StatusCode AddNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId) { /* Get the node */ const UA_Node *type = NULL; const UA_Node *node = UA_NODESTORE_GET(server, nodeId); if(!node) return UA_STATUSCODE_BADNODEIDUNKNOWN; /* Set the ReferenceTypesSet of subtypes in the ReferenceTypeNode */ UA_StatusCode retval = UA_STATUSCODE_GOOD; if(node->head.nodeClass == UA_NODECLASS_REFERENCETYPE) { retval = setReferenceTypeSubtypes(server, &node->referenceTypeNode); if(retval != UA_STATUSCODE_GOOD) goto cleanup; } /* Check NodeClass for 'hasSubtype'. UA_NODECLASS_VARIABLE not allowed * to have subtype */ if(node->head.nodeClass == UA_NODECLASS_VARIABLE) { for(size_t i = 0; i < node->head.referencesSize; i++) { if(node->head.references[i].referenceTypeIndex == UA_REFERENCETYPEINDEX_HASSUBTYPE) { UA_LOG_NODEID_INFO(&node->head.nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNode (%.*s): Variable not allowed " "to have HasSubType reference", (int)nodeIdStr.length, nodeIdStr.data)); retval = UA_STATUSCODE_BADREFERENCENOTALLOWED; goto cleanup; } } } /* Get the type node */ if(node->head.nodeClass == UA_NODECLASS_VARIABLE || node->head.nodeClass == UA_NODECLASS_VARIABLETYPE || node->head.nodeClass == UA_NODECLASS_OBJECT) { type = getNodeType(server, &node->head); if(!type) { if(server->bootstrapNS0) goto constructor; logAddNode(&server->config.logger, session, &node->head.nodeId, "Node type not found"); retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID; goto cleanup; } } /* Initialize and typecheck the variable */ if(node->head.nodeClass == UA_NODECLASS_VARIABLE || node->head.nodeClass == UA_NODECLASS_VARIABLETYPE) { /* Use attributes from the type. The value and value constraints are the * same for the variable and variabletype attribute structs. */ retval = useVariableTypeAttributes(server, session, &node->variableNode, &type->variableTypeNode); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_NODEID_INFO(&node->head.nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNode (%.*s): Using attributes for from " "the variable type failed with error code %s", (int)nodeIdStr.length, nodeIdStr.data, UA_StatusCode_name(retval))); goto cleanup; } /* Get a new pointer to the node. It might have been switched out */ #ifdef UA_ENABLE_IMMUTABLE_NODES UA_NODESTORE_RELEASE(server, node); node = UA_NODESTORE_GET(server, nodeId); if(!node || (node->head.nodeClass != UA_NODECLASS_VARIABLE && node->head.nodeClass != UA_NODECLASS_VARIABLETYPE)) { retval = UA_STATUSCODE_BADINTERNALERROR; goto cleanup; } #endif /* 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, &node->variableNode, &type->variableTypeNode); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_NODEID_INFO(&node->head.nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNode (%.*s): Type-checking " "failed with error code %s", (int)nodeIdStr.length, nodeIdStr.data, UA_StatusCode_name(retval))); goto cleanup; } } /* Add (mandatory) child nodes from the type definition */ if(node->head.nodeClass == UA_NODECLASS_VARIABLE || node->head.nodeClass == UA_NODECLASS_OBJECT) { retval = addTypeChildren(server, session, nodeId, &type->head.nodeId); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_NODEID_INFO(&node->head.nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNode (%.*s): Adding child nodes " "failed with error code %s", (int)nodeIdStr.length, nodeIdStr.data, UA_StatusCode_name(retval))); goto cleanup; } } /* Add (mandatory) child nodes from the HasInterface references */ if(node->head.nodeClass == UA_NODECLASS_OBJECT) { retval = addInterfaceChildren(server, session, nodeId, &type->head.nodeId); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_NODEID_INFO(&node->head.nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNode (%.*s): Adding child nodes " "interface failed with error code %s", (int)nodeIdStr.length, nodeIdStr.data, UA_StatusCode_name(retval))); goto cleanup; } } /* Set variables to dynamic (source and server timestamps are meaningful) if * they fulfill some conditions */ if(node->head.nodeClass == UA_NODECLASS_VARIABLE) { retval = checkSetIsDynamicVariable(server, session, nodeId); if(retval != UA_STATUSCODE_GOOD) goto cleanup; } /* Call the constructor(s) */ constructor: if(!node->head.constructed) retval = recursiveCallConstructors(server, session, nodeId, type); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_NODEID_INFO(&node->head.nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "AddNode (%.*s): Calling the node constructor(s) " "failed with status code %s", (int)nodeIdStr.length, nodeIdStr.data, UA_StatusCode_name(retval))); } cleanup: if(type) UA_NODESTORE_RELEASE(server, type); if(node) UA_NODESTORE_RELEASE(server, node); if(retval != UA_STATUSCODE_GOOD) deleteNode(server, *nodeId, true); 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_clear(&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"); UA_LOCK_ASSERT(&server->serviceMutex, 1); 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 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) { UA_LOCK_ASSERT(&server->serviceMutex, 1); /* 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; UA_ExtensionObject_setValueNoDelete(&item.nodeAttributes, (void*)(uintptr_t)attr, attributeType); /* 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_clear(&result.addedNodeId); return result.statusCode; } 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) { UA_LOCK(&server->serviceMutex); UA_StatusCode reval = addNode(server, nodeClass, requestedNewNodeId, parentNodeId, referenceTypeId, browseName, typeDefinition, attr, attributeType, nodeContext, outNewNodeId); UA_UNLOCK(&server->serviceMutex); return reval; } 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; UA_ExtensionObject_setValueNoDelete(&item.nodeAttributes, (void*)(uintptr_t)attr, attributeType); UA_LOCK(&server->serviceMutex); UA_StatusCode retval = Operation_addNode_begin(server, &server->adminSession, nodeContext, &item, &parentNodeId, &referenceTypeId, outNewNodeId); UA_UNLOCK(&server->serviceMutex); return retval; } UA_StatusCode UA_Server_addNode_finish(UA_Server *server, const UA_NodeId nodeId) { UA_LOCK(&server->serviceMutex); UA_StatusCode retval = AddNode_finish(server, &server->adminSession, &nodeId); UA_UNLOCK(&server->serviceMutex); return retval; } /****************/ /* 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_NodeHead *head) { UA_DeleteReferencesItem item; UA_DeleteReferencesItem_init(&item); item.targetNodeId.nodeId = head->nodeId; item.deleteBidirectional = false; UA_StatusCode dummy; for(size_t i = 0; i < head->referencesSize; ++i) { const UA_NodeReferenceKind *rk = &head->references[i]; item.isForward = rk->isInverse; item.referenceTypeId = *UA_NODESTORE_GETREFERENCETYPEID(server, rk->referenceTypeIndex); const UA_ReferenceTarget *t = NULL; while((t = UA_NodeReferenceKind_iterate(rk, t))) { if(!UA_NodePointer_isLocal(t->targetId)) continue; item.sourceNodeId = UA_NodePointer_toNodeId(t->targetId); Operation_deleteReference(server, session, NULL, &item, &dummy); } } } /* A node is auto-deleted if all its hierarchical parents are being deleted */ static UA_Boolean hasParentRef(const UA_NodeHead *head, const UA_ReferenceTypeSet *refSet, RefTree *refTree) { for(size_t i = 0; i < head->referencesSize; i++) { const UA_NodeReferenceKind *rk = &head->references[i]; if(!rk->isInverse) continue; if(!UA_ReferenceTypeSet_contains(refSet, rk->referenceTypeIndex)) continue; const UA_ReferenceTarget *t = NULL; while((t = UA_NodeReferenceKind_iterate(rk, t))) { if(!UA_NodePointer_isLocal(t->targetId)) continue; UA_NodeId tmpId = UA_NodePointer_toNodeId(t->targetId); if(!RefTree_containsNodeId(refTree, &tmpId)) return true; } } return false; } static void deconstructNodeSet(UA_Server *server, UA_Session *session, UA_ReferenceTypeSet *hierarchRefsSet, RefTree *refTree) { /* Deconstruct the nodes based on the RefTree entries, parent nodes first */ for(size_t i = 0; i < refTree->size; i++) { const UA_Node *member = UA_NODESTORE_GET(server, &refTree->targets[i].nodeId); if(!member) continue; /* Call the type-level destructor */ void *context = member->head.context; /* No longer needed after this function */ if(member->head.nodeClass == UA_NODECLASS_OBJECT || member->head.nodeClass == UA_NODECLASS_VARIABLE) { const UA_Node *type = getNodeType(server, &member->head); if(type) { /* Get the lifecycle */ const UA_NodeTypeLifecycle *lifecycle; if(member->head.nodeClass == UA_NODECLASS_OBJECT) lifecycle = &type->objectTypeNode.lifecycle; else lifecycle = &type->variableTypeNode.lifecycle; /* Call the destructor */ if(lifecycle->destructor) { UA_UNLOCK(&server->serviceMutex); lifecycle->destructor(server, &session->sessionId, session->sessionHandle, &type->head.nodeId, type->head.context, &member->head.nodeId, &context); UA_LOCK(&server->serviceMutex); } /* Release the type node */ UA_NODESTORE_RELEASE(server, type); } } /* Call the global destructor */ if(server->config.nodeLifecycle.destructor) { UA_UNLOCK(&server->serviceMutex); server->config.nodeLifecycle.destructor(server, &session->sessionId, session->sessionHandle, &member->head.nodeId, context); UA_LOCK(&server->serviceMutex); } /* Release the node. Don't access the node context from here on. */ UA_NODESTORE_RELEASE(server, member); /* Set the constructed flag to false */ UA_Server_editNode(server, &server->adminSession, &refTree->targets[i].nodeId, (UA_EditNodeCallback)setDeconstructedNode, NULL); } } /* The processNodeLayer function searches all children's of the head node and * adds the children node to the RefTree if all incoming references sources are * contained in the RefTree (No external references to this node --> node can be * deleted) */ static UA_StatusCode autoDeleteChildren(UA_Server *server, UA_Session *session, RefTree *refTree, const UA_ReferenceTypeSet *hierarchRefsSet, const UA_NodeHead *head){ UA_StatusCode res = UA_STATUSCODE_GOOD; for(size_t i = 0; i < head->referencesSize; ++i) { /* Check if the ReferenceType is hierarchical */ UA_NodeReferenceKind *refs = &head->references[i]; if(!UA_ReferenceTypeSet_contains(hierarchRefsSet, refs->referenceTypeIndex)) continue; /* Check if the references are forward (to a child) */ if(refs->isInverse) continue; /* Loop over the references */ const UA_ReferenceTarget *t = NULL; while((t = UA_NodeReferenceKind_iterate(refs, t))) { /* Get the child */ const UA_Node *child = UA_NODESTORE_GETFROMREF(server, t->targetId); if(!child) continue; /* Only delete child nodes that have no other parent */ if(!hasParentRef(&child->head, hierarchRefsSet, refTree)) res = RefTree_addNodeId(refTree, &child->head.nodeId, NULL); UA_NODESTORE_RELEASE(server, child); if(res != UA_STATUSCODE_GOOD) return res; } } return UA_STATUSCODE_GOOD; } /* Build up an ordered set (tree) of all nodes that can be deleted. Step through * the ordered set in order to avoid recursion. */ static UA_StatusCode buildDeleteNodeSet(UA_Server *server, UA_Session *session, const UA_ReferenceTypeSet *hierarchRefsSet, const UA_NodeId *initial, UA_Boolean removeTargetRefs, RefTree *refTree) { /* Add the initial node to delete */ UA_StatusCode res = RefTree_addNodeId(refTree, initial, NULL); if(res != UA_STATUSCODE_GOOD) return res; /* Find out which hierarchical children should also be deleted. We know * there are no "external" ExpandedNodeId in the RefTree. */ size_t pos = 0; while(pos < refTree->size) { const UA_Node *member = UA_NODESTORE_GET(server, &refTree->targets[pos].nodeId); pos++; if(!member) continue; res |= autoDeleteChildren(server, session, refTree, hierarchRefsSet, &member->head); UA_NODESTORE_RELEASE(server, member); } return res; } static void deleteNodeSet(UA_Server *server, UA_Session *session, const UA_ReferenceTypeSet *hierarchRefsSet, UA_Boolean removeTargetRefs, RefTree *refTree) { /* Delete the nodes based on the RefTree entries */ for(size_t i = refTree->size; i > 0; --i) { const UA_Node *member = UA_NODESTORE_GET(server, &refTree->targets[i-1].nodeId); if(!member) continue; UA_NODESTORE_RELEASE(server, member); if(removeTargetRefs) removeIncomingReferences(server, session, &member->head); UA_NODESTORE_REMOVE(server, &member->head.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) { UA_UNLOCK(&server->serviceMutex); if(!server->config.accessControl. allowDeleteNode(server, &server->config.accessControl, &session->sessionId, session->sessionHandle, item)) { UA_LOCK(&server->serviceMutex); *result = UA_STATUSCODE_BADUSERACCESSDENIED; return; } UA_LOCK(&server->serviceMutex); } const UA_Node *node = UA_NODESTORE_GET(server, &item->nodeId); if(!node) { *result = UA_STATUSCODE_BADNODEIDUNKNOWN; return; } if(UA_Node_hasSubTypeOrInstances(&node->head)) { UA_LOG_NODEID_INFO(&node->head.nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session, "DeleteNode (%.*s): " "Cannot delete a type node with active instances or " "subtypes", (int)nodeIdStr.length, nodeIdStr.data)); UA_NODESTORE_RELEASE(server, 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 */ /* Relase the node. Don't access the pointer after this! */ UA_NODESTORE_RELEASE(server, node); /* 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_ReferenceTypeSet hierarchRefsSet; UA_NodeId hr = UA_NODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES); *result = referenceTypeIndices(server, &hr, &hierarchRefsSet, true); if(*result != UA_STATUSCODE_GOOD) return; /* The list of childs is needed for the deconstructing and deleting phase. * Within the processNodeLayer we generate a RefTree based set of childs * which can be deleted beside the parent node. */ RefTree refTree; *result = RefTree_init(&refTree); if(*result != UA_STATUSCODE_GOOD) return; *result = buildDeleteNodeSet(server, session, &hierarchRefsSet, &item->nodeId, item->deleteTargetReferences, &refTree); if(*result != UA_STATUSCODE_GOOD) { UA_LOG_WARNING_SESSION(&server->config.logger, session, "DeleteNode: Incomplete lookup of nodes. " "Still deleting what we have."); /* Continue, so the RefTree is cleaned up. Return the error message * anyway. */ } /* Deconstruct, then delete, then clean up the set */ deconstructNodeSet(server, session, &hierarchRefsSet, &refTree); deleteNodeSet(server, session, &hierarchRefsSet, item->deleteTargetReferences, &refTree); RefTree_clear(&refTree); } 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"); UA_LOCK_ASSERT(&server->serviceMutex, 1); 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_LOCK(&server->serviceMutex); UA_StatusCode retval = deleteNode(server, nodeId, deleteReferences); UA_UNLOCK(&server->serviceMutex); return retval; } UA_StatusCode deleteNode(UA_Server *server, const UA_NodeId nodeId, UA_Boolean deleteReferences) { UA_LOCK_ASSERT(&server->serviceMutex, 1); 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 */ /******************/ struct AddNodeInfo { UA_Byte refTypeIndex; UA_Boolean isForward; const UA_ExpandedNodeId *targetNodeId; UA_UInt32 targetBrowseNameHash; }; static UA_StatusCode addOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node, const struct AddNodeInfo *info) { return UA_Node_addReference(node, info->refTypeIndex, info->isForward, info->targetNodeId, info->targetBrowseNameHash); } static UA_StatusCode deleteOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node, const UA_DeleteReferencesItem *item) { const UA_Node *refType = UA_NODESTORE_GET(server, &item->referenceTypeId); if(!refType) return UA_STATUSCODE_BADREFERENCETYPEIDINVALID; if(refType->head.nodeClass != UA_NODECLASS_REFERENCETYPE) { UA_NODESTORE_RELEASE(server, refType); return UA_STATUSCODE_BADREFERENCETYPEIDINVALID; } UA_Byte refTypeIndex = refType->referenceTypeNode.referenceTypeIndex; UA_NODESTORE_RELEASE(server, refType); return UA_Node_deleteReference(node, refTypeIndex, item->isForward, &item->targetNodeId); } static void Operation_addReference(UA_Server *server, UA_Session *session, void *context, const UA_AddReferencesItem *item, UA_StatusCode *retval) { (void)context; UA_assert(session); /* Check access rights */ if(session != &server->adminSession && server->config.accessControl.allowAddReference) { UA_UNLOCK(&server->serviceMutex); if (!server->config.accessControl. allowAddReference(server, &server->config.accessControl, &session->sessionId, session->sessionHandle, item)) { UA_LOCK(&server->serviceMutex); *retval = UA_STATUSCODE_BADUSERACCESSDENIED; return; } UA_LOCK(&server->serviceMutex); } /* TODO: Currently no expandednodeids are allowed */ if(item->targetServerUri.length > 0) { *retval = UA_STATUSCODE_BADNOTIMPLEMENTED; return; } /* Check the ReferenceType and get the index */ const UA_Node *refType = UA_NODESTORE_GET(server, &item->referenceTypeId); if(!refType) { *retval = UA_STATUSCODE_BADREFERENCETYPEIDINVALID; return; } if(refType->head.nodeClass != UA_NODECLASS_REFERENCETYPE) { UA_NODESTORE_RELEASE(server, refType); *retval = UA_STATUSCODE_BADREFERENCETYPEIDINVALID; return; } UA_Byte refTypeIndex = refType->referenceTypeNode.referenceTypeIndex; UA_NODESTORE_RELEASE(server, refType); /* Get the source and target node BrowseName hash */ const UA_Node *targetNode = UA_NODESTORE_GET(server, &item->targetNodeId.nodeId); if(!targetNode) { *retval = UA_STATUSCODE_BADTARGETNODEIDINVALID; return; } UA_UInt32 targetNameHash = UA_QualifiedName_hash(&targetNode->head.browseName); UA_NODESTORE_RELEASE(server, targetNode); const UA_Node *sourceNode = UA_NODESTORE_GET(server, &item->sourceNodeId); if(!sourceNode) { *retval = UA_STATUSCODE_BADSOURCENODEIDINVALID; return; } UA_UInt32 sourceNameHash = UA_QualifiedName_hash(&sourceNode->head.browseName); UA_NODESTORE_RELEASE(server, sourceNode); /* Compute the BrowseName hash and release the target */ struct AddNodeInfo info; info.refTypeIndex = refTypeIndex; info.targetNodeId = &item->targetNodeId; info.isForward = item->isForward; info.targetBrowseNameHash = targetNameHash; /* Add the first direction */ *retval = UA_Server_editNode(server, session, &item->sourceNodeId, (UA_EditNodeCallback)addOneWayReference, &info); UA_Boolean firstExisted = false; if(*retval == UA_STATUSCODE_BADDUPLICATEREFERENCENOTALLOWED) { *retval = UA_STATUSCODE_GOOD; firstExisted = true; } if(*retval != UA_STATUSCODE_GOOD) return; /* Add the second direction */ UA_ExpandedNodeId target2; UA_ExpandedNodeId_init(&target2); target2.nodeId = item->sourceNodeId; info.targetNodeId = &target2; info.isForward = !info.isForward; info.targetBrowseNameHash = sourceNameHash; *retval = UA_Server_editNode(server, session, &item->targetNodeId.nodeId, (UA_EditNodeCallback)addOneWayReference, &info); /* Second direction existed already */ if(*retval == UA_STATUSCODE_BADDUPLICATEREFERENCENOTALLOWED) { /* Calculate common duplicate reference not allowed result and set bad * result if BOTH directions already existed */ if(UA_NodeId_equal(&item->sourceNodeId, &item->targetNodeId.nodeId)) { *retval = UA_STATUSCODE_GOOD; UA_LOG_INFO_SESSION(&server->config.logger, session, "The source node and the target node are identical. The check for duplicate references is skipped."); } else if(firstExisted) { *retval = UA_STATUSCODE_BADDUPLICATEREFERENCENOTALLOWED; return; } *retval = UA_STATUSCODE_GOOD; } /* Remove first direction if the second direction failed */ 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 status code */ UA_Server_editNode(server, session, &item->sourceNodeId, (UA_EditNodeCallback)deleteOneWayReference, &deleteItem); } } 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"); UA_LOCK_ASSERT(&server->serviceMutex, 1); UA_assert(session); 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; UA_LOCK(&server->serviceMutex); Operation_addReference(server, &server->adminSession, NULL, &item, &retval); UA_UNLOCK(&server->serviceMutex); 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) { UA_UNLOCK(&server->serviceMutex); if (!server->config.accessControl. allowDeleteReference(server, &server->config.accessControl, &session->sessionId, session->sessionHandle, item)){ UA_LOCK(&server->serviceMutex); *retval = UA_STATUSCODE_BADUSERACCESSDENIED; return; } UA_LOCK(&server->serviceMutex); } // 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"); UA_LOCK_ASSERT(&server->serviceMutex, 1); 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; UA_LOCK(&server->serviceMutex); Operation_deleteReference(server, &server->adminSession, NULL, &item, &retval); UA_UNLOCK(&server->serviceMutex); return retval; } /**********************/ /* Set Value Callback */ /**********************/ static UA_StatusCode setValueCallback(UA_Server *server, UA_Session *session, UA_VariableNode *node, const UA_ValueCallback *callback) { if(node->head.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) { UA_LOCK(&server->serviceMutex); UA_StatusCode retval = UA_Server_editNode(server, &server->adminSession, &nodeId, (UA_EditNodeCallback)setValueCallback, /* cast away const because * callback uses const anyway */ (UA_ValueCallback *)(uintptr_t) &callback); UA_UNLOCK(&server->serviceMutex); return retval; } /***************************************************/ /* 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; UA_ExtensionObject_setValueNoDelete(&item.nodeAttributes, (void*)(uintptr_t)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES]); UA_NodeId newNodeId; if(!outNewNodeId) { newNodeId = UA_NODEID_NULL; outNewNodeId = &newNodeId; } UA_LOCK(&server->serviceMutex); /* 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 = 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: UA_UNLOCK(&server->serviceMutex); if(outNewNodeId == &newNodeId) UA_NodeId_clear(&newNodeId); return retval; } static UA_StatusCode setDataSource(UA_Server *server, UA_Session *session, UA_VariableNode *node, const UA_DataSource *dataSource) { if(node->head.nodeClass != UA_NODECLASS_VARIABLE) return UA_STATUSCODE_BADNODECLASSINVALID; if(node->valueSource == UA_VALUESOURCE_DATA) UA_DataValue_clear(&node->value.data.value); node->value.dataSource = *dataSource; node->valueSource = UA_VALUESOURCE_DATASOURCE; return UA_STATUSCODE_GOOD; } UA_StatusCode setVariableNode_dataSource(UA_Server *server, const UA_NodeId nodeId, const UA_DataSource dataSource) { UA_LOCK_ASSERT(&server->serviceMutex, 1); 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); } UA_StatusCode UA_Server_setVariableNode_dataSource(UA_Server *server, const UA_NodeId nodeId, const UA_DataSource dataSource) { UA_LOCK(&server->serviceMutex); UA_StatusCode retval = setVariableNode_dataSource(server, nodeId, dataSource); UA_UNLOCK(&server->serviceMutex); return retval; } /******************************/ /* Set External Value Source */ /******************************/ static UA_StatusCode setExternalValueSource(UA_Server *server, UA_Session *session, UA_VariableNode *node, const UA_ValueBackend *externalValueSource) { if(node->head.nodeClass != UA_NODECLASS_VARIABLE) return UA_STATUSCODE_BADNODECLASSINVALID; node->valueBackend.backendType = UA_VALUEBACKENDTYPE_EXTERNAL; node->valueBackend.backend.external.value = externalValueSource->backend.external.value; node->valueBackend.backend.external.callback.notificationRead = externalValueSource->backend.external.callback.notificationRead; node->valueBackend.backend.external.callback.userWrite = externalValueSource->backend.external.callback.userWrite; return UA_STATUSCODE_GOOD; } /****************************/ /* Set Data Source Callback */ /****************************/ static UA_StatusCode setDataSourceCallback(UA_Server *server, UA_Session *session, UA_VariableNode *node, const UA_DataSource *dataSource) { if(node->head.nodeClass != UA_NODECLASS_VARIABLE) return UA_STATUSCODE_BADNODECLASSINVALID; node->valueBackend.backendType = UA_VALUEBACKENDTYPE_DATA_SOURCE_CALLBACK; node->valueBackend.backend.dataSource.read = dataSource->read; node->valueBackend.backend.dataSource.write = dataSource->write; return UA_STATUSCODE_GOOD; } /**********************/ /* Set Value Backend */ /**********************/ UA_StatusCode UA_Server_setVariableNode_valueBackend(UA_Server *server, const UA_NodeId nodeId, const UA_ValueBackend valueBackend){ UA_StatusCode retval = UA_STATUSCODE_GOOD; UA_LOCK(&server->serviceMutex); switch(valueBackend.backendType){ case UA_VALUEBACKENDTYPE_NONE: UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_BADCONFIGURATIONERROR; case UA_VALUEBACKENDTYPE_DATA_SOURCE_CALLBACK: retval = UA_Server_editNode(server, &server->adminSession, &nodeId, (UA_EditNodeCallback) setDataSourceCallback, (UA_DataSource *)(uintptr_t) &valueBackend.backend.dataSource); break; case UA_VALUEBACKENDTYPE_INTERNAL: break; case UA_VALUEBACKENDTYPE_EXTERNAL: retval = UA_Server_editNode(server, &server->adminSession, &nodeId, (UA_EditNodeCallback) setExternalValueSource, /* cast away const because callback uses const anyway */ (UA_ValueCallback *)(uintptr_t) &valueBackend); break; } // UA_StatusCode retval = UA_Server_editNode(server, &server->adminSession, &nodeId, // (UA_EditNodeCallback)setValueCallback, /* cast away const because callback uses const anyway */ // (UA_ValueCallback *)(uintptr_t) &callback); UA_UNLOCK(&server->serviceMutex); return retval; } /************************************/ /* 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) { deleteNode(server, nodeId, true); UA_BrowseResult_clear(&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 = addNode(server, UA_NODECLASS_VARIABLE, &inputArgumentsRequestedNewNodeId, &nodeId, &hasproperty, UA_QUALIFIEDNAME(0, name), &propertytype, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES], 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 = addNode(server, UA_NODECLASS_VARIABLE, &outputArgumentsRequestedNewNodeId, &nodeId, &hasproperty, UA_QUALIFIEDNAME(0, name), &propertytype, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES], NULL, &outputArgsId); if(retval != UA_STATUSCODE_GOOD) goto error; } retval = 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_clear(&br); return retval; error: deleteNode(server, nodeId, true); deleteNode(server, inputArgsId, true); deleteNode(server, outputArgsId, true); UA_BrowseResult_clear(&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) { UA_LOCK(&server->serviceMutex); UA_StatusCode retval = UA_Server_addMethodNodeEx_finish(server, nodeId, method, inputArgumentsSize, inputArguments, UA_NODEID_NULL, NULL, outputArgumentsSize, outputArguments, UA_NODEID_NULL, NULL); UA_UNLOCK(&server->serviceMutex); return retval; } 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; UA_ExtensionObject_setValueNoDelete(&item.nodeAttributes, (void*)(uintptr_t)&attr, &UA_TYPES[UA_TYPES_METHODATTRIBUTES]); UA_NodeId newId; if(!outNewNodeId) { UA_NodeId_init(&newId); outNewNodeId = &newId; } UA_LOCK(&server->serviceMutex); UA_StatusCode retval = Operation_addNode_begin(server, &server->adminSession, nodeContext, &item, &parentNodeId, &referenceTypeId, outNewNodeId); if(retval != UA_STATUSCODE_GOOD) { UA_UNLOCK(&server->serviceMutex); return retval; } retval = UA_Server_addMethodNodeEx_finish(server, *outNewNodeId, method, inputArgumentsSize, inputArguments, inputArgumentsRequestedNewNodeId, inputArgumentsOutNewNodeId, outputArgumentsSize, outputArguments, outputArgumentsRequestedNewNodeId, outputArgumentsOutNewNodeId); UA_UNLOCK(&server->serviceMutex); if(outNewNodeId == &newId) UA_NodeId_clear(&newId); return retval; } static UA_StatusCode editMethodCallback(UA_Server *server, UA_Session* session, UA_Node *node, UA_MethodCallback methodCallback) { if(node->head.nodeClass != UA_NODECLASS_METHOD) return UA_STATUSCODE_BADNODECLASSINVALID; node->methodNode.method = methodCallback; return UA_STATUSCODE_GOOD; } static UA_StatusCode setMethodNode_callback(UA_Server *server, const UA_NodeId methodNodeId, UA_MethodCallback methodCallback) { UA_LOCK_ASSERT(&server->serviceMutex, 1); return UA_Server_editNode(server, &server->adminSession, &methodNodeId, (UA_EditNodeCallback)editMethodCallback, (void*)(uintptr_t)methodCallback); } UA_StatusCode UA_Server_setMethodNodeCallback(UA_Server *server, const UA_NodeId methodNodeId, UA_MethodCallback methodCallback) { UA_LOCK(&server->serviceMutex); UA_StatusCode retVal = setMethodNode_callback(server, methodNodeId, methodCallback); UA_UNLOCK(&server->serviceMutex); return retVal; } UA_StatusCode UA_Server_getMethodNodeCallback(UA_Server *server, const UA_NodeId methodNodeId, UA_MethodCallback *outMethodCallback) { UA_LOCK(&server->serviceMutex); const UA_Node *node = UA_NODESTORE_GET(server, &methodNodeId); if(!node) { UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_BADNODEIDUNKNOWN; } if(node->head.nodeClass != UA_NODECLASS_METHOD) { UA_NODESTORE_RELEASE(server, node); UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_BADNODECLASSINVALID; } *outMethodCallback = node->methodNode.method; UA_NODESTORE_RELEASE(server, node); UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_GOOD; } #endif /************************/ /* Lifecycle Management */ /************************/ void UA_EXPORT UA_Server_setAdminSessionContext(UA_Server *server, void *context) { server->adminSession.sessionHandle = context; } static UA_StatusCode setNodeTypeLifecycle(UA_Server *server, UA_Session *session, UA_Node *node, UA_NodeTypeLifecycle *lifecycle) { if(node->head.nodeClass == UA_NODECLASS_OBJECTTYPE) { node->objectTypeNode.lifecycle = *lifecycle; } else if(node->head.nodeClass == UA_NODECLASS_VARIABLETYPE) { node->variableTypeNode.lifecycle = *lifecycle; } else { return UA_STATUSCODE_BADNODECLASSINVALID; } return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Server_setNodeTypeLifecycle(UA_Server *server, UA_NodeId nodeId, UA_NodeTypeLifecycle lifecycle) { UA_LOCK(&server->serviceMutex); UA_StatusCode retval = UA_Server_editNode(server, &server->adminSession, &nodeId, (UA_EditNodeCallback)setNodeTypeLifecycle, &lifecycle); UA_UNLOCK(&server->serviceMutex); return retval; } /**** amalgamated original file "/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) #if UA_MULTITHREADING >= 100 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_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.mdnsConfig.serverCapabilities, server->config.mdnsConfig.serverCapabilitiesSize, true); 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) { initMulticastDiscoveryServer(&server->discoveryManager, server); UA_String *appName = &server->config.mdnsConfig.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); #if UA_MULTITHREADING >= 100 multicastListenStart(server); # endif } void stopMulticastDiscoveryServer(UA_Server *server) { if (!server->discoveryManager.mdnsDaemon) return; for (size_t i=0; iconfig.networkLayersSize; i++) { UA_String hostname = UA_STRING_NULL; UA_String path = UA_STRING_NULL; UA_UInt16 port = 0; UA_StatusCode retval = UA_parseEndpointUrl(&server->config.networkLayers[i].discoveryUrl, &hostname, &port, &path); if (retval != UA_STATUSCODE_GOOD) continue; UA_Discovery_removeRecord(server, &server->config.mdnsConfig.mdnsServerName, &hostname, port, true); } #if UA_MULTITHREADING >= 100 multicastListenStop(server); # else // send out last package with TTL = 0 iterateMulticastDiscoveryServer(server, NULL, false); # endif } /* All filter criteria must be fulfilled in the list entry. The comparison is case * insensitive. * @returns true if the entry matches the filter. False if the filter does not match. * */ static UA_Boolean entryMatchesCapabilityFilter(size_t serverCapabilityFilterSize, UA_String *serverCapabilityFilter, serverOnNetwork_list_entry* current) { // if the entry has less capabilities defined than the filter, there's no match if (serverCapabilityFilterSize > current->serverOnNetwork.serverCapabilitiesSize) return UA_FALSE; for(size_t i = 0; i < serverCapabilityFilterSize; i++) { UA_Boolean capabilityFound = UA_FALSE; for(size_t j = 0; j < current->serverOnNetwork.serverCapabilitiesSize; j++) { if(UA_String_equal_ignorecase(&serverCapabilityFilter[i], ¤t->serverOnNetwork.serverCapabilities[j])) { capabilityFound = UA_TRUE; break; } } if (!capabilityFound) return UA_FALSE; // entry does not match capability } return UA_TRUE; } void Service_FindServersOnNetwork(UA_Server *server, UA_Session *session, const UA_FindServersOnNetworkRequest *request, UA_FindServersOnNetworkResponse *response) { UA_LOCK_ASSERT(&server->serviceMutex, 1); if (!server->config.mdnsEnabled) { 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(!entryMatchesCapabilityFilter(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, false); 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) { UA_LOCK(&server->serviceMutex); server->discoveryManager.serverOnNetworkCallback = cb; server->discoveryManager.serverOnNetworkCallbackData = data; UA_UNLOCK(&server->serviceMutex); } 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; //replace all dots with minus. Otherwise mDNS is not valid for (size_t i=servernameLen+1; idata); 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, UA_Boolean isSelf) { // 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); if (isSelf && server->discoveryManager.selfFqdnMdnsRecord.length == 0) { server->discoveryManager.selfFqdnMdnsRecord = UA_STRING_ALLOC(fullServiceDomain); if (!server->discoveryManager.selfFqdnMdnsRecord.data) return UA_STATUSCODE_BADOUTOFMEMORY; } struct serverOnNetwork_list_entry *listEntry; // The servername is servername + hostname. It is the same which we get through mDNS and therefore we need to match servername UA_StatusCode retval = UA_DiscoveryManager_addEntryToServersOnNetwork(server, fullServiceDomain, fullServiceDomain, UA_MIN(63, (servernameLen+hostnameLen)+1), &listEntry); if (retval != UA_STATUSCODE_GOOD && retval != UA_STATUSCODE_BADALREADYEXISTS) return retval; // If entry is already in list, skip initialization of capabilities and txt+srv if (retval != UA_STATUSCODE_BADALREADYEXISTS) { // if capabilitiesSize is 0, then add default cap 'NA' listEntry->serverOnNetwork.serverCapabilitiesSize = UA_MAX(1, capabilitiesSize); listEntry->serverOnNetwork.serverCapabilities = (UA_String *) UA_Array_new(listEntry->serverOnNetwork.serverCapabilitiesSize, &UA_TYPES[UA_TYPES_STRING]); if (!listEntry->serverOnNetwork.serverCapabilities) return UA_STATUSCODE_BADOUTOFMEMORY; if (capabilitiesSize == 0) { UA_String na; na.length = 2; na.data = (UA_Byte *) (uintptr_t) "NA"; UA_String_copy(&na, &listEntry->serverOnNetwork.serverCapabilities[0]); } else { for (size_t i = 0; i < capabilitiesSize; i++) { UA_String_copy(&capabilites[i], &listEntry->serverOnNetwork.serverCapabilities[i]); } } listEntry->txtSet = true; UA_STACKARRAY(char, newUrl, 10 + hostnameLen + 8 + path->length + 1); UA_snprintf(newUrl, 10 + hostnameLen + 8 + path->length + 1, "opc.tcp://%.*s:%d%s%.*s", (int) hostnameLen, hostname->data, port, path->length > 0 ? "/" : "", (int) path->length, path->data); listEntry->serverOnNetwork.discoveryUrl = UA_String_fromChars(newUrl); listEntry->srvSet = true; } // _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); UA_StatusCode retval = UA_DiscoveryManager_removeEntryFromServersOnNetwork( server, fullServiceDomain, fullServiceDomain, UA_MIN(63, (servernameLen+hostnameLen)+1)); if (retval != UA_STATUSCODE_GOOD) return retval; // _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 "/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-2020 (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 * Copyright 2020 (c) Christian von Arnim, ISW University of Stuttgart */ /********************/ /* Client Lifecycle */ /********************/ static void UA_Client_init(UA_Client* client) { UA_SecureChannel_init(&client->channel, &client->config.localConnectionConfig); client->connectStatus = UA_STATUSCODE_GOOD; UA_Timer_init(&client->timer); notifyClientState(client); } UA_Client UA_EXPORT * UA_Client_newWithConfig(const UA_ClientConfig *config) { if(!config) return NULL; UA_Client *client = (UA_Client*)UA_malloc(sizeof(UA_Client)); if(!client) return NULL; memset(client, 0, sizeof(UA_Client)); client->config = *config; UA_Client_init(client); return client; } static void UA_ClientConfig_clear(UA_ClientConfig *config) { UA_ApplicationDescription_clear(&config->clientDescription); UA_ExtensionObject_clear(&config->userIdentityToken); UA_String_clear(&config->securityPolicyUri); UA_EndpointDescription_clear(&config->endpoint); UA_UserTokenPolicy_clear(&config->userTokenPolicy); UA_String_clear(&config->applicationUri); if(config->certificateVerification.clear) config->certificateVerification.clear(&config->certificateVerification); /* Delete the SecurityPolicies */ if(config->securityPolicies == 0) return; for(size_t i = 0; i < config->securityPoliciesSize; i++) config->securityPolicies[i].clear(&config->securityPolicies[i]); UA_free(config->securityPolicies); config->securityPolicies = 0; /* Logger */ if(config->logger.clear) config->logger.clear(config->logger.context); config->logger.log = NULL; config->logger.clear = NULL; if (config->sessionLocaleIdsSize > 0 && config->sessionLocaleIds) { UA_Array_delete(config->sessionLocaleIds, config->sessionLocaleIdsSize, &UA_TYPES[UA_TYPES_LOCALEID]); } config->sessionLocaleIds = NULL; config->sessionLocaleIdsSize = 0; } static void UA_Client_clear(UA_Client *client) { /* Prevent new async service calls in UA_Client_AsyncService_removeAll */ UA_SessionState oldState = client->sessionState; client->sessionState = UA_SESSIONSTATE_CLOSING; /* Delete the async service calls with BADHSUTDOWN */ UA_Client_AsyncService_removeAll(client, UA_STATUSCODE_BADSHUTDOWN); /* Reset to the old state to properly close the session */ client->sessionState = oldState; UA_Client_disconnect(client); UA_String_clear(&client->endpointUrl); UA_String_clear(&client->discoveryUrl); UA_String_clear(&client->remoteNonce); UA_String_clear(&client->localNonce); /* Delete the subscriptions */ #ifdef UA_ENABLE_SUBSCRIPTIONS UA_Client_Subscriptions_clean(client); #endif /* Delete the timed work */ UA_Timer_clear(&client->timer); } void UA_Client_delete(UA_Client* client) { UA_Client_clear(client); UA_ClientConfig_clear(&client->config); UA_free(client); } void UA_Client_getState(UA_Client *client, UA_SecureChannelState *channelState, UA_SessionState *sessionState, UA_StatusCode *connectStatus) { if(channelState) *channelState = client->channel.state; if(sessionState) *sessionState = client->sessionState; if(connectStatus) *connectStatus = client->connectStatus; } UA_ClientConfig * UA_Client_getConfig(UA_Client *client) { if(!client) return NULL; return &client->config; } #if UA_LOGLEVEL <= 300 static const char *channelStateTexts[9] = { "Fresh", "HELSent", "HELReceived", "ACKSent", "AckReceived", "OPNSent", "Open", "Closing", "Closed"}; static const char *sessionStateTexts[6] = {"Closed", "CreateRequested", "Created", "ActivateRequested", "Activated", "Closing"}; #endif void notifyClientState(UA_Client *client) { if(client->connectStatus == client->oldConnectStatus && client->channel.state == client->oldChannelState && client->sessionState == client->oldSessionState) return; #if UA_LOGLEVEL <= 300 UA_Boolean info = (client->connectStatus != UA_STATUSCODE_GOOD); if(client->oldChannelState != client->channel.state) info |= (client->channel.state == UA_SECURECHANNELSTATE_OPEN || client->channel.state == UA_SECURECHANNELSTATE_CLOSED); if(client->oldSessionState != client->sessionState) info |= (client->sessionState == UA_SESSIONSTATE_CREATED || client->sessionState == UA_SESSIONSTATE_ACTIVATED || client->sessionState == UA_SESSIONSTATE_CLOSED); const char *channelStateText = channelStateTexts[client->channel.state]; const char *sessionStateText = sessionStateTexts[client->sessionState]; const char *connectStatusText = UA_StatusCode_name(client->connectStatus); if(info) UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Client Status: ChannelState: %s, SessionState: %s, ConnectStatus: %s", channelStateText, sessionStateText, connectStatusText); else UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Client Status: ChannelState: %s, SessionState: %s, ConnectStatus: %s", channelStateText, sessionStateText, connectStatusText); #endif client->oldConnectStatus = client->connectStatus; client->oldChannelState = client->channel.state; client->oldSessionState = client->sessionState; if(client->config.stateCallback) client->config.stateCallback(client, client->channel.state, client->sessionState, client->connectStatus); } /****************/ /* 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) { /* Renew SecureChannel if necessary */ UA_Client_renewSecureChannel(client); if(client->connectStatus != UA_STATUSCODE_GOOD) return client->connectStatus; /* Adjusting the request header. The const attribute is violated, but we * only touch the following members: */ UA_RequestHeader *rr = (UA_RequestHeader*)(uintptr_t)request; UA_NodeId oldToken = rr->authenticationToken; /* Put back in place later */ rr->authenticationToken = client->authenticationToken; rr->timestamp = UA_DateTime_now(); rr->requestHandle = ++client->requestHandle; UA_UInt32 rqId = ++client->requestId; #ifdef UA_ENABLE_TYPEDESCRIPTION UA_LOG_DEBUG_CHANNEL(&client->config.logger, &client->channel, "Sending request with RequestId %u of type %s", (unsigned)rqId, requestType->typeName); #else UA_LOG_DEBUG_CHANNEL(&client->config.logger, &client->channel, "Sending request with RequestId %u of type %" PRIu32, (unsigned)rqId, requestType->binaryEncodingId.identifier.numeric); #endif /* Send the message */ UA_StatusCode retval = UA_SecureChannel_sendSymmetricMessage(&client->channel, rqId, UA_MESSAGETYPE_MSG, rr, requestType); rr->authenticationToken = oldToken; /* Set the original token */ *requestId = rqId; return retval; } 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; } /* Part 6, 6.7.6: After the security validation is complete the receiver * shall verify the RequestId and the SequenceNumber. If these checks fail a * Bad_SecurityChecksFailed error is reported. The RequestId only needs to * be verified by the Client since only the Client knows if it is valid or * not.*/ if(!ac) return UA_STATUSCODE_BADSECURITYCHECKSFAILED; /* Dequeue ac. We might disconnect (remove all ac) in the callback. */ LIST_REMOVE(ac, pointers); /* Verify the type of the response */ UA_Response response; const UA_DataType *responseType = ac->responseType; UA_StatusCode retval = UA_STATUSCODE_GOOD; if(!UA_NodeId_equal(responseTypeId, &ac->responseType->binaryEncodingId)) { 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_decodeBinaryInternal(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", (unsigned)requestId, UA_StatusCode_name(retval)); response.responseHeader.serviceResult = retval; } else if(response.responseHeader.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(response.responseHeader.serviceResult)); } /* Call the callback */ if(ac->callback) ac->callback(client, ac->userdata, requestId, &response); UA_clear(&response, ac->responseType); /* Remove the callback */ 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 UA_StatusCode processServiceResponse(void *application, UA_SecureChannel *channel, UA_MessageType messageType, UA_UInt32 requestId, UA_ByteString *message) { SyncResponseDescription *rd = (SyncResponseDescription*)application; /* Process ACK response */ switch(messageType) { case UA_MESSAGETYPE_ACK: processACKResponse(rd->client, message); return UA_STATUSCODE_GOOD; case UA_MESSAGETYPE_OPN: processOPNResponse(rd->client, message); return UA_STATUSCODE_GOOD; case UA_MESSAGETYPE_ERR: processERRResponse(rd->client, message); return UA_STATUSCODE_GOOD; case UA_MESSAGETYPE_MSG: /* Continue below */ break; default: UA_LOG_TRACE_CHANNEL(&rd->client->config.logger, channel, "Invalid message type"); channel->state = UA_SECURECHANNELSTATE_CLOSING; return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; } /* 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 */ if(!UA_NodeId_equal(&responseId, &rd->responseType->binaryEncodingId)) { if(UA_NodeId_equal(&responseId, &serviceFaultId)) { UA_init(rd->response, rd->responseType); retval = UA_decodeBinaryInternal(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_TYPEDESCRIPTION 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 %" PRIu32, responseId.identifier.numeric); #endif /* Decode the response */ retval = UA_decodeBinaryInternal(message, &offset, rd->response, rd->responseType, rd->client->config.customDataTypes); finish: UA_NodeId_clear(&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; } } return retval; } /* Receive and process messages until a synchronous message arrives or the * timout finishes */ static UA_StatusCode receiveResponse(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 = UA_STATUSCODE_GOOD; UA_DateTime now = UA_DateTime_nowMonotonic(); do { UA_UInt32 timeout2 = (UA_UInt32)((maxDate - now) / UA_DATETIME_MSEC); if(maxDate < now) timeout2 = 0; retval = UA_SecureChannel_receive(&client->channel, &rd, processServiceResponse, timeout2); if(retval == UA_STATUSCODE_GOODNONCRITICALTIMEOUT) break; if(retval != UA_STATUSCODE_GOOD || client->channel.state == UA_SECURECHANNELSTATE_CLOSING) { UA_LOG_WARNING_CHANNEL(&client->config.logger, &client->channel, "Receiving the response failed with StatusCode %s", UA_StatusCode_name(retval)); closeSecureChannel(client); retval = UA_STATUSCODE_BADCONNECTIONCLOSED; break; } now = UA_DateTime_nowMonotonic(); if(maxDate < now) break; } while(!rd.received && responseType); /* Return if we don't wait for an async response */ return retval; } UA_StatusCode receiveResponseAsync(UA_Client *client, UA_UInt32 timeout) { UA_DateTime maxDate = UA_DateTime_nowMonotonic() + ((UA_DateTime)timeout * UA_DATETIME_MSEC); UA_StatusCode res = receiveResponse(client, NULL, NULL, maxDate, NULL); return (res != UA_STATUSCODE_GOODNONCRITICALTIMEOUT) ? res : UA_STATUSCODE_GOOD; } void __UA_Client_Service(UA_Client *client, const void *request, const UA_DataType *requestType, void *response, const UA_DataType *responseType) { /* Initialize. Response is valied in case of aborting. */ UA_init(response, responseType); if(client->channel.state != UA_SECURECHANNELSTATE_OPEN) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "SecureChannel must be connected before sending requests"); UA_ResponseHeader *respHeader = (UA_ResponseHeader*)response; respHeader->serviceResult = UA_STATUSCODE_BADCONNECTIONCLOSED; return; } /* Send the request */ UA_UInt32 requestId; UA_StatusCode retval = sendSymmetricServiceRequest(client, request, requestType, &requestId); UA_ResponseHeader *respHeader = (UA_ResponseHeader*)response; if(retval == UA_STATUSCODE_GOOD) { /* Retrieve the response */ UA_DateTime maxDate = UA_DateTime_nowMonotonic() + ((UA_DateTime)client->config.timeout * UA_DATETIME_MSEC); retval = receiveResponse(client, response, responseType, maxDate, &requestId); } else if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED) { respHeader->serviceResult = UA_STATUSCODE_BADREQUESTTOOLARGE; return; } /* In synchronous service, if we have don't have a reply we need to close * the connection. For all other error cases, receiveResponse has already * closed the channel. */ if(retval == UA_STATUSCODE_GOODNONCRITICALTIMEOUT || client->channel.state == UA_SECURECHANNELSTATE_CLOSING) { closeSecureChannel(client); retval = UA_STATUSCODE_BADCONNECTIONCLOSED; } if(retval != UA_STATUSCODE_GOOD) respHeader->serviceResult = retval; notifyClientState(client); } void UA_Client_AsyncService_cancel(UA_Client *client, AsyncServiceCall *ac, UA_StatusCode statusCode) { /* Create an empty response with the statuscode */ UA_Response response; UA_init(&response, ac->responseType); response.responseHeader.serviceResult = statusCode; if(ac->callback) ac->callback(client, ac->userdata, ac->requestId, &response); /* Clean up the response. Users might move data into it. For whatever reasons. */ UA_clear(&response, 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_modifyAsyncCallback(UA_Client *client, UA_UInt32 requestId, void *userdata, UA_ClientAsyncServiceCallback callback) { AsyncServiceCall *ac; LIST_FOREACH(ac, &client->asyncServiceCalls, pointers) { if(ac->requestId == requestId) { ac->callback = callback; ac->userdata = userdata; return UA_STATUSCODE_GOOD; } } return UA_STATUSCODE_BADNOTFOUND; } 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) { if(client->channel.state != UA_SECURECHANNELSTATE_OPEN) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "SecureChannel must be connected before sending requests"); return UA_STATUSCODE_BADSERVERNOTCONNECTED; } /* 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); closeSecureChannel(client); notifyClientState(client); 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; notifyClientState(client); 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) { 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, NULL, UA_TIMER_HANDLE_CYCLEMISS_WITH_CURRENTTIME, callbackId); } UA_StatusCode UA_Client_changeRepeatedCallbackInterval(UA_Client *client, UA_UInt64 callbackId, UA_Double interval_ms) { return UA_Timer_changeRepeatedCallback(&client->timer, callbackId, interval_ms, NULL, UA_TIMER_HANDLE_CYCLEMISS_WITH_CURRENTTIME); } void UA_Client_removeCallback(UA_Client *client, UA_UInt64 callbackId) { UA_Timer_removeCallback(&client->timer, callbackId); } static void asyncServiceTimeoutCheck(UA_Client *client) { AsyncServiceCall *ac, *ac_tmp; UA_DateTime now = UA_DateTime_nowMonotonic(); 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 void UA_Client_backgroundConnectivity(UA_Client *client) { if(!client->config.connectivityCheckInterval) return; if(client->pendingConnectivityCheck) return; UA_DateTime now = UA_DateTime_nowMonotonic(); UA_DateTime nextDate = client->lastConnectivityCheck + (UA_DateTime)(client->config.connectivityCheckInterval * UA_DATETIME_MSEC); if(now <= nextDate) return; /* Prepare the request */ UA_ReadValueId rvid; UA_ReadValueId_init(&rvid); rvid.attributeId = UA_ATTRIBUTEID_VALUE; rvid.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STATE); UA_ReadRequest request; UA_ReadRequest_init(&request); 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); if(retval == UA_STATUSCODE_GOOD) client->pendingConnectivityCheck = true; } static void clientExecuteRepeatedCallback(void *executionApplication, UA_ApplicationCallback cb, void *callbackApplication, void *data) { cb(callbackApplication, data); } UA_StatusCode UA_Client_run_iterate(UA_Client *client, UA_UInt32 timeout) { /* Process timed (repeated) jobs */ UA_DateTime now = UA_DateTime_nowMonotonic(); UA_DateTime maxDate = UA_Timer_process(&client->timer, now, (UA_TimerExecutionCallback) clientExecuteRepeatedCallback, client); if(maxDate > now + ((UA_DateTime)timeout * UA_DATETIME_MSEC)) maxDate = now + ((UA_DateTime)timeout * UA_DATETIME_MSEC); /* Make sure we have an open channel */ UA_StatusCode retval = UA_STATUSCODE_GOOD; if(client->endpointsHandshake || client->findServersHandshake || client->discoveryUrl.length == 0 || (client->noSession && client->channel.state != UA_SECURECHANNELSTATE_OPEN) || client->sessionState < UA_SESSIONSTATE_ACTIVATED) { retval = connectIterate(client, timeout); notifyClientState(client); return retval; } /* Renew Secure Channel */ UA_Client_renewSecureChannel(client); if(client->connectStatus != UA_STATUSCODE_GOOD) return client->connectStatus; /* Feed the server PublishRequests for the Subscriptions */ #ifdef UA_ENABLE_SUBSCRIPTIONS UA_Client_Subscriptions_backgroundPublish(client); #endif /* Send read requests from time to time to test the connectivity */ UA_Client_backgroundConnectivity(client); /* Listen on the network for the given timeout */ retval = receiveResponse(client, NULL, NULL, maxDate, NULL); if(retval == UA_STATUSCODE_GOODNONCRITICALTIMEOUT) retval = UA_STATUSCODE_GOOD; if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING_CHANNEL(&client->config.logger, &client->channel, "Could not receive with StatusCode %s", UA_StatusCode_name(retval)); } #ifdef UA_ENABLE_SUBSCRIPTIONS /* The inactivity check must be done after receiveServiceResponse*/ UA_Client_Subscriptions_backgroundPublishInactivityCheck(client); #endif /* Did async services time out? Process callbacks with an error code */ asyncServiceTimeoutCheck(client); /* Log and notify user if the client state has changed */ notifyClientState(client); return client->connectStatus; } const UA_DataType * UA_Client_findDataType(UA_Client *client, const UA_NodeId *typeId) { return UA_findDataTypeWithCustom(typeId, client->config.customDataTypes); } /**** amalgamated original file "/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-2020 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2017-2019 (c) Fraunhofer IOSB (Author: Mark Giraud) */ /* Some OPC UA servers only return all Endpoints if the EndpointURL used during * the HEL/ACK handshake exactly matches -- including the path following the * address and port! Hence for the first connection we only call FindServers and * reopen a new TCP connection using then EndpointURL found there. * * The overall process is this: * - Connect with the EndpointURL provided by the user (HEL/ACK) * - Call FindServers * - If one of the results has an exactly matching EndpointUrl, continue. * - Otherwise select a matching server, update the endpointURL member of * UA_Client and reconnect. - * - Call GetEndpoints and select an Endpoint * - Open a SecureChannel and Session for that Endpoint */ #define UA_MINMESSAGESIZE 8192 #define UA_SESSION_LOCALNONCELENGTH 32 #define MAX_DATA_SIZE 4096 static void closeSession(UA_Client *client); static UA_StatusCode createSessionAsync(UA_Client *client); static 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 UA_Boolean endpointUnconfigured(UA_Client *client) { char test = 0; char *pos = (char *)&client->config.endpoint; for(size_t i = 0; i < sizeof(UA_EndpointDescription); i++) test = test | *(pos + i); pos = (char *)&client->config.userTokenPolicy; for(size_t i = 0; i < sizeof(UA_UserTokenPolicy); i++) test = test | *(pos + i); return (test == 0); } #ifdef UA_ENABLE_ENCRYPTION /* Function to create a signature using remote certificate and nonce */ static UA_StatusCode signActivateSessionRequest(UA_Client *client, 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(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 + client->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, client->remoteNonce.data, client->remoteNonce.length); retval = sp->certificateSigningAlgorithm.sign(channel->channelContext, &dataToSign, &sd->signature); /* Clean up */ UA_ByteString_clear(&dataToSign); return retval; } static 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(channelContext); size_t encryptedBlockSize = sp->asymmetricModule.cryptoModule. encryptionAlgorithm.getRemoteBlockSize(channelContext); UA_UInt32 length = (UA_UInt32)(tokenData->length + client->remoteNonce.length); UA_UInt32 totalLength = length + 4; /* Including the length field */ size_t blocks = totalLength / plainTextBlockSize; if(totalLength % plainTextBlockSize != 0) blocks++; size_t encryptedLength = blocks * encryptedBlockSize; /* Allocate memory for encryption overhead */ UA_ByteString encrypted; retval = UA_ByteString_allocBuffer(&encrypted, encryptedLength); 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]; retval = UA_UInt32_encodeBinary(&length, &pos, end); memcpy(pos, tokenData->data, tokenData->length); memcpy(&pos[tokenData->length], client->remoteNonce.data, client->remoteNonce.length); UA_assert(retval == UA_STATUSCODE_GOOD); /* 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(channelContext, &encrypted); encrypted.length = encryptedLength; 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_clear(tokenData); *tokenData = encrypted; /* Delete the temp channel context */ sp->channelModule.deleteContext(channelContext); return retval; } /* Function to verify the signature corresponds to ClientNonce * using the local certificate */ static UA_StatusCode checkCreateSessionSignature(UA_Client *client, 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 + client->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, client->localNonce.data, client->localNonce.length); retval = sp->certificateSigningAlgorithm.verify(channel->channelContext, &dataToVerify, &response->serverSignature.signature); UA_ByteString_clear(&dataToVerify); return retval; } #endif /***********************/ /* Open the Connection */ /***********************/ void processERRResponse(UA_Client *client, const UA_ByteString *chunk) { client->channel.state = UA_SECURECHANNELSTATE_CLOSING; size_t offset = 0; UA_TcpErrorMessage errMessage; UA_StatusCode res = UA_decodeBinaryInternal(chunk, &offset, &errMessage, &UA_TRANSPORT[UA_TRANSPORT_TCPERRORMESSAGE], NULL); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR_CHANNEL(&client->config.logger, &client->channel, "Received an ERR response that could not be decoded with StatusCode %s", UA_StatusCode_name(res)); client->connectStatus = res; return; } UA_LOG_ERROR_CHANNEL(&client->config.logger, &client->channel, "Received an ERR response with StatusCode %s and the following reason: %.*s", UA_StatusCode_name(errMessage.error), (int)errMessage.reason.length, errMessage.reason.data); client->connectStatus = errMessage.error; UA_TcpErrorMessage_clear(&errMessage); } void processACKResponse(UA_Client *client, const UA_ByteString *chunk) { UA_SecureChannel *channel = &client->channel; if(channel->state != UA_SECURECHANNELSTATE_HEL_SENT) { UA_LOG_ERROR_CHANNEL(&client->config.logger, channel, "Expected an ACK response"); channel->state = UA_SECURECHANNELSTATE_CLOSING; return; } UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_NETWORK, "Received ACK message"); /* Decode the message */ size_t offset = 0; UA_TcpAcknowledgeMessage ackMessage; client->connectStatus = UA_decodeBinaryInternal(chunk, &offset, &ackMessage, &UA_TRANSPORT[UA_TRANSPORT_TCPACKNOWLEDGEMESSAGE], NULL); if(client->connectStatus != UA_STATUSCODE_GOOD) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_NETWORK, "Decoding ACK message failed"); closeSecureChannel(client); return; } client->connectStatus = UA_SecureChannel_processHELACK(channel, &ackMessage); if(client->connectStatus != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_NETWORK, "Processing the ACK message failed with StatusCode %s", UA_StatusCode_name(client->connectStatus)); closeSecureChannel(client); return; } client->channel.state = UA_SECURECHANNELSTATE_ACK_RECEIVED; } 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; hello.protocolVersion = 0; hello.receiveBufferSize = client->config.localConnectionConfig.recvBufferSize; hello.sendBufferSize = client->config.localConnectionConfig.sendBufferSize; hello.maxMessageSize = client->config.localConnectionConfig.localMaxMessageSize; hello.maxChunkCount = client->config.localConnectionConfig.localMaxChunkCount; /* Use the DiscoveryUrl -- if already known via FindServers */ if(client->discoveryUrl.length > 0) { hello.endpointUrl = client->discoveryUrl; } else { hello.endpointUrl = client->endpointUrl; } UA_Byte *bufPos = &message.data[8]; /* skip the header */ const UA_Byte *bufEnd = &message.data[message.length]; client->connectStatus = UA_encodeBinaryInternal(&hello, &UA_TRANSPORT[UA_TRANSPORT_TCPHELLOMESSAGE], &bufPos, &bufEnd, NULL, NULL); /* 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_encodeBinaryInternal(&messageHeader, &UA_TRANSPORT[UA_TRANSPORT_TCPMESSAGEHEADER], &bufPos, &bufEnd, NULL, NULL); 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) { client->channel.state = UA_SECURECHANNELSTATE_HEL_SENT; 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; } void processOPNResponse(UA_Client *client, const UA_ByteString *message) { /* Is the content of the expected type? */ size_t offset = 0; UA_NodeId responseId; UA_NodeId expectedId = UA_NODEID_NUMERIC(0, UA_NS0ID_OPENSECURECHANNELRESPONSE_ENCODING_DEFAULTBINARY); UA_StatusCode retval = UA_NodeId_decodeBinary(message, &offset, &responseId); if(retval != UA_STATUSCODE_GOOD) { closeSecureChannel(client); return; } if(!UA_NodeId_equal(&responseId, &expectedId)) { UA_NodeId_clear(&responseId); closeSecureChannel(client); return; } /* Decode the response */ UA_OpenSecureChannelResponse response; retval = UA_decodeBinaryInternal(message, &offset, &response, &UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE], NULL); if(retval != UA_STATUSCODE_GOOD) { closeSecureChannel(client); return; } /* Check whether the nonce was reused */ if(client->channel.securityMode != UA_MESSAGESECURITYMODE_NONE && UA_ByteString_equal(&client->channel.remoteNonce, &response.serverNonce)) { UA_LOG_ERROR_CHANNEL(&client->config.logger, &client->channel, "The server reused the last nonce"); client->connectStatus = UA_STATUSCODE_BADSECURITYCHECKSFAILED; closeSecureChannel(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); /* Move the nonce out of the response */ UA_ByteString_clear(&client->channel.remoteNonce); client->channel.remoteNonce = response.serverNonce; UA_ByteString_init(&response.serverNonce); UA_ResponseHeader_clear(&response.responseHeader); /* Replace the token. Keep the current token as the old token. Messages * might still arrive for the old token. */ client->channel.altSecurityToken = client->channel.securityToken; client->channel.securityToken = response.securityToken; client->channel.renewState = UA_SECURECHANNELRENEWSTATE_NEWTOKEN_CLIENT; /* Compute the new local keys. The remote keys are updated when a message * with the new SecurityToken is received. */ retval = UA_SecureChannel_generateLocalKeys(&client->channel); if(retval != UA_STATUSCODE_GOOD) { closeSecureChannel(client); return; } UA_Float lifetime = (UA_Float)response.securityToken.revisedLifetime / 1000; UA_Boolean renew = (client->channel.state == UA_SECURECHANNELSTATE_OPEN); if(renew) { UA_LOG_INFO_CHANNEL(&client->config.logger, &client->channel, "SecureChannel " "renewed with a revised lifetime of %.2fs", lifetime); } else { UA_LOG_INFO_CHANNEL(&client->config.logger, &client->channel, "SecureChannel opened with SecurityPolicy %.*s " "and a revised lifetime of %.2fs", (int)client->channel.securityPolicy->policyUri.length, client->channel.securityPolicy->policyUri.data, lifetime); } client->channel.state = UA_SECURECHANNELSTATE_OPEN; } /* OPN messges to renew the channel are sent asynchronous */ static UA_StatusCode sendOPNAsync(UA_Client *client, UA_Boolean renew) { UA_Connection *conn = &client->connection; if(conn->state != UA_CONNECTIONSTATE_ESTABLISHED) { closeSecureChannel(client); return UA_STATUSCODE_BADNOTCONNECTED; } UA_StatusCode retval = UA_SecureChannel_generateLocalNonce(&client->channel); if(retval != UA_STATUSCODE_GOOD) return retval; /* Prepare the OpenSecureChannelRequest */ UA_OpenSecureChannelRequest opnSecRq; UA_OpenSecureChannelRequest_init(&opnSecRq); opnSecRq.requestHeader.timestamp = UA_DateTime_now(); opnSecRq.requestHeader.authenticationToken = client->authenticationToken; opnSecRq.securityMode = client->channel.securityMode; opnSecRq.clientNonce = client->channel.localNonce; opnSecRq.requestedLifetime = client->config.secureChannelLifeTime; if(renew) { opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_RENEW; UA_LOG_DEBUG_CHANNEL(&client->config.logger, &client->channel, "Requesting to renew the SecureChannel"); } else { opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_ISSUE; UA_LOG_DEBUG_CHANNEL(&client->config.logger, &client->channel, "Requesting to open a SecureChannel"); } /* Prepare the entry for the linked list */ UA_UInt32 requestId = ++client->requestId; /* Send the OPN message */ UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "Requesting to open a SecureChannel"); retval = UA_SecureChannel_sendAsymmetricOPNMessage(&client->channel, requestId, &opnSecRq, &UA_TYPES[UA_TYPES_OPENSECURECHANNELREQUEST]); 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)); closeSecureChannel(client); return retval; } client->channel.renewState = UA_SECURECHANNELRENEWSTATE_SENT; if(client->channel.state < UA_SECURECHANNELSTATE_OPN_SENT) client->channel.state = UA_SECURECHANNELSTATE_OPN_SENT; UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "OPN message sent"); return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Client_renewSecureChannel(UA_Client *client) { /* Check if OPN has been sent or the SecureChannel is still valid */ if(client->channel.state != UA_SECURECHANNELSTATE_OPEN || client->channel.renewState == UA_SECURECHANNELRENEWSTATE_SENT || client->nextChannelRenewal > UA_DateTime_nowMonotonic()) return UA_STATUSCODE_GOODCALLAGAIN; sendOPNAsync(client, true); return client->connectStatus; } static void responseActivateSession(UA_Client *client, void *userdata, UA_UInt32 requestId, void *response) { UA_ActivateSessionResponse *activateResponse = (UA_ActivateSessionResponse *)response; if(activateResponse->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "ActivateSession failed with error code %s", UA_StatusCode_name(activateResponse->responseHeader.serviceResult)); if(activateResponse->responseHeader.serviceResult == UA_STATUSCODE_BADSESSIONIDINVALID || activateResponse->responseHeader.serviceResult == UA_STATUSCODE_BADSESSIONCLOSED) { /* The session is lost. Create a new one. */ closeSession(client); createSessionAsync(client); UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Session cannot be activated. Create a new Session."); } else { /* Something else is wrong. Give up. */ client->connectStatus = activateResponse->responseHeader.serviceResult; } return; } /* Replace the nonce */ UA_ByteString_clear(&client->remoteNonce); client->remoteNonce = activateResponse->serverNonce; UA_ByteString_init(&activateResponse->serverNonce); client->sessionState = UA_SESSIONSTATE_ACTIVATED; notifyClientState(client); } static UA_StatusCode activateSessionAsync(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 (client->config.sessionLocaleIdsSize && client->config.sessionLocaleIds) { retval = UA_Array_copy(client->config.sessionLocaleIds, client->config.sessionLocaleIdsSize, (void **)&request.localeIds, &UA_TYPES[UA_TYPES_LOCALEID]); if (retval != UA_STATUSCODE_GOOD) return retval; request.localeIdsSize = client->config.sessionLocaleIdsSize; } /* 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_clear(&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); retval |= signActivateSessionRequest(client, &client->channel, &request); #endif if(retval == UA_STATUSCODE_GOOD) retval = UA_Client_sendAsyncRequest(client, &request, &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST], (UA_ClientAsyncServiceCallback) responseActivateSession, &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE], NULL, NULL); UA_ActivateSessionRequest_clear(&request); if(retval == UA_STATUSCODE_GOOD) client->sessionState = UA_SESSIONSTATE_ACTIVATE_REQUESTED; else UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "ActivateSession failed when sending the request with error code %s", UA_StatusCode_name(retval)); return retval; } /* Combination of UA_Client_getEndpointsInternal and getEndpoints */ static void responseGetEndpoints(UA_Client *client, void *userdata, UA_UInt32 requestId, void *response) { client->endpointsHandshake = false; UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Received GetEndpointsResponse"); UA_GetEndpointsResponse *resp = (UA_GetEndpointsResponse*)response; /* GetEndpoints not possible. Fail the connection */ 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_clear(resp); return; } /* Warn if the Endpoints look incomplete / don't match the EndpointUrl */ Client_warnEndpointsResult(client, resp, &client->discoveryUrl); UA_Boolean endpointFound = false; UA_Boolean tokenFound = false; const UA_String binaryTransport = UA_STRING("http://opcfoundation.org/UA-Profile/" "Transport/uatcp-uasc-uabinary"); /* Find a matching combination of Endpoint and UserTokenPolicy */ for(size_t i = 0; i < resp->endpointsSize; ++i) { UA_EndpointDescription* endpoint = &resp->endpoints[i]; /* Filter by the ApplicationURI if defined */ if(client->config.applicationUri.length > 0 && !UA_String_equal(&client->config.applicationUri, &endpoint->server.applicationUri)) continue; /* 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; /* 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; /* Look for a user token policy with an anonymous token */ for(size_t j = 0; j < endpoint->userIdentityTokensSize; ++j) { UA_UserTokenPolicy* tokenPolicy = &endpoint->userIdentityTokens[j]; const UA_DataType *tokenType = client->config.userIdentityToken.content.decoded.type; /* Usertokens also have a security policy... */ if(tokenPolicy->tokenType != UA_USERTOKENTYPE_ANONYMOUS && tokenPolicy->securityPolicyUri.length > 0 && !getSecurityPolicy(client, tokenPolicy->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)tokenPolicy->securityPolicyUri.length, tokenPolicy->securityPolicyUri.data); continue; } if(tokenPolicy->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; } if(tokenPolicy->tokenType == UA_USERTOKENTYPE_ANONYMOUS && tokenType != &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN] && tokenType != 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(tokenPolicy->tokenType == UA_USERTOKENTYPE_USERNAME && tokenType != &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(tokenPolicy->tokenType == UA_USERTOKENTYPE_CERTIFICATE && tokenType != &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(tokenPolicy->tokenType == UA_USERTOKENTYPE_ISSUEDTOKEN && tokenType != &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 */ #if UA_LOGLEVEL <= 300 const char *securityModeNames[3] = {"None", "Sign", "SignAndEncrypt"}; const char *userTokenTypeNames[4] = {"Anonymous", "UserName", "Certificate", "IssuedToken"}; UA_String *securityPolicyUri = &tokenPolicy->securityPolicyUri; if(securityPolicyUri->length == 0) securityPolicyUri = &endpoint->securityPolicyUri; /* Log the selected endpoint */ UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Selected endpoint %lu in URL %.*s with SecurityMode %s and SecurityPolicy %.*s", (long unsigned)i, (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)tokenPolicy->policyId.length, tokenPolicy->policyId.data, userTokenTypeNames[tokenPolicy->tokenType], (int)securityPolicyUri->length, securityPolicyUri->data); #endif /* Move to the client config */ tokenFound = true; UA_EndpointDescription_clear(&client->config.endpoint); client->config.endpoint = *endpoint; UA_EndpointDescription_init(endpoint); UA_UserTokenPolicy_clear(&client->config.userTokenPolicy); client->config.userTokenPolicy = *tokenPolicy; UA_UserTokenPolicy_init(tokenPolicy); break; } if(tokenFound) break; } 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; } /* Close the SecureChannel if a different SecurityPolicy is defined by the Endpoint */ if(client->config.endpoint.securityMode != client->channel.securityMode || !UA_String_equal(&client->config.endpoint.securityPolicyUri, &client->channel.securityPolicy->policyUri)) closeSecureChannel(client); } static UA_StatusCode requestGetEndpoints(UA_Client *client) { UA_GetEndpointsRequest request; UA_GetEndpointsRequest_init(&request); request.requestHeader.timestamp = UA_DateTime_now(); request.requestHeader.timeoutHint = 10000; if(client->discoveryUrl.length > 0) request.endpointUrl = client->discoveryUrl; else request.endpointUrl = client->endpointUrl; UA_StatusCode retval = UA_Client_sendAsyncRequest(client, &request, &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST], (UA_ClientAsyncServiceCallback) responseGetEndpoints, &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE], NULL, NULL); if(retval == UA_STATUSCODE_GOOD) client->endpointsHandshake = true; else UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "RequestGetEndpoints failed when sending the request with error code %s", UA_StatusCode_name(retval)); return retval; } static void responseFindServers(UA_Client *client, void *userdata, UA_UInt32 requestId, void *response) { client->findServersHandshake = false; UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Received FindServersResponse"); /* Error handling. Log the error but continue to connect with the current * EndpointURL. */ UA_FindServersResponse *fsr = (UA_FindServersResponse*)response; if(fsr->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&client->config.logger, UA_LOGCATEGORY_CLIENT, "FindServers failed with error code %s. Continue with the " "EndpointURL %.*s.", UA_StatusCode_name(fsr->responseHeader.serviceResult), (int)client->endpointUrl.length, client->endpointUrl.data); UA_String_copy(&client->endpointUrl, &client->discoveryUrl); return; } /* Check if one of the returned servers matches the EndpointURL already used */ for(size_t i = 0; i < fsr->serversSize; i++) { UA_ApplicationDescription *server = &fsr->servers[i]; /* Filter by the ApplicationURI if defined */ if(client->config.applicationUri.length > 0 && !UA_String_equal(&client->config.applicationUri, &server->applicationUri)) continue; for(size_t j = 0; j < server->discoveryUrlsSize; j++) { if(UA_String_equal(&client->endpointUrl, &server->discoveryUrls[j])) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "The initially defined EndpointURL %.*s" "is valid for the server", (int)client->endpointUrl.length, client->endpointUrl.data); UA_String_copy(&client->endpointUrl, &client->discoveryUrl); return; } } } /* The current EndpointURL is not usable. Pick the first "opc.tcp" DiscoveryUrl of a * returned server. */ for(size_t i = 0; i < fsr->serversSize; i++) { UA_ApplicationDescription *server = &fsr->servers[i]; if(server->applicationType != UA_APPLICATIONTYPE_SERVER) continue; /* Filter by the ApplicationURI if defined */ if(client->config.applicationUri.length > 0 && !UA_String_equal(&client->config.applicationUri, &server->applicationUri)) continue; for(size_t j = 0; j < server->discoveryUrlsSize; j++) { /* Try to parse the DiscoveryUrl. This weeds out http schemas (etc.) * and invalid DiscoveryUrls in general. */ UA_String hostname, path; UA_UInt16 port; UA_StatusCode res = UA_parseEndpointUrl(&server->discoveryUrls[j], &hostname, &port, &path); if(res != UA_STATUSCODE_GOOD) continue; /* Use this DiscoveryUrl in the client */ UA_String_clear(&client->discoveryUrl); client->discoveryUrl = server->discoveryUrls[j]; UA_String_init(&server->discoveryUrls[j]); UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Use the EndpointURL %.*s returned from FindServers", (int)client->discoveryUrl.length, client->discoveryUrl.data); /* Close the SecureChannel to build it up new with the correct * EndpointURL in the HEL/ACK handshake */ closeSecureChannel(client); return; } } /* Could not find a suitable server. Try to continue with the * original EndpointURL. */ UA_LOG_WARNING(&client->config.logger, UA_LOGCATEGORY_CLIENT, "FindServers did not returned a suitable DiscoveryURL. " "Continue with the EndpointURL %.*s.", (int)client->endpointUrl.length, client->endpointUrl.data); UA_String_copy(&client->endpointUrl, &client->discoveryUrl); } static UA_StatusCode requestFindServers(UA_Client *client) { UA_FindServersRequest request; UA_FindServersRequest_init(&request); request.requestHeader.timestamp = UA_DateTime_now(); request.requestHeader.timeoutHint = 10000; request.endpointUrl = client->endpointUrl; UA_StatusCode retval = UA_Client_sendAsyncRequest(client, &request, &UA_TYPES[UA_TYPES_FINDSERVERSREQUEST], (UA_ClientAsyncServiceCallback) responseFindServers, &UA_TYPES[UA_TYPES_FINDSERVERSRESPONSE], NULL, NULL); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "FindServers failed when sending the request with error code %s", UA_StatusCode_name(retval)); return retval; } client->findServersHandshake = true; return UA_STATUSCODE_GOOD; } static void responseSessionCallback(UA_Client *client, void *userdata, UA_UInt32 requestId, void *response) { UA_CreateSessionResponse *sessionResponse = (UA_CreateSessionResponse*)response; UA_StatusCode res = sessionResponse->responseHeader.serviceResult; if(res != UA_STATUSCODE_GOOD) goto cleanup; #ifdef UA_ENABLE_ENCRYPTION if(client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGN || client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) { /* Verify the session response was created with the same certificate as * the SecureChannel */ if(!UA_ByteString_equal(&sessionResponse->serverCertificate, &client->channel.remoteCertificate)) { res = UA_STATUSCODE_BADCERTIFICATEINVALID; goto cleanup; } /* Verify the client signature */ res = checkCreateSessionSignature(client, &client->channel, sessionResponse); if(res != UA_STATUSCODE_GOOD) goto cleanup; } #endif /* Copy nonce and AuthenticationToken */ UA_ByteString_clear(&client->remoteNonce); UA_NodeId_clear(&client->authenticationToken); res |= UA_ByteString_copy(&sessionResponse->serverNonce, &client->remoteNonce); res |= UA_NodeId_copy(&sessionResponse->authenticationToken, &client->authenticationToken); if(res != UA_STATUSCODE_GOOD) goto cleanup; /* Activate the new Session */ client->sessionState = UA_SESSIONSTATE_CREATED; cleanup: client->connectStatus = res; if(client->connectStatus != UA_STATUSCODE_GOOD) client->sessionState = UA_SESSIONSTATE_CLOSED; } static UA_StatusCode createSessionAsync(UA_Client *client) { UA_StatusCode retval = UA_STATUSCODE_GOOD; if(client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGN || client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) { if(client->localNonce.length != UA_SESSION_LOCALNONCELENGTH) { UA_ByteString_clear(&client->localNonce); retval = UA_ByteString_allocBuffer(&client->localNonce, UA_SESSION_LOCALNONCELENGTH); if(retval != UA_STATUSCODE_GOOD) return retval; } retval = client->channel.securityPolicy->symmetricModule. generateNonce(client->channel.securityPolicy->policyContext, &client->localNonce); if(retval != UA_STATUSCODE_GOOD) return retval; } UA_CreateSessionRequest request; UA_CreateSessionRequest_init(&request); request.requestHeader.requestHandle = ++client->requestHandle; request.requestHeader.timestamp = UA_DateTime_now(); request.requestHeader.timeoutHint = 10000; UA_ByteString_copy(&client->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); } retval = UA_Client_sendAsyncRequest(client, &request, &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST], (UA_ClientAsyncServiceCallback)responseSessionCallback, &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE], NULL, NULL); UA_CreateSessionRequest_clear(&request); if(retval == UA_STATUSCODE_GOOD) client->sessionState = UA_SESSIONSTATE_CREATE_REQUESTED; else UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "CreateSession failed when sending the request with error code %s", UA_StatusCode_name(retval)); return retval; } static UA_StatusCode initConnect(UA_Client *client); /* A workaround if the DiscoveryUrl returned by the FindServers service doesn't work. * Then default back to the initial EndpointUrl and pretend that was returned * by FindServers. */ static void fixBadDiscoveryUrl(UA_Client* client) { if(client->connectStatus == UA_STATUSCODE_GOOD) return; if(client->discoveryUrl.length == 0 || UA_String_equal(&client->discoveryUrl, &client->endpointUrl)) return; UA_LOG_WARNING(&client->config.logger, UA_LOGCATEGORY_CLIENT, "The DiscoveryUrl returned by the FindServers service (%.*s) could not be " "connected. Trying with the original EndpointUrl.", (int)client->discoveryUrl.length, client->discoveryUrl.data); UA_String_clear(&client->discoveryUrl); UA_String_copy(&client->endpointUrl, &client->discoveryUrl); client->connectStatus = UA_STATUSCODE_GOOD; } UA_StatusCode connectIterate(UA_Client *client, UA_UInt32 timeout) { UA_LOG_TRACE(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Client connect iterate"); /* Already connected */ if(client->sessionState == UA_SESSIONSTATE_ACTIVATED) return UA_STATUSCODE_GOOD; /* Could not connect */ if(client->connectStatus != UA_STATUSCODE_GOOD) return client->connectStatus; /* The connection was already closed */ if(client->channel.state == UA_SECURECHANNELSTATE_CLOSING) { client->connectStatus = UA_STATUSCODE_BADCONNECTIONCLOSED; return UA_STATUSCODE_BADCONNECTIONCLOSED; } /* The connection is closed. Reset the SecureChannel and open a new TCP * connection */ if(client->connection.state == UA_CONNECTIONSTATE_CLOSED) return initConnect(client); /* Poll the connection status */ if(client->connection.state == UA_CONNECTIONSTATE_OPENING) { client->connectStatus = client->config.pollConnectionFunc(&client->connection, timeout, &client->config.logger); return client->connectStatus; } /* Attach the connection to the SecureChannel */ if(!client->channel.connection) UA_Connection_attachSecureChannel(&client->connection, &client->channel); /* Set the SecurityPolicy */ if(!client->channel.securityPolicy) { client->channel.securityMode = client->config.endpoint.securityMode; if(client->channel.securityMode == UA_MESSAGESECURITYMODE_INVALID) client->channel.securityMode = UA_MESSAGESECURITYMODE_NONE; UA_SecurityPolicy *sp = NULL; if(client->config.endpoint.securityPolicyUri.length == 0) { sp = getSecurityPolicy(client, UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None")); } else { sp = getSecurityPolicy(client, client->config.endpoint.securityPolicyUri); } if(!sp) { client->connectStatus = UA_STATUSCODE_BADINTERNALERROR; return client->connectStatus; } client->connectStatus = UA_SecureChannel_setSecurityPolicy(&client->channel, sp, &client->config.endpoint.serverCertificate); if(client->connectStatus != UA_STATUSCODE_GOOD) return client->connectStatus; } /* Open the SecureChannel */ switch(client->channel.state) { case UA_SECURECHANNELSTATE_FRESH: client->connectStatus = sendHELMessage(client); if(client->connectStatus == UA_STATUSCODE_GOOD) { client->channel.state = UA_SECURECHANNELSTATE_HEL_SENT; } else { client->connection.close(&client->connection); client->connection.free(&client->connection); } fixBadDiscoveryUrl(client); return client->connectStatus; case UA_SECURECHANNELSTATE_ACK_RECEIVED: client->connectStatus = sendOPNAsync(client, false); fixBadDiscoveryUrl(client); return client->connectStatus; case UA_SECURECHANNELSTATE_HEL_SENT: case UA_SECURECHANNELSTATE_OPN_SENT: client->connectStatus = receiveResponseAsync(client, timeout); fixBadDiscoveryUrl(client); return client->connectStatus; default: break; } /* Call FindServers to see whether we need to reconnect with the correct * EndpointUrl */ if(!client->findServersHandshake && client->discoveryUrl.length == 0) { client->connectStatus = requestFindServers(client); return client->connectStatus; } /* FindServers was sent out, but we are still waiting for the response */ if(client->discoveryUrl.length == 0) { receiveResponseAsync(client, timeout); return client->connectStatus; } /* Have a SecureChannel but no session */ if(client->noSession) return client->connectStatus; /* Create and Activate the Session */ switch(client->sessionState) { case UA_SESSIONSTATE_CLOSED: if(!endpointUnconfigured(client)) { client->connectStatus = createSessionAsync(client); return client->connectStatus; } if(!client->endpointsHandshake) { client->connectStatus = requestGetEndpoints(client); return client->connectStatus; } receiveResponseAsync(client, timeout); return client->connectStatus; case UA_SESSIONSTATE_CREATE_REQUESTED: case UA_SESSIONSTATE_ACTIVATE_REQUESTED: receiveResponseAsync(client, timeout); return client->connectStatus; case UA_SESSIONSTATE_CREATED: client->connectStatus = activateSessionAsync(client); return client->connectStatus; default: break; } return client->connectStatus; } /* The local ApplicationURI has to match the certificates of the * SecurityPolicies */ static void verifyClientApplicationURI(const UA_Client *client) { #if defined(UA_ENABLE_ENCRYPTION) && (UA_LOGLEVEL <= 400) for(size_t i = 0; i < client->config.securityPoliciesSize; i++) { UA_SecurityPolicy *sp = &client->config.securityPolicies[i]; UA_StatusCode retval = client->config.certificateVerification. verifyApplicationURI(client->config.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 } static UA_StatusCode client_configure_securechannel(void *application, UA_SecureChannel *channel, const UA_AsymmetricAlgorithmSecurityHeader *asymHeader) { // TODO: Verify if certificate is the same as configured in the client endpoint config return UA_STATUSCODE_GOOD; } static UA_StatusCode initConnect(UA_Client *client) { if(client->connection.state > UA_CONNECTIONSTATE_CLOSED) { UA_LOG_WARNING(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Client already connected"); return UA_STATUSCODE_GOOD; } if(client->config.initConnectionFunc == NULL) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Client connection not configured"); return UA_STATUSCODE_BADINTERNALERROR; } /* Consistency check the client's own ApplicationURI */ verifyClientApplicationURI(client); /* Reset the connect status */ client->connectStatus = UA_STATUSCODE_GOOD; client->channel.renewState = UA_SECURECHANNELRENEWSTATE_NORMAL; client->endpointsHandshake = false; /* Initialize the SecureChannel */ UA_SecureChannel_init(&client->channel, &client->config.localConnectionConfig); client->channel.certificateVerification = &client->config.certificateVerification; client->channel.processOPNHeader = client_configure_securechannel; if(client->connection.free) client->connection.free(&client->connection); /* Initialize the TCP connection */ client->connection = client->config.initConnectionFunc(client->config.localConnectionConfig, client->endpointUrl, client->config.timeout, &client->config.logger); if(client->connection.state != UA_CONNECTIONSTATE_OPENING) { UA_LOG_WARNING(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Could not open a TCP connection to %.*s", (int)client->endpointUrl.length, client->endpointUrl.data); client->connectStatus = UA_STATUSCODE_BADCONNECTIONCLOSED; closeSecureChannel(client); } return client->connectStatus; } UA_StatusCode UA_Client_connectAsync(UA_Client *client, const char *endpointUrl) { /* Set the endpoint URL the client connects to */ UA_String_clear(&client->endpointUrl); UA_String_clear(&client->discoveryUrl); client->endpointUrl = UA_STRING_ALLOC(endpointUrl); /* Open a Session when possible */ client->noSession = false; /* Connect Async */ return initConnect(client); } UA_StatusCode UA_Client_connectSecureChannelAsync(UA_Client *client, const char *endpointUrl) { /* Set the endpoint URL the client connects to */ UA_String_clear(&client->endpointUrl); UA_String_clear(&client->discoveryUrl); client->endpointUrl = UA_STRING_ALLOC(endpointUrl); /* Don't open a Session */ client->noSession = true; /* Connect Async */ return initConnect(client); } UA_StatusCode connectSync(UA_Client *client) { UA_DateTime now = UA_DateTime_nowMonotonic(); UA_DateTime maxDate = now + ((UA_DateTime)client->config.timeout * UA_DATETIME_MSEC); UA_StatusCode retval = initConnect(client); if(retval != UA_STATUSCODE_GOOD) return retval; while(retval == UA_STATUSCODE_GOOD) { if(!client->endpointsHandshake && !client->findServersHandshake && client->discoveryUrl.length > 0 && (client->sessionState == UA_SESSIONSTATE_ACTIVATED || (client->noSession && client->channel.state == UA_SECURECHANNELSTATE_OPEN))) break; now = UA_DateTime_nowMonotonic(); if(maxDate < now) return UA_STATUSCODE_BADTIMEOUT; retval = UA_Client_run_iterate(client, (UA_UInt32)((maxDate - now) / UA_DATETIME_MSEC)); } return retval; } UA_StatusCode UA_Client_connect(UA_Client *client, const char *endpointUrl) { /* Set the endpoint URL the client connects to */ UA_String_clear(&client->endpointUrl); UA_String_clear(&client->discoveryUrl); client->endpointUrl = UA_STRING_ALLOC(endpointUrl); /* Open a Session when possible */ client->noSession = false; /* Connect Synchronous */ return connectSync(client); } UA_StatusCode UA_Client_connectSecureChannel(UA_Client *client, const char *endpointUrl) { /* Set the endpoint URL the client connects to */ UA_String_clear(&client->endpointUrl); UA_String_clear(&client->discoveryUrl); client->endpointUrl = UA_STRING_ALLOC(endpointUrl); /* Don't open a Session */ client->noSession = true; /* Connect Synchronous */ return connectSync(client); } /************************/ /* Close the Connection */ /************************/ void closeSecureChannel(UA_Client *client) { /* Send CLO if the SecureChannel is open */ if(client->channel.state == UA_SECURECHANNELSTATE_OPEN) { 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(&client->channel, ++client->requestId, UA_MESSAGETYPE_CLO, &request, &UA_TYPES[UA_TYPES_CLOSESECURECHANNELREQUEST]); } /* Clean up */ client->channel.renewState = UA_SECURECHANNELRENEWSTATE_NORMAL; UA_SecureChannel_close(&client->channel); if(client->connection.free) client->connection.free(&client->connection); /* Set the Session to "Created" if it was "Activated" */ if(client->sessionState > UA_SESSIONSTATE_CREATED) client->sessionState = UA_SESSIONSTATE_CREATED; /* Delete outstanding async services - the RequestId is no longr valid. Do * this after setting the Session state. Otherwise we send out new Publish * Requests immediately. */ UA_Client_AsyncService_removeAll(client, UA_STATUSCODE_BADSECURECHANNELCLOSED); } static void sendCloseSession(UA_Client *client) { /* Set before sending the message to prevent recursion */ client->sessionState = UA_SESSIONSTATE_CLOSING; 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_clear(&request); UA_CloseSessionResponse_clear(&response); } static void closeSession(UA_Client *client) { /* Is a session established? */ if(client->sessionState == UA_SESSIONSTATE_ACTIVATED) sendCloseSession(client); UA_NodeId_clear(&client->authenticationToken); client->requestHandle = 0; client->sessionState = UA_SESSIONSTATE_CLOSED; #ifdef UA_ENABLE_SUBSCRIPTIONS /* We need to clean up the subscriptions */ UA_Client_Subscriptions_clean(client); #endif /* Reset so the next async connect creates a session by default */ client->noSession = false; /* Delete outstanding async services */ UA_Client_AsyncService_removeAll(client, UA_STATUSCODE_BADSESSIONCLOSED); #ifdef UA_ENABLE_SUBSCRIPTIONS client->currentlyOutStandingPublishRequests = 0; #endif } static void closeSessionCallback(UA_Client *client, void *userdata, UA_UInt32 requestId, void *response) { closeSession(client); closeSecureChannel(client); notifyClientState(client); } UA_StatusCode UA_EXPORT UA_Client_disconnectAsync(UA_Client *client) { /* Set before sending the message to prevent recursion */ client->sessionState = UA_SESSIONSTATE_CLOSING; UA_CloseSessionRequest request; UA_CloseSessionRequest_init(&request); request.requestHeader.timestamp = UA_DateTime_now(); request.requestHeader.timeoutHint = 10000; request.deleteSubscriptions = true; UA_StatusCode res = UA_Client_sendAsyncRequest(client, &request, &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST], (UA_ClientAsyncServiceCallback)closeSessionCallback, &UA_TYPES[UA_TYPES_CLOSESESSIONRESPONSE], NULL, NULL); notifyClientState(client); return res; } UA_StatusCode UA_Client_disconnectSecureChannel(UA_Client *client) { closeSecureChannel(client); notifyClientState(client); return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Client_disconnect(UA_Client *client) { closeSession(client); closeSecureChannel(client); notifyClientState(client); return UA_STATUSCODE_GOOD; } /**** amalgamated original file "/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 */ /* Helper method for additional warnings */ void Client_warnEndpointsResult(UA_Client *client, const UA_GetEndpointsResponse *response, const UA_String *endpointUrl) { if(response->endpointsSize == 0) { UA_LOG_WARNING(&client->config.logger, UA_LOGCATEGORY_CLIENT, "The server did not return any endpoints. " "Did you use the correct endpointUrl?"); return; } if(!UA_String_equal(endpointUrl, &response->endpoints[0].endpointUrl) || (response->endpoints[0].server.discoveryUrlsSize > 0 && !UA_String_equal(endpointUrl, &response->endpoints[0].server.discoveryUrls[0]))) { UA_String *betterUrl = &response->endpoints[0].endpointUrl; if(response->endpoints[0].server.discoveryUrlsSize > 0) betterUrl = &response->endpoints[0].server.discoveryUrls[0]; UA_LOG_WARNING(&client->config.logger, UA_LOGCATEGORY_CLIENT, "The server returned Endpoints with a different EndpointUrl %.*s than was " "used to initialize the connection: %.*s. Some servers require a complete " "match of the EndpointUrl/DiscoveryUrl (including the path) " "to return all endpoints.", (int)betterUrl->length, betterUrl->data, (int)endpointUrl->length, endpointUrl->data); } } /* Gets a list of endpoints. Memory is allocated for endpointDescription array */ static 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_clear(&response); return retval; } *endpointDescriptions = response.endpoints; *endpointDescriptionsSize = response.endpointsSize; response.endpoints = NULL; response.endpointsSize = 0; UA_GetEndpointsResponse_clear(&response); return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Client_getEndpoints(UA_Client *client, const char *serverUrl, size_t* endpointDescriptionsSize, UA_EndpointDescription** endpointDescriptions) { UA_Boolean connected = (client->channel.state == UA_SECURECHANNELSTATE_OPEN); /* 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_connectSecureChannel(client, serverUrl); 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->channel.state == UA_SECURECHANNELSTATE_OPEN); /* 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; if(!connected) { retval = UA_Client_connectSecureChannel(client, serverUrl); 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_clear(&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->channel.state == UA_SECURECHANNELSTATE_OPEN); /* 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; if(!connected) { retval = UA_Client_connectSecureChannel(client, serverUrl); 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_clear(&response); if(!connected) UA_Client_disconnect(client); return retval; } #endif /**** amalgamated original file "/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-2021 (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 */ /* The highlevel client API is an "outer onion layer". This file does not * include ua_client_internal.h on purpose. */ 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_clear(&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_clear(&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_clear(&bReq); UA_BrowseResponse_clear(&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_clear(&response); return retval; } if(response.resultsSize != 1) { UA_AddReferencesResponse_clear(&response); return UA_STATUSCODE_BADUNEXPECTEDERROR; } retval = response.results[0]; UA_AddReferencesResponse_clear(&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_clear(&response); return retval; } if(response.resultsSize != 1) { UA_DeleteReferencesResponse_clear(&response); return UA_STATUSCODE_BADUNEXPECTEDERROR; } retval = response.results[0]; UA_DeleteReferencesResponse_clear(&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_clear(&response); return retval; } if(response.resultsSize != 1) { UA_DeleteNodesResponse_clear(&response); return UA_STATUSCODE_BADUNEXPECTEDERROR; } retval = response.results[0]; UA_DeleteNodesResponse_clear(&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_clear(&response); return retval; } if(response.resultsSize != 1) { UA_AddNodesResponse_clear(&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_clear(&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_clear(&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_clear(&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_clear(&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_clear(&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(!UA_StatusCode_isEqualTop(retval,UA_STATUSCODE_GOOD)) { UA_ReadResponse_clear(&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) { retval = UA_STATUSCODE_BADUNEXPECTEDERROR; UA_ReadResponse_clear(&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_clear(&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(!UA_StatusCode_isEqualTop(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_clear(&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_clear(&response); UA_ByteString_clear(&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 (!UA_StatusCode_isEqualTop(retval,UA_STATUSCODE_GOOD)) goto cleanup; UA_HistoryReadResult *res = response.results; /* Clear old and check / store new continuation point */ UA_ByteString_clear(&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_clear(&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); 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_clear(&response); UA_NodeId_clear(&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_clear(&response); UA_NodeId_clear(&details.nodeId); return ret; } #endif // UA_ENABLE_HISTORIZING /*******************/ /* Async Functions */ /*******************/ /*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_Cient_translateBrowsePathsToNodeIds_async(UA_Client *client, char **paths, */ /* UA_UInt32 *ids, size_t pathSize, */ /* UA_ClientAsyncTranslateCallback callback, */ /* void *userdata, UA_UInt32 *reqId) { */ /* return UA_STATUSCODE_BADNOTIMPLEMENTED; */ /* } */ /*************************/ /* Read Single Attribute */ /*************************/ typedef struct { UA_ClientAsyncOperationCallback userCallback; void *userContext; const UA_DataType *resultType; /* DataValue -> Value attribute, * Variant -> ArrayDimensions attribute */ } UA_AttributeReadContext; static void AttributeReadCallback(UA_Client *client, void *userdata, UA_UInt32 requestId, UA_ReadResponse *rr) { UA_AttributeReadContext *ctx = (UA_AttributeReadContext*)userdata; UA_LOG_DEBUG(&UA_Client_getConfig(client)->logger, UA_LOGCATEGORY_CLIENT, "Async read response for request %" PRIu32, requestId); /* Check the ServiceResult */ UA_StatusCode res = rr->responseHeader.serviceResult; if(res != UA_STATUSCODE_GOOD) goto finish; /* Check result array size */ if(rr->resultsSize != 1) { res = UA_STATUSCODE_BADINTERNALERROR; goto finish; } /* A Value attribute */ UA_DataValue *dv = &rr->results[0]; if(ctx->resultType == &UA_TYPES[UA_TYPES_DATAVALUE]) { ctx->userCallback(client, ctx->userContext, requestId, UA_STATUSCODE_GOOD, dv); goto finish; } /* An ArrayDimensions attribute. Has to be an array of UInt32. */ if(ctx->resultType == &UA_TYPES[UA_TYPES_VARIANT]) { if(dv->hasValue && UA_Variant_hasArrayType(&dv->value, &UA_TYPES[UA_TYPES_UINT32])) { ctx->userCallback(client, ctx->userContext, requestId, UA_STATUSCODE_GOOD, &dv->value); } else { res = UA_STATUSCODE_BADINTERNALERROR; } goto finish; } /* Check we have a scalar value of the right datatype */ if(!dv->hasValue || !UA_Variant_hasScalarType(&dv->value, ctx->resultType)) { res = UA_STATUSCODE_BADINTERNALERROR; goto finish; } /* Callback into userland */ ctx->userCallback(client, ctx->userContext, requestId, UA_STATUSCODE_GOOD, dv->value.data); finish: if(res != UA_STATUSCODE_GOOD) ctx->userCallback(client, ctx->userContext, requestId, res, NULL); UA_free(ctx); } static UA_StatusCode readAttribute_async(UA_Client *client, const UA_ReadValueId *rvi, UA_TimestampsToReturn timestampsToReturn, const UA_DataType *resultType, /* For the specialized reads */ UA_ClientAsyncOperationCallback callback, void *userdata, UA_UInt32 *requestId) { UA_AttributeReadContext *ctx = (UA_AttributeReadContext*) UA_malloc(sizeof(UA_AttributeReadContext)); if(!ctx) return UA_STATUSCODE_BADOUTOFMEMORY; ctx->userCallback = callback; ctx->userContext = userdata; ctx->resultType = resultType; UA_ReadRequest request; UA_ReadRequest_init(&request); request.nodesToRead = (UA_ReadValueId*)(uintptr_t)rvi; /* hack, treated as const */ request.nodesToReadSize = 1; request.timestampsToReturn = timestampsToReturn; UA_StatusCode res = __UA_Client_AsyncService(client, &request, &UA_TYPES[UA_TYPES_READREQUEST], (UA_ClientAsyncServiceCallback)AttributeReadCallback, &UA_TYPES[UA_TYPES_READRESPONSE], ctx, requestId); if(res != UA_STATUSCODE_GOOD) UA_free(ctx); return res; } UA_StatusCode UA_Client_readAttribute_async(UA_Client *client, const UA_ReadValueId *rvi, UA_TimestampsToReturn timestampsToReturn, UA_ClientAsyncReadAttributeCallback callback, void *userdata, UA_UInt32 *requestId) { return readAttribute_async(client, rvi, timestampsToReturn, &UA_TYPES[UA_TYPES_DATAVALUE], /* special handling */ (UA_ClientAsyncOperationCallback)callback, userdata, requestId); } /* Helper to keep the code short */ static UA_StatusCode readAttribute_simpleAsync(UA_Client *client, const UA_NodeId *nodeId, UA_AttributeId attributeId, const UA_DataType *resultType, UA_ClientAsyncOperationCallback callback, void *userdata, UA_UInt32 *requestId) { UA_ReadValueId rvi; UA_ReadValueId_init(&rvi); rvi.nodeId = *nodeId; rvi.attributeId = attributeId; return readAttribute_async(client, &rvi, UA_TIMESTAMPSTORETURN_NEITHER, resultType, callback, userdata, requestId); } UA_StatusCode UA_Client_readValueAttribute_async(UA_Client *client, const UA_NodeId nodeId, UA_ClientAsyncReadValueAttributeCallback callback, void *userdata, UA_UInt32 *requestId) { return readAttribute_simpleAsync(client, &nodeId, UA_ATTRIBUTEID_VALUE, &UA_TYPES[UA_TYPES_DATAVALUE], /* special hndling */ (UA_ClientAsyncOperationCallback)callback, userdata, requestId); } UA_StatusCode UA_Client_readDataTypeAttribute_async(UA_Client *client, const UA_NodeId nodeId, UA_ClientAsyncReadDataTypeAttributeCallback callback, void *userdata, UA_UInt32 *requestId) { return readAttribute_simpleAsync(client, &nodeId, UA_ATTRIBUTEID_DATATYPE, &UA_TYPES[UA_TYPES_NODEID], (UA_ClientAsyncOperationCallback)callback, userdata, requestId); } UA_StatusCode UA_Client_readArrayDimensionsAttribute_async(UA_Client *client, const UA_NodeId nodeId, UA_ClientReadArrayDimensionsAttributeCallback callback, void *userdata, UA_UInt32 *requestId) { return readAttribute_simpleAsync(client, &nodeId, UA_ATTRIBUTEID_ARRAYDIMENSIONS, &UA_TYPES[UA_TYPES_VARIANT], /* special handling */ (UA_ClientAsyncOperationCallback)callback, userdata, requestId); } UA_StatusCode UA_Client_readNodeClassAttribute_async(UA_Client *client, const UA_NodeId nodeId, UA_ClientAsyncReadNodeClassAttributeCallback callback, void *userdata, UA_UInt32 *requestId) { return readAttribute_simpleAsync(client, &nodeId, UA_ATTRIBUTEID_NODECLASS, &UA_TYPES[UA_TYPES_NODECLASS], (UA_ClientAsyncOperationCallback)callback, userdata, requestId); } UA_StatusCode UA_Client_readBrowseNameAttribute_async(UA_Client *client, const UA_NodeId nodeId, UA_ClientAsyncReadBrowseNameAttributeCallback callback, void *userdata, UA_UInt32 *requestId) { return readAttribute_simpleAsync(client, &nodeId, UA_ATTRIBUTEID_BROWSENAME, &UA_TYPES[UA_TYPES_QUALIFIEDNAME], (UA_ClientAsyncOperationCallback)callback, userdata, requestId); } UA_StatusCode UA_Client_readDisplayNameAttribute_async(UA_Client *client, const UA_NodeId nodeId, UA_ClientAsyncReadDisplayNameAttributeCallback callback, void *userdata, UA_UInt32 *requestId) { return readAttribute_simpleAsync(client, &nodeId, UA_ATTRIBUTEID_DISPLAYNAME, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], (UA_ClientAsyncOperationCallback)callback, userdata, requestId); } UA_StatusCode UA_Client_readDescriptionAttribute_async(UA_Client *client, const UA_NodeId nodeId, UA_ClientAsyncReadDescriptionAttributeCallback callback, void *userdata, UA_UInt32 *requestId) { return readAttribute_simpleAsync(client, &nodeId, UA_ATTRIBUTEID_DESCRIPTION, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], (UA_ClientAsyncOperationCallback)callback, userdata, requestId); } UA_StatusCode UA_Client_readWriteMaskAttribute_async(UA_Client *client, const UA_NodeId nodeId, UA_ClientAsyncReadWriteMaskAttributeCallback callback, void *userdata, UA_UInt32 *requestId) { return readAttribute_simpleAsync(client, &nodeId, UA_ATTRIBUTEID_WRITEMASK, &UA_TYPES[UA_TYPES_UINT32], (UA_ClientAsyncOperationCallback)callback, userdata, requestId); } UA_StatusCode UA_EXPORT UA_Client_readUserWriteMaskAttribute_async(UA_Client *client, const UA_NodeId nodeId, UA_ClientAsyncReadUserWriteMaskAttributeCallback callback, void *userdata, UA_UInt32 *requestId) { return readAttribute_simpleAsync(client, &nodeId, UA_ATTRIBUTEID_USERWRITEMASK, &UA_TYPES[UA_TYPES_UINT32], (UA_ClientAsyncOperationCallback)callback, userdata, requestId); } UA_StatusCode UA_Client_readIsAbstractAttribute_async(UA_Client *client, const UA_NodeId nodeId, UA_ClientAsyncReadIsAbstractAttributeCallback callback, void *userdata, UA_UInt32 *requestId) { return readAttribute_simpleAsync(client, &nodeId, UA_ATTRIBUTEID_ISABSTRACT, &UA_TYPES[UA_TYPES_BOOLEAN], (UA_ClientAsyncOperationCallback)callback, userdata, requestId); } UA_StatusCode UA_Client_readSymmetricAttribute_async(UA_Client *client, const UA_NodeId nodeId, UA_ClientAsyncReadSymmetricAttributeCallback callback, void *userdata, UA_UInt32 *requestId) { return readAttribute_simpleAsync(client, &nodeId, UA_ATTRIBUTEID_SYMMETRIC, &UA_TYPES[UA_TYPES_BOOLEAN], (UA_ClientAsyncOperationCallback)callback, userdata, requestId); } UA_StatusCode UA_Client_readInverseNameAttribute_async(UA_Client *client, const UA_NodeId nodeId, UA_ClientAsyncReadInverseNameAttributeCallback callback, void *userdata, UA_UInt32 *requestId) { return readAttribute_simpleAsync(client, &nodeId, UA_ATTRIBUTEID_INVERSENAME, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], (UA_ClientAsyncOperationCallback)callback, userdata, requestId); } UA_StatusCode UA_Client_readContainsNoLoopsAttribute_async(UA_Client *client, const UA_NodeId nodeId, UA_ClientAsyncReadContainsNoLoopsAttributeCallback callback, void *userdata, UA_UInt32 *requestId) { return readAttribute_simpleAsync(client, &nodeId, UA_ATTRIBUTEID_CONTAINSNOLOOPS, &UA_TYPES[UA_TYPES_BOOLEAN], (UA_ClientAsyncOperationCallback)callback, userdata, requestId); } UA_StatusCode UA_Client_readEventNotifierAttribute_async(UA_Client *client, const UA_NodeId nodeId, UA_ClientAsyncReadEventNotifierAttributeCallback callback, void *userdata, UA_UInt32 *requestId) { return readAttribute_simpleAsync(client, &nodeId, UA_ATTRIBUTEID_EVENTNOTIFIER, &UA_TYPES[UA_TYPES_BYTE], (UA_ClientAsyncOperationCallback)callback, userdata, requestId); } UA_StatusCode UA_Client_readValueRankAttribute_async(UA_Client *client, const UA_NodeId nodeId, UA_ClientAsyncReadValueRankAttributeCallback callback, void *userdata, UA_UInt32 *requestId) { return readAttribute_simpleAsync(client, &nodeId, UA_ATTRIBUTEID_VALUERANK, &UA_TYPES[UA_TYPES_INT32], (UA_ClientAsyncOperationCallback)callback, userdata, requestId); } UA_StatusCode UA_Client_readAccessLevelAttribute_async(UA_Client *client, const UA_NodeId nodeId, UA_ClientAsyncReadAccessLevelAttributeCallback callback, void *userdata, UA_UInt32 *requestId) { return readAttribute_simpleAsync(client, &nodeId, UA_ATTRIBUTEID_ACCESSLEVEL, &UA_TYPES[UA_TYPES_BYTE], (UA_ClientAsyncOperationCallback)callback, userdata, requestId); } UA_StatusCode UA_Client_readUserAccessLevelAttribute_async(UA_Client *client, const UA_NodeId nodeId, UA_ClientAsyncReadUserAccessLevelAttributeCallback callback, void *userdata, UA_UInt32 *requestId) { return readAttribute_simpleAsync(client, &nodeId, UA_ATTRIBUTEID_USERACCESSLEVEL, &UA_TYPES[UA_TYPES_BYTE], (UA_ClientAsyncOperationCallback)callback, userdata, requestId); } UA_StatusCode UA_Client_readMinimumSamplingIntervalAttribute_async(UA_Client *client, const UA_NodeId nodeId, UA_ClientAsyncReadMinimumSamplingIntervalAttributeCallback callback, void *userdata, UA_UInt32 *requestId) { return readAttribute_simpleAsync(client, &nodeId, UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL, &UA_TYPES[UA_TYPES_DOUBLE], (UA_ClientAsyncOperationCallback)callback, userdata, requestId); } UA_StatusCode UA_Client_readHistorizingAttribute_async(UA_Client *client, const UA_NodeId nodeId, UA_ClientAsyncReadHistorizingAttributeCallback callback, void *userdata, UA_UInt32 *requestId) { return readAttribute_simpleAsync(client, &nodeId, UA_ATTRIBUTEID_HISTORIZING, &UA_TYPES[UA_TYPES_BOOLEAN], (UA_ClientAsyncOperationCallback)callback, userdata, requestId); } UA_StatusCode UA_Client_readExecutableAttribute_async(UA_Client *client, const UA_NodeId nodeId, UA_ClientAsyncReadExecutableAttributeCallback callback, void *userdata, UA_UInt32 *requestId) { return readAttribute_simpleAsync(client, &nodeId, UA_ATTRIBUTEID_EXECUTABLE, &UA_TYPES[UA_TYPES_BOOLEAN], (UA_ClientAsyncOperationCallback)callback, userdata, requestId); } UA_StatusCode UA_Client_readUserExecutableAttribute_async(UA_Client *client, const UA_NodeId nodeId, UA_ClientAsyncReadUserExecutableAttributeCallback callback, void *userdata, UA_UInt32 *requestId) { return readAttribute_simpleAsync(client, &nodeId, UA_ATTRIBUTEID_USEREXECUTABLE, &UA_TYPES[UA_TYPES_BOOLEAN], (UA_ClientAsyncOperationCallback)callback, userdata, requestId); } /**** amalgamated original file "/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 */ /*****************/ static void MonitoredItem_delete(UA_Client *client, UA_Client_Subscription *sub, UA_Client_MonitoredItem *mon); static void ua_Subscriptions_create(UA_Client *client, UA_Client_Subscription *newSub, UA_CreateSubscriptionResponse *response) { newSub->subscriptionId = response->subscriptionId; newSub->sequenceNumber = 0; newSub->lastActivity = UA_DateTime_nowMonotonic(); newSub->publishingInterval = response->revisedPublishingInterval; newSub->maxKeepAliveCount = response->revisedMaxKeepAliveCount; LIST_INIT(&newSub->monitoredItems); LIST_INSERT_HEAD(&client->subscriptions, newSub, listEntry); } static void ua_Subscriptions_create_handler(UA_Client *client, void *data, UA_UInt32 requestId, void *r) { UA_CreateSubscriptionResponse *response = (UA_CreateSubscriptionResponse *)r; CustomCallback *cc = (CustomCallback *)data; UA_Client_Subscription *newSub = (UA_Client_Subscription *)cc->clientData; if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { UA_free(newSub); goto cleanup; } /* Prepare the internal representation */ ua_Subscriptions_create(client, newSub, response); cleanup: if(cc->userCallback) cc->userCallback(client, cc->userData, requestId, response); UA_free(cc); } UA_CreateSubscriptionResponse 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_Client_Subscription *sub = (UA_Client_Subscription *) UA_malloc(sizeof(UA_Client_Subscription)); if(!sub) { UA_CreateSubscriptionResponse_init(&response); response.responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY; return response; } sub->context = subscriptionContext; sub->statusChangeCallback = statusChangeCallback; sub->deleteCallback = deleteCallback; /* 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 (sub); return response; } ua_Subscriptions_create(client, sub, &response); return response; } UA_StatusCode UA_Client_Subscriptions_create_async(UA_Client *client, const UA_CreateSubscriptionRequest request, void *subscriptionContext, UA_Client_StatusChangeNotificationCallback statusChangeCallback, UA_Client_DeleteSubscriptionCallback deleteCallback, UA_ClientAsyncServiceCallback createCallback, void *userdata, UA_UInt32 *requestId) { CustomCallback *cc = (CustomCallback *)UA_calloc(1, sizeof(CustomCallback)); if(!cc) return UA_STATUSCODE_BADOUTOFMEMORY; UA_Client_Subscription *sub = (UA_Client_Subscription *) UA_malloc(sizeof(UA_Client_Subscription)); if(!sub) { UA_free(cc); return UA_STATUSCODE_BADOUTOFMEMORY; } sub->context = subscriptionContext; sub->statusChangeCallback = statusChangeCallback; sub->deleteCallback = deleteCallback; cc->userCallback = createCallback; cc->userData = userdata; cc->clientData = sub; /* Send the request as asynchronous service call */ return __UA_Client_AsyncService( client, &request, &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONREQUEST], ua_Subscriptions_create_handler, &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONRESPONSE], cc, requestId); } 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; } static void ua_Subscriptions_modify(UA_Client *client, UA_Client_Subscription *sub, const UA_ModifySubscriptionResponse *response) { sub->publishingInterval = response->revisedPublishingInterval; sub->maxKeepAliveCount = response->revisedMaxKeepAliveCount; } static void ua_Subscriptions_modify_handler(UA_Client *client, void *data, UA_UInt32 requestId, void *r) { UA_ModifySubscriptionResponse *response = (UA_ModifySubscriptionResponse *)r; CustomCallback *cc = (CustomCallback *)data; UA_Client_Subscription *sub = findSubscription(client, (UA_UInt32)(uintptr_t)cc->clientData); if(sub) { ua_Subscriptions_modify(client, sub, response); } else { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "No internal representation of subscription %" PRIu32, (UA_UInt32)(uintptr_t)cc->clientData); } if(cc->userCallback) cc->userCallback(client, cc->userData, requestId, response); UA_free(cc); } UA_ModifySubscriptionResponse 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 */ ua_Subscriptions_modify(client, sub, &response); return response; } UA_StatusCode UA_Client_Subscriptions_modify_async(UA_Client *client, const UA_ModifySubscriptionRequest request, UA_ClientAsyncServiceCallback callback, void *userdata, UA_UInt32 *requestId) { /* Find the internal representation */ UA_Client_Subscription *sub = findSubscription(client, request.subscriptionId); if(!sub) return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; CustomCallback *cc = (CustomCallback *)UA_calloc(1, sizeof(CustomCallback)); if(!cc) return UA_STATUSCODE_BADOUTOFMEMORY; cc->clientData = (void *)(uintptr_t)request.subscriptionId; cc->userData = userdata; cc->userCallback = callback; return __UA_Client_AsyncService( client, &request, &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONREQUEST], ua_Subscriptions_modify_handler, &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONRESPONSE], cc, requestId); } static void UA_Client_Subscription_deleteInternal(UA_Client *client, UA_Client_Subscription *sub) { /* Remove the MonitoredItems */ UA_Client_MonitoredItem *mon; UA_Client_MonitoredItem *mon_tmp; LIST_FOREACH_SAFE(mon, &sub->monitoredItems, listEntry, mon_tmp) MonitoredItem_delete(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); } static void UA_Client_Subscription_processDelete(UA_Client *client, const UA_DeleteSubscriptionsRequest *request, const UA_DeleteSubscriptionsResponse *response) { if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) return; /* Check that the request and response size -- use the same index for both */ if(request->subscriptionIdsSize != response->resultsSize) return; for(size_t i = 0; i < request->subscriptionIdsSize; i++) { if(response->results[i] != UA_STATUSCODE_GOOD && response->results[i] != UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID) continue; /* Get the Subscription */ UA_Client_Subscription *sub = findSubscription(client, request->subscriptionIds[i]); if(!sub) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "No internal representation of subscription %" PRIu32, request->subscriptionIds[i]); continue; } /* Delete the Subscription */ UA_Client_Subscription_deleteInternal(client, sub); } } typedef struct { UA_DeleteSubscriptionsRequest request; UA_ClientAsyncServiceCallback userCallback; void *userData; } DeleteSubscriptionCallback; static void ua_Subscriptions_delete_handler(UA_Client *client, void *data, UA_UInt32 requestId, void *r) { UA_DeleteSubscriptionsResponse *response = (UA_DeleteSubscriptionsResponse *)r; DeleteSubscriptionCallback *dsc = (DeleteSubscriptionCallback*)data; /* Delete */ UA_Client_Subscription_processDelete(client, &dsc->request, response); /* Userland Callback */ dsc->userCallback(client, dsc->userData, requestId, response); /* Cleanup */ UA_DeleteSubscriptionsRequest_clear(&dsc->request); UA_free(dsc); } UA_StatusCode UA_Client_Subscriptions_delete_async(UA_Client *client, const UA_DeleteSubscriptionsRequest request, UA_ClientAsyncServiceCallback callback, void *userdata, UA_UInt32 *requestId) { /* Make a copy of the request that persists into the async callback */ DeleteSubscriptionCallback *dsc = (DeleteSubscriptionCallback*)UA_malloc(sizeof(DeleteSubscriptionCallback)); if(!dsc) return UA_STATUSCODE_BADOUTOFMEMORY; dsc->userCallback = callback; dsc->userData = userdata; UA_StatusCode res = UA_DeleteSubscriptionsRequest_copy(&request, &dsc->request); if(res != UA_STATUSCODE_GOOD) { UA_free(dsc); return res; } /* Make the async call */ return __UA_Client_AsyncService( client, &request, &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSREQUEST], ua_Subscriptions_delete_handler, &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSRESPONSE], dsc, requestId); } UA_DeleteSubscriptionsResponse UA_Client_Subscriptions_delete(UA_Client *client, const UA_DeleteSubscriptionsRequest request) { /* Send the request */ UA_DeleteSubscriptionsResponse response; __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSREQUEST], &response, &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSRESPONSE]); /* Process */ UA_Client_Subscription_processDelete(client, &request, &response); return response; } UA_StatusCode 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_clear(&response); return retval; } if(response.resultsSize != 1) { UA_DeleteSubscriptionsResponse_clear(&response); return UA_STATUSCODE_BADINTERNALERROR; } retval = response.results[0]; UA_DeleteSubscriptionsResponse_clear(&response); return retval; } /******************/ /* MonitoredItems */ /******************/ static void MonitoredItem_delete(UA_Client *client, UA_Client_Subscription *sub, UA_Client_MonitoredItem *mon) { LIST_REMOVE(mon, listEntry); if(mon->deleteCallback) mon->deleteCallback(client, sub->subscriptionId, sub->context, mon->monitoredItemId, mon->context); UA_free(mon); } typedef struct { void **contexts; UA_Client_DeleteMonitoredItemCallback *deleteCallbacks; void **handlingCallbacks; UA_CreateMonitoredItemsRequest request; /* Notify the user that the async callback was processed */ UA_ClientAsyncServiceCallback userCallback; void *userData; } MonitoredItems_CreateData; static void MonitoredItems_CreateData_clear(UA_Client *client, MonitoredItems_CreateData *data) { UA_free(data->contexts); UA_free(data->deleteCallbacks); UA_free(data->handlingCallbacks); UA_CreateMonitoredItemsRequest_clear(&data->request); } static void ua_MonitoredItems_create(UA_Client *client, MonitoredItems_CreateData *data, UA_CreateMonitoredItemsResponse *response) { UA_CreateMonitoredItemsRequest *request = &data->request; UA_Client_DeleteMonitoredItemCallback *deleteCallbacks = data->deleteCallbacks; UA_Client_Subscription *sub = findSubscription(client, data->request.subscriptionId); if(!sub) goto cleanup; if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) goto cleanup; if(response->resultsSize != request->itemsToCreateSize) { response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR; goto cleanup; } /* Add internally */ for(size_t i = 0; i < request->itemsToCreateSize; i++) { if(response->results[i].statusCode != UA_STATUSCODE_GOOD) { if(deleteCallbacks[i]) deleteCallbacks[i](client, sub->subscriptionId, sub->context, 0, data->contexts[i]); continue; } UA_Client_MonitoredItem *newMon = (UA_Client_MonitoredItem *) UA_malloc(sizeof(UA_Client_MonitoredItem)); if(!newMon) { if(deleteCallbacks[i]) deleteCallbacks[i](client, sub->subscriptionId, sub->context, 0, data->contexts[i]); continue; } newMon->monitoredItemId = response->results[i].monitoredItemId; newMon->clientHandle = request->itemsToCreate[i].requestedParameters.clientHandle; newMon->context = data->contexts[i]; newMon->deleteCallback = deleteCallbacks[i]; newMon->handler.dataChangeCallback = (UA_Client_DataChangeNotificationCallback)(uintptr_t) data->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 %" PRIu32 " | Added a MonitoredItem with handle %" PRIu32, sub->subscriptionId, newMon->clientHandle); } return; /* Adding failed */ cleanup: for(size_t i = 0; i < request->itemsToCreateSize; i++) { if(deleteCallbacks[i]) deleteCallbacks[i](client, data->request.subscriptionId, sub ? sub->context : NULL, 0, data->contexts[i]); } } static void ua_MonitoredItems_create_async_handler(UA_Client *client, void *d, UA_UInt32 requestId, void *r) { UA_CreateMonitoredItemsResponse *response = (UA_CreateMonitoredItemsResponse *)r; MonitoredItems_CreateData *data = (MonitoredItems_CreateData *)d; ua_MonitoredItems_create(client, data, response); if(data->userCallback) data->userCallback(client, data->userData, requestId, response); MonitoredItems_CreateData_clear(client, data); UA_free(data); } static UA_StatusCode MonitoredItems_CreateData_prepare(UA_Client *client, const UA_CreateMonitoredItemsRequest *request, void **contexts, void **handlingCallbacks, UA_Client_DeleteMonitoredItemCallback *deleteCallbacks, MonitoredItems_CreateData *data) { /* Align arrays and copy over */ UA_StatusCode retval = UA_STATUSCODE_BADOUTOFMEMORY; data->contexts = (void **)UA_calloc(request->itemsToCreateSize, sizeof(void *)); if(!data->contexts) goto cleanup; if(contexts) memcpy(data->contexts, contexts, request->itemsToCreateSize * sizeof(void *)); data->deleteCallbacks = (UA_Client_DeleteMonitoredItemCallback *) UA_calloc(request->itemsToCreateSize, sizeof(UA_Client_DeleteMonitoredItemCallback)); if(!data->deleteCallbacks) goto cleanup; if(deleteCallbacks) memcpy(data->deleteCallbacks, deleteCallbacks, request->itemsToCreateSize * sizeof(UA_Client_DeleteMonitoredItemCallback)); data->handlingCallbacks = (void **) UA_calloc(request->itemsToCreateSize, sizeof(void *)); if(!data->handlingCallbacks) goto cleanup; if(handlingCallbacks) memcpy(data->handlingCallbacks, handlingCallbacks, request->itemsToCreateSize * sizeof(void *)); retval = UA_CreateMonitoredItemsRequest_copy(request, &data->request); if(retval != UA_STATUSCODE_GOOD) goto cleanup; /* Set the clientHandle */ for(size_t i = 0; i < data->request.itemsToCreateSize; i++) data->request.itemsToCreate[i].requestedParameters.clientHandle = ++client->monitoredItemHandles; return UA_STATUSCODE_GOOD; cleanup: MonitoredItems_CreateData_clear(client, data); return retval; } 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; } /* Test if the subscription is valid */ UA_Client_Subscription *sub = findSubscription(client, request->subscriptionId); if(!sub) { response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; return; } MonitoredItems_CreateData data; memset(&data, 0, sizeof(MonitoredItems_CreateData)); UA_StatusCode res = MonitoredItems_CreateData_prepare(client, request, contexts, handlingCallbacks, deleteCallbacks, &data); if(res != UA_STATUSCODE_GOOD) { response->responseHeader.serviceResult = res; return; } /* Call the service. Use data->request as it contains the client handle * information. */ __UA_Client_Service(client, &data.request, &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSREQUEST], response, &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSRESPONSE]); /* Add internal representation */ ua_MonitoredItems_create(client, &data, response); MonitoredItems_CreateData_clear(client, &data); } static UA_StatusCode ua_Client_MonitoredItems_createDataChanges_async( UA_Client *client, const UA_CreateMonitoredItemsRequest request, void **contexts, void **callbacks, UA_Client_DeleteMonitoredItemCallback *deleteCallbacks, UA_ClientAsyncServiceCallback createCallback, void *userdata, UA_UInt32 *requestId) { UA_Client_Subscription *sub = findSubscription(client, request.subscriptionId); if(!sub) return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; MonitoredItems_CreateData *data = (MonitoredItems_CreateData *) UA_calloc(1, sizeof(MonitoredItems_CreateData)); if(!data) return UA_STATUSCODE_BADOUTOFMEMORY; data->userCallback = createCallback; data->userData = userdata; UA_StatusCode res = MonitoredItems_CreateData_prepare( client, &request, contexts, callbacks, deleteCallbacks, data); if(res != UA_STATUSCODE_GOOD) { UA_free(data); return res; } return __UA_Client_AsyncService( client, &data->request, &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSREQUEST], ua_MonitoredItems_create_async_handler, &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSRESPONSE], data, requestId); } UA_CreateMonitoredItemsResponse 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 **)callbacks, deleteCallbacks, &response); return response; } UA_StatusCode UA_Client_MonitoredItems_createDataChanges_async(UA_Client *client, const UA_CreateMonitoredItemsRequest request, void **contexts, UA_Client_DataChangeNotificationCallback *callbacks, UA_Client_DeleteMonitoredItemCallback *deleteCallbacks, UA_ClientAsyncServiceCallback createCallback, void *userdata, UA_UInt32 *requestId) { return ua_Client_MonitoredItems_createDataChanges_async( client, request, contexts, (void **)callbacks, deleteCallbacks, createCallback, userdata, requestId); } UA_MonitoredItemCreateResult 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_clear(&response); return result; } UA_CreateMonitoredItemsResponse 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 **)callback, deleteCallback, &response); return response; } /* Monitor the EventNotifier attribute only */ UA_StatusCode UA_Client_MonitoredItems_createEvents_async(UA_Client *client, const UA_CreateMonitoredItemsRequest request, void **contexts, UA_Client_EventNotificationCallback *callbacks, UA_Client_DeleteMonitoredItemCallback *deleteCallbacks, UA_ClientAsyncServiceCallback createCallback, void *userdata, UA_UInt32 *requestId) { return ua_Client_MonitoredItems_createDataChanges_async( client, request, contexts, (void **)callbacks, deleteCallbacks, createCallback, userdata, requestId); } UA_MonitoredItemCreateResult 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_clear(&response); result.statusCode = retval; return result; } UA_MonitoredItemCreateResult_copy(response.results , &result); UA_CreateMonitoredItemsResponse_clear(&response); return result; } static void ua_MonitoredItems_delete(UA_Client *client, UA_Client_Subscription *sub, const UA_DeleteMonitoredItemsRequest *request, const UA_DeleteMonitoredItemsResponse *response) { #ifdef __clang_analyzer__ return; #endif /* 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; } /* Delete the internal representation */ UA_Client_MonitoredItem *mon; LIST_FOREACH(mon, &sub->monitoredItems, listEntry) { if(mon->monitoredItemId == request->monitoredItemIds[i]) { MonitoredItem_delete(client, sub, mon); break; } } } } static void ua_MonitoredItems_delete_handler(UA_Client *client, void *d, UA_UInt32 requestId, void *r) { CustomCallback *cc = (CustomCallback *)d; UA_DeleteMonitoredItemsResponse *response = (UA_DeleteMonitoredItemsResponse *)r; UA_DeleteMonitoredItemsRequest *request = (UA_DeleteMonitoredItemsRequest *)cc->clientData; if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) goto cleanup; UA_Client_Subscription *sub = findSubscription(client, request->subscriptionId); if(!sub) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "No internal representation of subscription %" PRIu32, request->subscriptionId); goto cleanup; } /* Delete MonitoredItems from the internal representation */ ua_MonitoredItems_delete(client, sub, request, response); cleanup: if(cc->userCallback) cc->userCallback(client, cc->userData, requestId, response); UA_DeleteMonitoredItemsRequest_delete(request); UA_free(cc); } UA_DeleteMonitoredItemsResponse 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]); /* A problem occured remote? */ if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) return response; /* Find the internal subscription representation */ UA_Client_Subscription *sub = findSubscription(client, request.subscriptionId); if(!sub) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT, "No internal representation of subscription %" PRIu32, request.subscriptionId); return response; } /* Remove MonitoredItems in the internal representation */ ua_MonitoredItems_delete(client, sub, &request, &response); return response; } UA_StatusCode UA_Client_MonitoredItems_delete_async(UA_Client *client, const UA_DeleteMonitoredItemsRequest request, UA_ClientAsyncServiceCallback callback, void *userdata, UA_UInt32 *requestId) { /* Send the request */ CustomCallback *cc = (CustomCallback *)UA_calloc(1, sizeof(CustomCallback)); if(!cc) return UA_STATUSCODE_BADOUTOFMEMORY; UA_DeleteMonitoredItemsRequest *req_copy = UA_DeleteMonitoredItemsRequest_new(); if(!req_copy) { UA_free(cc); return UA_STATUSCODE_BADOUTOFMEMORY; } UA_DeleteMonitoredItemsRequest_copy(&request, req_copy); cc->clientData = req_copy; cc->userCallback = callback; cc->userData = userdata; return __UA_Client_AsyncService( client, &request, &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSREQUEST], ua_MonitoredItems_delete_handler, &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSRESPONSE], cc, requestId); } UA_StatusCode 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_clear(&response); return retval; } if(response.resultsSize != 1) { UA_DeleteMonitoredItemsResponse_clear(&response); return UA_STATUSCODE_BADINTERNALERROR; } retval = response.results[0]; UA_DeleteMonitoredItemsResponse_clear(&response); return retval; } UA_ModifyMonitoredItemsResponse UA_Client_MonitoredItems_modify(UA_Client *client, const UA_ModifyMonitoredItemsRequest request) { UA_ModifyMonitoredItemsResponse response; UA_Client_Subscription *sub; 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; 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_clear(&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_WARNING(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Could not process a notification with clienthandle %" PRIu32 " on subscription %" PRIu32, min->clientHandle, sub->subscriptionId); continue; } if(mon->isEventMonitoredItem) { UA_LOG_WARNING(&client->config.logger, UA_LOGCATEGORY_CLIENT, "MonitoredItem is configured for Events. But received a " "DataChangeNotification."); continue; } if(mon->handler.dataChangeCallback) { 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 %" PRIu32 " on subscription %" PRIu32, 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"); } static 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 %" PRId16, 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->sessionState != UA_SESSIONSTATE_ACTIVATED) { 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.subscriptionInactivityCallback) { UA_Client_Subscription* sub = findSubscription(client, response->subscriptionId); if (sub != NULL) client->config.subscriptionInactivityCallback(client, sub->subscriptionId, sub->context); } 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 %" PRIu32 " but got %" PRIu32, 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 %" PRIu32, 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; UA_Client_NotificationsAckNumber *tmp; LIST_FOREACH_SAFE(n, &client->pendingNotificationsAcks, listEntry, tmp) { LIST_REMOVE(n, listEntry); UA_free(n); } UA_Client_Subscription *sub; UA_Client_Subscription *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->sessionState < UA_SESSIONSTATE_ACTIVATED) 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 %" PRIu32 ".", sub->subscriptionId); } } } void UA_Client_Subscriptions_backgroundPublish(UA_Client *client) { if(client->sessionState != UA_SESSIONSTATE_ACTIVATED) return; /* The session must have at least one subscription */ if(!LIST_FIRST(&client->subscriptions)) return; while(client->currentlyOutStandingPublishRequests < client->config.outStandingPublishRequests) { UA_PublishRequest *request = UA_PublishRequest_new(); if(!request) return; request->requestHeader.timeoutHint=60000; UA_StatusCode retval = UA_Client_preparePublishRequest(client, request); if(retval != UA_STATUSCODE_GOOD) { UA_PublishRequest_delete(request); return; } 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; } } } #endif /* UA_ENABLE_SUBSCRIPTIONS */ /**** amalgamated original file "/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 "/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 "/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) { // we need a minimum length if(len <= 2) { *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) { if (last + 1 >= len) { UA_free(str); *out_len = 0; return (unsigned char*)UA_EMPTY_ARRAY_SENTINEL; } uint32_t n = from_b64[p[last]] << 18 | from_b64[p[last + 1]] << 12; *pos++ = (unsigned char)(n >> 16); if(pad2) { if (last + 2 >= len) { UA_free(str); *out_len = 0; return (unsigned char*)UA_EMPTY_ARRAY_SENTINEL; } n |= from_b64[p[last + 2]] << 6; *pos++ = (unsigned char)(n >> 8 & 0xFF); } } *out_len = (uintptr_t)(pos - str); return str; } /**** amalgamated original file "/deps/aa_tree.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 2020 (c) Fraunhofer IOSB (Author: Julius Pfrommer) */ #include #ifdef UNDER_CE /* Windows CE: uintptr_t has already been defined by windows.h */ #elif !defined(_MSC_VER) || _MSC_VER >= 1800 # include #elif !defined(uintptr_t) /* Workaround missing standard includes in older Visual Studio */ # if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 typedef _W64 unsigned int uintptr_t; # else typedef unsigned __int64 uintptr_t; # endif #endif #define aa_entry_container(head, entry) \ ((void*)((uintptr_t)entry - head->entry_offset)) #define aa_entry_key(head, entry) \ ((const void*)((uintptr_t)entry + head->key_offset - head->entry_offset)) #define aa_container_entry(head, container) \ ((struct aa_entry*)((uintptr_t)container + head->entry_offset)) #define aa_container_key(head, container) \ ((const void*)((uintptr_t)container + head->key_offset)) void aa_init(struct aa_head *head, enum aa_cmp (*cmp)(const void*, const void*), unsigned int entry_offset, unsigned int key_offset) { head->root = NULL; head->cmp = cmp; head->entry_offset = entry_offset; head->key_offset = key_offset; } static struct aa_entry * _aa_skew(struct aa_entry *n) { if(!n) return NULL; if(n->left && n->level == n->left->level) { struct aa_entry *l = n->left; n->left = l->right; l->right = n; return l; } return n; } static struct aa_entry * _aa_split(struct aa_entry *n) { if(!n) return NULL; if(n->right && n->right->right && n->right->right->level == n->level) { struct aa_entry *r = n->right; n->right = r->left; r->left = n; r->level++; return r; } return n; } static struct aa_entry * _aa_fixup(struct aa_entry *n) { unsigned int should_be = 0; if(n->left) should_be = n->left->level; if(n->right && n->right->level < should_be) should_be = n->right->level; should_be++; if(should_be < n->level) n->level = should_be; if(n->right && n->right->level > should_be) n->right->level = should_be; n = _aa_skew(n); n->right = _aa_skew(n->right); if(n->right) n->right->right = _aa_skew(n->right->right); n = _aa_split(n); n->right = _aa_split(n->right); return n; } static struct aa_entry * _aa_insert(struct aa_head *h, struct aa_entry *n, void *elem) { if(!n) { struct aa_entry *e = aa_container_entry(h, elem); e->left = NULL; e->right = NULL; e->level = 1; return e; } const void *n_key = aa_entry_key(h, n); const void *key = aa_container_key(h, elem); enum aa_cmp eq = h->cmp(key, n_key); if(eq == AA_CMP_EQ) eq = (key > n_key) ? AA_CMP_MORE : AA_CMP_LESS; if(eq == AA_CMP_LESS) n->left = _aa_insert(h, n->left, elem); else n->right = _aa_insert(h, n->right, elem); return _aa_split(_aa_skew(n)); } void aa_insert(struct aa_head *h, void *elem) { h->root = _aa_insert(h, h->root, elem); } void * aa_find(const struct aa_head *h, const void *key) { struct aa_entry *n = h->root; while(n) { enum aa_cmp eq = h->cmp(key, aa_entry_key(h, n)); if(eq == AA_CMP_EQ) return aa_entry_container(h, n); n = (eq == AA_CMP_LESS) ? n->left : n->right; } return NULL; } static struct aa_entry * unlink_succ(struct aa_entry *n, struct aa_entry **succ) { if(!n->left) { *succ = n; return n->right; } n->left = unlink_succ(n->left, succ); return _aa_fixup(n); } static struct aa_entry * unlink_pred(struct aa_entry *n, struct aa_entry **pred) { if(!n->right) { *pred = n; return n->left; } n->right = unlink_pred(n->right, pred); return _aa_fixup(n); } static struct aa_entry * _aa_remove(struct aa_head *h, void *elem, struct aa_entry *n) { if(!n) return NULL; const void *elem_key = aa_container_key(h, elem); const void *n_key = aa_entry_key(h, n); if(n_key == elem_key) { if(!n->left && !n->right) return NULL; struct aa_entry *replace = NULL; if(!n->left) n->right = unlink_succ(n->right, &replace); else n->left = unlink_pred(n->left, &replace); replace->left = n->left; replace->right = n->right; replace->level = n->level; return _aa_fixup(replace); } enum aa_cmp eq = h->cmp(elem_key, n_key); if(eq == AA_CMP_EQ) eq = (elem_key > n_key) ? AA_CMP_MORE : AA_CMP_LESS; if(eq == AA_CMP_LESS) n->left = _aa_remove(h, elem, n->left); else n->right = _aa_remove(h, elem, n->right); return _aa_fixup(n); } void aa_remove(struct aa_head *head, void *elem) { head->root = _aa_remove(head, elem, head->root); } void * aa_min(const struct aa_head *head) { struct aa_entry *e = head->root; if(!e) return NULL; while(e->left) e = e->left; return aa_entry_container(head, e); } void * aa_max(const struct aa_head *head) { struct aa_entry *e = head->root; if(!e) return NULL; while(e->right) e = e->right; return aa_entry_container(head, e); } void * aa_next(const struct aa_head *head, const void *elem) { struct aa_entry *e = aa_container_entry(head, elem); if(e->right) { e = e->right; while(e->left) e = e->left; return aa_entry_container(head, e); } struct aa_entry *next = NULL; struct aa_entry *n = head->root; const void *key = aa_container_key(head, elem); while(n && n != e) { const void *n_key = aa_entry_key(head, n); enum aa_cmp eq = head->cmp(key, n_key); if(eq == AA_CMP_EQ) eq = (key > n_key) ? AA_CMP_MORE : AA_CMP_LESS; if(eq == AA_CMP_MORE) { n = n->right; } else { next = n; n = n->left; } } return (next) ? aa_entry_container(head, next) : NULL; } void * aa_prev(const struct aa_head *head, const void *elem) { struct aa_entry *e = aa_container_entry(head, elem); if(e->left) { e = e->left; while(e->right) e = e->right; return aa_entry_container(head, e); } struct aa_entry *prev = NULL; struct aa_entry *n = head->root; const void *key = aa_container_key(head, elem); while(n && n != e) { const void *n_key = aa_entry_key(head, n); enum aa_cmp eq = head->cmp(key, n_key); if(eq == AA_CMP_EQ) eq = (key > n_key) ? AA_CMP_MORE : AA_CMP_LESS; if(eq == AA_CMP_MORE) { prev = n; n = n->right; } else { n = n->left; } } return (prev) ? aa_entry_container(head, prev) : NULL; } /**** amalgamated original file "/deps/ziptree.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 2021 (c) Julius Pfrommer */ #if defined(_MSC_VER) #include #endif unsigned char __ZIP_FFS32(unsigned int v) { #if defined(__GNUC__) || defined(__clang__) return (unsigned char)((unsigned char)__builtin_ffs((int)v) - 1u); #elif defined(_MSC_VER) unsigned long index = 255; _BitScanForward(&index, v); return (unsigned char)index; #else if(v == 0) return 255; unsigned int t = 1; unsigned char r = 0; while((v & t) == 0) { t = t << 1; r++; } return r; #endif } /* Generic analog to ZIP_ENTRY(type) */ struct zip_entry { void *left; void *right; unsigned char rank; }; #define ZIP_ENTRY_PTR(x) (struct zip_entry*)((char*)x + fieldoffset) #define ZIP_KEY_PTR(x) (void*)((char*)x + keyoffset) void * __ZIP_INSERT(zip_cmp_cb cmp, unsigned short fieldoffset, unsigned short keyoffset, void *root, void *elm) { struct zip_entry *root_entry = ZIP_ENTRY_PTR(root); struct zip_entry *elm_entry = ZIP_ENTRY_PTR(elm); if(!root) { elm_entry->left = NULL; elm_entry->right = NULL; return elm; } enum ZIP_CMP order = cmp(ZIP_KEY_PTR(elm), ZIP_KEY_PTR(root)); if(order == ZIP_CMP_LESS) { if(__ZIP_INSERT(cmp, fieldoffset, keyoffset, root_entry->left, elm) == elm) { if(elm_entry->rank < root_entry->rank) { root_entry->left = elm; } else { root_entry->left = elm_entry->right; elm_entry->right = root; return elm; } } } else { if(__ZIP_INSERT(cmp, fieldoffset, keyoffset, root_entry->right, elm) == elm) { if(elm_entry->rank <= root_entry->rank) { root_entry->right = elm; } else { root_entry->right = elm_entry->left; elm_entry->left = root; return elm; } } } return root; } static void * __ZIP(unsigned short fieldoffset, void *x, void *y) { if(!x) return y; if(!y) return x; struct zip_entry *x_entry = ZIP_ENTRY_PTR(x); struct zip_entry *y_entry = ZIP_ENTRY_PTR(y); if(x_entry->rank < y_entry->rank) { y_entry->left = __ZIP(fieldoffset, x, y_entry->left); return y; } else { x_entry->right = __ZIP(fieldoffset, x_entry->right, y); return x; } } /* Modified from the original algorithm. Allow multiple elements with the same * key. */ void * __ZIP_REMOVE(zip_cmp_cb cmp, unsigned short fieldoffset, unsigned short keyoffset, void *root, void *elm) { struct zip_entry *root_entry = ZIP_ENTRY_PTR(root); if(root == elm) return __ZIP(fieldoffset, root_entry->left, root_entry->right); void *left = root_entry->left; void *right = root_entry->right; enum ZIP_CMP eq = cmp(ZIP_KEY_PTR(elm), ZIP_KEY_PTR(root)); if(eq == ZIP_CMP_LESS) { struct zip_entry *left_entry = ZIP_ENTRY_PTR(left); if(elm == left) root_entry->left = __ZIP(fieldoffset, left_entry->left, left_entry->right); else if(left) __ZIP_REMOVE(cmp, fieldoffset, keyoffset, left, elm); } else if(eq == ZIP_CMP_MORE) { struct zip_entry *right_entry = ZIP_ENTRY_PTR(right); if(elm == right) root_entry->right = __ZIP(fieldoffset, right_entry->left, right_entry->right); else if(right) __ZIP_REMOVE(cmp, fieldoffset, keyoffset, right, elm); } else { /* ZIP_CMP_EQ, but root != elm */ if(right) root_entry->right = __ZIP_REMOVE(cmp, fieldoffset, keyoffset, right, elm); if(left) root_entry->left = __ZIP_REMOVE(cmp, fieldoffset, keyoffset, left, elm); } return root; } void * __ZIP_FIND(zip_cmp_cb cmp, unsigned short fieldoffset, unsigned short keyoffset, void *root, const void *key) { if(!root) return NULL; enum ZIP_CMP eq = cmp(key, ZIP_KEY_PTR(root)); if(eq == ZIP_CMP_EQ) return root; struct zip_entry *root_entry = ZIP_ENTRY_PTR(root); if(eq == ZIP_CMP_LESS) return __ZIP_FIND(cmp, fieldoffset, keyoffset, root_entry->left, key); else return __ZIP_FIND(cmp, fieldoffset, keyoffset, root_entry->right, key); } void * __ZIP_MIN(unsigned short fieldoffset, void *elm) { if(!elm) return NULL; struct zip_entry *elm_entry = ZIP_ENTRY_PTR(elm); while(elm_entry->left) { elm = elm_entry->left; elm_entry = (struct zip_entry*)((char*)elm + fieldoffset); } return elm; } void * __ZIP_MAX(unsigned short fieldoffset, void *elm) { if(!elm) return NULL; struct zip_entry *elm_entry = ZIP_ENTRY_PTR(elm); while(elm_entry->right) { elm = elm_entry->right; elm_entry = (struct zip_entry*)((char*)elm + fieldoffset); } return elm; } void __ZIP_ITER(unsigned short fieldoffset, __zip_iter_cb cb, void *context, void *elm) { if(!elm) return; struct zip_entry *elm_entry = ZIP_ENTRY_PTR(elm); __ZIP_ITER(fieldoffset, cb, context, elm_entry->left); __ZIP_ITER(fieldoffset, cb, context, elm_entry->right); cb(elm, context); } /**** amalgamated original file "/src/pubsub/ua_pubsub_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 (c) 2020 Yannick Wallerer, Siemens AG * Copyright (c) 2020 Thomas Fischer, Siemens AG */ #ifdef UA_ENABLE_PUBSUB #ifdef UA_ENABLE_PUBSUB_ETH_UADP #endif #endif #ifdef UA_ENABLE_PUBSUB_FILE_CONFIG static UA_StatusCode createPubSubConnection(UA_Server *server, const UA_PubSubConnectionDataType *connection, UA_UInt32 pdsCount, UA_NodeId *pdsIdent); static UA_StatusCode createWriterGroup(UA_Server *server, const UA_WriterGroupDataType *writerGroupParameters, UA_NodeId connectionIdent, UA_UInt32 pdsCount, const UA_NodeId *pdsIdent); static UA_StatusCode createDataSetWriter(UA_Server *server, const UA_DataSetWriterDataType *dataSetWriterParameters, UA_NodeId writerGroupIdent, UA_UInt32 pdsCount, const UA_NodeId *pdsIdent); static UA_StatusCode createReaderGroup(UA_Server *server, const UA_ReaderGroupDataType *readerGroupParameters, UA_NodeId connectionIdent); static UA_StatusCode createDataSetReader(UA_Server *server, const UA_DataSetReaderDataType *dataSetReaderParameters, UA_NodeId readerGroupIdent); static UA_StatusCode createPublishedDataSet(UA_Server *server, const UA_PublishedDataSetDataType *publishedDataSetParameters, UA_NodeId *publishedDataSetIdent); static UA_StatusCode createDataSetFields(UA_Server *server, const UA_NodeId *publishedDataSetIdent, const UA_PublishedDataSetDataType *publishedDataSetParameters); static UA_StatusCode generatePubSubConfigurationDataType(const UA_Server *server, UA_PubSubConfigurationDataType *pubSubConfiguration); /* Gets PubSub Configuration from an ExtensionObject */ static UA_StatusCode extractPubSubConfigFromExtensionObject(const UA_ExtensionObject *src, UA_PubSubConfigurationDataType **dst) { if(src->encoding != UA_EXTENSIONOBJECT_DECODED || src->content.decoded.type != &UA_TYPES[UA_TYPES_UABINARYFILEDATATYPE]) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_extractPubSubConfigFromDecodedObject] " "Reading extensionObject failed"); return UA_STATUSCODE_BADINVALIDARGUMENT; } UA_UABinaryFileDataType *binFile = (UA_UABinaryFileDataType*)src->content.decoded.data; if(binFile->body.arrayLength != 0 || binFile->body.arrayDimensionsSize != 0) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_extractPubSubConfigFromDecodedObject] " "Loading multiple configurations is not supported"); return UA_STATUSCODE_BADNOTIMPLEMENTED; } if(binFile->body.type != &UA_TYPES[UA_TYPES_PUBSUBCONFIGURATIONDATATYPE]) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_extractPubSubConfigFromDecodedObject] " "Invalid datatype encoded in the binary file"); return UA_STATUSCODE_BADTYPEMISMATCH; } *dst = (UA_PubSubConfigurationDataType*)binFile->body.data; return UA_STATUSCODE_GOOD; } /* Configures a PubSub Server with given PubSubConfigurationDataType object */ static UA_StatusCode updatePubSubConfig(UA_Server *server, const UA_PubSubConfigurationDataType *configurationParameters) { if(server == NULL || configurationParameters == NULL) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_updatePubSubConfig] Invalid argument"); return UA_STATUSCODE_BADINVALIDARGUMENT; } UA_PubSubManager_delete(server, &server->pubSubManager); /* Configuration of Published DataSets: */ UA_UInt32 pdsCount = (UA_UInt32)configurationParameters->publishedDataSetsSize; UA_NodeId *publishedDataSetIdent = (UA_NodeId*)UA_calloc(pdsCount, sizeof(UA_NodeId)); if(!publishedDataSetIdent) return UA_STATUSCODE_BADOUTOFMEMORY; UA_StatusCode res = UA_STATUSCODE_GOOD; for(UA_UInt32 i = 0; i < pdsCount; i++) { res = createPublishedDataSet(server, &configurationParameters->publishedDataSets[i], &publishedDataSetIdent[i]); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_updatePubSubConfig] PDS creation failed"); UA_free(publishedDataSetIdent); return res; } } /* Configuration of PubSub Connections: */ if(configurationParameters->connectionsSize < 1) { UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_updatePubSubConfig] no connection in " "UA_PubSubConfigurationDataType"); UA_free(publishedDataSetIdent); return UA_STATUSCODE_GOOD; } for(size_t i = 0; i < configurationParameters->connectionsSize; i++) { res = createPubSubConnection(server, &configurationParameters->connections[i], pdsCount, publishedDataSetIdent); if(res != UA_STATUSCODE_GOOD) break; } UA_free(publishedDataSetIdent); return res; } /* Function called by UA_PubSubManager_createPubSubConnection to set the * PublisherId of a certain connection. */ static UA_StatusCode setConnectionPublisherId(const UA_PubSubConnectionDataType *src, UA_PubSubConnectionConfig *dst) { if(src->publisherId.type == &UA_TYPES[UA_TYPES_STRING]) { dst->publisherIdType = UA_PUBSUB_PUBLISHERID_STRING; dst->publisherId.string = *(UA_String*)src->publisherId.data; } else if(src->publisherId.type == &UA_TYPES[UA_TYPES_BYTE] || src->publisherId.type == &UA_TYPES[UA_TYPES_UINT16] || src->publisherId.type == &UA_TYPES[UA_TYPES_UINT32]) { dst->publisherIdType = UA_PUBSUB_PUBLISHERID_NUMERIC; dst->publisherId.numeric = *(UA_UInt32*)src->publisherId.data; } else if(src->publisherId.type == &UA_TYPES[UA_TYPES_UINT64]) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_setConnectionPublisherId] PublisherId is UInt64 " "(not implemented); Recommended dataType for PublisherId: UInt32"); return UA_STATUSCODE_BADNOTIMPLEMENTED; } else { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_setConnectionPublisherId] PublisherId is not valid."); return UA_STATUSCODE_BADINTERNALERROR; } return UA_STATUSCODE_GOOD; } /* Function called by UA_PubSubManager_createPubSubConnection to create all WriterGroups * and ReaderGroups that belong to a certain connection. */ static UA_StatusCode createComponentsForConnection(UA_Server *server, const UA_PubSubConnectionDataType *connParams, UA_NodeId connectionIdent, UA_UInt32 pdsCount, const UA_NodeId *pdsIdent) { /* WriterGroups configuration */ UA_StatusCode res = UA_STATUSCODE_GOOD; for(size_t i = 0; i < connParams->writerGroupsSize; i++) { res = createWriterGroup(server, &connParams->writerGroups[i], connectionIdent, pdsCount, pdsIdent); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_createComponentsForConnection] " "Error occured during %d. WriterGroup Creation", (UA_UInt32)i+1); return res; } } /* ReaderGroups configuration */ for(size_t j = 0; j < connParams->readerGroupsSize; j++) { res = createReaderGroup(server, &connParams->readerGroups[j], connectionIdent); if(res == UA_STATUSCODE_GOOD) res |= UA_PubSubConnection_regist(server, &connectionIdent); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_createComponentsForConnection] " "Error occured during %d. ReaderGroup Creation", (UA_UInt32)j+1); return res; } } return res; } /* Checks if transportLayer for the specified transportProfileUri exists. * * @param server Server object that shall be configured * @param transportProfileUri String that specifies the transport protocol */ static UA_Boolean transportLayerExists(UA_Server *server, UA_String transportProfileUri) { for(size_t i = 0; i < server->config.pubSubConfig.transportLayersSize; i++) { if(UA_String_equal(&server->config.pubSubConfig.transportLayers[i].transportProfileUri, &transportProfileUri)) { return true; } } return false; } /* Creates transportlayer for specified transport protocol if this layer doesn't exist yet */ static UA_StatusCode createTransportLayer(UA_Server *server, const UA_String transportProfileUri) { if(transportLayerExists(server, transportProfileUri)) return UA_STATUSCODE_GOOD; UA_ServerConfig *config = UA_Server_getConfig(server); UA_PubSubTransportLayer tl; do { UA_String strUDP = UA_STRING("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp"); if(UA_String_equal(&transportProfileUri, &strUDP)) { tl = UA_PubSubTransportLayerUDPMP(); break; } #ifdef UA_ENABLE_PUBSUB_ETH_UADP UA_String strETH = UA_STRING("http://opcfoundation.org/UA-Profile/Transport/pubsub-eth-uadp"); if(UA_String_equal(&transportProfileUri, &strETH)) { tl = UA_PubSubTransportLayerEthernet(); break; } #endif UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_createTransportLayer] " "invalid transportProfileUri"); return UA_STATUSCODE_BADINVALIDARGUMENT; } while(0); if(config->pubSubConfig.transportLayersSize > 0) { config->pubSubConfig.transportLayers = (UA_PubSubTransportLayer *) UA_realloc(config->pubSubConfig.transportLayers, (config->pubSubConfig.transportLayersSize + 1) * sizeof(UA_PubSubTransportLayer)); } else { config->pubSubConfig.transportLayers = (UA_PubSubTransportLayer *) UA_calloc(1, sizeof(UA_PubSubTransportLayer)); } if(config->pubSubConfig.transportLayers == NULL) { UA_Server_delete(server); return UA_STATUSCODE_BADOUTOFMEMORY; } config->pubSubConfig.transportLayers[config->pubSubConfig.transportLayersSize] = tl; config->pubSubConfig.transportLayersSize++; return UA_STATUSCODE_GOOD; } /* Creates PubSubConnection configuration from PubSubConnectionDataType object * * @param server Server object that shall be configured * @param connParams PubSub connection configuration * @param pdsCount Number of published DataSets * @param pdsIdent Array of NodeIds of the published DataSets */ static UA_StatusCode createPubSubConnection(UA_Server *server, const UA_PubSubConnectionDataType *connParams, UA_UInt32 pdsCount, UA_NodeId *pdsIdent) { UA_PubSubConnectionConfig config; memset(&config, 0, sizeof(UA_PubSubConnectionConfig)); config.name = connParams->name; config.enabled = connParams->enabled; config.transportProfileUri = connParams->transportProfileUri; config.connectionPropertiesSize = connParams->connectionPropertiesSize; if(config.connectionPropertiesSize > 0) { config.connectionProperties = connParams->connectionProperties; } UA_StatusCode res = setConnectionPublisherId(connParams, &config); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_createPubSubConnection] " "Setting PublisherId failed"); return res; } if(connParams->address.encoding == UA_EXTENSIONOBJECT_DECODED) { UA_Variant_setScalar(&(config.address), connParams->address.content.decoded.data, connParams->address.content.decoded.type); } else { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_createPubSubConnection] " "Reading connection address failed"); return UA_STATUSCODE_BADINTERNALERROR; } if(connParams->transportSettings.encoding == UA_EXTENSIONOBJECT_DECODED) { UA_Variant_setScalar(&(config.connectionTransportSettings), connParams->transportSettings.content.decoded.data, connParams->transportSettings.content.decoded.type); } else { UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_createPubSubConnection] " "TransportSettings can not be read"); } res = createTransportLayer(server, connParams->transportProfileUri); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_createPubSubConnection] " "Creating transportLayer failed"); return res; } /* Load connection config into server: */ UA_NodeId connectionIdent; res = UA_Server_addPubSubConnection(server, &config, &connectionIdent); if(res == UA_STATUSCODE_GOOD) { /* Configuration of all Components that belong to this connection: */ res = createComponentsForConnection(server, connParams, connectionIdent, pdsCount, pdsIdent); } else { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_createPubSubConnection] " "Connection creation failed"); } return res; } /* Function called by UA_PubSubManager_createWriterGroup to configure the messageSettings * of a writerGroup */ static UA_StatusCode setWriterGroupEncodingType(const UA_WriterGroupDataType *writerGroupParameters, UA_WriterGroupConfig *config) { if(writerGroupParameters->messageSettings.encoding != UA_EXTENSIONOBJECT_DECODED) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_setWriterGroupEncodingType] " "getting message type information failed"); return UA_STATUSCODE_BADINVALIDARGUMENT; } if(writerGroupParameters->messageSettings.content.decoded.type == &UA_TYPES[UA_TYPES_UADPWRITERGROUPMESSAGEDATATYPE]) { config->encodingMimeType = UA_PUBSUB_ENCODING_UADP; } else if(writerGroupParameters->messageSettings.content.decoded.type == &UA_TYPES[UA_TYPES_JSONWRITERGROUPMESSAGEDATATYPE]) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_setWriterGroupEncodingType] " "encoding type: JSON (not implemented!)"); return UA_STATUSCODE_BADNOTIMPLEMENTED; } else { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_setWriterGroupEncodingType] " "invalid message encoding type"); return UA_STATUSCODE_BADINVALIDARGUMENT; } return UA_STATUSCODE_GOOD; } /* WriterGroup configuration from WriterGroup object * * @param server Server object that shall be configured * @param writerGroupParameters WriterGroup configuration * @param connectionIdent NodeId of the PubSub connection, the WriterGroup belongs to * @param pdsCount Number of published DataSets * @param pdsIdent Array of NodeIds of the published DataSets */ static UA_StatusCode createWriterGroup(UA_Server *server, const UA_WriterGroupDataType *writerGroupParameters, UA_NodeId connectionIdent, UA_UInt32 pdsCount, const UA_NodeId *pdsIdent) { UA_WriterGroupConfig config; memset(&config, 0, sizeof(UA_WriterGroupConfig)); config.name = writerGroupParameters->name; config.enabled = writerGroupParameters->enabled; config.writerGroupId = writerGroupParameters->writerGroupId; config.publishingInterval = writerGroupParameters->publishingInterval; config.keepAliveTime = writerGroupParameters->keepAliveTime; config.priority = writerGroupParameters->priority; config.securityMode = writerGroupParameters->securityMode; config.transportSettings = writerGroupParameters->transportSettings; config.messageSettings = writerGroupParameters->messageSettings; config.groupPropertiesSize = writerGroupParameters->groupPropertiesSize; if(config.groupPropertiesSize > 0) config.groupProperties = writerGroupParameters->groupProperties; config.maxEncapsulatedDataSetMessageCount = 255; /* non std parameter */ UA_StatusCode res = setWriterGroupEncodingType(writerGroupParameters, &config); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_createWriterGroup] " "Setting message settings failed"); return res; } /* Load config into server: */ UA_NodeId writerGroupIdent; res = UA_Server_addWriterGroup(server, connectionIdent, &config, &writerGroupIdent); UA_Server_setWriterGroupOperational(server, writerGroupIdent); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_createWriterGroup] " "Adding WriterGroup to server failed: 0x%x", res); return res; } /* Configuration of all DataSetWriters that belong to this WriterGroup */ for(size_t dsw = 0; dsw < writerGroupParameters->dataSetWritersSize; dsw++) { res = createDataSetWriter(server, &writerGroupParameters->dataSetWriters[dsw], writerGroupIdent, pdsCount, pdsIdent); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_createWriterGroup] " "DataSetWriter Creation failed."); break; } } return res; } /* Function called by UA_PubSubManager_createDataSetWriter. It searches for a * PublishedDataSet that is referenced by the DataSetWriter. If a related PDS is found, * the DSWriter will be added to the server, otherwise, no DSWriter will be added. * * @param server UA_Server object that shall be configured * @param writerGroupIdent NodeId of writerGroup, the DataSetWriter belongs to * @param dsWriterConfig WriterGroup configuration * @param pdsCount Number of published DataSets * @param pdsIdent Array of NodeIds of the published DataSets */ static UA_StatusCode addDataSetWriterWithPdsReference(UA_Server *server, UA_NodeId writerGroupIdent, const UA_DataSetWriterConfig *dsWriterConfig, UA_UInt32 pdsCount, const UA_NodeId *pdsIdent) { UA_NodeId dataSetWriterIdent; UA_PublishedDataSetConfig pdsConfig; UA_Boolean pdsFound = false; UA_StatusCode res = UA_STATUSCODE_GOOD; for(size_t pds = 0; pds < pdsCount && res == UA_STATUSCODE_GOOD; pds++) { res = UA_Server_getPublishedDataSetConfig(server, pdsIdent[pds], &pdsConfig); /* members of pdsConfig must be deleted manually */ if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_addDataSetWriterWithPdsReference] " "Getting pdsConfig from NodeId failed."); return res; } if(dsWriterConfig->dataSetName.length == pdsConfig.name.length && 0 == strncmp((const char *)dsWriterConfig->dataSetName.data, (const char *)pdsConfig.name.data, dsWriterConfig->dataSetName.length)) { /* DSWriter will only be created, if a matching PDS is found: */ res = UA_Server_addDataSetWriter(server, writerGroupIdent, pdsIdent[pds], dsWriterConfig, &dataSetWriterIdent); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_addDataSetWriterWithPdsReference] " "Adding DataSetWriter failed"); } else { pdsFound = true; } UA_PublishedDataSetConfig_clear(&pdsConfig); if(pdsFound) break; /* break loop if corresponding publishedDataSet was found */ } } if(!pdsFound) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_addDataSetWriterWithPdsReference] " "No matching DataSet found; no DataSetWriter created"); } return res; } /* Creates DataSetWriter configuration from DataSetWriter object * * @param server UA_Server object that shall be configured * @param dataSetWriterParameters DataSetWriter Configuration * @param writerGroupIdent NodeId of writerGroup, the DataSetWriter belongs to * @param pdsCount Number of published DataSets * @param pdsIdent Array of NodeIds of the published DataSets */ static UA_StatusCode createDataSetWriter(UA_Server *server, const UA_DataSetWriterDataType *dataSetWriterParameters, UA_NodeId writerGroupIdent, UA_UInt32 pdsCount, const UA_NodeId *pdsIdent) { UA_DataSetWriterConfig config; memset(&config, 0, sizeof(UA_DataSetWriterConfig)); config.name = dataSetWriterParameters->name; config.dataSetWriterId = dataSetWriterParameters->dataSetWriterId; config.keyFrameCount = dataSetWriterParameters->keyFrameCount; config.dataSetFieldContentMask = dataSetWriterParameters->dataSetFieldContentMask; config.messageSettings = dataSetWriterParameters->messageSettings; config.dataSetName = dataSetWriterParameters->dataSetName; config.dataSetWriterPropertiesSize = dataSetWriterParameters->dataSetWriterPropertiesSize; if(config.dataSetWriterPropertiesSize > 0) config.dataSetWriterProperties = dataSetWriterParameters->dataSetWriterProperties; UA_StatusCode res = addDataSetWriterWithPdsReference(server, writerGroupIdent, &config, pdsCount, pdsIdent); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_createDataSetWriter] " "Referencing related PDS failed"); } return res; } /* Creates ReaderGroup configuration from ReaderGroup object * * @param server UA_Server object that shall be configured * @param readerGroupParameters ReaderGroup configuration * @param connectionIdent NodeId of the PubSub connection, the ReaderGroup belongs to */ static UA_StatusCode createReaderGroup(UA_Server *server, const UA_ReaderGroupDataType *readerGroupParameters, UA_NodeId connectionIdent) { UA_ReaderGroupConfig config; memset(&config, 0, sizeof(UA_ReaderGroupConfig)); config.name = readerGroupParameters->name; config.securityParameters.securityMode = readerGroupParameters->securityMode; UA_NodeId readerGroupIdent; UA_StatusCode res = UA_Server_addReaderGroup(server, connectionIdent, &config, &readerGroupIdent); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_createReaderGroup] Adding ReaderGroup " "to server failed: 0x%x", res); return res; } UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_createReaderGroup] ReaderGroup successfully added."); for(UA_UInt32 i = 0; i < readerGroupParameters->dataSetReadersSize; i++) { res = createDataSetReader(server, &readerGroupParameters->dataSetReaders[i], readerGroupIdent); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_createReaderGroup] Creating DataSetReader failed"); break; } } if(res == UA_STATUSCODE_GOOD) UA_Server_setReaderGroupOperational(server, readerGroupIdent); return res; } /* Creates TargetVariables or SubscribedDataSetMirror for a given DataSetReader * * @param server UA_Server object that shall be configured * @param dsReaderIdent NodeId of the DataSetReader the SubscribedDataSet belongs to * @param dataSetReaderParameters Configuration Parameters of the DataSetReader */ static UA_StatusCode addSubscribedDataSet(UA_Server *server, const UA_NodeId dsReaderIdent, const UA_ExtensionObject *subscribedDataSet) { if(subscribedDataSet->content.decoded.type == &UA_TYPES[UA_TYPES_TARGETVARIABLESDATATYPE]) { UA_TargetVariablesDataType *tmpTargetVars = (UA_TargetVariablesDataType*) subscribedDataSet->content.decoded.data; UA_FieldTargetVariable *targetVars = (UA_FieldTargetVariable *) UA_calloc(tmpTargetVars->targetVariablesSize, sizeof(UA_FieldTargetVariable)); for(size_t index = 0; index < tmpTargetVars->targetVariablesSize; index++) { UA_FieldTargetDataType_copy(&tmpTargetVars->targetVariables[index], &targetVars[index].targetVariable); } UA_StatusCode res = UA_Server_DataSetReader_createTargetVariables(server, dsReaderIdent, tmpTargetVars->targetVariablesSize, targetVars); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_addSubscribedDataSet] " "create TargetVariables failed"); } for(size_t index = 0; index < tmpTargetVars->targetVariablesSize; index++) { UA_FieldTargetDataType_clear(&targetVars[index].targetVariable); } UA_free(targetVars); return res; } if(subscribedDataSet->content.decoded.type == &UA_TYPES[UA_TYPES_SUBSCRIBEDDATASETMIRRORDATATYPE]) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_addSubscribedDataSet] " "DataSetMirror is currently not supported"); return UA_STATUSCODE_BADINVALIDARGUMENT; } UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_addSubscribedDataSet] " "Invalid Type of SubscribedDataSet"); return UA_STATUSCODE_BADINTERNALERROR; } /* Creates DataSetReader configuration from DataSetReader object * * @param server UA_Server object that shall be configured * @param dataSetReaderParameters DataSetReader configuration * @param writerGroupIdent NodeId of readerGroupParameters, the DataSetReader belongs to */ static UA_StatusCode createDataSetReader(UA_Server *server, const UA_DataSetReaderDataType *dsrParams, UA_NodeId readerGroupIdent) { UA_DataSetReaderConfig config; memset(&config, 0, sizeof(UA_DataSetReaderConfig)); config.name = dsrParams->name; config.publisherId = dsrParams->publisherId; config.writerGroupId = dsrParams->writerGroupId; config.dataSetWriterId = dsrParams->dataSetWriterId; config.dataSetMetaData = dsrParams->dataSetMetaData; config.dataSetFieldContentMask = dsrParams->dataSetFieldContentMask; config.messageReceiveTimeout = dsrParams->messageReceiveTimeout; config.messageSettings = dsrParams->messageSettings; UA_NodeId dsReaderIdent; UA_StatusCode res = UA_Server_addDataSetReader(server, readerGroupIdent, &config, &dsReaderIdent); if(res == UA_STATUSCODE_GOOD) res = addSubscribedDataSet(server, dsReaderIdent, &dsrParams->subscribedDataSet); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_createDataSetReader] " "create subscribedDataSet failed"); } return res; } /* Determines whether PublishedDataSet is of type PublishedItems or PublishedEvents. * (PublishedEvents are currently not supported!) * * @param publishedDataSetParameters PublishedDataSet parameters * @param config PublishedDataSet configuration object */ static UA_StatusCode setPublishedDataSetType(const UA_PublishedDataSetDataType *pdsParams, UA_PublishedDataSetConfig *config) { if(pdsParams->dataSetSource.encoding != UA_EXTENSIONOBJECT_DECODED) return UA_STATUSCODE_BADINTERNALERROR; const UA_DataType *sourceType = pdsParams->dataSetSource.content.decoded.type; if(sourceType == &UA_TYPES[UA_TYPES_PUBLISHEDDATAITEMSDATATYPE]) { config->publishedDataSetType = UA_PUBSUB_DATASET_PUBLISHEDITEMS; return UA_STATUSCODE_GOOD; } else if(sourceType == &UA_TYPES[UA_TYPES_PUBLISHEDEVENTSDATATYPE]) { /* config.publishedDataSetType = UA_PUBSUB_DATASET_PUBLISHEDEVENTS; */ UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_setPublishedDataSetType] Published events not supported."); return UA_STATUSCODE_BADNOTIMPLEMENTED; } UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_setPublishedDataSetType] Invalid DataSetSourceDataType."); return UA_STATUSCODE_BADINTERNALERROR; } /* Creates PublishedDataSetConfig object from PublishedDataSet object * * @param server UA_Server object that shall be configured * @param pdsParams publishedDataSet configuration * @param pdsIdent NodeId of the publishedDataSet */ static UA_StatusCode createPublishedDataSet(UA_Server *server, const UA_PublishedDataSetDataType *pdsParams, UA_NodeId *pdsIdent) { UA_PublishedDataSetConfig config; memset(&config, 0, sizeof(UA_PublishedDataSetConfig)); config.name = pdsParams->name; UA_StatusCode res = setPublishedDataSetType(pdsParams, &config); if(res != UA_STATUSCODE_GOOD) return res; res = UA_Server_addPublishedDataSet(server, &config, pdsIdent).addResult; if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_createPublishedDataSet] " "Adding PublishedDataSet failed."); return res; } /* DataSetField configuration for this publishedDataSet: */ res = createDataSetFields(server, pdsIdent, pdsParams); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_createPublishedDataSet] " "Creating DataSetFieldConfig failed."); } return res; } /* Adds DataSetField Variables bound to a certain PublishedDataSet. This method does NOT * check, whether the PublishedDataSet actually contains Variables instead of Events! * * @param server UA_Server object that shall be configured * @param pdsIdent NodeId of the publishedDataSet, the DataSetField belongs to * @param publishedDataSetParameters publishedDataSet configuration */ static UA_StatusCode addDataSetFieldVariables(UA_Server *server, const UA_NodeId *pdsIdent, const UA_PublishedDataSetDataType *pdsParams) { UA_PublishedDataItemsDataType *pdItems = (UA_PublishedDataItemsDataType *) pdsParams->dataSetSource.content.decoded.data; if(pdItems->publishedDataSize != pdsParams->dataSetMetaData.fieldsSize) return UA_STATUSCODE_BADINTERNALERROR; for(size_t i = 0; i < pdItems->publishedDataSize; i++) { UA_DataSetFieldConfig fc; memset(&fc, 0, sizeof(UA_DataSetFieldConfig)); fc.dataSetFieldType = UA_PUBSUB_DATASETFIELD_VARIABLE; fc.field.variable.configurationVersion = pdsParams->dataSetMetaData.configurationVersion; fc.field.variable.fieldNameAlias = pdsParams->dataSetMetaData.fields[i].name; fc.field.variable.promotedField = pdsParams->dataSetMetaData. fields[i].fieldFlags & 0x0001; fc.field.variable.publishParameters = pdItems->publishedData[i]; UA_NodeId fieldIdent; UA_StatusCode res = UA_Server_addDataSetField(server, *pdsIdent, &fc, &fieldIdent).result; if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_addDataSetFieldVariables] " "Adding DataSetField Variable failed."); return res; } } return UA_STATUSCODE_GOOD; } /* Checks if PublishedDataSet contains event or variable fields and calls the * corresponding method to add these fields to the server. * * @param server UA_Server object that shall be configured * @param pdsIdent NodeId of the publishedDataSet, the DataSetFields belongs to * @param pdsParams publishedDataSet configuration */ static UA_StatusCode createDataSetFields(UA_Server *server, const UA_NodeId *pdsIdent, const UA_PublishedDataSetDataType *pdsParams) { if(pdsParams->dataSetSource.encoding != UA_EXTENSIONOBJECT_DECODED) return UA_STATUSCODE_BADINTERNALERROR; if(pdsParams->dataSetSource.content.decoded.type == &UA_TYPES[UA_TYPES_PUBLISHEDDATAITEMSDATATYPE]) return addDataSetFieldVariables(server, pdsIdent, pdsParams); /* TODO: Implement Routine for adding Event DataSetFields */ if(pdsParams->dataSetSource.content.decoded.type == &UA_TYPES[UA_TYPES_PUBLISHEDEVENTSDATATYPE]) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_createDataSetFields] " "Published events not supported."); return UA_STATUSCODE_BADNOTIMPLEMENTED; } UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_createDataSetFields] " "Invalid DataSetSourceDataType."); return UA_STATUSCODE_BADINTERNALERROR; } UA_StatusCode UA_PubSubManager_loadPubSubConfigFromByteString(UA_Server *server, const UA_ByteString buffer) { size_t offset = 0; UA_ExtensionObject decodedFile; UA_StatusCode res = UA_ExtensionObject_decodeBinary(&buffer, &offset, &decodedFile); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_decodeBinFile] decoding UA_Binary failed"); goto cleanup; } UA_PubSubConfigurationDataType *pubSubConfig = NULL; res = extractPubSubConfigFromExtensionObject(&decodedFile, &pubSubConfig); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_loadPubSubConfigFromByteString] " "Extracting PubSub Configuration failed"); goto cleanup; } res = updatePubSubConfig(server, pubSubConfig); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_loadPubSubConfigFromByteString] " "Loading PubSub configuration into server failed"); goto cleanup; } cleanup: UA_ExtensionObject_clear(&decodedFile); return res; } /* Encodes a PubSubConfigurationDataType object as ByteString using the UA Binary Data * Encoding */ static UA_StatusCode encodePubSubConfiguration(UA_PubSubConfigurationDataType *configurationParameters, UA_ByteString *buffer) { UA_UABinaryFileDataType binFile; memset(&binFile, 0, sizeof(UA_UABinaryFileDataType)); /*Perhaps, additional initializations of binFile are necessary here.*/ UA_Variant_setScalar(&binFile.body, configurationParameters, &UA_TYPES[UA_TYPES_PUBSUBCONFIGURATIONDATATYPE]); UA_ExtensionObject container; memset(&container, 0, sizeof(UA_ExtensionObject)); container.encoding = UA_EXTENSIONOBJECT_DECODED; container.content.decoded.type = &UA_TYPES[UA_TYPES_UABINARYFILEDATATYPE]; container.content.decoded.data = &binFile; size_t fileSize = UA_ExtensionObject_calcSizeBinary(&container); buffer->data = (UA_Byte*)UA_calloc(fileSize, sizeof(UA_Byte)); if(buffer->data == NULL) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_encodePubSubConfiguration] Allocating buffer failed"); return UA_STATUSCODE_BADOUTOFMEMORY; } buffer->length = fileSize; UA_Byte *bufferPos = buffer->data; UA_StatusCode res = UA_ExtensionObject_encodeBinary(&container, &bufferPos, bufferPos + fileSize); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_encodePubSubConfiguration] Encoding failed"); } return res; } static UA_StatusCode generatePublishedDataSetDataType(const UA_PublishedDataSet *src, UA_PublishedDataSetDataType *dst) { if(src->config.publishedDataSetType != UA_PUBSUB_DATASET_PUBLISHEDITEMS) return UA_STATUSCODE_BADNOTIMPLEMENTED; memset(dst, 0, sizeof(UA_PublishedDataSetDataType)); UA_PublishedDataItemsDataType *tmp = UA_PublishedDataItemsDataType_new(); UA_String_copy(&src->config.name, &dst->name); dst->dataSetMetaData.fieldsSize = src->fieldSize; size_t index = 0; tmp->publishedDataSize = src->fieldSize; tmp->publishedData = (UA_PublishedVariableDataType*) UA_Array_new(tmp->publishedDataSize, &UA_TYPES[UA_TYPES_PUBLISHEDVARIABLEDATATYPE]); if(tmp->publishedData == NULL) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Allocation memory failed"); return UA_STATUSCODE_BADOUTOFMEMORY; } dst->dataSetMetaData.fields = (UA_FieldMetaData*) UA_Array_new(dst->dataSetMetaData.fieldsSize, &UA_TYPES[UA_TYPES_FIELDMETADATA]); if(dst->dataSetMetaData.fields == NULL) { UA_free(tmp->publishedData); UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Allocation memory failed"); return UA_STATUSCODE_BADOUTOFMEMORY; } UA_DataSetField *dsf, *dsf_tmp = NULL; TAILQ_FOREACH_SAFE(dsf ,&src->fields, listEntry, dsf_tmp) { UA_String_copy(&dsf->config.field.variable.fieldNameAlias, &dst->dataSetMetaData.fields[index].name); UA_PublishedVariableDataType_copy(&dsf->config.field.variable.publishParameters, &tmp->publishedData[index]); UA_ConfigurationVersionDataType_copy(&dsf->config.field.variable.configurationVersion, &dst->dataSetMetaData.configurationVersion); dst->dataSetMetaData.fields[index].fieldFlags = dsf->config.field.variable.promotedField; index++; } UA_ExtensionObject_setValue(&dst->dataSetSource, tmp, &UA_TYPES[UA_TYPES_PUBLISHEDDATAITEMSDATATYPE]); return UA_STATUSCODE_GOOD; } static UA_StatusCode generateDataSetWriterDataType(const UA_DataSetWriter *src, UA_DataSetWriterDataType *dst) { memset(dst, 0, sizeof(UA_DataSetWriterDataType)); UA_String_copy(&src->config.name, &dst->name); dst->dataSetWriterId = src->config.dataSetWriterId; dst->keyFrameCount = src->config.keyFrameCount; dst->dataSetFieldContentMask = src->config.dataSetFieldContentMask; UA_ExtensionObject_copy(&src->config.messageSettings, &dst->messageSettings); UA_String_copy(&src->config.dataSetName, &dst->dataSetName); dst->dataSetWriterPropertiesSize = src->config.dataSetWriterPropertiesSize; for(size_t i = 0; i < src->config.dataSetWriterPropertiesSize; i++) { UA_KeyValuePair_copy(&src->config.dataSetWriterProperties[i], &dst->dataSetWriterProperties[i]); } return UA_STATUSCODE_GOOD; } static UA_StatusCode generateWriterGroupDataType(const UA_WriterGroup *src, UA_WriterGroupDataType *dst) { memset(dst, 0, sizeof(UA_WriterGroupDataType)); UA_String_copy(&src->config.name, &dst->name); dst->enabled = src->config.enabled; dst->writerGroupId = src->config.writerGroupId; dst->publishingInterval = src->config.publishingInterval; dst->keepAliveTime = src->config.keepAliveTime; dst->priority = src->config.priority; dst->securityMode = src->config.securityMode; UA_ExtensionObject_copy(&src->config.transportSettings, &dst->transportSettings); UA_ExtensionObject_copy(&src->config.messageSettings, &dst->messageSettings); dst->groupPropertiesSize = src->config.groupPropertiesSize; dst->groupProperties = (UA_KeyValuePair*) UA_Array_new(dst->groupPropertiesSize, &UA_TYPES[UA_TYPES_KEYVALUEPAIR]); for(size_t index = 0; index < dst->groupPropertiesSize; index++) { UA_KeyValuePair_copy(&src->config.groupProperties[index], &dst->groupProperties[index]); } dst->dataSetWriters = (UA_DataSetWriterDataType*) UA_calloc(src->writersCount, sizeof(UA_DataSetWriterDataType)); if(!dst->dataSetWriters) return UA_STATUSCODE_BADOUTOFMEMORY; dst->dataSetWritersSize = src->writersCount; UA_DataSetWriter *dsw; size_t dsWriterIndex = 0; LIST_FOREACH(dsw, &src->writers, listEntry) { UA_StatusCode res = generateDataSetWriterDataType(dsw, &dst->dataSetWriters[dsWriterIndex]); if(res != UA_STATUSCODE_GOOD) return res; dsWriterIndex++; } return UA_STATUSCODE_GOOD; } static UA_StatusCode generateDataSetReaderDataType(const UA_DataSetReader *src, UA_DataSetReaderDataType *dst) { UA_StatusCode res = UA_STATUSCODE_GOOD; memset(dst, 0, sizeof(UA_DataSetReaderDataType)); dst->writerGroupId = src->config.writerGroupId; dst->dataSetWriterId = src->config.dataSetWriterId; dst->dataSetFieldContentMask = src->config.dataSetFieldContentMask; dst->messageReceiveTimeout = src->config.messageReceiveTimeout; res |= UA_String_copy(&src->config.name, &dst->name); res |= UA_Variant_copy(&src->config.publisherId, &dst->publisherId); res |= UA_DataSetMetaDataType_copy(&src->config.dataSetMetaData, &dst->dataSetMetaData); res |= UA_ExtensionObject_copy(&src->config.messageSettings, &dst->messageSettings); UA_TargetVariablesDataType *tmpTarget = UA_TargetVariablesDataType_new(); if(!tmpTarget) return UA_STATUSCODE_BADOUTOFMEMORY; UA_ExtensionObject_setValue(&dst->subscribedDataSet, tmpTarget, &UA_TYPES[UA_TYPES_TARGETVARIABLESDATATYPE]); const UA_TargetVariables *targets = &src->config.subscribedDataSet.subscribedDataSetTarget; tmpTarget->targetVariables = (UA_FieldTargetDataType *) UA_calloc(targets->targetVariablesSize, sizeof(UA_FieldTargetDataType)); if(!tmpTarget->targetVariables) return UA_STATUSCODE_BADOUTOFMEMORY; tmpTarget->targetVariablesSize = targets->targetVariablesSize; for(size_t i = 0; i < tmpTarget->targetVariablesSize; i++) { res |= UA_FieldTargetDataType_copy(&targets->targetVariables[i].targetVariable, &tmpTarget->targetVariables[i]); } return res; } static UA_StatusCode generateReaderGroupDataType(const UA_ReaderGroup *src, UA_ReaderGroupDataType *dst) { memset(dst, 0, sizeof(UA_ReaderGroupDataType)); UA_String_copy(&src->config.name, &dst->name); dst->dataSetReaders = (UA_DataSetReaderDataType*) UA_calloc(src->readersCount, sizeof(UA_DataSetReaderDataType)); if(dst->dataSetReaders == NULL) return UA_STATUSCODE_BADOUTOFMEMORY; dst->dataSetReadersSize = src->readersCount; size_t i = 0; UA_DataSetReader *dsr, *dsr_tmp = NULL; LIST_FOREACH_SAFE(dsr, &src->readers, listEntry, dsr_tmp) { UA_StatusCode res = generateDataSetReaderDataType(dsr, &dst->dataSetReaders[i]); if(res != UA_STATUSCODE_GOOD) return res; i++; } return UA_STATUSCODE_GOOD; } /* Generates a PubSubConnectionDataType object from a PubSubConnection. */ static UA_StatusCode generatePubSubConnectionDataType(const UA_PubSubConnection *src, UA_PubSubConnectionDataType *dst) { memset(dst, 0, sizeof(UA_PubSubConnectionDataType)); UA_String_copy(&src->config->name, &dst->name); UA_String_copy(&src->config->transportProfileUri, &dst->transportProfileUri); dst->enabled = src->config->enabled; dst->connectionPropertiesSize = src->config->connectionPropertiesSize; for(size_t i = 0; i < src->config->connectionPropertiesSize; i++) { UA_KeyValuePair_copy(&src->config->connectionProperties[i], &dst->connectionProperties[i]); } if(src->config->publisherIdType == UA_PUBSUB_PUBLISHERID_NUMERIC) { UA_Variant_setScalarCopy(&dst->publisherId, &src->config->publisherId.numeric, &UA_TYPES[UA_TYPES_UINT32]); } else if(src->config->publisherIdType == UA_PUBSUB_PUBLISHERID_STRING) { UA_Variant_setScalarCopy(&dst->publisherId, &src->config->publisherId.string, &UA_TYPES[UA_TYPES_STRING]); } /* Possibly, array size and dimensions of src->config->address and * src->config->connectionTransportSettings should be checked beforehand. */ dst->address.encoding = UA_EXTENSIONOBJECT_DECODED; dst->address.content.decoded.type = src->config->address.type; UA_StatusCode res = UA_Array_copy(src->config->address.data, 1, &dst->address.content.decoded.data, src->config->address.type); if(res != UA_STATUSCODE_GOOD) return res; if(src->config->connectionTransportSettings.data) { dst->transportSettings.encoding = UA_EXTENSIONOBJECT_DECODED; dst->transportSettings.content.decoded.type = src->config->connectionTransportSettings.type; res = UA_Array_copy(src->config->connectionTransportSettings.data, 1, &dst->transportSettings.content.decoded.data, src->config->connectionTransportSettings.type); if(res != UA_STATUSCODE_GOOD) return res; } dst->writerGroups = (UA_WriterGroupDataType*) UA_calloc(src->writerGroupsSize, sizeof(UA_WriterGroupDataType)); if(dst->writerGroups == NULL) { return UA_STATUSCODE_BADOUTOFMEMORY; } dst->writerGroupsSize = src->writerGroupsSize; UA_WriterGroup *wg, *wg_tmp = NULL; size_t wgIndex = 0; LIST_FOREACH_SAFE(wg ,&src->writerGroups, listEntry, wg_tmp) { res = generateWriterGroupDataType(wg, &dst->writerGroups[wgIndex]); if(res != UA_STATUSCODE_GOOD) return res; wgIndex++; } dst->readerGroups = (UA_ReaderGroupDataType*) UA_calloc(src->readerGroupsSize, sizeof(UA_ReaderGroupDataType)); if(dst->readerGroups == NULL) { return UA_STATUSCODE_BADOUTOFMEMORY; } dst->readerGroupsSize = src->readerGroupsSize; UA_ReaderGroup *rg = NULL; size_t rgIndex = 0; LIST_FOREACH(rg, &src->readerGroups, listEntry) { res = generateReaderGroupDataType(rg, &dst->readerGroups[rgIndex]); if(res != UA_STATUSCODE_GOOD) return res; rgIndex++; } return UA_STATUSCODE_GOOD; } UA_StatusCode generatePubSubConfigurationDataType(const UA_Server* server, UA_PubSubConfigurationDataType *configDT) { UA_StatusCode res = UA_STATUSCODE_GOOD; const UA_PubSubManager *manager = &server->pubSubManager; memset(configDT, 0, sizeof(UA_PubSubConfigurationDataType)); configDT->publishedDataSets = (UA_PublishedDataSetDataType*) UA_calloc(manager->publishedDataSetsSize, sizeof(UA_PublishedDataSetDataType)); if(configDT->publishedDataSets == NULL) return UA_STATUSCODE_BADOUTOFMEMORY; configDT->publishedDataSetsSize = manager->publishedDataSetsSize; UA_PublishedDataSet *pds; UA_UInt32 pdsIndex = 0; TAILQ_FOREACH(pds, &manager->publishedDataSets, listEntry) { UA_PublishedDataSetDataType *dst = &configDT->publishedDataSets[pdsIndex]; res = generatePublishedDataSetDataType(pds, dst); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_generatePubSubConfigurationDataType] " "retrieving PublishedDataSet configuration failed"); return res; } pdsIndex++; } configDT->connections = (UA_PubSubConnectionDataType*) UA_calloc(manager->connectionsSize, sizeof(UA_PubSubConnectionDataType)); if(configDT->connections == NULL) return UA_STATUSCODE_BADOUTOFMEMORY; configDT->connectionsSize = manager->connectionsSize; UA_UInt32 connectionIndex = 0; UA_PubSubConnection *connection; TAILQ_FOREACH(connection, &manager->connections, listEntry) { UA_PubSubConnectionDataType *cdt = &configDT->connections[connectionIndex]; res = generatePubSubConnectionDataType(connection, cdt); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "[UA_PubSubManager_generatePubSubConfigurationDataType] " "retrieving PubSubConnection configuration failed"); return res; } connectionIndex++; } return UA_STATUSCODE_GOOD; } UA_StatusCode UA_PubSubManager_getEncodedPubSubConfiguration(UA_Server *server, UA_ByteString *buffer) { UA_PubSubConfigurationDataType config; memset(&config, 0, sizeof(UA_PubSubConfigurationDataType)); UA_StatusCode res = generatePubSubConfigurationDataType(server, &config); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "retrieving PubSub configuration from server failed"); goto cleanup; } res = encodePubSubConfiguration(&config, buffer); if(res != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "encoding PubSub configuration failed"); goto cleanup; } UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Saving PubSub config was successful"); cleanup: UA_PubSubConfigurationDataType_clear(&config); return res; } #endif /* UA_ENABLE_PUBSUB_FILE_CONFIG */ /**** amalgamated original file "/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], 17604LU), UA_NODEID_NUMERIC(ns[0], 32LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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], 17604LU) ); } /* HasHistoricalConfiguration - ns=0;i=56 */ 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("", "HistoricalConfigurationOf"); attr.displayName = UA_LOCALIZEDTEXT("", "HasHistoricalConfiguration"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_REFERENCETYPE, UA_NODEID_NUMERIC(ns[0], 56LU), UA_NODEID_NUMERIC(ns[0], 44LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_1_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 56LU) ); } /* HasEffect - ns=0;i=54 */ static UA_StatusCode function_namespace0_generated_2_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], 54LU), UA_NODEID_NUMERIC(ns[0], 32LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_2_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 54LU) ); } /* HasCause - ns=0;i=53 */ static UA_StatusCode function_namespace0_generated_3_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], 53LU), UA_NODEID_NUMERIC(ns[0], 32LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_3_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 53LU) ); } /* ToState - ns=0;i=52 */ static UA_StatusCode function_namespace0_generated_4_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], 52LU), UA_NODEID_NUMERIC(ns[0], 32LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_4_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 52LU) ); } /* FromState - ns=0;i=51 */ static UA_StatusCode function_namespace0_generated_5_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], 51LU), UA_NODEID_NUMERIC(ns[0], 32LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_5_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 51LU) ); } /* DiagnosticInfo - ns=0;i=25 */ 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("", "DiagnosticInfo"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 25LU), UA_NODEID_NUMERIC(ns[0], 24LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_6_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 25LU) ); } /* DataValue - ns=0;i=23 */ 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.displayName = UA_LOCALIZEDTEXT("", "DataValue"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 23LU), UA_NODEID_NUMERIC(ns[0], 24LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_7_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 23LU) ); } /* Structure - ns=0;i=22 */ 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.isAbstract = true; attr.displayName = UA_LOCALIZEDTEXT("", "Structure"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 22LU), UA_NODEID_NUMERIC(ns[0], 24LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_8_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 22LU) ); } /* TimeZoneDataType - ns=0;i=8912 */ 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("", "TimeZoneDataType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 8912LU), UA_NODEID_NUMERIC(ns[0], 22LU), UA_NODEID_NUMERIC(ns[0], 45LU), UA_QUALIFIEDNAME(ns[0], "TimeZoneDataType"), 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], 8912LU) ); } /* EUInformation - ns=0;i=887 */ 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("", "EUInformation"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 887LU), UA_NODEID_NUMERIC(ns[0], 22LU), UA_NODEID_NUMERIC(ns[0], 45LU), UA_QUALIFIEDNAME(ns[0], "EUInformation"), 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], 887LU) ); } /* Range - ns=0;i=884 */ 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("", "Range"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 884LU), UA_NODEID_NUMERIC(ns[0], 22LU), UA_NODEID_NUMERIC(ns[0], 45LU), UA_QUALIFIEDNAME(ns[0], "Range"), 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], 884LU) ); } /* ServerStatusDataType - ns=0;i=862 */ 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("", "ServerStatusDataType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 862LU), UA_NODEID_NUMERIC(ns[0], 22LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_12_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 862LU) ); } /* EnumValueType - ns=0;i=7594 */ 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("", "EnumValueType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 7594LU), UA_NODEID_NUMERIC(ns[0], 22LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_13_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 7594LU) ); } /* SignedSoftwareCertificate - ns=0;i=344 */ 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("", "SignedSoftwareCertificate"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 344LU), UA_NODEID_NUMERIC(ns[0], 22LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_14_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 344LU) ); } /* BuildInfo - ns=0;i=338 */ 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("", "BuildInfo"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 338LU), UA_NODEID_NUMERIC(ns[0], 22LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_15_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 338LU) ); } /* Argument - ns=0;i=296 */ 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.displayName = UA_LOCALIZEDTEXT("", "Argument"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 296LU), UA_NODEID_NUMERIC(ns[0], 22LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_16_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 296LU) ); } /* Union - ns=0;i=12756 */ 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.isAbstract = true; attr.displayName = UA_LOCALIZEDTEXT("", "Union"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 12756LU), UA_NODEID_NUMERIC(ns[0], 22LU), UA_NODEID_NUMERIC(ns[0], 45LU), UA_QUALIFIEDNAME(ns[0], "Union"), 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], 12756LU) ); } /* LocalizedText - ns=0;i=21 */ 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.displayName = UA_LOCALIZEDTEXT("", "LocalizedText"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 21LU), UA_NODEID_NUMERIC(ns[0], 24LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_18_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 21LU) ); } /* QualifiedName - ns=0;i=20 */ 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("", "QualifiedName"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 20LU), UA_NODEID_NUMERIC(ns[0], 24LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_19_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 20LU) ); } /* StatusCode - ns=0;i=19 */ 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("", "StatusCode"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 19LU), UA_NODEID_NUMERIC(ns[0], 24LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_20_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 19LU) ); } /* ExpandedNodeId - ns=0;i=18 */ 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("", "ExpandedNodeId"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 18LU), UA_NODEID_NUMERIC(ns[0], 24LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_21_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 18LU) ); } /* NodeId - ns=0;i=17 */ 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("", "NodeId"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 17LU), UA_NODEID_NUMERIC(ns[0], 24LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_22_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 17LU) ); } /* XmlElement - ns=0;i=16 */ 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.displayName = UA_LOCALIZEDTEXT("", "XmlElement"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 16LU), UA_NODEID_NUMERIC(ns[0], 24LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_23_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 16LU) ); } /* ByteString - ns=0;i=15 */ 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("", "ByteString"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 15LU), UA_NODEID_NUMERIC(ns[0], 24LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_24_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 15LU) ); } /* Image - ns=0;i=30 */ 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.isAbstract = true; attr.displayName = UA_LOCALIZEDTEXT("", "Image"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 30LU), UA_NODEID_NUMERIC(ns[0], 15LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_25_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 30LU) ); } /* Guid - ns=0;i=14 */ 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("", "Guid"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 14LU), UA_NODEID_NUMERIC(ns[0], 24LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_26_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 14LU) ); } /* DateTime - ns=0;i=13 */ 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("", "DateTime"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 13LU), UA_NODEID_NUMERIC(ns[0], 24LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_27_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 13LU) ); } /* UtcTime - ns=0;i=294 */ 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("", "UtcTime"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 294LU), UA_NODEID_NUMERIC(ns[0], 13LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_28_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 294LU) ); } /* String - ns=0;i=12 */ 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("", "String"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 12LU), UA_NODEID_NUMERIC(ns[0], 24LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_29_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 12LU) ); } /* LocaleId - ns=0;i=295 */ 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("", "LocaleId"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 295LU), UA_NODEID_NUMERIC(ns[0], 12LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_30_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 295LU) ); } /* Boolean - ns=0;i=1 */ 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("", "Boolean"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 1LU), UA_NODEID_NUMERIC(ns[0], 24LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_31_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 1LU) ); } /* 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], 29LU), UA_NODEID_NUMERIC(ns[0], 24LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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], 29LU) ); } /* ServerState - ns=0;i=852 */ 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("", "ServerState"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 852LU), UA_NODEID_NUMERIC(ns[0], 29LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_33_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 852LU) ); } /* RedundancySupport - ns=0;i=851 */ static UA_StatusCode function_namespace0_generated_34_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], 851LU), UA_NODEID_NUMERIC(ns[0], 29LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_34_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 851LU) ); } /* EnumStrings - ns=0;i=7611 */ static UA_StatusCode function_namespace0_generated_35_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], 21LU); 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], 7611LU), UA_NODEID_NUMERIC(ns[0], 851LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "EnumStrings"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],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], 7611LU) ); } /* AxisScaleEnumeration - ns=0;i=12077 */ static UA_StatusCode function_namespace0_generated_36_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_DataTypeAttributes attr = UA_DataTypeAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "AxisScaleEnumeration"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 12077LU), UA_NODEID_NUMERIC(ns[0], 29LU), UA_NODEID_NUMERIC(ns[0], 45LU), UA_QUALIFIEDNAME(ns[0], "AxisScaleEnumeration"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],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], 12077LU) ); } /* EnumStrings - ns=0;i=12078 */ static UA_StatusCode function_namespace0_generated_37_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], 21LU); UA_LocalizedText variablenode_ns_0_i_12078_variant_DataContents[3]; variablenode_ns_0_i_12078_variant_DataContents[0] = UA_LOCALIZEDTEXT("", "Linear"); variablenode_ns_0_i_12078_variant_DataContents[1] = UA_LOCALIZEDTEXT("", "Log"); variablenode_ns_0_i_12078_variant_DataContents[2] = UA_LOCALIZEDTEXT("", "Ln"); UA_Variant_setArray(&attr.value, &variablenode_ns_0_i_12078_variant_DataContents, (UA_Int32) 3, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]); attr.displayName = UA_LOCALIZEDTEXT("", "EnumStrings"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 12078LU), UA_NODEID_NUMERIC(ns[0], 12077LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "EnumStrings"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],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], 12078LU) ); } /* NamingRuleType - ns=0;i=120 */ static UA_StatusCode function_namespace0_generated_38_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], 120LU), UA_NODEID_NUMERIC(ns[0], 29LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_38_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 120LU) ); } /* EnumValues - ns=0;i=12169 */ static UA_StatusCode function_namespace0_generated_39_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], 7594LU); 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], 12169LU), UA_NODEID_NUMERIC(ns[0], 120LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "EnumValues"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],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], 12169LU) ); } /* Number - ns=0;i=26 */ static UA_StatusCode function_namespace0_generated_40_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], 26LU), UA_NODEID_NUMERIC(ns[0], 24LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_40_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 26LU) ); } /* Decimal - ns=0;i=50 */ static UA_StatusCode function_namespace0_generated_41_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], 50LU), UA_NODEID_NUMERIC(ns[0], 26LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_41_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 50LU) ); } /* UInteger - ns=0;i=28 */ static UA_StatusCode function_namespace0_generated_42_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], 28LU), UA_NODEID_NUMERIC(ns[0], 26LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_42_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 28LU) ); } /* UInt64 - ns=0;i=9 */ 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("", "UInt64"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 9LU), UA_NODEID_NUMERIC(ns[0], 28LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_43_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 9LU) ); } /* UInt32 - ns=0;i=7 */ 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("", "UInt32"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 7LU), UA_NODEID_NUMERIC(ns[0], 28LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_44_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 7LU) ); } /* UInt16 - ns=0;i=5 */ 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("", "UInt16"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 5LU), UA_NODEID_NUMERIC(ns[0], 28LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_45_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 5LU) ); } /* Byte - ns=0;i=3 */ 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("", "Byte"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 3LU), UA_NODEID_NUMERIC(ns[0], 28LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_46_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 3LU) ); } /* Integer - ns=0;i=27 */ 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.isAbstract = true; attr.displayName = UA_LOCALIZEDTEXT("", "Integer"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 27LU), UA_NODEID_NUMERIC(ns[0], 26LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_47_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 27LU) ); } /* Int64 - ns=0;i=8 */ 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("", "Int64"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 8LU), UA_NODEID_NUMERIC(ns[0], 27LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_48_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 8LU) ); } /* Int32 - ns=0;i=6 */ 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("", "Int32"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 6LU), UA_NODEID_NUMERIC(ns[0], 27LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_49_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 6LU) ); } /* Int16 - ns=0;i=4 */ 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("", "Int16"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NODEID_NUMERIC(ns[0], 4LU), UA_NODEID_NUMERIC(ns[0], 27LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_50_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 4LU) ); } /* SByte - ns=0;i=2 */ static UA_StatusCode function_namespace0_generated_51_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], 2LU), UA_NODEID_NUMERIC(ns[0], 27LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_51_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2LU) ); } /* Double - ns=0;i=11 */ static UA_StatusCode function_namespace0_generated_52_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], 11LU), UA_NODEID_NUMERIC(ns[0], 26LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_52_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11LU) ); } /* Duration - ns=0;i=290 */ static UA_StatusCode function_namespace0_generated_53_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], 290LU), UA_NODEID_NUMERIC(ns[0], 11LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_53_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 290LU) ); } /* Float - ns=0;i=10 */ static UA_StatusCode function_namespace0_generated_54_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], 10LU), UA_NODEID_NUMERIC(ns[0], 26LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_54_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 10LU) ); } /* DataItemType - ns=0;i=2365 */ static UA_StatusCode function_namespace0_generated_55_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableTypeAttributes attr = UA_VariableTypeAttributes_default; attr.valueRank = -2; /* DataType inherited */ attr.dataType = UA_NODEID_NUMERIC(ns[0], 24LU); attr.displayName = UA_LOCALIZEDTEXT("", "DataItemType"); #ifdef UA_ENABLE_NODESET_COMPILER_DESCRIPTIONS attr.description = UA_LOCALIZEDTEXT("", "A variable that contains live automation data."); #endif retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLETYPE, UA_NODEID_NUMERIC(ns[0], 2365LU), UA_NODEID_NUMERIC(ns[0], 63LU), UA_NODEID_NUMERIC(ns[0], 45LU), UA_QUALIFIEDNAME(ns[0], "DataItemType"), UA_NODEID_NUMERIC(ns[0], 0LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES],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], 2365LU) ); } /* DiscreteItemType - ns=0;i=2372 */ 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; attr.isAbstract = true; attr.valueRank = -2; /* DataType inherited */ attr.dataType = UA_NODEID_NUMERIC(ns[0], 24LU); attr.displayName = UA_LOCALIZEDTEXT("", "DiscreteItemType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLETYPE, UA_NODEID_NUMERIC(ns[0], 2372LU), UA_NODEID_NUMERIC(ns[0], 2365LU), UA_NODEID_NUMERIC(ns[0], 45LU), UA_QUALIFIEDNAME(ns[0], "DiscreteItemType"), UA_NODEID_NUMERIC(ns[0], 0LU), (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], 2372LU) ); } /* MultiStateDiscreteType - ns=0;i=2376 */ static UA_StatusCode function_namespace0_generated_57_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableTypeAttributes attr = UA_VariableTypeAttributes_default; attr.valueRank = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 28LU); attr.displayName = UA_LOCALIZEDTEXT("", "MultiStateDiscreteType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLETYPE, UA_NODEID_NUMERIC(ns[0], 2376LU), UA_NODEID_NUMERIC(ns[0], 2372LU), UA_NODEID_NUMERIC(ns[0], 45LU), UA_QUALIFIEDNAME(ns[0], "MultiStateDiscreteType"), UA_NODEID_NUMERIC(ns[0], 0LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES],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], 2376LU) ); } /* EnumStrings - ns=0;i=2377 */ 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; attr.valueRank = 1; attr.arrayDimensionsSize = 1; UA_UInt32 arrayDimensions[1]; arrayDimensions[0] = 0; attr.arrayDimensions = &arrayDimensions[0]; attr.dataType = UA_NODEID_NUMERIC(ns[0], 21LU); attr.displayName = UA_LOCALIZEDTEXT("", "EnumStrings"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2377LU), UA_NODEID_NUMERIC(ns[0], 2376LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "EnumStrings"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 2377LU) ); } /* TwoStateDiscreteType - ns=0;i=2373 */ 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; attr.valueRank = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 1LU); attr.displayName = UA_LOCALIZEDTEXT("", "TwoStateDiscreteType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLETYPE, UA_NODEID_NUMERIC(ns[0], 2373LU), UA_NODEID_NUMERIC(ns[0], 2372LU), UA_NODEID_NUMERIC(ns[0], 45LU), UA_QUALIFIEDNAME(ns[0], "TwoStateDiscreteType"), UA_NODEID_NUMERIC(ns[0], 0LU), (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], 2373LU) ); } /* TrueState - ns=0;i=2375 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 21LU); attr.displayName = UA_LOCALIZEDTEXT("", "TrueState"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2375LU), UA_NODEID_NUMERIC(ns[0], 2373LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "TrueState"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 2375LU) ); } /* FalseState - ns=0;i=2374 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 21LU); attr.displayName = UA_LOCALIZEDTEXT("", "FalseState"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2374LU), UA_NODEID_NUMERIC(ns[0], 2373LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "FalseState"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 2374LU) ); } /* MultiStateValueDiscreteType - ns=0;i=11238 */ static UA_StatusCode function_namespace0_generated_62_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableTypeAttributes attr = UA_VariableTypeAttributes_default; attr.valueRank = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 26LU); attr.displayName = UA_LOCALIZEDTEXT("", "MultiStateValueDiscreteType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLETYPE, UA_NODEID_NUMERIC(ns[0], 11238LU), UA_NODEID_NUMERIC(ns[0], 2372LU), UA_NODEID_NUMERIC(ns[0], 45LU), UA_QUALIFIEDNAME(ns[0], "MultiStateValueDiscreteType"), UA_NODEID_NUMERIC(ns[0], 0LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES],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], 11238LU) ); } /* ValueAsText - ns=0;i=11461 */ static UA_StatusCode function_namespace0_generated_63_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 21LU); attr.displayName = UA_LOCALIZEDTEXT("", "ValueAsText"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11461LU), UA_NODEID_NUMERIC(ns[0], 11238LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "ValueAsText"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],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], 11461LU) ); } /* EnumValues - ns=0;i=11241 */ 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; attr.valueRank = 1; attr.arrayDimensionsSize = 1; UA_UInt32 arrayDimensions[1]; arrayDimensions[0] = 0; attr.arrayDimensions = &arrayDimensions[0]; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7594LU); attr.displayName = UA_LOCALIZEDTEXT("", "EnumValues"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11241LU), UA_NODEID_NUMERIC(ns[0], 11238LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "EnumValues"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 11241LU) ); } /* AnalogItemType - ns=0;i=2368 */ static UA_StatusCode function_namespace0_generated_65_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableTypeAttributes attr = UA_VariableTypeAttributes_default; attr.valueRank = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 26LU); attr.displayName = UA_LOCALIZEDTEXT("", "AnalogItemType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLETYPE, UA_NODEID_NUMERIC(ns[0], 2368LU), UA_NODEID_NUMERIC(ns[0], 2365LU), UA_NODEID_NUMERIC(ns[0], 45LU), UA_QUALIFIEDNAME(ns[0], "AnalogItemType"), UA_NODEID_NUMERIC(ns[0], 0LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES],NULL, NULL); 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], 2368LU) ); } /* EngineeringUnits - ns=0;i=2371 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 887LU); attr.displayName = UA_LOCALIZEDTEXT("", "EngineeringUnits"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2371LU), UA_NODEID_NUMERIC(ns[0], 2368LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "EngineeringUnits"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 2371LU) ); } /* InstrumentRange - ns=0;i=2370 */ static UA_StatusCode function_namespace0_generated_67_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 884LU); attr.displayName = UA_LOCALIZEDTEXT("", "InstrumentRange"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2370LU), UA_NODEID_NUMERIC(ns[0], 2368LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "InstrumentRange"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],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], 2370LU) ); } /* EURange - ns=0;i=2369 */ static UA_StatusCode function_namespace0_generated_68_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 = 3; attr.accessLevel = 3; /* Value rank inherited */ attr.valueRank = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 884LU); attr.displayName = UA_LOCALIZEDTEXT("", "EURange"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2369LU), UA_NODEID_NUMERIC(ns[0], 2368LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "EURange"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],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], 2369LU) ); } /* ValuePrecision - ns=0;i=2367 */ static UA_StatusCode function_namespace0_generated_69_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 11LU); attr.displayName = UA_LOCALIZEDTEXT("", "ValuePrecision"); #ifdef UA_ENABLE_NODESET_COMPILER_DESCRIPTIONS attr.description = UA_LOCALIZEDTEXT("", "The maximum precision that the server can maintain for the item based on restrictions in the target environment."); #endif retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2367LU), UA_NODEID_NUMERIC(ns[0], 2365LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "ValuePrecision"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 2367LU) ); } /* Definition - ns=0;i=2366 */ static UA_StatusCode function_namespace0_generated_70_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12LU); attr.displayName = UA_LOCALIZEDTEXT("", "Definition"); #ifdef UA_ENABLE_NODESET_COMPILER_DESCRIPTIONS attr.description = UA_LOCALIZEDTEXT("", "A vendor-specific, human readable string that specifies how the value of this DataItem is calculated."); #endif retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2366LU), UA_NODEID_NUMERIC(ns[0], 2365LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "Definition"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 2366LU) ); } /* HistoryServerCapabilitiesType - ns=0;i=2330 */ 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("", "HistoryServerCapabilitiesType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECTTYPE, UA_NODEID_NUMERIC(ns[0], 2330LU), UA_NODEID_NUMERIC(ns[0], 58LU), UA_NODEID_NUMERIC(ns[0], 45LU), UA_QUALIFIEDNAME(ns[0], "HistoryServerCapabilitiesType"), 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], 2330LU) ); } /* EventQueueOverflowEventType - ns=0;i=3035 */ static UA_StatusCode function_namespace0_generated_72_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("", "EventQueueOverflowEventType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECTTYPE, UA_NODEID_NUMERIC(ns[0], 3035LU), UA_NODEID_NUMERIC(ns[0], 2041LU), UA_NODEID_NUMERIC(ns[0], 45LU), UA_QUALIFIEDNAME(ns[0], "EventQueueOverflowEventType"), UA_NODEID_NULL, (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES],NULL, NULL); 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], 3035LU) ); } /* Severity - ns=0;i=2051 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 5LU); attr.displayName = UA_LOCALIZEDTEXT("", "Severity"); #ifdef UA_ENABLE_NODESET_COMPILER_DESCRIPTIONS attr.description = UA_LOCALIZEDTEXT("", "Indicates how urgent an event is."); #endif retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2051LU), UA_NODEID_NUMERIC(ns[0], 2041LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "Severity"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 2051LU) ); } /* Message - ns=0;i=2050 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 21LU); attr.displayName = UA_LOCALIZEDTEXT("", "Message"); #ifdef UA_ENABLE_NODESET_COMPILER_DESCRIPTIONS attr.description = UA_LOCALIZEDTEXT("", "A localized description of the event."); #endif retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2050LU), UA_NODEID_NUMERIC(ns[0], 2041LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "Message"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 2050LU) ); } /* LocalTime - ns=0;i=3190 */ static UA_StatusCode function_namespace0_generated_75_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 8912LU); attr.displayName = UA_LOCALIZEDTEXT("", "LocalTime"); #ifdef UA_ENABLE_NODESET_COMPILER_DESCRIPTIONS attr.description = UA_LOCALIZEDTEXT("", "Information about the local time where the event originated."); #endif retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 3190LU), UA_NODEID_NUMERIC(ns[0], 2041LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "LocalTime"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 3190LU) ); } /* ReceiveTime - ns=0;i=2047 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 294LU); attr.displayName = UA_LOCALIZEDTEXT("", "ReceiveTime"); #ifdef UA_ENABLE_NODESET_COMPILER_DESCRIPTIONS attr.description = UA_LOCALIZEDTEXT("", "When the server received the event from the underlying system."); #endif retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2047LU), UA_NODEID_NUMERIC(ns[0], 2041LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "ReceiveTime"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 2047LU) ); } /* Time - ns=0;i=2046 */ static UA_StatusCode function_namespace0_generated_77_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 294LU); attr.displayName = UA_LOCALIZEDTEXT("", "Time"); #ifdef UA_ENABLE_NODESET_COMPILER_DESCRIPTIONS attr.description = UA_LOCALIZEDTEXT("", "When the event occurred."); #endif retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2046LU), UA_NODEID_NUMERIC(ns[0], 2041LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "Time"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],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], 2046LU) ); } /* SourceName - ns=0;i=2045 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12LU); attr.displayName = UA_LOCALIZEDTEXT("", "SourceName"); #ifdef UA_ENABLE_NODESET_COMPILER_DESCRIPTIONS attr.description = UA_LOCALIZEDTEXT("", "A description of the source of the event."); #endif retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2045LU), UA_NODEID_NUMERIC(ns[0], 2041LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "SourceName"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 2045LU) ); } /* SourceNode - ns=0;i=2044 */ static UA_StatusCode function_namespace0_generated_79_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 17LU); attr.displayName = UA_LOCALIZEDTEXT("", "SourceNode"); #ifdef UA_ENABLE_NODESET_COMPILER_DESCRIPTIONS attr.description = UA_LOCALIZEDTEXT("", "The source of the event."); #endif retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2044LU), UA_NODEID_NUMERIC(ns[0], 2041LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "SourceNode"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],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], 2044LU) ); } /* EventType - ns=0;i=2043 */ static UA_StatusCode function_namespace0_generated_80_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 17LU); attr.displayName = UA_LOCALIZEDTEXT("", "EventType"); #ifdef UA_ENABLE_NODESET_COMPILER_DESCRIPTIONS attr.description = UA_LOCALIZEDTEXT("", "The identifier for the event type."); #endif retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2043LU), UA_NODEID_NUMERIC(ns[0], 2041LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "EventType"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],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], 2043LU) ); } /* EventId - ns=0;i=2042 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 15LU); attr.displayName = UA_LOCALIZEDTEXT("", "EventId"); #ifdef UA_ENABLE_NODESET_COMPILER_DESCRIPTIONS attr.description = UA_LOCALIZEDTEXT("", "A globally unique identifier for the event."); #endif retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2042LU), UA_NODEID_NUMERIC(ns[0], 2041LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "EventId"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 2042LU) ); } /* InterfaceTypes - ns=0;i=17708 */ static UA_StatusCode function_namespace0_generated_82_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], 17708LU), UA_NODEID_NUMERIC(ns[0], 86LU), UA_NODEID_NUMERIC(ns[0], 35LU), UA_QUALIFIEDNAME(ns[0], "InterfaceTypes"), UA_NODEID_NUMERIC(ns[0], 61LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); 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], 17708LU) ); } /* BaseInterfaceType - ns=0;i=17602 */ static UA_StatusCode function_namespace0_generated_83_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], 17602LU), UA_NODEID_NUMERIC(ns[0], 58LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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], 17602LU), UA_NODEID_NUMERIC(ns[0], 35LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 17708LU), false); 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], 17602LU) ); } /* BuildInfoType - ns=0;i=3051 */ static UA_StatusCode function_namespace0_generated_84_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableTypeAttributes attr = UA_VariableTypeAttributes_default; /* Value rank inherited */ attr.valueRank = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 338LU); attr.displayName = UA_LOCALIZEDTEXT("", "BuildInfoType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLETYPE, UA_NODEID_NUMERIC(ns[0], 3051LU), UA_NODEID_NUMERIC(ns[0], 63LU), UA_NODEID_NUMERIC(ns[0], 45LU), UA_QUALIFIEDNAME(ns[0], "BuildInfoType"), UA_NODEID_NUMERIC(ns[0], 0LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES],NULL, NULL); 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], 3051LU) ); } /* ServerStatusType - ns=0;i=2138 */ static UA_StatusCode function_namespace0_generated_85_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableTypeAttributes attr = UA_VariableTypeAttributes_default; /* Value rank inherited */ attr.valueRank = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 862LU); attr.displayName = UA_LOCALIZEDTEXT("", "ServerStatusType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLETYPE, UA_NODEID_NUMERIC(ns[0], 2138LU), UA_NODEID_NUMERIC(ns[0], 63LU), UA_NODEID_NUMERIC(ns[0], 45LU), UA_QUALIFIEDNAME(ns[0], "ServerStatusType"), UA_NODEID_NUMERIC(ns[0], 0LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES],NULL, NULL); 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], 2138LU) ); } /* OperationLimitsType - ns=0;i=11564 */ static UA_StatusCode function_namespace0_generated_86_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], 11564LU), UA_NODEID_NUMERIC(ns[0], 61LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_86_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11564LU) ); } /* MaxMonitoredItemsPerCall - ns=0;i=11574 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7LU); attr.displayName = UA_LOCALIZEDTEXT("", "MaxMonitoredItemsPerCall"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11574LU), UA_NODEID_NUMERIC(ns[0], 11564LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "MaxMonitoredItemsPerCall"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 11574LU) ); } /* MaxNodesPerNodeManagement - ns=0;i=11573 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7LU); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerNodeManagement"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11573LU), UA_NODEID_NUMERIC(ns[0], 11564LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerNodeManagement"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 11573LU) ); } /* MaxNodesPerTranslateBrowsePathsToNodeIds - ns=0;i=11572 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7LU); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerTranslateBrowsePathsToNodeIds"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11572LU), UA_NODEID_NUMERIC(ns[0], 11564LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerTranslateBrowsePathsToNodeIds"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 11572LU) ); } /* MaxNodesPerRegisterNodes - ns=0;i=11571 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7LU); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerRegisterNodes"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11571LU), UA_NODEID_NUMERIC(ns[0], 11564LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerRegisterNodes"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 11571LU) ); } /* MaxNodesPerBrowse - ns=0;i=11570 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7LU); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerBrowse"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11570LU), UA_NODEID_NUMERIC(ns[0], 11564LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerBrowse"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 11570LU) ); } /* MaxNodesPerMethodCall - ns=0;i=11569 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7LU); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerMethodCall"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11569LU), UA_NODEID_NUMERIC(ns[0], 11564LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerMethodCall"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 11569LU) ); } /* MaxNodesPerWrite - ns=0;i=11567 */ static UA_StatusCode function_namespace0_generated_93_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7LU); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerWrite"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11567LU), UA_NODEID_NUMERIC(ns[0], 11564LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerWrite"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],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], 11567LU) ); } /* MaxNodesPerRead - ns=0;i=11565 */ static UA_StatusCode function_namespace0_generated_94_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7LU); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerRead"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11565LU), UA_NODEID_NUMERIC(ns[0], 11564LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerRead"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],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], 11565LU) ); } /* ServerRedundancyType - ns=0;i=2034 */ static UA_StatusCode function_namespace0_generated_95_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], 2034LU), UA_NODEID_NUMERIC(ns[0], 58LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_95_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2034LU) ); } /* RedundancySupport - ns=0;i=2035 */ static UA_StatusCode function_namespace0_generated_96_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 851LU); attr.displayName = UA_LOCALIZEDTEXT("", "RedundancySupport"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2035LU), UA_NODEID_NUMERIC(ns[0], 2034LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "RedundancySupport"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_96_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2035LU) ); } /* VendorServerInfoType - ns=0;i=2033 */ static UA_StatusCode function_namespace0_generated_97_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], 2033LU), UA_NODEID_NUMERIC(ns[0], 58LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_97_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2033LU) ); } /* ServerDiagnosticsType - ns=0;i=2020 */ static UA_StatusCode function_namespace0_generated_98_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], 2020LU), UA_NODEID_NUMERIC(ns[0], 58LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_98_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2020LU) ); } /* ServerCapabilitiesType - ns=0;i=2013 */ static UA_StatusCode function_namespace0_generated_99_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], 2013LU), UA_NODEID_NUMERIC(ns[0], 58LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_99_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2013LU) ); } /* OperationLimits - ns=0;i=11551 */ static UA_StatusCode function_namespace0_generated_100_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], 11551LU), UA_NODEID_NUMERIC(ns[0], 2013LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "OperationLimits"), UA_NODEID_NUMERIC(ns[0], 11564LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],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], 11551LU) ); } /* ServerType - ns=0;i=2004 */ static UA_StatusCode function_namespace0_generated_101_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], 2004LU), UA_NODEID_NUMERIC(ns[0], 58LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_101_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 2004LU) ); } /* Server - ns=0;i=2253 */ static UA_StatusCode function_namespace0_generated_102_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.eventNotifier = UA_EVENTNOTIFIER_SUBSCRIBE_TO_EVENT; attr.displayName = UA_LOCALIZEDTEXT("", "Server"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 2253LU), UA_NODEID_NUMERIC(ns[0], 85LU), UA_NODEID_NUMERIC(ns[0], 35LU), UA_QUALIFIEDNAME(ns[0], "Server"), UA_NODEID_NUMERIC(ns[0], 2004LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],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], 2253LU) ); } /* Auditing - ns=0;i=2994 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 1LU); attr.displayName = UA_LOCALIZEDTEXT("", "Auditing"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2994LU), UA_NODEID_NUMERIC(ns[0], 2253LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "Auditing"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 2994LU) ); } /* ServerRedundancy - ns=0;i=2296 */ static UA_StatusCode function_namespace0_generated_104_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], 2296LU), UA_NODEID_NUMERIC(ns[0], 2253LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "ServerRedundancy"), UA_NODEID_NUMERIC(ns[0], 2034LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],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], 2296LU) ); } /* RedundancySupport - ns=0;i=3709 */ 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 = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 851LU); attr.displayName = UA_LOCALIZEDTEXT("", "RedundancySupport"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 3709LU), UA_NODEID_NUMERIC(ns[0], 2296LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "RedundancySupport"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 3709LU) ); } /* VendorServerInfo - ns=0;i=2295 */ static UA_StatusCode function_namespace0_generated_106_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], 2295LU), UA_NODEID_NUMERIC(ns[0], 2253LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "VendorServerInfo"), UA_NODEID_NUMERIC(ns[0], 2033LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],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], 2295LU) ); } /* ServerDiagnostics - ns=0;i=2274 */ static UA_StatusCode function_namespace0_generated_107_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], 2274LU), UA_NODEID_NUMERIC(ns[0], 2253LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "ServerDiagnostics"), UA_NODEID_NUMERIC(ns[0], 2020LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],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], 2274LU) ); } /* EnabledFlag - ns=0;i=2294 */ 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 = 3; /* Value rank inherited */ attr.valueRank = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 1LU); attr.displayName = UA_LOCALIZEDTEXT("", "EnabledFlag"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2294LU), UA_NODEID_NUMERIC(ns[0], 2274LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "EnabledFlag"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 2294LU) ); } /* ServerCapabilities - ns=0;i=2268 */ static UA_StatusCode function_namespace0_generated_109_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], 2268LU), UA_NODEID_NUMERIC(ns[0], 2253LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "ServerCapabilities"), UA_NODEID_NUMERIC(ns[0], 2013LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],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], 2268LU) ); } /* SoftwareCertificates - ns=0;i=3704 */ 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; attr.valueRank = 1; attr.arrayDimensionsSize = 1; UA_UInt32 arrayDimensions[1]; arrayDimensions[0] = 0; attr.arrayDimensions = &arrayDimensions[0]; attr.dataType = UA_NODEID_NUMERIC(ns[0], 344LU); attr.displayName = UA_LOCALIZEDTEXT("", "SoftwareCertificates"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 3704LU), UA_NODEID_NUMERIC(ns[0], 2268LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "SoftwareCertificates"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 3704LU) ); } /* AggregateFunctions - ns=0;i=2997 */ static UA_StatusCode function_namespace0_generated_111_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], 2997LU), UA_NODEID_NUMERIC(ns[0], 2268LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "AggregateFunctions"), UA_NODEID_NUMERIC(ns[0], 61LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],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], 2997LU) ); } /* ModellingRules - ns=0;i=2996 */ 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("", "ModellingRules"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 2996LU), UA_NODEID_NUMERIC(ns[0], 2268LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "ModellingRules"), UA_NODEID_NUMERIC(ns[0], 61LU), (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], 2996LU) ); } /* MaxHistoryContinuationPoints - ns=0;i=2737 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 5LU); attr.displayName = UA_LOCALIZEDTEXT("", "MaxHistoryContinuationPoints"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2737LU), UA_NODEID_NUMERIC(ns[0], 2268LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "MaxHistoryContinuationPoints"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 2737LU) ); } /* MaxQueryContinuationPoints - ns=0;i=2736 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 5LU); attr.displayName = UA_LOCALIZEDTEXT("", "MaxQueryContinuationPoints"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2736LU), UA_NODEID_NUMERIC(ns[0], 2268LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "MaxQueryContinuationPoints"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 2736LU) ); } /* MaxBrowseContinuationPoints - ns=0;i=2735 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 5LU); attr.displayName = UA_LOCALIZEDTEXT("", "MaxBrowseContinuationPoints"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2735LU), UA_NODEID_NUMERIC(ns[0], 2268LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "MaxBrowseContinuationPoints"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 2735LU) ); } /* MinSupportedSampleRate - ns=0;i=2272 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 290LU); attr.displayName = UA_LOCALIZEDTEXT("", "MinSupportedSampleRate"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2272LU), UA_NODEID_NUMERIC(ns[0], 2268LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "MinSupportedSampleRate"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 2272LU) ); } /* LocaleIdArray - ns=0;i=2271 */ 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; attr.valueRank = 1; attr.arrayDimensionsSize = 1; UA_UInt32 arrayDimensions[1]; arrayDimensions[0] = 0; attr.arrayDimensions = &arrayDimensions[0]; attr.dataType = UA_NODEID_NUMERIC(ns[0], 295LU); attr.displayName = UA_LOCALIZEDTEXT("", "LocaleIdArray"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2271LU), UA_NODEID_NUMERIC(ns[0], 2268LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "LocaleIdArray"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 2271LU) ); } /* ServerProfileArray - ns=0;i=2269 */ 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; attr.valueRank = 1; attr.arrayDimensionsSize = 1; UA_UInt32 arrayDimensions[1]; arrayDimensions[0] = 0; attr.arrayDimensions = &arrayDimensions[0]; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12LU); attr.displayName = UA_LOCALIZEDTEXT("", "ServerProfileArray"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2269LU), UA_NODEID_NUMERIC(ns[0], 2268LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "ServerProfileArray"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 2269LU) ); } /* OperationLimits - ns=0;i=11704 */ static UA_StatusCode function_namespace0_generated_119_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], 11704LU), UA_NODEID_NUMERIC(ns[0], 2268LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "OperationLimits"), UA_NODEID_NUMERIC(ns[0], 11564LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],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], 11704LU) ); } /* MaxMonitoredItemsPerCall - ns=0;i=11714 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7LU); attr.displayName = UA_LOCALIZEDTEXT("", "MaxMonitoredItemsPerCall"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11714LU), UA_NODEID_NUMERIC(ns[0], 11704LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "MaxMonitoredItemsPerCall"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 11714LU) ); } /* MaxNodesPerNodeManagement - ns=0;i=11713 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7LU); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerNodeManagement"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11713LU), UA_NODEID_NUMERIC(ns[0], 11704LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerNodeManagement"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 11713LU) ); } /* MaxNodesPerTranslateBrowsePathsToNodeIds - ns=0;i=11712 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7LU); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerTranslateBrowsePathsToNodeIds"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11712LU), UA_NODEID_NUMERIC(ns[0], 11704LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerTranslateBrowsePathsToNodeIds"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 11712LU) ); } /* MaxNodesPerRegisterNodes - ns=0;i=11711 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7LU); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerRegisterNodes"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11711LU), UA_NODEID_NUMERIC(ns[0], 11704LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerRegisterNodes"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 11711LU) ); } /* MaxNodesPerBrowse - ns=0;i=11710 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7LU); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerBrowse"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11710LU), UA_NODEID_NUMERIC(ns[0], 11704LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerBrowse"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 11710LU) ); } /* MaxNodesPerMethodCall - ns=0;i=11709 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7LU); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerMethodCall"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11709LU), UA_NODEID_NUMERIC(ns[0], 11704LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerMethodCall"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 11709LU) ); } /* MaxNodesPerWrite - ns=0;i=11707 */ 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 = 1; /* Value rank inherited */ attr.valueRank = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7LU); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerWrite"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11707LU), UA_NODEID_NUMERIC(ns[0], 11704LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerWrite"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 11707LU) ); } /* MaxNodesPerRead - ns=0;i=11705 */ static UA_StatusCode function_namespace0_generated_127_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7LU); attr.displayName = UA_LOCALIZEDTEXT("", "MaxNodesPerRead"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11705LU), UA_NODEID_NUMERIC(ns[0], 11704LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "MaxNodesPerRead"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],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], 11705LU) ); } /* HistoryServerCapabilities - ns=0;i=11192 */ static UA_StatusCode function_namespace0_generated_128_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "HistoryServerCapabilities"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 11192LU), UA_NODEID_NUMERIC(ns[0], 2268LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "HistoryServerCapabilities"), UA_NODEID_NUMERIC(ns[0], 2330LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],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], 11192LU) ); } /* DeleteEventCapability - ns=0;i=11502 */ 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 = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 1LU); attr.displayName = UA_LOCALIZEDTEXT("", "DeleteEventCapability"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11502LU), UA_NODEID_NUMERIC(ns[0], 11192LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "DeleteEventCapability"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 11502LU) ); } /* UpdateEventCapability - ns=0;i=11283 */ 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 = 0.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 1LU); attr.displayName = UA_LOCALIZEDTEXT("", "UpdateEventCapability"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11283LU), UA_NODEID_NUMERIC(ns[0], 11192LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "UpdateEventCapability"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 11283LU) ); } /* ReplaceEventCapability - ns=0;i=11282 */ static UA_StatusCode function_namespace0_generated_131_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 1LU); attr.displayName = UA_LOCALIZEDTEXT("", "ReplaceEventCapability"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11282LU), UA_NODEID_NUMERIC(ns[0], 11192LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "ReplaceEventCapability"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],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], 11282LU) ); } /* InsertEventCapability - ns=0;i=11281 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 1LU); attr.displayName = UA_LOCALIZEDTEXT("", "InsertEventCapability"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11281LU), UA_NODEID_NUMERIC(ns[0], 11192LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "InsertEventCapability"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 11281LU) ); } /* InsertAnnotationCapability - ns=0;i=11275 */ static UA_StatusCode function_namespace0_generated_133_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 1LU); attr.displayName = UA_LOCALIZEDTEXT("", "InsertAnnotationCapability"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11275LU), UA_NODEID_NUMERIC(ns[0], 11192LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "InsertAnnotationCapability"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 11275LU) ); } /* MaxReturnEventValues - ns=0;i=11274 */ static UA_StatusCode function_namespace0_generated_134_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7LU); attr.displayName = UA_LOCALIZEDTEXT("", "MaxReturnEventValues"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11274LU), UA_NODEID_NUMERIC(ns[0], 11192LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "MaxReturnEventValues"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],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], 11274LU) ); } /* MaxReturnDataValues - ns=0;i=11273 */ static UA_StatusCode function_namespace0_generated_135_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7LU); attr.displayName = UA_LOCALIZEDTEXT("", "MaxReturnDataValues"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11273LU), UA_NODEID_NUMERIC(ns[0], 11192LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "MaxReturnDataValues"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 11273LU) ); } /* AccessHistoryEventsCapability - ns=0;i=11242 */ static UA_StatusCode function_namespace0_generated_136_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 1LU); attr.displayName = UA_LOCALIZEDTEXT("", "AccessHistoryEventsCapability"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11242LU), UA_NODEID_NUMERIC(ns[0], 11192LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "AccessHistoryEventsCapability"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],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], 11242LU) ); } /* AggregateFunctions - ns=0;i=11201 */ static UA_StatusCode function_namespace0_generated_137_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], 11201LU), UA_NODEID_NUMERIC(ns[0], 11192LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "AggregateFunctions"), UA_NODEID_NUMERIC(ns[0], 61LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); 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], 11201LU) ); } /* DeleteAtTimeCapability - ns=0;i=11200 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 1LU); attr.displayName = UA_LOCALIZEDTEXT("", "DeleteAtTimeCapability"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11200LU), UA_NODEID_NUMERIC(ns[0], 11192LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "DeleteAtTimeCapability"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 11200LU) ); } /* DeleteRawCapability - ns=0;i=11199 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 1LU); attr.displayName = UA_LOCALIZEDTEXT("", "DeleteRawCapability"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11199LU), UA_NODEID_NUMERIC(ns[0], 11192LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "DeleteRawCapability"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 11199LU) ); } /* UpdateDataCapability - ns=0;i=11198 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 1LU); attr.displayName = UA_LOCALIZEDTEXT("", "UpdateDataCapability"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11198LU), UA_NODEID_NUMERIC(ns[0], 11192LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "UpdateDataCapability"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 11198LU) ); } /* ReplaceDataCapability - ns=0;i=11197 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 1LU); attr.displayName = UA_LOCALIZEDTEXT("", "ReplaceDataCapability"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11197LU), UA_NODEID_NUMERIC(ns[0], 11192LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "ReplaceDataCapability"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 11197LU) ); } /* InsertDataCapability - ns=0;i=11196 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 1LU); attr.displayName = UA_LOCALIZEDTEXT("", "InsertDataCapability"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11196LU), UA_NODEID_NUMERIC(ns[0], 11192LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "InsertDataCapability"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 11196LU) ); } /* AccessHistoryDataCapability - ns=0;i=11193 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 1LU); attr.displayName = UA_LOCALIZEDTEXT("", "AccessHistoryDataCapability"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 11193LU), UA_NODEID_NUMERIC(ns[0], 11192LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "AccessHistoryDataCapability"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 11193LU) ); } /* ServiceLevel - ns=0;i=2267 */ 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 = 1000.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 3LU); attr.displayName = UA_LOCALIZEDTEXT("", "ServiceLevel"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2267LU), UA_NODEID_NUMERIC(ns[0], 2253LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "ServiceLevel"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 2267LU) ); } /* ServerStatus - ns=0;i=2256 */ static UA_StatusCode function_namespace0_generated_145_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 862LU); attr.displayName = UA_LOCALIZEDTEXT("", "ServerStatus"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2256LU), UA_NODEID_NUMERIC(ns[0], 2253LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "ServerStatus"), UA_NODEID_NUMERIC(ns[0], 2138LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],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], 2256LU) ); } /* ShutdownReason - ns=0;i=2993 */ static UA_StatusCode function_namespace0_generated_146_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 21LU); attr.displayName = UA_LOCALIZEDTEXT("", "ShutdownReason"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2993LU), UA_NODEID_NUMERIC(ns[0], 2256LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "ShutdownReason"), UA_NODEID_NUMERIC(ns[0], 63LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); 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], 2993LU) ); } /* SecondsTillShutdown - ns=0;i=2992 */ static UA_StatusCode function_namespace0_generated_147_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 7LU); attr.displayName = UA_LOCALIZEDTEXT("", "SecondsTillShutdown"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2992LU), UA_NODEID_NUMERIC(ns[0], 2256LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "SecondsTillShutdown"), UA_NODEID_NUMERIC(ns[0], 63LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],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], 2992LU) ); } /* BuildInfo - ns=0;i=2260 */ 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; /* Value rank inherited */ attr.valueRank = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 338LU); attr.displayName = UA_LOCALIZEDTEXT("", "BuildInfo"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2260LU), UA_NODEID_NUMERIC(ns[0], 2256LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "BuildInfo"), UA_NODEID_NUMERIC(ns[0], 3051LU), (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], 2260LU) ); } /* BuildDate - ns=0;i=2266 */ static UA_StatusCode function_namespace0_generated_149_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 294LU); attr.displayName = UA_LOCALIZEDTEXT("", "BuildDate"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2266LU), UA_NODEID_NUMERIC(ns[0], 2260LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "BuildDate"), UA_NODEID_NUMERIC(ns[0], 63LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],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], 2266LU) ); } /* BuildNumber - ns=0;i=2265 */ static UA_StatusCode function_namespace0_generated_150_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12LU); attr.displayName = UA_LOCALIZEDTEXT("", "BuildNumber"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2265LU), UA_NODEID_NUMERIC(ns[0], 2260LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "BuildNumber"), UA_NODEID_NUMERIC(ns[0], 63LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],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], 2265LU) ); } /* SoftwareVersion - ns=0;i=2264 */ static UA_StatusCode function_namespace0_generated_151_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12LU); attr.displayName = UA_LOCALIZEDTEXT("", "SoftwareVersion"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2264LU), UA_NODEID_NUMERIC(ns[0], 2260LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "SoftwareVersion"), UA_NODEID_NUMERIC(ns[0], 63LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],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], 2264LU) ); } /* ManufacturerName - ns=0;i=2263 */ 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 = 1000.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12LU); attr.displayName = UA_LOCALIZEDTEXT("", "ManufacturerName"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2263LU), UA_NODEID_NUMERIC(ns[0], 2260LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "ManufacturerName"), UA_NODEID_NUMERIC(ns[0], 63LU), (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], 2263LU) ); } /* ProductUri - ns=0;i=2262 */ 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 = 1000.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12LU); attr.displayName = UA_LOCALIZEDTEXT("", "ProductUri"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2262LU), UA_NODEID_NUMERIC(ns[0], 2260LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "ProductUri"), UA_NODEID_NUMERIC(ns[0], 63LU), (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], 2262LU) ); } /* ProductName - ns=0;i=2261 */ 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 = 1000.000000; attr.userAccessLevel = 1; attr.accessLevel = 1; /* Value rank inherited */ attr.valueRank = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12LU); attr.displayName = UA_LOCALIZEDTEXT("", "ProductName"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2261LU), UA_NODEID_NUMERIC(ns[0], 2260LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "ProductName"), UA_NODEID_NUMERIC(ns[0], 63LU), (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], 2261LU) ); } /* State - ns=0;i=2259 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 852LU); attr.displayName = UA_LOCALIZEDTEXT("", "State"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2259LU), UA_NODEID_NUMERIC(ns[0], 2256LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "State"), UA_NODEID_NUMERIC(ns[0], 63LU), (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], 2259LU) ); } /* CurrentTime - ns=0;i=2258 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 294LU); attr.displayName = UA_LOCALIZEDTEXT("", "CurrentTime"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2258LU), UA_NODEID_NUMERIC(ns[0], 2256LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "CurrentTime"), UA_NODEID_NUMERIC(ns[0], 63LU), (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], 2258LU) ); } /* StartTime - ns=0;i=2257 */ 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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 294LU); attr.displayName = UA_LOCALIZEDTEXT("", "StartTime"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2257LU), UA_NODEID_NUMERIC(ns[0], 2256LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "StartTime"), UA_NODEID_NUMERIC(ns[0], 63LU), (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], 2257LU) ); } /* NamespaceArray - ns=0;i=2255 */ 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 = 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], 12LU); attr.displayName = UA_LOCALIZEDTEXT("", "NamespaceArray"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2255LU), UA_NODEID_NUMERIC(ns[0], 2253LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "NamespaceArray"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 2255LU) ); } /* ServerArray - ns=0;i=2254 */ 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 = 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], 12LU); attr.displayName = UA_LOCALIZEDTEXT("", "ServerArray"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 2254LU), UA_NODEID_NUMERIC(ns[0], 2253LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "ServerArray"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 2254LU) ); } /* GetMonitoredItems - ns=0;i=11492 */ static UA_StatusCode function_namespace0_generated_160_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], 11492LU), UA_NODEID_NUMERIC(ns[0], 2253LU), UA_NODEID_NUMERIC(ns[0], 47LU), 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_160_finish(UA_Server *server, UA_UInt16* ns) { #ifdef UA_ENABLE_METHODCALLS return UA_Server_addMethodNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11492LU) , 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_161_begin(UA_Server *server, UA_UInt16* ns) { #ifdef UA_ENABLE_METHODCALLS 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], 296LU); 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], 7LU); 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].arrayDimensionsSize = 1; 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], 7LU); 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].arrayDimensionsSize = 1; 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], 11494LU), UA_NODEID_NUMERIC(ns[0], 11492LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "OutputArguments"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; #else return UA_STATUSCODE_GOOD; #endif /* UA_ENABLE_METHODCALLS */ } static UA_StatusCode function_namespace0_generated_161_finish(UA_Server *server, UA_UInt16* ns) { #ifdef UA_ENABLE_METHODCALLS return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11494LU) ); #else return UA_STATUSCODE_GOOD; #endif /* UA_ENABLE_METHODCALLS */ } /* InputArguments - ns=0;i=11493 */ static UA_StatusCode function_namespace0_generated_162_begin(UA_Server *server, UA_UInt16* ns) { #ifdef UA_ENABLE_METHODCALLS 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], 296LU); 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], 7LU); 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], 11493LU), UA_NODEID_NUMERIC(ns[0], 11492LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "InputArguments"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); return retVal; #else return UA_STATUSCODE_GOOD; #endif /* UA_ENABLE_METHODCALLS */ } static UA_StatusCode function_namespace0_generated_162_finish(UA_Server *server, UA_UInt16* ns) { #ifdef UA_ENABLE_METHODCALLS return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11493LU) ); #else return UA_STATUSCODE_GOOD; #endif /* UA_ENABLE_METHODCALLS */ } /* VendorServerInfo - ns=0;i=2011 */ static UA_StatusCode function_namespace0_generated_163_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], 2011LU), UA_NODEID_NUMERIC(ns[0], 2004LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "VendorServerInfo"), UA_NODEID_NUMERIC(ns[0], 2033LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],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], 2011LU) ); } /* ModellingRuleType - ns=0;i=77 */ static UA_StatusCode function_namespace0_generated_164_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], 77LU), UA_NODEID_NUMERIC(ns[0], 58LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_164_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 77LU) ); } /* ExposesItsArray - ns=0;i=83 */ static UA_StatusCode function_namespace0_generated_165_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "ExposesItsArray"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 83LU), UA_NODEID_NUMERIC(ns[0], 0LU), UA_NODEID_NUMERIC(ns[0], 0LU), UA_QUALIFIEDNAME(ns[0], "ExposesItsArray"), UA_NODEID_NUMERIC(ns[0], 77LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],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], 83LU) ); } /* NamingRule - ns=0;i=114 */ static UA_StatusCode function_namespace0_generated_166_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 120LU); UA_Int32 *variablenode_ns_0_i_114_variant_DataContents = UA_Int32_new(); if (!variablenode_ns_0_i_114_variant_DataContents) return UA_STATUSCODE_BADOUTOFMEMORY; UA_Int32_init(variablenode_ns_0_i_114_variant_DataContents); *variablenode_ns_0_i_114_variant_DataContents = (UA_Int32) 3; UA_Variant_setScalar(&attr.value, variablenode_ns_0_i_114_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], 114LU), UA_NODEID_NUMERIC(ns[0], 83LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "NamingRule"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); UA_Int32_delete(variablenode_ns_0_i_114_variant_DataContents); return retVal; } static UA_StatusCode function_namespace0_generated_166_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 114LU) ); } /* Optional - ns=0;i=80 */ static UA_StatusCode function_namespace0_generated_167_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], 80LU), UA_NODEID_NUMERIC(ns[0], 0LU), UA_NODEID_NUMERIC(ns[0], 0LU), UA_QUALIFIEDNAME(ns[0], "Optional"), UA_NODEID_NUMERIC(ns[0], 77LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 80LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 3190LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 80LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 2367LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 80LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 11571LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 80LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 11565LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 80LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 2370LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 80LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 11572LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 80LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 11569LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 80LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 11570LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 80LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 2371LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 80LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 11567LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 80LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 11551LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 80LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 2366LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 80LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 11573LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 80LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 11574LU), false); return retVal; } static UA_StatusCode function_namespace0_generated_167_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 80LU) ); } /* NamingRule - ns=0;i=113 */ static UA_StatusCode function_namespace0_generated_168_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 120LU); 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], 113LU), UA_NODEID_NUMERIC(ns[0], 80LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "NamingRule"), UA_NODEID_NUMERIC(ns[0], 68LU), (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_168_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 113LU) ); } /* Mandatory - ns=0;i=78 */ static UA_StatusCode function_namespace0_generated_169_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], 78LU), UA_NODEID_NUMERIC(ns[0], 0LU), UA_NODEID_NUMERIC(ns[0], 0LU), UA_QUALIFIEDNAME(ns[0], "Mandatory"), UA_NODEID_NUMERIC(ns[0], 77LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 78LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 2043LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 78LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 2011LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 78LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 11241LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 78LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 2046LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 78LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 12169LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 78LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 2051LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 78LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 2044LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 78LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 2047LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 78LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 2374LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 78LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 7611LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 78LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 2050LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 78LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 2042LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 78LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 11461LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 78LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 12078LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 78LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 2035LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 78LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 2375LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 78LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 2045LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 78LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 2369LU), false); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 78LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 2377LU), false); return retVal; } static UA_StatusCode function_namespace0_generated_169_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 78LU) ); } /* NamingRule - ns=0;i=112 */ static UA_StatusCode function_namespace0_generated_170_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 120LU); 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], 112LU), UA_NODEID_NUMERIC(ns[0], 78LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "NamingRule"), UA_NODEID_NUMERIC(ns[0], 68LU), (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_170_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 112LU) ); } /* MandatoryPlaceholder - ns=0;i=11510 */ static UA_StatusCode function_namespace0_generated_171_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "MandatoryPlaceholder"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 11510LU), UA_NODEID_NUMERIC(ns[0], 0LU), UA_NODEID_NUMERIC(ns[0], 0LU), UA_QUALIFIEDNAME(ns[0], "MandatoryPlaceholder"), UA_NODEID_NUMERIC(ns[0], 77LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_171_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11510LU) ); } /* NamingRule - ns=0;i=11511 */ static UA_StatusCode function_namespace0_generated_172_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 120LU); UA_Int32 *variablenode_ns_0_i_11511_variant_DataContents = UA_Int32_new(); if (!variablenode_ns_0_i_11511_variant_DataContents) return UA_STATUSCODE_BADOUTOFMEMORY; UA_Int32_init(variablenode_ns_0_i_11511_variant_DataContents); *variablenode_ns_0_i_11511_variant_DataContents = (UA_Int32) 1; UA_Variant_setScalar(&attr.value, variablenode_ns_0_i_11511_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], 11511LU), UA_NODEID_NUMERIC(ns[0], 11510LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "NamingRule"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); UA_Int32_delete(variablenode_ns_0_i_11511_variant_DataContents); return retVal; } static UA_StatusCode function_namespace0_generated_172_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11511LU) ); } /* OptionalPlaceholder - ns=0;i=11508 */ static UA_StatusCode function_namespace0_generated_173_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "OptionalPlaceholder"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 11508LU), UA_NODEID_NUMERIC(ns[0], 0LU), UA_NODEID_NUMERIC(ns[0], 0LU), UA_QUALIFIEDNAME(ns[0], "OptionalPlaceholder"), UA_NODEID_NUMERIC(ns[0], 77LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_173_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11508LU) ); } /* NamingRule - ns=0;i=11509 */ static UA_StatusCode function_namespace0_generated_174_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 120LU); UA_Int32 *variablenode_ns_0_i_11509_variant_DataContents = UA_Int32_new(); if (!variablenode_ns_0_i_11509_variant_DataContents) return UA_STATUSCODE_BADOUTOFMEMORY; UA_Int32_init(variablenode_ns_0_i_11509_variant_DataContents); *variablenode_ns_0_i_11509_variant_DataContents = (UA_Int32) 2; UA_Variant_setScalar(&attr.value, variablenode_ns_0_i_11509_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], 11509LU), UA_NODEID_NUMERIC(ns[0], 11508LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "NamingRule"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); UA_Int32_delete(variablenode_ns_0_i_11509_variant_DataContents); return retVal; } static UA_StatusCode function_namespace0_generated_174_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 11509LU) ); } /* NamingRule - ns=0;i=111 */ static UA_StatusCode function_namespace0_generated_175_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 120LU); 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], 111LU), UA_NODEID_NUMERIC(ns[0], 77LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "NamingRule"), UA_NODEID_NUMERIC(ns[0], 68LU), (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], 111LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 78LU), true); return retVal; } static UA_StatusCode function_namespace0_generated_175_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 111LU) ); } /* DataTypeEncodingType - ns=0;i=76 */ static UA_StatusCode function_namespace0_generated_176_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], 76LU), UA_NODEID_NUMERIC(ns[0], 58LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_176_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 76LU) ); } /* Default Binary - ns=0;i=8251 */ static UA_StatusCode function_namespace0_generated_177_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], 8251LU), UA_NODEID_NUMERIC(ns[0], 0LU), UA_NODEID_NUMERIC(ns[0], 0LU), UA_QUALIFIEDNAME(ns[0], "Default Binary"), UA_NODEID_NUMERIC(ns[0], 76LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 8251LU), UA_NODEID_NUMERIC(ns[0], 38LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 7594LU), false); return retVal; } static UA_StatusCode function_namespace0_generated_177_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 8251LU) ); } /* Default Binary - ns=0;i=298 */ static UA_StatusCode function_namespace0_generated_178_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], 298LU), UA_NODEID_NUMERIC(ns[0], 0LU), UA_NODEID_NUMERIC(ns[0], 0LU), UA_QUALIFIEDNAME(ns[0], "Default Binary"), UA_NODEID_NUMERIC(ns[0], 76LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 298LU), UA_NODEID_NUMERIC(ns[0], 38LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 296LU), false); return retVal; } static UA_StatusCode function_namespace0_generated_178_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 298LU) ); } /* Default JSON - ns=0;i=15376 */ static UA_StatusCode function_namespace0_generated_179_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "Default JSON"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 15376LU), UA_NODEID_NUMERIC(ns[0], 0LU), UA_NODEID_NUMERIC(ns[0], 0LU), UA_QUALIFIEDNAME(ns[0], "Default JSON"), UA_NODEID_NUMERIC(ns[0], 76LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 15376LU), UA_NODEID_NUMERIC(ns[0], 38LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 887LU), false); return retVal; } static UA_StatusCode function_namespace0_generated_179_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 15376LU) ); } /* Default JSON - ns=0;i=15375 */ static UA_StatusCode function_namespace0_generated_180_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_ObjectAttributes attr = UA_ObjectAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("", "Default JSON"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT, UA_NODEID_NUMERIC(ns[0], 15375LU), UA_NODEID_NUMERIC(ns[0], 0LU), UA_NODEID_NUMERIC(ns[0], 0LU), UA_QUALIFIEDNAME(ns[0], "Default JSON"), UA_NODEID_NUMERIC(ns[0], 76LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 15375LU), UA_NODEID_NUMERIC(ns[0], 38LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 884LU), false); return retVal; } static UA_StatusCode function_namespace0_generated_180_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 15375LU) ); } /* DataTypeSystemType - ns=0;i=75 */ static UA_StatusCode function_namespace0_generated_181_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], 75LU), UA_NODEID_NUMERIC(ns[0], 58LU), UA_NODEID_NUMERIC(ns[0], 45LU), 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_181_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 75LU) ); } /* OPC Binary - ns=0;i=93 */ static UA_StatusCode function_namespace0_generated_182_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], 93LU), UA_NODEID_NUMERIC(ns[0], 90LU), UA_NODEID_NUMERIC(ns[0], 35LU), UA_QUALIFIEDNAME(ns[0], "OPC Binary"), UA_NODEID_NUMERIC(ns[0], 75LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_182_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 93LU) ); } /* XML Schema - ns=0;i=92 */ static UA_StatusCode function_namespace0_generated_183_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], 92LU), UA_NODEID_NUMERIC(ns[0], 90LU), UA_NODEID_NUMERIC(ns[0], 35LU), UA_QUALIFIEDNAME(ns[0], "XML Schema"), UA_NODEID_NUMERIC(ns[0], 75LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_183_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 92LU) ); } /* DataTypeDictionaryType - ns=0;i=72 */ static UA_StatusCode function_namespace0_generated_184_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableTypeAttributes attr = UA_VariableTypeAttributes_default; /* Value rank inherited */ attr.valueRank = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 15LU); attr.displayName = UA_LOCALIZEDTEXT("", "DataTypeDictionaryType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLETYPE, UA_NODEID_NUMERIC(ns[0], 72LU), UA_NODEID_NUMERIC(ns[0], 63LU), UA_NODEID_NUMERIC(ns[0], 45LU), UA_QUALIFIEDNAME(ns[0], "DataTypeDictionaryType"), UA_NODEID_NUMERIC(ns[0], 0LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_184_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 72LU) ); } /* Opc.Ua - ns=0;i=7617 */ static const UA_Byte variablenode_ns_0_i_7617_variant_DataContents_byteArray[177218] = {60, 111, 112, 99, 58, 84, 121, 112, 101, 68, 105, 99, 116, 105, 111, 110, 97, 114, 121, 13, 10, 32, 32, 120, 109, 108, 110, 115, 58, 111, 112, 99, 61, 34, 104, 116, 116, 112, 58, 47, 47, 111, 112, 99, 102, 111, 117, 110, 100, 97, 116, 105, 111, 110, 46, 111, 114, 103, 47, 66, 105, 110, 97, 114, 121, 83, 99, 104, 101, 109, 97, 47, 34, 13, 10, 32, 32, 120, 109, 108, 110, 115, 58, 120, 115, 105, 61, 34, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 119, 51, 46, 111, 114, 103, 47, 50, 48, 48, 49, 47, 88, 77, 76, 83, 99, 104, 101, 109, 97, 45, 105, 110, 115, 116, 97, 110, 99, 101, 34, 13, 10, 32, 32, 120, 109, 108, 110, 115, 58, 117, 97, 61, 34, 104, 116, 116, 112, 58, 47, 47, 111, 112, 99, 102, 111, 117, 110, 100, 97, 116, 105, 111, 110, 46, 111, 114, 103, 47, 85, 65, 47, 34, 13, 10, 32, 32, 120, 109, 108, 110, 115, 58, 116, 110, 115, 61, 34, 104, 116, 116, 112, 58, 47, 47, 111, 112, 99, 102, 111, 117, 110, 100, 97, 116, 105, 111, 110, 46, 111, 114, 103, 47, 85, 65, 47, 34, 13, 10, 32, 32, 68, 101, 102, 97, 117, 108, 116, 66, 121, 116, 101, 79, 114, 100, 101, 114, 61, 34, 76, 105, 116, 116, 108, 101, 69, 110, 100, 105, 97, 110, 34, 13, 10, 32, 32, 84, 97, 114, 103, 101, 116, 78, 97, 109, 101, 115, 112, 97, 99, 101, 61, 34, 104, 116, 116, 112, 58, 47, 47, 111, 112, 99, 102, 111, 117, 110, 100, 97, 116, 105, 111, 110, 46, 111, 114, 103, 47, 85, 65, 47, 34, 13, 10, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 73, 109, 112, 111, 114, 116, 32, 78, 97, 109, 101, 115, 112, 97, 99, 101, 61, 34, 104, 116, 116, 112, 58, 47, 47, 111, 112, 99, 102, 111, 117, 110, 100, 97, 116, 105, 111, 110, 46, 111, 114, 103, 47, 66, 105, 110, 97, 114, 121, 83, 99, 104, 101, 109, 97, 47, 34, 32, 47, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 88, 109, 108, 69, 108, 101, 109, 101, 110, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 68, 111, 99, 117, 109, 101, 110, 116, 97, 116, 105, 111, 110, 62, 65, 110, 32, 88, 77, 76, 32, 101, 108, 101, 109, 101, 110, 116, 32, 101, 110, 99, 111, 100, 101, 100, 32, 97, 115, 32, 97, 32, 85, 84, 70, 45, 56, 32, 115, 116, 114, 105, 110, 103, 46, 60, 47, 111, 112, 99, 58, 68, 111, 99, 117, 109, 101, 110, 116, 97, 116, 105, 111, 110, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 101, 110, 103, 116, 104, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 67, 104, 97, 114, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 76, 101, 110, 103, 116, 104, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 54, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 68, 111, 99, 117, 109, 101, 110, 116, 97, 116, 105, 111, 110, 62, 84, 104, 101, 32, 112, 111, 115, 115, 105, 98, 108, 101, 32, 101, 110, 99, 111, 100, 105, 110, 103, 115, 32, 102, 111, 114, 32, 97, 32, 78, 111, 100, 101, 73, 100, 32, 118, 97, 108, 117, 101, 46, 60, 47, 111, 112, 99, 58, 68, 111, 99, 117, 109, 101, 110, 116, 97, 116, 105, 111, 110, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 84, 119, 111, 66, 121, 116, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 70, 111, 117, 114, 66, 121, 116, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 117, 109, 101, 114, 105, 99, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 116, 114, 105, 110, 103, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 71, 117, 105, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 86, 97, 108, 117, 101, 61, 34, 53, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 84, 119, 111, 66, 121, 116, 101, 78, 111, 100, 101, 73, 100, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 70, 111, 117, 114, 66, 121, 116, 101, 78, 111, 100, 101, 73, 100, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 115, 112, 97, 99, 101, 73, 110, 100, 101, 120, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 78, 117, 109, 101, 114, 105, 99, 78, 111, 100, 101, 73, 100, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 115, 112, 97, 99, 101, 73, 110, 100, 101, 120, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 116, 114, 105, 110, 103, 78, 111, 100, 101, 73, 100, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 115, 112, 97, 99, 101, 73, 110, 100, 101, 120, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 67, 104, 97, 114, 65, 114, 114, 97, 121, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 71, 117, 105, 100, 78, 111, 100, 101, 73, 100, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 115, 112, 97, 99, 101, 73, 110, 100, 101, 120, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 71, 117, 105, 100, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 78, 111, 100, 101, 73, 100, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 115, 112, 97, 99, 101, 73, 110, 100, 101, 120, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 68, 111, 99, 117, 109, 101, 110, 116, 97, 116, 105, 111, 110, 62, 65, 110, 32, 105, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 102, 111, 114, 32, 97, 32, 110, 111, 100, 101, 32, 105, 110, 32, 97, 32, 85, 65, 32, 115, 101, 114, 118, 101, 114, 32, 97, 100, 100, 114, 101, 115, 115, 32, 115, 112, 97, 99, 101, 46, 60, 47, 111, 112, 99, 58, 68, 111, 99, 117, 109, 101, 110, 116, 97, 116, 105, 111, 110, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 101, 114, 118, 101, 100, 49, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 76, 101, 110, 103, 116, 104, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 119, 111, 66, 121, 116, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 84, 119, 111, 66, 121, 116, 101, 78, 111, 100, 101, 73, 100, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 100, 101, 73, 100, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 70, 111, 117, 114, 66, 121, 116, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 70, 111, 117, 114, 66, 121, 116, 101, 78, 111, 100, 101, 73, 100, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 100, 101, 73, 100, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 117, 109, 101, 114, 105, 99, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 117, 109, 101, 114, 105, 99, 78, 111, 100, 101, 73, 100, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 100, 101, 73, 100, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 114, 105, 110, 103, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 114, 105, 110, 103, 78, 111, 100, 101, 73, 100, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 100, 101, 73, 100, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 51, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 71, 117, 105, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 71, 117, 105, 100, 78, 111, 100, 101, 73, 100, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 100, 101, 73, 100, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 78, 111, 100, 101, 73, 100, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 100, 101, 73, 100, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 53, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 69, 120, 112, 97, 110, 100, 101, 100, 78, 111, 100, 101, 73, 100, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 68, 111, 99, 117, 109, 101, 110, 116, 97, 116, 105, 111, 110, 62, 65, 110, 32, 105, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 102, 111, 114, 32, 97, 32, 110, 111, 100, 101, 32, 105, 110, 32, 97, 32, 85, 65, 32, 115, 101, 114, 118, 101, 114, 32, 97, 100, 100, 114, 101, 115, 115, 32, 115, 112, 97, 99, 101, 32, 113, 117, 97, 108, 105, 102, 105, 101, 100, 32, 119, 105, 116, 104, 32, 97, 32, 99, 111, 109, 112, 108, 101, 116, 101, 32, 110, 97, 109, 101, 115, 112, 97, 99, 101, 32, 115, 116, 114, 105, 110, 103, 46, 60, 47, 111, 112, 99, 58, 68, 111, 99, 117, 109, 101, 110, 116, 97, 116, 105, 111, 110, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 73, 110, 100, 101, 120, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 115, 112, 97, 99, 101, 85, 82, 73, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 119, 111, 66, 121, 116, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 84, 119, 111, 66, 121, 116, 101, 78, 111, 100, 101, 73, 100, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 100, 101, 73, 100, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 70, 111, 117, 114, 66, 121, 116, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 70, 111, 117, 114, 66, 121, 116, 101, 78, 111, 100, 101, 73, 100, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 100, 101, 73, 100, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 117, 109, 101, 114, 105, 99, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 117, 109, 101, 114, 105, 99, 78, 111, 100, 101, 73, 100, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 100, 101, 73, 100, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 114, 105, 110, 103, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 114, 105, 110, 103, 78, 111, 100, 101, 73, 100, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 100, 101, 73, 100, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 51, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 71, 117, 105, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 71, 117, 105, 100, 78, 111, 100, 101, 73, 100, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 100, 101, 73, 100, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 78, 111, 100, 101, 73, 100, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 100, 101, 73, 100, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 53, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 115, 112, 97, 99, 101, 85, 82, 73, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 67, 104, 97, 114, 65, 114, 114, 97, 121, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 78, 97, 109, 101, 115, 112, 97, 99, 101, 85, 82, 73, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 73, 110, 100, 101, 120, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 83, 101, 114, 118, 101, 114, 73, 110, 100, 101, 120, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 32, 66, 121, 116, 101, 79, 114, 100, 101, 114, 83, 105, 103, 110, 105, 102, 105, 99, 97, 110, 116, 61, 34, 116, 114, 117, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 68, 111, 99, 117, 109, 101, 110, 116, 97, 116, 105, 111, 110, 62, 65, 32, 51, 50, 45, 98, 105, 116, 32, 115, 116, 97, 116, 117, 115, 32, 99, 111, 100, 101, 32, 118, 97, 108, 117, 101, 46, 60, 47, 111, 112, 99, 58, 68, 111, 99, 117, 109, 101, 110, 116, 97, 116, 105, 111, 110, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 68, 111, 99, 117, 109, 101, 110, 116, 97, 116, 105, 111, 110, 62, 65, 32, 114, 101, 99, 117, 114, 115, 105, 118, 101, 32, 115, 116, 114, 117, 99, 116, 117, 114, 101, 32, 99, 111, 110, 116, 97, 105, 110, 105, 110, 103, 32, 100, 105, 97, 103, 110, 111, 115, 116, 105, 99, 32, 105, 110, 102, 111, 114, 109, 97, 116, 105, 111, 110, 32, 97, 115, 115, 111, 99, 105, 97, 116, 101, 100, 32, 119, 105, 116, 104, 32, 97, 32, 115, 116, 97, 116, 117, 115, 32, 99, 111, 100, 101, 46, 60, 47, 111, 112, 99, 58, 68, 111, 99, 117, 109, 101, 110, 116, 97, 116, 105, 111, 110, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 121, 109, 98, 111, 108, 105, 99, 73, 100, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 115, 112, 97, 99, 101, 85, 82, 73, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 111, 99, 97, 108, 101, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 100, 100, 105, 116, 105, 111, 110, 97, 108, 73, 110, 102, 111, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 110, 101, 114, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 110, 101, 114, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 101, 114, 118, 101, 100, 49, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 76, 101, 110, 103, 116, 104, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 121, 109, 98, 111, 108, 105, 99, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 83, 121, 109, 98, 111, 108, 105, 99, 73, 100, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 115, 112, 97, 99, 101, 85, 82, 73, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 78, 97, 109, 101, 115, 112, 97, 99, 101, 85, 82, 73, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 111, 99, 97, 108, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 76, 111, 99, 97, 108, 101, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 100, 100, 105, 116, 105, 111, 110, 97, 108, 73, 110, 102, 111, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 67, 104, 97, 114, 65, 114, 114, 97, 121, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 65, 100, 100, 105, 116, 105, 111, 110, 97, 108, 73, 110, 102, 111, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 110, 101, 114, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 73, 110, 110, 101, 114, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 110, 101, 114, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 73, 110, 110, 101, 114, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 68, 111, 99, 117, 109, 101, 110, 116, 97, 116, 105, 111, 110, 62, 65, 32, 115, 116, 114, 105, 110, 103, 32, 113, 117, 97, 108, 105, 102, 105, 101, 100, 32, 119, 105, 116, 104, 32, 97, 32, 110, 97, 109, 101, 115, 112, 97, 99, 101, 32, 105, 110, 100, 101, 120, 46, 60, 47, 111, 112, 99, 58, 68, 111, 99, 117, 109, 101, 110, 116, 97, 116, 105, 111, 110, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 115, 112, 97, 99, 101, 73, 110, 100, 101, 120, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 67, 104, 97, 114, 65, 114, 114, 97, 121, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 68, 111, 99, 117, 109, 101, 110, 116, 97, 116, 105, 111, 110, 62, 65, 32, 115, 116, 114, 105, 110, 103, 32, 113, 117, 97, 108, 105, 102, 105, 101, 100, 32, 119, 105, 116, 104, 32, 97, 32, 110, 97, 109, 101, 115, 112, 97, 99, 101, 32, 105, 110, 100, 101, 120, 46, 60, 47, 111, 112, 99, 58, 68, 111, 99, 117, 109, 101, 110, 116, 97, 116, 105, 111, 110, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 111, 99, 97, 108, 101, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 101, 120, 116, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 101, 114, 118, 101, 100, 49, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 76, 101, 110, 103, 116, 104, 61, 34, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 111, 99, 97, 108, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 67, 104, 97, 114, 65, 114, 114, 97, 121, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 76, 111, 99, 97, 108, 101, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 101, 120, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 67, 104, 97, 114, 65, 114, 114, 97, 121, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 84, 101, 120, 116, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 86, 97, 108, 117, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 68, 111, 99, 117, 109, 101, 110, 116, 97, 116, 105, 111, 110, 62, 65, 32, 118, 97, 108, 117, 101, 32, 119, 105, 116, 104, 32, 97, 110, 32, 97, 115, 115, 111, 99, 105, 97, 116, 101, 100, 32, 116, 105, 109, 101, 115, 116, 97, 109, 112, 44, 32, 97, 110, 100, 32, 113, 117, 97, 108, 105, 116, 121, 46, 60, 47, 111, 112, 99, 58, 68, 111, 99, 117, 109, 101, 110, 116, 97, 116, 105, 111, 110, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 111, 117, 114, 99, 101, 84, 105, 109, 101, 115, 116, 97, 109, 112, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 84, 105, 109, 101, 115, 116, 97, 109, 112, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 111, 117, 114, 99, 101, 80, 105, 99, 111, 115, 101, 99, 111, 110, 100, 115, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 80, 105, 99, 111, 115, 101, 99, 111, 110, 100, 115, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 101, 114, 118, 101, 100, 49, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 76, 101, 110, 103, 116, 104, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 86, 97, 114, 105, 97, 110, 116, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 108, 117, 101, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 111, 117, 114, 99, 101, 84, 105, 109, 101, 115, 116, 97, 109, 112, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 83, 111, 117, 114, 99, 101, 84, 105, 109, 101, 115, 116, 97, 109, 112, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 111, 117, 114, 99, 101, 80, 105, 99, 111, 115, 101, 99, 111, 110, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 83, 111, 117, 114, 99, 101, 80, 105, 99, 111, 115, 101, 99, 111, 110, 100, 115, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 84, 105, 109, 101, 115, 116, 97, 109, 112, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 83, 101, 114, 118, 101, 114, 84, 105, 109, 101, 115, 116, 97, 109, 112, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 80, 105, 99, 111, 115, 101, 99, 111, 110, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 83, 101, 114, 118, 101, 114, 80, 105, 99, 111, 115, 101, 99, 111, 110, 100, 115, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 68, 111, 99, 117, 109, 101, 110, 116, 97, 116, 105, 111, 110, 62, 65, 32, 115, 101, 114, 105, 97, 108, 105, 122, 101, 100, 32, 111, 98, 106, 101, 99, 116, 32, 112, 114, 101, 102, 105, 120, 101, 100, 32, 119, 105, 116, 104, 32, 105, 116, 115, 32, 100, 97, 116, 97, 32, 116, 121, 112, 101, 32, 105, 100, 101, 110, 116, 105, 102, 105, 101, 114, 46, 60, 47, 111, 112, 99, 58, 68, 111, 99, 117, 109, 101, 110, 116, 97, 116, 105, 111, 110, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 121, 112, 101, 73, 100, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 105, 110, 97, 114, 121, 66, 111, 100, 121, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 88, 109, 108, 66, 111, 100, 121, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 101, 114, 118, 101, 100, 49, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 76, 101, 110, 103, 116, 104, 61, 34, 53, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 121, 112, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 112, 97, 110, 100, 101, 100, 78, 111, 100, 101, 73, 100, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 84, 121, 112, 101, 73, 100, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 111, 100, 121, 76, 101, 110, 103, 116, 104, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 111, 100, 121, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 66, 111, 100, 121, 76, 101, 110, 103, 116, 104, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 86, 97, 114, 105, 97, 110, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 68, 111, 99, 117, 109, 101, 110, 116, 97, 116, 105, 111, 110, 62, 65, 32, 117, 110, 105, 111, 110, 32, 111, 102, 32, 115, 101, 118, 101, 114, 97, 108, 32, 116, 121, 112, 101, 115, 46, 60, 47, 111, 112, 99, 58, 68, 111, 99, 117, 109, 101, 110, 116, 97, 116, 105, 111, 110, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 76, 101, 110, 103, 116, 104, 61, 34, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 76, 101, 110, 103, 116, 104, 61, 34, 49, 34, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 105, 116, 34, 32, 76, 101, 110, 103, 116, 104, 61, 34, 49, 34, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 111, 111, 108, 101, 97, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 66, 121, 116, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 66, 121, 116, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 121, 116, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 51, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 116, 49, 54, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 49, 54, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 73, 110, 116, 49, 54, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 53, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 116, 51, 50, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 73, 110, 116, 51, 50, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 55, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 116, 54, 52, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 54, 52, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 73, 110, 116, 54, 52, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 54, 52, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 57, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 70, 108, 111, 97, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 70, 108, 111, 97, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 49, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 111, 117, 98, 108, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 49, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 114, 105, 110, 103, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 67, 104, 97, 114, 65, 114, 114, 97, 121, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 49, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 49, 51, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 71, 117, 105, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 71, 117, 105, 100, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 49, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 49, 53, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 88, 109, 108, 69, 108, 101, 109, 101, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 88, 109, 108, 69, 108, 101, 109, 101, 110, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 49, 55, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 120, 112, 97, 110, 100, 101, 100, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 112, 97, 110, 100, 101, 100, 78, 111, 100, 101, 73, 100, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 49, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 49, 57, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 50, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 50, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 50, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 86, 97, 108, 117, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 97, 116, 97, 86, 97, 108, 117, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 50, 51, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 114, 105, 97, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 86, 97, 114, 105, 97, 110, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 50, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 86, 97, 114, 105, 97, 110, 116, 84, 121, 112, 101, 34, 32, 83, 119, 105, 116, 99, 104, 86, 97, 108, 117, 101, 61, 34, 50, 53, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 83, 119, 105, 116, 99, 104, 70, 105, 101, 108, 100, 61, 34, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 105, 110, 103, 82, 117, 108, 101, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 77, 97, 110, 100, 97, 116, 111, 114, 121, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 79, 112, 116, 105, 111, 110, 97, 108, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 67, 111, 110, 115, 116, 114, 97, 105, 110, 116, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 32, 32, 32, 32, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 73, 109, 97, 103, 101, 66, 77, 80, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 73, 109, 97, 103, 101, 71, 73, 70, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 73, 109, 97, 103, 101, 74, 80, 71, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 73, 109, 97, 103, 101, 80, 78, 71, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 117, 100, 105, 111, 68, 97, 116, 97, 84, 121, 112, 101, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 66, 105, 116, 70, 105, 101, 108, 100, 77, 97, 115, 107, 68, 97, 116, 97, 84, 121, 112, 101, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 75, 101, 121, 86, 97, 108, 117, 101, 80, 97, 105, 114, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 75, 101, 121, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 86, 97, 114, 105, 97, 110, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 100, 100, 105, 116, 105, 111, 110, 97, 108, 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 75, 101, 121, 86, 97, 108, 117, 101, 80, 97, 105, 114, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 69, 112, 104, 101, 109, 101, 114, 97, 108, 75, 101, 121, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 99, 75, 101, 121, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 105, 103, 110, 97, 116, 117, 114, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 69, 110, 100, 112, 111, 105, 110, 116, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 100, 112, 111, 105, 110, 116, 85, 114, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 77, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 77, 101, 115, 115, 97, 103, 101, 83, 101, 99, 117, 114, 105, 116, 121, 77, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 80, 111, 108, 105, 99, 121, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 114, 97, 110, 115, 112, 111, 114, 116, 80, 114, 111, 102, 105, 108, 101, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 97, 116, 105, 111, 110, 97, 108, 78, 117, 109, 98, 101, 114, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 117, 109, 101, 114, 97, 116, 111, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 110, 111, 109, 105, 110, 97, 116, 111, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 86, 101, 99, 116, 111, 114, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 84, 104, 114, 101, 101, 68, 86, 101, 99, 116, 111, 114, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 86, 101, 99, 116, 111, 114, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 88, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 89, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 90, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 97, 114, 116, 101, 115, 105, 97, 110, 67, 111, 111, 114, 100, 105, 110, 97, 116, 101, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 84, 104, 114, 101, 101, 68, 67, 97, 114, 116, 101, 115, 105, 97, 110, 67, 111, 111, 114, 100, 105, 110, 97, 116, 101, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 67, 97, 114, 116, 101, 115, 105, 97, 110, 67, 111, 111, 114, 100, 105, 110, 97, 116, 101, 115, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 88, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 89, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 90, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 79, 114, 105, 101, 110, 116, 97, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 84, 104, 114, 101, 101, 68, 79, 114, 105, 101, 110, 116, 97, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 79, 114, 105, 101, 110, 116, 97, 116, 105, 111, 110, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 70, 114, 97, 109, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 84, 104, 114, 101, 101, 68, 70, 114, 97, 109, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 70, 114, 97, 109, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 97, 114, 116, 101, 115, 105, 97, 110, 67, 111, 111, 114, 100, 105, 110, 97, 116, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 84, 104, 114, 101, 101, 68, 67, 97, 114, 116, 101, 115, 105, 97, 110, 67, 111, 111, 114, 100, 105, 110, 97, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 79, 114, 105, 101, 110, 116, 97, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 84, 104, 114, 101, 101, 68, 79, 114, 105, 101, 110, 116, 97, 116, 105, 111, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 79, 112, 101, 110, 70, 105, 108, 101, 77, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 97, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 69, 114, 97, 115, 101, 69, 120, 105, 115, 116, 105, 110, 103, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 112, 112, 101, 110, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 56, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 73, 100, 101, 110, 116, 105, 116, 121, 67, 114, 105, 116, 101, 114, 105, 97, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 78, 97, 109, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 84, 104, 117, 109, 98, 112, 114, 105, 110, 116, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 82, 111, 108, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 71, 114, 111, 117, 112, 73, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 110, 111, 110, 121, 109, 111, 117, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 53, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 117, 116, 104, 101, 110, 116, 105, 99, 97, 116, 101, 100, 85, 115, 101, 114, 34, 32, 86, 97, 108, 117, 101, 61, 34, 54, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 73, 100, 101, 110, 116, 105, 116, 121, 77, 97, 112, 112, 105, 110, 103, 82, 117, 108, 101, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 114, 105, 116, 101, 114, 105, 97, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 73, 100, 101, 110, 116, 105, 116, 121, 67, 114, 105, 116, 101, 114, 105, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 114, 105, 116, 101, 114, 105, 97, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 117, 114, 114, 101, 110, 99, 121, 85, 110, 105, 116, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 117, 109, 101, 114, 105, 99, 67, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 120, 112, 111, 110, 101, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 66, 121, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 108, 112, 104, 97, 98, 101, 116, 105, 99, 67, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 117, 114, 114, 101, 110, 99, 121, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 84, 114, 117, 115, 116, 76, 105, 115, 116, 77, 97, 115, 107, 115, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 110, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 84, 114, 117, 115, 116, 101, 100, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 84, 114, 117, 115, 116, 101, 100, 67, 114, 108, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 73, 115, 115, 117, 101, 114, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 73, 115, 115, 117, 101, 114, 67, 114, 108, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 108, 108, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 53, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 84, 114, 117, 115, 116, 76, 105, 115, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 112, 101, 99, 105, 102, 105, 101, 100, 76, 105, 115, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 84, 114, 117, 115, 116, 101, 100, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 114, 117, 115, 116, 101, 100, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 84, 114, 117, 115, 116, 101, 100, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 84, 114, 117, 115, 116, 101, 100, 67, 114, 108, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 114, 117, 115, 116, 101, 100, 67, 114, 108, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 84, 114, 117, 115, 116, 101, 100, 67, 114, 108, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 73, 115, 115, 117, 101, 114, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 115, 115, 117, 101, 114, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 73, 115, 115, 117, 101, 114, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 73, 115, 115, 117, 101, 114, 67, 114, 108, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 115, 115, 117, 101, 114, 67, 114, 108, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 73, 115, 115, 117, 101, 114, 67, 114, 108, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 101, 99, 105, 109, 97, 108, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 99, 97, 108, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 84, 121, 112, 101, 83, 99, 104, 101, 109, 97, 72, 101, 97, 100, 101, 114, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 78, 97, 109, 101, 115, 112, 97, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 115, 112, 97, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 78, 97, 109, 101, 115, 112, 97, 99, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 116, 114, 117, 99, 116, 117, 114, 101, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 114, 117, 99, 116, 117, 114, 101, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 116, 114, 117, 99, 116, 117, 114, 101, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 69, 110, 117, 109, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 117, 109, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 69, 110, 117, 109, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 69, 110, 117, 109, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 105, 109, 112, 108, 101, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 105, 109, 112, 108, 101, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 105, 109, 112, 108, 101, 84, 121, 112, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 105, 109, 112, 108, 101, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 84, 121, 112, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 84, 121, 112, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 116, 114, 117, 99, 116, 117, 114, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 84, 121, 112, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 84, 121, 112, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 84, 121, 112, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 84, 121, 112, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 114, 117, 99, 116, 117, 114, 101, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 69, 110, 117, 109, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 84, 121, 112, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 84, 121, 112, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 84, 121, 112, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 84, 121, 112, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 117, 109, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 69, 110, 117, 109, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 117, 105, 108, 116, 73, 110, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 105, 109, 112, 108, 101, 84, 121, 112, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 84, 121, 112, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 84, 121, 112, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 84, 121, 112, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 84, 121, 112, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 97, 115, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 117, 105, 108, 116, 73, 110, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 85, 65, 66, 105, 110, 97, 114, 121, 70, 105, 108, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 84, 121, 112, 101, 83, 99, 104, 101, 109, 97, 72, 101, 97, 100, 101, 114, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 78, 97, 109, 101, 115, 112, 97, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 115, 112, 97, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 78, 97, 109, 101, 115, 112, 97, 99, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 116, 114, 117, 99, 116, 117, 114, 101, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 114, 117, 99, 116, 117, 114, 101, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 116, 114, 117, 99, 116, 117, 114, 101, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 69, 110, 117, 109, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 117, 109, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 69, 110, 117, 109, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 69, 110, 117, 109, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 105, 109, 112, 108, 101, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 105, 109, 112, 108, 101, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 105, 109, 112, 108, 101, 84, 121, 112, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 105, 109, 112, 108, 101, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 99, 104, 101, 109, 97, 76, 111, 99, 97, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 70, 105, 108, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 70, 105, 108, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 75, 101, 121, 86, 97, 108, 117, 101, 80, 97, 105, 114, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 70, 105, 108, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 111, 100, 121, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 86, 97, 114, 105, 97, 110, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 83, 117, 98, 83, 116, 97, 116, 101, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 97, 98, 108, 101, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 80, 97, 117, 115, 101, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 79, 112, 101, 114, 97, 116, 105, 111, 110, 97, 108, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 69, 114, 114, 111, 114, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 77, 101, 116, 97, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 84, 121, 112, 101, 83, 99, 104, 101, 109, 97, 72, 101, 97, 100, 101, 114, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 78, 97, 109, 101, 115, 112, 97, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 115, 112, 97, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 78, 97, 109, 101, 115, 112, 97, 99, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 116, 114, 117, 99, 116, 117, 114, 101, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 114, 117, 99, 116, 117, 114, 101, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 116, 114, 117, 99, 116, 117, 114, 101, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 69, 110, 117, 109, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 117, 109, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 69, 110, 117, 109, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 69, 110, 117, 109, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 105, 109, 112, 108, 101, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 105, 109, 112, 108, 101, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 105, 109, 112, 108, 101, 84, 121, 112, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 105, 109, 112, 108, 101, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 70, 105, 101, 108, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 70, 105, 101, 108, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 70, 105, 101, 108, 100, 77, 101, 116, 97, 68, 97, 116, 97, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 70, 105, 101, 108, 100, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 67, 108, 97, 115, 115, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 71, 117, 105, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 86, 101, 114, 115, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 86, 101, 114, 115, 105, 111, 110, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 70, 105, 101, 108, 100, 77, 101, 116, 97, 68, 97, 116, 97, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 70, 105, 101, 108, 100, 70, 108, 97, 103, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 83, 101, 116, 70, 105, 101, 108, 100, 70, 108, 97, 103, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 117, 105, 108, 116, 73, 110, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 82, 97, 110, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 120, 83, 116, 114, 105, 110, 103, 76, 101, 110, 103, 116, 104, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 70, 105, 101, 108, 100, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 71, 117, 105, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 75, 101, 121, 86, 97, 108, 117, 101, 80, 97, 105, 114, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 70, 105, 101, 108, 100, 70, 108, 97, 103, 115, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 49, 54, 34, 32, 73, 115, 79, 112, 116, 105, 111, 110, 83, 101, 116, 61, 34, 116, 114, 117, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 110, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 80, 114, 111, 109, 111, 116, 101, 100, 70, 105, 101, 108, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 86, 101, 114, 115, 105, 111, 110, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 106, 111, 114, 86, 101, 114, 115, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 105, 110, 111, 114, 86, 101, 114, 115, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 101, 100, 68, 97, 116, 97, 83, 101, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 97, 116, 97, 83, 101, 116, 70, 111, 108, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 70, 111, 108, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 97, 116, 97, 83, 101, 116, 70, 111, 108, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 77, 101, 116, 97, 68, 97, 116, 97, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 83, 101, 116, 77, 101, 116, 97, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 69, 120, 116, 101, 110, 115, 105, 111, 110, 70, 105, 101, 108, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 120, 116, 101, 110, 115, 105, 111, 110, 70, 105, 101, 108, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 75, 101, 121, 86, 97, 108, 117, 101, 80, 97, 105, 114, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 69, 120, 116, 101, 110, 115, 105, 111, 110, 70, 105, 101, 108, 100, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 83, 111, 117, 114, 99, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 101, 100, 68, 97, 116, 97, 83, 101, 116, 83, 111, 117, 114, 99, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 101, 100, 86, 97, 114, 105, 97, 98, 108, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 101, 100, 86, 97, 114, 105, 97, 98, 108, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 116, 116, 114, 105, 98, 117, 116, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 97, 109, 112, 108, 105, 110, 103, 73, 110, 116, 101, 114, 118, 97, 108, 72, 105, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 97, 100, 98, 97, 110, 100, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 97, 100, 98, 97, 110, 100, 86, 97, 108, 117, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 100, 101, 120, 82, 97, 110, 103, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 117, 98, 115, 116, 105, 116, 117, 116, 101, 86, 97, 108, 117, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 86, 97, 114, 105, 97, 110, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 77, 101, 116, 97, 68, 97, 116, 97, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 101, 116, 97, 68, 97, 116, 97, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 77, 101, 116, 97, 68, 97, 116, 97, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 101, 100, 68, 97, 116, 97, 73, 116, 101, 109, 115, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 80, 117, 98, 108, 105, 115, 104, 101, 100, 68, 97, 116, 97, 83, 101, 116, 83, 111, 117, 114, 99, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 80, 117, 98, 108, 105, 115, 104, 101, 100, 68, 97, 116, 97, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 101, 100, 68, 97, 116, 97, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 80, 117, 98, 108, 105, 115, 104, 101, 100, 86, 97, 114, 105, 97, 98, 108, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 80, 117, 98, 108, 105, 115, 104, 101, 100, 68, 97, 116, 97, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 101, 100, 69, 118, 101, 110, 116, 115, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 80, 117, 98, 108, 105, 115, 104, 101, 100, 68, 97, 116, 97, 83, 101, 116, 83, 111, 117, 114, 99, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 118, 101, 110, 116, 78, 111, 116, 105, 102, 105, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 101, 108, 101, 99, 116, 101, 100, 70, 105, 101, 108, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 108, 101, 99, 116, 101, 100, 70, 105, 101, 108, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 105, 109, 112, 108, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 79, 112, 101, 114, 97, 110, 100, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 101, 108, 101, 99, 116, 101, 100, 70, 105, 101, 108, 100, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 70, 105, 108, 116, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 67, 111, 110, 116, 101, 110, 116, 70, 105, 108, 116, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 70, 105, 101, 108, 100, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 32, 73, 115, 79, 112, 116, 105, 111, 110, 83, 101, 116, 61, 34, 116, 114, 117, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 110, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 111, 117, 114, 99, 101, 84, 105, 109, 101, 115, 116, 97, 109, 112, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 84, 105, 109, 101, 115, 116, 97, 109, 112, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 111, 117, 114, 99, 101, 80, 105, 99, 111, 83, 101, 99, 111, 110, 100, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 80, 105, 99, 111, 83, 101, 99, 111, 110, 100, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 82, 97, 119, 68, 97, 116, 97, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 87, 114, 105, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 97, 98, 108, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 87, 114, 105, 116, 101, 114, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 70, 105, 101, 108, 100, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 83, 101, 116, 70, 105, 101, 108, 100, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 75, 101, 121, 70, 114, 97, 109, 101, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 97, 116, 97, 83, 101, 116, 87, 114, 105, 116, 101, 114, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 87, 114, 105, 116, 101, 114, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 75, 101, 121, 86, 97, 108, 117, 101, 80, 97, 105, 114, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 97, 116, 97, 83, 101, 116, 87, 114, 105, 116, 101, 114, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 114, 97, 110, 115, 112, 111, 114, 116, 83, 101, 116, 116, 105, 110, 103, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 101, 115, 115, 97, 103, 101, 83, 101, 116, 116, 105, 110, 103, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 87, 114, 105, 116, 101, 114, 84, 114, 97, 110, 115, 112, 111, 114, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 87, 114, 105, 116, 101, 114, 77, 101, 115, 115, 97, 103, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 83, 117, 98, 71, 114, 111, 117, 112, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 97, 98, 108, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 77, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 77, 101, 115, 115, 97, 103, 101, 83, 101, 99, 117, 114, 105, 116, 121, 77, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 71, 114, 111, 117, 112, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 101, 99, 117, 114, 105, 116, 121, 75, 101, 121, 83, 101, 114, 118, 105, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 75, 101, 121, 83, 101, 114, 118, 105, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 69, 110, 100, 112, 111, 105, 110, 116, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 101, 99, 117, 114, 105, 116, 121, 75, 101, 121, 83, 101, 114, 118, 105, 99, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 120, 78, 101, 116, 119, 111, 114, 107, 77, 101, 115, 115, 97, 103, 101, 83, 105, 122, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 71, 114, 111, 117, 112, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 71, 114, 111, 117, 112, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 75, 101, 121, 86, 97, 108, 117, 101, 80, 97, 105, 114, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 71, 114, 111, 117, 112, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 114, 71, 114, 111, 117, 112, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 80, 117, 98, 83, 117, 98, 71, 114, 111, 117, 112, 68, 97, 116, 97, 84, 121, 112, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 80, 117, 98, 83, 117, 98, 71, 114, 111, 117, 112, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 97, 98, 108, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 80, 117, 98, 83, 117, 98, 71, 114, 111, 117, 112, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 77, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 77, 101, 115, 115, 97, 103, 101, 83, 101, 99, 117, 114, 105, 116, 121, 77, 111, 100, 101, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 80, 117, 98, 83, 117, 98, 71, 114, 111, 117, 112, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 71, 114, 111, 117, 112, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 80, 117, 98, 83, 117, 98, 71, 114, 111, 117, 112, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 101, 99, 117, 114, 105, 116, 121, 75, 101, 121, 83, 101, 114, 118, 105, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 75, 101, 121, 83, 101, 114, 118, 105, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 69, 110, 100, 112, 111, 105, 110, 116, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 101, 99, 117, 114, 105, 116, 121, 75, 101, 121, 83, 101, 114, 118, 105, 99, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 120, 78, 101, 116, 119, 111, 114, 107, 77, 101, 115, 115, 97, 103, 101, 83, 105, 122, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 80, 117, 98, 83, 117, 98, 71, 114, 111, 117, 112, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 71, 114, 111, 117, 112, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 71, 114, 111, 117, 112, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 75, 101, 121, 86, 97, 108, 117, 101, 80, 97, 105, 114, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 71, 114, 111, 117, 112, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 114, 71, 114, 111, 117, 112, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 105, 110, 103, 73, 110, 116, 101, 114, 118, 97, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 75, 101, 101, 112, 65, 108, 105, 118, 101, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 114, 105, 111, 114, 105, 116, 121, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 76, 111, 99, 97, 108, 101, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 111, 99, 97, 108, 101, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 76, 111, 99, 97, 108, 101, 73, 100, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 72, 101, 97, 100, 101, 114, 76, 97, 121, 111, 117, 116, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 114, 97, 110, 115, 112, 111, 114, 116, 83, 101, 116, 116, 105, 110, 103, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 101, 115, 115, 97, 103, 101, 83, 101, 116, 116, 105, 110, 103, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 97, 116, 97, 83, 101, 116, 87, 114, 105, 116, 101, 114, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 87, 114, 105, 116, 101, 114, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 83, 101, 116, 87, 114, 105, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 97, 116, 97, 83, 101, 116, 87, 114, 105, 116, 101, 114, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 114, 71, 114, 111, 117, 112, 84, 114, 97, 110, 115, 112, 111, 114, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 114, 71, 114, 111, 117, 112, 77, 101, 115, 115, 97, 103, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 83, 117, 98, 67, 111, 110, 110, 101, 99, 116, 105, 111, 110, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 97, 98, 108, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 101, 114, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 86, 97, 114, 105, 97, 110, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 114, 97, 110, 115, 112, 111, 114, 116, 80, 114, 111, 102, 105, 108, 101, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 100, 100, 114, 101, 115, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 67, 111, 110, 110, 101, 99, 116, 105, 111, 110, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 111, 110, 110, 101, 99, 116, 105, 111, 110, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 75, 101, 121, 86, 97, 108, 117, 101, 80, 97, 105, 114, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 67, 111, 110, 110, 101, 99, 116, 105, 111, 110, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 114, 97, 110, 115, 112, 111, 114, 116, 83, 101, 116, 116, 105, 110, 103, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 87, 114, 105, 116, 101, 114, 71, 114, 111, 117, 112, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 114, 71, 114, 111, 117, 112, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 87, 114, 105, 116, 101, 114, 71, 114, 111, 117, 112, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 87, 114, 105, 116, 101, 114, 71, 114, 111, 117, 112, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 97, 100, 101, 114, 71, 114, 111, 117, 112, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 97, 100, 101, 114, 71, 114, 111, 117, 112, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 97, 100, 101, 114, 71, 114, 111, 117, 112, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 97, 100, 101, 114, 71, 114, 111, 117, 112, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 111, 110, 110, 101, 99, 116, 105, 111, 110, 84, 114, 97, 110, 115, 112, 111, 114, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 78, 101, 116, 119, 111, 114, 107, 65, 100, 100, 114, 101, 115, 115, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 101, 116, 119, 111, 114, 107, 73, 110, 116, 101, 114, 102, 97, 99, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 78, 101, 116, 119, 111, 114, 107, 65, 100, 100, 114, 101, 115, 115, 85, 114, 108, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 101, 116, 119, 111, 114, 107, 65, 100, 100, 114, 101, 115, 115, 68, 97, 116, 97, 84, 121, 112, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 101, 116, 119, 111, 114, 107, 73, 110, 116, 101, 114, 102, 97, 99, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 101, 116, 119, 111, 114, 107, 65, 100, 100, 114, 101, 115, 115, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 114, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 97, 100, 101, 114, 71, 114, 111, 117, 112, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 80, 117, 98, 83, 117, 98, 71, 114, 111, 117, 112, 68, 97, 116, 97, 84, 121, 112, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 80, 117, 98, 83, 117, 98, 71, 114, 111, 117, 112, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 97, 98, 108, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 80, 117, 98, 83, 117, 98, 71, 114, 111, 117, 112, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 77, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 77, 101, 115, 115, 97, 103, 101, 83, 101, 99, 117, 114, 105, 116, 121, 77, 111, 100, 101, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 80, 117, 98, 83, 117, 98, 71, 114, 111, 117, 112, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 71, 114, 111, 117, 112, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 80, 117, 98, 83, 117, 98, 71, 114, 111, 117, 112, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 101, 99, 117, 114, 105, 116, 121, 75, 101, 121, 83, 101, 114, 118, 105, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 75, 101, 121, 83, 101, 114, 118, 105, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 69, 110, 100, 112, 111, 105, 110, 116, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 101, 99, 117, 114, 105, 116, 121, 75, 101, 121, 83, 101, 114, 118, 105, 99, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 120, 78, 101, 116, 119, 111, 114, 107, 77, 101, 115, 115, 97, 103, 101, 83, 105, 122, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 80, 117, 98, 83, 117, 98, 71, 114, 111, 117, 112, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 71, 114, 111, 117, 112, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 71, 114, 111, 117, 112, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 75, 101, 121, 86, 97, 108, 117, 101, 80, 97, 105, 114, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 71, 114, 111, 117, 112, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 114, 97, 110, 115, 112, 111, 114, 116, 83, 101, 116, 116, 105, 110, 103, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 101, 115, 115, 97, 103, 101, 83, 101, 116, 116, 105, 110, 103, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 97, 116, 97, 83, 101, 116, 82, 101, 97, 100, 101, 114, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 82, 101, 97, 100, 101, 114, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 83, 101, 116, 82, 101, 97, 100, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 97, 116, 97, 83, 101, 116, 82, 101, 97, 100, 101, 114, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 97, 100, 101, 114, 71, 114, 111, 117, 112, 84, 114, 97, 110, 115, 112, 111, 114, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 97, 100, 101, 114, 71, 114, 111, 117, 112, 77, 101, 115, 115, 97, 103, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 82, 101, 97, 100, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 97, 98, 108, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 101, 114, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 86, 97, 114, 105, 97, 110, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 114, 71, 114, 111, 117, 112, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 87, 114, 105, 116, 101, 114, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 77, 101, 116, 97, 68, 97, 116, 97, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 83, 101, 116, 77, 101, 116, 97, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 70, 105, 101, 108, 100, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 83, 101, 116, 70, 105, 101, 108, 100, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 101, 115, 115, 97, 103, 101, 82, 101, 99, 101, 105, 118, 101, 84, 105, 109, 101, 111, 117, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 75, 101, 121, 70, 114, 97, 109, 101, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 72, 101, 97, 100, 101, 114, 76, 97, 121, 111, 117, 116, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 77, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 77, 101, 115, 115, 97, 103, 101, 83, 101, 99, 117, 114, 105, 116, 121, 77, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 71, 114, 111, 117, 112, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 101, 99, 117, 114, 105, 116, 121, 75, 101, 121, 83, 101, 114, 118, 105, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 75, 101, 121, 83, 101, 114, 118, 105, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 69, 110, 100, 112, 111, 105, 110, 116, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 101, 99, 117, 114, 105, 116, 121, 75, 101, 121, 83, 101, 114, 118, 105, 99, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 97, 116, 97, 83, 101, 116, 82, 101, 97, 100, 101, 114, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 82, 101, 97, 100, 101, 114, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 75, 101, 121, 86, 97, 108, 117, 101, 80, 97, 105, 114, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 97, 116, 97, 83, 101, 116, 82, 101, 97, 100, 101, 114, 80, 114, 111, 112, 101, 114, 116, 105, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 114, 97, 110, 115, 112, 111, 114, 116, 83, 101, 116, 116, 105, 110, 103, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 101, 115, 115, 97, 103, 101, 83, 101, 116, 116, 105, 110, 103, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 117, 98, 115, 99, 114, 105, 98, 101, 100, 68, 97, 116, 97, 83, 101, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 82, 101, 97, 100, 101, 114, 84, 114, 97, 110, 115, 112, 111, 114, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 82, 101, 97, 100, 101, 114, 77, 101, 115, 115, 97, 103, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 117, 98, 115, 99, 114, 105, 98, 101, 100, 68, 97, 116, 97, 83, 101, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 84, 97, 114, 103, 101, 116, 86, 97, 114, 105, 97, 98, 108, 101, 115, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 83, 117, 98, 115, 99, 114, 105, 98, 101, 100, 68, 97, 116, 97, 83, 101, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 84, 97, 114, 103, 101, 116, 86, 97, 114, 105, 97, 98, 108, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 97, 114, 103, 101, 116, 86, 97, 114, 105, 97, 98, 108, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 70, 105, 101, 108, 100, 84, 97, 114, 103, 101, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 84, 97, 114, 103, 101, 116, 86, 97, 114, 105, 97, 98, 108, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 70, 105, 101, 108, 100, 84, 97, 114, 103, 101, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 70, 105, 101, 108, 100, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 71, 117, 105, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 99, 101, 105, 118, 101, 114, 73, 110, 100, 101, 120, 82, 97, 110, 103, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 97, 114, 103, 101, 116, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 116, 116, 114, 105, 98, 117, 116, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 73, 110, 100, 101, 120, 82, 97, 110, 103, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 79, 118, 101, 114, 114, 105, 100, 101, 86, 97, 108, 117, 101, 72, 97, 110, 100, 108, 105, 110, 103, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 79, 118, 101, 114, 114, 105, 100, 101, 86, 97, 108, 117, 101, 72, 97, 110, 100, 108, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 79, 118, 101, 114, 114, 105, 100, 101, 86, 97, 108, 117, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 86, 97, 114, 105, 97, 110, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 79, 118, 101, 114, 114, 105, 100, 101, 86, 97, 108, 117, 101, 72, 97, 110, 100, 108, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 97, 98, 108, 101, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 76, 97, 115, 116, 85, 115, 97, 98, 108, 101, 86, 97, 108, 117, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 79, 118, 101, 114, 114, 105, 100, 101, 86, 97, 108, 117, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 117, 98, 115, 99, 114, 105, 98, 101, 100, 68, 97, 116, 97, 83, 101, 116, 77, 105, 114, 114, 111, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 83, 117, 98, 115, 99, 114, 105, 98, 101, 100, 68, 97, 116, 97, 83, 101, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 97, 114, 101, 110, 116, 78, 111, 100, 101, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 83, 117, 98, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 80, 117, 98, 108, 105, 115, 104, 101, 100, 68, 97, 116, 97, 83, 101, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 101, 100, 68, 97, 116, 97, 83, 101, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 80, 117, 98, 108, 105, 115, 104, 101, 100, 68, 97, 116, 97, 83, 101, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 80, 117, 98, 108, 105, 115, 104, 101, 100, 68, 97, 116, 97, 83, 101, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 67, 111, 110, 110, 101, 99, 116, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 111, 110, 110, 101, 99, 116, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 80, 117, 98, 83, 117, 98, 67, 111, 110, 110, 101, 99, 116, 105, 111, 110, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 67, 111, 110, 110, 101, 99, 116, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 97, 98, 108, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 79, 114, 100, 101, 114, 105, 110, 103, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 85, 110, 100, 101, 102, 105, 110, 101, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 115, 99, 101, 110, 100, 105, 110, 103, 87, 114, 105, 116, 101, 114, 73, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 115, 99, 101, 110, 100, 105, 110, 103, 87, 114, 105, 116, 101, 114, 73, 100, 83, 105, 110, 103, 108, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 85, 97, 100, 112, 78, 101, 116, 119, 111, 114, 107, 77, 101, 115, 115, 97, 103, 101, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 32, 73, 115, 79, 112, 116, 105, 111, 110, 83, 101, 116, 61, 34, 116, 114, 117, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 110, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 101, 114, 73, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 71, 114, 111, 117, 112, 72, 101, 97, 100, 101, 114, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 114, 71, 114, 111, 117, 112, 73, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 71, 114, 111, 117, 112, 86, 101, 114, 115, 105, 111, 110, 34, 32, 86, 97, 108, 117, 101, 61, 34, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 101, 116, 119, 111, 114, 107, 77, 101, 115, 115, 97, 103, 101, 78, 117, 109, 98, 101, 114, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 113, 117, 101, 110, 99, 101, 78, 117, 109, 98, 101, 114, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 80, 97, 121, 108, 111, 97, 100, 72, 101, 97, 100, 101, 114, 34, 32, 86, 97, 108, 117, 101, 61, 34, 54, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 84, 105, 109, 101, 115, 116, 97, 109, 112, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 50, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 80, 105, 99, 111, 83, 101, 99, 111, 110, 100, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 53, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 67, 108, 97, 115, 115, 73, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 53, 49, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 80, 114, 111, 109, 111, 116, 101, 100, 70, 105, 101, 108, 100, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 48, 50, 52, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 85, 97, 100, 112, 87, 114, 105, 116, 101, 114, 71, 114, 111, 117, 112, 77, 101, 115, 115, 97, 103, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 87, 114, 105, 116, 101, 114, 71, 114, 111, 117, 112, 77, 101, 115, 115, 97, 103, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 71, 114, 111, 117, 112, 86, 101, 114, 115, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 79, 114, 100, 101, 114, 105, 110, 103, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 83, 101, 116, 79, 114, 100, 101, 114, 105, 110, 103, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 101, 116, 119, 111, 114, 107, 77, 101, 115, 115, 97, 103, 101, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 85, 97, 100, 112, 78, 101, 116, 119, 111, 114, 107, 77, 101, 115, 115, 97, 103, 101, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 97, 109, 112, 108, 105, 110, 103, 79, 102, 102, 115, 101, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 80, 117, 98, 108, 105, 115, 104, 105, 110, 103, 79, 102, 102, 115, 101, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 105, 110, 103, 79, 102, 102, 115, 101, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 80, 117, 98, 108, 105, 115, 104, 105, 110, 103, 79, 102, 102, 115, 101, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 85, 97, 100, 112, 68, 97, 116, 97, 83, 101, 116, 77, 101, 115, 115, 97, 103, 101, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 32, 73, 115, 79, 112, 116, 105, 111, 110, 83, 101, 116, 61, 34, 116, 114, 117, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 110, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 84, 105, 109, 101, 115, 116, 97, 109, 112, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 80, 105, 99, 111, 83, 101, 99, 111, 110, 100, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 77, 97, 106, 111, 114, 86, 101, 114, 115, 105, 111, 110, 34, 32, 86, 97, 108, 117, 101, 61, 34, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 77, 105, 110, 111, 114, 86, 101, 114, 115, 105, 111, 110, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 113, 117, 101, 110, 99, 101, 78, 117, 109, 98, 101, 114, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 85, 97, 100, 112, 68, 97, 116, 97, 83, 101, 116, 87, 114, 105, 116, 101, 114, 77, 101, 115, 115, 97, 103, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 83, 101, 116, 87, 114, 105, 116, 101, 114, 77, 101, 115, 115, 97, 103, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 77, 101, 115, 115, 97, 103, 101, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 85, 97, 100, 112, 68, 97, 116, 97, 83, 101, 116, 77, 101, 115, 115, 97, 103, 101, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 111, 110, 102, 105, 103, 117, 114, 101, 100, 83, 105, 122, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 101, 116, 119, 111, 114, 107, 77, 101, 115, 115, 97, 103, 101, 78, 117, 109, 98, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 79, 102, 102, 115, 101, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 85, 97, 100, 112, 68, 97, 116, 97, 83, 101, 116, 82, 101, 97, 100, 101, 114, 77, 101, 115, 115, 97, 103, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 83, 101, 116, 82, 101, 97, 100, 101, 114, 77, 101, 115, 115, 97, 103, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 71, 114, 111, 117, 112, 86, 101, 114, 115, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 101, 116, 119, 111, 114, 107, 77, 101, 115, 115, 97, 103, 101, 78, 117, 109, 98, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 79, 102, 102, 115, 101, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 67, 108, 97, 115, 115, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 71, 117, 105, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 101, 116, 119, 111, 114, 107, 77, 101, 115, 115, 97, 103, 101, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 85, 97, 100, 112, 78, 101, 116, 119, 111, 114, 107, 77, 101, 115, 115, 97, 103, 101, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 77, 101, 115, 115, 97, 103, 101, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 85, 97, 100, 112, 68, 97, 116, 97, 83, 101, 116, 77, 101, 115, 115, 97, 103, 101, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 105, 110, 103, 73, 110, 116, 101, 114, 118, 97, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 99, 101, 105, 118, 101, 79, 102, 102, 115, 101, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 114, 111, 99, 101, 115, 115, 105, 110, 103, 79, 102, 102, 115, 101, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 74, 115, 111, 110, 78, 101, 116, 119, 111, 114, 107, 77, 101, 115, 115, 97, 103, 101, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 32, 73, 115, 79, 112, 116, 105, 111, 110, 83, 101, 116, 61, 34, 116, 114, 117, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 110, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 101, 116, 119, 111, 114, 107, 77, 101, 115, 115, 97, 103, 101, 72, 101, 97, 100, 101, 114, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 77, 101, 115, 115, 97, 103, 101, 72, 101, 97, 100, 101, 114, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 105, 110, 103, 108, 101, 68, 97, 116, 97, 83, 101, 116, 77, 101, 115, 115, 97, 103, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 101, 114, 73, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 67, 108, 97, 115, 115, 73, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 112, 108, 121, 84, 111, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 74, 115, 111, 110, 87, 114, 105, 116, 101, 114, 71, 114, 111, 117, 112, 77, 101, 115, 115, 97, 103, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 87, 114, 105, 116, 101, 114, 71, 114, 111, 117, 112, 77, 101, 115, 115, 97, 103, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 101, 116, 119, 111, 114, 107, 77, 101, 115, 115, 97, 103, 101, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 74, 115, 111, 110, 78, 101, 116, 119, 111, 114, 107, 77, 101, 115, 115, 97, 103, 101, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 74, 115, 111, 110, 68, 97, 116, 97, 83, 101, 116, 77, 101, 115, 115, 97, 103, 101, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 32, 73, 115, 79, 112, 116, 105, 111, 110, 83, 101, 116, 61, 34, 116, 114, 117, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 110, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 87, 114, 105, 116, 101, 114, 73, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 77, 101, 116, 97, 68, 97, 116, 97, 86, 101, 114, 115, 105, 111, 110, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 113, 117, 101, 110, 99, 101, 78, 117, 109, 98, 101, 114, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 84, 105, 109, 101, 115, 116, 97, 109, 112, 34, 32, 86, 97, 108, 117, 101, 61, 34, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 74, 115, 111, 110, 68, 97, 116, 97, 83, 101, 116, 87, 114, 105, 116, 101, 114, 77, 101, 115, 115, 97, 103, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 83, 101, 116, 87, 114, 105, 116, 101, 114, 77, 101, 115, 115, 97, 103, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 77, 101, 115, 115, 97, 103, 101, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 74, 115, 111, 110, 68, 97, 116, 97, 83, 101, 116, 77, 101, 115, 115, 97, 103, 101, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 74, 115, 111, 110, 68, 97, 116, 97, 83, 101, 116, 82, 101, 97, 100, 101, 114, 77, 101, 115, 115, 97, 103, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 83, 101, 116, 82, 101, 97, 100, 101, 114, 77, 101, 115, 115, 97, 103, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 101, 116, 119, 111, 114, 107, 77, 101, 115, 115, 97, 103, 101, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 74, 115, 111, 110, 78, 101, 116, 119, 111, 114, 107, 77, 101, 115, 115, 97, 103, 101, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 101, 116, 77, 101, 115, 115, 97, 103, 101, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 74, 115, 111, 110, 68, 97, 116, 97, 83, 101, 116, 77, 101, 115, 115, 97, 103, 101, 67, 111, 110, 116, 101, 110, 116, 77, 97, 115, 107, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 103, 114, 97, 109, 67, 111, 110, 110, 101, 99, 116, 105, 111, 110, 84, 114, 97, 110, 115, 112, 111, 114, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 67, 111, 110, 110, 101, 99, 116, 105, 111, 110, 84, 114, 97, 110, 115, 112, 111, 114, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 99, 111, 118, 101, 114, 121, 65, 100, 100, 114, 101, 115, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 103, 114, 97, 109, 87, 114, 105, 116, 101, 114, 71, 114, 111, 117, 112, 84, 114, 97, 110, 115, 112, 111, 114, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 87, 114, 105, 116, 101, 114, 71, 114, 111, 117, 112, 84, 114, 97, 110, 115, 112, 111, 114, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 101, 115, 115, 97, 103, 101, 82, 101, 112, 101, 97, 116, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 101, 115, 115, 97, 103, 101, 82, 101, 112, 101, 97, 116, 68, 101, 108, 97, 121, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 107, 101, 114, 67, 111, 110, 110, 101, 99, 116, 105, 111, 110, 84, 114, 97, 110, 115, 112, 111, 114, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 67, 111, 110, 110, 101, 99, 116, 105, 111, 110, 84, 114, 97, 110, 115, 112, 111, 114, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 111, 117, 114, 99, 101, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 117, 116, 104, 101, 110, 116, 105, 99, 97, 116, 105, 111, 110, 80, 114, 111, 102, 105, 108, 101, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 107, 101, 114, 84, 114, 97, 110, 115, 112, 111, 114, 116, 81, 117, 97, 108, 105, 116, 121, 79, 102, 83, 101, 114, 118, 105, 99, 101, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 116, 83, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 66, 101, 115, 116, 69, 102, 102, 111, 114, 116, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 116, 76, 101, 97, 115, 116, 79, 110, 99, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 116, 77, 111, 115, 116, 79, 110, 99, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 69, 120, 97, 99, 116, 108, 121, 79, 110, 99, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 107, 101, 114, 87, 114, 105, 116, 101, 114, 71, 114, 111, 117, 112, 84, 114, 97, 110, 115, 112, 111, 114, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 87, 114, 105, 116, 101, 114, 71, 114, 111, 117, 112, 84, 114, 97, 110, 115, 112, 111, 114, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 81, 117, 101, 117, 101, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 111, 117, 114, 99, 101, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 117, 116, 104, 101, 110, 116, 105, 99, 97, 116, 105, 111, 110, 80, 114, 111, 102, 105, 108, 101, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 101, 100, 68, 101, 108, 105, 118, 101, 114, 121, 71, 117, 97, 114, 97, 110, 116, 101, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 66, 114, 111, 107, 101, 114, 84, 114, 97, 110, 115, 112, 111, 114, 116, 81, 117, 97, 108, 105, 116, 121, 79, 102, 83, 101, 114, 118, 105, 99, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 107, 101, 114, 68, 97, 116, 97, 83, 101, 116, 87, 114, 105, 116, 101, 114, 84, 114, 97, 110, 115, 112, 111, 114, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 83, 101, 116, 87, 114, 105, 116, 101, 114, 84, 114, 97, 110, 115, 112, 111, 114, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 81, 117, 101, 117, 101, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 111, 117, 114, 99, 101, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 117, 116, 104, 101, 110, 116, 105, 99, 97, 116, 105, 111, 110, 80, 114, 111, 102, 105, 108, 101, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 101, 100, 68, 101, 108, 105, 118, 101, 114, 121, 71, 117, 97, 114, 97, 110, 116, 101, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 66, 114, 111, 107, 101, 114, 84, 114, 97, 110, 115, 112, 111, 114, 116, 81, 117, 97, 108, 105, 116, 121, 79, 102, 83, 101, 114, 118, 105, 99, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 101, 116, 97, 68, 97, 116, 97, 81, 117, 101, 117, 101, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 101, 116, 97, 68, 97, 116, 97, 85, 112, 100, 97, 116, 101, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 107, 101, 114, 68, 97, 116, 97, 83, 101, 116, 82, 101, 97, 100, 101, 114, 84, 114, 97, 110, 115, 112, 111, 114, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 83, 101, 116, 82, 101, 97, 100, 101, 114, 84, 114, 97, 110, 115, 112, 111, 114, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 81, 117, 101, 117, 101, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 111, 117, 114, 99, 101, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 117, 116, 104, 101, 110, 116, 105, 99, 97, 116, 105, 111, 110, 80, 114, 111, 102, 105, 108, 101, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 101, 100, 68, 101, 108, 105, 118, 101, 114, 121, 71, 117, 97, 114, 97, 110, 116, 101, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 66, 114, 111, 107, 101, 114, 84, 114, 97, 110, 115, 112, 111, 114, 116, 81, 117, 97, 108, 105, 116, 121, 79, 102, 83, 101, 114, 118, 105, 99, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 101, 116, 97, 68, 97, 116, 97, 81, 117, 101, 117, 101, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 115, 76, 101, 118, 101, 108, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 66, 97, 115, 105, 99, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 100, 118, 97, 110, 99, 101, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 73, 110, 102, 111, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 76, 111, 103, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 68, 101, 98, 117, 103, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 83, 117, 98, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 115, 67, 111, 117, 110, 116, 101, 114, 67, 108, 97, 115, 115, 105, 102, 105, 99, 97, 116, 105, 111, 110, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 73, 110, 102, 111, 114, 109, 97, 116, 105, 111, 110, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 69, 114, 114, 111, 114, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 108, 105, 97, 115, 78, 97, 109, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 108, 105, 97, 115, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 100, 78, 111, 100, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 100, 78, 111, 100, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 112, 97, 110, 100, 101, 100, 78, 111, 100, 101, 73, 100, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 100, 78, 111, 100, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 73, 100, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 117, 109, 101, 114, 105, 99, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 116, 114, 105, 110, 103, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 71, 117, 105, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 79, 112, 97, 113, 117, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 85, 110, 115, 112, 101, 99, 105, 102, 105, 101, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 79, 98, 106, 101, 99, 116, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 86, 97, 114, 105, 97, 98, 108, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 77, 101, 116, 104, 111, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 79, 98, 106, 101, 99, 116, 84, 121, 112, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 86, 97, 114, 105, 97, 98, 108, 101, 84, 121, 112, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 84, 121, 112, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 54, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 86, 105, 101, 119, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 50, 56, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 32, 73, 115, 79, 112, 116, 105, 111, 110, 83, 101, 116, 61, 34, 116, 114, 117, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 110, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 97, 100, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 72, 105, 115, 116, 111, 114, 105, 122, 105, 110, 103, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 97, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 54, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 97, 100, 72, 105, 115, 116, 111, 114, 121, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 50, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 73, 110, 115, 101, 114, 116, 72, 105, 115, 116, 111, 114, 121, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 53, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 77, 111, 100, 105, 102, 121, 72, 105, 115, 116, 111, 114, 121, 34, 32, 86, 97, 108, 117, 101, 61, 34, 53, 49, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 68, 101, 108, 101, 116, 101, 72, 105, 115, 116, 111, 114, 121, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 48, 50, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 99, 101, 105, 118, 101, 69, 118, 101, 110, 116, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 48, 52, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 67, 97, 108, 108, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 48, 57, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 100, 100, 82, 101, 102, 101, 114, 101, 110, 99, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 56, 49, 57, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 109, 111, 118, 101, 82, 101, 102, 101, 114, 101, 110, 99, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 54, 51, 56, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 68, 101, 108, 101, 116, 101, 78, 111, 100, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 50, 55, 54, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 100, 100, 78, 111, 100, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 54, 53, 53, 51, 54, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 99, 99, 101, 115, 115, 76, 101, 118, 101, 108, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 56, 34, 32, 73, 115, 79, 112, 116, 105, 111, 110, 83, 101, 116, 61, 34, 116, 114, 117, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 110, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 67, 117, 114, 114, 101, 110, 116, 82, 101, 97, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 67, 117, 114, 114, 101, 110, 116, 87, 114, 105, 116, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 82, 101, 97, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 87, 114, 105, 116, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 109, 97, 110, 116, 105, 99, 67, 104, 97, 110, 103, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 87, 114, 105, 116, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 84, 105, 109, 101, 115, 116, 97, 109, 112, 87, 114, 105, 116, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 54, 52, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 99, 99, 101, 115, 115, 76, 101, 118, 101, 108, 69, 120, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 32, 73, 115, 79, 112, 116, 105, 111, 110, 83, 101, 116, 61, 34, 116, 114, 117, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 110, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 67, 117, 114, 114, 101, 110, 116, 82, 101, 97, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 67, 117, 114, 114, 101, 110, 116, 87, 114, 105, 116, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 82, 101, 97, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 87, 114, 105, 116, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 109, 97, 110, 116, 105, 99, 67, 104, 97, 110, 103, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 87, 114, 105, 116, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 84, 105, 109, 101, 115, 116, 97, 109, 112, 87, 114, 105, 116, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 54, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 110, 97, 116, 111, 109, 105, 99, 82, 101, 97, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 53, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 110, 97, 116, 111, 109, 105, 99, 87, 114, 105, 116, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 53, 49, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 70, 117, 108, 108, 65, 114, 114, 97, 121, 79, 110, 108, 121, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 48, 50, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 83, 117, 98, 68, 97, 116, 97, 84, 121, 112, 101, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 48, 52, 56, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 69, 118, 101, 110, 116, 78, 111, 116, 105, 102, 105, 101, 114, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 56, 34, 32, 73, 115, 79, 112, 116, 105, 111, 110, 83, 101, 116, 61, 34, 116, 114, 117, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 110, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 117, 98, 115, 99, 114, 105, 98, 101, 84, 111, 69, 118, 101, 110, 116, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 82, 101, 97, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 87, 114, 105, 116, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 56, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 99, 99, 101, 115, 115, 82, 101, 115, 116, 114, 105, 99, 116, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 49, 54, 34, 32, 73, 115, 79, 112, 116, 105, 111, 110, 83, 101, 116, 61, 34, 116, 114, 117, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 110, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 105, 103, 110, 105, 110, 103, 82, 101, 113, 117, 105, 114, 101, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 69, 110, 99, 114, 121, 112, 116, 105, 111, 110, 82, 101, 113, 117, 105, 114, 101, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 115, 115, 105, 111, 110, 82, 101, 113, 117, 105, 114, 101, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 112, 112, 108, 121, 82, 101, 115, 116, 114, 105, 99, 116, 105, 111, 110, 115, 84, 111, 66, 114, 111, 119, 115, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 56, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 111, 108, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 116, 114, 117, 99, 116, 117, 114, 101, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 116, 114, 117, 99, 116, 117, 114, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 116, 114, 117, 99, 116, 117, 114, 101, 87, 105, 116, 104, 79, 112, 116, 105, 111, 110, 97, 108, 70, 105, 101, 108, 100, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 85, 110, 105, 111, 110, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 116, 114, 117, 99, 116, 117, 114, 101, 70, 105, 101, 108, 100, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 82, 97, 110, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 120, 83, 116, 114, 105, 110, 103, 76, 101, 110, 103, 116, 104, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 115, 79, 112, 116, 105, 111, 110, 97, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 116, 114, 117, 99, 116, 117, 114, 101, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 84, 121, 112, 101, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 102, 97, 117, 108, 116, 69, 110, 99, 111, 100, 105, 110, 103, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 97, 115, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 114, 117, 99, 116, 117, 114, 101, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 70, 105, 101, 108, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 70, 105, 101, 108, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 70, 105, 101, 108, 100, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 70, 105, 101, 108, 100, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 69, 110, 117, 109, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 84, 121, 112, 101, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 70, 105, 101, 108, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 70, 105, 101, 108, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 69, 110, 117, 109, 70, 105, 101, 108, 100, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 70, 105, 101, 108, 100, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 99, 99, 101, 115, 115, 82, 101, 115, 116, 114, 105, 99, 116, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 102, 101, 114, 101, 110, 99, 101, 78, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 73, 110, 115, 116, 97, 110, 99, 101, 78, 111, 100, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 99, 99, 101, 115, 115, 82, 101, 115, 116, 114, 105, 99, 116, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 102, 101, 114, 101, 110, 99, 101, 78, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 84, 121, 112, 101, 78, 111, 100, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 99, 99, 101, 115, 115, 82, 101, 115, 116, 114, 105, 99, 116, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 102, 101, 114, 101, 110, 99, 101, 78, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 79, 98, 106, 101, 99, 116, 78, 111, 100, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 73, 110, 115, 116, 97, 110, 99, 101, 78, 111, 100, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 99, 99, 101, 115, 115, 82, 101, 115, 116, 114, 105, 99, 116, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 102, 101, 114, 101, 110, 99, 101, 78, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 118, 101, 110, 116, 78, 111, 116, 105, 102, 105, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 79, 98, 106, 101, 99, 116, 84, 121, 112, 101, 78, 111, 100, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 84, 121, 112, 101, 78, 111, 100, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 99, 99, 101, 115, 115, 82, 101, 115, 116, 114, 105, 99, 116, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 102, 101, 114, 101, 110, 99, 101, 78, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 115, 65, 98, 115, 116, 114, 97, 99, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 86, 97, 114, 105, 97, 98, 108, 101, 78, 111, 100, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 73, 110, 115, 116, 97, 110, 99, 101, 78, 111, 100, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 99, 99, 101, 115, 115, 82, 101, 115, 116, 114, 105, 99, 116, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 102, 101, 114, 101, 110, 99, 101, 78, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 86, 97, 114, 105, 97, 110, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 82, 97, 110, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 99, 99, 101, 115, 115, 76, 101, 118, 101, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 65, 99, 99, 101, 115, 115, 76, 101, 118, 101, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 105, 110, 105, 109, 117, 109, 83, 97, 109, 112, 108, 105, 110, 103, 73, 110, 116, 101, 114, 118, 97, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 105, 122, 105, 110, 103, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 99, 99, 101, 115, 115, 76, 101, 118, 101, 108, 69, 120, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 86, 97, 114, 105, 97, 98, 108, 101, 84, 121, 112, 101, 78, 111, 100, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 84, 121, 112, 101, 78, 111, 100, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 99, 99, 101, 115, 115, 82, 101, 115, 116, 114, 105, 99, 116, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 102, 101, 114, 101, 110, 99, 101, 78, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 86, 97, 114, 105, 97, 110, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 82, 97, 110, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 115, 65, 98, 115, 116, 114, 97, 99, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 84, 121, 112, 101, 78, 111, 100, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 84, 121, 112, 101, 78, 111, 100, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 99, 99, 101, 115, 115, 82, 101, 115, 116, 114, 105, 99, 116, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 102, 101, 114, 101, 110, 99, 101, 78, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 115, 65, 98, 115, 116, 114, 97, 99, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 121, 109, 109, 101, 116, 114, 105, 99, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 118, 101, 114, 115, 101, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 77, 101, 116, 104, 111, 100, 78, 111, 100, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 73, 110, 115, 116, 97, 110, 99, 101, 78, 111, 100, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 99, 99, 101, 115, 115, 82, 101, 115, 116, 114, 105, 99, 116, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 102, 101, 114, 101, 110, 99, 101, 78, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 120, 101, 99, 117, 116, 97, 98, 108, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 69, 120, 101, 99, 117, 116, 97, 98, 108, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 86, 105, 101, 119, 78, 111, 100, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 73, 110, 115, 116, 97, 110, 99, 101, 78, 111, 100, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 99, 99, 101, 115, 115, 82, 101, 115, 116, 114, 105, 99, 116, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 102, 101, 114, 101, 110, 99, 101, 78, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 111, 110, 116, 97, 105, 110, 115, 78, 111, 76, 111, 111, 112, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 118, 101, 110, 116, 78, 111, 116, 105, 102, 105, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 84, 121, 112, 101, 78, 111, 100, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 84, 121, 112, 101, 78, 111, 100, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 85, 115, 101, 114, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 99, 99, 101, 115, 115, 82, 101, 115, 116, 114, 105, 99, 116, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 49, 54, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 102, 101, 114, 101, 110, 99, 101, 78, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 115, 65, 98, 115, 116, 114, 97, 99, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 84, 121, 112, 101, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 78, 111, 100, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 84, 121, 112, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 115, 73, 110, 118, 101, 114, 115, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 97, 114, 103, 101, 116, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 112, 97, 110, 100, 101, 100, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 114, 103, 117, 109, 101, 110, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 82, 97, 110, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 69, 110, 117, 109, 86, 97, 108, 117, 101, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 54, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 69, 110, 117, 109, 70, 105, 101, 108, 100, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 69, 110, 117, 109, 86, 97, 108, 117, 101, 84, 121, 112, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 54, 52, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 69, 110, 117, 109, 86, 97, 108, 117, 101, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 69, 110, 117, 109, 86, 97, 108, 117, 101, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 69, 110, 117, 109, 86, 97, 108, 117, 101, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 79, 112, 116, 105, 111, 110, 83, 101, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 105, 100, 66, 105, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 85, 110, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 114, 109, 97, 108, 105, 122, 101, 100, 83, 116, 114, 105, 110, 103, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 101, 99, 105, 109, 97, 108, 83, 116, 114, 105, 110, 103, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 117, 114, 97, 116, 105, 111, 110, 83, 116, 114, 105, 110, 103, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 84, 105, 109, 101, 83, 116, 114, 105, 110, 103, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 101, 83, 116, 114, 105, 110, 103, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 117, 114, 97, 116, 105, 111, 110, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 85, 116, 99, 84, 105, 109, 101, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 76, 111, 99, 97, 108, 101, 73, 100, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 84, 105, 109, 101, 90, 111, 110, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 79, 102, 102, 115, 101, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 121, 108, 105, 103, 104, 116, 83, 97, 118, 105, 110, 103, 73, 110, 79, 102, 102, 115, 101, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 73, 110, 100, 101, 120, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 73, 110, 116, 101, 103, 101, 114, 73, 100, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 67, 108, 105, 101, 110, 116, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 67, 108, 105, 101, 110, 116, 65, 110, 100, 83, 101, 114, 118, 101, 114, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 99, 111, 118, 101, 114, 121, 83, 101, 114, 118, 101, 114, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 114, 111, 100, 117, 99, 116, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 65, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 71, 97, 116, 101, 119, 97, 121, 83, 101, 114, 118, 101, 114, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 99, 111, 118, 101, 114, 121, 80, 114, 111, 102, 105, 108, 101, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 115, 99, 111, 118, 101, 114, 121, 85, 114, 108, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 99, 111, 118, 101, 114, 121, 85, 114, 108, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 115, 99, 111, 118, 101, 114, 121, 85, 114, 108, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 117, 116, 104, 101, 110, 116, 105, 99, 97, 116, 105, 111, 110, 84, 111, 107, 101, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 105, 109, 101, 115, 116, 97, 109, 112, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 97, 110, 100, 108, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 116, 117, 114, 110, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 117, 100, 105, 116, 69, 110, 116, 114, 121, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 105, 109, 101, 111, 117, 116, 72, 105, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 100, 100, 105, 116, 105, 111, 110, 97, 108, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 105, 109, 101, 115, 116, 97, 109, 112, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 97, 110, 100, 108, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 105, 99, 101, 82, 101, 115, 117, 108, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 105, 99, 101, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 116, 114, 105, 110, 103, 84, 97, 98, 108, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 114, 105, 110, 103, 84, 97, 98, 108, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 116, 114, 105, 110, 103, 84, 97, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 100, 100, 105, 116, 105, 111, 110, 97, 108, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 86, 101, 114, 115, 105, 111, 110, 84, 105, 109, 101, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 105, 99, 101, 70, 97, 117, 108, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 115, 115, 105, 111, 110, 108, 101, 115, 115, 73, 110, 118, 111, 107, 101, 82, 101, 113, 117, 101, 115, 116, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 114, 105, 115, 86, 101, 114, 115, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 78, 97, 109, 101, 115, 112, 97, 99, 101, 85, 114, 105, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 115, 112, 97, 99, 101, 85, 114, 105, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 78, 97, 109, 101, 115, 112, 97, 99, 101, 85, 114, 105, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 101, 114, 118, 101, 114, 85, 114, 105, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 85, 114, 105, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 101, 114, 118, 101, 114, 85, 114, 105, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 76, 111, 99, 97, 108, 101, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 111, 99, 97, 108, 101, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 76, 111, 99, 97, 108, 101, 73, 100, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 105, 99, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 115, 115, 105, 111, 110, 108, 101, 115, 115, 73, 110, 118, 111, 107, 101, 82, 101, 115, 112, 111, 110, 115, 101, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 78, 97, 109, 101, 115, 112, 97, 99, 101, 85, 114, 105, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 115, 112, 97, 99, 101, 85, 114, 105, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 78, 97, 109, 101, 115, 112, 97, 99, 101, 85, 114, 105, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 101, 114, 118, 101, 114, 85, 114, 105, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 85, 114, 105, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 101, 114, 118, 101, 114, 85, 114, 105, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 105, 99, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 70, 105, 110, 100, 83, 101, 114, 118, 101, 114, 115, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 100, 112, 111, 105, 110, 116, 85, 114, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 76, 111, 99, 97, 108, 101, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 111, 99, 97, 108, 101, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 76, 111, 99, 97, 108, 101, 73, 100, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 101, 114, 118, 101, 114, 85, 114, 105, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 85, 114, 105, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 101, 114, 118, 101, 114, 85, 114, 105, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 70, 105, 110, 100, 83, 101, 114, 118, 101, 114, 115, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 101, 114, 118, 101, 114, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 65, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 101, 114, 118, 101, 114, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 79, 110, 78, 101, 116, 119, 111, 114, 107, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 99, 111, 114, 100, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 99, 111, 118, 101, 114, 121, 85, 114, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 101, 114, 118, 101, 114, 67, 97, 112, 97, 98, 105, 108, 105, 116, 105, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 67, 97, 112, 97, 98, 105, 108, 105, 116, 105, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 101, 114, 118, 101, 114, 67, 97, 112, 97, 98, 105, 108, 105, 116, 105, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 70, 105, 110, 100, 83, 101, 114, 118, 101, 114, 115, 79, 110, 78, 101, 116, 119, 111, 114, 107, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 114, 116, 105, 110, 103, 82, 101, 99, 111, 114, 100, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 120, 82, 101, 99, 111, 114, 100, 115, 84, 111, 82, 101, 116, 117, 114, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 101, 114, 118, 101, 114, 67, 97, 112, 97, 98, 105, 108, 105, 116, 121, 70, 105, 108, 116, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 67, 97, 112, 97, 98, 105, 108, 105, 116, 121, 70, 105, 108, 116, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 101, 114, 118, 101, 114, 67, 97, 112, 97, 98, 105, 108, 105, 116, 121, 70, 105, 108, 116, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 70, 105, 110, 100, 83, 101, 114, 118, 101, 114, 115, 79, 110, 78, 101, 116, 119, 111, 114, 107, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 97, 115, 116, 67, 111, 117, 110, 116, 101, 114, 82, 101, 115, 101, 116, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 101, 114, 118, 101, 114, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 101, 114, 79, 110, 78, 101, 116, 119, 111, 114, 107, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 101, 114, 118, 101, 114, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 73, 110, 115, 116, 97, 110, 99, 101, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 77, 101, 115, 115, 97, 103, 101, 83, 101, 99, 117, 114, 105, 116, 121, 77, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 73, 110, 118, 97, 108, 105, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 110, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 105, 103, 110, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 105, 103, 110, 65, 110, 100, 69, 110, 99, 114, 121, 112, 116, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 84, 111, 107, 101, 110, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 110, 111, 110, 121, 109, 111, 117, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 78, 97, 109, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 73, 115, 115, 117, 101, 100, 84, 111, 107, 101, 110, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 84, 111, 107, 101, 110, 80, 111, 108, 105, 99, 121, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 111, 108, 105, 99, 121, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 111, 107, 101, 110, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 85, 115, 101, 114, 84, 111, 107, 101, 110, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 115, 115, 117, 101, 100, 84, 111, 107, 101, 110, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 115, 115, 117, 101, 114, 69, 110, 100, 112, 111, 105, 110, 116, 85, 114, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 80, 111, 108, 105, 99, 121, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 69, 110, 100, 112, 111, 105, 110, 116, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 100, 112, 111, 105, 110, 116, 85, 114, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 65, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 77, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 77, 101, 115, 115, 97, 103, 101, 83, 101, 99, 117, 114, 105, 116, 121, 77, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 80, 111, 108, 105, 99, 121, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 85, 115, 101, 114, 73, 100, 101, 110, 116, 105, 116, 121, 84, 111, 107, 101, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 73, 100, 101, 110, 116, 105, 116, 121, 84, 111, 107, 101, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 85, 115, 101, 114, 84, 111, 107, 101, 110, 80, 111, 108, 105, 99, 121, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 85, 115, 101, 114, 73, 100, 101, 110, 116, 105, 116, 121, 84, 111, 107, 101, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 114, 97, 110, 115, 112, 111, 114, 116, 80, 114, 111, 102, 105, 108, 101, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 76, 101, 118, 101, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 71, 101, 116, 69, 110, 100, 112, 111, 105, 110, 116, 115, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 100, 112, 111, 105, 110, 116, 85, 114, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 76, 111, 99, 97, 108, 101, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 111, 99, 97, 108, 101, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 76, 111, 99, 97, 108, 101, 73, 100, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 80, 114, 111, 102, 105, 108, 101, 85, 114, 105, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 114, 111, 102, 105, 108, 101, 85, 114, 105, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 80, 114, 111, 102, 105, 108, 101, 85, 114, 105, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 71, 101, 116, 69, 110, 100, 112, 111, 105, 110, 116, 115, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 69, 110, 100, 112, 111, 105, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 100, 112, 111, 105, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 69, 110, 100, 112, 111, 105, 110, 116, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 69, 110, 100, 112, 111, 105, 110, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 103, 105, 115, 116, 101, 114, 101, 100, 83, 101, 114, 118, 101, 114, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 114, 111, 100, 117, 99, 116, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 101, 114, 118, 101, 114, 78, 97, 109, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 78, 97, 109, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 101, 114, 118, 101, 114, 78, 97, 109, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 65, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 71, 97, 116, 101, 119, 97, 121, 83, 101, 114, 118, 101, 114, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 115, 99, 111, 118, 101, 114, 121, 85, 114, 108, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 99, 111, 118, 101, 114, 121, 85, 114, 108, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 115, 99, 111, 118, 101, 114, 121, 85, 114, 108, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 109, 97, 112, 104, 111, 114, 101, 70, 105, 108, 101, 80, 97, 116, 104, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 115, 79, 110, 108, 105, 110, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 103, 105, 115, 116, 101, 114, 83, 101, 114, 118, 101, 114, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 103, 105, 115, 116, 101, 114, 101, 100, 83, 101, 114, 118, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 103, 105, 115, 116, 101, 114, 83, 101, 114, 118, 101, 114, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 99, 111, 118, 101, 114, 121, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 77, 100, 110, 115, 68, 105, 115, 99, 111, 118, 101, 114, 121, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 68, 105, 115, 99, 111, 118, 101, 114, 121, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 100, 110, 115, 83, 101, 114, 118, 101, 114, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 101, 114, 118, 101, 114, 67, 97, 112, 97, 98, 105, 108, 105, 116, 105, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 67, 97, 112, 97, 98, 105, 108, 105, 116, 105, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 101, 114, 118, 101, 114, 67, 97, 112, 97, 98, 105, 108, 105, 116, 105, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 103, 105, 115, 116, 101, 114, 83, 101, 114, 118, 101, 114, 50, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 103, 105, 115, 116, 101, 114, 101, 100, 83, 101, 114, 118, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 115, 99, 111, 118, 101, 114, 121, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 99, 111, 118, 101, 114, 121, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 115, 99, 111, 118, 101, 114, 121, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 103, 105, 115, 116, 101, 114, 83, 101, 114, 118, 101, 114, 50, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 84, 111, 107, 101, 110, 82, 101, 113, 117, 101, 115, 116, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 73, 115, 115, 117, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 110, 101, 119, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 104, 97, 110, 110, 101, 108, 83, 101, 99, 117, 114, 105, 116, 121, 84, 111, 107, 101, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 104, 97, 110, 110, 101, 108, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 111, 107, 101, 110, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 114, 101, 97, 116, 101, 100, 65, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 118, 105, 115, 101, 100, 76, 105, 102, 101, 116, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 79, 112, 101, 110, 83, 101, 99, 117, 114, 101, 67, 104, 97, 110, 110, 101, 108, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 108, 105, 101, 110, 116, 80, 114, 111, 116, 111, 99, 111, 108, 86, 101, 114, 115, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 99, 117, 114, 105, 116, 121, 84, 111, 107, 101, 110, 82, 101, 113, 117, 101, 115, 116, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 77, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 77, 101, 115, 115, 97, 103, 101, 83, 101, 99, 117, 114, 105, 116, 121, 77, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 108, 105, 101, 110, 116, 78, 111, 110, 99, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 101, 100, 76, 105, 102, 101, 116, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 79, 112, 101, 110, 83, 101, 99, 117, 114, 101, 67, 104, 97, 110, 110, 101, 108, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 80, 114, 111, 116, 111, 99, 111, 108, 86, 101, 114, 115, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 84, 111, 107, 101, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 67, 104, 97, 110, 110, 101, 108, 83, 101, 99, 117, 114, 105, 116, 121, 84, 111, 107, 101, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 78, 111, 110, 99, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 108, 111, 115, 101, 83, 101, 99, 117, 114, 101, 67, 104, 97, 110, 110, 101, 108, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 108, 111, 115, 101, 83, 101, 99, 117, 114, 101, 67, 104, 97, 110, 110, 101, 108, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 105, 103, 110, 101, 100, 83, 111, 102, 116, 119, 97, 114, 101, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 68, 97, 116, 97, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 105, 103, 110, 97, 116, 117, 114, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 115, 115, 105, 111, 110, 65, 117, 116, 104, 101, 110, 116, 105, 99, 97, 116, 105, 111, 110, 84, 111, 107, 101, 110, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 105, 103, 110, 97, 116, 117, 114, 101, 68, 97, 116, 97, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 108, 103, 111, 114, 105, 116, 104, 109, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 105, 103, 110, 97, 116, 117, 114, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 114, 101, 97, 116, 101, 83, 101, 115, 115, 105, 111, 110, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 108, 105, 101, 110, 116, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 65, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 100, 112, 111, 105, 110, 116, 85, 114, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 115, 115, 105, 111, 110, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 108, 105, 101, 110, 116, 78, 111, 110, 99, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 108, 105, 101, 110, 116, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 101, 100, 83, 101, 115, 115, 105, 111, 110, 84, 105, 109, 101, 111, 117, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 120, 82, 101, 115, 112, 111, 110, 115, 101, 77, 101, 115, 115, 97, 103, 101, 83, 105, 122, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 114, 101, 97, 116, 101, 83, 101, 115, 115, 105, 111, 110, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 115, 115, 105, 111, 110, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 117, 116, 104, 101, 110, 116, 105, 99, 97, 116, 105, 111, 110, 84, 111, 107, 101, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 118, 105, 115, 101, 100, 83, 101, 115, 115, 105, 111, 110, 84, 105, 109, 101, 111, 117, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 78, 111, 110, 99, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 101, 114, 118, 101, 114, 69, 110, 100, 112, 111, 105, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 69, 110, 100, 112, 111, 105, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 69, 110, 100, 112, 111, 105, 110, 116, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 101, 114, 118, 101, 114, 69, 110, 100, 112, 111, 105, 110, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 101, 114, 118, 101, 114, 83, 111, 102, 116, 119, 97, 114, 101, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 83, 111, 102, 116, 119, 97, 114, 101, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 105, 103, 110, 101, 100, 83, 111, 102, 116, 119, 97, 114, 101, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 101, 114, 118, 101, 114, 83, 111, 102, 116, 119, 97, 114, 101, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 83, 105, 103, 110, 97, 116, 117, 114, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 105, 103, 110, 97, 116, 117, 114, 101, 68, 97, 116, 97, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 120, 82, 101, 113, 117, 101, 115, 116, 77, 101, 115, 115, 97, 103, 101, 83, 105, 122, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 73, 100, 101, 110, 116, 105, 116, 121, 84, 111, 107, 101, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 111, 108, 105, 99, 121, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 110, 111, 110, 121, 109, 111, 117, 115, 73, 100, 101, 110, 116, 105, 116, 121, 84, 111, 107, 101, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 85, 115, 101, 114, 73, 100, 101, 110, 116, 105, 116, 121, 84, 111, 107, 101, 110, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 111, 108, 105, 99, 121, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 85, 115, 101, 114, 73, 100, 101, 110, 116, 105, 116, 121, 84, 111, 107, 101, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 78, 97, 109, 101, 73, 100, 101, 110, 116, 105, 116, 121, 84, 111, 107, 101, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 85, 115, 101, 114, 73, 100, 101, 110, 116, 105, 116, 121, 84, 111, 107, 101, 110, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 111, 108, 105, 99, 121, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 85, 115, 101, 114, 73, 100, 101, 110, 116, 105, 116, 121, 84, 111, 107, 101, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 97, 115, 115, 119, 111, 114, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 99, 114, 121, 112, 116, 105, 111, 110, 65, 108, 103, 111, 114, 105, 116, 104, 109, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 88, 53, 48, 57, 73, 100, 101, 110, 116, 105, 116, 121, 84, 111, 107, 101, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 85, 115, 101, 114, 73, 100, 101, 110, 116, 105, 116, 121, 84, 111, 107, 101, 110, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 111, 108, 105, 99, 121, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 85, 115, 101, 114, 73, 100, 101, 110, 116, 105, 116, 121, 84, 111, 107, 101, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 68, 97, 116, 97, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 73, 115, 115, 117, 101, 100, 73, 100, 101, 110, 116, 105, 116, 121, 84, 111, 107, 101, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 85, 115, 101, 114, 73, 100, 101, 110, 116, 105, 116, 121, 84, 111, 107, 101, 110, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 111, 108, 105, 99, 121, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 85, 115, 101, 114, 73, 100, 101, 110, 116, 105, 116, 121, 84, 111, 107, 101, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 111, 107, 101, 110, 68, 97, 116, 97, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 99, 114, 121, 112, 116, 105, 111, 110, 65, 108, 103, 111, 114, 105, 116, 104, 109, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 115, 97, 69, 110, 99, 114, 121, 112, 116, 101, 100, 83, 101, 99, 114, 101, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 69, 99, 99, 69, 110, 99, 114, 121, 112, 116, 101, 100, 83, 101, 99, 114, 101, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 99, 116, 105, 118, 97, 116, 101, 83, 101, 115, 115, 105, 111, 110, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 108, 105, 101, 110, 116, 83, 105, 103, 110, 97, 116, 117, 114, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 105, 103, 110, 97, 116, 117, 114, 101, 68, 97, 116, 97, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 67, 108, 105, 101, 110, 116, 83, 111, 102, 116, 119, 97, 114, 101, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 108, 105, 101, 110, 116, 83, 111, 102, 116, 119, 97, 114, 101, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 105, 103, 110, 101, 100, 83, 111, 102, 116, 119, 97, 114, 101, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 67, 108, 105, 101, 110, 116, 83, 111, 102, 116, 119, 97, 114, 101, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 76, 111, 99, 97, 108, 101, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 111, 99, 97, 108, 101, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 76, 111, 99, 97, 108, 101, 73, 100, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 73, 100, 101, 110, 116, 105, 116, 121, 84, 111, 107, 101, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 84, 111, 107, 101, 110, 83, 105, 103, 110, 97, 116, 117, 114, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 105, 103, 110, 97, 116, 117, 114, 101, 68, 97, 116, 97, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 99, 116, 105, 118, 97, 116, 101, 83, 101, 115, 115, 105, 111, 110, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 78, 111, 110, 99, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 108, 111, 115, 101, 83, 101, 115, 115, 105, 111, 110, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 108, 101, 116, 101, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 108, 111, 115, 101, 83, 101, 115, 115, 105, 111, 110, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 97, 110, 99, 101, 108, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 97, 110, 100, 108, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 97, 110, 99, 101, 108, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 97, 110, 99, 101, 108, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 77, 97, 115, 107, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 110, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 99, 99, 101, 115, 115, 76, 101, 118, 101, 108, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 78, 97, 109, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 67, 111, 110, 116, 97, 105, 110, 115, 78, 111, 76, 111, 111, 112, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 54, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 69, 118, 101, 110, 116, 78, 111, 116, 105, 102, 105, 101, 114, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 50, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 69, 120, 101, 99, 117, 116, 97, 98, 108, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 53, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 105, 122, 105, 110, 103, 34, 32, 86, 97, 108, 117, 101, 61, 34, 53, 49, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 73, 110, 118, 101, 114, 115, 101, 78, 97, 109, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 48, 50, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 73, 115, 65, 98, 115, 116, 114, 97, 99, 116, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 48, 52, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 77, 105, 110, 105, 109, 117, 109, 83, 97, 109, 112, 108, 105, 110, 103, 73, 110, 116, 101, 114, 118, 97, 108, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 48, 57, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 56, 49, 57, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 54, 51, 56, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 121, 109, 109, 101, 116, 114, 105, 99, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 50, 55, 54, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 65, 99, 99, 101, 115, 115, 76, 101, 118, 101, 108, 34, 32, 86, 97, 108, 117, 101, 61, 34, 54, 53, 53, 51, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 69, 120, 101, 99, 117, 116, 97, 98, 108, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 51, 49, 48, 55, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 54, 50, 49, 52, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 82, 97, 110, 107, 34, 32, 86, 97, 108, 117, 101, 61, 34, 53, 50, 52, 50, 56, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 48, 52, 56, 53, 55, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 48, 57, 55, 49, 53, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 84, 121, 112, 101, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 49, 57, 52, 51, 48, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 56, 51, 56, 56, 54, 48, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 99, 99, 101, 115, 115, 82, 101, 115, 116, 114, 105, 99, 116, 105, 111, 110, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 54, 55, 55, 55, 50, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 108, 108, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 51, 53, 53, 52, 52, 51, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 66, 97, 115, 101, 78, 111, 100, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 54, 53, 48, 49, 50, 50, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 79, 98, 106, 101, 99, 116, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 54, 53, 48, 49, 51, 52, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 79, 98, 106, 101, 99, 116, 84, 121, 112, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 54, 53, 48, 51, 50, 54, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 86, 97, 114, 105, 97, 98, 108, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 54, 53, 55, 49, 51, 56, 51, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 86, 97, 114, 105, 97, 98, 108, 101, 84, 121, 112, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 56, 54, 48, 48, 52, 51, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 77, 101, 116, 104, 111, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 54, 54, 51, 50, 53, 52, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 84, 121, 112, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 54, 53, 51, 55, 48, 54, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 86, 105, 101, 119, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 54, 53, 48, 49, 51, 53, 54, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 112, 101, 99, 105, 102, 105, 101, 100, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 79, 98, 106, 101, 99, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 112, 101, 99, 105, 102, 105, 101, 100, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 118, 101, 110, 116, 78, 111, 116, 105, 102, 105, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 86, 97, 114, 105, 97, 98, 108, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 112, 101, 99, 105, 102, 105, 101, 100, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 86, 97, 114, 105, 97, 110, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 82, 97, 110, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 99, 99, 101, 115, 115, 76, 101, 118, 101, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 65, 99, 99, 101, 115, 115, 76, 101, 118, 101, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 105, 110, 105, 109, 117, 109, 83, 97, 109, 112, 108, 105, 110, 103, 73, 110, 116, 101, 114, 118, 97, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 105, 122, 105, 110, 103, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 77, 101, 116, 104, 111, 100, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 112, 101, 99, 105, 102, 105, 101, 100, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 120, 101, 99, 117, 116, 97, 98, 108, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 69, 120, 101, 99, 117, 116, 97, 98, 108, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 79, 98, 106, 101, 99, 116, 84, 121, 112, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 112, 101, 99, 105, 102, 105, 101, 100, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 115, 65, 98, 115, 116, 114, 97, 99, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 86, 97, 114, 105, 97, 98, 108, 101, 84, 121, 112, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 112, 101, 99, 105, 102, 105, 101, 100, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 86, 97, 114, 105, 97, 110, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 82, 97, 110, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 115, 65, 98, 115, 116, 114, 97, 99, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 84, 121, 112, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 112, 101, 99, 105, 102, 105, 101, 100, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 115, 65, 98, 115, 116, 114, 97, 99, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 121, 109, 109, 101, 116, 114, 105, 99, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 118, 101, 114, 115, 101, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 84, 121, 112, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 112, 101, 99, 105, 102, 105, 101, 100, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 115, 65, 98, 115, 116, 114, 97, 99, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 86, 105, 101, 119, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 112, 101, 99, 105, 102, 105, 101, 100, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 111, 110, 116, 97, 105, 110, 115, 78, 111, 76, 111, 111, 112, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 118, 101, 110, 116, 78, 111, 116, 105, 102, 105, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 71, 101, 110, 101, 114, 105, 99, 65, 116, 116, 114, 105, 98, 117, 116, 101, 86, 97, 108, 117, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 116, 116, 114, 105, 98, 117, 116, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 86, 97, 114, 105, 97, 110, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 71, 101, 110, 101, 114, 105, 99, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 112, 101, 99, 105, 102, 105, 101, 100, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 65, 116, 116, 114, 105, 98, 117, 116, 101, 86, 97, 108, 117, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 116, 116, 114, 105, 98, 117, 116, 101, 86, 97, 108, 117, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 71, 101, 110, 101, 114, 105, 99, 65, 116, 116, 114, 105, 98, 117, 116, 101, 86, 97, 108, 117, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 65, 116, 116, 114, 105, 98, 117, 116, 101, 86, 97, 108, 117, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 100, 100, 78, 111, 100, 101, 115, 73, 116, 101, 109, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 97, 114, 101, 110, 116, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 112, 97, 110, 100, 101, 100, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 84, 121, 112, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 101, 100, 78, 101, 119, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 112, 97, 110, 100, 101, 100, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 121, 112, 101, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 112, 97, 110, 100, 101, 100, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 100, 100, 78, 111, 100, 101, 115, 82, 101, 115, 117, 108, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 100, 100, 101, 100, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 100, 100, 78, 111, 100, 101, 115, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 78, 111, 100, 101, 115, 84, 111, 65, 100, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 115, 84, 111, 65, 100, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 65, 100, 100, 78, 111, 100, 101, 115, 73, 116, 101, 109, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 78, 111, 100, 101, 115, 84, 111, 65, 100, 100, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 100, 100, 78, 111, 100, 101, 115, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 65, 100, 100, 78, 111, 100, 101, 115, 82, 101, 115, 117, 108, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 100, 100, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 73, 116, 101, 109, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 111, 117, 114, 99, 101, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 84, 121, 112, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 115, 70, 111, 114, 119, 97, 114, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 97, 114, 103, 101, 116, 83, 101, 114, 118, 101, 114, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 97, 114, 103, 101, 116, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 112, 97, 110, 100, 101, 100, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 97, 114, 103, 101, 116, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 100, 100, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 84, 111, 65, 100, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 84, 111, 65, 100, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 65, 100, 100, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 73, 116, 101, 109, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 84, 111, 65, 100, 100, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 100, 100, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 101, 108, 101, 116, 101, 78, 111, 100, 101, 115, 73, 116, 101, 109, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 108, 101, 116, 101, 84, 97, 114, 103, 101, 116, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 101, 108, 101, 116, 101, 78, 111, 100, 101, 115, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 78, 111, 100, 101, 115, 84, 111, 68, 101, 108, 101, 116, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 115, 84, 111, 68, 101, 108, 101, 116, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 68, 101, 108, 101, 116, 101, 78, 111, 100, 101, 115, 73, 116, 101, 109, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 78, 111, 100, 101, 115, 84, 111, 68, 101, 108, 101, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 101, 108, 101, 116, 101, 78, 111, 100, 101, 115, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 101, 108, 101, 116, 101, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 73, 116, 101, 109, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 111, 117, 114, 99, 101, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 84, 121, 112, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 115, 70, 111, 114, 119, 97, 114, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 97, 114, 103, 101, 116, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 112, 97, 110, 100, 101, 100, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 108, 101, 116, 101, 66, 105, 100, 105, 114, 101, 99, 116, 105, 111, 110, 97, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 101, 108, 101, 116, 101, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 84, 111, 68, 101, 108, 101, 116, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 84, 111, 68, 101, 108, 101, 116, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 68, 101, 108, 101, 116, 101, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 73, 116, 101, 109, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 84, 111, 68, 101, 108, 101, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 101, 108, 101, 116, 101, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 116, 116, 114, 105, 98, 117, 116, 101, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 32, 73, 115, 79, 112, 116, 105, 111, 110, 83, 101, 116, 61, 34, 116, 114, 117, 101, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 110, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 99, 99, 101, 115, 115, 76, 101, 118, 101, 108, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 114, 114, 97, 121, 68, 105, 109, 101, 110, 115, 105, 111, 110, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 78, 97, 109, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 67, 111, 110, 116, 97, 105, 110, 115, 78, 111, 76, 111, 111, 112, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 54, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 69, 118, 101, 110, 116, 78, 111, 116, 105, 102, 105, 101, 114, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 50, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 69, 120, 101, 99, 117, 116, 97, 98, 108, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 53, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 105, 122, 105, 110, 103, 34, 32, 86, 97, 108, 117, 101, 61, 34, 53, 49, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 73, 110, 118, 101, 114, 115, 101, 78, 97, 109, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 48, 50, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 73, 115, 65, 98, 115, 116, 114, 97, 99, 116, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 48, 52, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 77, 105, 110, 105, 109, 117, 109, 83, 97, 109, 112, 108, 105, 110, 103, 73, 110, 116, 101, 114, 118, 97, 108, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 48, 57, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 56, 49, 57, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 54, 51, 56, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 121, 109, 109, 101, 116, 114, 105, 99, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 50, 55, 54, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 65, 99, 99, 101, 115, 115, 76, 101, 118, 101, 108, 34, 32, 86, 97, 108, 117, 101, 61, 34, 54, 53, 53, 51, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 69, 120, 101, 99, 117, 116, 97, 98, 108, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 51, 49, 48, 55, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 54, 50, 49, 52, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 82, 97, 110, 107, 34, 32, 86, 97, 108, 117, 101, 61, 34, 53, 50, 52, 50, 56, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 77, 97, 115, 107, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 48, 52, 56, 53, 55, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 70, 111, 114, 86, 97, 114, 105, 97, 98, 108, 101, 84, 121, 112, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 48, 57, 55, 49, 53, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 84, 121, 112, 101, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 49, 57, 52, 51, 48, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 82, 111, 108, 101, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 56, 51, 56, 56, 54, 48, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 99, 99, 101, 115, 115, 82, 101, 115, 116, 114, 105, 99, 116, 105, 111, 110, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 54, 55, 55, 55, 50, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 99, 99, 101, 115, 115, 76, 101, 118, 101, 108, 69, 120, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 51, 53, 53, 52, 52, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 68, 105, 114, 101, 99, 116, 105, 111, 110, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 70, 111, 114, 119, 97, 114, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 73, 110, 118, 101, 114, 115, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 66, 111, 116, 104, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 73, 110, 118, 97, 108, 105, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 86, 105, 101, 119, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 105, 101, 119, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 105, 109, 101, 115, 116, 97, 109, 112, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 105, 101, 119, 86, 101, 114, 115, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 68, 105, 114, 101, 99, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 66, 114, 111, 119, 115, 101, 68, 105, 114, 101, 99, 116, 105, 111, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 84, 121, 112, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 99, 108, 117, 100, 101, 83, 117, 98, 116, 121, 112, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 67, 108, 97, 115, 115, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 117, 108, 116, 77, 97, 115, 107, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 82, 101, 115, 117, 108, 116, 77, 97, 115, 107, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 110, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 84, 121, 112, 101, 73, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 73, 115, 70, 111, 114, 119, 97, 114, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 78, 97, 109, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 84, 121, 112, 101, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 108, 108, 34, 32, 86, 97, 108, 117, 101, 61, 34, 54, 51, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 84, 121, 112, 101, 73, 110, 102, 111, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 84, 97, 114, 103, 101, 116, 73, 110, 102, 111, 34, 32, 86, 97, 108, 117, 101, 61, 34, 54, 48, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 84, 121, 112, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 115, 70, 111, 114, 119, 97, 114, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 112, 97, 110, 100, 101, 100, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 67, 108, 97, 115, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 121, 112, 101, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 112, 97, 110, 100, 101, 100, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 111, 110, 116, 105, 110, 117, 97, 116, 105, 111, 110, 80, 111, 105, 110, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 82, 101, 115, 117, 108, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 111, 110, 116, 105, 110, 117, 97, 116, 105, 111, 110, 80, 111, 105, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 102, 101, 114, 101, 110, 99, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 105, 101, 119, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 86, 105, 101, 119, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 101, 100, 77, 97, 120, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 80, 101, 114, 78, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 78, 111, 100, 101, 115, 84, 111, 66, 114, 111, 119, 115, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 115, 84, 111, 66, 114, 111, 119, 115, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 66, 114, 111, 119, 115, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 78, 111, 100, 101, 115, 84, 111, 66, 114, 111, 119, 115, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 66, 114, 111, 119, 115, 101, 82, 101, 115, 117, 108, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 78, 101, 120, 116, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 108, 101, 97, 115, 101, 67, 111, 110, 116, 105, 110, 117, 97, 116, 105, 111, 110, 80, 111, 105, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 67, 111, 110, 116, 105, 110, 117, 97, 116, 105, 111, 110, 80, 111, 105, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 111, 110, 116, 105, 110, 117, 97, 116, 105, 111, 110, 80, 111, 105, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 67, 111, 110, 116, 105, 110, 117, 97, 116, 105, 111, 110, 80, 111, 105, 110, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 78, 101, 120, 116, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 66, 114, 111, 119, 115, 101, 82, 101, 115, 117, 108, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 108, 97, 116, 105, 118, 101, 80, 97, 116, 104, 69, 108, 101, 109, 101, 110, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 84, 121, 112, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 115, 73, 110, 118, 101, 114, 115, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 99, 108, 117, 100, 101, 83, 117, 98, 116, 121, 112, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 97, 114, 103, 101, 116, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 108, 97, 116, 105, 118, 101, 80, 97, 116, 104, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 69, 108, 101, 109, 101, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 108, 101, 109, 101, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 108, 97, 116, 105, 118, 101, 80, 97, 116, 104, 69, 108, 101, 109, 101, 110, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 69, 108, 101, 109, 101, 110, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 80, 97, 116, 104, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 114, 116, 105, 110, 103, 78, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 108, 97, 116, 105, 118, 101, 80, 97, 116, 104, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 108, 97, 116, 105, 118, 101, 80, 97, 116, 104, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 80, 97, 116, 104, 84, 97, 114, 103, 101, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 97, 114, 103, 101, 116, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 112, 97, 110, 100, 101, 100, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 109, 97, 105, 110, 105, 110, 103, 80, 97, 116, 104, 73, 110, 100, 101, 120, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 80, 97, 116, 104, 82, 101, 115, 117, 108, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 84, 97, 114, 103, 101, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 97, 114, 103, 101, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 66, 114, 111, 119, 115, 101, 80, 97, 116, 104, 84, 97, 114, 103, 101, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 84, 97, 114, 103, 101, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 84, 114, 97, 110, 115, 108, 97, 116, 101, 66, 114, 111, 119, 115, 101, 80, 97, 116, 104, 115, 84, 111, 78, 111, 100, 101, 73, 100, 115, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 66, 114, 111, 119, 115, 101, 80, 97, 116, 104, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 80, 97, 116, 104, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 66, 114, 111, 119, 115, 101, 80, 97, 116, 104, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 66, 114, 111, 119, 115, 101, 80, 97, 116, 104, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 84, 114, 97, 110, 115, 108, 97, 116, 101, 66, 114, 111, 119, 115, 101, 80, 97, 116, 104, 115, 84, 111, 78, 111, 100, 101, 73, 100, 115, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 66, 114, 111, 119, 115, 101, 80, 97, 116, 104, 82, 101, 115, 117, 108, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 103, 105, 115, 116, 101, 114, 78, 111, 100, 101, 115, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 78, 111, 100, 101, 115, 84, 111, 82, 101, 103, 105, 115, 116, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 115, 84, 111, 82, 101, 103, 105, 115, 116, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 78, 111, 100, 101, 115, 84, 111, 82, 101, 103, 105, 115, 116, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 103, 105, 115, 116, 101, 114, 78, 111, 100, 101, 115, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 103, 105, 115, 116, 101, 114, 101, 100, 78, 111, 100, 101, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 103, 105, 115, 116, 101, 114, 101, 100, 78, 111, 100, 101, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 103, 105, 115, 116, 101, 114, 101, 100, 78, 111, 100, 101, 73, 100, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 85, 110, 114, 101, 103, 105, 115, 116, 101, 114, 78, 111, 100, 101, 115, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 78, 111, 100, 101, 115, 84, 111, 85, 110, 114, 101, 103, 105, 115, 116, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 115, 84, 111, 85, 110, 114, 101, 103, 105, 115, 116, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 78, 111, 100, 101, 115, 84, 111, 85, 110, 114, 101, 103, 105, 115, 116, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 85, 110, 114, 101, 103, 105, 115, 116, 101, 114, 78, 111, 100, 101, 115, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 111, 117, 110, 116, 101, 114, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 78, 117, 109, 101, 114, 105, 99, 82, 97, 110, 103, 101, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 79, 112, 97, 113, 117, 101, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 69, 110, 100, 112, 111, 105, 110, 116, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 79, 112, 101, 114, 97, 116, 105, 111, 110, 84, 105, 109, 101, 111, 117, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 66, 105, 110, 97, 114, 121, 69, 110, 99, 111, 100, 105, 110, 103, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 120, 83, 116, 114, 105, 110, 103, 76, 101, 110, 103, 116, 104, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 120, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 76, 101, 110, 103, 116, 104, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 120, 65, 114, 114, 97, 121, 76, 101, 110, 103, 116, 104, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 120, 77, 101, 115, 115, 97, 103, 101, 83, 105, 122, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 120, 66, 117, 102, 102, 101, 114, 83, 105, 122, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 104, 97, 110, 110, 101, 108, 76, 105, 102, 101, 116, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 84, 111, 107, 101, 110, 76, 105, 102, 101, 116, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 81, 117, 101, 114, 121, 68, 97, 116, 97, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 108, 97, 116, 105, 118, 101, 80, 97, 116, 104, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 108, 97, 116, 105, 118, 101, 80, 97, 116, 104, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 116, 116, 114, 105, 98, 117, 116, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 100, 101, 120, 82, 97, 110, 103, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 84, 121, 112, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 121, 112, 101, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 78, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 112, 97, 110, 100, 101, 100, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 99, 108, 117, 100, 101, 83, 117, 98, 84, 121, 112, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 97, 116, 97, 84, 111, 82, 101, 116, 117, 114, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 84, 111, 82, 101, 116, 117, 114, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 81, 117, 101, 114, 121, 68, 97, 116, 97, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 97, 116, 97, 84, 111, 82, 101, 116, 117, 114, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 70, 105, 108, 116, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 69, 113, 117, 97, 108, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 73, 115, 78, 117, 108, 108, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 71, 114, 101, 97, 116, 101, 114, 84, 104, 97, 110, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 76, 101, 115, 115, 84, 104, 97, 110, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 71, 114, 101, 97, 116, 101, 114, 84, 104, 97, 110, 79, 114, 69, 113, 117, 97, 108, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 76, 101, 115, 115, 84, 104, 97, 110, 79, 114, 69, 113, 117, 97, 108, 34, 32, 86, 97, 108, 117, 101, 61, 34, 53, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 76, 105, 107, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 116, 34, 32, 86, 97, 108, 117, 101, 61, 34, 55, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 66, 101, 116, 119, 101, 101, 110, 34, 32, 86, 97, 108, 117, 101, 61, 34, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 73, 110, 76, 105, 115, 116, 34, 32, 86, 97, 108, 117, 101, 61, 34, 57, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 110, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 79, 114, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 67, 97, 115, 116, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 73, 110, 86, 105, 101, 119, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 51, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 79, 102, 84, 121, 112, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 108, 97, 116, 101, 100, 84, 111, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 53, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 66, 105, 116, 119, 105, 115, 101, 65, 110, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 66, 105, 116, 119, 105, 115, 101, 79, 114, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 55, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 81, 117, 101, 114, 121, 68, 97, 116, 97, 83, 101, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 112, 97, 110, 100, 101, 100, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 121, 112, 101, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 78, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 112, 97, 110, 100, 101, 100, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 86, 97, 108, 117, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 86, 97, 114, 105, 97, 110, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 86, 97, 108, 117, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 82, 101, 102, 101, 114, 101, 110, 99, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 84, 121, 112, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 115, 70, 111, 114, 119, 97, 114, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 100, 78, 111, 100, 101, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 100, 78, 111, 100, 101, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 102, 101, 114, 101, 110, 99, 101, 100, 78, 111, 100, 101, 73, 100, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 111, 110, 116, 101, 110, 116, 70, 105, 108, 116, 101, 114, 69, 108, 101, 109, 101, 110, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 70, 105, 108, 116, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 70, 105, 108, 116, 101, 114, 79, 112, 101, 114, 97, 116, 111, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 70, 105, 108, 116, 101, 114, 79, 112, 101, 114, 97, 110, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 70, 105, 108, 116, 101, 114, 79, 112, 101, 114, 97, 110, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 70, 105, 108, 116, 101, 114, 79, 112, 101, 114, 97, 110, 100, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 111, 110, 116, 101, 110, 116, 70, 105, 108, 116, 101, 114, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 69, 108, 101, 109, 101, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 108, 101, 109, 101, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 67, 111, 110, 116, 101, 110, 116, 70, 105, 108, 116, 101, 114, 69, 108, 101, 109, 101, 110, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 69, 108, 101, 109, 101, 110, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 70, 105, 108, 116, 101, 114, 79, 112, 101, 114, 97, 110, 100, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 69, 108, 101, 109, 101, 110, 116, 79, 112, 101, 114, 97, 110, 100, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 70, 105, 108, 116, 101, 114, 79, 112, 101, 114, 97, 110, 100, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 100, 101, 120, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 76, 105, 116, 101, 114, 97, 108, 79, 112, 101, 114, 97, 110, 100, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 70, 105, 108, 116, 101, 114, 79, 112, 101, 114, 97, 110, 100, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 86, 97, 114, 105, 97, 110, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 116, 116, 114, 105, 98, 117, 116, 101, 79, 112, 101, 114, 97, 110, 100, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 70, 105, 108, 116, 101, 114, 79, 112, 101, 114, 97, 110, 100, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 108, 105, 97, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 80, 97, 116, 104, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 108, 97, 116, 105, 118, 101, 80, 97, 116, 104, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 116, 116, 114, 105, 98, 117, 116, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 100, 101, 120, 82, 97, 110, 103, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 105, 109, 112, 108, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 79, 112, 101, 114, 97, 110, 100, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 70, 105, 108, 116, 101, 114, 79, 112, 101, 114, 97, 110, 100, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 121, 112, 101, 68, 101, 102, 105, 110, 105, 116, 105, 111, 110, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 66, 114, 111, 119, 115, 101, 80, 97, 116, 104, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 80, 97, 116, 104, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 66, 114, 111, 119, 115, 101, 80, 97, 116, 104, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 116, 116, 114, 105, 98, 117, 116, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 100, 101, 120, 82, 97, 110, 103, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 111, 110, 116, 101, 110, 116, 70, 105, 108, 116, 101, 114, 69, 108, 101, 109, 101, 110, 116, 82, 101, 115, 117, 108, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 79, 112, 101, 114, 97, 110, 100, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 79, 112, 101, 114, 97, 110, 100, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 79, 112, 101, 114, 97, 110, 100, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 79, 112, 101, 114, 97, 110, 100, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 79, 112, 101, 114, 97, 110, 100, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 79, 112, 101, 114, 97, 110, 100, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 111, 110, 116, 101, 110, 116, 70, 105, 108, 116, 101, 114, 82, 101, 115, 117, 108, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 69, 108, 101, 109, 101, 110, 116, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 108, 101, 109, 101, 110, 116, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 67, 111, 110, 116, 101, 110, 116, 70, 105, 108, 116, 101, 114, 69, 108, 101, 109, 101, 110, 116, 82, 101, 115, 117, 108, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 69, 108, 101, 109, 101, 110, 116, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 69, 108, 101, 109, 101, 110, 116, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 108, 101, 109, 101, 110, 116, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 69, 108, 101, 109, 101, 110, 116, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 80, 97, 114, 115, 105, 110, 103, 82, 101, 115, 117, 108, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 97, 116, 97, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 97, 116, 97, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 97, 116, 97, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 97, 116, 97, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 81, 117, 101, 114, 121, 70, 105, 114, 115, 116, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 105, 101, 119, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 86, 105, 101, 119, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 78, 111, 100, 101, 84, 121, 112, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 84, 121, 112, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 78, 111, 100, 101, 84, 121, 112, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 78, 111, 100, 101, 84, 121, 112, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 70, 105, 108, 116, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 67, 111, 110, 116, 101, 110, 116, 70, 105, 108, 116, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 120, 68, 97, 116, 97, 83, 101, 116, 115, 84, 111, 82, 101, 116, 117, 114, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 120, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 84, 111, 82, 101, 116, 117, 114, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 81, 117, 101, 114, 121, 70, 105, 114, 115, 116, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 81, 117, 101, 114, 121, 68, 97, 116, 97, 83, 101, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 81, 117, 101, 114, 121, 68, 97, 116, 97, 83, 101, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 81, 117, 101, 114, 121, 68, 97, 116, 97, 83, 101, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 81, 117, 101, 114, 121, 68, 97, 116, 97, 83, 101, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 111, 110, 116, 105, 110, 117, 97, 116, 105, 111, 110, 80, 111, 105, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 80, 97, 114, 115, 105, 110, 103, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 97, 114, 115, 105, 110, 103, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 80, 97, 114, 115, 105, 110, 103, 82, 101, 115, 117, 108, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 80, 97, 114, 115, 105, 110, 103, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 70, 105, 108, 116, 101, 114, 82, 101, 115, 117, 108, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 67, 111, 110, 116, 101, 110, 116, 70, 105, 108, 116, 101, 114, 82, 101, 115, 117, 108, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 81, 117, 101, 114, 121, 78, 101, 120, 116, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 108, 101, 97, 115, 101, 67, 111, 110, 116, 105, 110, 117, 97, 116, 105, 111, 110, 80, 111, 105, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 111, 110, 116, 105, 110, 117, 97, 116, 105, 111, 110, 80, 111, 105, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 81, 117, 101, 114, 121, 78, 101, 120, 116, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 81, 117, 101, 114, 121, 68, 97, 116, 97, 83, 101, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 81, 117, 101, 114, 121, 68, 97, 116, 97, 83, 101, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 81, 117, 101, 114, 121, 68, 97, 116, 97, 83, 101, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 81, 117, 101, 114, 121, 68, 97, 116, 97, 83, 101, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 118, 105, 115, 101, 100, 67, 111, 110, 116, 105, 110, 117, 97, 116, 105, 111, 110, 80, 111, 105, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 84, 105, 109, 101, 115, 116, 97, 109, 112, 115, 84, 111, 82, 101, 116, 117, 114, 110, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 111, 117, 114, 99, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 66, 111, 116, 104, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 101, 105, 116, 104, 101, 114, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 73, 110, 118, 97, 108, 105, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 97, 100, 86, 97, 108, 117, 101, 73, 100, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 116, 116, 114, 105, 98, 117, 116, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 100, 101, 120, 82, 97, 110, 103, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 69, 110, 99, 111, 100, 105, 110, 103, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 97, 100, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 120, 65, 103, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 105, 109, 101, 115, 116, 97, 109, 112, 115, 84, 111, 82, 101, 116, 117, 114, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 84, 105, 109, 101, 115, 116, 97, 109, 112, 115, 84, 111, 82, 101, 116, 117, 114, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 78, 111, 100, 101, 115, 84, 111, 82, 101, 97, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 115, 84, 111, 82, 101, 97, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 97, 100, 86, 97, 108, 117, 101, 73, 100, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 78, 111, 100, 101, 115, 84, 111, 82, 101, 97, 100, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 97, 100, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 97, 116, 97, 86, 97, 108, 117, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 82, 101, 97, 100, 86, 97, 108, 117, 101, 73, 100, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 100, 101, 120, 82, 97, 110, 103, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 69, 110, 99, 111, 100, 105, 110, 103, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 81, 117, 97, 108, 105, 102, 105, 101, 100, 78, 97, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 111, 110, 116, 105, 110, 117, 97, 116, 105, 111, 110, 80, 111, 105, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 82, 101, 97, 100, 82, 101, 115, 117, 108, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 111, 110, 116, 105, 110, 117, 97, 116, 105, 111, 110, 80, 111, 105, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 68, 97, 116, 97, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 82, 101, 97, 100, 68, 101, 116, 97, 105, 108, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 97, 100, 69, 118, 101, 110, 116, 68, 101, 116, 97, 105, 108, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 72, 105, 115, 116, 111, 114, 121, 82, 101, 97, 100, 68, 101, 116, 97, 105, 108, 115, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 117, 109, 86, 97, 108, 117, 101, 115, 80, 101, 114, 78, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 114, 116, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 100, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 70, 105, 108, 116, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 69, 118, 101, 110, 116, 70, 105, 108, 116, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 97, 100, 82, 97, 119, 77, 111, 100, 105, 102, 105, 101, 100, 68, 101, 116, 97, 105, 108, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 72, 105, 115, 116, 111, 114, 121, 82, 101, 97, 100, 68, 101, 116, 97, 105, 108, 115, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 115, 82, 101, 97, 100, 77, 111, 100, 105, 102, 105, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 114, 116, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 100, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 117, 109, 86, 97, 108, 117, 101, 115, 80, 101, 114, 78, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 116, 117, 114, 110, 66, 111, 117, 110, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 97, 100, 80, 114, 111, 99, 101, 115, 115, 101, 100, 68, 101, 116, 97, 105, 108, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 72, 105, 115, 116, 111, 114, 121, 82, 101, 97, 100, 68, 101, 116, 97, 105, 108, 115, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 114, 116, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 100, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 114, 111, 99, 101, 115, 115, 105, 110, 103, 73, 110, 116, 101, 114, 118, 97, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 65, 103, 103, 114, 101, 103, 97, 116, 101, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 103, 103, 114, 101, 103, 97, 116, 101, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 65, 103, 103, 114, 101, 103, 97, 116, 101, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 103, 103, 114, 101, 103, 97, 116, 101, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 65, 103, 103, 114, 101, 103, 97, 116, 101, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 97, 100, 65, 116, 84, 105, 109, 101, 68, 101, 116, 97, 105, 108, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 72, 105, 115, 116, 111, 114, 121, 82, 101, 97, 100, 68, 101, 116, 97, 105, 108, 115, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 113, 84, 105, 109, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 84, 105, 109, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 113, 84, 105, 109, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 83, 105, 109, 112, 108, 101, 66, 111, 117, 110, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 97, 100, 65, 110, 110, 111, 116, 97, 116, 105, 111, 110, 68, 97, 116, 97, 68, 101, 116, 97, 105, 108, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 72, 105, 115, 116, 111, 114, 121, 82, 101, 97, 100, 68, 101, 116, 97, 105, 108, 115, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 113, 84, 105, 109, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 84, 105, 109, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 113, 84, 105, 109, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 68, 97, 116, 97, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 97, 116, 97, 86, 97, 108, 117, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 86, 97, 108, 117, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 97, 116, 97, 86, 97, 108, 117, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 97, 116, 97, 86, 97, 108, 117, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 77, 111, 100, 105, 102, 105, 99, 97, 116, 105, 111, 110, 73, 110, 102, 111, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 111, 100, 105, 102, 105, 99, 97, 116, 105, 111, 110, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 112, 100, 97, 116, 101, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 72, 105, 115, 116, 111, 114, 121, 85, 112, 100, 97, 116, 101, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 77, 111, 100, 105, 102, 105, 101, 100, 68, 97, 116, 97, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 72, 105, 115, 116, 111, 114, 121, 68, 97, 116, 97, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 97, 116, 97, 86, 97, 108, 117, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 86, 97, 108, 117, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 97, 116, 97, 86, 97, 108, 117, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 97, 116, 97, 86, 97, 108, 117, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 77, 111, 100, 105, 102, 105, 99, 97, 116, 105, 111, 110, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 111, 100, 105, 102, 105, 99, 97, 116, 105, 111, 110, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 77, 111, 100, 105, 102, 105, 99, 97, 116, 105, 111, 110, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 77, 111, 100, 105, 102, 105, 99, 97, 116, 105, 111, 110, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 69, 118, 101, 110, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 69, 118, 101, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 118, 101, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 72, 105, 115, 116, 111, 114, 121, 69, 118, 101, 110, 116, 70, 105, 101, 108, 100, 76, 105, 115, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 69, 118, 101, 110, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 82, 101, 97, 100, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 82, 101, 97, 100, 68, 101, 116, 97, 105, 108, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 105, 109, 101, 115, 116, 97, 109, 112, 115, 84, 111, 82, 101, 116, 117, 114, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 84, 105, 109, 101, 115, 116, 97, 109, 112, 115, 84, 111, 82, 101, 116, 117, 114, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 108, 101, 97, 115, 101, 67, 111, 110, 116, 105, 110, 117, 97, 116, 105, 111, 110, 80, 111, 105, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 78, 111, 100, 101, 115, 84, 111, 82, 101, 97, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 115, 84, 111, 82, 101, 97, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 72, 105, 115, 116, 111, 114, 121, 82, 101, 97, 100, 86, 97, 108, 117, 101, 73, 100, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 78, 111, 100, 101, 115, 84, 111, 82, 101, 97, 100, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 82, 101, 97, 100, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 72, 105, 115, 116, 111, 114, 121, 82, 101, 97, 100, 82, 101, 115, 117, 108, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 86, 97, 108, 117, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 116, 116, 114, 105, 98, 117, 116, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 100, 101, 120, 82, 97, 110, 103, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 97, 116, 97, 86, 97, 108, 117, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 78, 111, 100, 101, 115, 84, 111, 87, 114, 105, 116, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 115, 84, 111, 87, 114, 105, 116, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 87, 114, 105, 116, 101, 86, 97, 108, 117, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 78, 111, 100, 101, 115, 84, 111, 87, 114, 105, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 85, 112, 100, 97, 116, 101, 68, 101, 116, 97, 105, 108, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 85, 112, 100, 97, 116, 101, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 73, 110, 115, 101, 114, 116, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 112, 108, 97, 99, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 85, 112, 100, 97, 116, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 68, 101, 108, 101, 116, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 80, 101, 114, 102, 111, 114, 109, 85, 112, 100, 97, 116, 101, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 73, 110, 115, 101, 114, 116, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 112, 108, 97, 99, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 85, 112, 100, 97, 116, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 109, 111, 118, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 85, 112, 100, 97, 116, 101, 68, 97, 116, 97, 68, 101, 116, 97, 105, 108, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 72, 105, 115, 116, 111, 114, 121, 85, 112, 100, 97, 116, 101, 68, 101, 116, 97, 105, 108, 115, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 72, 105, 115, 116, 111, 114, 121, 85, 112, 100, 97, 116, 101, 68, 101, 116, 97, 105, 108, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 101, 114, 102, 111, 114, 109, 73, 110, 115, 101, 114, 116, 82, 101, 112, 108, 97, 99, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 80, 101, 114, 102, 111, 114, 109, 85, 112, 100, 97, 116, 101, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 85, 112, 100, 97, 116, 101, 86, 97, 108, 117, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 112, 100, 97, 116, 101, 86, 97, 108, 117, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 97, 116, 97, 86, 97, 108, 117, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 85, 112, 100, 97, 116, 101, 86, 97, 108, 117, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 85, 112, 100, 97, 116, 101, 83, 116, 114, 117, 99, 116, 117, 114, 101, 68, 97, 116, 97, 68, 101, 116, 97, 105, 108, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 72, 105, 115, 116, 111, 114, 121, 85, 112, 100, 97, 116, 101, 68, 101, 116, 97, 105, 108, 115, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 72, 105, 115, 116, 111, 114, 121, 85, 112, 100, 97, 116, 101, 68, 101, 116, 97, 105, 108, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 101, 114, 102, 111, 114, 109, 73, 110, 115, 101, 114, 116, 82, 101, 112, 108, 97, 99, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 80, 101, 114, 102, 111, 114, 109, 85, 112, 100, 97, 116, 101, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 85, 112, 100, 97, 116, 101, 86, 97, 108, 117, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 112, 100, 97, 116, 101, 86, 97, 108, 117, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 97, 116, 97, 86, 97, 108, 117, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 85, 112, 100, 97, 116, 101, 86, 97, 108, 117, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 85, 112, 100, 97, 116, 101, 69, 118, 101, 110, 116, 68, 101, 116, 97, 105, 108, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 72, 105, 115, 116, 111, 114, 121, 85, 112, 100, 97, 116, 101, 68, 101, 116, 97, 105, 108, 115, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 72, 105, 115, 116, 111, 114, 121, 85, 112, 100, 97, 116, 101, 68, 101, 116, 97, 105, 108, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 101, 114, 102, 111, 114, 109, 73, 110, 115, 101, 114, 116, 82, 101, 112, 108, 97, 99, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 80, 101, 114, 102, 111, 114, 109, 85, 112, 100, 97, 116, 101, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 70, 105, 108, 116, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 69, 118, 101, 110, 116, 70, 105, 108, 116, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 69, 118, 101, 110, 116, 68, 97, 116, 97, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 118, 101, 110, 116, 68, 97, 116, 97, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 72, 105, 115, 116, 111, 114, 121, 69, 118, 101, 110, 116, 70, 105, 101, 108, 100, 76, 105, 115, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 69, 118, 101, 110, 116, 68, 97, 116, 97, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 101, 108, 101, 116, 101, 82, 97, 119, 77, 111, 100, 105, 102, 105, 101, 100, 68, 101, 116, 97, 105, 108, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 72, 105, 115, 116, 111, 114, 121, 85, 112, 100, 97, 116, 101, 68, 101, 116, 97, 105, 108, 115, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 72, 105, 115, 116, 111, 114, 121, 85, 112, 100, 97, 116, 101, 68, 101, 116, 97, 105, 108, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 115, 68, 101, 108, 101, 116, 101, 77, 111, 100, 105, 102, 105, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 114, 116, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 100, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 101, 108, 101, 116, 101, 65, 116, 84, 105, 109, 101, 68, 101, 116, 97, 105, 108, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 72, 105, 115, 116, 111, 114, 121, 85, 112, 100, 97, 116, 101, 68, 101, 116, 97, 105, 108, 115, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 72, 105, 115, 116, 111, 114, 121, 85, 112, 100, 97, 116, 101, 68, 101, 116, 97, 105, 108, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 113, 84, 105, 109, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 84, 105, 109, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 113, 84, 105, 109, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 101, 108, 101, 116, 101, 69, 118, 101, 110, 116, 68, 101, 116, 97, 105, 108, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 72, 105, 115, 116, 111, 114, 121, 85, 112, 100, 97, 116, 101, 68, 101, 116, 97, 105, 108, 115, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 83, 111, 117, 114, 99, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 72, 105, 115, 116, 111, 114, 121, 85, 112, 100, 97, 116, 101, 68, 101, 116, 97, 105, 108, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 69, 118, 101, 110, 116, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 118, 101, 110, 116, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 69, 118, 101, 110, 116, 73, 100, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 85, 112, 100, 97, 116, 101, 82, 101, 115, 117, 108, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 79, 112, 101, 114, 97, 116, 105, 111, 110, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 79, 112, 101, 114, 97, 116, 105, 111, 110, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 79, 112, 101, 114, 97, 116, 105, 111, 110, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 85, 112, 100, 97, 116, 101, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 72, 105, 115, 116, 111, 114, 121, 85, 112, 100, 97, 116, 101, 68, 101, 116, 97, 105, 108, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 85, 112, 100, 97, 116, 101, 68, 101, 116, 97, 105, 108, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 72, 105, 115, 116, 111, 114, 121, 85, 112, 100, 97, 116, 101, 68, 101, 116, 97, 105, 108, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 85, 112, 100, 97, 116, 101, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 72, 105, 115, 116, 111, 114, 121, 85, 112, 100, 97, 116, 101, 82, 101, 115, 117, 108, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 97, 108, 108, 77, 101, 116, 104, 111, 100, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 79, 98, 106, 101, 99, 116, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 101, 116, 104, 111, 100, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 73, 110, 112, 117, 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 112, 117, 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 86, 97, 114, 105, 97, 110, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 73, 110, 112, 117, 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 97, 108, 108, 77, 101, 116, 104, 111, 100, 82, 101, 115, 117, 108, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 73, 110, 112, 117, 116, 65, 114, 103, 117, 109, 101, 110, 116, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 112, 117, 116, 65, 114, 103, 117, 109, 101, 110, 116, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 73, 110, 112, 117, 116, 65, 114, 103, 117, 109, 101, 110, 116, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 73, 110, 112, 117, 116, 65, 114, 103, 117, 109, 101, 110, 116, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 112, 117, 116, 65, 114, 103, 117, 109, 101, 110, 116, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 73, 110, 112, 117, 116, 65, 114, 103, 117, 109, 101, 110, 116, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 79, 117, 116, 112, 117, 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 79, 117, 116, 112, 117, 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 86, 97, 114, 105, 97, 110, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 79, 117, 116, 112, 117, 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 97, 108, 108, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 77, 101, 116, 104, 111, 100, 115, 84, 111, 67, 97, 108, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 101, 116, 104, 111, 100, 115, 84, 111, 67, 97, 108, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 67, 97, 108, 108, 77, 101, 116, 104, 111, 100, 82, 101, 113, 117, 101, 115, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 77, 101, 116, 104, 111, 100, 115, 84, 111, 67, 97, 108, 108, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 97, 108, 108, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 67, 97, 108, 108, 77, 101, 116, 104, 111, 100, 82, 101, 115, 117, 108, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 77, 111, 110, 105, 116, 111, 114, 105, 110, 103, 77, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 97, 98, 108, 101, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 97, 109, 112, 108, 105, 110, 103, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 112, 111, 114, 116, 105, 110, 103, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 67, 104, 97, 110, 103, 101, 84, 114, 105, 103, 103, 101, 114, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 86, 97, 108, 117, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 86, 97, 108, 117, 101, 84, 105, 109, 101, 115, 116, 97, 109, 112, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 101, 97, 100, 98, 97, 110, 100, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 110, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 98, 115, 111, 108, 117, 116, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 80, 101, 114, 99, 101, 110, 116, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 77, 111, 110, 105, 116, 111, 114, 105, 110, 103, 70, 105, 108, 116, 101, 114, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 67, 104, 97, 110, 103, 101, 70, 105, 108, 116, 101, 114, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 77, 111, 110, 105, 116, 111, 114, 105, 110, 103, 70, 105, 108, 116, 101, 114, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 114, 105, 103, 103, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 68, 97, 116, 97, 67, 104, 97, 110, 103, 101, 84, 114, 105, 103, 103, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 97, 100, 98, 97, 110, 100, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 97, 100, 98, 97, 110, 100, 86, 97, 108, 117, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 69, 118, 101, 110, 116, 70, 105, 108, 116, 101, 114, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 77, 111, 110, 105, 116, 111, 114, 105, 110, 103, 70, 105, 108, 116, 101, 114, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 101, 108, 101, 99, 116, 67, 108, 97, 117, 115, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 108, 101, 99, 116, 67, 108, 97, 117, 115, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 105, 109, 112, 108, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101, 79, 112, 101, 114, 97, 110, 100, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 101, 108, 101, 99, 116, 67, 108, 97, 117, 115, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 104, 101, 114, 101, 67, 108, 97, 117, 115, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 67, 111, 110, 116, 101, 110, 116, 70, 105, 108, 116, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 103, 103, 114, 101, 103, 97, 116, 101, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 83, 101, 114, 118, 101, 114, 67, 97, 112, 97, 98, 105, 108, 105, 116, 105, 101, 115, 68, 101, 102, 97, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 114, 101, 97, 116, 85, 110, 99, 101, 114, 116, 97, 105, 110, 65, 115, 66, 97, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 101, 114, 99, 101, 110, 116, 68, 97, 116, 97, 66, 97, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 101, 114, 99, 101, 110, 116, 68, 97, 116, 97, 71, 111, 111, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 83, 108, 111, 112, 101, 100, 69, 120, 116, 114, 97, 112, 111, 108, 97, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 103, 103, 114, 101, 103, 97, 116, 101, 70, 105, 108, 116, 101, 114, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 77, 111, 110, 105, 116, 111, 114, 105, 110, 103, 70, 105, 108, 116, 101, 114, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 114, 116, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 103, 103, 114, 101, 103, 97, 116, 101, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 114, 111, 99, 101, 115, 115, 105, 110, 103, 73, 110, 116, 101, 114, 118, 97, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 103, 103, 114, 101, 103, 97, 116, 101, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 65, 103, 103, 114, 101, 103, 97, 116, 101, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 77, 111, 110, 105, 116, 111, 114, 105, 110, 103, 70, 105, 108, 116, 101, 114, 82, 101, 115, 117, 108, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 69, 118, 101, 110, 116, 70, 105, 108, 116, 101, 114, 82, 101, 115, 117, 108, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 77, 111, 110, 105, 116, 111, 114, 105, 110, 103, 70, 105, 108, 116, 101, 114, 82, 101, 115, 117, 108, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 101, 108, 101, 99, 116, 67, 108, 97, 117, 115, 101, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 108, 101, 99, 116, 67, 108, 97, 117, 115, 101, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 101, 108, 101, 99, 116, 67, 108, 97, 117, 115, 101, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 101, 108, 101, 99, 116, 67, 108, 97, 117, 115, 101, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 108, 101, 99, 116, 67, 108, 97, 117, 115, 101, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 101, 108, 101, 99, 116, 67, 108, 97, 117, 115, 101, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 104, 101, 114, 101, 67, 108, 97, 117, 115, 101, 82, 101, 115, 117, 108, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 67, 111, 110, 116, 101, 110, 116, 70, 105, 108, 116, 101, 114, 82, 101, 115, 117, 108, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 103, 103, 114, 101, 103, 97, 116, 101, 70, 105, 108, 116, 101, 114, 82, 101, 115, 117, 108, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 77, 111, 110, 105, 116, 111, 114, 105, 110, 103, 70, 105, 108, 116, 101, 114, 82, 101, 115, 117, 108, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 118, 105, 115, 101, 100, 83, 116, 97, 114, 116, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 118, 105, 115, 101, 100, 80, 114, 111, 99, 101, 115, 115, 105, 110, 103, 73, 110, 116, 101, 114, 118, 97, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 118, 105, 115, 101, 100, 65, 103, 103, 114, 101, 103, 97, 116, 101, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 65, 103, 103, 114, 101, 103, 97, 116, 101, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 77, 111, 110, 105, 116, 111, 114, 105, 110, 103, 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 108, 105, 101, 110, 116, 72, 97, 110, 100, 108, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 97, 109, 112, 108, 105, 110, 103, 73, 110, 116, 101, 114, 118, 97, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 70, 105, 108, 116, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 81, 117, 101, 117, 101, 83, 105, 122, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 99, 97, 114, 100, 79, 108, 100, 101, 115, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 67, 114, 101, 97, 116, 101, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 116, 101, 109, 84, 111, 77, 111, 110, 105, 116, 111, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 97, 100, 86, 97, 108, 117, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 111, 110, 105, 116, 111, 114, 105, 110, 103, 77, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 77, 111, 110, 105, 116, 111, 114, 105, 110, 103, 77, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 101, 100, 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 77, 111, 110, 105, 116, 111, 114, 105, 110, 103, 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 67, 114, 101, 97, 116, 101, 82, 101, 115, 117, 108, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 118, 105, 115, 101, 100, 83, 97, 109, 112, 108, 105, 110, 103, 73, 110, 116, 101, 114, 118, 97, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 118, 105, 115, 101, 100, 81, 117, 101, 117, 101, 83, 105, 122, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 70, 105, 108, 116, 101, 114, 82, 101, 115, 117, 108, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 114, 101, 97, 116, 101, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 115, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 105, 109, 101, 115, 116, 97, 109, 112, 115, 84, 111, 82, 101, 116, 117, 114, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 84, 105, 109, 101, 115, 116, 97, 109, 112, 115, 84, 111, 82, 101, 116, 117, 114, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 73, 116, 101, 109, 115, 84, 111, 67, 114, 101, 97, 116, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 116, 101, 109, 115, 84, 111, 67, 114, 101, 97, 116, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 67, 114, 101, 97, 116, 101, 82, 101, 113, 117, 101, 115, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 73, 116, 101, 109, 115, 84, 111, 67, 114, 101, 97, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 114, 101, 97, 116, 101, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 115, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 67, 114, 101, 97, 116, 101, 82, 101, 115, 117, 108, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 77, 111, 100, 105, 102, 121, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 101, 100, 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 77, 111, 110, 105, 116, 111, 114, 105, 110, 103, 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 77, 111, 100, 105, 102, 121, 82, 101, 115, 117, 108, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 118, 105, 115, 101, 100, 83, 97, 109, 112, 108, 105, 110, 103, 73, 110, 116, 101, 114, 118, 97, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 118, 105, 115, 101, 100, 81, 117, 101, 117, 101, 83, 105, 122, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 70, 105, 108, 116, 101, 114, 82, 101, 115, 117, 108, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 77, 111, 100, 105, 102, 121, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 115, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 105, 109, 101, 115, 116, 97, 109, 112, 115, 84, 111, 82, 101, 116, 117, 114, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 84, 105, 109, 101, 115, 116, 97, 109, 112, 115, 84, 111, 82, 101, 116, 117, 114, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 73, 116, 101, 109, 115, 84, 111, 77, 111, 100, 105, 102, 121, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 116, 101, 109, 115, 84, 111, 77, 111, 100, 105, 102, 121, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 77, 111, 100, 105, 102, 121, 82, 101, 113, 117, 101, 115, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 73, 116, 101, 109, 115, 84, 111, 77, 111, 100, 105, 102, 121, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 77, 111, 100, 105, 102, 121, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 115, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 77, 111, 100, 105, 102, 121, 82, 101, 115, 117, 108, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 116, 77, 111, 110, 105, 116, 111, 114, 105, 110, 103, 77, 111, 100, 101, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 111, 110, 105, 116, 111, 114, 105, 110, 103, 77, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 77, 111, 110, 105, 116, 111, 114, 105, 110, 103, 77, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 73, 100, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 116, 77, 111, 110, 105, 116, 111, 114, 105, 110, 103, 77, 111, 100, 101, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 116, 84, 114, 105, 103, 103, 101, 114, 105, 110, 103, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 114, 105, 103, 103, 101, 114, 105, 110, 103, 73, 116, 101, 109, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 76, 105, 110, 107, 115, 84, 111, 65, 100, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 105, 110, 107, 115, 84, 111, 65, 100, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 76, 105, 110, 107, 115, 84, 111, 65, 100, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 76, 105, 110, 107, 115, 84, 111, 82, 101, 109, 111, 118, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 105, 110, 107, 115, 84, 111, 82, 101, 109, 111, 118, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 76, 105, 110, 107, 115, 84, 111, 82, 101, 109, 111, 118, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 116, 84, 114, 105, 103, 103, 101, 114, 105, 110, 103, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 65, 100, 100, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 100, 100, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 65, 100, 100, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 65, 100, 100, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 100, 100, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 65, 100, 100, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 109, 111, 118, 101, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 109, 111, 118, 101, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 109, 111, 118, 101, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 109, 111, 118, 101, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 109, 111, 118, 101, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 109, 111, 118, 101, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 101, 108, 101, 116, 101, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 115, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 73, 100, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 101, 108, 101, 116, 101, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 115, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 114, 101, 97, 116, 101, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 101, 100, 80, 117, 98, 108, 105, 115, 104, 105, 110, 103, 73, 110, 116, 101, 114, 118, 97, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 101, 100, 76, 105, 102, 101, 116, 105, 109, 101, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 101, 100, 77, 97, 120, 75, 101, 101, 112, 65, 108, 105, 118, 101, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 120, 78, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 115, 80, 101, 114, 80, 117, 98, 108, 105, 115, 104, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 105, 110, 103, 69, 110, 97, 98, 108, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 114, 105, 111, 114, 105, 116, 121, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 114, 101, 97, 116, 101, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 118, 105, 115, 101, 100, 80, 117, 98, 108, 105, 115, 104, 105, 110, 103, 73, 110, 116, 101, 114, 118, 97, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 118, 105, 115, 101, 100, 76, 105, 102, 101, 116, 105, 109, 101, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 118, 105, 115, 101, 100, 77, 97, 120, 75, 101, 101, 112, 65, 108, 105, 118, 101, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 77, 111, 100, 105, 102, 121, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 101, 100, 80, 117, 98, 108, 105, 115, 104, 105, 110, 103, 73, 110, 116, 101, 114, 118, 97, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 101, 100, 76, 105, 102, 101, 116, 105, 109, 101, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 101, 100, 77, 97, 120, 75, 101, 101, 112, 65, 108, 105, 118, 101, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 120, 78, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 115, 80, 101, 114, 80, 117, 98, 108, 105, 115, 104, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 114, 105, 111, 114, 105, 116, 121, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 77, 111, 100, 105, 102, 121, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 118, 105, 115, 101, 100, 80, 117, 98, 108, 105, 115, 104, 105, 110, 103, 73, 110, 116, 101, 114, 118, 97, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 118, 105, 115, 101, 100, 76, 105, 102, 101, 116, 105, 109, 101, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 118, 105, 115, 101, 100, 77, 97, 120, 75, 101, 101, 112, 65, 108, 105, 118, 101, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 116, 80, 117, 98, 108, 105, 115, 104, 105, 110, 103, 77, 111, 100, 101, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 105, 110, 103, 69, 110, 97, 98, 108, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 73, 100, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 116, 80, 117, 98, 108, 105, 115, 104, 105, 110, 103, 77, 111, 100, 101, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 77, 101, 115, 115, 97, 103, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 113, 117, 101, 110, 99, 101, 78, 117, 109, 98, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 78, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 68, 97, 116, 97, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 68, 97, 116, 97, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 78, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 68, 97, 116, 97, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 68, 97, 116, 97, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 67, 104, 97, 110, 103, 101, 78, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 68, 97, 116, 97, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 78, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 78, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 108, 105, 101, 110, 116, 72, 97, 110, 100, 108, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 97, 116, 97, 86, 97, 108, 117, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 69, 118, 101, 110, 116, 78, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 76, 105, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 68, 97, 116, 97, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 69, 118, 101, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 118, 101, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 69, 118, 101, 110, 116, 70, 105, 101, 108, 100, 76, 105, 115, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 69, 118, 101, 110, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 69, 118, 101, 110, 116, 70, 105, 101, 108, 100, 76, 105, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 108, 105, 101, 110, 116, 72, 97, 110, 100, 108, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 69, 118, 101, 110, 116, 70, 105, 101, 108, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 118, 101, 110, 116, 70, 105, 101, 108, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 86, 97, 114, 105, 97, 110, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 69, 118, 101, 110, 116, 70, 105, 101, 108, 100, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 69, 118, 101, 110, 116, 70, 105, 101, 108, 100, 76, 105, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 69, 118, 101, 110, 116, 70, 105, 101, 108, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 118, 101, 110, 116, 70, 105, 101, 108, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 86, 97, 114, 105, 97, 110, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 69, 118, 101, 110, 116, 70, 105, 101, 108, 100, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 67, 104, 97, 110, 103, 101, 78, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 116, 110, 115, 58, 78, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 68, 97, 116, 97, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 65, 99, 107, 110, 111, 119, 108, 101, 100, 103, 101, 109, 101, 110, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 113, 117, 101, 110, 99, 101, 78, 117, 109, 98, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 65, 99, 107, 110, 111, 119, 108, 101, 100, 103, 101, 109, 101, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 65, 99, 107, 110, 111, 119, 108, 101, 100, 103, 101, 109, 101, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 65, 99, 107, 110, 111, 119, 108, 101, 100, 103, 101, 109, 101, 110, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 65, 99, 107, 110, 111, 119, 108, 101, 100, 103, 101, 109, 101, 110, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 65, 118, 97, 105, 108, 97, 98, 108, 101, 83, 101, 113, 117, 101, 110, 99, 101, 78, 117, 109, 98, 101, 114, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 118, 97, 105, 108, 97, 98, 108, 101, 83, 101, 113, 117, 101, 110, 99, 101, 78, 117, 109, 98, 101, 114, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 65, 118, 97, 105, 108, 97, 98, 108, 101, 83, 101, 113, 117, 101, 110, 99, 101, 78, 117, 109, 98, 101, 114, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 111, 114, 101, 78, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 77, 101, 115, 115, 97, 103, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 78, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 77, 101, 115, 115, 97, 103, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 112, 117, 98, 108, 105, 115, 104, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 116, 114, 97, 110, 115, 109, 105, 116, 83, 101, 113, 117, 101, 110, 99, 101, 78, 117, 109, 98, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 112, 117, 98, 108, 105, 115, 104, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 77, 101, 115, 115, 97, 103, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 78, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 77, 101, 115, 115, 97, 103, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 84, 114, 97, 110, 115, 102, 101, 114, 82, 101, 115, 117, 108, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 65, 118, 97, 105, 108, 97, 98, 108, 101, 83, 101, 113, 117, 101, 110, 99, 101, 78, 117, 109, 98, 101, 114, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 118, 97, 105, 108, 97, 98, 108, 101, 83, 101, 113, 117, 101, 110, 99, 101, 78, 117, 109, 98, 101, 114, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 65, 118, 97, 105, 108, 97, 98, 108, 101, 83, 101, 113, 117, 101, 110, 99, 101, 78, 117, 109, 98, 101, 114, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 84, 114, 97, 110, 115, 102, 101, 114, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 115, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 73, 100, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 110, 100, 73, 110, 105, 116, 105, 97, 108, 86, 97, 108, 117, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 84, 114, 97, 110, 115, 102, 101, 114, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 115, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 84, 114, 97, 110, 115, 102, 101, 114, 82, 101, 115, 117, 108, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 101, 108, 101, 116, 101, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 115, 82, 101, 113, 117, 101, 115, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 113, 117, 101, 115, 116, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 73, 100, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 101, 108, 101, 116, 101, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 115, 82, 101, 115, 112, 111, 110, 115, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 101, 115, 112, 111, 110, 115, 101, 72, 101, 97, 100, 101, 114, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 115, 117, 108, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 82, 101, 115, 117, 108, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 66, 117, 105, 108, 100, 73, 110, 102, 111, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 114, 111, 100, 117, 99, 116, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 110, 117, 102, 97, 99, 116, 117, 114, 101, 114, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 114, 111, 100, 117, 99, 116, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 111, 102, 116, 119, 97, 114, 101, 86, 101, 114, 115, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 117, 105, 108, 100, 78, 117, 109, 98, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 117, 105, 108, 100, 68, 97, 116, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 100, 117, 110, 100, 97, 110, 99, 121, 83, 117, 112, 112, 111, 114, 116, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 110, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 67, 111, 108, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 87, 97, 114, 109, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 72, 111, 116, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 84, 114, 97, 110, 115, 112, 97, 114, 101, 110, 116, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 72, 111, 116, 65, 110, 100, 77, 105, 114, 114, 111, 114, 101, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 53, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 83, 116, 97, 116, 101, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 82, 117, 110, 110, 105, 110, 103, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 70, 97, 105, 108, 101, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 117, 115, 112, 101, 110, 100, 101, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 83, 104, 117, 116, 100, 111, 119, 110, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 84, 101, 115, 116, 34, 32, 86, 97, 108, 117, 101, 61, 34, 53, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 67, 111, 109, 109, 117, 110, 105, 99, 97, 116, 105, 111, 110, 70, 97, 117, 108, 116, 34, 32, 86, 97, 108, 117, 101, 61, 34, 54, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 85, 110, 107, 110, 111, 119, 110, 34, 32, 86, 97, 108, 117, 101, 61, 34, 55, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 100, 117, 110, 100, 97, 110, 116, 83, 101, 114, 118, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 105, 99, 101, 76, 101, 118, 101, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 83, 116, 97, 116, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 101, 114, 83, 116, 97, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 69, 110, 100, 112, 111, 105, 110, 116, 85, 114, 108, 76, 105, 115, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 69, 110, 100, 112, 111, 105, 110, 116, 85, 114, 108, 76, 105, 115, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 100, 112, 111, 105, 110, 116, 85, 114, 108, 76, 105, 115, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 69, 110, 100, 112, 111, 105, 110, 116, 85, 114, 108, 76, 105, 115, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 78, 101, 116, 119, 111, 114, 107, 71, 114, 111, 117, 112, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 78, 101, 116, 119, 111, 114, 107, 80, 97, 116, 104, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 101, 116, 119, 111, 114, 107, 80, 97, 116, 104, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 69, 110, 100, 112, 111, 105, 110, 116, 85, 114, 108, 76, 105, 115, 116, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 78, 101, 116, 119, 111, 114, 107, 80, 97, 116, 104, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 97, 109, 112, 108, 105, 110, 103, 73, 110, 116, 101, 114, 118, 97, 108, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 115, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 97, 109, 112, 108, 105, 110, 103, 73, 110, 116, 101, 114, 118, 97, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 120, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 97, 98, 108, 101, 100, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 115, 83, 117, 109, 109, 97, 114, 121, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 86, 105, 101, 119, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 117, 114, 114, 101, 110, 116, 83, 101, 115, 115, 105, 111, 110, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 117, 109, 117, 108, 97, 116, 101, 100, 83, 101, 115, 115, 105, 111, 110, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 82, 101, 106, 101, 99, 116, 101, 100, 83, 101, 115, 115, 105, 111, 110, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 106, 101, 99, 116, 101, 100, 83, 101, 115, 115, 105, 111, 110, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 115, 115, 105, 111, 110, 84, 105, 109, 101, 111, 117, 116, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 115, 115, 105, 111, 110, 65, 98, 111, 114, 116, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 117, 114, 114, 101, 110, 116, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 117, 109, 117, 108, 97, 116, 101, 100, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 105, 110, 103, 73, 110, 116, 101, 114, 118, 97, 108, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 82, 101, 106, 101, 99, 116, 101, 100, 82, 101, 113, 117, 101, 115, 116, 115, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 106, 101, 99, 116, 101, 100, 82, 101, 113, 117, 101, 115, 116, 115, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 83, 116, 97, 116, 117, 115, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 114, 116, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 117, 114, 114, 101, 110, 116, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 101, 114, 83, 116, 97, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 117, 105, 108, 100, 73, 110, 102, 111, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 66, 117, 105, 108, 100, 73, 110, 102, 111, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 111, 110, 100, 115, 84, 105, 108, 108, 83, 104, 117, 116, 100, 111, 119, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 104, 117, 116, 100, 111, 119, 110, 82, 101, 97, 115, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 115, 115, 105, 111, 110, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 115, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 115, 115, 105, 111, 110, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 115, 115, 105, 111, 110, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 108, 105, 101, 110, 116, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 65, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 101, 114, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 100, 112, 111, 105, 110, 116, 85, 114, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 76, 111, 99, 97, 108, 101, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 111, 99, 97, 108, 101, 73, 100, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 76, 111, 99, 97, 108, 101, 73, 100, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 99, 116, 117, 97, 108, 83, 101, 115, 115, 105, 111, 110, 84, 105, 109, 101, 111, 117, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 120, 82, 101, 115, 112, 111, 110, 115, 101, 77, 101, 115, 115, 97, 103, 101, 83, 105, 122, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 108, 105, 101, 110, 116, 67, 111, 110, 110, 101, 99, 116, 105, 111, 110, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 108, 105, 101, 110, 116, 76, 97, 115, 116, 67, 111, 110, 116, 97, 99, 116, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 117, 114, 114, 101, 110, 116, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 115, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 117, 114, 114, 101, 110, 116, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 115, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 117, 114, 114, 101, 110, 116, 80, 117, 98, 108, 105, 115, 104, 82, 101, 113, 117, 101, 115, 116, 115, 73, 110, 81, 117, 101, 117, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 111, 116, 97, 108, 82, 101, 113, 117, 101, 115, 116, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 110, 97, 117, 116, 104, 111, 114, 105, 122, 101, 100, 82, 101, 113, 117, 101, 115, 116, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 97, 100, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 82, 101, 97, 100, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 87, 114, 105, 116, 101, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 72, 105, 115, 116, 111, 114, 121, 85, 112, 100, 97, 116, 101, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 97, 108, 108, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 114, 101, 97, 116, 101, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 115, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 111, 100, 105, 102, 121, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 115, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 116, 77, 111, 110, 105, 116, 111, 114, 105, 110, 103, 77, 111, 100, 101, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 116, 84, 114, 105, 103, 103, 101, 114, 105, 110, 103, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 108, 101, 116, 101, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 115, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 114, 101, 97, 116, 101, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 111, 100, 105, 102, 121, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 116, 80, 117, 98, 108, 105, 115, 104, 105, 110, 103, 77, 111, 100, 101, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 112, 117, 98, 108, 105, 115, 104, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 114, 97, 110, 115, 102, 101, 114, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 115, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 108, 101, 116, 101, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 115, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 100, 100, 78, 111, 100, 101, 115, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 100, 100, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 108, 101, 116, 101, 78, 111, 100, 101, 115, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 108, 101, 116, 101, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 66, 114, 111, 119, 115, 101, 78, 101, 120, 116, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 114, 97, 110, 115, 108, 97, 116, 101, 66, 114, 111, 119, 115, 101, 80, 97, 116, 104, 115, 84, 111, 78, 111, 100, 101, 73, 100, 115, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 81, 117, 101, 114, 121, 70, 105, 114, 115, 116, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 81, 117, 101, 114, 121, 78, 101, 120, 116, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 103, 105, 115, 116, 101, 114, 78, 111, 100, 101, 115, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 110, 114, 101, 103, 105, 115, 116, 101, 114, 78, 111, 100, 101, 115, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 115, 115, 105, 111, 110, 83, 101, 99, 117, 114, 105, 116, 121, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 115, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 115, 115, 105, 111, 110, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 108, 105, 101, 110, 116, 85, 115, 101, 114, 73, 100, 79, 102, 83, 101, 115, 115, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 67, 108, 105, 101, 110, 116, 85, 115, 101, 114, 73, 100, 72, 105, 115, 116, 111, 114, 121, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 108, 105, 101, 110, 116, 85, 115, 101, 114, 73, 100, 72, 105, 115, 116, 111, 114, 121, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 67, 108, 105, 101, 110, 116, 85, 115, 101, 114, 73, 100, 72, 105, 115, 116, 111, 114, 121, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 117, 116, 104, 101, 110, 116, 105, 99, 97, 116, 105, 111, 110, 77, 101, 99, 104, 97, 110, 105, 115, 109, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 99, 111, 100, 105, 110, 103, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 114, 97, 110, 115, 112, 111, 114, 116, 80, 114, 111, 116, 111, 99, 111, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 77, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 77, 101, 115, 115, 97, 103, 101, 83, 101, 99, 117, 114, 105, 116, 121, 77, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 99, 117, 114, 105, 116, 121, 80, 111, 108, 105, 99, 121, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 108, 105, 101, 110, 116, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 114, 118, 105, 99, 101, 67, 111, 117, 110, 116, 101, 114, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 111, 116, 97, 108, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 114, 114, 111, 114, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 82, 101, 115, 117, 108, 116, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 83, 116, 97, 116, 117, 115, 67, 111, 100, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 73, 110, 102, 111, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 115, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 101, 115, 115, 105, 111, 110, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 83, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 114, 105, 111, 114, 105, 116, 121, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 105, 110, 103, 73, 110, 116, 101, 114, 118, 97, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 120, 75, 101, 101, 112, 65, 108, 105, 118, 101, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 120, 76, 105, 102, 101, 116, 105, 109, 101, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 97, 120, 78, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 115, 80, 101, 114, 80, 117, 98, 108, 105, 115, 104, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 105, 110, 103, 69, 110, 97, 98, 108, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 111, 111, 108, 101, 97, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 111, 100, 105, 102, 121, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 97, 98, 108, 101, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 97, 98, 108, 101, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 112, 117, 98, 108, 105, 115, 104, 82, 101, 113, 117, 101, 115, 116, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 112, 117, 98, 108, 105, 115, 104, 77, 101, 115, 115, 97, 103, 101, 82, 101, 113, 117, 101, 115, 116, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 112, 117, 98, 108, 105, 115, 104, 77, 101, 115, 115, 97, 103, 101, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 114, 97, 110, 115, 102, 101, 114, 82, 101, 113, 117, 101, 115, 116, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 114, 97, 110, 115, 102, 101, 114, 114, 101, 100, 84, 111, 65, 108, 116, 67, 108, 105, 101, 110, 116, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 114, 97, 110, 115, 102, 101, 114, 114, 101, 100, 84, 111, 83, 97, 109, 101, 67, 108, 105, 101, 110, 116, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 80, 117, 98, 108, 105, 115, 104, 82, 101, 113, 117, 101, 115, 116, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 67, 104, 97, 110, 103, 101, 78, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 115, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 118, 101, 110, 116, 78, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 115, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 115, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 97, 116, 101, 80, 117, 98, 108, 105, 115, 104, 82, 101, 113, 117, 101, 115, 116, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 117, 114, 114, 101, 110, 116, 75, 101, 101, 112, 65, 108, 105, 118, 101, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 117, 114, 114, 101, 110, 116, 76, 105, 102, 101, 116, 105, 109, 101, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 110, 97, 99, 107, 110, 111, 119, 108, 101, 100, 103, 101, 100, 77, 101, 115, 115, 97, 103, 101, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 99, 97, 114, 100, 101, 100, 77, 101, 115, 115, 97, 103, 101, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 97, 98, 108, 101, 100, 77, 111, 110, 105, 116, 111, 114, 101, 100, 73, 116, 101, 109, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 111, 110, 105, 116, 111, 114, 105, 110, 103, 81, 117, 101, 117, 101, 79, 118, 101, 114, 102, 108, 111, 119, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 101, 120, 116, 83, 101, 113, 117, 101, 110, 99, 101, 78, 117, 109, 98, 101, 114, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 118, 101, 110, 116, 81, 117, 101, 117, 101, 79, 118, 101, 114, 70, 108, 111, 119, 67, 111, 117, 110, 116, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 85, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 77, 111, 100, 101, 108, 67, 104, 97, 110, 103, 101, 83, 116, 114, 117, 99, 116, 117, 114, 101, 86, 101, 114, 98, 77, 97, 115, 107, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 65, 100, 100, 101, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 78, 111, 100, 101, 68, 101, 108, 101, 116, 101, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 65, 100, 100, 101, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 82, 101, 102, 101, 114, 101, 110, 99, 101, 68, 101, 108, 101, 116, 101, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 56, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 68, 97, 116, 97, 84, 121, 112, 101, 67, 104, 97, 110, 103, 101, 100, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 54, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 77, 111, 100, 101, 108, 67, 104, 97, 110, 103, 101, 83, 116, 114, 117, 99, 116, 117, 114, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 102, 102, 101, 99, 116, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 102, 102, 101, 99, 116, 101, 100, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 101, 114, 98, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 66, 121, 116, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 83, 101, 109, 97, 110, 116, 105, 99, 67, 104, 97, 110, 103, 101, 83, 116, 114, 117, 99, 116, 117, 114, 101, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 102, 102, 101, 99, 116, 101, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 102, 102, 101, 99, 116, 101, 100, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 82, 97, 110, 103, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 111, 119, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 72, 105, 103, 104, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 69, 85, 73, 110, 102, 111, 114, 109, 97, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 97, 109, 101, 115, 112, 97, 99, 101, 85, 114, 105, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 110, 105, 116, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 105, 115, 112, 108, 97, 121, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 120, 105, 115, 83, 99, 97, 108, 101, 69, 110, 117, 109, 101, 114, 97, 116, 105, 111, 110, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 76, 105, 110, 101, 97, 114, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 76, 111, 103, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 76, 110, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 67, 111, 109, 112, 108, 101, 120, 78, 117, 109, 98, 101, 114, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 97, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 70, 108, 111, 97, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 109, 97, 103, 105, 110, 97, 114, 121, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 70, 108, 111, 97, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 68, 111, 117, 98, 108, 101, 67, 111, 109, 112, 108, 101, 120, 78, 117, 109, 98, 101, 114, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 82, 101, 97, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 109, 97, 103, 105, 110, 97, 114, 121, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 120, 105, 115, 73, 110, 102, 111, 114, 109, 97, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 110, 103, 105, 110, 101, 101, 114, 105, 110, 103, 85, 110, 105, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 69, 85, 73, 110, 102, 111, 114, 109, 97, 116, 105, 111, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 69, 85, 82, 97, 110, 103, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 82, 97, 110, 103, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 84, 105, 116, 108, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 76, 111, 99, 97, 108, 105, 122, 101, 100, 84, 101, 120, 116, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 120, 105, 115, 83, 99, 97, 108, 101, 84, 121, 112, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 65, 120, 105, 115, 83, 99, 97, 108, 101, 69, 110, 117, 109, 101, 114, 97, 116, 105, 111, 110, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 65, 120, 105, 115, 83, 116, 101, 112, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 120, 105, 115, 83, 116, 101, 112, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 65, 120, 105, 115, 83, 116, 101, 112, 115, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 88, 86, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 88, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 111, 117, 98, 108, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 86, 97, 108, 117, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 70, 108, 111, 97, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 80, 114, 111, 103, 114, 97, 109, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 114, 101, 97, 116, 101, 83, 101, 115, 115, 105, 111, 110, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 114, 101, 97, 116, 101, 67, 108, 105, 101, 110, 116, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 118, 111, 99, 97, 116, 105, 111, 110, 67, 114, 101, 97, 116, 105, 111, 110, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 97, 115, 116, 84, 114, 97, 110, 115, 105, 116, 105, 111, 110, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 67, 97, 108, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 83, 101, 115, 115, 105, 111, 110, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 73, 110, 112, 117, 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 73, 110, 112, 117, 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 65, 114, 103, 117, 109, 101, 110, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 73, 110, 112, 117, 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 79, 117, 116, 112, 117, 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 79, 117, 116, 112, 117, 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 65, 114, 103, 117, 109, 101, 110, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 79, 117, 116, 112, 117, 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 67, 97, 108, 108, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 82, 101, 116, 117, 114, 110, 83, 116, 97, 116, 117, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 116, 97, 116, 117, 115, 82, 101, 115, 117, 108, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 80, 114, 111, 103, 114, 97, 109, 68, 105, 97, 103, 110, 111, 115, 116, 105, 99, 50, 68, 97, 116, 97, 84, 121, 112, 101, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 114, 101, 97, 116, 101, 83, 101, 115, 115, 105, 111, 110, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 67, 114, 101, 97, 116, 101, 67, 108, 105, 101, 110, 116, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 73, 110, 118, 111, 99, 97, 116, 105, 111, 110, 67, 114, 101, 97, 116, 105, 111, 110, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 97, 115, 116, 84, 114, 97, 110, 115, 105, 116, 105, 111, 110, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 67, 97, 108, 108, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 83, 101, 115, 115, 105, 111, 110, 73, 100, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 78, 111, 100, 101, 73, 100, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 73, 110, 112, 117, 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 73, 110, 112, 117, 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 65, 114, 103, 117, 109, 101, 110, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 73, 110, 112, 117, 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 79, 117, 116, 112, 117, 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 79, 117, 116, 112, 117, 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 65, 114, 103, 117, 109, 101, 110, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 79, 117, 116, 112, 117, 116, 65, 114, 103, 117, 109, 101, 110, 116, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 73, 110, 112, 117, 116, 86, 97, 108, 117, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 73, 110, 112, 117, 116, 86, 97, 108, 117, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 86, 97, 114, 105, 97, 110, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 73, 110, 112, 117, 116, 86, 97, 108, 117, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 78, 111, 79, 102, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 79, 117, 116, 112, 117, 116, 86, 97, 108, 117, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 73, 110, 116, 51, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 79, 117, 116, 112, 117, 116, 86, 97, 108, 117, 101, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 117, 97, 58, 86, 97, 114, 105, 97, 110, 116, 34, 32, 76, 101, 110, 103, 116, 104, 70, 105, 101, 108, 100, 61, 34, 78, 111, 79, 102, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 79, 117, 116, 112, 117, 116, 86, 97, 108, 117, 101, 115, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 67, 97, 108, 108, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 76, 97, 115, 116, 77, 101, 116, 104, 111, 100, 82, 101, 116, 117, 114, 110, 83, 116, 97, 116, 117, 115, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 116, 110, 115, 58, 83, 116, 97, 116, 117, 115, 82, 101, 115, 117, 108, 116, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 65, 110, 110, 111, 116, 97, 116, 105, 111, 110, 34, 32, 66, 97, 115, 101, 84, 121, 112, 101, 61, 34, 117, 97, 58, 69, 120, 116, 101, 110, 115, 105, 111, 110, 79, 98, 106, 101, 99, 116, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 77, 101, 115, 115, 97, 103, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 85, 115, 101, 114, 78, 97, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 83, 116, 114, 105, 110, 103, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 70, 105, 101, 108, 100, 32, 78, 97, 109, 101, 61, 34, 65, 110, 110, 111, 116, 97, 116, 105, 111, 110, 84, 105, 109, 101, 34, 32, 84, 121, 112, 101, 78, 97, 109, 101, 61, 34, 111, 112, 99, 58, 68, 97, 116, 101, 84, 105, 109, 101, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 83, 116, 114, 117, 99, 116, 117, 114, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 32, 78, 97, 109, 101, 61, 34, 69, 120, 99, 101, 112, 116, 105, 111, 110, 68, 101, 118, 105, 97, 116, 105, 111, 110, 70, 111, 114, 109, 97, 116, 34, 32, 76, 101, 110, 103, 116, 104, 73, 110, 66, 105, 116, 115, 61, 34, 51, 50, 34, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 65, 98, 115, 111, 108, 117, 116, 101, 86, 97, 108, 117, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 48, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 80, 101, 114, 99, 101, 110, 116, 79, 102, 86, 97, 108, 117, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 49, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 80, 101, 114, 99, 101, 110, 116, 79, 102, 82, 97, 110, 103, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 50, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 80, 101, 114, 99, 101, 110, 116, 79, 102, 69, 85, 82, 97, 110, 103, 101, 34, 32, 86, 97, 108, 117, 101, 61, 34, 51, 34, 32, 47, 62, 13, 10, 32, 32, 32, 32, 60, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 86, 97, 108, 117, 101, 32, 78, 97, 109, 101, 61, 34, 85, 110, 107, 110, 111, 119, 110, 34, 32, 86, 97, 108, 117, 101, 61, 34, 52, 34, 32, 47, 62, 13, 10, 32, 32, 60, 47, 111, 112, 99, 58, 69, 110, 117, 109, 101, 114, 97, 116, 101, 100, 84, 121, 112, 101, 62, 13, 10, 13, 10, 60, 47, 111, 112, 99, 58, 84, 121, 112, 101, 68, 105, 99, 116, 105, 111, 110, 97, 114, 121, 62}; static UA_StatusCode function_namespace0_generated_185_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 15LU); 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->length = 177218; variablenode_ns_0_i_7617_variant_DataContents->data = (UA_Byte *)(void*)(uintptr_t)variablenode_ns_0_i_7617_variant_DataContents_byteArray; 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], 7617LU), UA_NODEID_NUMERIC(ns[0], 93LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "Opc.Ua"), UA_NODEID_NUMERIC(ns[0], 72LU), (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_185_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 7617LU) ); } /* NamespaceUri - ns=0;i=107 */ static UA_StatusCode function_namespace0_generated_186_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12LU); 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], 107LU), UA_NODEID_NUMERIC(ns[0], 72LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "NamespaceUri"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 107LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 80LU), true); return retVal; } static UA_StatusCode function_namespace0_generated_186_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 107LU) ); } /* DataTypeVersion - ns=0;i=106 */ static UA_StatusCode function_namespace0_generated_187_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12LU); attr.displayName = UA_LOCALIZEDTEXT("", "DataTypeVersion"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 106LU), UA_NODEID_NUMERIC(ns[0], 72LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "DataTypeVersion"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 106LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 80LU), true); return retVal; } static UA_StatusCode function_namespace0_generated_187_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 106LU) ); } /* DataTypeDescriptionType - ns=0;i=69 */ static UA_StatusCode function_namespace0_generated_188_begin(UA_Server *server, UA_UInt16* ns) { UA_StatusCode retVal = UA_STATUSCODE_GOOD; UA_VariableTypeAttributes attr = UA_VariableTypeAttributes_default; /* Value rank inherited */ attr.valueRank = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12LU); attr.displayName = UA_LOCALIZEDTEXT("", "DataTypeDescriptionType"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLETYPE, UA_NODEID_NUMERIC(ns[0], 69LU), UA_NODEID_NUMERIC(ns[0], 63LU), UA_NODEID_NUMERIC(ns[0], 45LU), UA_QUALIFIEDNAME(ns[0], "DataTypeDescriptionType"), UA_NODEID_NUMERIC(ns[0], 0LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_188_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 69LU) ); } /* EnumValueType - ns=0;i=7656 */ static UA_StatusCode function_namespace0_generated_189_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12LU); 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], 7656LU), UA_NODEID_NUMERIC(ns[0], 7617LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "EnumValueType"), UA_NODEID_NUMERIC(ns[0], 69LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); UA_String_delete(variablenode_ns_0_i_7656_variant_DataContents); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 7656LU), UA_NODEID_NUMERIC(ns[0], 39LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 8251LU), false); return retVal; } static UA_StatusCode function_namespace0_generated_189_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 7656LU) ); } /* Argument - ns=0;i=7650 */ static UA_StatusCode function_namespace0_generated_190_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12LU); 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], 7650LU), UA_NODEID_NUMERIC(ns[0], 7617LU), UA_NODEID_NUMERIC(ns[0], 47LU), UA_QUALIFIEDNAME(ns[0], "Argument"), UA_NODEID_NUMERIC(ns[0], 69LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); UA_String_delete(variablenode_ns_0_i_7650_variant_DataContents); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 7650LU), UA_NODEID_NUMERIC(ns[0], 39LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 298LU), false); return retVal; } static UA_StatusCode function_namespace0_generated_190_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 7650LU) ); } /* DictionaryFragment - ns=0;i=105 */ static UA_StatusCode function_namespace0_generated_191_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 15LU); attr.displayName = UA_LOCALIZEDTEXT("", "DictionaryFragment"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 105LU), UA_NODEID_NUMERIC(ns[0], 69LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "DictionaryFragment"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 105LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 80LU), true); return retVal; } static UA_StatusCode function_namespace0_generated_191_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 105LU) ); } /* DataTypeVersion - ns=0;i=104 */ static UA_StatusCode function_namespace0_generated_192_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 = -2; attr.dataType = UA_NODEID_NUMERIC(ns[0], 12LU); attr.displayName = UA_LOCALIZEDTEXT("", "DataTypeVersion"); retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NODEID_NUMERIC(ns[0], 104LU), UA_NODEID_NUMERIC(ns[0], 69LU), UA_NODEID_NUMERIC(ns[0], 46LU), UA_QUALIFIEDNAME(ns[0], "DataTypeVersion"), UA_NODEID_NUMERIC(ns[0], 68LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL); retVal |= UA_Server_addReference(server, UA_NODEID_NUMERIC(ns[0], 104LU), UA_NODEID_NUMERIC(ns[0], 37LU), UA_EXPANDEDNODEID_NUMERIC(ns[0], 80LU), true); return retVal; } static UA_StatusCode function_namespace0_generated_192_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 104LU) ); } /* Default XML - ns=0;i=3063 */ static UA_StatusCode function_namespace0_generated_193_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], 3063LU), UA_NODEID_NUMERIC(ns[0], 0LU), UA_NODEID_NUMERIC(ns[0], 0LU), UA_QUALIFIEDNAME(ns[0], "Default XML"), UA_NODEID_NUMERIC(ns[0], 58LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_193_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 3063LU) ); } /* Default Binary - ns=0;i=3062 */ static UA_StatusCode function_namespace0_generated_194_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], 3062LU), UA_NODEID_NUMERIC(ns[0], 0LU), UA_NODEID_NUMERIC(ns[0], 0LU), UA_QUALIFIEDNAME(ns[0], "Default Binary"), UA_NODEID_NUMERIC(ns[0], 58LU), (const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],NULL, NULL); return retVal; } static UA_StatusCode function_namespace0_generated_194_finish(UA_Server *server, UA_UInt16* ns) { return UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(ns[0], 3062LU) ); } 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/"); /* Load custom datatype definitions into the server */ if((retVal = function_namespace0_generated_0_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_0_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_1_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_1_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_2_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_2_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_3_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_3_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_4_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_4_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_5_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_5_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_6_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_7_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_8_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_9_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_10_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_11_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_12_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_13_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_14_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_15_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_16_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_17_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_18_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_19_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_20_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_21_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_22_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_23_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_24_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_25_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_26_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_27_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_28_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_29_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_30_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_31_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_32_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_33_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_34_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_35_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_36_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_37_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_38_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_39_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_40_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_41_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_42_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_43_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_44_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_45_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_46_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_47_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_48_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_49_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_50_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_51_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_52_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_53_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_54_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_55_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_56_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_57_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_58_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_59_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_60_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_61_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_62_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_63_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_64_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_65_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_66_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_67_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_68_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_69_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_70_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_71_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_72_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_73_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_74_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_75_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_76_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_77_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_78_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_79_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_80_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_81_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_82_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_83_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_84_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_85_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_86_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_87_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_88_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_89_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_90_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_91_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_92_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_93_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_94_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_95_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_96_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_97_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_98_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_99_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_100_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_101_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_102_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_103_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_104_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_105_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_106_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_107_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_108_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_109_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_110_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_111_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_112_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_113_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_114_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_115_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_116_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_117_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_118_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_119_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_120_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_121_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_122_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_123_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_124_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_125_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_126_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_127_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_128_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_129_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_130_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_131_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_132_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_133_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_134_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_135_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_136_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_137_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_138_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_139_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_140_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_141_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_142_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_143_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_144_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_145_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_146_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_147_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_148_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_149_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_150_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_151_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_152_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_153_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_154_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_155_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_156_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_157_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_158_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_159_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_160_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_161_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_162_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_163_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_164_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_165_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_166_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_167_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_168_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_169_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_170_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_171_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_172_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_173_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_174_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_175_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_176_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_177_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_178_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_179_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_180_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_181_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_182_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_183_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_184_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_185_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_186_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_187_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_188_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_189_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_190_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_191_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_192_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_193_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_194_begin(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_194_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_193_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_192_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_191_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_190_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_189_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_188_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_187_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_186_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_185_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_184_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_183_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_182_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_181_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_180_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_179_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_178_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_177_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_176_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_175_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_174_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_173_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_172_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_171_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_170_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_169_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_168_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_167_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_166_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_165_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_164_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_163_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_162_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_161_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_160_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_159_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_158_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_157_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_156_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_155_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_154_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_153_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_152_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_151_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_150_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_149_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_148_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_147_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_146_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_145_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_144_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_143_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_142_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_141_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_140_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_139_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_138_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_137_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_136_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_135_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_134_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_133_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_132_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_131_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_130_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_129_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_128_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_127_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_126_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_125_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_124_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_123_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_122_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_121_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_120_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_119_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_118_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_117_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_116_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_115_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_114_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_113_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_112_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_111_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_110_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_109_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_108_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_107_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_106_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_105_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_104_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_103_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_102_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_101_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_100_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_99_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_98_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_97_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_96_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_95_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_94_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_93_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_92_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_91_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_90_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_89_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_88_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_87_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_86_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_85_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_84_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_83_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_82_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_81_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_80_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_79_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_78_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_77_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_76_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_75_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_74_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_73_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_72_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_71_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_70_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_69_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_68_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_67_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_66_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_65_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_64_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_63_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_62_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_61_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_60_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_59_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_58_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_57_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_56_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_55_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_54_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_53_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_52_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_51_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_50_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_49_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_48_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_47_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_46_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_45_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_44_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_43_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_42_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_41_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_40_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_39_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_38_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_37_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_36_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_35_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_34_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_33_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_32_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_31_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_30_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_29_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_28_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_27_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_26_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_25_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_24_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_23_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_22_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_21_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_20_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_19_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_18_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_17_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_16_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_15_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_14_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_13_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_12_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_11_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_10_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_9_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_8_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_7_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; if((retVal = function_namespace0_generated_6_finish(server, ns)) != UA_STATUSCODE_GOOD) return retVal; return retVal; } /**** amalgamated original file "/src/ua_types_lex.c" ****/ /* Generated by re2c 1.1.1 */ /* 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 2020 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * */ /* Lexing and parsing of builtin data types. These are helper functions that not * required by the SDK internally. But they are useful for users who want to use * standard-specified humand readable encodings for NodeIds, etc. * * This compilation unit uses the re2c lexer generator. The final C source is * generated with the following script: * * re2c -i --no-generation-date ua_types_lex.re > ua_types_lex.c * * In order that users of the SDK don't need to install re2c, always commit a * recent ua_types_lex.c if changes are made to the lexer. */ #define YYCURSOR pos #define YYMARKER context.marker #define YYPEEK() (YYCURSOR < end) ? *YYCURSOR : 0 /* The lexer sees a stream of * \0 when the input ends*/ #define YYSKIP() ++YYCURSOR; #define YYBACKUP() YYMARKER = YYCURSOR #define YYRESTORE() YYCURSOR = YYMARKER #define YYSTAGP(t) t = YYCURSOR #define YYSTAGN(t) t = NULL typedef struct { const char *marker; const char *yyt1;const char *yyt2;const char *yyt3;const char *yyt4;const char *yyt5; } LexContext; static UA_StatusCode parse_guid(UA_Guid *guid, const UA_Byte *s, const UA_Byte *e) { size_t len = (size_t)(e - s); if(len != 36 || s[8] != '-' || s[13] != '-' || s[23] != '-') return UA_STATUSCODE_BADINTERNALERROR; UA_UInt32 tmp; if(UA_readNumberWithBase(s, 8, &tmp, 16) != 8) return UA_STATUSCODE_BADINTERNALERROR; guid->data1 = tmp; if(UA_readNumberWithBase(&s[9], 4, &tmp, 16) != 4) return UA_STATUSCODE_BADINTERNALERROR; guid->data2 = (UA_UInt16)tmp; if(UA_readNumberWithBase(&s[14], 4, &tmp, 16) != 4) return UA_STATUSCODE_BADINTERNALERROR; guid->data3 = (UA_UInt16)tmp; if(UA_readNumberWithBase(&s[19], 2, &tmp, 16) != 2) return UA_STATUSCODE_BADINTERNALERROR; guid->data4[0] = (UA_Byte)tmp; if(UA_readNumberWithBase(&s[21], 2, &tmp, 16) != 2) return UA_STATUSCODE_BADINTERNALERROR; guid->data4[1] = (UA_Byte)tmp; for(size_t pos = 2, spos = 24; pos < 8; pos++, spos += 2) { if(UA_readNumberWithBase(&s[spos], 2, &tmp, 16) != 2) return UA_STATUSCODE_BADINTERNALERROR; guid->data4[pos] = (UA_Byte)tmp; } return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Guid_parse(UA_Guid *guid, const UA_String str) { UA_StatusCode res = parse_guid(guid, str.data, str.data + str.length); if(res != UA_STATUSCODE_GOOD) *guid = UA_GUID_NULL; return res; } static UA_StatusCode parse_nodeid_body(UA_NodeId *id, const char *body, const char *end) { size_t len = (size_t)(end - (body+2)); UA_StatusCode res = UA_STATUSCODE_GOOD; switch(*body) { case 'i': { if(UA_readNumber((const UA_Byte*)body+2, len, &id->identifier.numeric) != len) return UA_STATUSCODE_BADINTERNALERROR; id->identifierType = UA_NODEIDTYPE_NUMERIC; break; } case 's': { UA_String tmpstr; tmpstr.data = (UA_Byte*)(uintptr_t)body+2; tmpstr.length = len; res = UA_String_copy(&tmpstr, &id->identifier.string); if(res != UA_STATUSCODE_GOOD) break; id->identifierType = UA_NODEIDTYPE_STRING; break; } case 'g': res = parse_guid(&id->identifier.guid, (const UA_Byte*)body+2, (const UA_Byte*)end); if(res == UA_STATUSCODE_GOOD) id->identifierType = UA_NODEIDTYPE_GUID; break; case 'b': id->identifier.byteString.data = UA_unbase64((const unsigned char*)body+2, len, &id->identifier.byteString.length); if(!id->identifier.byteString.data && len > 0) return UA_STATUSCODE_BADINTERNALERROR; id->identifierType = UA_NODEIDTYPE_BYTESTRING; break; default: return UA_STATUSCODE_BADINTERNALERROR; } return res; } static UA_StatusCode parse_nodeid(UA_NodeId *id, const char *pos, const char *end) { *id = UA_NODEID_NULL; /* Reset the NodeId */ LexContext context; memset(&context, 0, sizeof(LexContext)); const char *ns = NULL, *nse= NULL; { char yych; yych = YYPEEK (); switch (yych) { case 'b': case 'g': case 'i': case 's': YYSTAGN (context.yyt1); YYSTAGN (context.yyt2); goto yy4; case 'n': goto yy5; default: goto yy2; } yy2: YYSKIP (); yy3: { (void)pos; return UA_STATUSCODE_BADINTERNALERROR; } yy4: YYSKIP (); yych = YYPEEK (); switch (yych) { case '=': goto yy6; default: goto yy3; } yy5: YYSKIP (); YYBACKUP (); yych = YYPEEK (); switch (yych) { case 's': goto yy8; default: goto yy3; } yy6: YYSKIP (); ns = context.yyt1; nse = context.yyt2; { (void)pos; // Get rid of a dead store clang-analyzer warning if(ns) { UA_UInt32 tmp; size_t len = (size_t)(nse - ns); if(UA_readNumber((const UA_Byte*)ns, len, &tmp) != len) return UA_STATUSCODE_BADINTERNALERROR; id->namespaceIndex = (UA_UInt16)tmp; } // From the current position until the end return parse_nodeid_body(id, &pos[-2], end); } yy8: YYSKIP (); yych = YYPEEK (); switch (yych) { case '=': goto yy10; default: goto yy9; } yy9: YYRESTORE (); goto yy3; yy10: YYSKIP (); yych = YYPEEK (); switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': YYSTAGP (context.yyt1); goto yy11; default: goto yy9; } yy11: YYSKIP (); yych = YYPEEK (); switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy11; case ';': YYSTAGP (context.yyt2); goto yy13; default: goto yy9; } yy13: YYSKIP (); yych = YYPEEK (); switch (yych) { case 'b': case 'g': case 'i': case 's': goto yy14; default: goto yy9; } yy14: YYSKIP (); yych = YYPEEK (); switch (yych) { case '=': goto yy6; default: goto yy9; } } } UA_StatusCode UA_NodeId_parse(UA_NodeId *id, const UA_String str) { UA_StatusCode res = parse_nodeid(id, (const char*)str.data, (const char*)str.data+str.length); if(res != UA_STATUSCODE_GOOD) UA_NodeId_clear(id); return res; } static UA_StatusCode parse_expandednodeid(UA_ExpandedNodeId *id, const char *pos, const char *end) { *id = UA_EXPANDEDNODEID_NULL; /* Reset the NodeId */ LexContext context; memset(&context, 0, sizeof(LexContext)); const char *svr = NULL, *svre = NULL, *nsu = NULL, *ns = NULL, *body = NULL; { char yych; yych = YYPEEK (); switch (yych) { case 'b': case 'g': case 'i': YYSTAGN (context.yyt1); YYSTAGN (context.yyt2); YYSTAGN (context.yyt3); YYSTAGP (context.yyt4); YYSTAGN (context.yyt5); goto yy19; case 'n': YYSTAGN (context.yyt1); YYSTAGN (context.yyt2); goto yy20; case 's': YYSTAGN (context.yyt1); YYSTAGN (context.yyt2); YYSTAGN (context.yyt3); YYSTAGP (context.yyt4); YYSTAGN (context.yyt5); goto yy21; default: goto yy17; } yy17: YYSKIP (); yy18: { (void)pos; return UA_STATUSCODE_BADINTERNALERROR; } yy19: YYSKIP (); yych = YYPEEK (); switch (yych) { case '=': goto yy22; default: goto yy18; } yy20: YYSKIP (); YYBACKUP (); yych = YYPEEK (); switch (yych) { case 's': goto yy24; default: goto yy18; } yy21: YYSKIP (); YYBACKUP (); yych = YYPEEK (); switch (yych) { case '=': goto yy22; case 'v': goto yy26; default: goto yy18; } yy22: YYSKIP (); svr = context.yyt1; svre = context.yyt2; ns = context.yyt3; nsu = context.yyt5; body = context.yyt4; { (void)pos; // Get rid of a dead store clang-analyzer warning if(svr) { size_t len = (size_t)((svre) - svr); if(UA_readNumber((const UA_Byte*)svr, len, &id->serverIndex) != len) return UA_STATUSCODE_BADINTERNALERROR; } if(nsu) { size_t len = (size_t)((body-1) - nsu); UA_String nsuri; nsuri.data = (UA_Byte*)(uintptr_t)nsu; nsuri.length = len; UA_StatusCode res = UA_String_copy(&nsuri, &id->namespaceUri); if(res != UA_STATUSCODE_GOOD) return res; } else if(ns) { UA_UInt32 tmp; size_t len = (size_t)((body-1) - ns); if(UA_readNumber((const UA_Byte*)ns, len, &tmp) != len) return UA_STATUSCODE_BADINTERNALERROR; id->nodeId.namespaceIndex = (UA_UInt16)tmp; } // From the current position until the end return parse_nodeid_body(&id->nodeId, &pos[-2], end); } yy24: YYSKIP (); yych = YYPEEK (); switch (yych) { case '=': goto yy27; case 'u': goto yy28; default: goto yy25; } yy25: YYRESTORE (); goto yy18; yy26: YYSKIP (); yych = YYPEEK (); switch (yych) { case 'r': goto yy29; default: goto yy25; } yy27: YYSKIP (); yych = YYPEEK (); switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': YYSTAGP (context.yyt3); goto yy30; default: goto yy25; } yy28: YYSKIP (); yych = YYPEEK (); switch (yych) { case '=': goto yy32; default: goto yy25; } yy29: YYSKIP (); yych = YYPEEK (); switch (yych) { case '=': goto yy33; default: goto yy25; } yy30: YYSKIP (); yych = YYPEEK (); switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy30; case ';': goto yy34; default: goto yy25; } yy32: YYSKIP (); yych = YYPEEK (); switch (yych) { case '\n': goto yy25; case ';': YYSTAGP (context.yyt5); goto yy37; default: YYSTAGP (context.yyt5); goto yy35; } yy33: YYSKIP (); yych = YYPEEK (); switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': YYSTAGP (context.yyt1); goto yy38; default: goto yy25; } yy34: YYSKIP (); yych = YYPEEK (); switch (yych) { case 'b': case 'g': case 'i': case 's': YYSTAGP (context.yyt4); YYSTAGN (context.yyt5); goto yy40; default: goto yy25; } yy35: YYSKIP (); yych = YYPEEK (); switch (yych) { case '\n': goto yy25; case ';': goto yy37; default: goto yy35; } yy37: YYSKIP (); yych = YYPEEK (); switch (yych) { case 'b': case 'g': case 'i': case 's': YYSTAGN (context.yyt3); YYSTAGP (context.yyt4); goto yy40; default: goto yy25; } yy38: YYSKIP (); yych = YYPEEK (); switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy38; case ';': YYSTAGP (context.yyt2); goto yy41; default: goto yy25; } yy40: YYSKIP (); yych = YYPEEK (); switch (yych) { case '=': goto yy22; default: goto yy25; } yy41: YYSKIP (); yych = YYPEEK (); switch (yych) { case 'b': case 'g': case 'i': case 's': YYSTAGN (context.yyt3); YYSTAGP (context.yyt4); YYSTAGN (context.yyt5); goto yy40; case 'n': goto yy42; default: goto yy25; } yy42: YYSKIP (); yych = YYPEEK (); switch (yych) { case 's': goto yy24; default: goto yy25; } } } UA_StatusCode UA_ExpandedNodeId_parse(UA_ExpandedNodeId *id, const UA_String str) { UA_StatusCode res = parse_expandednodeid(id, (const char*)str.data, (const char*)str.data+str.length); if(res != UA_STATUSCODE_GOOD) UA_ExpandedNodeId_clear(id); return res; } static UA_StatusCode relativepath_addelem(UA_RelativePath *rp, UA_RelativePathElement *el) { /* Allocate memory */ UA_RelativePathElement *newArray = (UA_RelativePathElement*) UA_realloc(rp->elements, sizeof(UA_RelativePathElement) * (rp->elementsSize + 1)); if(!newArray) return UA_STATUSCODE_BADOUTOFMEMORY; rp->elements = newArray; /* Move to the target */ rp->elements[rp->elementsSize] = *el; rp->elementsSize++; return UA_STATUSCODE_GOOD; } /* Parse name string with '&' as the escape character */ static UA_StatusCode parse_refpath_qn_name(UA_QualifiedName *qn, const char **pos, const char *end) { /* Allocate the max length the name can have */ size_t maxlen = (size_t)(end - *pos); if(maxlen == 0) { qn->name.data = (UA_Byte*)UA_EMPTY_ARRAY_SENTINEL; return UA_STATUSCODE_GOOD; } char *name = (char*)UA_malloc(maxlen); if(!name) return UA_STATUSCODE_BADOUTOFMEMORY; size_t index = 0; for(; *pos < end; (*pos)++) { char c = **pos; /* Unescaped special characer: The end of the QualifiedName */ if(c == '/' || c == '.' || c == '<' || c == '>' || c == ':' || c == '#' || c == '!') break; /* Escaped character */ if(c == '&') { (*pos)++; if(*pos >= end || (**pos != '/' && **pos != '.' && **pos != '<' && **pos != '>' && **pos != ':' && **pos != '#' && **pos != '!' && **pos != '&')) { UA_free(name); return UA_STATUSCODE_BADINTERNALERROR; } c = **pos; } /* Unescaped normal character */ name[index] = c; index++; } if(index > 0) { qn->name.data = (UA_Byte*)name; qn->name.length = index; } else { qn->name.data = (UA_Byte*)UA_EMPTY_ARRAY_SENTINEL; UA_free(name); } return UA_STATUSCODE_GOOD; } static UA_StatusCode parse_refpath_qn(UA_QualifiedName *qn, const char *pos, const char *end) { LexContext context; memset(&context, 0, sizeof(LexContext)); const char *ns = NULL, *nse = NULL; UA_QualifiedName_init(qn); { char yych; yych = YYPEEK (); switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': YYSTAGP (context.yyt1); goto yy47; default: goto yy45; } yy45: YYSKIP (); yy46: { pos--; goto parse_qn_name; } yy47: YYSKIP (); YYBACKUP (); yych = YYPEEK (); switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case ':': goto yy49; default: goto yy46; } yy48: YYSKIP (); yych = YYPEEK (); yy49: switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy48; case ':': YYSTAGP (context.yyt2); goto yy51; default: goto yy50; } yy50: YYRESTORE (); goto yy46; yy51: YYSKIP (); ns = context.yyt1; nse = context.yyt2; { UA_UInt32 tmp; size_t len = (size_t)(nse - ns); if(UA_readNumber((const UA_Byte*)ns, len, &tmp) != len) return UA_STATUSCODE_BADINTERNALERROR; qn->namespaceIndex = (UA_UInt16)tmp; goto parse_qn_name; } } parse_qn_name: return parse_refpath_qn_name(qn, &pos, end); } /* List of well-known ReferenceTypes that don't require lookup in the server */ typedef struct { char *browseName; UA_UInt32 identifier; } RefTypeNames; #define KNOWNREFTYPES 17 static const RefTypeNames knownRefTypes[KNOWNREFTYPES] = { {"References", UA_NS0ID_REFERENCES}, {"HierachicalReferences", UA_NS0ID_HIERARCHICALREFERENCES}, {"NonHierachicalReferences", UA_NS0ID_NONHIERARCHICALREFERENCES}, {"HasChild", UA_NS0ID_HASCHILD}, {"Aggregates", UA_NS0ID_AGGREGATES}, {"HasComponent", UA_NS0ID_HASCOMPONENT}, {"HasProperty", UA_NS0ID_HASPROPERTY}, {"HasOrderedComponent", UA_NS0ID_HASORDEREDCOMPONENT}, {"HasSubtype", UA_NS0ID_HASSUBTYPE}, {"Organizes", UA_NS0ID_ORGANIZES}, {"HasModellingRule", UA_NS0ID_HASMODELLINGRULE}, {"HasTypeDefinition", UA_NS0ID_HASTYPEDEFINITION}, {"HasEncoding", UA_NS0ID_HASENCODING}, {"GeneratesEvent", UA_NS0ID_GENERATESEVENT}, {"AlwaysGeneratesEvent", UA_NS0ID_ALWAYSGENERATESEVENT}, {"HasEventSource", UA_NS0ID_HASEVENTSOURCE}, {"HasNotifier", UA_NS0ID_HASNOTIFIER} }; static UA_StatusCode lookup_reftype(UA_NodeId *refTypeId, UA_QualifiedName *qn) { if(qn->namespaceIndex != 0) return UA_STATUSCODE_BADNOTFOUND; for(size_t i = 0; i < KNOWNREFTYPES; i++) { UA_String tmp = UA_STRING(knownRefTypes[i].browseName); if(UA_String_equal(&qn->name, &tmp)) { *refTypeId = UA_NODEID_NUMERIC(0, knownRefTypes[i].identifier); return UA_STATUSCODE_GOOD; } } return UA_STATUSCODE_BADNOTFOUND; } static UA_StatusCode parse_relativepath(UA_RelativePath *rp, const char *pos, const char *end) { LexContext context; memset(&context, 0, sizeof(LexContext)); const char *begin = NULL, *finish = NULL; UA_StatusCode res = UA_STATUSCODE_GOOD; UA_RelativePath_init(rp); /* Reset the BrowsePath */ /* Add one element to the path in every iteration */ UA_RelativePathElement current; loop: UA_RelativePathElement_init(¤t); current.includeSubtypes = true; /* Follow subtypes by default */ /* Get the ReferenceType and its modifiers */ { char yych; unsigned int yyaccept = 0; yych = YYPEEK (); switch (yych) { case 0x00: goto yy55; case '.': goto yy59; case '/': goto yy61; case '<': goto yy63; default: goto yy57; } yy55: YYSKIP (); { (void)pos; return UA_STATUSCODE_GOOD; } yy57: YYSKIP (); yy58: { (void)pos; return UA_STATUSCODE_BADINTERNALERROR; } yy59: YYSKIP (); { current.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES); goto reftype_target; } yy61: YYSKIP (); { current.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES); goto reftype_target; } yy63: yyaccept = 0; YYSKIP (); YYBACKUP (); yych = YYPEEK (); switch (yych) { case 0x00: case '>': goto yy58; case '&': YYSTAGP (context.yyt1); goto yy67; default: YYSTAGP (context.yyt1); goto yy64; } yy64: YYSKIP (); yych = YYPEEK (); switch (yych) { case 0x00: goto yy66; case '&': goto yy67; case '>': YYSTAGP (context.yyt2); goto yy69; default: goto yy64; } yy66: YYRESTORE (); if (yyaccept == 0) { goto yy58; } else { goto yy70; } yy67: YYSKIP (); yych = YYPEEK (); switch (yych) { case 0x00: goto yy66; case '&': goto yy67; case '>': YYSTAGP (context.yyt2); goto yy71; default: goto yy64; } yy69: YYSKIP (); yy70: begin = context.yyt1; finish = context.yyt2; { for(; begin < finish; begin++) { if(*begin== '#') current.includeSubtypes = false; else if(*begin == '!') current.isInverse = true; else break; } UA_QualifiedName refqn; res |= parse_refpath_qn(&refqn, begin, finish); res |= lookup_reftype(¤t.referenceTypeId, &refqn); UA_QualifiedName_clear(&refqn); goto reftype_target; } yy71: yyaccept = 1; YYSKIP (); YYBACKUP (); yych = YYPEEK (); switch (yych) { case 0x00: goto yy70; case '&': goto yy67; case '>': YYSTAGP (context.yyt2); goto yy69; default: goto yy64; } } /* Get the TargetName component */ reftype_target: if(res != UA_STATUSCODE_GOOD) return res; { char yych; yych = YYPEEK (); switch (yych) { case 0x00: case '.': case '/': case '<': goto yy74; case '&': YYSTAGP (context.yyt1); goto yy79; default: YYSTAGP (context.yyt1); goto yy76; } yy74: YYSKIP (); { pos--; goto add_element; } yy76: YYSKIP (); yych = YYPEEK (); switch (yych) { case 0x00: case '.': case '/': case '<': goto yy78; case '&': goto yy79; default: goto yy76; } yy78: begin = context.yyt1; { res = parse_refpath_qn(¤t.targetName, begin, pos); goto add_element; } yy79: YYSKIP (); yych = YYPEEK (); switch (yych) { case 0x00: goto yy78; case '&': goto yy79; default: goto yy76; } } /* Add the current element to the path and continue to the next element */ add_element: res |= relativepath_addelem(rp, ¤t); if(res != UA_STATUSCODE_GOOD) { UA_RelativePathElement_clear(¤t); return res; } goto loop; } UA_StatusCode UA_RelativePath_parse(UA_RelativePath *rp, const UA_String str) { UA_StatusCode res = parse_relativepath(rp, (const char*)str.data, (const char*)str.data+str.length); if(res != UA_STATUSCODE_GOOD) UA_RelativePath_clear(rp); return res; } /**** amalgamated original file "/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) * Copyright 2019 (c) HMS Industrial Networks AB (Author: Jonas Green) */ #ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */ #define UA_MAX_RETRANSMISSIONQUEUESIZE 256 UA_Subscription * UA_Subscription_new(void) { /* Allocate the memory */ UA_Subscription *newSub = (UA_Subscription*)UA_calloc(1, sizeof(UA_Subscription)); if(!newSub) return NULL; /* The first publish response is sent immediately */ newSub->state = UA_SUBSCRIPTIONSTATE_NORMAL; /* 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_delete(UA_Server *server, UA_Subscription *sub) { UA_LOCK_ASSERT(&server->serviceMutex, 1); /* Unregister the publish callback */ Subscription_unregisterPublishCallback(server, sub); /* Remove the diagnostics object for the subscription */ #ifdef UA_ENABLE_DIAGNOSTICS if(sub->session) { /* Use a browse path to find the node */ char subIdStr[32]; snprintf(subIdStr, 32, "%u", sub->subscriptionId); UA_BrowsePath bp; UA_BrowsePath_init(&bp); bp.startingNode = sub->session->sessionId; UA_RelativePathElement rpe[2]; memset(rpe, 0, sizeof(UA_RelativePathElement) * 2); rpe[0].targetName = UA_QUALIFIEDNAME(0, "SubscriptionDiagnosticsArray"); rpe[1].targetName = UA_QUALIFIEDNAME(0, subIdStr); bp.relativePath.elements = rpe; bp.relativePath.elementsSize = 2; UA_BrowsePathResult bpr = translateBrowsePathToNodeIds(server, &bp); /* Delete all nodes matching the browse path */ for(size_t i = 0; i < bpr.targetsSize; i++) { if(bpr.targets[i].remainingPathIndex < UA_UINT32_MAX) continue; deleteNode(server, bpr.targets[i].targetId.nodeId, true); } UA_BrowsePathResult_clear(&bpr); } #endif UA_LOG_INFO_SUBSCRIPTION(&server->config.logger, sub, "Subscription deleted"); /* Detach from the session if necessary */ if(sub->session) UA_Session_detachSubscription(server, sub->session, sub, true); /* Remove from the server if not previously registered */ if(sub->serverListEntry.le_prev) { LIST_REMOVE(sub, serverListEntry); UA_assert(server->subscriptionsSize > 0); server->subscriptionsSize--; server->serverDiagnosticsSummary.currentSubscriptionCount--; } /* Delete monitored Items */ UA_assert(server->monitoredItemsSize >= sub->monitoredItemsSize); UA_MonitoredItem *mon, *tmp_mon; LIST_FOREACH_SAFE(mon, &sub->monitoredItems, listEntry, tmp_mon) { UA_MonitoredItem_delete(server, mon); } UA_assert(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_clear(&nme->message); UA_free(nme); if(sub->session) --sub->session->totalRetransmissionQueueSize; --sub->retransmissionQueueSize; } UA_assert(sub->retransmissionQueueSize == 0); /* Add a delayed callback to remove the Subscription when the current jobs * have completed. Pointers to the subscription may still exist upwards in * the call stack. */ sub->delayedFreePointers.callback = NULL; sub->delayedFreePointers.application = server; sub->delayedFreePointers.data = NULL; sub->delayedFreePointers.nextTime = UA_DateTime_nowMonotonic() + 1; sub->delayedFreePointers.interval = 0; /* Remove the structure */ UA_Timer_addTimerEntry(&server->timer, &sub->delayedFreePointers, NULL); } 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; } static void removeOldestRetransmissionMessageFromSub(UA_Subscription *sub) { UA_NotificationMessageEntry *oldestEntry = TAILQ_LAST(&sub->retransmissionQueue, NotificationMessageQueue); TAILQ_REMOVE(&sub->retransmissionQueue, oldestEntry, listEntry); UA_NotificationMessage_clear(&oldestEntry->message); UA_free(oldestEntry); --sub->retransmissionQueueSize; if(sub->session) --sub->session->totalRetransmissionQueueSize; #ifdef UA_ENABLE_DIAGNOSTICS sub->discardedMessageCount++; #endif } static void removeOldestRetransmissionMessageFromSession(UA_Session *session) { UA_NotificationMessageEntry *oldestEntry = NULL; UA_Subscription *oldestSub = NULL; UA_Subscription *sub; TAILQ_FOREACH(sub, &session->subscriptions, sessionListEntry) { UA_NotificationMessageEntry *first = TAILQ_LAST(&sub->retransmissionQueue, NotificationMessageQueue); if(!first) continue; if(!oldestEntry || oldestEntry->message.publishTime > first->message.publishTime) { oldestEntry = first; oldestSub = sub; } } UA_assert(oldestEntry); UA_assert(oldestSub); removeOldestRetransmissionMessageFromSub(oldestSub); } static void UA_Subscription_addRetransmissionMessage(UA_Server *server, UA_Subscription *sub, UA_NotificationMessageEntry *entry) { /* Release the oldest entry if there is not enough space */ UA_Session *session = sub->session; if(sub->retransmissionQueueSize >= UA_MAX_RETRANSMISSIONQUEUESIZE) { UA_LOG_WARNING_SUBSCRIPTION(&server->config.logger, sub, "Subscription retransmission queue overflow"); removeOldestRetransmissionMessageFromSub(sub); } else if(session && server->config.maxRetransmissionQueueSize > 0 && session->totalRetransmissionQueueSize >= server->config.maxRetransmissionQueueSize) { UA_LOG_WARNING_SUBSCRIPTION(&server->config.logger, sub, "Session-wide retransmission queue overflow"); removeOldestRetransmissionMessageFromSession(sub->session); } /* Add entry */ TAILQ_INSERT_TAIL(&sub->retransmissionQueue, entry, listEntry); ++sub->retransmissionQueueSize; if(session) ++session->totalRetransmissionQueueSize; } 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->retransmissionQueueSize; UA_NotificationMessage_clear(&entry->message); UA_free(entry); if(sub->session) --sub->session->totalRetransmissionQueueSize; return UA_STATUSCODE_GOOD; } /* The output counters are only set when the preparation is successful */ static UA_StatusCode prepareNotificationMessage(UA_Server *server, UA_Subscription *sub, UA_NotificationMessage *message, size_t maxNotifications) { UA_assert(maxNotifications > 0); /* Allocate an ExtensionObject for Event- and DataChange-Notifications. Also * there can be StatusChange-Notifications. The standard says in Part 4, * 7.2.1: * * If a Subscription contains MonitoredItems for events and data, this array * should have not more than 2 elements. */ 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; size_t dcnPos = 0; /* How many DataChangeNotifications? */ UA_DataChangeNotification *dcn = NULL; if(sub->dataChangeNotifications > 0) { dcn = UA_DataChangeNotification_new(); if(!dcn) { UA_NotificationMessage_clear(message); return UA_STATUSCODE_BADOUTOFMEMORY; } UA_ExtensionObject_setValue(message->notificationData, dcn, &UA_TYPES[UA_TYPES_DATACHANGENOTIFICATION]); size_t dcnSize = sub->dataChangeNotifications; if(dcnSize > maxNotifications) dcnSize = maxNotifications; dcn->monitoredItems = (UA_MonitoredItemNotification*) UA_Array_new(dcnSize, &UA_TYPES[UA_TYPES_MONITOREDITEMNOTIFICATION]); if(!dcn->monitoredItems) { UA_NotificationMessage_clear(message); return UA_STATUSCODE_BADOUTOFMEMORY; } dcn->monitoredItemsSize = dcnSize; notificationDataIdx++; } #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS size_t enlPos = 0; /* How many EventNotifications? */ UA_EventNotificationList *enl = NULL; if(sub->eventNotifications > 0) { enl = UA_EventNotificationList_new(); if(!enl) { UA_NotificationMessage_clear(message); return UA_STATUSCODE_BADOUTOFMEMORY; } UA_ExtensionObject_setValue(&message->notificationData[notificationDataIdx], enl, &UA_TYPES[UA_TYPES_EVENTNOTIFICATIONLIST]); size_t enlSize = sub->eventNotifications; if(enlSize > maxNotifications) enlSize = maxNotifications; enl->events = (UA_EventFieldList*) UA_Array_new(enlSize, &UA_TYPES[UA_TYPES_EVENTFIELDLIST]); if(!enl->events) { UA_NotificationMessage_clear(message); return UA_STATUSCODE_BADOUTOFMEMORY; } enl->eventsSize = enlSize; notificationDataIdx++; } #endif UA_assert(notificationDataIdx > 0); message->notificationDataSize = notificationDataIdx; /* <-- The point of no return --> */ /* How many notifications were moved to the response overall? */ size_t totalNotifications = 0; UA_Notification *notification, *notification_tmp; TAILQ_FOREACH_SAFE(notification, &sub->notificationQueue, globalEntry, notification_tmp) { if(totalNotifications >= maxNotifications) break; /* Move the content to the response */ switch(notification->mon->itemToMonitor.attributeId) { #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS case UA_ATTRIBUTEID_EVENTNOTIFIER: UA_assert(enl != NULL); /* Have at least one event notification */ enl->events[enlPos] = notification->data.event; UA_EventFieldList_init(¬ification->data.event); enlPos++; break; #endif default: UA_assert(dcn != NULL); /* Have at least one change notification */ dcn->monitoredItems[dcnPos] = notification->data.dataChange; UA_DataValue_init(¬ification->data.dataChange.value); dcnPos++; break; } /* If there are Notifications *before this one* in the MonitoredItem- * local queue, remove all of them. These are earlier Notifications that * are non-reporting. And we don't want them to show up after the * current Notification has been sent out. */ UA_Notification *prev; while((prev = TAILQ_PREV(notification, NotificationQueue, localEntry))) { UA_Notification_delete(prev); } /* Delete the notification, remove from the queues and decrease the counters */ 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) { UA_LOCK(&server->serviceMutex); UA_Subscription_sampleAndPublish(server, sub); UA_UNLOCK(&server->serviceMutex); } static void sendStatusChangeDelete(UA_Server *server, UA_Subscription *sub, UA_PublishResponseEntry *pre) { /* Cannot send out the StatusChange because no response is queued. * Delete the Subscription without sending the StatusChange. */ if(!pre) { UA_LOG_DEBUG_SUBSCRIPTION(&server->config.logger, sub, "Cannot send the StatusChange notification. " "Removing the subscription."); UA_Subscription_delete(server, sub); return; } UA_LOG_DEBUG_SUBSCRIPTION(&server->config.logger, sub, "Sending out a StatusChange " "notification and removing the subscription"); /* Populate the response */ UA_PublishResponse *response = &pre->response; UA_StatusChangeNotification scn; UA_StatusChangeNotification_init(&scn); scn.status = sub->statusChange; UA_ExtensionObject notificationData; UA_ExtensionObject_setValue(¬ificationData, &scn, &UA_TYPES[UA_TYPES_STATUSCHANGENOTIFICATION]); response->responseHeader.timestamp = UA_DateTime_now(); response->notificationMessage.notificationData = ¬ificationData; response->notificationMessage.notificationDataSize = 1; response->subscriptionId = sub->subscriptionId; response->notificationMessage.publishTime = response->responseHeader.timestamp; response->notificationMessage.sequenceNumber = sub->nextSequenceNumber; /* Send the response */ UA_assert(sub->session); /* Otherwise pre is NULL */ UA_LOG_DEBUG_SUBSCRIPTION(&server->config.logger, sub, "Sending out a publish response"); sendResponse(server, sub->session, sub->session->header.channel, pre->requestId, (UA_Response *)response, &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]); /* Clean up */ response->notificationMessage.notificationData = NULL; response->notificationMessage.notificationDataSize = 0; UA_PublishResponse_clear(&pre->response); UA_free(pre); /* Delete the subscription */ UA_Subscription_delete(server, sub); } /* Called every time we set the subscription late (or it is still late) */ static void UA_Subscription_isLate(UA_Subscription *sub) { sub->state = UA_SUBSCRIPTIONSTATE_LATE; #ifdef UA_ENABLE_DIAGNOSTICS sub->latePublishRequestCount++; #endif } /* Returns true if done */ UA_Boolean UA_Subscription_publishOnce(UA_Server *server, UA_Subscription *sub) { /* Dequeue a response */ UA_PublishResponseEntry *pre = NULL; if(sub->session) pre = UA_Session_dequeuePublishReq(sub->session); /* Update the LifetimeCounter */ if(pre) { sub->currentLifetimeCount = 0; } else { UA_LOG_DEBUG_SUBSCRIPTION(&server->config.logger, sub, "The publish queue is empty"); ++sub->currentLifetimeCount; if(sub->currentLifetimeCount > sub->lifeTimeCount) { UA_LOG_WARNING_SUBSCRIPTION(&server->config.logger, sub, "End of subscription lifetime"); /* Set the StatusChange to delete the subscription. */ sub->statusChange = UA_STATUSCODE_BADTIMEOUT; } } /* Send a StatusChange notification if possible and delete the * Subscription */ if(sub->statusChange != UA_STATUSCODE_GOOD) { sendStatusChangeDelete(server, sub, pre); return true; } /* Count the available notifications */ UA_UInt32 notifications = (sub->publishingEnabled) ? sub->notificationQueueSize : 0; if(notifications > sub->notificationsPerPublish) notifications = sub->notificationsPerPublish; /* 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 true; } UA_LOG_DEBUG_SUBSCRIPTION(&server->config.logger, sub, "Sending a KeepAlive"); } /* We want to send a response, but cannot. Either because there is no queued * response or because the Subscription is detached from a Session or because * the SecureChannel for the Session is closed. */ if(!pre || !sub->session || !sub->session->header.channel) { UA_LOG_DEBUG_SUBSCRIPTION(&server->config.logger, sub, "Want to send a publish response but cannot. " "The subscription is late."); UA_Subscription_isLate(sub); if(pre) UA_Session_queuePublishReq(sub->session, pre, true); /* Re-enqueue */ return true; } UA_assert(pre); UA_assert(sub->session); /* Otherwise pre is NULL */ /* Prepare the response */ UA_PublishResponse *response = &pre->response; UA_NotificationMessage *message = &response->notificationMessage; UA_NotificationMessageEntry *retransmission = NULL; #ifdef UA_ENABLE_DIAGNOSTICS size_t priorDataChangeNotifications = sub->dataChangeNotifications; size_t priorEventNotifications = sub->eventNotifications; #endif if(notifications > 0) { if(server->config.enableRetransmissionQueue) { /* Allocate the retransmission entry */ retransmission = (UA_NotificationMessageEntry*) UA_malloc(sizeof(UA_NotificationMessageEntry)); if(!retransmission) { UA_LOG_WARNING_SUBSCRIPTION(&server->config.logger, sub, "Could not allocate memory for retransmission. " "The subscription is late."); UA_Subscription_isLate(sub); UA_Session_queuePublishReq(sub->session, pre, true); /* Re-enqueue */ return true; } } /* Prepare the response */ UA_StatusCode retval = prepareNotificationMessage(server, sub, message, notifications); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING_SUBSCRIPTION(&server->config.logger, sub, "Could not prepare the notification message. " "The subscription is late."); /* If the retransmission queue is enabled a retransmission message is allocated */ if(retransmission) UA_free(retransmission); UA_Subscription_isLate(sub); UA_Session_queuePublishReq(sub->session, pre, true); /* Re-enqueue */ return true; } } /* <-- The point of no return --> */ /* Set up the response */ response->responseHeader.timestamp = UA_DateTime_now(); response->subscriptionId = sub->subscriptionId; response->moreNotifications = (sub->notificationQueueSize > 0); 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 */ UA_assert(sub->retransmissionQueueSize <= UA_MAX_RETRANSMISSIONQUEUESIZE); UA_UInt32 seqNumbers[UA_MAX_RETRANSMISSIONQUEUESIZE]; response->availableSequenceNumbers = seqNumbers; response->availableSequenceNumbersSize = sub->retransmissionQueueSize; size_t i = 0; UA_NotificationMessageEntry *nme; TAILQ_FOREACH(nme, &sub->retransmissionQueue, listEntry) { response->availableSequenceNumbers[i] = nme->message.sequenceNumber; ++i; } UA_assert(i == sub->retransmissionQueueSize); /* Send the response */ UA_LOG_DEBUG_SUBSCRIPTION(&server->config.logger, sub, "Sending out a publish response with %" PRIu32 " notifications", notifications); sendResponse(server, sub->session, sub->session->header.channel, pre->requestId, (UA_Response*)response, &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]); /* Reset the Subscription state to NORMAL. But only if all notifications * have been sent out. Otherwise keep the Subscription in the LATE state. So * we immediately answer incoming Publish requests. * * (We also check that session->responseQueueSize > 0 in Service_Publish. To * avoid answering Publish requests out of order. As we additionally may * schedule an immediate next publishOnce if UA_Subscription_publishOnce * returns "not done".) */ if(sub->notificationQueueSize == 0) sub->state = UA_SUBSCRIPTIONSTATE_NORMAL; /* Reset the KeepAlive after publishing */ sub->currentKeepAliveCount = 0; /* Free the response */ if(retransmission) /* NotificationMessage was moved into retransmission queue */ UA_NotificationMessage_init(&response->notificationMessage); response->availableSequenceNumbers = NULL; response->availableSequenceNumbersSize = 0; UA_PublishResponse_clear(&pre->response); UA_free(pre); /* Update the diagnostics statistics */ #ifdef UA_ENABLE_DIAGNOSTICS sub->publishRequestCount++; UA_UInt32 sentDCN = (UA_UInt32) (priorDataChangeNotifications - sub->dataChangeNotifications); UA_UInt32 sentEN = (UA_UInt32)(priorEventNotifications - sub->eventNotifications); sub->dataChangeNotificationsCount += sentDCN; sub->eventNotificationsCount += sentEN; sub->notificationsCount += (sentDCN + sentEN); #endif /* Re-run publishing if notifications are remaining */ return (sub->notificationQueueSize == 0); } /* Repeat the main publishing callback until all notifications are sent out or * we have to stop */ void UA_Subscription_publish(UA_Server *server, UA_Subscription *sub) { UA_Boolean done = false; do { done = UA_Subscription_publishOnce(server, sub); } while(!done); } void UA_Subscription_sampleAndPublish(UA_Server *server, UA_Subscription *sub) { UA_LOCK_ASSERT(&server->serviceMutex, 1); UA_LOG_DEBUG_SUBSCRIPTION(&server->config.logger, sub, "Sample and Publish Callback"); UA_assert(sub); /* Sample the MonitoredItems with sampling interval <0 (which implies * sampling in the same interval as the subscription) */ UA_MonitoredItem *mon; LIST_FOREACH(mon, &sub->samplingMonitoredItems, sampling.samplingListEntry) { monitoredItem_sampleCallback(server, mon); } /* Publish the queued notifications */ UA_Subscription_publish(server, sub); } UA_Boolean UA_Session_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"); sendResponse(server, session, session->header.channel, pre->requestId, (UA_Response*)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_clear */ return true; } UA_StatusCode Subscription_registerPublishCallback(UA_Server *server, UA_Subscription *sub) { UA_LOG_DEBUG_SUBSCRIPTION(&server->config.logger, sub, "Register subscription publishing callback"); UA_LOCK_ASSERT(&server->serviceMutex, 1); if(sub->publishCallbackId > 0) return UA_STATUSCODE_GOOD; UA_StatusCode retval = addRepeatedCallback(server, (UA_ServerCallback)publishCallback, sub, sub->publishingInterval, &sub->publishCallbackId); if(retval != UA_STATUSCODE_GOOD) return retval; UA_assert(sub->publishCallbackId > 0); return UA_STATUSCODE_GOOD; } void Subscription_unregisterPublishCallback(UA_Server *server, UA_Subscription *sub) { UA_LOG_DEBUG_SUBSCRIPTION(&server->config.logger, sub, "Unregister subscription publishing callback"); if(sub->publishCallbackId == 0) return; removeCallback(server, sub->publishCallbackId); sub->publishCallbackId = 0; } #endif /* UA_ENABLE_SUBSCRIPTIONS */ /**** amalgamated original file "/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 * Copyright 2020-2021 (c) Christian von Arnim, ISW University of Stuttgart (for VDW and umati) */ #ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */ /****************/ /* Notification */ /****************/ #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS static const UA_NodeId eventQueueOverflowEventType = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_EVENTQUEUEOVERFLOWEVENTTYPE}}; /* 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) { /* Avoid creating two adjacent overflow events */ UA_Notification *indicator = NULL; if(mon->parameters.discardOldest) { indicator = TAILQ_FIRST(&mon->queue); UA_assert(indicator); /* must exist */ if(indicator->isOverflowEvent) return UA_STATUSCODE_GOOD; } else { indicator = TAILQ_LAST(&mon->queue, NotificationQueue); UA_assert(indicator); /* must exist */ /* Skip the last element. It is the recently added notification that * shall be kept. We know it is not an OverflowEvent. */ UA_Notification *before = TAILQ_PREV(indicator, NotificationQueue, localEntry); if(before && before->isOverflowEvent) return UA_STATUSCODE_GOOD; } /* A Notification is inserted into the queue which includes only the * NodeId of the OverflowEventType. */ /* Allocate the notification */ UA_Notification *overflowNotification = UA_Notification_new(); if(!overflowNotification) return UA_STATUSCODE_BADOUTOFMEMORY; /* Set the notification fields */ overflowNotification->isOverflowEvent = true; overflowNotification->mon = mon; overflowNotification->data.event.clientHandle = mon->parameters.clientHandle; overflowNotification->data.event.eventFields = UA_Variant_new(); if(!overflowNotification->data.event.eventFields) { UA_free(overflowNotification); return UA_STATUSCODE_BADOUTOFMEMORY; } overflowNotification->data.event.eventFieldsSize = 1; UA_StatusCode retval = UA_Variant_setScalarCopy(overflowNotification->data.event.eventFields, &eventQueueOverflowEventType, &UA_TYPES[UA_TYPES_NODEID]); if(retval != UA_STATUSCODE_GOOD) { UA_Notification_delete(overflowNotification); return retval; } /* Insert before the removed 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. * * Ensure that the following is consistent with UA_Notification_enqueueMon * and UA_Notification_enqueueSub! */ TAILQ_INSERT_BEFORE(indicator, overflowNotification, localEntry); ++mon->eventOverflows; ++mon->queueSize; /* Test for consistency */ UA_assert(mon->queueSize >= mon->eventOverflows); UA_assert(mon->eventOverflows <= mon->queueSize - mon->eventOverflows + 1); if(TAILQ_NEXT(indicator, globalEntry) != UA_SUBSCRIPTION_QUEUE_SENTINEL) { /* Insert just before the indicator */ TAILQ_INSERT_BEFORE(indicator, overflowNotification, globalEntry); } else { /* The indicator was not reporting or not added yet. */ if(!mon->parameters.discardOldest) { /* Add last to the per-Subscription queue */ TAILQ_INSERT_TAIL(&mon->subscription->notificationQueue, overflowNotification, globalEntry); } else { /* Find the oldest reported element. Add before that. */ while(indicator) { indicator = TAILQ_PREV(indicator, NotificationQueue, localEntry); if(!indicator) { TAILQ_INSERT_TAIL(&mon->subscription->notificationQueue, overflowNotification, globalEntry); break; } if(TAILQ_NEXT(indicator, globalEntry) != UA_SUBSCRIPTION_QUEUE_SENTINEL) { TAILQ_INSERT_BEFORE(indicator, overflowNotification, globalEntry); break; } } } } ++sub->notificationQueueSize; ++sub->eventNotifications; /* Update the diagnostics statistics */ #ifdef UA_ENABLE_DIAGNOSTICS sub->eventQueueOverFlowCount++; #endif return UA_STATUSCODE_GOOD; } #endif /* Set the InfoBits that a datachange notification was removed */ static void setOverflowInfoBits(UA_MonitoredItem *mon) { /* Only for queues with more than one element */ if(mon->parameters.queueSize == 1) return; UA_Notification *indicator = NULL; if(mon->parameters.discardOldest) { indicator = TAILQ_FIRST(&mon->queue); } else { indicator = TAILQ_LAST(&mon->queue, NotificationQueue); } UA_assert(indicator); /* must exist */ indicator->data.dataChange.value.hasStatus = true; indicator->data.dataChange.value.status |= (UA_STATUSCODE_INFOTYPE_DATAVALUE | UA_STATUSCODE_INFOBITS_OVERFLOW); } /* Remove the InfoBits when the queueSize was reduced to 1 */ void UA_MonitoredItem_removeOverflowInfoBits(UA_MonitoredItem *mon) { /* Don't consider queue size > 1 and Event MonitoredItems */ if(mon->parameters.queueSize > 1 || mon->itemToMonitor.attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) return; /* Get the first notification */ UA_Notification *n = TAILQ_FIRST(&mon->queue); if(!n) return; /* Assertion that at most one notification is in the queue */ UA_assert(n == TAILQ_LAST(&mon->queue, NotificationQueue)); /* Remve the Infobits */ n->data.dataChange.value.status &= ~(UA_StatusCode) (UA_STATUSCODE_INFOTYPE_DATAVALUE | UA_STATUSCODE_INFOBITS_OVERFLOW); } UA_Notification * UA_Notification_new(void) { UA_Notification *n = (UA_Notification*)UA_calloc(1, sizeof(UA_Notification)); if(n) { /* Set the sentinel for a notification that is not enqueued */ TAILQ_NEXT(n, globalEntry) = UA_SUBSCRIPTION_QUEUE_SENTINEL; TAILQ_NEXT(n, localEntry) = UA_SUBSCRIPTION_QUEUE_SENTINEL; } return n; } static void UA_Notification_dequeueMon(UA_Notification *n); static void UA_Notification_enqueueSub(UA_Notification *n); static void UA_Notification_dequeueSub(UA_Notification *n); void UA_Notification_delete(UA_Notification *n) { UA_assert(n != UA_SUBSCRIPTION_QUEUE_SENTINEL); if(n->mon) { UA_Notification_dequeueMon(n); UA_Notification_dequeueSub(n); switch(n->mon->itemToMonitor.attributeId) { #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS case UA_ATTRIBUTEID_EVENTNOTIFIER: UA_EventFieldList_clear(&n->data.event); UA_EventFilterResult_clear(&n->result); break; #endif default: UA_MonitoredItemNotification_clear(&n->data.dataChange); break; } } UA_free(n); } /* Add to the MonitoredItem queue, update all counters and then handle overflow */ static void UA_Notification_enqueueMon(UA_Server *server, UA_Notification *n) { UA_MonitoredItem *mon = n->mon; UA_assert(mon); UA_assert(TAILQ_NEXT(n, localEntry) == UA_SUBSCRIPTION_QUEUE_SENTINEL); /* Add to the MonitoredItem */ TAILQ_INSERT_TAIL(&mon->queue, n, localEntry); ++mon->queueSize; #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS if(n->isOverflowEvent) ++mon->eventOverflows; #endif /* Test for consistency */ UA_assert(mon->queueSize >= mon->eventOverflows); UA_assert(mon->eventOverflows <= mon->queueSize - mon->eventOverflows + 1); /* Ensure enough space is available in the MonitoredItem. Do this only after * adding the new Notification. */ UA_MonitoredItem_ensureQueueSpace(server, mon); UA_LOG_DEBUG_SUBSCRIPTION(&server->config.logger, mon->subscription, "MonitoredItem %" PRIi32 " | " "Notification enqueued (Queue size %lu / %lu)", mon->monitoredItemId, (long unsigned)mon->queueSize, (long unsigned)mon->parameters.queueSize); } void UA_Notification_enqueueSub(UA_Notification *n) { UA_MonitoredItem *mon = n->mon; UA_assert(mon); UA_Subscription *sub = mon->subscription; UA_assert(sub); if(TAILQ_NEXT(n, globalEntry) != UA_SUBSCRIPTION_QUEUE_SENTINEL) return; /* Add to the subscription if reporting is enabled */ TAILQ_INSERT_TAIL(&sub->notificationQueue, n, globalEntry); ++sub->notificationQueueSize; switch(mon->itemToMonitor.attributeId) { #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS case UA_ATTRIBUTEID_EVENTNOTIFIER: ++sub->eventNotifications; break; #endif default: ++sub->dataChangeNotifications; break; } } void UA_Notification_enqueueAndTrigger(UA_Server *server, UA_Notification *n) { UA_MonitoredItem *mon = n->mon; UA_Subscription *sub = mon->subscription; UA_assert(sub); /* This function is never called for local MonitoredItems */ /* If reporting or (sampled+triggered), enqueue into the Subscription first * and then into the MonitoredItem. UA_MonitoredItem_ensureQueueSpace * (called within UA_Notification_enqueueMon) assumes the notification is * already in the Subscription's publishing queue. */ if(mon->monitoringMode == UA_MONITORINGMODE_REPORTING || (mon->monitoringMode == UA_MONITORINGMODE_SAMPLING && mon->triggeredUntil > UA_DateTime_nowMonotonic())) { UA_Notification_enqueueSub(n); mon->triggeredUntil = UA_INT64_MIN; UA_LOG_DEBUG_SUBSCRIPTION(&server->config.logger, mon->subscription, "Notification enqueued (Queue size %lu)", (long unsigned)mon->subscription->notificationQueueSize); } /* Insert into the MonitoredItem. This checks the queue size and * handles overflow. */ UA_Notification_enqueueMon(server, n); for(size_t i = mon->triggeringLinksSize - 1; i < mon->triggeringLinksSize; i--) { /* Get the triggered MonitoredItem. Remove the link if the MI doesn't exist. */ UA_MonitoredItem *triggeredMon = UA_Subscription_getMonitoredItem(sub, mon->triggeringLinks[i]); if(!triggeredMon) { UA_MonitoredItem_removeLink(sub, mon, mon->triggeringLinks[i]); continue; } /* Only sampling MonitoredItems receive a trigger. Reporting * MonitoredItems send out Notifications anyway and disabled * MonitoredItems don't create samples to send. */ if(triggeredMon->monitoringMode != UA_MONITORINGMODE_SAMPLING) continue; /* Get the latest sampled Notification from the triggered MonitoredItem. * Enqueue for publication. */ UA_Notification *n2 = TAILQ_LAST(&triggeredMon->queue, NotificationQueue); if(n2) UA_Notification_enqueueSub(n2); /* The next Notification within the publishing interval is going to be * published as well. (Falsely) assume that the publishing cycle has * started right now, so that we don't have to loop over MonitoredItems * to deactivate the triggering after the publishing cycle. */ triggeredMon->triggeredUntil = UA_DateTime_nowMonotonic() + (UA_DateTime)(sub->publishingInterval * (UA_Double)UA_DATETIME_MSEC); UA_LOG_DEBUG_SUBSCRIPTION(&server->config.logger, sub, "MonitoredItem %u triggers MonitoredItem %u", mon->monitoredItemId, triggeredMon->monitoredItemId); } } /* Remove from the MonitoredItem queue and adjust all counters */ static void UA_Notification_dequeueMon(UA_Notification *n) { UA_MonitoredItem *mon = n->mon; UA_assert(mon); if(TAILQ_NEXT(n, localEntry) == UA_SUBSCRIPTION_QUEUE_SENTINEL) return; /* Remove from the MonitoredItem queue */ #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS if(n->isOverflowEvent) --mon->eventOverflows; #endif TAILQ_REMOVE(&mon->queue, n, localEntry); --mon->queueSize; /* Test for consistency */ UA_assert(mon->queueSize >= mon->eventOverflows); UA_assert(mon->eventOverflows <= mon->queueSize - mon->eventOverflows + 1); /* Reset the sentintel */ TAILQ_NEXT(n, localEntry) = UA_SUBSCRIPTION_QUEUE_SENTINEL; } void UA_Notification_dequeueSub(UA_Notification *n) { if(TAILQ_NEXT(n, globalEntry) == UA_SUBSCRIPTION_QUEUE_SENTINEL) return; UA_MonitoredItem *mon = n->mon; UA_assert(mon); UA_Subscription *sub = mon->subscription; UA_assert(sub); switch(mon->itemToMonitor.attributeId) { #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS case UA_ATTRIBUTEID_EVENTNOTIFIER: --sub->eventNotifications; break; #endif default: --sub->dataChangeNotifications; break; } TAILQ_REMOVE(&sub->notificationQueue, n, globalEntry); --sub->notificationQueueSize; /* Reset the sentinel */ TAILQ_NEXT(n, globalEntry) = UA_SUBSCRIPTION_QUEUE_SENTINEL; } /*****************/ /* MonitoredItem */ /*****************/ void UA_MonitoredItem_init(UA_MonitoredItem *mon) { memset(mon, 0, sizeof(UA_MonitoredItem)); TAILQ_INIT(&mon->queue); mon->triggeredUntil = UA_INT64_MIN; } static UA_StatusCode addMonitoredItemBackpointer(UA_Server *server, UA_Session *session, UA_Node *node, void *data) { UA_MonitoredItem *mon = (UA_MonitoredItem*)data; UA_assert(mon != (UA_MonitoredItem*)~0); mon->sampling.nodeListNext = node->head.monitoredItems; node->head.monitoredItems = mon; return UA_STATUSCODE_GOOD; } static UA_StatusCode removeMonitoredItemBackPointer(UA_Server *server, UA_Session *session, UA_Node *node, void *data) { if(!node->head.monitoredItems) return UA_STATUSCODE_GOOD; /* Edge case that it's the first element */ UA_MonitoredItem *remove = (UA_MonitoredItem*)data; if(node->head.monitoredItems == remove) { node->head.monitoredItems = remove->sampling.nodeListNext; return UA_STATUSCODE_GOOD; } UA_MonitoredItem *prev = node->head.monitoredItems; UA_MonitoredItem *entry = prev->sampling.nodeListNext; for(; entry != NULL; prev = entry, entry = entry->sampling.nodeListNext) { if(entry == remove) { prev->sampling.nodeListNext = entry->sampling.nodeListNext; break; } } return UA_STATUSCODE_GOOD; } void UA_Server_registerMonitoredItem(UA_Server *server, UA_MonitoredItem *mon) { if(mon->registered) return; /* Register in Subscription and Server */ UA_Subscription *sub = mon->subscription; if(sub) { mon->monitoredItemId = ++sub->lastMonitoredItemId; mon->subscription = sub; sub->monitoredItemsSize++; LIST_INSERT_HEAD(&sub->monitoredItems, mon, listEntry); } else { mon->monitoredItemId = ++server->lastLocalMonitoredItemId; LIST_INSERT_HEAD(&server->localMonitoredItems, mon, listEntry); } server->monitoredItemsSize++; /* Register the MonitoredItem in userland */ if(server->config.monitoredItemRegisterCallback) { UA_Session *session = &server->adminSession; if(sub) session = sub->session; void *targetContext = NULL; getNodeContext(server, mon->itemToMonitor.nodeId, &targetContext); UA_UNLOCK(&server->serviceMutex); server->config.monitoredItemRegisterCallback(server, session ? &session->sessionId : NULL, session ? session->sessionHandle : NULL, &mon->itemToMonitor.nodeId, targetContext, mon->itemToMonitor.attributeId, false); UA_LOCK(&server->serviceMutex); } mon->registered = true; } static void UA_Server_unregisterMonitoredItem(UA_Server *server, UA_MonitoredItem *mon) { if(!mon->registered) return; UA_Subscription *sub = mon->subscription; UA_LOG_INFO_SUBSCRIPTION(&server->config.logger, sub, "MonitoredItem %" PRIi32 " | Deleting the MonitoredItem", mon->monitoredItemId); /* Deregister MonitoredItem in userland */ if(server->config.monitoredItemRegisterCallback) { UA_Session *session = &server->adminSession; if(sub) session = sub->session; void *targetContext = NULL; getNodeContext(server, mon->itemToMonitor.nodeId, &targetContext); UA_UNLOCK(&server->serviceMutex); server->config.monitoredItemRegisterCallback(server, session ? &session->sessionId : NULL, session ? session->sessionHandle : NULL, &mon->itemToMonitor.nodeId, targetContext, mon->itemToMonitor.attributeId, true); UA_LOCK(&server->serviceMutex); } /* Deregister in Subscription and server */ if(sub) sub->monitoredItemsSize--; LIST_REMOVE(mon, listEntry); /* Also for LocalMonitoredItems */ server->monitoredItemsSize--; mon->registered = false; } UA_StatusCode UA_MonitoredItem_setMonitoringMode(UA_Server *server, UA_MonitoredItem *mon, UA_MonitoringMode monitoringMode) { /* Check if the MonitoringMode is valid or not */ if(monitoringMode > UA_MONITORINGMODE_REPORTING) return UA_STATUSCODE_BADMONITORINGMODEINVALID; /* Set the MonitoringMode, store the old mode */ UA_MonitoringMode oldMode = mon->monitoringMode; mon->monitoringMode = monitoringMode; UA_Notification *notification; /* Reporting is disabled. This causes all Notifications to be dequeued and * deleted. Also remove the last samples so that we immediately generate a * Notification when re-activated. */ if(mon->monitoringMode == UA_MONITORINGMODE_DISABLED) { UA_Notification *notification_tmp; UA_MonitoredItem_unregisterSampling(server, mon); TAILQ_FOREACH_SAFE(notification, &mon->queue, localEntry, notification_tmp) { UA_Notification_delete(notification); } UA_DataValue_clear(&mon->lastValue); return UA_STATUSCODE_GOOD; } /* 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) { /* Make all notifications reporting. Re-enqueue to ensure they have the * right order if some notifications are already reported by a trigger * link. */ TAILQ_FOREACH(notification, &mon->queue, localEntry) { UA_Notification_dequeueSub(notification); UA_Notification_enqueueSub(notification); } } else /* mon->monitoringMode == UA_MONITORINGMODE_SAMPLING */ { /* Make all notifications non-reporting */ TAILQ_FOREACH(notification, &mon->queue, localEntry) UA_Notification_dequeueSub(notification); } /* Register the sampling callback with an interval. If registering the * sampling callback failed, set to disabled. But don't delete the current * notifications. */ UA_StatusCode res = UA_MonitoredItem_registerSampling(server, mon); if(res != UA_STATUSCODE_GOOD) { mon->monitoringMode = UA_MONITORINGMODE_DISABLED; return res; } /* Manually create the first sample if the MonitoredItem was disabled, the * MonitoredItem is now sampling (or reporting) and it is not an * Event-MonitoredItem */ if(oldMode == UA_MONITORINGMODE_DISABLED && mon->monitoringMode > UA_MONITORINGMODE_DISABLED && mon->itemToMonitor.attributeId != UA_ATTRIBUTEID_EVENTNOTIFIER) monitoredItem_sampleCallback(server, mon); return UA_STATUSCODE_GOOD; } void UA_MonitoredItem_delete(UA_Server *server, UA_MonitoredItem *mon) { UA_LOCK_ASSERT(&server->serviceMutex, 1); /* Remove the sampling callback */ UA_MonitoredItem_unregisterSampling(server, mon); /* Deregister in Server and Subscription */ if(mon->registered) UA_Server_unregisterMonitoredItem(server, mon); /* Remove the TriggeringLinks */ if(mon->triggeringLinksSize > 0) { UA_free(mon->triggeringLinks); mon->triggeringLinks = NULL; mon->triggeringLinksSize = 0; } /* Remove the queued notifications attached to the subscription */ UA_Notification *notification, *notification_tmp; TAILQ_FOREACH_SAFE(notification, &mon->queue, localEntry, notification_tmp) { UA_Notification_delete(notification); } /* Remove the settings */ UA_ReadValueId_clear(&mon->itemToMonitor); UA_MonitoringParameters_clear(&mon->parameters); /* Remove the last samples */ UA_DataValue_clear(&mon->lastValue); /* Add a delayed callback to remove the MonitoredItem when the current jobs * have completed. This is needed to allow that a local MonitoredItem can * remove itself in the callback. */ mon->delayedFreePointers.callback = NULL; mon->delayedFreePointers.application = server; mon->delayedFreePointers.data = NULL; mon->delayedFreePointers.nextTime = UA_DateTime_nowMonotonic() + 1; mon->delayedFreePointers.interval = 0; UA_Timer_addTimerEntry(&server->timer, &mon->delayedFreePointers, NULL); } void UA_MonitoredItem_ensureQueueSpace(UA_Server *server, UA_MonitoredItem *mon) { /* There can be only one EventOverflow more than normal entries. Because * EventOverflows are never adjacent. */ UA_assert(mon->queueSize >= mon->eventOverflows); UA_assert(mon->eventOverflows <= mon->queueSize - mon->eventOverflows + 1); /* Always attached to a Subscription (no local MonitoredItem) */ UA_Subscription *sub = mon->subscription; UA_assert(sub); /* Nothing to do */ if(mon->queueSize - mon->eventOverflows <= mon->parameters.queueSize) return; /* Remove notifications until the required queue size is reached */ UA_Boolean reporting = false; size_t remove = mon->queueSize - mon->eventOverflows - mon->parameters.queueSize; while(remove > 0) { /* The minimum queue size (without EventOverflows) is 1. At least two * notifications that are not EventOverflows are 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->parameters.discardOldest) { /* Remove the oldest */ del = TAILQ_FIRST(&mon->queue); #if defined(UA_ENABLE_SUBSCRIPTIONS_EVENTS) && !defined(__clang_analyzer__) while(del->isOverflowEvent) del = TAILQ_NEXT(del, localEntry); /* 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, localEntry); #if defined(UA_ENABLE_SUBSCRIPTIONS_EVENTS) && !defined(__clang_analyzer__) while(del->isOverflowEvent) del = TAILQ_PREV(del, NotificationQueue, localEntry); /* skip overflow events */ #endif } UA_assert(del); /* There must have been one entry that can be deleted */ /* Only create OverflowEvents (and set InfoBits) if the notification * that is removed is reported */ if(TAILQ_NEXT(del, globalEntry) != UA_SUBSCRIPTION_QUEUE_SENTINEL) reporting = true; /* Move the entry after del in the per-MonitoredItem queue right after * del in the per-Subscription queue. So we don't starve MonitoredItems * with a high sampling interval in the Subscription queue by always * removing their first appearance in the per-Subscription queue. * * With MonitoringMode == SAMPLING, the Notifications are not (all) in * the per-Subscription queue. Don't reinsert in that case. * * For the reinsertion to work, first insert into the per-Subscription * queue. */ if(TAILQ_NEXT(del, globalEntry) != UA_SUBSCRIPTION_QUEUE_SENTINEL) { UA_Notification *after_del = TAILQ_NEXT(del, localEntry); UA_assert(after_del); /* There must be one remaining element after del */ if(TAILQ_NEXT(after_del, globalEntry) != UA_SUBSCRIPTION_QUEUE_SENTINEL) { TAILQ_REMOVE(&sub->notificationQueue, after_del, globalEntry); TAILQ_INSERT_AFTER(&sub->notificationQueue, del, after_del, globalEntry); } } remove--; /* Delete the notification and remove it from the queues */ UA_Notification_delete(del); /* Update the subscription diagnostics statistics */ #ifdef UA_ENABLE_DIAGNOSTICS sub->monitoringQueueOverflowCount++; #endif /* Assertions to help Clang's scan-analyzer */ UA_assert(del != TAILQ_FIRST(&mon->queue)); UA_assert(del != TAILQ_LAST(&mon->queue, NotificationQueue)); UA_assert(del != TAILQ_PREV(TAILQ_LAST(&mon->queue, NotificationQueue), NotificationQueue, localEntry)); } /* Leave an entry to indicate that notifications were removed */ if(reporting) { #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS if(mon->itemToMonitor.attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) createEventOverflowNotification(server, sub, mon); else #endif setOverflowInfoBits(mon); } } UA_StatusCode UA_MonitoredItem_registerSampling(UA_Server *server, UA_MonitoredItem *mon) { UA_LOCK_ASSERT(&server->serviceMutex, 1); /* Sampling is already registered */ if(mon->samplingType != UA_MONITOREDITEMSAMPLINGTYPE_NONE) return UA_STATUSCODE_GOOD; UA_StatusCode res = UA_STATUSCODE_GOOD; UA_Subscription *sub = mon->subscription; if(mon->itemToMonitor.attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER || mon->parameters.samplingInterval == 0.0) { /* Add to the linked list in the node */ UA_Session *session = &server->adminSession; if(sub) session = sub->session; res = UA_Server_editNode(server, session, &mon->itemToMonitor.nodeId, addMonitoredItemBackpointer, mon); if(res == UA_STATUSCODE_GOOD) mon->samplingType = UA_MONITOREDITEMSAMPLINGTYPE_EVENT; return res; } else if(mon->parameters.samplingInterval < 0.0) { /* Add to the subscription for sampling before every publish */ if(!sub) return UA_STATUSCODE_BADINTERNALERROR; /* Not possible for local MonitoredItems */ LIST_INSERT_HEAD(&sub->samplingMonitoredItems, mon, sampling.samplingListEntry); mon->samplingType = UA_MONITOREDITEMSAMPLINGTYPE_PUBLISH; } else { /* DataChange MonitoredItems with a positive sampling interval have a * repeated callback. Other MonitoredItems are attached to the Node in a * linked list of backpointers. */ res = addRepeatedCallback(server, (UA_ServerCallback)UA_MonitoredItem_sampleCallback, mon, mon->parameters.samplingInterval, &mon->sampling.callbackId); if(res == UA_STATUSCODE_GOOD) mon->samplingType = UA_MONITOREDITEMSAMPLINGTYPE_CYCLIC; } return res; } void UA_MonitoredItem_unregisterSampling(UA_Server *server, UA_MonitoredItem *mon) { UA_LOCK_ASSERT(&server->serviceMutex, 1); switch(mon->samplingType) { case UA_MONITOREDITEMSAMPLINGTYPE_CYCLIC: /* Remove repeated callback */ removeCallback(server, mon->sampling.callbackId); break; case UA_MONITOREDITEMSAMPLINGTYPE_EVENT: { /* Added to a node */ UA_Subscription *sub = mon->subscription; UA_Session *session = &server->adminSession; if(sub) session = sub->session; UA_Server_editNode(server, session, &mon->itemToMonitor.nodeId, removeMonitoredItemBackPointer, mon); break; } case UA_MONITOREDITEMSAMPLINGTYPE_PUBLISH: /* Added to the subscription */ LIST_REMOVE(mon, sampling.samplingListEntry); break; case UA_MONITOREDITEMSAMPLINGTYPE_NONE: default: /* Sampling is not registered */ break; } mon->samplingType = UA_MONITOREDITEMSAMPLINGTYPE_NONE; } UA_StatusCode UA_MonitoredItem_removeLink(UA_Subscription *sub, UA_MonitoredItem *mon, UA_UInt32 linkId) { /* Find the index */ size_t i = 0; for(; i < mon->triggeringLinksSize; i++) { if(mon->triggeringLinks[i] == linkId) break; } /* Not existing / already removed */ if(i == mon->triggeringLinksSize) return UA_STATUSCODE_BADMONITOREDITEMIDINVALID; /* Remove the link */ mon->triggeringLinksSize--; if(mon->triggeringLinksSize == 0) { UA_free(mon->triggeringLinks); mon->triggeringLinks = NULL; } else { mon->triggeringLinks[i] = mon->triggeringLinks[mon->triggeringLinksSize]; UA_UInt32 *tmpLinks = (UA_UInt32*) UA_realloc(mon->triggeringLinks, mon->triggeringLinksSize * sizeof(UA_UInt32)); if(tmpLinks) mon->triggeringLinks = tmpLinks; } /* Does the target MonitoredItem exist? This is stupid, but the CTT wants us * to to this. We don't auto-remove links together with the target * MonitoredItem. Links to removed MonitoredItems are removed when the link * triggers and the target no longer exists. */ UA_MonitoredItem *mon2 = UA_Subscription_getMonitoredItem(sub, linkId); if(!mon2) return UA_STATUSCODE_BADMONITOREDITEMIDINVALID; return UA_STATUSCODE_GOOD; } UA_StatusCode UA_MonitoredItem_addLink(UA_Subscription *sub, UA_MonitoredItem *mon, UA_UInt32 linkId) { /* Does the target MonitoredItem exist? */ UA_MonitoredItem *mon2 = UA_Subscription_getMonitoredItem(sub, linkId); if(!mon2) return UA_STATUSCODE_BADMONITOREDITEMIDINVALID; /* Does the link already exist? */ for(size_t i = 0 ; i < mon->triggeringLinksSize; i++) { if(mon->triggeringLinks[i] == linkId) return UA_STATUSCODE_GOOD; } /* Allocate the memory */ UA_UInt32 *tmpLinkIds = (UA_UInt32*) UA_realloc(mon->triggeringLinks, (mon->triggeringLinksSize + 1) * sizeof(UA_UInt32)); if(!tmpLinkIds) return UA_STATUSCODE_BADOUTOFMEMORY; mon->triggeringLinks = tmpLinkIds; /* Add the link */ mon->triggeringLinks[mon->triggeringLinksSize] = linkId; mon->triggeringLinksSize++; return UA_STATUSCODE_GOOD; } #endif /* UA_ENABLE_SUBSCRIPTIONS */ /**** amalgamated original file "/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-2020 (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 */ /* Detect value changes outside the deadband */ #define UA_DETECT_DEADBAND(TYPE) do { \ TYPE v1 = *(const TYPE*)data1; \ TYPE v2 = *(const TYPE*)data2; \ TYPE diff = (v1 > v2) ? (TYPE)(v1 - v2) : (TYPE)(v2 - v1); \ return ((UA_Double)diff > deadband); \ } while(false); static UA_Boolean detectScalarDeadBand(const void *data1, const void *data2, const UA_DataType *type, const UA_Double deadband) { if(type->typeKind == UA_DATATYPEKIND_SBYTE) { UA_DETECT_DEADBAND(UA_SByte); } else if(type->typeKind == UA_DATATYPEKIND_BYTE) { UA_DETECT_DEADBAND(UA_Byte); } else if(type->typeKind == UA_DATATYPEKIND_INT16) { UA_DETECT_DEADBAND(UA_Int16); } else if(type->typeKind == UA_DATATYPEKIND_UINT16) { UA_DETECT_DEADBAND(UA_UInt16); } else if(type->typeKind == UA_DATATYPEKIND_INT32) { UA_DETECT_DEADBAND(UA_Int32); } else if(type->typeKind == UA_DATATYPEKIND_UINT32) { UA_DETECT_DEADBAND(UA_UInt32); } else if(type->typeKind == UA_DATATYPEKIND_INT64) { UA_DETECT_DEADBAND(UA_Int64); } else if(type->typeKind == UA_DATATYPEKIND_UINT64) { UA_DETECT_DEADBAND(UA_UInt64); } else if(type->typeKind == UA_DATATYPEKIND_FLOAT) { UA_DETECT_DEADBAND(UA_Float); } else if(type->typeKind == UA_DATATYPEKIND_DOUBLE) { UA_DETECT_DEADBAND(UA_Double); } else { return false; /* Not a known numerical type */ } } static UA_Boolean detectVariantDeadband(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; size_t length = 1; if(!UA_Variant_isScalar(value)) length = value->arrayLength; uintptr_t data = (uintptr_t)value->data; uintptr_t oldData = (uintptr_t)oldValue->data; UA_UInt32 memSize = value->type->memSize; for(size_t i = 0; i < length; ++i) { if(detectScalarDeadBand((const void*)data, (const void*)oldData, value->type, deadbandValue)) return true; data += memSize; oldData += memSize; } return false; } static UA_Boolean detectValueChange(UA_Server *server, UA_MonitoredItem *mon, const UA_DataValue *value) { UA_LOCK_ASSERT(&server->serviceMutex, 1); /* Status changes are always reported */ if(value->hasStatus != mon->lastValue.hasStatus || value->status != mon->lastValue.status) { return true; } /* Default trigger is Status + Value */ UA_DataChangeTrigger trigger = UA_DATACHANGETRIGGER_STATUSVALUE; /* Use the configured trigger */ const UA_DataChangeFilter *dcf = NULL; const UA_ExtensionObject *filter = &mon->parameters.filter; if(filter->content.decoded.type == &UA_TYPES[UA_TYPES_DATACHANGEFILTER]) { dcf = (UA_DataChangeFilter*)filter->content.decoded.data; trigger = dcf->trigger; } /* The status was already tested above */ if(trigger == UA_DATACHANGETRIGGER_STATUS) return false; UA_assert(trigger == UA_DATACHANGETRIGGER_STATUSVALUE || trigger == UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP); /* Test absolute deadband */ if(dcf && dcf->deadbandType == UA_DEADBANDTYPE_ABSOLUTE && value->value.type != NULL && UA_DataType_isNumeric(value->value.type)) return detectVariantDeadband(&value->value, &mon->lastValue.value, dcf->deadbandValue); /* Compare the source timestamp if the trigger requires that */ if(trigger == UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP) { if(value->hasSourceTimestamp != mon->lastValue.hasSourceTimestamp) return true; if(value->hasSourceTimestamp && value->sourceTimestamp != mon->lastValue.sourceTimestamp) return true; } /* Has the value changed? */ if(value->hasValue != mon->lastValue.hasValue) return true; return (UA_order(&value->value, &mon->lastValue.value, &UA_TYPES[UA_TYPES_VARIANT]) != UA_ORDER_EQ); } UA_StatusCode UA_MonitoredItem_createDataChangeNotification(UA_Server *server, UA_Subscription *sub, UA_MonitoredItem *mon, const UA_DataValue *value) { /* Allocate a new notification */ UA_Notification *newNotification = UA_Notification_new(); if(!newNotification) return UA_STATUSCODE_BADOUTOFMEMORY; /* Prepare the notification */ newNotification->mon = mon; newNotification->data.dataChange.clientHandle = mon->parameters.clientHandle; UA_StatusCode retval = UA_DataValue_copy(value, &newNotification->data.dataChange.value); if(retval != UA_STATUSCODE_GOOD) { UA_free(newNotification); return retval; } /* Enqueue the notification */ UA_assert(sub); UA_Notification_enqueueAndTrigger(server, newNotification); return UA_STATUSCODE_GOOD; } /* Moves the value to the MonitoredItem if successful */ UA_StatusCode sampleCallbackWithValue(UA_Server *server, UA_Subscription *sub, UA_MonitoredItem *mon, UA_DataValue *value) { UA_assert(mon->itemToMonitor.attributeId != UA_ATTRIBUTEID_EVENTNOTIFIER); /* Has the value changed (with the filters applied)? */ UA_Boolean changed = detectValueChange(server, mon, value); if(!changed) { UA_LOG_DEBUG_SUBSCRIPTION(&server->config.logger, sub, "MonitoredItem %" PRIi32 " | " "The value has not changed", mon->monitoredItemId); UA_DataValue_clear(value); return UA_STATUSCODE_GOOD; } /* The MonitoredItem is attached to a subscription (not server-local). * Prepare a notification and enqueue it. */ if(sub) { UA_StatusCode retval = UA_MonitoredItem_createDataChangeNotification(server, sub, mon, value); if(retval != UA_STATUSCODE_GOOD) return retval; } /* <-- Point of no return --> */ /* Move/store the value for filter comparison and TransferSubscription */ UA_DataValue_clear(&mon->lastValue); mon->lastValue = *value; /* 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; getNodeContext(server, mon->itemToMonitor.nodeId, &nodeContext); UA_UNLOCK(&server->serviceMutex); localMon->callback.dataChangeCallback(server, mon->monitoredItemId, localMon->context, &mon->itemToMonitor.nodeId, nodeContext, mon->itemToMonitor.attributeId, value); UA_LOCK(&server->serviceMutex); } return UA_STATUSCODE_GOOD; } void UA_MonitoredItem_sampleCallback(UA_Server *server, UA_MonitoredItem *monitoredItem) { UA_LOCK(&server->serviceMutex); monitoredItem_sampleCallback(server, monitoredItem); UA_UNLOCK(&server->serviceMutex); } void monitoredItem_sampleCallback(UA_Server *server, UA_MonitoredItem *monitoredItem) { UA_LOCK_ASSERT(&server->serviceMutex, 1); UA_Subscription *sub = monitoredItem->subscription; UA_Session *session = &server->adminSession; if(sub) session = sub->session; UA_LOG_DEBUG_SUBSCRIPTION(&server->config.logger, sub, "MonitoredItem %" PRIi32 " | " "Sample callback called", monitoredItem->monitoredItemId); UA_assert(monitoredItem->itemToMonitor.attributeId != UA_ATTRIBUTEID_EVENTNOTIFIER); /* Sample the value. The sample can still point into the node. */ UA_DataValue value = UA_Server_readWithSession(server, session, &monitoredItem->itemToMonitor, monitoredItem->timestampsToReturn); /* Operate on the sample. The sample is consumed when the status is good. */ UA_StatusCode res = sampleCallbackWithValue(server, sub, monitoredItem, &value); if(res != UA_STATUSCODE_GOOD) { UA_DataValue_clear(&value); UA_LOG_WARNING_SUBSCRIPTION(&server->config.logger, sub, "MonitoredItem %" PRIi32 " | " "Sampling returned the statuscode %s", monitoredItem->monitoredItemId, UA_StatusCode_name(res)); } } #endif /* UA_ENABLE_SUBSCRIPTIONS */ /**** amalgamated original file "/src/server/ua_subscription_events.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) Ari Breitkreuz, fortiss GmbH * Copyright 2020 (c) Christian von Arnim, ISW University of Stuttgart (for VDW and umati) * Copyright 2021 (c) Fraunhofer IOSB (Author: Andreas Ebner) */ #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS /* We use a 16-Byte ByteString as an identifier */ UA_StatusCode UA_Event_generateEventId(UA_ByteString *generatedId) { /* EventId is a ByteString, which is basically just a string * We will use a 16-Byte ByteString as an identifier */ UA_StatusCode res = UA_ByteString_allocBuffer(generatedId, 16 * sizeof(UA_Byte)); if(res != UA_STATUSCODE_GOOD) return res; UA_UInt32 *ids = (UA_UInt32*)generatedId->data; ids[0] = UA_UInt32_random(); ids[1] = UA_UInt32_random(); ids[2] = UA_UInt32_random(); ids[3] = UA_UInt32_random(); return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Server_createEvent(UA_Server *server, const UA_NodeId eventType, UA_NodeId *outNodeId) { UA_LOCK(&server->serviceMutex); if(!outNodeId) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_USERLAND, "outNodeId must not be NULL. The event's NodeId must be returned " "so it can be triggered."); UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_BADINVALIDARGUMENT; } /* Make sure the eventType is a subtype of BaseEventType */ UA_NodeId baseEventTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE); if(!isNodeInTree_singleRef(server, &eventType, &baseEventTypeId, UA_REFERENCETYPEINDEX_HASSUBTYPE)) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_USERLAND, "Event type must be a subtype of BaseEventType!"); UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_BADINVALIDARGUMENT; } /* Create an ObjectNode which represents the event */ UA_QualifiedName name; /* set a dummy name. This is not used. */ name = UA_QUALIFIEDNAME(0,"E"); UA_NodeId newNodeId = UA_NODEID_NULL; UA_ObjectAttributes oAttr = UA_ObjectAttributes_default; UA_StatusCode retval = addNode(server, UA_NODECLASS_OBJECT, &UA_NODEID_NULL, /* Set a random unused NodeId */ &UA_NODEID_NULL, /* No parent */ &UA_NODEID_NULL, /* No parent reference */ name, /* an event does not have a name */ &eventType, /* the type of the event */ (const UA_NodeAttributes*)&oAttr, /* default attributes are fine */ &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES], NULL, /* no node context */ &newNodeId); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_USERLAND, "Adding event failed. StatusCode %s", UA_StatusCode_name(retval)); UA_UNLOCK(&server->serviceMutex); return retval; } /* Find the eventType variable */ name = UA_QUALIFIEDNAME(0, "EventType"); UA_BrowsePathResult bpr = browseSimplifiedBrowsePath(server, newNodeId, 1, &name); if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) { retval = bpr.statusCode; UA_BrowsePathResult_clear(&bpr); deleteNode(server, newNodeId, true); UA_NodeId_clear(&newNodeId); UA_UNLOCK(&server->serviceMutex); return retval; } /* Set the EventType */ UA_Variant value; UA_Variant_init(&value); UA_Variant_setScalar(&value, (void*)(uintptr_t)&eventType, &UA_TYPES[UA_TYPES_NODEID]); retval = writeValueAttribute(server, &server->adminSession, &bpr.targets[0].targetId.nodeId, &value); UA_BrowsePathResult_clear(&bpr); if(retval != UA_STATUSCODE_GOOD) { deleteNode(server, newNodeId, true); UA_NodeId_clear(&newNodeId); UA_UNLOCK(&server->serviceMutex); return retval; } *outNodeId = newNodeId; UA_UNLOCK(&server->serviceMutex); return UA_STATUSCODE_GOOD; } static UA_StatusCode eventSetStandardFields(UA_Server *server, const UA_NodeId *event, const UA_NodeId *origin, UA_ByteString *outEventId) { /* Set the SourceNode */ UA_StatusCode retval; UA_QualifiedName name = UA_QUALIFIEDNAME(0, "SourceNode"); UA_BrowsePathResult bpr = browseSimplifiedBrowsePath(server, *event, 1, &name); if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) { retval = bpr.statusCode; UA_BrowsePathResult_clear(&bpr); return retval; } UA_Variant value; UA_Variant_init(&value); UA_Variant_setScalarCopy(&value, origin, &UA_TYPES[UA_TYPES_NODEID]); retval = writeValueAttribute(server, &server->adminSession, &bpr.targets[0].targetId.nodeId, &value); UA_Variant_clear(&value); UA_BrowsePathResult_clear(&bpr); if(retval != UA_STATUSCODE_GOOD) return retval; /* Set the ReceiveTime */ name = UA_QUALIFIEDNAME(0, "ReceiveTime"); bpr = browseSimplifiedBrowsePath(server, *event, 1, &name); if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) { retval = bpr.statusCode; UA_BrowsePathResult_clear(&bpr); return retval; } UA_DateTime rcvTime = UA_DateTime_now(); UA_Variant_setScalar(&value, &rcvTime, &UA_TYPES[UA_TYPES_DATETIME]); retval = writeValueAttribute(server, &server->adminSession, &bpr.targets[0].targetId.nodeId, &value); UA_BrowsePathResult_clear(&bpr); if(retval != UA_STATUSCODE_GOOD) return retval; /* Set the EventId */ UA_ByteString eventId = UA_BYTESTRING_NULL; retval = UA_Event_generateEventId(&eventId); if(retval != UA_STATUSCODE_GOOD) return retval; name = UA_QUALIFIEDNAME(0, "EventId"); bpr = browseSimplifiedBrowsePath(server, *event, 1, &name); if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) { retval = bpr.statusCode; UA_ByteString_clear(&eventId); UA_BrowsePathResult_clear(&bpr); return retval; } UA_Variant_init(&value); UA_Variant_setScalar(&value, &eventId, &UA_TYPES[UA_TYPES_BYTESTRING]); retval = writeValueAttribute(server, &server->adminSession, &bpr.targets[0].targetId.nodeId, &value); UA_BrowsePathResult_clear(&bpr); if(retval != UA_STATUSCODE_GOOD) { UA_ByteString_clear(&eventId); return retval; } /* Return the EventId */ if(outEventId) *outEventId = eventId; else UA_ByteString_clear(&eventId); return UA_STATUSCODE_GOOD; } /* Filters an event according to the filter specified by mon and then adds it to * mons notification queue */ UA_StatusCode UA_Event_addEventToMonitoredItem(UA_Server *server, const UA_NodeId *event, UA_MonitoredItem *mon) { UA_Notification *notification = UA_Notification_new(); if(!notification) return UA_STATUSCODE_BADOUTOFMEMORY; if(mon->parameters.filter.content.decoded.type != &UA_TYPES[UA_TYPES_EVENTFILTER]) return UA_STATUSCODE_BADFILTERNOTALLOWED; UA_EventFilter *eventFilter = (UA_EventFilter*) mon->parameters.filter.content.decoded.data; /* The MonitoredItem must be attached to a Subscription. This code path is * not taken for local MonitoredItems (once they are enabled for Events). */ UA_Subscription *sub = mon->subscription; UA_assert(sub); UA_Session *session = sub->session; UA_StatusCode retval = filterEvent(server, session, event, eventFilter, ¬ification->data.event, ¬ification->result); if(retval != UA_STATUSCODE_GOOD) { UA_Notification_delete(notification); if(retval == UA_STATUSCODE_BADNOMATCH) return UA_STATUSCODE_GOOD; return retval; } notification->data.event.clientHandle = mon->parameters.clientHandle; notification->mon = mon; UA_Notification_enqueueAndTrigger(server, notification); return UA_STATUSCODE_GOOD; } #ifdef UA_ENABLE_HISTORIZING static void setHistoricalEvent(UA_Server *server, const UA_NodeId *origin, const UA_NodeId *emitNodeId, const UA_NodeId *eventNodeId) { UA_Variant historicalEventFilterValue; UA_Variant_init(&historicalEventFilterValue); /* A HistoricalEventNode that has event history available will provide this property */ UA_StatusCode retval = readObjectProperty(server, *emitNodeId, UA_QUALIFIEDNAME(0, "HistoricalEventFilter"), &historicalEventFilterValue); if(retval != UA_STATUSCODE_GOOD) { /* Do not vex users with no match errors */ if(retval != UA_STATUSCODE_BADNOMATCH) UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Cannot read the HistoricalEventFilter property of a " "listening node. StatusCode %s", UA_StatusCode_name(retval)); return; } /* If found then check if HistoricalEventFilter property has a valid value */ if(UA_Variant_isEmpty(&historicalEventFilterValue) || !UA_Variant_isScalar(&historicalEventFilterValue) || historicalEventFilterValue.type != &UA_TYPES[UA_TYPES_EVENTFILTER]) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "HistoricalEventFilter property of a listening node " "does not have a valid value"); UA_Variant_clear(&historicalEventFilterValue); return; } /* Finally, if found and valid then filter */ UA_EventFilter *filter = (UA_EventFilter*) historicalEventFilterValue.data; UA_EventFieldList efl; UA_EventFilterResult result; retval = filterEvent(server, &server->adminSession, eventNodeId, filter, &efl, &result); if(retval == UA_STATUSCODE_GOOD) server->config.historyDatabase.setEvent(server, server->config.historyDatabase.context, origin, emitNodeId, filter, &efl); UA_EventFilterResult_clear(&result); UA_Variant_clear(&historicalEventFilterValue); UA_EventFieldList_clear(&efl); } #endif static const UA_NodeId objectsFolderId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_OBJECTSFOLDER}}; #define EMIT_REFS_ROOT_COUNT 4 static const UA_NodeId emitReferencesRoots[EMIT_REFS_ROOT_COUNT] = {{0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_ORGANIZES}}, {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASCOMPONENT}}, {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASEVENTSOURCE}}, {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASNOTIFIER}}}; static const UA_NodeId isInFolderReferences[2] = {{0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_ORGANIZES}}, {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASCOMPONENT}}}; UA_StatusCode triggerEvent(UA_Server *server, const UA_NodeId eventNodeId, const UA_NodeId origin, UA_ByteString *outEventId, const UA_Boolean deleteEventNode) { UA_LOCK_ASSERT(&server->serviceMutex, 1); UA_LOG_NODEID_DEBUG(&origin, UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER, "Events: An event is triggered on node %.*s", (int)nodeIdStr.length, nodeIdStr.data)); #ifdef UA_ENABLE_SUBSCRIPTIONS_ALARMS_CONDITIONS UA_Boolean isCallerAC = false; if(isConditionOrBranch(server, &eventNodeId, &origin, &isCallerAC)) { if(!isCallerAC) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Condition Events: Please use A&C API to trigger Condition Events 0x%08X", UA_STATUSCODE_BADINVALIDARGUMENT); return UA_STATUSCODE_BADINVALIDARGUMENT; } } #endif /* UA_ENABLE_SUBSCRIPTIONS_ALARMS_CONDITIONS */ /* Check that the origin node exists */ const UA_Node *originNode = UA_NODESTORE_GET(server, &origin); if(!originNode) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_USERLAND, "Origin node for event does not exist."); return UA_STATUSCODE_BADNOTFOUND; } UA_NODESTORE_RELEASE(server, originNode); /* Make sure the origin is in the ObjectsFolder (TODO: or in the ViewsFolder) */ /* Only use Organizes and HasComponent to check if we are below the ObjectsFolder */ UA_StatusCode retval; UA_ReferenceTypeSet refTypes; UA_ReferenceTypeSet_init(&refTypes); for(int i = 0; i < 2; ++i) { UA_ReferenceTypeSet tmpRefTypes; retval = referenceTypeIndices(server, &isInFolderReferences[i], &tmpRefTypes, true); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Events: Could not create the list of references and their subtypes " "with StatusCode %s", UA_StatusCode_name(retval)); } refTypes = UA_ReferenceTypeSet_union(refTypes, tmpRefTypes); } if(!isNodeInTree(server, &origin, &objectsFolderId, &refTypes)) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_USERLAND, "Node for event must be in ObjectsFolder!"); return UA_STATUSCODE_BADINVALIDARGUMENT; } /* Update the standard fields of the event */ retval = eventSetStandardFields(server, &eventNodeId, &origin, outEventId); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Events: Could not set the standard event fields with StatusCode %s", UA_StatusCode_name(retval)); return retval; } /* List of nodes that emit the node. Events propagate upwards (bubble up) in * the node hierarchy. */ UA_ExpandedNodeId *emitNodes = NULL; size_t emitNodesSize = 0; /* Add the server node to the list of nodes from which the event is emitted. * The server node emits all events. * * Part 3, 7.17: In particular, the root notifier of a Server, the Server * Object defined in Part 5, is always capable of supplying all Events from * a Server and as such has implied HasEventSource References to every event * source in a Server. */ UA_NodeId emitStartNodes[2]; emitStartNodes[0] = origin; emitStartNodes[1] = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER); /* Get all ReferenceTypes over which the events propagate */ UA_ReferenceTypeSet emitRefTypes; UA_ReferenceTypeSet_init(&emitRefTypes); for(size_t i = 0; i < EMIT_REFS_ROOT_COUNT; i++) { UA_ReferenceTypeSet tmpRefTypes; retval = referenceTypeIndices(server, &emitReferencesRoots[i], &tmpRefTypes, true); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Events: Could not create the list of references for event " "propagation with StatusCode %s", UA_StatusCode_name(retval)); goto cleanup; } emitRefTypes = UA_ReferenceTypeSet_union(emitRefTypes, tmpRefTypes); } /* Get the list of nodes in the hierarchy that emits the event. */ retval = browseRecursive(server, 2, emitStartNodes, UA_BROWSEDIRECTION_INVERSE, &emitRefTypes, UA_NODECLASS_UNSPECIFIED, true, &emitNodesSize, &emitNodes); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Events: Could not create the list of nodes listening on the " "event with StatusCode %s", UA_StatusCode_name(retval)); goto cleanup; } /* Add the event to the listening MonitoredItems at each relevant node */ for(size_t i = 0; i < emitNodesSize; i++) { /* Get the node */ const UA_Node *node = UA_NODESTORE_GET(server, &emitNodes[i].nodeId); if(!node) continue; /* Only consider objects */ if(node->head.nodeClass != UA_NODECLASS_OBJECT) { UA_NODESTORE_RELEASE(server, node); continue; } /* Add event to monitoreditems */ UA_MonitoredItem *mon = node->head.monitoredItems; for(; mon != NULL; mon = mon->sampling.nodeListNext) { /* Is this an Event-MonitoredItem? */ if(mon->itemToMonitor.attributeId != UA_ATTRIBUTEID_EVENTNOTIFIER) continue; retval = UA_Event_addEventToMonitoredItem(server, &eventNodeId, mon); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Events: Could not add the event to a listening " "node with StatusCode %s", UA_StatusCode_name(retval)); retval = UA_STATUSCODE_GOOD; /* Only log problems with individual emit nodes */ } } UA_NODESTORE_RELEASE(server, node); /* Add event entry in the historical database */ #ifdef UA_ENABLE_HISTORIZING if(server->config.historyDatabase.setEvent) setHistoricalEvent(server, &origin, &emitNodes[i].nodeId, &eventNodeId); #endif } /* Delete the node representation of the event */ if(deleteEventNode) { retval = deleteNode(server, eventNodeId, true); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER, "Attempt to remove event using deleteNode failed. StatusCode %s", UA_StatusCode_name(retval)); } } cleanup: UA_Array_delete(emitNodes, emitNodesSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); return retval; } UA_StatusCode UA_Server_triggerEvent(UA_Server *server, const UA_NodeId eventNodeId, const UA_NodeId origin, UA_ByteString *outEventId, const UA_Boolean deleteEventNode) { UA_LOCK(&server->serviceMutex); UA_StatusCode res = triggerEvent(server, eventNodeId, origin, outEventId, deleteEventNode); UA_UNLOCK(&server->serviceMutex); return res; } #endif /* UA_ENABLE_SUBSCRIPTIONS_EVENTS */ /**** amalgamated original file "/src/server/ua_subscription_events_filter.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) Ari Breitkreuz, fortiss GmbH * Copyright 2020 (c) Christian von Arnim, ISW University of Stuttgart (for VDW and umati) * Copyright 2021 (c) Fraunhofer IOSB (Author: Andreas Ebner) */ #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS typedef struct { UA_Server *server; UA_Session *session; const UA_NodeId *eventNode; const UA_ContentFilter *contentFilter; UA_ContentFilterResult *contentFilterResult; UA_Variant *valueResult; UA_UInt16 index; } UA_FilterOperatorContext; static UA_StatusCode evaluateWhereClauseContentFilter(UA_FilterOperatorContext *ctx); /* Resolves a variant of type string or boolean into a corresponding status code */ static UA_StatusCode resolveBoolean(UA_Variant operand) { UA_String value; value = UA_STRING("True"); if(((operand.type == &UA_TYPES[UA_TYPES_STRING]) && (UA_String_equal((UA_String *)operand.data, &value))) || ((operand.type == &UA_TYPES[UA_TYPES_BOOLEAN]) && (*(UA_Boolean *)operand.data == UA_TRUE))) { return UA_STATUSCODE_GOOD; } value = UA_STRING("False"); if(((operand.type == &UA_TYPES[UA_TYPES_STRING]) && (UA_String_equal((UA_String *)operand.data, &value))) || ((operand.type == &UA_TYPES[UA_TYPES_BOOLEAN]) && (*(UA_Boolean *)operand.data == UA_FALSE))) { return UA_STATUSCODE_BADNOMATCH; } /* If the operand can't be resolved, an error is returned */ return UA_STATUSCODE_BADFILTEROPERANDINVALID; } /* Part 4: 7.4.4.5 SimpleAttributeOperand * The clause can point to any attribute of nodes. Either a child of the event * node and also the event type. */ static UA_StatusCode resolveSimpleAttributeOperand(UA_Server *server, UA_Session *session, const UA_NodeId *origin, const UA_SimpleAttributeOperand *sao, UA_Variant *value) { /* Prepare the ReadValueId */ UA_ReadValueId rvi; UA_ReadValueId_init(&rvi); rvi.indexRange = sao->indexRange; rvi.attributeId = sao->attributeId; UA_DataValue v; if(sao->browsePathSize == 0) { /* If this list (browsePath) is empty, the Node is the instance of the * TypeDefinition. (Part 4, 7.4.4.5) */ rvi.nodeId = *origin; /* A Condition is an indirection. Look up the target node. */ /* TODO: check for Branches! One Condition could have multiple Branches */ UA_NodeId conditionTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_CONDITIONTYPE); if(UA_NodeId_equal(&sao->typeDefinitionId, &conditionTypeId)) { #ifdef UA_ENABLE_SUBSCRIPTIONS_ALARMS_CONDITIONS UA_StatusCode res = UA_getConditionId(server, origin, &rvi.nodeId); if(res != UA_STATUSCODE_GOOD) return res; #else return UA_STATUSCODE_BADNOTSUPPORTED; #endif } v = UA_Server_readWithSession(server, session, &rvi, UA_TIMESTAMPSTORETURN_NEITHER); } else { /* Resolve the browse path, starting from the event-source (and not the * typeDefinitionId). */ UA_BrowsePathResult bpr = browseSimplifiedBrowsePath(server, *origin, sao->browsePathSize, sao->browsePath); if(bpr.targetsSize == 0 && bpr.statusCode == UA_STATUSCODE_GOOD) bpr.statusCode = UA_STATUSCODE_BADNOTFOUND; if(bpr.statusCode != UA_STATUSCODE_GOOD) { UA_StatusCode res = bpr.statusCode; UA_BrowsePathResult_clear(&bpr); return res; } /* Use the first match */ rvi.nodeId = bpr.targets[0].targetId.nodeId; v = UA_Server_readWithSession(server, session, &rvi, UA_TIMESTAMPSTORETURN_NEITHER); UA_BrowsePathResult_clear(&bpr); } /* Move the result to the output */ if(v.status == UA_STATUSCODE_GOOD && v.hasValue) *value = v.value; else UA_Variant_clear(&v.value); return v.status; } /* Resolve operands to variants according to the operand type. * Part 4: 7.17.3 Table 142 specifies the allowed types. */ static UA_Variant resolveOperand(UA_FilterOperatorContext *ctx, UA_UInt16 nr) { UA_StatusCode res; UA_Variant variant; UA_Variant_init(&variant); UA_ExtensionObject *op = &ctx->contentFilter->elements[ctx->index].filterOperands[nr]; if(op->content.decoded.type == &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND]) { /* SimpleAttributeOperand */ res = resolveSimpleAttributeOperand(ctx->server, ctx->session, ctx->eventNode, (UA_SimpleAttributeOperand *)op->content.decoded.data, &variant); } else if(op->content.decoded.type == &UA_TYPES[UA_TYPES_LITERALOPERAND]) { /* LiteralOperand */ variant = ((UA_LiteralOperand *)op->content.decoded.data)->value; res = UA_STATUSCODE_GOOD; } else if(op->content.decoded.type == &UA_TYPES[UA_TYPES_ELEMENTOPERAND]) { /* ElementOperand */ UA_UInt16 oldIndex = ctx->index; ctx->index = (UA_UInt16)((UA_ElementOperand *)op->content.decoded.data)->index; res = evaluateWhereClauseContentFilter(ctx); variant = ctx->valueResult[ctx->index]; ctx->index = oldIndex; /* restore the old index */ } else { res = UA_STATUSCODE_BADFILTEROPERANDINVALID; } if(res != UA_STATUSCODE_GOOD && res != UA_STATUSCODE_BADNOMATCH) { variant.type = NULL; ctx->contentFilterResult->elementResults[ctx->index].operandStatusCodes[nr] = res; } return variant; } static UA_StatusCode ofTypeOperator(UA_FilterOperatorContext *ctx) { UA_ContentFilterElement *pElement = &ctx->contentFilter->elements[ctx->index]; UA_Boolean result = false; ctx->valueResult[ctx->index].type = &UA_TYPES[UA_TYPES_BOOLEAN]; if(pElement->filterOperandsSize != 1) return UA_STATUSCODE_BADFILTEROPERANDCOUNTMISMATCH; if(pElement->filterOperands[0].content.decoded.type != &UA_TYPES[UA_TYPES_LITERALOPERAND]) return UA_STATUSCODE_BADFILTEROPERATORUNSUPPORTED; UA_LiteralOperand *literalOperand = (UA_LiteralOperand *) pElement->filterOperands[0].content.decoded.data; if(!UA_Variant_isScalar(&literalOperand->value)) return UA_STATUSCODE_BADEVENTFILTERINVALID; if(literalOperand->value.type != &UA_TYPES[UA_TYPES_NODEID] || literalOperand->value.data == NULL) return UA_STATUSCODE_BADEVENTFILTERINVALID; UA_NodeId *literalOperandNodeId = (UA_NodeId *) literalOperand->value.data; UA_Variant typeNodeIdVariant; UA_Variant_init(&typeNodeIdVariant); UA_StatusCode readStatusCode = readObjectProperty(ctx->server, *ctx->eventNode, UA_QUALIFIEDNAME(0, "EventType"), &typeNodeIdVariant); if(readStatusCode != UA_STATUSCODE_GOOD) return readStatusCode; if(!UA_Variant_isScalar(&typeNodeIdVariant) || typeNodeIdVariant.type != &UA_TYPES[UA_TYPES_NODEID] || typeNodeIdVariant.data == NULL) { UA_LOG_ERROR(&ctx->server->config.logger, UA_LOGCATEGORY_SERVER, "EventType has an invalid type."); UA_Variant_clear(&typeNodeIdVariant); return UA_STATUSCODE_BADINTERNALERROR; } /* check if the eventtype-nodeid is equal to the given oftype argument */ result = UA_NodeId_equal((UA_NodeId*) typeNodeIdVariant.data, literalOperandNodeId); /* check if the eventtype-nodeid is a subtype of the given oftype argument */ if(!result) result = isNodeInTree_singleRef(ctx->server, (UA_NodeId*) typeNodeIdVariant.data, literalOperandNodeId, UA_REFERENCETYPEINDEX_HASSUBTYPE); UA_Variant_clear(&typeNodeIdVariant); if(!result) return UA_STATUSCODE_BADNOMATCH; return UA_STATUSCODE_GOOD; } static UA_StatusCode andOperator(UA_FilterOperatorContext *ctx) { UA_StatusCode firstBoolean_and = resolveBoolean(resolveOperand(ctx, 0)); if(firstBoolean_and == UA_STATUSCODE_BADNOMATCH) { ctx->valueResult[ctx->index].type = &UA_TYPES[UA_TYPES_BOOLEAN]; return UA_STATUSCODE_BADNOMATCH; } /* Evaluation of second operand */ UA_StatusCode secondBoolean = resolveBoolean(resolveOperand(ctx, 1)); /* Filteroperator AND */ if(secondBoolean == UA_STATUSCODE_BADNOMATCH) { ctx->valueResult[ctx->index].type = &UA_TYPES[UA_TYPES_BOOLEAN]; return UA_STATUSCODE_BADNOMATCH; } if((firstBoolean_and == UA_STATUSCODE_GOOD) && (secondBoolean == UA_STATUSCODE_GOOD)) { ctx->valueResult[ctx->index].type = &UA_TYPES[UA_TYPES_BOOLEAN]; return UA_STATUSCODE_GOOD; } return UA_STATUSCODE_BADFILTERELEMENTINVALID; } static UA_StatusCode orOperator(UA_FilterOperatorContext *ctx) { UA_StatusCode firstBoolean_or = resolveBoolean(resolveOperand(ctx, 0)); if(firstBoolean_or == UA_STATUSCODE_GOOD) { ctx->valueResult[ctx->index].type = &UA_TYPES[UA_TYPES_BOOLEAN]; return UA_STATUSCODE_GOOD; } /* Evaluation of second operand */ UA_StatusCode secondBoolean = resolveBoolean(resolveOperand(ctx, 1)); if(secondBoolean == UA_STATUSCODE_GOOD) { ctx->valueResult[ctx->index].type = &UA_TYPES[UA_TYPES_BOOLEAN]; return UA_STATUSCODE_GOOD; } if((firstBoolean_or == UA_STATUSCODE_BADNOMATCH) && (secondBoolean == UA_STATUSCODE_BADNOMATCH)) { ctx->valueResult[ctx->index].type = &UA_TYPES[UA_TYPES_BOOLEAN]; return UA_STATUSCODE_BADNOMATCH; } return UA_STATUSCODE_BADFILTERELEMENTINVALID; } static UA_Boolean isNumericUnsigned(UA_UInt32 dataTypeKind){ if(dataTypeKind == UA_DATATYPEKIND_UINT64 || dataTypeKind == UA_DATATYPEKIND_UINT32 || dataTypeKind == UA_DATATYPEKIND_UINT16 || dataTypeKind == UA_DATATYPEKIND_BYTE) return true; return false; } static UA_Boolean isNumericSigned(UA_UInt32 dataTypeKind){ if(dataTypeKind == UA_DATATYPEKIND_INT64 || dataTypeKind == UA_DATATYPEKIND_INT32 || dataTypeKind == UA_DATATYPEKIND_INT16 || dataTypeKind == UA_DATATYPEKIND_SBYTE) return true; return false; } static UA_Boolean isFloatingPoint(UA_UInt32 dataTypeKind){ if(dataTypeKind == UA_DATATYPEKIND_FLOAT || dataTypeKind == UA_DATATYPEKIND_DOUBLE) return true; return false; } static UA_Boolean isStringType(UA_UInt32 dataTypeKind){ if(dataTypeKind == UA_DATATYPEKIND_STRING || dataTypeKind == UA_DATATYPEKIND_BYTESTRING) return true; return false; } static UA_StatusCode implicitNumericVariantTransformation(UA_Variant *variant, void *data){ if(variant->type == &UA_TYPES[UA_TYPES_UINT64]){ *(UA_UInt64 *)data = *(UA_UInt64 *)variant->data; UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_UINT64]); } else if(variant->type == &UA_TYPES[UA_TYPES_UINT32]){ *(UA_UInt64 *)data = *(UA_UInt32 *)variant->data; UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_UINT64]); } else if(variant->type == &UA_TYPES[UA_TYPES_UINT16]){ *(UA_UInt64 *)data = *(UA_UInt16 *)variant->data; UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_UINT64]); } else if(variant->type == &UA_TYPES[UA_TYPES_BYTE]){ *(UA_UInt64 *)data = *(UA_Byte *)variant->data; UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_UINT64]); } else if(variant->type == &UA_TYPES[UA_TYPES_INT64]){ *(UA_Int64 *)data = *(UA_Int64 *)variant->data; UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_INT64]); } else if(variant->type == &UA_TYPES[UA_TYPES_INT32]){ *(UA_Int64 *)data = *(UA_Int32 *)variant->data; UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_INT64]); } else if(variant->type == &UA_TYPES[UA_TYPES_INT16]){ *(UA_Int64 *)data = *(UA_Int16 *)variant->data; UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_INT64]); } else if(variant->type == &UA_TYPES[UA_TYPES_SBYTE]){ *(UA_Int64 *)data = *(UA_SByte *)variant->data; UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_INT64]); } else if(variant->type == &UA_TYPES[UA_TYPES_DOUBLE]){ *(UA_Double *)data = *(UA_Double *)variant->data; UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_DOUBLE]); } else if(variant->type == &UA_TYPES[UA_TYPES_SBYTE]){ *(UA_Double *)data = *(UA_Float *)variant->data; UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_DOUBLE]); } else { return UA_STATUSCODE_BADTYPEMISMATCH; } return UA_STATUSCODE_GOOD; } static UA_StatusCode implicitNumericVariantTransformationUnsingedToSigned(UA_Variant *variant, void *data){ if(variant->type == &UA_TYPES[UA_TYPES_UINT64]){ if(*(UA_UInt64 *)variant->data > UA_INT64_MAX) return UA_STATUSCODE_BADTYPEMISMATCH; *(UA_Int64 *)data = *(UA_Int64 *)variant->data; UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_INT64]); } else if(variant->type == &UA_TYPES[UA_TYPES_UINT32]){ *(UA_Int64 *)data = *(UA_Int32 *)variant->data; UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_INT64]); } else if(variant->type == &UA_TYPES[UA_TYPES_UINT16]){ *(UA_Int64 *)data = *(UA_Int16 *)variant->data; UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_INT64]); } else if(variant->type == &UA_TYPES[UA_TYPES_BYTE]){ *(UA_Int64 *)data = *(UA_Byte *)variant->data; UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_INT64]); } else { return UA_STATUSCODE_BADTYPEMISMATCH; } return UA_STATUSCODE_GOOD; } static UA_StatusCode implicitNumericVariantTransformationSignedToUnSigned(UA_Variant *variant, void *data){ if(*(UA_Int64 *)variant->data < 0) return UA_STATUSCODE_BADTYPEMISMATCH; if(variant->type == &UA_TYPES[UA_TYPES_INT64]){ *(UA_UInt64 *)data = *(UA_UInt64 *)variant->data; UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_UINT64]); } else if(variant->type == &UA_TYPES[UA_TYPES_INT32]){ *(UA_UInt64 *)data = *(UA_UInt32 *)variant->data; UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_UINT64]); } else if(variant->type == &UA_TYPES[UA_TYPES_INT16]){ *(UA_UInt64 *)data = *(UA_UInt16 *)variant->data; UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_UINT64]); } else if(variant->type == &UA_TYPES[UA_TYPES_SBYTE]){ *(UA_UInt64 *)data = *(UA_Byte *)variant->data; UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_UINT64]); } else { return UA_STATUSCODE_BADTYPEMISMATCH; } return UA_STATUSCODE_GOOD; } /* 0 -> Same Type, 1 -> Implicit Cast, 2 -> Only explicit Cast, -1 -> cast invalid */ static UA_SByte convertLookup[21][21] = { { 0, 1,-1,-1, 1,-1, 1,-1, 1, 1, 1,-1, 1,-1, 2,-1,-1, 1, 1, 1,-1}, { 2, 0,-1,-1, 1,-1, 1,-1, 1, 1, 1,-1, 1,-1, 2,-1,-1, 1, 1, 1,-1}, {-1,-1, 0,-1,-1,-1,-1, 2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, {-1,-1,-1, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 2,-1,-1,-1,-1,-1,-1}, { 2, 2,-1,-1, 0,-1, 2,-1, 2, 2, 2,-1, 2,-1, 2,-1,-1, 2, 2, 2,-1}, {-1,-1,-1,-1,-1, 0,-1,-1,-1,-1,-1, 2,-1,-1, 1,-1,-1,-1,-1,-1,-1}, { 2, 2,-1,-1, 1,-1, 0,-1, 2, 2, 2,-1, 2,-1, 2,-1,-1, 2, 2, 2,-1}, {-1,-1, 2,-1,-1,-1,-1, 0,-1,-1,-1,-1,-1,-1, 2,-1,-1,-1,-1,-1,-1}, { 2, 2,-1,-1, 1,-1, 1,-1, 0, 1, 1,-1, 2,-1, 2,-1,-1, 2, 1, 1,-1}, { 2, 2,-1,-1, 1,-1, 1,-1, 2, 0, 1,-1, 2, 2, 2,-1,-1, 2, 2, 1,-1}, { 2, 2,-1,-1, 1,-1, 1,-1, 2, 2, 0,-1, 2, 2, 2,-1,-1, 2, 2, 2,-1}, {-1,-1,-1,-1,-1, 1,-1,-1,-1,-1,-1, 0,-1,-1, 1,-1,-1,-1,-1,-1,-1}, { 2, 2,-1,-1, 1,-1, 1,-1, 1, 1, 1,-1, 0,-1, 2,-1,-1, 1, 1, 1,-1}, {-1,-1,-1,-1,-1,-1,-1,-1,-1, 1, 1,-1,-1, 0,-1,-1,-1, 2, 1, 1,-1}, { 1, 1,-1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1,-1, 0, 2, 2, 1, 1, 1,-1}, {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 1, 0,-1,-1,-1,-1,-1}, {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 1, 1, 0,-1,-1,-1,-1}, { 2, 2,-1,-1, 1,-1, 1,-1, 1, 1, 1,-1, 2, 1, 2,-1,-1, 0, 1, 1,-1}, { 2, 2,-1,-1, 1,-1, 1,-1, 2, 1, 1,-1, 2, 2, 2,-1,-1, 2, 0, 1,-1}, { 2, 2,-1,-1, 1,-1, 1,-1, 2, 2, 1,-1, 2, 2, 2,-1,-1, 2, 2, 0,-1}, {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 0} }; /* This array maps the index of the * standard DataType-Kind order to the * order of the type convertion array */ static UA_Byte dataTypeKindIndex[30] = { 0, 12, 1, 8, 17, 9, 18, 10, 19, 6, 4, 14, 3, 7, 2, 20, 11, 5, 13, 16, 15, 255,255,255,255, 255,255,255,255,255 }; /* The OPC UA Standard defines in Part 4 several data type casting-rules. (see * 1.04 part 4 Table 122) * Return: * 0 -> same type * 1 -> types can be casted implicit * 2 -> types can only be explicitly casted * -1 -> types can't be casted */ static UA_SByte checkTypeCastingOption(const UA_DataType *cast_target, const UA_DataType *cast_source) { UA_Byte firstOperatorTypeKindIndex = dataTypeKindIndex[cast_target->typeKind]; UA_Byte secondOperatorTypeKindIndex = dataTypeKindIndex[cast_source->typeKind]; if(firstOperatorTypeKindIndex == UA_BYTE_MAX || secondOperatorTypeKindIndex == UA_BYTE_MAX) return -1; return convertLookup[firstOperatorTypeKindIndex][secondOperatorTypeKindIndex]; } /* Compare operation for equal, gt, le, gte, lee * UA_STATUSCODE_GOOD if the comparison was true * UA_STATUSCODE_BADNOMATCH if the comparison was false * UA_STATUSCODE_BADFILTEROPERATORINVALID for invalid operators * UA_STATUSCODE_BADTYPEMISMATCH if one of the operands was not numeric * ToDo Array-Casting */ static UA_StatusCode compareOperation(UA_Variant *firstOperand, UA_Variant *secondOperand, UA_FilterOperator op) { /* get precedence of the operand types */ UA_Int16 firstOperand_precedence = UA_DataType_getPrecedence(firstOperand->type); UA_Int16 secondOperand_precedence = UA_DataType_getPrecedence(secondOperand->type); /* if the types are not equal and one of the precedence-ranks is -1, then there is no implicit conversion possible and therefore no compare */ if(!UA_NodeId_equal(&firstOperand->type->typeId, &secondOperand->type->typeId) && (firstOperand_precedence == -1 || secondOperand_precedence == -1)){ return UA_STATUSCODE_BADTYPEMISMATCH; } /* check if the precedence order of the operators is swapped */ UA_Variant *firstCompareOperand = firstOperand; UA_Variant *secondCompareOperand = secondOperand; UA_Boolean swapped = false; if (firstOperand_precedence < secondOperand_precedence){ firstCompareOperand = secondOperand; secondCompareOperand = firstOperand; swapped = true; } UA_SByte castRule = checkTypeCastingOption(firstCompareOperand->type, secondCompareOperand->type); if(!(castRule == 0 || castRule == 1)){ return UA_STATUSCODE_BADTYPEMISMATCH; } /* The operand Data-Types influence the behavior and steps for the comparison. * We need to check the operand types and store a rule which is used to select * the right behavior afterwards. */ enum compareHandlingRuleEnum { UA_TYPES_EQUAL_ORDERED, UA_TYPES_EQUAL_UNORDERED, UA_TYPES_DIFFERENT_NUMERIC_UNSIGNED, UA_TYPES_DIFFERENT_NUMERIC_SIGNED, UA_TYPES_DIFFERENT_NUMERIC_FLOATING_POINT, UA_TYPES_DIFFERENT_TEXT, UA_TYPES_DIFFERENT_COMPARE_FORBIDDEN, UA_TYPES_DIFFERENT_COMPARE_EXPLIC, UA_TYPES_DIFFERENT_SIGNEDNESS_CAST_TO_SIGNED, UA_TYPES_DIFFERENT_SIGNEDNESS_CAST_TO_UNSIGNED } compareHandlingRuleEnum; if(castRule == 0 && (UA_DataType_isNumeric(firstOperand->type) || firstCompareOperand->type->typeKind == (UA_UInt32) UA_DATATYPEKIND_DATETIME || firstCompareOperand->type->typeKind == (UA_UInt32) UA_DATATYPEKIND_STRING || firstCompareOperand->type->typeKind == (UA_UInt32) UA_DATATYPEKIND_BYTESTRING)){ /* Data-Types with a natural order (allow le, gt, lee, gte) */ compareHandlingRuleEnum = UA_TYPES_EQUAL_ORDERED; } else if(castRule == 0){ /* Data-Types without a natural order (le, gt, lee, gte are not allowed) */ compareHandlingRuleEnum = UA_TYPES_EQUAL_UNORDERED; } else if(castRule == 1 && isNumericSigned(firstOperand->type->typeKind) && isNumericSigned(secondOperand->type->typeKind)){ compareHandlingRuleEnum = UA_TYPES_DIFFERENT_NUMERIC_SIGNED; } else if(castRule == 1 && isNumericUnsigned(firstOperand->type->typeKind) && isNumericUnsigned(secondOperand->type->typeKind)){ compareHandlingRuleEnum = UA_TYPES_DIFFERENT_NUMERIC_UNSIGNED; } else if(castRule == 1 && isFloatingPoint(firstOperand->type->typeKind) && isFloatingPoint(secondOperand->type->typeKind)){ compareHandlingRuleEnum = UA_TYPES_DIFFERENT_NUMERIC_FLOATING_POINT; } else if(castRule == 1 && isStringType(firstOperand->type->typeKind)&& isStringType(secondOperand->type->typeKind)){ compareHandlingRuleEnum = UA_TYPES_DIFFERENT_TEXT; } else if(castRule == 1 && isNumericSigned(firstOperand->type->typeKind) && isNumericUnsigned(secondOperand->type->typeKind)){ compareHandlingRuleEnum = UA_TYPES_DIFFERENT_SIGNEDNESS_CAST_TO_SIGNED; } else if(castRule == 1 && isNumericSigned(secondOperand->type->typeKind) && isNumericUnsigned(firstOperand->type->typeKind)){ compareHandlingRuleEnum = UA_TYPES_DIFFERENT_SIGNEDNESS_CAST_TO_UNSIGNED; } else if(castRule == -1 || castRule == 2){ compareHandlingRuleEnum = UA_TYPES_DIFFERENT_COMPARE_EXPLIC; } else { compareHandlingRuleEnum = UA_TYPES_DIFFERENT_COMPARE_FORBIDDEN; } if(compareHandlingRuleEnum == UA_TYPES_DIFFERENT_COMPARE_FORBIDDEN) return UA_STATUSCODE_BADFILTEROPERATORINVALID; if(swapped){ firstCompareOperand = secondCompareOperand; secondCompareOperand = firstCompareOperand; } if(op == UA_FILTEROPERATOR_EQUALS){ UA_Byte variantContent[16]; memset(&variantContent, 0, sizeof(UA_Byte) * 16); if(compareHandlingRuleEnum == UA_TYPES_DIFFERENT_NUMERIC_SIGNED || compareHandlingRuleEnum == UA_TYPES_DIFFERENT_NUMERIC_UNSIGNED || compareHandlingRuleEnum == UA_TYPES_DIFFERENT_NUMERIC_FLOATING_POINT) { implicitNumericVariantTransformation(firstCompareOperand, variantContent); implicitNumericVariantTransformation(secondCompareOperand, &variantContent[8]); } else if(compareHandlingRuleEnum == UA_TYPES_DIFFERENT_SIGNEDNESS_CAST_TO_SIGNED) { implicitNumericVariantTransformation(firstCompareOperand, variantContent); implicitNumericVariantTransformationUnsingedToSigned(secondCompareOperand, &variantContent[8]); } else if(compareHandlingRuleEnum == UA_TYPES_DIFFERENT_SIGNEDNESS_CAST_TO_UNSIGNED) { implicitNumericVariantTransformation(firstCompareOperand, variantContent); implicitNumericVariantTransformationSignedToUnSigned(secondCompareOperand, &variantContent[8]); } else if(compareHandlingRuleEnum == UA_TYPES_DIFFERENT_TEXT) { firstCompareOperand->type = &UA_TYPES[UA_TYPES_STRING]; secondCompareOperand->type = &UA_TYPES[UA_TYPES_STRING]; } else if(compareHandlingRuleEnum == UA_TYPES_DIFFERENT_COMPARE_FORBIDDEN || compareHandlingRuleEnum == UA_TYPES_DIFFERENT_COMPARE_EXPLIC ){ return UA_STATUSCODE_BADFILTEROPERATORINVALID; } if(UA_order(firstCompareOperand, secondCompareOperand, &UA_TYPES[UA_TYPES_VARIANT]) == UA_ORDER_EQ) { return UA_STATUSCODE_GOOD; } } else { UA_Byte variantContent[16]; memset(&variantContent, 0, sizeof(UA_Byte) * 16); if(compareHandlingRuleEnum == UA_TYPES_DIFFERENT_NUMERIC_SIGNED || compareHandlingRuleEnum == UA_TYPES_DIFFERENT_NUMERIC_UNSIGNED || compareHandlingRuleEnum == UA_TYPES_DIFFERENT_NUMERIC_FLOATING_POINT) { implicitNumericVariantTransformation(firstCompareOperand, variantContent); implicitNumericVariantTransformation(secondCompareOperand, &variantContent[8]); } else if(compareHandlingRuleEnum == UA_TYPES_DIFFERENT_SIGNEDNESS_CAST_TO_SIGNED) { implicitNumericVariantTransformation(firstCompareOperand, variantContent); implicitNumericVariantTransformationUnsingedToSigned(secondCompareOperand, &variantContent[8]); } else if(compareHandlingRuleEnum == UA_TYPES_DIFFERENT_SIGNEDNESS_CAST_TO_UNSIGNED) { implicitNumericVariantTransformation(firstCompareOperand, variantContent); implicitNumericVariantTransformationSignedToUnSigned(secondCompareOperand, &variantContent[8]); } else if(compareHandlingRuleEnum == UA_TYPES_DIFFERENT_TEXT) { firstCompareOperand->type = &UA_TYPES[UA_TYPES_STRING]; secondCompareOperand->type = &UA_TYPES[UA_TYPES_STRING]; } else if(compareHandlingRuleEnum == UA_TYPES_EQUAL_UNORDERED) { return UA_STATUSCODE_BADFILTEROPERATORINVALID; } else if(compareHandlingRuleEnum == UA_TYPES_DIFFERENT_COMPARE_FORBIDDEN || compareHandlingRuleEnum == UA_TYPES_DIFFERENT_COMPARE_EXPLIC) { return UA_STATUSCODE_BADFILTEROPERATORINVALID; } UA_Order gte_result = UA_order(firstCompareOperand, secondCompareOperand, &UA_TYPES[UA_TYPES_VARIANT]); if(op == UA_FILTEROPERATOR_LESSTHAN) { if(gte_result == UA_ORDER_LESS) { return UA_STATUSCODE_GOOD; } } else if(op == UA_FILTEROPERATOR_GREATERTHAN) { if(gte_result == UA_ORDER_MORE) { return UA_STATUSCODE_GOOD; } } else if(op == UA_FILTEROPERATOR_LESSTHANOREQUAL) { if(gte_result == UA_ORDER_LESS || gte_result == UA_ORDER_EQ) { return UA_STATUSCODE_GOOD; } } else if(op == UA_FILTEROPERATOR_GREATERTHANOREQUAL) { if(gte_result == UA_ORDER_MORE || gte_result == UA_ORDER_EQ) { return UA_STATUSCODE_GOOD; } } } return UA_STATUSCODE_BADNOMATCH; } static UA_StatusCode compareOperator(UA_FilterOperatorContext *ctx) { ctx->valueResult[ctx->index].type = &UA_TYPES[UA_TYPES_BOOLEAN]; UA_Variant firstOperand = resolveOperand(ctx, 0); if(UA_Variant_isEmpty(&firstOperand)) return UA_STATUSCODE_BADFILTEROPERANDINVALID; UA_Variant secondOperand = resolveOperand(ctx, 1); if(UA_Variant_isEmpty(&secondOperand)) { return UA_STATUSCODE_BADFILTEROPERANDINVALID; } /* ToDo remove the following restriction: Add support for arrays */ if(!UA_Variant_isScalar(&firstOperand) || !UA_Variant_isScalar(&secondOperand)){ return UA_STATUSCODE_BADFILTEROPERATORUNSUPPORTED; } return compareOperation(&firstOperand, &secondOperand, ctx->contentFilter->elements[ctx->index].filterOperator); } static UA_StatusCode bitwiseOperator(UA_FilterOperatorContext *ctx) { /* The bitwise operators all have 2 operands which are evaluated equally. */ UA_Variant firstOperand = resolveOperand(ctx, 0); if(UA_Variant_isEmpty(&firstOperand)) { return UA_STATUSCODE_BADFILTEROPERANDINVALID; } UA_Variant secondOperand = resolveOperand(ctx, 1); if(UA_Variant_isEmpty(&secondOperand)) { return UA_STATUSCODE_BADFILTEROPERANDINVALID; } UA_Boolean bitwiseAnd = ctx->contentFilter->elements[ctx->index].filterOperator == UA_FILTEROPERATOR_BITWISEAND; /* check if the operators are integers */ if(!UA_DataType_isNumeric(firstOperand.type) || !UA_DataType_isNumeric(secondOperand.type) || !UA_Variant_isScalar(&firstOperand) || !UA_Variant_isScalar(&secondOperand) || (firstOperand.type == &UA_TYPES[UA_TYPES_DOUBLE]) || (secondOperand.type == &UA_TYPES[UA_TYPES_DOUBLE]) || (secondOperand.type == &UA_TYPES[UA_TYPES_FLOAT]) || (firstOperand.type == &UA_TYPES[UA_TYPES_FLOAT])) { return UA_STATUSCODE_BADFILTEROPERANDINVALID; } /* check which is the return type (higher precedence == bigger integer)*/ UA_Int16 precedence = UA_DataType_getPrecedence(firstOperand.type); if(precedence > UA_DataType_getPrecedence(secondOperand.type)) { precedence = UA_DataType_getPrecedence(secondOperand.type); } switch(precedence){ case 3: ctx->valueResult[ctx->index].type = &UA_TYPES[UA_TYPES_INT64]; UA_Int64 result_int64; if(bitwiseAnd) { result_int64 = *((UA_Int64 *)firstOperand.data) & *((UA_Int64 *)secondOperand.data); } else { result_int64 = *((UA_Int64 *)firstOperand.data) | *((UA_Int64 *)secondOperand.data); } UA_Int64_copy(&result_int64, (UA_Int64 *) ctx->valueResult[ctx->index].data); break; case 4: ctx->valueResult[ctx->index].type = &UA_TYPES[UA_TYPES_UINT64]; UA_UInt64 result_uint64s; if(bitwiseAnd) { result_uint64s = *((UA_UInt64 *)firstOperand.data) & *((UA_UInt64 *)secondOperand.data); } else { result_uint64s = *((UA_UInt64 *)firstOperand.data) | *((UA_UInt64 *)secondOperand.data); } UA_UInt64_copy(&result_uint64s, (UA_UInt64 *) ctx->valueResult[ctx->index].data); break; case 5: ctx->valueResult[ctx->index].type = &UA_TYPES[UA_TYPES_INT32]; UA_Int32 result_int32; if(bitwiseAnd) { result_int32 = *((UA_Int32 *)firstOperand.data) & *((UA_Int32 *)secondOperand.data); } else { result_int32 = *((UA_Int32 *)firstOperand.data) | *((UA_Int32 *)secondOperand.data); } UA_Int32_copy(&result_int32, (UA_Int32 *) ctx->valueResult[ctx->index].data); break; case 6: ctx->valueResult[ctx->index].type = &UA_TYPES[UA_TYPES_UINT32]; UA_UInt32 result_uint32; if(bitwiseAnd) { result_uint32 = *((UA_UInt32 *)firstOperand.data) & *((UA_UInt32 *)secondOperand.data); } else { result_uint32 = *((UA_UInt32 *)firstOperand.data) | *((UA_UInt32 *)secondOperand.data); } UA_UInt32_copy(&result_uint32, (UA_UInt32 *) ctx->valueResult[ctx->index].data); break; case 8: ctx->valueResult[ctx->index].type = &UA_TYPES[UA_TYPES_INT16]; UA_Int16 result_int16; if(bitwiseAnd) { result_int16 = *((UA_Int16 *)firstOperand.data) & *((UA_Int16 *)secondOperand.data); } else { result_int16 = *((UA_Int16 *)firstOperand.data) | *((UA_Int16 *)secondOperand.data); } UA_Int16_copy(&result_int16, (UA_Int16 *) ctx->valueResult[ctx->index].data); break; case 9: ctx->valueResult[ctx->index].type = &UA_TYPES[UA_TYPES_UINT16]; UA_UInt16 result_uint16; if(bitwiseAnd) { result_uint16 = *((UA_UInt16 *)firstOperand.data) & *((UA_UInt16 *)secondOperand.data); } else { result_uint16 = *((UA_UInt16 *)firstOperand.data) | *((UA_UInt16 *)secondOperand.data); } UA_UInt16_copy(&result_uint16, (UA_UInt16 *) ctx->valueResult[ctx->index].data); break; case 10: ctx->valueResult[ctx->index].type = &UA_TYPES[UA_TYPES_SBYTE]; UA_SByte result_sbyte; if(bitwiseAnd) { result_sbyte = *((UA_SByte *)firstOperand.data) & *((UA_SByte *)secondOperand.data); } else { result_sbyte = *((UA_SByte *)firstOperand.data) | *((UA_SByte *)secondOperand.data); } UA_SByte_copy(&result_sbyte, (UA_SByte *) ctx->valueResult[ctx->index].data); break; case 11: ctx->valueResult[ctx->index].type = &UA_TYPES[UA_TYPES_BYTE]; UA_Byte result_byte; if(bitwiseAnd) { result_byte = *((UA_Byte *)firstOperand.data) & *((UA_Byte *)secondOperand.data); } else { result_byte = *((UA_Byte *)firstOperand.data) | *((UA_Byte *)secondOperand.data); } UA_Byte_copy(&result_byte, (UA_Byte *) ctx->valueResult[ctx->index].data); break; default: return UA_STATUSCODE_BADFILTEROPERANDINVALID; } return UA_STATUSCODE_GOOD; } static UA_StatusCode betweenOperator(UA_FilterOperatorContext *ctx) { ctx->valueResult[ctx->index].type = &UA_TYPES[UA_TYPES_BOOLEAN]; UA_Variant firstOperand = resolveOperand(ctx, 0); UA_Variant secondOperand = resolveOperand(ctx, 1); UA_Variant thirdOperand = resolveOperand(ctx, 2); if((UA_Variant_isEmpty(&firstOperand) || UA_Variant_isEmpty(&secondOperand) || UA_Variant_isEmpty(&thirdOperand)) || (!UA_DataType_isNumeric(firstOperand.type) || !UA_DataType_isNumeric(secondOperand.type) || !UA_DataType_isNumeric(thirdOperand.type)) || (!UA_Variant_isScalar(&firstOperand) || !UA_Variant_isScalar(&secondOperand) || !UA_Variant_isScalar(&thirdOperand))) { return UA_STATUSCODE_BADFILTEROPERANDINVALID; } /* Between can be evaluated through greaterThanOrEqual and lessThanOrEqual */ if(compareOperation(&firstOperand, &secondOperand, UA_FILTEROPERATOR_GREATERTHANOREQUAL) == UA_STATUSCODE_GOOD && compareOperation(&firstOperand, &thirdOperand, UA_FILTEROPERATOR_LESSTHANOREQUAL) == UA_STATUSCODE_GOOD){ return UA_STATUSCODE_GOOD; } return UA_STATUSCODE_BADNOMATCH; } static UA_StatusCode inListOperator(UA_FilterOperatorContext *ctx) { ctx->valueResult[ctx->index].type = &UA_TYPES[UA_TYPES_BOOLEAN]; UA_Variant firstOperand = resolveOperand(ctx, 0); if(UA_Variant_isEmpty(&firstOperand) || !UA_Variant_isScalar(&firstOperand)) { return UA_STATUSCODE_BADFILTEROPERATORUNSUPPORTED; } /* Evaluating the list of operands */ for(size_t i = 1; i < ctx->contentFilter->elements[ctx->index].filterOperandsSize; i++) { /* Resolving the current operand */ UA_Variant currentOperator = resolveOperand(ctx, (UA_UInt16)i); /* Check if the operand conforms to the operator*/ if(UA_Variant_isEmpty(¤tOperator) || !UA_Variant_isScalar(¤tOperator)) { return UA_STATUSCODE_BADFILTEROPERATORUNSUPPORTED; } if(compareOperation(&firstOperand, ¤tOperator, UA_FILTEROPERATOR_EQUALS)) { return UA_STATUSCODE_GOOD; } } return UA_STATUSCODE_BADNOMATCH; } static UA_StatusCode isNullOperator(UA_FilterOperatorContext *ctx) { /* Checking if operand is NULL. This is done by reducing the operand to a * variant and then checking if it is empty. */ UA_Variant operand = resolveOperand(ctx, 0); ctx->valueResult[ctx->index].type = &UA_TYPES[UA_TYPES_BOOLEAN]; if(!UA_Variant_isEmpty(&operand)) return UA_STATUSCODE_BADNOMATCH; return UA_STATUSCODE_GOOD; } static UA_StatusCode notOperator(UA_FilterOperatorContext *ctx) { /* Inverting the boolean value of the operand. */ UA_StatusCode res = resolveBoolean(resolveOperand(ctx, 0)); ctx->valueResult[ctx->index].type = &UA_TYPES[UA_TYPES_BOOLEAN]; /* invert result */ if(res == UA_STATUSCODE_GOOD) return UA_STATUSCODE_BADNOMATCH; return UA_STATUSCODE_GOOD; } static UA_StatusCode evaluateWhereClauseContentFilter(UA_FilterOperatorContext *ctx) { UA_LOCK_ASSERT(&ctx->server->serviceMutex, 1); if(ctx->contentFilter->elements == NULL || ctx->contentFilter->elementsSize == 0) { /* Nothing to do.*/ return UA_STATUSCODE_GOOD; } /* The first element needs to be evaluated, this might be linked to other * elements, which are evaluated in these cases. See 7.4.1 in Part 4. */ UA_ContentFilterElement *pElement = &ctx->contentFilter->elements[ctx->index]; UA_StatusCode *result = &ctx->contentFilterResult->elementResults[ctx->index].statusCode; switch(pElement->filterOperator) { case UA_FILTEROPERATOR_INVIEW: /* Fallthrough */ case UA_FILTEROPERATOR_RELATEDTO: /* Not allowed for event WhereClause according to 7.17.3 in Part 4 */ return UA_STATUSCODE_BADEVENTFILTERINVALID; case UA_FILTEROPERATOR_EQUALS: /* Fallthrough */ case UA_FILTEROPERATOR_GREATERTHAN: /* Fallthrough */ case UA_FILTEROPERATOR_LESSTHAN: /* Fallthrough */ case UA_FILTEROPERATOR_GREATERTHANOREQUAL: /* Fallthrough */ case UA_FILTEROPERATOR_LESSTHANOREQUAL: *result = compareOperator(ctx); break; case UA_FILTEROPERATOR_LIKE: return UA_STATUSCODE_BADFILTEROPERATORUNSUPPORTED; case UA_FILTEROPERATOR_NOT: *result = notOperator(ctx); break; case UA_FILTEROPERATOR_BETWEEN: *result = betweenOperator(ctx); break; case UA_FILTEROPERATOR_INLIST: /* ToDo currently only numeric types are allowed */ *result = inListOperator(ctx); break; case UA_FILTEROPERATOR_ISNULL: *result = isNullOperator(ctx); break; case UA_FILTEROPERATOR_AND: *result = andOperator(ctx); break; case UA_FILTEROPERATOR_OR: *result = orOperator(ctx); break; case UA_FILTEROPERATOR_CAST: return UA_STATUSCODE_BADFILTEROPERATORUNSUPPORTED; case UA_FILTEROPERATOR_BITWISEAND: *result = bitwiseOperator(ctx); break; case UA_FILTEROPERATOR_BITWISEOR: *result = bitwiseOperator(ctx); break; case UA_FILTEROPERATOR_OFTYPE: *result = ofTypeOperator(ctx); break; default: return UA_STATUSCODE_BADFILTEROPERATORINVALID; } if(ctx->valueResult[ctx->index].type == &UA_TYPES[UA_TYPES_BOOLEAN]) { UA_Boolean *res = UA_Boolean_new(); if(ctx->contentFilterResult->elementResults[ctx->index].statusCode == UA_STATUSCODE_GOOD) *res = true; else *res = false; ctx->valueResult[ctx->index].data = res; } return ctx->contentFilterResult->elementResults[ctx->index].statusCode; } /* Exposes the filters For unit tests */ UA_StatusCode UA_Server_evaluateWhereClauseContentFilter(UA_Server *server, UA_Session *session, const UA_NodeId *eventNode, const UA_ContentFilter *contentFilter, UA_ContentFilterResult *contentFilterResult) { if(contentFilter->elementsSize == 0) return UA_STATUSCODE_GOOD; /* TODO add maximum lenth size to the server config */ if(contentFilter->elementsSize > 256) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_Variant valueResult[256]; for(size_t i = 0; i < contentFilter->elementsSize; ++i) { UA_Variant_init(&valueResult[i]); } UA_FilterOperatorContext ctx; ctx.server = server; ctx.session = session; ctx.eventNode = eventNode; ctx.contentFilter = contentFilter; ctx.contentFilterResult = contentFilterResult; ctx.valueResult = valueResult; ctx.index = 0; UA_StatusCode res = evaluateWhereClauseContentFilter(&ctx); for(size_t i = 0; i < ctx.contentFilter->elementsSize; i++) { if(!UA_Variant_isEmpty(&ctx.valueResult[i])) UA_Variant_clear(&ctx.valueResult[i]); } return res; } static UA_Boolean isValidEvent(UA_Server *server, const UA_NodeId *validEventParent, const UA_NodeId *eventId) { /* find the eventType variableNode */ UA_QualifiedName findName = UA_QUALIFIEDNAME(0, "EventType"); UA_BrowsePathResult bpr = browseSimplifiedBrowsePath(server, *eventId, 1, &findName); if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) { UA_BrowsePathResult_clear(&bpr); return false; } /* Get the EventType Property Node */ UA_Variant tOutVariant; UA_Variant_init(&tOutVariant); /* Read the Value of EventType Property Node (the Value should be a NodeId) */ UA_StatusCode retval = readWithReadValue(server, &bpr.targets[0].targetId.nodeId, UA_ATTRIBUTEID_VALUE, &tOutVariant); if(retval != UA_STATUSCODE_GOOD || !UA_Variant_hasScalarType(&tOutVariant, &UA_TYPES[UA_TYPES_NODEID])) { UA_BrowsePathResult_clear(&bpr); return false; } const UA_NodeId *tEventType = (UA_NodeId*)tOutVariant.data; /* check whether the EventType is a Subtype of CondtionType * (Part 9 first implementation) */ UA_NodeId conditionTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_CONDITIONTYPE); if(UA_NodeId_equal(validEventParent, &conditionTypeId) && isNodeInTree_singleRef(server, tEventType, &conditionTypeId, UA_REFERENCETYPEINDEX_HASSUBTYPE)) { UA_BrowsePathResult_clear(&bpr); UA_Variant_clear(&tOutVariant); return true; } /*EventType is not a Subtype of CondtionType *(ConditionId Clause won't be present in Events, which are not Conditions)*/ /* check whether Valid Event other than Conditions */ UA_NodeId baseEventTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE); UA_Boolean isSubtypeOfBaseEvent = isNodeInTree_singleRef(server, tEventType, &baseEventTypeId, UA_REFERENCETYPEINDEX_HASSUBTYPE); UA_BrowsePathResult_clear(&bpr); UA_Variant_clear(&tOutVariant); return isSubtypeOfBaseEvent; } UA_StatusCode filterEvent(UA_Server *server, UA_Session *session, const UA_NodeId *eventNode, UA_EventFilter *filter, UA_EventFieldList *efl, UA_EventFilterResult *result) { if(filter->selectClausesSize == 0) return UA_STATUSCODE_BADEVENTFILTERINVALID; UA_EventFieldList_init(efl); efl->eventFields = (UA_Variant *) UA_Array_new(filter->selectClausesSize, &UA_TYPES[UA_TYPES_VARIANT]); if(!efl->eventFields) return UA_STATUSCODE_BADOUTOFMEMORY; efl->eventFieldsSize = filter->selectClausesSize; /* empty event filter result */ UA_EventFilterResult_init(result); result->selectClauseResultsSize = filter->selectClausesSize; result->selectClauseResults = (UA_StatusCode *) UA_Array_new(filter->selectClausesSize, &UA_TYPES[UA_TYPES_STATUSCODE]); if(!result->selectClauseResults) { UA_EventFieldList_clear(efl); UA_EventFilterResult_clear(result); return UA_STATUSCODE_BADOUTOFMEMORY; } /* prepare content filter result structure */ if(filter->whereClause.elementsSize != 0) { result->whereClauseResult.elementResultsSize = filter->whereClause.elementsSize; result->whereClauseResult.elementResults = (UA_ContentFilterElementResult *) UA_Array_new(filter->whereClause.elementsSize, &UA_TYPES[UA_TYPES_CONTENTFILTERELEMENTRESULT]); if(!result->whereClauseResult.elementResults) { UA_EventFieldList_clear(efl); UA_EventFilterResult_clear(result); return UA_STATUSCODE_BADOUTOFMEMORY; } for(size_t i = 0; i < result->whereClauseResult.elementResultsSize; ++i) { result->whereClauseResult.elementResults[i].operandStatusCodesSize = filter->whereClause.elements->filterOperandsSize; result->whereClauseResult.elementResults[i].operandStatusCodes = (UA_StatusCode *)UA_Array_new( filter->whereClause.elements->filterOperandsSize, &UA_TYPES[UA_TYPES_STATUSCODE]); if(!result->whereClauseResult.elementResults[i].operandStatusCodes) { UA_EventFieldList_clear(efl); UA_EventFilterResult_clear(result); return UA_STATUSCODE_BADOUTOFMEMORY; } } } /* Apply the content (where) filter */ UA_StatusCode res = UA_Server_evaluateWhereClauseContentFilter(server, session, eventNode, &filter->whereClause, &result->whereClauseResult); if(res != UA_STATUSCODE_GOOD){ UA_EventFieldList_clear(efl); UA_EventFilterResult_clear(result); return res; } /* Apply the select filter */ /* Check if the browsePath is BaseEventType, in which case nothing more * needs to be checked */ UA_NodeId baseEventTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE); for(size_t i = 0; i < filter->selectClausesSize; i++) { if(!UA_NodeId_equal(&filter->selectClauses[i].typeDefinitionId, &baseEventTypeId) && !isValidEvent(server, &filter->selectClauses[i].typeDefinitionId, eventNode)) { UA_Variant_init(&efl->eventFields[i]); /* EventFilterResult currently isn't being used notification->result.selectClauseResults[i] = UA_STATUSCODE_BADTYPEDEFINITIONINVALID; */ continue; } /* TODO: Put the result into the selectClausResults */ resolveSimpleAttributeOperand(server, session, eventNode, &filter->selectClauses[i], &efl->eventFields[i]); } return UA_STATUSCODE_GOOD; } /*****************************************/ /* Validation of Filters during Creation */ /*****************************************/ /* Initial select clause validation. The following checks are currently performed: * - Check if typedefenitionid or browsepath of any clause is NULL * - Check if the eventType is a subtype of BaseEventType * - Check if attributeId is valid * - Check if browsePath contains null * - Check if indexRange is defined and if it is parsable * - Check if attributeId is value */ void UA_Event_staticSelectClauseValidation(UA_Server *server, const UA_EventFilter *eventFilter, UA_StatusCode *result) { /* The selectClause only has to be checked, if the size is not zero */ if(eventFilter->selectClausesSize == 0) return; for(size_t i = 0; i < eventFilter->selectClausesSize; ++i) { result[i] = UA_STATUSCODE_GOOD; /* /typedefenitionid or browsepath of any clause is not NULL ? */ if(UA_NodeId_isNull(&eventFilter->selectClauses[i].typeDefinitionId)) { result[i] = UA_STATUSCODE_BADTYPEDEFINITIONINVALID; continue; } /*ToDo: Check the following workaround. In UaExpert Event View the selection * of the Server Object set up 7 select filter entries by default. The last * element ist from node 2782 (A&C ConditionType). Since the reduced * information model dos not contain this type, the result has a brows path of * "null" which results in an error. */ UA_NodeId ac_conditionType = UA_NODEID_NUMERIC(0, UA_NS0ID_CONDITIONTYPE); if(UA_NodeId_equal(&eventFilter->selectClauses[i].typeDefinitionId, &ac_conditionType)) { continue; } if(&eventFilter->selectClauses[i].browsePath[0] == NULL) { result[i] = UA_STATUSCODE_BADBROWSENAMEINVALID; continue; } /* eventType is a subtype of BaseEventType ? */ UA_NodeId baseEventTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE); if(!isNodeInTree_singleRef( server, &eventFilter->selectClauses[i].typeDefinitionId, &baseEventTypeId, UA_REFERENCETYPEINDEX_HASSUBTYPE)) { result[i] = UA_STATUSCODE_BADTYPEDEFINITIONINVALID; continue; } /* attributeId is valid ? */ if(!((0 < eventFilter->selectClauses[i].attributeId) && (eventFilter->selectClauses[i].attributeId < 28))) { result[i] = UA_STATUSCODE_BADATTRIBUTEIDINVALID; continue; } /* browsePath contains null ? */ for(size_t j = 0; j < eventFilter->selectClauses[i].browsePathSize; ++j) { if(UA_QualifiedName_isNull( &eventFilter->selectClauses[i].browsePath[j])) { result[i] = UA_STATUSCODE_BADBROWSENAMEINVALID; break; } } /* Get the list of Subtypes from current node */ UA_ReferenceTypeSet reftypes_interface = UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASSUBTYPE); UA_ExpandedNodeId *chilTypeNodes = NULL; size_t chilTypeNodesSize = 0; UA_StatusCode res; res = browseRecursive(server, 1, &eventFilter->selectClauses[i].typeDefinitionId, UA_BROWSEDIRECTION_FORWARD, &reftypes_interface, UA_NODECLASS_OBJECTTYPE, true, &chilTypeNodesSize, &chilTypeNodes); if(res!=UA_STATUSCODE_GOOD){ result[i] = UA_STATUSCODE_BADATTRIBUTEIDINVALID; continue; } UA_Boolean subTypeContainField = false; for (size_t j = 0; j < chilTypeNodesSize; ++j) { /* browsPath element is defined in path */ UA_BrowsePathResult bpr = browseSimplifiedBrowsePath(server, chilTypeNodes[j].nodeId, eventFilter->selectClauses[i].browsePathSize, eventFilter->selectClauses[i].browsePath); if(bpr.statusCode != UA_STATUSCODE_GOOD){ UA_BrowsePathResult_clear(&bpr); continue; } subTypeContainField = true; UA_BrowsePathResult_clear(&bpr); } if(!subTypeContainField) result[i] = UA_STATUSCODE_BADNODEIDUNKNOWN; UA_Array_delete(chilTypeNodes, chilTypeNodesSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); if(result[i] != UA_STATUSCODE_GOOD) continue; /*indexRange is defined ? */ if(!UA_String_equal(&eventFilter->selectClauses[i].indexRange, &UA_STRING_NULL)) { /* indexRange is parsable ? */ UA_NumericRange numericRange = UA_NUMERICRANGE(""); if(UA_NumericRange_parse(&numericRange, eventFilter->selectClauses[i].indexRange) != UA_STATUSCODE_GOOD) { result[i] = UA_STATUSCODE_BADINDEXRANGEINVALID; continue; } UA_free(numericRange.dimensions); /* attributeId is value ? */ if(eventFilter->selectClauses[i].attributeId != UA_ATTRIBUTEID_VALUE) { result[i] = UA_STATUSCODE_BADTYPEMISMATCH; continue; } } } } /* Initial content filter (where clause) check. Current checks: * - Number of operands for each (supported) operator */ UA_StatusCode UA_Event_staticWhereClauseValidation(UA_Server *server, const UA_ContentFilter *filter, UA_ContentFilterResult *result) { UA_ContentFilterResult_init(result); result->elementResultsSize = filter->elementsSize; if(result->elementResultsSize == 0) return UA_STATUSCODE_GOOD; result->elementResults = (UA_ContentFilterElementResult *)UA_Array_new( result->elementResultsSize, &UA_TYPES[UA_TYPES_CONTENTFILTERELEMENTRESULT]); if(!result->elementResults) return UA_STATUSCODE_BADOUTOFMEMORY; for(size_t i = 0; i < result->elementResultsSize; ++i) { UA_ContentFilterElementResult *er = &result->elementResults[i]; UA_ContentFilterElement ef = filter->elements[i]; UA_ContentFilterElementResult_init(er); er->operandStatusCodes = (UA_StatusCode *)UA_Array_new( ef.filterOperandsSize, &UA_TYPES[UA_TYPES_STATUSCODE]); er->operandStatusCodesSize = ef.filterOperandsSize; switch(ef.filterOperator) { case UA_FILTEROPERATOR_INVIEW: case UA_FILTEROPERATOR_RELATEDTO: { /* Not allowed for event WhereClause according to 7.17.3 in Part 4 */ er->statusCode = UA_STATUSCODE_BADEVENTFILTERINVALID; break; } case UA_FILTEROPERATOR_EQUALS: case UA_FILTEROPERATOR_GREATERTHAN: case UA_FILTEROPERATOR_LESSTHAN: case UA_FILTEROPERATOR_GREATERTHANOREQUAL: case UA_FILTEROPERATOR_LESSTHANOREQUAL: case UA_FILTEROPERATOR_LIKE: case UA_FILTEROPERATOR_CAST: case UA_FILTEROPERATOR_BITWISEAND: case UA_FILTEROPERATOR_BITWISEOR: { if(ef.filterOperandsSize != 2) { er->statusCode = UA_STATUSCODE_BADFILTEROPERANDCOUNTMISMATCH; break; } er->statusCode = UA_STATUSCODE_GOOD; break; } case UA_FILTEROPERATOR_AND: case UA_FILTEROPERATOR_OR: { if(ef.filterOperandsSize != 2) { er->statusCode = UA_STATUSCODE_BADFILTEROPERANDCOUNTMISMATCH; break; } for(size_t j = 0; j < 2; ++j) { if(ef.filterOperands[j].content.decoded.type != &UA_TYPES[UA_TYPES_ELEMENTOPERAND]) { er->operandStatusCodes[j] = UA_STATUSCODE_BADFILTEROPERANDINVALID; er->statusCode = UA_STATUSCODE_BADFILTEROPERANDINVALID; break; } if(((UA_ElementOperand *)ef.filterOperands[j] .content.decoded.data)->index > filter->elementsSize - 1) { er->operandStatusCodes[j] = UA_STATUSCODE_BADINDEXRANGEINVALID; er->statusCode = UA_STATUSCODE_BADINDEXRANGEINVALID; break; } } er->statusCode = UA_STATUSCODE_GOOD; break; } case UA_FILTEROPERATOR_ISNULL: case UA_FILTEROPERATOR_NOT: { if(ef.filterOperandsSize != 1) { er->statusCode = UA_STATUSCODE_BADFILTEROPERANDCOUNTMISMATCH; break; } er->statusCode = UA_STATUSCODE_GOOD; break; } case UA_FILTEROPERATOR_INLIST: { if(ef.filterOperandsSize <= 2) { er->statusCode = UA_STATUSCODE_BADFILTEROPERANDCOUNTMISMATCH; break; } er->statusCode = UA_STATUSCODE_GOOD; break; } case UA_FILTEROPERATOR_BETWEEN: { if(ef.filterOperandsSize != 3) { er->statusCode = UA_STATUSCODE_BADFILTEROPERANDCOUNTMISMATCH; break; } er->statusCode = UA_STATUSCODE_GOOD; break; } case UA_FILTEROPERATOR_OFTYPE: { if(ef.filterOperandsSize != 1) { er->statusCode = UA_STATUSCODE_BADFILTEROPERANDCOUNTMISMATCH; break; } er->operandStatusCodesSize = ef.filterOperandsSize; if(ef.filterOperands[0].content.decoded.type != &UA_TYPES[UA_TYPES_LITERALOPERAND]) { er->statusCode = UA_STATUSCODE_BADFILTEROPERANDINVALID; break; } const UA_LiteralOperand *lit = (const UA_LiteralOperand *) ef.filterOperands[0].content.decoded.data; if(!UA_Variant_hasScalarType(&lit->value, &UA_TYPES[UA_TYPES_NODEID])) { er->statusCode = UA_STATUSCODE_BADFILTEROPERANDINVALID; break; } const UA_NodeId *ofTypeId = (const UA_NodeId*)lit->value.data; /* Make sure the &pOperand->nodeId is a subtype of BaseEventType */ UA_NodeId baseEventTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE); if(!isNodeInTree_singleRef(server, ofTypeId, &baseEventTypeId, UA_REFERENCETYPEINDEX_HASSUBTYPE)) { er->statusCode = UA_STATUSCODE_BADNODEIDINVALID; break; } er->statusCode = UA_STATUSCODE_GOOD; break; } default: er->statusCode = UA_STATUSCODE_BADFILTEROPERATORUNSUPPORTED; break; } } return UA_STATUSCODE_GOOD; } #endif /* UA_ENABLE_SUBSCRIPTIONS_EVENTS */ /**** amalgamated original file "/plugins/crypto/openssl/securitypolicy_openssl_common.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 2020 (c) Wind River Systems, Inc. * Copyright 2020 (c) basysKom GmbH * */ #if defined(UA_ENABLE_ENCRYPTION_OPENSSL) || defined(UA_ENABLE_ENCRYPTION_LIBRESSL) #include #include _UA_BEGIN_DECLS void saveDataToFile(const char *fileName, const UA_ByteString *str); void UA_Openssl_Init(void); UA_StatusCode UA_copyCertificate(UA_ByteString *dst, const UA_ByteString *src); UA_StatusCode UA_OpenSSL_RSA_PKCS1_V15_SHA256_Verify(const UA_ByteString *msg, X509 *publicKeyX509, const UA_ByteString *signature); UA_StatusCode UA_Openssl_X509_GetCertificateThumbprint(const UA_ByteString *certficate, UA_ByteString *pThumbprint, bool bThumbPrint); UA_StatusCode UA_Openssl_RSA_Oaep_Decrypt(UA_ByteString *data, EVP_PKEY *privateKey); UA_StatusCode UA_Openssl_RSA_OAEP_Encrypt(UA_ByteString *data, /* The data that is encrypted. The encrypted data will overwrite the data that was supplied. */ size_t paddingSize, X509 *publicX509); UA_StatusCode UA_Openssl_Random_Key_PSHA256_Derive(const UA_ByteString *secret, const UA_ByteString *seed, UA_ByteString *out); UA_StatusCode UA_Openssl_RSA_Public_GetKeyLength(X509 *publicKeyX509, UA_Int32 *keyLen); UA_StatusCode UA_Openssl_RSA_PKCS1_V15_SHA256_Sign(const UA_ByteString *data, EVP_PKEY *privateKey, UA_ByteString *outSignature); UA_StatusCode UA_OpenSSL_HMAC_SHA256_Verify(const UA_ByteString *message, const UA_ByteString *key, const UA_ByteString *signature); UA_StatusCode UA_OpenSSL_HMAC_SHA256_Sign(const UA_ByteString *message, const UA_ByteString *key, UA_ByteString *signature); UA_StatusCode UA_OpenSSL_AES_256_CBC_Decrypt(const UA_ByteString *iv, const UA_ByteString *key, UA_ByteString *data /* [in/out]*/); UA_StatusCode UA_OpenSSL_AES_256_CBC_Encrypt(const UA_ByteString *iv, const UA_ByteString *key, UA_ByteString *data /* [in/out]*/); UA_StatusCode UA_OpenSSL_X509_compare(const UA_ByteString *cert, const X509 *b); UA_StatusCode UA_Openssl_RSA_Private_GetKeyLength(EVP_PKEY *privateKey, UA_Int32 *keyLen) ; UA_StatusCode UA_OpenSSL_RSA_PKCS1_V15_SHA1_Verify(const UA_ByteString *msg, X509 *publicKeyX509, const UA_ByteString *signature); UA_StatusCode UA_Openssl_RSA_PKCS1_V15_SHA1_Sign(const UA_ByteString *message, EVP_PKEY *privateKey, UA_ByteString *outSignature); UA_StatusCode UA_Openssl_Random_Key_PSHA1_Derive(const UA_ByteString *secret, const UA_ByteString *seed, UA_ByteString *out); UA_StatusCode UA_OpenSSL_HMAC_SHA1_Verify(const UA_ByteString *message, const UA_ByteString *key, const UA_ByteString *signature); UA_StatusCode UA_OpenSSL_HMAC_SHA1_Sign(const UA_ByteString *message, const UA_ByteString *key, UA_ByteString *signature); UA_StatusCode UA_Openssl_RSA_PKCS1_V15_Decrypt(UA_ByteString *data, EVP_PKEY *privateKey); UA_StatusCode UA_Openssl_RSA_PKCS1_V15_Encrypt(UA_ByteString *data, size_t paddingSize, X509 *publicX509); UA_StatusCode UA_OpenSSL_AES_128_CBC_Decrypt(const UA_ByteString *iv, const UA_ByteString *key, UA_ByteString *data /* [in/out]*/); UA_StatusCode UA_OpenSSL_AES_128_CBC_Encrypt(const UA_ByteString *iv, const UA_ByteString *key, UA_ByteString *data /* [in/out]*/); EVP_PKEY * UA_OpenSSL_LoadPrivateKey(const UA_ByteString *privateKey); X509 * UA_OpenSSL_LoadCertificate(const UA_ByteString *certificate); X509 * UA_OpenSSL_LoadDerCertificate(const UA_ByteString *certificate); X509 * UA_OpenSSL_LoadPemCertificate(const UA_ByteString *certificate); UA_StatusCode UA_OpenSSL_LoadLocalCertificate(const UA_ByteString *certificate, UA_ByteString *target); _UA_END_DECLS #endif /* defined(UA_ENABLE_ENCRYPTION_OPENSSL) || defined(UA_ENABLE_ENCRYPTION_LIBRESSL) */ /**** amalgamated original file "/plugins/crypto/openssl/ua_openssl_version_abstraction.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 2021 (c) Christian von Arnim, ISW University of Stuttgart (for VDW and umati) * */ #if defined(UA_ENABLE_ENCRYPTION_OPENSSL) || defined(UA_ENABLE_ENCRYPTION_LIBRESSL) #include #if !defined(OPENSSL_VERSION_NUMBER) #error "OPENSSL_VERSION_NUMBER is not defined." #endif #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) #define X509_STORE_CTX_set0_trusted_stack(STORE_CTX, CTX_SKTRUSTED) X509_STORE_CTX_trusted_stack(STORE_CTX, CTX_SKTRUSTED) #endif #if OPENSSL_VERSION_NUMBER < 0x1010000fL || defined(LIBRESSL_VERSION_NUMBER) #define X509_STORE_CTX_get_check_issued(STORE_CTX) STORE_CTX->check_issued #endif #if OPENSSL_VERSION_NUMBER < 0x1010000fL || defined(LIBRESSL_VERSION_NUMBER) #define get_pkey_rsa(evp) ((evp)->pkey.rsa) #else #define get_pkey_rsa(evp) EVP_PKEY_get0_RSA(evp) #endif #if OPENSSL_VERSION_NUMBER < 0x1010000fL || defined(LIBRESSL_VERSION_NUMBER) #define X509_get0_subject_key_id(PX509_CERT) (const ASN1_OCTET_STRING *)X509_get_ext_d2i(PX509_CERT, NID_subject_key_identifier, NULL, NULL); #endif #endif /* defined(UA_ENABLE_ENCRYPTION_OPENSSL) || defined(UA_ENABLE_ENCRYPTION_LIBRESSL) */ /**** amalgamated original file "/plugins/crypto/openssl/securitypolicy_openssl_common.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 2020 (c) Wind River Systems, Inc. * Copyright 2020 (c) basysKom GmbH * Copyright 2022 (c) Wind River Systems, Inc. */ /* modification history -------------------- 01feb20,lan written */ #if defined(UA_ENABLE_ENCRYPTION_OPENSSL) || defined(UA_ENABLE_ENCRYPTION_LIBRESSL) #include #include #include #include #include #include #include #include #define SHA1_DIGEST_LENGTH 20 /* 160 bits */ #define RSA_DECRYPT_BUFFER_LENGTH 2048 /* bytes */ /** P_SHA256 Context */ typedef struct UA_Openssl_P_SHA256_Ctx_ { size_t seedLen; size_t secretLen; UA_Byte A[32]; /* 32 bytes of SHA256 output */ /* char seed[seedLen]; char secret[secretLen]; */ } UA_Openssl_P_SHA256_Ctx; #define UA_Openssl_P_SHA256_SEED(ctx) ((ctx)->A+32) #define UA_Openssl_P_SHA256_SECRET(ctx) ((ctx)->A+32+(ctx)->seedLen) /** P_SHA1 Context */ typedef struct UA_Openssl_P_SHA1_Ctx_ { size_t seedLen; size_t secretLen; UA_Byte A[SHA1_DIGEST_LENGTH]; /* 20 bytes of SHA1 output */ /* char seed[seedLen]; char secret[secretLen]; */ } UA_Openssl_P_SHA1_Ctx; #define UA_Openssl_P_SHA1_SEED(ctx) ((ctx)->A + SHA1_DIGEST_LENGTH) #define UA_Openssl_P_SHA1_SECRET(ctx) ((ctx)->A + SHA1_DIGEST_LENGTH +(ctx)->seedLen) void UA_Openssl_Init (void) { /* VxWorks7 has initialized the openssl. */ #ifndef __VXWORKS__ static UA_Int16 bInit = 0; if (bInit == 1) return; #if defined(OPENSSL_API_COMPAT) && (OPENSSL_API_COMPAT < 0x10100000L) /* only needed, if OpenSSL < V1.1 */ OpenSSL_add_all_algorithms (); ERR_load_crypto_strings (); #endif bInit = 1; #endif } static int UA_OpenSSL_RSA_Key_Size (EVP_PKEY * key){ #if (OPENSSL_VERSION_NUMBER >= 0x30000000L) return EVP_PKEY_get_size (key); #else return RSA_size (get_pkey_rsa(key)); #endif } /* UA_copyCertificate - allocalte the buffer, copy the certificate and * add a NULL to the end */ UA_StatusCode UA_copyCertificate (UA_ByteString * dst, const UA_ByteString * src) { UA_StatusCode retval = UA_ByteString_allocBuffer (dst, src->length + 1); if (retval != UA_STATUSCODE_GOOD) return retval; (void) memcpy (dst->data, src->data, src->length); dst->data[dst->length - 1] = '\0'; dst->length--; return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_OpenSSL_RSA_Public_Verify (const UA_ByteString * message, const EVP_MD * evpMd, X509 * publicKeyX509, UA_Int16 padding, const UA_ByteString * signature ) { EVP_MD_CTX * mdctx = NULL; int opensslRet; EVP_PKEY_CTX * evpKeyCtx; EVP_PKEY * evpPublicKey = NULL; UA_StatusCode ret; mdctx = EVP_MD_CTX_create (); if (mdctx == NULL) { ret = UA_STATUSCODE_BADOUTOFMEMORY; goto errout; } evpPublicKey = X509_get_pubkey (publicKeyX509); if (evpPublicKey == NULL) { ret = UA_STATUSCODE_BADOUTOFMEMORY; goto errout; } opensslRet = EVP_DigestVerifyInit (mdctx, &evpKeyCtx, evpMd, NULL, evpPublicKey); if (opensslRet != 1) { ret = UA_STATUSCODE_BADINTERNALERROR; goto errout; } EVP_PKEY_CTX_set_rsa_padding (evpKeyCtx, padding); opensslRet = EVP_DigestVerifyUpdate (mdctx, message->data, message->length); if (opensslRet != 1) { ret = UA_STATUSCODE_BADINTERNALERROR; goto errout; } opensslRet = EVP_DigestVerifyFinal(mdctx, signature->data, signature->length); if (opensslRet != 1) { ret = UA_STATUSCODE_BADINTERNALERROR; goto errout; } ret = UA_STATUSCODE_GOOD; errout: if (evpPublicKey != NULL) { EVP_PKEY_free (evpPublicKey); } if (mdctx != NULL) { EVP_MD_CTX_destroy (mdctx); } return ret; } UA_StatusCode UA_OpenSSL_RSA_PKCS1_V15_SHA256_Verify (const UA_ByteString * msg, X509 * publicKeyX509, const UA_ByteString * signature ) { return UA_OpenSSL_RSA_Public_Verify (msg, EVP_sha256(), publicKeyX509, NID_sha256, signature); } /* Get certificate thumbprint, and allocate the buffer. */ UA_StatusCode UA_Openssl_X509_GetCertificateThumbprint (const UA_ByteString * certficate, UA_ByteString * pThumbprint, bool bThumbPrint) { if (bThumbPrint) { pThumbprint->length = SHA_DIGEST_LENGTH; UA_StatusCode ret = UA_ByteString_allocBuffer (pThumbprint, pThumbprint->length); if (ret != UA_STATUSCODE_GOOD) { return ret; } } else { if (pThumbprint->length != SHA_DIGEST_LENGTH) { return UA_STATUSCODE_BADINTERNALERROR; } } X509 * x509Certificate = UA_OpenSSL_LoadCertificate(certficate); if (x509Certificate == NULL) { if (bThumbPrint) { UA_ByteString_clear (pThumbprint); } return UA_STATUSCODE_BADINTERNALERROR; } if (X509_digest (x509Certificate, EVP_sha1(), pThumbprint->data, NULL) != 1) { if (bThumbPrint) { UA_ByteString_clear (pThumbprint); } return UA_STATUSCODE_BADINTERNALERROR; } X509_free(x509Certificate); return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_Openssl_RSA_Private_Decrypt (UA_ByteString * data, EVP_PKEY * privateKey, UA_Int16 padding) { if (data == NULL || privateKey == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } if (privateKey == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } size_t keySize = (size_t) UA_OpenSSL_RSA_Key_Size (privateKey); size_t cipherOffset = 0; size_t outOffset = 0; unsigned char buf[RSA_DECRYPT_BUFFER_LENGTH]; size_t decryptedBytes; EVP_PKEY_CTX * ctx; int opensslRet; ctx = EVP_PKEY_CTX_new (privateKey, NULL); if (ctx == NULL) { return UA_STATUSCODE_BADOUTOFMEMORY; } opensslRet = EVP_PKEY_decrypt_init (ctx); if (opensslRet != 1) { EVP_PKEY_CTX_free (ctx); return UA_STATUSCODE_BADINTERNALERROR; } opensslRet = EVP_PKEY_CTX_set_rsa_padding (ctx, padding); if (opensslRet != 1) { EVP_PKEY_CTX_free (ctx); return UA_STATUSCODE_BADINTERNALERROR; } while (cipherOffset < data->length) { decryptedBytes = RSA_DECRYPT_BUFFER_LENGTH; opensslRet = EVP_PKEY_decrypt (ctx, buf, /* where to decrypt */ &decryptedBytes, data->data + cipherOffset, /* what to decrypt */ keySize ); if (opensslRet != 1) { EVP_PKEY_CTX_free (ctx); return UA_STATUSCODE_BADSECURITYCHECKSFAILED; } (void) memcpy(data->data + outOffset, buf, decryptedBytes); cipherOffset += (size_t) keySize; outOffset += decryptedBytes; } data->length = outOffset; EVP_PKEY_CTX_free (ctx); return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Openssl_RSA_Oaep_Decrypt (UA_ByteString * data, EVP_PKEY * privateKey) { return UA_Openssl_RSA_Private_Decrypt (data, privateKey, RSA_PKCS1_OAEP_PADDING); } static UA_StatusCode UA_Openssl_RSA_Public_Encrypt (const UA_ByteString * message, X509 * publicX509, UA_Int16 padding, size_t paddingSize, UA_ByteString * encrypted) { EVP_PKEY_CTX * ctx = NULL; EVP_PKEY * evpPublicKey = NULL; int opensslRet; UA_StatusCode ret; size_t encryptedTextLen = 0; size_t dataPos = 0; size_t encryptedPos = 0; size_t bytesToEncrypt = 0; size_t encryptedBlockSize = 0; size_t keySize = 0; evpPublicKey = X509_get_pubkey (publicX509); if (evpPublicKey == NULL) { ret = UA_STATUSCODE_BADOUTOFMEMORY; goto errout; } ctx = EVP_PKEY_CTX_new (evpPublicKey, NULL); if (ctx == NULL) { ret = UA_STATUSCODE_BADOUTOFMEMORY; goto errout; } opensslRet = EVP_PKEY_encrypt_init (ctx); if (opensslRet != 1) { ret = UA_STATUSCODE_BADINTERNALERROR; goto errout; } opensslRet = EVP_PKEY_CTX_set_rsa_padding (ctx, padding); if (opensslRet != 1) { ret = UA_STATUSCODE_BADINTERNALERROR; goto errout; } /* get the encrypted block size */ keySize = (size_t) UA_OpenSSL_RSA_Key_Size (evpPublicKey); if (keySize == 0) { ret = UA_STATUSCODE_BADINTERNALERROR; goto errout; } switch (padding) { case RSA_PKCS1_OAEP_PADDING: case RSA_PKCS1_PADDING: if (keySize <= paddingSize) { ret = UA_STATUSCODE_BADINTERNALERROR; goto errout; } encryptedBlockSize = keySize - paddingSize; break; default: ret = UA_STATUSCODE_BADNOTSUPPORTED; goto errout; break; } /* encrypt in reverse order so that [data] may alias [encrypted] */ dataPos = message->length; encryptedPos = ((dataPos - 1) / encryptedBlockSize + 1) * keySize; bytesToEncrypt = (dataPos - 1) % encryptedBlockSize + 1; encryptedTextLen = encryptedPos; while (dataPos > 0) { size_t outlen = keySize; encryptedPos -= keySize; dataPos -= bytesToEncrypt; opensslRet = EVP_PKEY_encrypt (ctx, encrypted->data + encryptedPos, &outlen, message->data + dataPos, bytesToEncrypt); if (opensslRet != 1) { ret = UA_STATUSCODE_BADINTERNALERROR; goto errout; } bytesToEncrypt = encryptedBlockSize; } encrypted->length = encryptedTextLen; ret = UA_STATUSCODE_GOOD; errout: if (evpPublicKey != NULL) { EVP_PKEY_free (evpPublicKey); } if (ctx != NULL) { EVP_PKEY_CTX_free (ctx); } return ret; } UA_StatusCode UA_Openssl_RSA_OAEP_Encrypt (UA_ByteString * data, size_t paddingSize, X509 * publicX509) { UA_ByteString message; UA_StatusCode ret; ret = UA_ByteString_copy (data, &message); if (ret != UA_STATUSCODE_GOOD) { return ret; } ret = UA_Openssl_RSA_Public_Encrypt (&message, publicX509, RSA_PKCS1_OAEP_PADDING, paddingSize, data); UA_ByteString_clear (&message); return ret; } static UA_Openssl_P_SHA256_Ctx * P_SHA256_Ctx_Create (const UA_ByteString * secret, const UA_ByteString * seed) { size_t size = (UA_Int32)sizeof (UA_Openssl_P_SHA256_Ctx) + secret->length + seed->length; UA_Openssl_P_SHA256_Ctx * ctx = (UA_Openssl_P_SHA256_Ctx *) UA_malloc (size); if (ctx == NULL) { return NULL; } ctx->secretLen = secret->length; ctx->seedLen = seed->length; (void) memcpy (UA_Openssl_P_SHA256_SEED(ctx), seed->data, seed->length); (void) memcpy (UA_Openssl_P_SHA256_SECRET(ctx), secret->data, secret->length); /* A(0) = seed A(n) = HMAC_HASH(secret, A(n-1)) */ if (HMAC (EVP_sha256(), secret->data, (int) secret->length, seed->data, seed->length, ctx->A, NULL) == NULL) { UA_free (ctx); return NULL; } return ctx; } static UA_StatusCode P_SHA256_Hash_Generate (UA_Openssl_P_SHA256_Ctx * ctx, UA_Byte * pHas ) { /* Calculate P_SHA256(n) = HMAC_SHA256(secret, A(n)+seed) */ if (HMAC (EVP_sha256(),UA_Openssl_P_SHA256_SECRET(ctx), (int) ctx->secretLen, ctx->A, sizeof (ctx->A) + ctx->seedLen, pHas, NULL) == NULL) { return UA_STATUSCODE_BADINTERNALERROR; } /* Calculate A(n) = HMAC_SHA256(secret, A(n-1)) */ if (HMAC (EVP_sha256(),UA_Openssl_P_SHA256_SECRET(ctx), (int) ctx->secretLen, ctx->A, sizeof (ctx->A), ctx->A, NULL) == NULL) { return UA_STATUSCODE_BADINTERNALERROR; } return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Openssl_Random_Key_PSHA256_Derive (const UA_ByteString * secret, const UA_ByteString * seed, UA_ByteString * out) { size_t keyLen = out->length; size_t iter = keyLen/32 + ((keyLen%32)?1:0); size_t bufferLen = iter * 32; size_t i; UA_StatusCode st; UA_Byte * pBuffer = (UA_Byte *) UA_malloc (bufferLen); if (pBuffer == NULL) { return UA_STATUSCODE_BADOUTOFMEMORY; } UA_Openssl_P_SHA256_Ctx * ctx = P_SHA256_Ctx_Create (secret, seed); if (ctx == NULL) { UA_free (pBuffer); return UA_STATUSCODE_BADOUTOFMEMORY; } for (i = 0; i < iter; i++) { st = P_SHA256_Hash_Generate (ctx, pBuffer + (i * 32)); if (st != UA_STATUSCODE_GOOD) { UA_free (pBuffer); UA_free (ctx); return st; } } (void) memcpy (out->data, pBuffer, keyLen); UA_free (pBuffer); UA_free (ctx); return UA_STATUSCODE_GOOD; } /* return the key bytes */ UA_StatusCode UA_Openssl_RSA_Public_GetKeyLength (X509 * publicKeyX509, UA_Int32 * keyLen) { EVP_PKEY * evpKey = X509_get_pubkey (publicKeyX509); if (evpKey == NULL) { return UA_STATUSCODE_BADINTERNALERROR; } *keyLen = UA_OpenSSL_RSA_Key_Size (evpKey); EVP_PKEY_free (evpKey); return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Openssl_RSA_Private_GetKeyLength (EVP_PKEY * privateKey, UA_Int32 * keyLen) { if (privateKey == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } *keyLen = UA_OpenSSL_RSA_Key_Size (privateKey); return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_Openssl_RSA_Private_Sign (const UA_ByteString * message, EVP_PKEY * privateKey, const EVP_MD * evpMd, UA_Int16 padding, UA_ByteString * outSignature) { EVP_MD_CTX * mdctx = NULL; int opensslRet; EVP_PKEY_CTX * evpKeyCtx; UA_StatusCode ret; mdctx = EVP_MD_CTX_create (); if (mdctx == NULL) { ret = UA_STATUSCODE_BADOUTOFMEMORY; goto errout; } if (privateKey == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } opensslRet = EVP_DigestSignInit (mdctx, &evpKeyCtx, evpMd, NULL, privateKey); if (opensslRet != 1) { ret = UA_STATUSCODE_BADINTERNALERROR; goto errout; } EVP_PKEY_CTX_set_rsa_padding (evpKeyCtx, padding); opensslRet = EVP_DigestSignUpdate (mdctx, message->data, message->length); if (opensslRet != 1) { ret = UA_STATUSCODE_BADINTERNALERROR; goto errout; } opensslRet = EVP_DigestSignFinal (mdctx, outSignature->data, &outSignature->length); if (opensslRet != 1) { ret = UA_STATUSCODE_BADINTERNALERROR; goto errout; } ret = UA_STATUSCODE_GOOD; errout: if (mdctx != NULL) { EVP_MD_CTX_destroy (mdctx); } return ret; } UA_StatusCode UA_Openssl_RSA_PKCS1_V15_SHA256_Sign (const UA_ByteString * message, EVP_PKEY * privateKey, UA_ByteString * outSignature) { return UA_Openssl_RSA_Private_Sign (message, privateKey, EVP_sha256(), NID_sha256, outSignature); } UA_StatusCode UA_OpenSSL_HMAC_SHA256_Verify (const UA_ByteString * message, const UA_ByteString * key, const UA_ByteString * signature ) { unsigned char buf[SHA256_DIGEST_LENGTH] = {0}; UA_ByteString mac = {SHA256_DIGEST_LENGTH, buf}; if (HMAC (EVP_sha256(), key->data, (int) key->length, message->data, message->length, mac.data, (unsigned int *) &mac.length) == NULL) { return UA_STATUSCODE_BADINTERNALERROR; } if (UA_ByteString_equal (signature, &mac)) { return UA_STATUSCODE_GOOD; } else { return UA_STATUSCODE_BADINTERNALERROR; } } UA_StatusCode UA_OpenSSL_HMAC_SHA256_Sign (const UA_ByteString * message, const UA_ByteString * key, UA_ByteString * signature ) { if (HMAC (EVP_sha256(), key->data, (int) key->length, message->data, message->length, signature->data, (unsigned int *) &(signature->length)) == NULL) { return UA_STATUSCODE_BADINTERNALERROR; } return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_OpenSSL_Decrypt (const UA_ByteString * iv, const UA_ByteString * key, const EVP_CIPHER * cipherAlg, UA_ByteString * data /* [in/out]*/) { UA_ByteString ivCopy = {0, NULL}; UA_ByteString cipherTxt = {0, NULL}; EVP_CIPHER_CTX * ctx = NULL; UA_StatusCode ret; int opensslRet; int outLen; int tmpLen; /* copy the IV because the AES_cbc_encrypt function overwrites it. */ ret = UA_ByteString_copy (iv, &ivCopy); if (ret != UA_STATUSCODE_GOOD) { goto errout; } ret = UA_ByteString_copy (data, &cipherTxt); if (ret != UA_STATUSCODE_GOOD) { goto errout; } ctx = EVP_CIPHER_CTX_new (); if (ctx == NULL) { ret = UA_STATUSCODE_BADOUTOFMEMORY; goto errout; } /* call EVP_* to decrypt */ opensslRet = EVP_DecryptInit_ex (ctx, cipherAlg, NULL, key->data, ivCopy.data); if (opensslRet != 1) { ret = UA_STATUSCODE_BADINTERNALERROR; goto errout; } /* EVP_DecryptFinal() will return an error code if padding is enabled * and the final block is not correctly formatted. */ EVP_CIPHER_CTX_set_padding (ctx, 0); opensslRet = EVP_DecryptUpdate (ctx, data->data, &outLen, cipherTxt.data, (int) cipherTxt.length); if (opensslRet != 1) { ret = UA_STATUSCODE_BADINTERNALERROR; goto errout; } opensslRet = EVP_DecryptFinal_ex (ctx, data->data + outLen, &tmpLen); if (opensslRet != 1) { ret = UA_STATUSCODE_BADINTERNALERROR; goto errout; } outLen += tmpLen; data->length = (size_t) outLen; ret = UA_STATUSCODE_GOOD; errout: UA_ByteString_clear (&ivCopy); UA_ByteString_clear (&cipherTxt); if (ctx != NULL) { EVP_CIPHER_CTX_free(ctx); } return ret; } static UA_StatusCode UA_OpenSSL_Encrypt (const UA_ByteString * iv, const UA_ByteString * key, const EVP_CIPHER * cipherAlg, UA_ByteString * data /* [in/out]*/ ) { UA_ByteString ivCopy = {0, NULL}; UA_ByteString plainTxt = {0, NULL}; EVP_CIPHER_CTX * ctx = NULL; UA_StatusCode ret; int opensslRet; int outLen; int tmpLen; /* copy the IV because the AES_cbc_encrypt function overwrites it. */ ret = UA_ByteString_copy (iv, &ivCopy); if (ret != UA_STATUSCODE_GOOD) { goto errout; } ret = UA_ByteString_copy (data, &plainTxt); if (ret != UA_STATUSCODE_GOOD) { goto errout; } ctx = EVP_CIPHER_CTX_new (); if (ctx == NULL) { ret = UA_STATUSCODE_BADOUTOFMEMORY; goto errout; } /* call EVP_* to encrypt */ opensslRet = EVP_EncryptInit_ex (ctx, cipherAlg, NULL, key->data, ivCopy.data); if (opensslRet != 1) { ret = UA_STATUSCODE_BADINTERNALERROR; goto errout; } /* Disable padding. Padding is done in the stack before calling encryption. * Ensure that we have a multiple of the block size */ if(data->length % (size_t)EVP_CIPHER_CTX_block_size(ctx)) { ret = UA_STATUSCODE_BADINTERNALERROR; goto errout; } opensslRet = EVP_CIPHER_CTX_set_padding(ctx, 0); if (opensslRet != 1) { ret = UA_STATUSCODE_BADINTERNALERROR; goto errout; } /* Encrypt the data */ opensslRet = EVP_EncryptUpdate (ctx, data->data, &outLen, plainTxt.data, (int) plainTxt.length); if (opensslRet != 1) { ret = UA_STATUSCODE_BADINTERNALERROR; goto errout; } /* Encrypt-final does nothing as padding is disabled */ opensslRet = EVP_EncryptFinal_ex(ctx, data->data + outLen, &tmpLen); if (opensslRet != 1) { ret = UA_STATUSCODE_BADINTERNALERROR; goto errout; } outLen += tmpLen; data->length = (size_t) outLen; ret = UA_STATUSCODE_GOOD; errout: UA_ByteString_clear (&ivCopy); UA_ByteString_clear (&plainTxt); if (ctx != NULL) { EVP_CIPHER_CTX_free(ctx); } return ret; } UA_StatusCode UA_OpenSSL_AES_256_CBC_Decrypt (const UA_ByteString * iv, const UA_ByteString * key, UA_ByteString * data /* [in/out]*/ ) { return UA_OpenSSL_Decrypt (iv, key, EVP_aes_256_cbc (), data); } UA_StatusCode UA_OpenSSL_AES_256_CBC_Encrypt (const UA_ByteString * iv, const UA_ByteString * key, UA_ByteString * data /* [in/out]*/ ) { return UA_OpenSSL_Encrypt (iv, key, EVP_aes_256_cbc (), data); } UA_StatusCode UA_OpenSSL_X509_compare (const UA_ByteString * cert, const X509 * bcert) { X509 * acert = UA_OpenSSL_LoadCertificate(cert); if (acert == NULL) { return UA_STATUSCODE_BADCERTIFICATEINVALID; } int opensslRet = X509_cmp (acert, bcert); X509_free (acert); if (opensslRet == 0) return UA_STATUSCODE_GOOD; return UA_STATUSCODE_UNCERTAINSUBNORMAL; } UA_StatusCode UA_OpenSSL_RSA_PKCS1_V15_SHA1_Verify (const UA_ByteString * msg, X509 * publicKeyX509, const UA_ByteString * signature) { return UA_OpenSSL_RSA_Public_Verify (msg, EVP_sha1(), publicKeyX509, NID_sha1, signature); } UA_StatusCode UA_Openssl_RSA_PKCS1_V15_SHA1_Sign (const UA_ByteString * message, EVP_PKEY * privateKey, UA_ByteString * outSignature) { return UA_Openssl_RSA_Private_Sign (message, privateKey, EVP_sha1(), NID_sha1, outSignature); } static UA_Openssl_P_SHA1_Ctx * P_SHA1_Ctx_Create (const UA_ByteString * secret, const UA_ByteString * seed) { size_t size = (UA_Int32)sizeof (UA_Openssl_P_SHA1_Ctx) + secret->length + seed->length; UA_Openssl_P_SHA1_Ctx * ctx = (UA_Openssl_P_SHA1_Ctx *) UA_malloc (size); if (ctx == NULL) { return NULL; } ctx->secretLen = secret->length; ctx->seedLen = seed->length; (void) memcpy (UA_Openssl_P_SHA1_SEED(ctx), seed->data, seed->length); (void) memcpy (UA_Openssl_P_SHA1_SECRET(ctx), secret->data, secret->length); /* A(0) = seed A(n) = HMAC_HASH(secret, A(n-1)) */ if (HMAC (EVP_sha1(), secret->data, (int) secret->length, seed->data, seed->length, ctx->A, NULL) == NULL) { UA_free (ctx); return NULL; } return ctx; } static UA_StatusCode P_SHA1_Hash_Generate (UA_Openssl_P_SHA1_Ctx * ctx, UA_Byte * pHas ) { /* Calculate P_SHA1(n) = HMAC_SHA1(secret, A(n)+seed) */ if (HMAC (EVP_sha1 (), UA_Openssl_P_SHA1_SECRET(ctx), (int) ctx->secretLen, ctx->A, sizeof (ctx->A) + ctx->seedLen, pHas, NULL) == NULL) { return UA_STATUSCODE_BADINTERNALERROR; } /* Calculate A(n) = HMAC_SHA1(secret, A(n-1)) */ if (HMAC (EVP_sha1(), UA_Openssl_P_SHA1_SECRET(ctx), (int) ctx->secretLen, ctx->A, sizeof (ctx->A), ctx->A, NULL) == NULL) { return UA_STATUSCODE_BADINTERNALERROR; } return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Openssl_Random_Key_PSHA1_Derive (const UA_ByteString * secret, const UA_ByteString * seed, UA_ByteString * out) { size_t keyLen = out->length; size_t iter = keyLen / SHA1_DIGEST_LENGTH + ((keyLen % SHA1_DIGEST_LENGTH)?1:0); size_t bufferLen = iter * SHA1_DIGEST_LENGTH; UA_Byte * pBuffer = (UA_Byte *) UA_malloc (bufferLen); if (pBuffer == NULL) { return UA_STATUSCODE_BADOUTOFMEMORY; } UA_Openssl_P_SHA1_Ctx * ctx = P_SHA1_Ctx_Create (secret, seed); if (ctx == NULL) { UA_free (pBuffer); return UA_STATUSCODE_BADOUTOFMEMORY; } size_t i; UA_StatusCode st; for (i = 0; i < iter; i++) { st = P_SHA1_Hash_Generate (ctx, pBuffer + (i * SHA1_DIGEST_LENGTH)); if (st != UA_STATUSCODE_GOOD) { UA_free (pBuffer); UA_free (ctx); return st; } } (void) memcpy (out->data, pBuffer, keyLen); UA_free (pBuffer); UA_free (ctx); return UA_STATUSCODE_GOOD; } UA_StatusCode UA_OpenSSL_HMAC_SHA1_Verify (const UA_ByteString * message, const UA_ByteString * key, const UA_ByteString * signature ) { unsigned char buf[SHA1_DIGEST_LENGTH] = {0}; UA_ByteString mac = {SHA1_DIGEST_LENGTH, buf}; if(HMAC (EVP_sha1(), key->data, (int) key->length, message->data, message->length, mac.data, (unsigned int *) &mac.length) == NULL) { return UA_STATUSCODE_BADINTERNALERROR; } if (UA_ByteString_equal (signature, &mac)) { return UA_STATUSCODE_GOOD; } else { return UA_STATUSCODE_BADINTERNALERROR; } } UA_StatusCode UA_OpenSSL_HMAC_SHA1_Sign (const UA_ByteString * message, const UA_ByteString * key, UA_ByteString * signature ) { if (HMAC (EVP_sha1(), key->data, (int) key->length, message->data, message->length, signature->data, (unsigned int *) &(signature->length)) == NULL) { return UA_STATUSCODE_BADINTERNALERROR; } return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Openssl_RSA_PKCS1_V15_Decrypt (UA_ByteString * data, EVP_PKEY * privateKey) { return UA_Openssl_RSA_Private_Decrypt (data, privateKey, RSA_PKCS1_PADDING); } UA_StatusCode UA_Openssl_RSA_PKCS1_V15_Encrypt (UA_ByteString * data, size_t paddingSize, X509 * publicX509) { UA_ByteString message; UA_StatusCode ret = UA_ByteString_copy (data, &message); if (ret != UA_STATUSCODE_GOOD) { return ret; } ret = UA_Openssl_RSA_Public_Encrypt (&message, publicX509, RSA_PKCS1_PADDING, paddingSize, data); UA_ByteString_clear (&message); return ret; } UA_StatusCode UA_OpenSSL_AES_128_CBC_Decrypt (const UA_ByteString * iv, const UA_ByteString * key, UA_ByteString * data /* [in/out]*/ ) { return UA_OpenSSL_Decrypt (iv, key, EVP_aes_128_cbc (), data); } UA_StatusCode UA_OpenSSL_AES_128_CBC_Encrypt (const UA_ByteString * iv, const UA_ByteString * key, UA_ByteString * data /* [in/out]*/ ) { return UA_OpenSSL_Encrypt (iv, key, EVP_aes_128_cbc (), data); } EVP_PKEY * UA_OpenSSL_LoadPrivateKey(const UA_ByteString *privateKey) { const unsigned char * pkData = privateKey->data; long len = (long) privateKey->length; if(len == 0) return NULL; EVP_PKEY *result = NULL; if (len > 1 && pkData[0] == 0x30 && pkData[1] == 0x82) { // Magic number for DER encoded keys result = d2i_PrivateKey(EVP_PKEY_RSA, NULL, &pkData, len); } else { BIO *bio = NULL; bio = BIO_new_mem_buf((void *) privateKey->data, (int) privateKey->length); result = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); BIO_free(bio); } return result; } X509 * UA_OpenSSL_LoadCertificate(const UA_ByteString *certificate) { X509 * result = NULL; const unsigned char *pData = certificate->data; if (certificate->length > 1 && pData[0] == 0x30 && pData[1] == 0x82) { // Magic number for DER encoded files result = UA_OpenSSL_LoadDerCertificate(certificate); } else { result = UA_OpenSSL_LoadPemCertificate(certificate); } return result; } X509 * UA_OpenSSL_LoadDerCertificate(const UA_ByteString *certificate) { const unsigned char *pData = certificate->data; return d2i_X509(NULL, &pData, (long) certificate->length); } X509 * UA_OpenSSL_LoadPemCertificate(const UA_ByteString *certificate) { X509 * result = NULL; BIO* bio = NULL; bio = BIO_new_mem_buf((void *) certificate->data, (int) certificate->length); result = PEM_read_bio_X509(bio, NULL, NULL, NULL); BIO_free(bio); return result; } UA_StatusCode UA_OpenSSL_LoadLocalCertificate(const UA_ByteString *certificate, UA_ByteString *target) { X509 *cert = UA_OpenSSL_LoadCertificate(certificate); if (!cert) { UA_ByteString_init(target); return UA_STATUSCODE_BADINVALIDARGUMENT; } unsigned char *derData = NULL; int length = i2d_X509(cert, &derData); X509_free(cert); if (length > 0) { UA_ByteString temp; temp.length = (size_t) length; temp.data = derData; UA_ByteString_copy(&temp, target); OPENSSL_free(derData); return UA_STATUSCODE_GOOD; } else { UA_ByteString_init(target); } return UA_STATUSCODE_BADINVALIDARGUMENT; } #endif /**** amalgamated original file "/plugins/crypto/openssl/ua_openssl_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 2020 (c) Wind River Systems, Inc. * Copyright 2020 (c) basysKom GmbH */ #if defined(UA_ENABLE_ENCRYPTION_OPENSSL) || defined(UA_ENABLE_ENCRYPTION_LIBRESSL) #include #include #include #define UA_SHA1_LENGTH 20 #define UA_SECURITYPOLICY_BASIC128RSA15_RSAPADDING_LEN 11 #define UA_SECURITYPOLICY_BASIC128RSA15_SYM_ENCRYPTION_KEY_LENGTH 16 #define UA_SECURITYPOLICY_BASIC128RSA15_SYM_ENCRYPTION_BLOCK_SIZE 16 #define UA_SECURITYPOLICY_BASIC128RSA15_SYM_SIGNING_KEY_LENGTH 16 #define UA_SHA1_LENGTH 20 typedef struct { EVP_PKEY * localPrivateKey; UA_ByteString localCertThumbprint; const UA_Logger * logger; } Policy_Context_Basic128Rsa15; typedef struct { UA_ByteString localSymSigningKey; UA_ByteString localSymEncryptingKey; UA_ByteString localSymIv; UA_ByteString remoteSymSigningKey; UA_ByteString remoteSymEncryptingKey; UA_ByteString remoteSymIv; Policy_Context_Basic128Rsa15 * policyContext; UA_ByteString remoteCertificate; X509 * remoteCertificateX509; } Channel_Context_Basic128Rsa15; static UA_StatusCode UA_Policy_Basic128Rsa15_New_Context (UA_SecurityPolicy * securityPolicy, const UA_ByteString localPrivateKey, const UA_Logger * logger) { Policy_Context_Basic128Rsa15 * context = (Policy_Context_Basic128Rsa15 *) UA_malloc (sizeof (Policy_Context_Basic128Rsa15)); if (context == NULL) { return UA_STATUSCODE_BADOUTOFMEMORY; } context->localPrivateKey = UA_OpenSSL_LoadPrivateKey(&localPrivateKey); if (!context->localPrivateKey) { UA_free(context); return UA_STATUSCODE_BADINVALIDARGUMENT; } UA_StatusCode retval = UA_Openssl_X509_GetCertificateThumbprint ( &securityPolicy->localCertificate, &context->localCertThumbprint, true ); if (retval != UA_STATUSCODE_GOOD) { EVP_PKEY_free(context->localPrivateKey); UA_free (context); return retval; } context->logger = logger; securityPolicy->policyContext = context; return UA_STATUSCODE_GOOD; } static void UA_Policy_Basic128Rsa15_Clear_Context (UA_SecurityPolicy *policy) { if (policy == NULL) { return; } UA_ByteString_clear(&policy->localCertificate); Policy_Context_Basic128Rsa15 * ctx = (Policy_Context_Basic128Rsa15 *) policy->policyContext; if (ctx == NULL) { return; } /* delete all allocated members in the context */ EVP_PKEY_free(ctx->localPrivateKey); UA_ByteString_clear(&ctx->localCertThumbprint); UA_free (ctx); return; } /* create the channel context */ static UA_StatusCode UA_ChannelModule_Basic128Rsa15_New_Context (const UA_SecurityPolicy * securityPolicy, const UA_ByteString * remoteCertificate, void ** channelContext) { if (securityPolicy == NULL || remoteCertificate == NULL || channelContext == NULL) { return UA_STATUSCODE_BADINTERNALERROR; } Channel_Context_Basic128Rsa15 * context = (Channel_Context_Basic128Rsa15 *) UA_malloc (sizeof (Channel_Context_Basic128Rsa15)); if (context == NULL) { return UA_STATUSCODE_BADOUTOFMEMORY; } UA_ByteString_init(&context->localSymSigningKey); UA_ByteString_init(&context->localSymEncryptingKey); UA_ByteString_init(&context->localSymIv); UA_ByteString_init(&context->remoteSymSigningKey); UA_ByteString_init(&context->remoteSymEncryptingKey); UA_ByteString_init(&context->remoteSymIv); UA_StatusCode retval = UA_copyCertificate (&context->remoteCertificate, remoteCertificate); if (retval != UA_STATUSCODE_GOOD) { UA_free (context); return retval; } /* decode to X509 */ context->remoteCertificateX509 = UA_OpenSSL_LoadCertificate(&context->remoteCertificate); if (context->remoteCertificateX509 == NULL) { UA_ByteString_clear (&context->remoteCertificate); UA_free (context); return UA_STATUSCODE_BADCERTIFICATECHAININCOMPLETE; } context->policyContext = (Policy_Context_Basic128Rsa15 *) (securityPolicy->policyContext); *channelContext = context; UA_LOG_INFO (securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "The Basic128Rsa15 security policy channel with openssl is created."); return UA_STATUSCODE_GOOD; } /* delete the channel context */ static void UA_ChannelModule_Basic128Rsa15_Delete_Context (void * channelContext) { if (channelContext != NULL) { Channel_Context_Basic128Rsa15 * cc = (Channel_Context_Basic128Rsa15 *) channelContext; X509_free (cc->remoteCertificateX509); UA_ByteString_clear (&cc->remoteCertificate); UA_ByteString_clear (&cc->localSymSigningKey); UA_ByteString_clear (&cc->localSymEncryptingKey); UA_ByteString_clear (&cc->localSymIv); UA_ByteString_clear (&cc->remoteSymSigningKey); UA_ByteString_clear (&cc->remoteSymEncryptingKey); UA_ByteString_clear (&cc->remoteSymIv); UA_LOG_INFO (cc->policyContext->logger, UA_LOGCATEGORY_SECURITYPOLICY, "The Basic128Rsa15 security policy channel with openssl is deleted."); UA_free (cc); } } static UA_StatusCode UA_ChannelModule_Basic128Rsa15_setLocalSymSigningKey (void * channelContext, const UA_ByteString * key) { if (key == NULL || channelContext == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } Channel_Context_Basic128Rsa15 * cc = (Channel_Context_Basic128Rsa15 *) channelContext; UA_ByteString_clear(&cc->localSymSigningKey); return UA_ByteString_copy(key, &cc->localSymSigningKey); } static UA_StatusCode UA_ChannelModule_Basic128Rsa15_setLocalSymEncryptingKey (void * channelContext, const UA_ByteString * key) { if (key == NULL || channelContext == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } Channel_Context_Basic128Rsa15 * cc = (Channel_Context_Basic128Rsa15 *) channelContext; UA_ByteString_clear(&cc->localSymEncryptingKey); return UA_ByteString_copy(key, &cc->localSymEncryptingKey); } static UA_StatusCode UA_ChannelModule_Basic128Rsa15_setLocalSymIv (void * channelContext, const UA_ByteString * iv) { if (iv == NULL || channelContext == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } Channel_Context_Basic128Rsa15 * cc = (Channel_Context_Basic128Rsa15 *) channelContext; UA_ByteString_clear(&cc->localSymIv); return UA_ByteString_copy(iv, &cc->localSymIv); } static UA_StatusCode UA_ChannelModule_Basic128Rsa15_setRemoteSymSigningKey (void * channelContext, const UA_ByteString * key) { if (key == NULL || channelContext == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } Channel_Context_Basic128Rsa15 * cc = (Channel_Context_Basic128Rsa15 *) channelContext; UA_ByteString_clear(&cc->remoteSymSigningKey); return UA_ByteString_copy(key, &cc->remoteSymSigningKey); } static UA_StatusCode UA_ChannelModule_Basic128Rsa15_setRemoteSymEncryptingKey (void * channelContext, const UA_ByteString * key) { if (key == NULL || channelContext == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } Channel_Context_Basic128Rsa15 * cc = (Channel_Context_Basic128Rsa15 *) channelContext; UA_ByteString_clear(&cc->remoteSymEncryptingKey); return UA_ByteString_copy(key, &cc->remoteSymEncryptingKey); } static UA_StatusCode UA_ChannelModule_Basic128Rsa15_setRemoteSymIv (void * channelContext, const UA_ByteString * key) { if (key == NULL || channelContext == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } Channel_Context_Basic128Rsa15 * cc = (Channel_Context_Basic128Rsa15 *) channelContext; UA_ByteString_clear(&cc->remoteSymIv); return UA_ByteString_copy(key, &cc->remoteSymIv); } static UA_StatusCode UA_ChannelModule_Basic128Rsa15_compareCertificate (const void * channelContext, const UA_ByteString * certificate) { if(channelContext == NULL || certificate == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } const Channel_Context_Basic128Rsa15 * cc = (const Channel_Context_Basic128Rsa15 *) channelContext; return UA_OpenSSL_X509_compare (certificate, cc->remoteCertificateX509); } static UA_StatusCode UA_Asy_Basic128Rsa15_compareCertificateThumbprint (const UA_SecurityPolicy * securityPolicy, const UA_ByteString * certificateThumbprint) { if (securityPolicy == NULL || certificateThumbprint == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } Policy_Context_Basic128Rsa15 *pc = (Policy_Context_Basic128Rsa15 *) securityPolicy->policyContext; if(!UA_ByteString_equal(certificateThumbprint, &pc->localCertThumbprint)) { return UA_STATUSCODE_BADCERTIFICATEINVALID; } return UA_STATUSCODE_GOOD; } /* Generates a thumbprint for the specified certificate */ static UA_StatusCode UA_Asy_Basic128Rsa15_makeCertificateThumbprint (const UA_SecurityPolicy * securityPolicy, const UA_ByteString * certificate, UA_ByteString * thumbprint) { return UA_Openssl_X509_GetCertificateThumbprint (certificate, thumbprint, false); } static size_t UA_AsySig_Basic128Rsa15_getRemoteSignatureSize (const void * channelContext) { if (channelContext == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } const Channel_Context_Basic128Rsa15 * cc = (const Channel_Context_Basic128Rsa15 *) channelContext; UA_Int32 keyLen = 0; UA_Openssl_RSA_Public_GetKeyLength (cc->remoteCertificateX509, &keyLen); return (size_t) keyLen; } static size_t UA_AsySig_Basic128Rsa15_getLocalSignatureSize (const void * channelContext) { if (channelContext == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } const Channel_Context_Basic128Rsa15 * cc = (const Channel_Context_Basic128Rsa15 *) channelContext; Policy_Context_Basic128Rsa15 * pc = (Policy_Context_Basic128Rsa15 *) cc->policyContext; UA_Int32 keyLen = 0; UA_Openssl_RSA_Private_GetKeyLength (pc->localPrivateKey, &keyLen); return (size_t) keyLen; } static UA_StatusCode UA_AsySig_Basic128Rsa15_Verify (void * channelContext, const UA_ByteString * message, const UA_ByteString * signature) { if (message == NULL || signature == NULL || channelContext == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } Channel_Context_Basic128Rsa15 * cc = (Channel_Context_Basic128Rsa15 *) channelContext; UA_StatusCode retval = UA_OpenSSL_RSA_PKCS1_V15_SHA1_Verify (message, cc->remoteCertificateX509, signature); return retval; } static UA_StatusCode UA_AsySig_Basic128Rsa15_Sign (void * channelContext, const UA_ByteString * message, UA_ByteString * signature) { if (channelContext == NULL || message == NULL || signature == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } const Channel_Context_Basic128Rsa15 * cc = (const Channel_Context_Basic128Rsa15 *) channelContext; Policy_Context_Basic128Rsa15 *pc = cc->policyContext; return UA_Openssl_RSA_PKCS1_V15_SHA1_Sign (message, pc->localPrivateKey, signature); } static size_t UA_AsymEn_Basic128Rsa15_getRemotePlainTextBlockSize (const void *channelContext) { if (channelContext == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } const Channel_Context_Basic128Rsa15 * cc = (const Channel_Context_Basic128Rsa15 *) channelContext; UA_Int32 keyLen = 0; UA_Openssl_RSA_Public_GetKeyLength (cc->remoteCertificateX509, &keyLen); return (size_t) keyLen - UA_SECURITYPOLICY_BASIC128RSA15_RSAPADDING_LEN; } static size_t UA_AsymEn_Basic128Rsa15_getRemoteBlockSize (const void *channelContext) { if (channelContext == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } const Channel_Context_Basic128Rsa15 * cc = (const Channel_Context_Basic128Rsa15 *) channelContext; UA_Int32 keyLen = 0; UA_Openssl_RSA_Public_GetKeyLength (cc->remoteCertificateX509, &keyLen); return (size_t) keyLen; } static size_t UA_AsymEn_Basic128Rsa15_getRemoteKeyLength (const void *channelContext) { if (channelContext == NULL) return UA_STATUSCODE_BADINVALIDARGUMENT; const Channel_Context_Basic128Rsa15 * cc = (const Channel_Context_Basic128Rsa15 *) channelContext; UA_Int32 keyLen = 0; UA_Openssl_RSA_Public_GetKeyLength (cc->remoteCertificateX509, &keyLen); return (size_t) keyLen * 8; } static size_t UA_AsymEn_Basic128Rsa15_getLocalKeyLength (const void *channelContext) { if (channelContext == NULL) return UA_STATUSCODE_BADINVALIDARGUMENT; const Channel_Context_Basic128Rsa15 *cc = (const Channel_Context_Basic128Rsa15 *) channelContext; Policy_Context_Basic128Rsa15 *pc = cc->policyContext; UA_Int32 keyLen = 0; UA_Openssl_RSA_Private_GetKeyLength (pc->localPrivateKey, &keyLen); return (size_t) keyLen * 8; } static UA_StatusCode UA_AsymEn_Basic128Rsa15_Decrypt (void * channelContext, UA_ByteString * data) { if (channelContext == NULL || data == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } Channel_Context_Basic128Rsa15 * cc = (Channel_Context_Basic128Rsa15 *) channelContext; UA_StatusCode ret = UA_Openssl_RSA_PKCS1_V15_Decrypt (data, cc->policyContext->localPrivateKey); return ret; } static UA_StatusCode UA_AsymEn_Basic128Rsa15_Encrypt (void * channelContext, UA_ByteString * data) { if (channelContext == NULL || data == NULL) return UA_STATUSCODE_BADINVALIDARGUMENT; Channel_Context_Basic128Rsa15 * cc = (Channel_Context_Basic128Rsa15 *) channelContext; return UA_Openssl_RSA_PKCS1_V15_Encrypt (data, UA_SECURITYPOLICY_BASIC128RSA15_RSAPADDING_LEN, cc->remoteCertificateX509); } static UA_StatusCode UA_Sym_Basic128Rsa15_generateNonce(void *policyContext, UA_ByteString *out) { UA_Int32 rc = RAND_bytes(out->data, (int) out->length); if (rc != 1) { return UA_STATUSCODE_BADUNEXPECTEDERROR; } return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_Sym_Basic128Rsa15_generateKey(void *policyContext, const UA_ByteString *secret, const UA_ByteString *seed, UA_ByteString *out) { return UA_Openssl_Random_Key_PSHA1_Derive(secret, seed, out); } static size_t UA_SymEn_Basic128Rsa15_getLocalKeyLength (const void *channelContext) { /* 16 bytes 128 bits */ return UA_SECURITYPOLICY_BASIC128RSA15_SYM_ENCRYPTION_KEY_LENGTH; } static size_t UA_SymEn_Basic128Rsa15_getBlockSize (const void *channelContext) { return UA_SECURITYPOLICY_BASIC128RSA15_SYM_ENCRYPTION_BLOCK_SIZE; } static size_t UA_SymEn_Basic128Rsa15_getRemoteKeyLength (const void * channelContext) { return UA_SECURITYPOLICY_BASIC128RSA15_SYM_ENCRYPTION_KEY_LENGTH; } static UA_StatusCode UA_SymEn_Basic128Rsa15_Encrypt (void *channelContext, UA_ByteString *data) { if(channelContext == NULL || data == NULL) return UA_STATUSCODE_BADINVALIDARGUMENT; Channel_Context_Basic128Rsa15 * cc = (Channel_Context_Basic128Rsa15 *) channelContext; return UA_OpenSSL_AES_128_CBC_Encrypt (&cc->localSymIv, &cc->localSymEncryptingKey, data); } static UA_StatusCode UA_SymEn_Basic128Rsa15_Decrypt (void * channelContext, UA_ByteString * data) { if(channelContext == NULL || data == NULL) return UA_STATUSCODE_BADINVALIDARGUMENT; Channel_Context_Basic128Rsa15 * cc = (Channel_Context_Basic128Rsa15 *) channelContext; return UA_OpenSSL_AES_128_CBC_Decrypt (&cc->remoteSymIv, &cc->remoteSymEncryptingKey, data); } static size_t UA_SymSig_Basic128Rsa15_getKeyLength (const void *channelContext) { return UA_SECURITYPOLICY_BASIC128RSA15_SYM_SIGNING_KEY_LENGTH; } static size_t UA_SymSig_Basic128Rsa15_getSignatureSize (const void *channelContext) { return UA_SHA1_LENGTH; } static UA_StatusCode UA_SymSig_Basic128Rsa15_Verify (void * channelContext, const UA_ByteString * message, const UA_ByteString * signature) { if (channelContext == NULL || message == NULL || signature == NULL) return UA_STATUSCODE_BADINVALIDARGUMENT; Channel_Context_Basic128Rsa15 * cc = (Channel_Context_Basic128Rsa15 *) channelContext; return UA_OpenSSL_HMAC_SHA1_Verify (message, &cc->remoteSymSigningKey, signature); } static UA_StatusCode UA_SymSig_Basic128Rsa15_Sign (void * channelContext, const UA_ByteString * message, UA_ByteString * signature) { if (channelContext == NULL || message == NULL || signature == NULL) return UA_STATUSCODE_BADINVALIDARGUMENT; Channel_Context_Basic128Rsa15 * cc = (Channel_Context_Basic128Rsa15 *) channelContext; return UA_OpenSSL_HMAC_SHA1_Sign (message, &cc->localSymSigningKey, signature); } /* the main entry of Basic128Rsa15 */ UA_StatusCode UA_SecurityPolicy_Basic128Rsa15 (UA_SecurityPolicy * policy, const UA_ByteString localCertificate, const UA_ByteString localPrivateKey, const UA_Logger * logger) { UA_SecurityPolicyAsymmetricModule * const asymmetricModule = &policy->asymmetricModule; UA_SecurityPolicySymmetricModule * const symmetricModule = &policy->symmetricModule; UA_SecurityPolicyChannelModule * const channelModule = &policy->channelModule; UA_StatusCode retval; UA_LOG_INFO (logger, UA_LOGCATEGORY_SECURITYPOLICY, "The Basic128Rsa15 security policy with openssl is added."); UA_Openssl_Init (); memset(policy, 0, sizeof(UA_SecurityPolicy)); policy->logger = logger; policy->policyUri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15\0"); /* set ChannelModule context */ channelModule->newContext = UA_ChannelModule_Basic128Rsa15_New_Context; channelModule->deleteContext = UA_ChannelModule_Basic128Rsa15_Delete_Context; channelModule->setLocalSymSigningKey = UA_ChannelModule_Basic128Rsa15_setLocalSymSigningKey; channelModule->setLocalSymEncryptingKey = UA_ChannelModule_Basic128Rsa15_setLocalSymEncryptingKey; channelModule->setLocalSymIv = UA_ChannelModule_Basic128Rsa15_setLocalSymIv; channelModule->setRemoteSymSigningKey = UA_ChannelModule_Basic128Rsa15_setRemoteSymSigningKey; channelModule->setRemoteSymEncryptingKey = UA_ChannelModule_Basic128Rsa15_setRemoteSymEncryptingKey; channelModule->setRemoteSymIv = UA_ChannelModule_Basic128Rsa15_setRemoteSymIv; channelModule->compareCertificate = UA_ChannelModule_Basic128Rsa15_compareCertificate; retval = UA_OpenSSL_LoadLocalCertificate(&localCertificate, &policy->localCertificate); if (retval != UA_STATUSCODE_GOOD) return retval; /* asymmetricModule */ asymmetricModule->compareCertificateThumbprint = UA_Asy_Basic128Rsa15_compareCertificateThumbprint; asymmetricModule->makeCertificateThumbprint = UA_Asy_Basic128Rsa15_makeCertificateThumbprint; /* AsymmetricModule - signature algorithm */ UA_SecurityPolicySignatureAlgorithm * asySigAlgorithm = &asymmetricModule->cryptoModule.signatureAlgorithm; asySigAlgorithm->uri = UA_STRING("http://www.w3.org/2000/09/xmldsig#rsa-sha1\0"); asySigAlgorithm->getRemoteSignatureSize = UA_AsySig_Basic128Rsa15_getRemoteSignatureSize; asySigAlgorithm->getLocalSignatureSize = UA_AsySig_Basic128Rsa15_getLocalSignatureSize; asySigAlgorithm->getLocalKeyLength = NULL; asySigAlgorithm->getRemoteKeyLength = NULL; asySigAlgorithm->verify = UA_AsySig_Basic128Rsa15_Verify; asySigAlgorithm->sign = UA_AsySig_Basic128Rsa15_Sign; /* AsymmetricModule encryption algorithm */ UA_SecurityPolicyEncryptionAlgorithm * asymEncryAlg = &asymmetricModule->cryptoModule.encryptionAlgorithm; asymEncryAlg->uri = UA_STRING("http://www.w3.org/2001/04/xmlenc#rsa-1_5\0"); asymEncryAlg->getRemotePlainTextBlockSize = UA_AsymEn_Basic128Rsa15_getRemotePlainTextBlockSize; asymEncryAlg->getRemoteBlockSize = UA_AsymEn_Basic128Rsa15_getRemoteBlockSize; asymEncryAlg->getRemoteKeyLength = UA_AsymEn_Basic128Rsa15_getRemoteKeyLength; asymEncryAlg->getLocalKeyLength = UA_AsymEn_Basic128Rsa15_getLocalKeyLength; asymEncryAlg->decrypt = UA_AsymEn_Basic128Rsa15_Decrypt; asymEncryAlg->encrypt = UA_AsymEn_Basic128Rsa15_Encrypt; /* SymmetricModule */ symmetricModule->secureChannelNonceLength = 16; /* 128 bits*/ symmetricModule->generateNonce = UA_Sym_Basic128Rsa15_generateNonce; symmetricModule->generateKey = UA_Sym_Basic128Rsa15_generateKey; /* Symmetric encryption Algorithm */ UA_SecurityPolicyEncryptionAlgorithm * symEncryptionAlgorithm = &symmetricModule->cryptoModule.encryptionAlgorithm; symEncryptionAlgorithm->uri = UA_STRING("http://www.w3.org/2001/04/xmlenc#aes128-cbc\0"); symEncryptionAlgorithm->getLocalKeyLength = UA_SymEn_Basic128Rsa15_getLocalKeyLength; symEncryptionAlgorithm->getRemoteKeyLength = UA_SymEn_Basic128Rsa15_getRemoteKeyLength; symEncryptionAlgorithm->getRemoteBlockSize = UA_SymEn_Basic128Rsa15_getBlockSize; symEncryptionAlgorithm->getRemotePlainTextBlockSize = UA_SymEn_Basic128Rsa15_getBlockSize; symEncryptionAlgorithm->decrypt = UA_SymEn_Basic128Rsa15_Decrypt; symEncryptionAlgorithm->encrypt = UA_SymEn_Basic128Rsa15_Encrypt; /* Symmetric signature Algorithm */ UA_SecurityPolicySignatureAlgorithm * symSignatureAlgorithm = &symmetricModule->cryptoModule.signatureAlgorithm; symSignatureAlgorithm->uri = UA_STRING("http://www.w3.org/2000/09/xmldsig#hmac-sha1\0"); symSignatureAlgorithm->getLocalKeyLength = UA_SymSig_Basic128Rsa15_getKeyLength; symSignatureAlgorithm->getRemoteKeyLength = UA_SymSig_Basic128Rsa15_getKeyLength; symSignatureAlgorithm->getRemoteSignatureSize = UA_SymSig_Basic128Rsa15_getSignatureSize; symSignatureAlgorithm->getLocalSignatureSize = UA_SymSig_Basic128Rsa15_getSignatureSize; symSignatureAlgorithm->verify = UA_SymSig_Basic128Rsa15_Verify; symSignatureAlgorithm->sign = UA_SymSig_Basic128Rsa15_Sign; /* set the policy context */ retval = UA_Policy_Basic128Rsa15_New_Context (policy, localPrivateKey, logger); if (retval != UA_STATUSCODE_GOOD) { UA_ByteString_clear (&policy->localCertificate); return retval; } policy->clear = UA_Policy_Basic128Rsa15_Clear_Context; /* Use the same signature algorithm as the asymmetric component for certificate signing (see standard) */ policy->certificateSigningAlgorithm = policy->asymmetricModule.cryptoModule.signatureAlgorithm; return UA_STATUSCODE_GOOD; } #endif /**** amalgamated original file "/plugins/crypto/openssl/ua_openssl_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 2020 (c) Wind River Systems, Inc. * Copyright 2020 (c) basysKom GmbH */ #if defined(UA_ENABLE_ENCRYPTION_OPENSSL) || defined(UA_ENABLE_ENCRYPTION_LIBRESSL) #include #include #define UA_SECURITYPOLICY_BASIC256SHA1_RSAPADDING_LEN 42 #define UA_SECURITYPOLICY_BASIC256_SYM_ENCRYPTION_KEY_LENGTH 32 #define UA_SECURITYPOLICY_BASIC256_SYM_ENCRYPTION_BLOCK_SIZE 16 #define UA_SECURITYPOLICY_BASIC256_SYM_SIGNING_KEY_LENGTH 24 #define UA_SHA1_LENGTH 20 typedef struct { EVP_PKEY * localPrivateKey; UA_ByteString localCertThumbprint; const UA_Logger * logger; } Policy_Context_Basic256; typedef struct { UA_ByteString localSymSigningKey; UA_ByteString localSymEncryptingKey; UA_ByteString localSymIv; UA_ByteString remoteSymSigningKey; UA_ByteString remoteSymEncryptingKey; UA_ByteString remoteSymIv; Policy_Context_Basic256 * policyContext; UA_ByteString remoteCertificate; X509 * remoteCertificateX509; } Channel_Context_Basic256; static UA_StatusCode UA_Policy_Basic256_New_Context (UA_SecurityPolicy * securityPolicy, const UA_ByteString localPrivateKey, const UA_Logger * logger) { Policy_Context_Basic256 * context = (Policy_Context_Basic256 *) UA_malloc (sizeof (Policy_Context_Basic256)); if (context == NULL) { return UA_STATUSCODE_BADOUTOFMEMORY; } context->localPrivateKey = UA_OpenSSL_LoadPrivateKey(&localPrivateKey); if (!context->localPrivateKey) { UA_free (context); return UA_STATUSCODE_BADINVALIDARGUMENT; } UA_StatusCode retval = UA_Openssl_X509_GetCertificateThumbprint ( &securityPolicy->localCertificate, &context->localCertThumbprint, true ); if (retval != UA_STATUSCODE_GOOD) { EVP_PKEY_free(context->localPrivateKey); UA_free (context); return retval; } context->logger = logger; securityPolicy->policyContext = context; return UA_STATUSCODE_GOOD; } static void UA_Policy_Basic256_Clear_Context (UA_SecurityPolicy *policy) { if (policy == NULL) { return; } UA_ByteString_clear(&policy->localCertificate); Policy_Context_Basic256 * ctx = (Policy_Context_Basic256 *) policy->policyContext; if (ctx == NULL) { return; } /* delete all allocated members in the context */ EVP_PKEY_free(ctx->localPrivateKey); UA_ByteString_clear(&ctx->localCertThumbprint); UA_free (ctx); return; } /* create the channel context */ static UA_StatusCode UA_ChannelModule_Basic256_New_Context (const UA_SecurityPolicy * securityPolicy, const UA_ByteString * remoteCertificate, void ** channelContext) { if (securityPolicy == NULL || remoteCertificate == NULL || channelContext == NULL) { return UA_STATUSCODE_BADINTERNALERROR; } Channel_Context_Basic256 * context = (Channel_Context_Basic256 *) UA_malloc (sizeof (Channel_Context_Basic256)); if (context == NULL) { return UA_STATUSCODE_BADOUTOFMEMORY; } UA_ByteString_init(&context->localSymSigningKey); UA_ByteString_init(&context->localSymEncryptingKey); UA_ByteString_init(&context->localSymIv); UA_ByteString_init(&context->remoteSymSigningKey); UA_ByteString_init(&context->remoteSymEncryptingKey); UA_ByteString_init(&context->remoteSymIv); UA_StatusCode retval = UA_copyCertificate (&context->remoteCertificate, remoteCertificate); if (retval != UA_STATUSCODE_GOOD) { UA_free (context); return retval; } /* decode to X509 */ context->remoteCertificateX509 = UA_OpenSSL_LoadCertificate(&context->remoteCertificate); if (context->remoteCertificateX509 == NULL) { UA_ByteString_clear (&context->remoteCertificate); UA_free (context); return UA_STATUSCODE_BADCERTIFICATECHAININCOMPLETE; } context->policyContext = (Policy_Context_Basic256 *) (securityPolicy->policyContext); *channelContext = context; UA_LOG_INFO (securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "The basic256 security policy channel with openssl is created."); return UA_STATUSCODE_GOOD; } /* delete the channel context */ static void UA_ChannelModule_Basic256_Delete_Context (void * channelContext) { if (channelContext != NULL) { Channel_Context_Basic256 * cc = (Channel_Context_Basic256 *) channelContext; X509_free (cc->remoteCertificateX509); UA_ByteString_clear (&cc->remoteCertificate); UA_ByteString_clear (&cc->localSymSigningKey); UA_ByteString_clear (&cc->localSymEncryptingKey); UA_ByteString_clear (&cc->localSymIv); UA_ByteString_clear (&cc->remoteSymSigningKey); UA_ByteString_clear (&cc->remoteSymEncryptingKey); UA_ByteString_clear (&cc->remoteSymIv); UA_LOG_INFO (cc->policyContext->logger, UA_LOGCATEGORY_SECURITYPOLICY, "The basic256 security policy channel with openssl is deleted."); UA_free (cc); } } /* Compares the supplied certificate with the certificate * in the endpoint context */ static UA_StatusCode UA_Asy_Basic256_compareCertificateThumbprint (const UA_SecurityPolicy * securityPolicy, const UA_ByteString * certificateThumbprint) { if (securityPolicy == NULL || certificateThumbprint == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } Policy_Context_Basic256 *pc = (Policy_Context_Basic256 *) securityPolicy->policyContext; if(!UA_ByteString_equal(certificateThumbprint, &pc->localCertThumbprint)) { return UA_STATUSCODE_BADCERTIFICATEINVALID; } return UA_STATUSCODE_GOOD; } /* Generates a thumbprint for the specified certificate */ static UA_StatusCode UA_Asy_Basic256_makeCertificateThumbprint (const UA_SecurityPolicy * securityPolicy, const UA_ByteString * certificate, UA_ByteString * thumbprint) { return UA_Openssl_X509_GetCertificateThumbprint (certificate, thumbprint, false); } static UA_StatusCode UA_ChannelModule_Basic256_setLocalSymSigningKey (void * channelContext, const UA_ByteString * key) { if (key == NULL || channelContext == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } Channel_Context_Basic256 * cc = (Channel_Context_Basic256 *) channelContext; UA_ByteString_clear(&cc->localSymSigningKey); return UA_ByteString_copy(key, &cc->localSymSigningKey); } static UA_StatusCode UA_ChannelModule_Basic256_setLocalSymEncryptingKey (void * channelContext, const UA_ByteString * key) { if (key == NULL || channelContext == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } Channel_Context_Basic256 * cc = (Channel_Context_Basic256 *) channelContext; UA_ByteString_clear(&cc->localSymEncryptingKey); return UA_ByteString_copy(key, &cc->localSymEncryptingKey); } static UA_StatusCode UA_ChannelModule_Basic256_setLocalSymIv (void * channelContext, const UA_ByteString * iv) { if (iv == NULL || channelContext == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } Channel_Context_Basic256 * cc = (Channel_Context_Basic256 *) channelContext; UA_ByteString_clear(&cc->localSymIv); return UA_ByteString_copy(iv, &cc->localSymIv); } static UA_StatusCode UA_ChannelModule_Basic256_setRemoteSymSigningKey (void * channelContext, const UA_ByteString * key) { if (key == NULL || channelContext == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } Channel_Context_Basic256 * cc = (Channel_Context_Basic256 *) channelContext; UA_ByteString_clear(&cc->remoteSymSigningKey); return UA_ByteString_copy(key, &cc->remoteSymSigningKey); } static UA_StatusCode UA_ChannelModule_Basic256_setRemoteSymEncryptingKey (void * channelContext, const UA_ByteString * key) { if (key == NULL || channelContext == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } Channel_Context_Basic256 * cc = (Channel_Context_Basic256 *) channelContext; UA_ByteString_clear(&cc->remoteSymEncryptingKey); return UA_ByteString_copy(key, &cc->remoteSymEncryptingKey); } static UA_StatusCode UA_ChannelModule_Basic256_setRemoteSymIv (void * channelContext, const UA_ByteString * key) { if (key == NULL || channelContext == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } Channel_Context_Basic256 * cc = (Channel_Context_Basic256 *) channelContext; UA_ByteString_clear(&cc->remoteSymIv); return UA_ByteString_copy(key, &cc->remoteSymIv); } static UA_StatusCode UA_ChannelModule_Basic256_compareCertificate (const void * channelContext, const UA_ByteString * certificate) { if(channelContext == NULL || certificate == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } const Channel_Context_Basic256 * cc = (const Channel_Context_Basic256 *) channelContext; return UA_OpenSSL_X509_compare (certificate, cc->remoteCertificateX509); } static size_t UA_AsySig_Basic256_getRemoteSignatureSize (const void *channelContext) { if (channelContext == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } const Channel_Context_Basic256 * cc = (const Channel_Context_Basic256 *) channelContext; UA_Int32 keyLen = 0; UA_Openssl_RSA_Public_GetKeyLength (cc->remoteCertificateX509, &keyLen); return (size_t) keyLen; } static size_t UA_AsySig_Basic256_getLocalSignatureSize (const void *channelContext) { if (channelContext == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } const Channel_Context_Basic256 * cc = (const Channel_Context_Basic256 *) channelContext; Policy_Context_Basic256 * pc = cc->policyContext; UA_Int32 keyLen = 0; UA_Openssl_RSA_Private_GetKeyLength (pc->localPrivateKey, &keyLen); return (size_t) keyLen; } static UA_StatusCode UA_AsySig_Basic256_Verify (void * channelContext, const UA_ByteString * message, const UA_ByteString * signature) { if (message == NULL || signature == NULL || channelContext == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } Channel_Context_Basic256 * cc = (Channel_Context_Basic256 *) channelContext; UA_StatusCode retval = UA_OpenSSL_RSA_PKCS1_V15_SHA1_Verify (message, cc->remoteCertificateX509, signature); return retval; } static UA_StatusCode UA_AsySig_Basic256_Sign (void * channelContext, const UA_ByteString * message, UA_ByteString * signature) { if (channelContext == NULL || message == NULL || signature == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } Channel_Context_Basic256 * cc = (Channel_Context_Basic256 *) channelContext; Policy_Context_Basic256 * pc = cc->policyContext; return UA_Openssl_RSA_PKCS1_V15_SHA1_Sign (message, pc->localPrivateKey, signature); } static size_t UA_AsymEn_Basic256_getRemotePlainTextBlockSize (const void *channelContext) { if (channelContext == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } const Channel_Context_Basic256 * cc = (const Channel_Context_Basic256 *) channelContext; UA_Int32 keyLen = 0; UA_Openssl_RSA_Public_GetKeyLength (cc->remoteCertificateX509, &keyLen); return (size_t) keyLen - UA_SECURITYPOLICY_BASIC256SHA1_RSAPADDING_LEN; } static size_t UA_AsymEn_Basic256_getRemoteBlockSize (const void *channelContext) { if (channelContext == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } const Channel_Context_Basic256 * cc = (const Channel_Context_Basic256 *) channelContext; UA_Int32 keyLen = 0; UA_Openssl_RSA_Public_GetKeyLength (cc->remoteCertificateX509, &keyLen); return (size_t) keyLen; } static size_t UA_AsymEn_Basic256_getRemoteKeyLength (const void *channelContext) { if (channelContext == NULL) return UA_STATUSCODE_BADINVALIDARGUMENT; const Channel_Context_Basic256 * cc = (const Channel_Context_Basic256 *) channelContext; UA_Int32 keyLen = 0; UA_Openssl_RSA_Public_GetKeyLength (cc->remoteCertificateX509, &keyLen); return (size_t) keyLen * 8; } static size_t UA_AsymEn_Basic256_getLocalKeyLength (const void *channelContext) { if (channelContext == NULL) return UA_STATUSCODE_BADINVALIDARGUMENT; const Channel_Context_Basic256 *cc = (const Channel_Context_Basic256 *) channelContext; Policy_Context_Basic256 *pc = cc->policyContext; UA_Int32 keyLen = 0; UA_Openssl_RSA_Private_GetKeyLength (pc->localPrivateKey, &keyLen); return (size_t) keyLen * 8; } static UA_StatusCode UA_AsymEn_Basic256_Decrypt (void * channelContext, UA_ByteString * data) { if (channelContext == NULL || data == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } Channel_Context_Basic256 * cc = (Channel_Context_Basic256 *) channelContext; UA_StatusCode ret = UA_Openssl_RSA_Oaep_Decrypt (data, cc->policyContext->localPrivateKey); return ret; } static UA_StatusCode UA_AsymEn_Basic256_Encrypt (void * channelContext, UA_ByteString * data) { if (channelContext == NULL || data == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } Channel_Context_Basic256 * cc = (Channel_Context_Basic256 *) channelContext; return UA_Openssl_RSA_OAEP_Encrypt (data, UA_SECURITYPOLICY_BASIC256SHA1_RSAPADDING_LEN, cc->remoteCertificateX509); } static UA_StatusCode UA_Sym_Basic256_generateNonce(void *policyContext, UA_ByteString *out) { UA_Int32 rc = RAND_bytes(out->data, (int) out->length); if (rc != 1) { return UA_STATUSCODE_BADUNEXPECTEDERROR; } return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_Sym_Basic256_generateKey(void *policyContext, const UA_ByteString *secret, const UA_ByteString *seed, UA_ByteString *out) { return UA_Openssl_Random_Key_PSHA1_Derive(secret, seed, out); } static size_t UA_SymEn_Basic256_getLocalKeyLength (const void * channelContext) { /* 32 bytes 256 bits */ return UA_SECURITYPOLICY_BASIC256_SYM_ENCRYPTION_KEY_LENGTH; } static size_t UA_SymEn_Basic256_getBlockSize (const void * channelContext) { return UA_SECURITYPOLICY_BASIC256_SYM_ENCRYPTION_BLOCK_SIZE; } static size_t UA_SymEn_Basic256_getRemoteKeyLength (const void * channelContext) { /* 32 bytes 256 bits */ return UA_SECURITYPOLICY_BASIC256_SYM_ENCRYPTION_KEY_LENGTH; } static UA_StatusCode UA_SymEn_Basic256_Encrypt (void * channelContext, UA_ByteString * data) { if(channelContext == NULL || data == NULL) return UA_STATUSCODE_BADINVALIDARGUMENT; Channel_Context_Basic256 * cc = (Channel_Context_Basic256 *) channelContext; return UA_OpenSSL_AES_256_CBC_Encrypt (&cc->localSymIv, &cc->localSymEncryptingKey, data); } static UA_StatusCode UA_SymEn_Basic256_Decrypt (void * channelContext, UA_ByteString * data) { if(channelContext == NULL || data == NULL) return UA_STATUSCODE_BADINVALIDARGUMENT; Channel_Context_Basic256 * cc = (Channel_Context_Basic256 *) channelContext; return UA_OpenSSL_AES_256_CBC_Decrypt (&cc->remoteSymIv, &cc->remoteSymEncryptingKey, data); } static size_t UA_SymSig_Basic256_getKeyLength (const void * channelContext) { return UA_SECURITYPOLICY_BASIC256_SYM_SIGNING_KEY_LENGTH; } static size_t UA_SymSig_Basic256_getSignatureSize (const void * channelContext) { return UA_SHA1_LENGTH; } static UA_StatusCode UA_SymSig_Basic256_Verify (void * channelContext, const UA_ByteString * message, const UA_ByteString * signature) { if (channelContext == NULL || message == NULL || signature == NULL) return UA_STATUSCODE_BADINVALIDARGUMENT; Channel_Context_Basic256 * cc = (Channel_Context_Basic256 *) channelContext; return UA_OpenSSL_HMAC_SHA1_Verify (message, &cc->remoteSymSigningKey, signature); } static UA_StatusCode UA_SymSig_Basic256_Sign (void * channelContext, const UA_ByteString * message, UA_ByteString * signature) { if (channelContext == NULL || message == NULL || signature == NULL) return UA_STATUSCODE_BADINVALIDARGUMENT; Channel_Context_Basic256 * cc = (Channel_Context_Basic256 *) channelContext; return UA_OpenSSL_HMAC_SHA1_Sign (message, &cc->localSymSigningKey, signature); } /* the main entry of Basic256 */ UA_StatusCode UA_SecurityPolicy_Basic256 (UA_SecurityPolicy * policy, const UA_ByteString localCertificate, const UA_ByteString localPrivateKey, const UA_Logger * logger) { UA_SecurityPolicyAsymmetricModule * const asymmetricModule = &policy->asymmetricModule; UA_SecurityPolicySymmetricModule * const symmetricModule = &policy->symmetricModule; UA_SecurityPolicyChannelModule * const channelModule = &policy->channelModule; UA_StatusCode retval; UA_LOG_INFO (logger, UA_LOGCATEGORY_SECURITYPOLICY, "The basic256 security policy with openssl is added."); UA_Openssl_Init (); memset(policy, 0, sizeof(UA_SecurityPolicy)); policy->logger = logger; policy->policyUri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Basic256\0"); /* set ChannelModule context */ channelModule->newContext = UA_ChannelModule_Basic256_New_Context; channelModule->deleteContext = UA_ChannelModule_Basic256_Delete_Context; channelModule->setLocalSymSigningKey = UA_ChannelModule_Basic256_setLocalSymSigningKey; channelModule->setLocalSymEncryptingKey = UA_ChannelModule_Basic256_setLocalSymEncryptingKey; channelModule->setLocalSymIv = UA_ChannelModule_Basic256_setLocalSymIv; channelModule->setRemoteSymSigningKey = UA_ChannelModule_Basic256_setRemoteSymSigningKey; channelModule->setRemoteSymEncryptingKey = UA_ChannelModule_Basic256_setRemoteSymEncryptingKey; channelModule->setRemoteSymIv = UA_ChannelModule_Basic256_setRemoteSymIv; channelModule->compareCertificate = UA_ChannelModule_Basic256_compareCertificate; retval = UA_OpenSSL_LoadLocalCertificate(&localCertificate, &policy->localCertificate); if (retval != UA_STATUSCODE_GOOD) return retval; /* asymmetricModule */ asymmetricModule->compareCertificateThumbprint = UA_Asy_Basic256_compareCertificateThumbprint; asymmetricModule->makeCertificateThumbprint = UA_Asy_Basic256_makeCertificateThumbprint; /* AsymmetricModule - signature algorithm */ UA_SecurityPolicySignatureAlgorithm * asySigAlgorithm = &asymmetricModule->cryptoModule.signatureAlgorithm; asySigAlgorithm->uri = UA_STRING("http://www.w3.org/2000/09/xmldsig#rsa-sha1\0"); asySigAlgorithm->getRemoteSignatureSize = UA_AsySig_Basic256_getRemoteSignatureSize; asySigAlgorithm->getLocalSignatureSize = UA_AsySig_Basic256_getLocalSignatureSize; asySigAlgorithm->verify = UA_AsySig_Basic256_Verify; asySigAlgorithm->sign = UA_AsySig_Basic256_Sign; asySigAlgorithm->getLocalKeyLength = NULL; asySigAlgorithm->getRemoteKeyLength = NULL; /* AsymmetricModule encryption algorithm */ UA_SecurityPolicyEncryptionAlgorithm * asymEncryAlg = &asymmetricModule->cryptoModule.encryptionAlgorithm; asymEncryAlg->uri = UA_STRING("http://www.w3.org/2001/04/xmlenc#rsa-oaep\0"); asymEncryAlg->getRemotePlainTextBlockSize = UA_AsymEn_Basic256_getRemotePlainTextBlockSize; asymEncryAlg->getRemoteBlockSize = UA_AsymEn_Basic256_getRemoteBlockSize; asymEncryAlg->getRemoteKeyLength = UA_AsymEn_Basic256_getRemoteKeyLength; asymEncryAlg->getLocalKeyLength = UA_AsymEn_Basic256_getLocalKeyLength; asymEncryAlg->decrypt = UA_AsymEn_Basic256_Decrypt; asymEncryAlg->encrypt = UA_AsymEn_Basic256_Encrypt; /* SymmetricModule */ symmetricModule->secureChannelNonceLength = 32; symmetricModule->generateNonce = UA_Sym_Basic256_generateNonce; symmetricModule->generateKey = UA_Sym_Basic256_generateKey; /* Symmetric encryption Algorithm */ UA_SecurityPolicyEncryptionAlgorithm * symEncryptionAlgorithm = &symmetricModule->cryptoModule.encryptionAlgorithm; symEncryptionAlgorithm->uri = UA_STRING("http://www.w3.org/2001/04/xmlenc#aes256-cbc\0"); symEncryptionAlgorithm->getLocalKeyLength = UA_SymEn_Basic256_getLocalKeyLength; symEncryptionAlgorithm->getRemoteKeyLength = UA_SymEn_Basic256_getRemoteKeyLength; symEncryptionAlgorithm->getRemoteBlockSize = UA_SymEn_Basic256_getBlockSize; symEncryptionAlgorithm->getRemotePlainTextBlockSize = UA_SymEn_Basic256_getBlockSize; symEncryptionAlgorithm->decrypt = UA_SymEn_Basic256_Decrypt; symEncryptionAlgorithm->encrypt = UA_SymEn_Basic256_Encrypt; /* Symmetric signature Algorithm */ UA_SecurityPolicySignatureAlgorithm * symSignatureAlgorithm = &symmetricModule->cryptoModule.signatureAlgorithm; symSignatureAlgorithm->uri = UA_STRING("http://www.w3.org/2000/09/xmldsig#hmac-sha1\0"); symSignatureAlgorithm->getLocalKeyLength = UA_SymSig_Basic256_getKeyLength; symSignatureAlgorithm->getRemoteKeyLength = UA_SymSig_Basic256_getKeyLength; symSignatureAlgorithm->getRemoteSignatureSize = UA_SymSig_Basic256_getSignatureSize; symSignatureAlgorithm->getLocalSignatureSize = UA_SymSig_Basic256_getSignatureSize; symSignatureAlgorithm->verify = UA_SymSig_Basic256_Verify; symSignatureAlgorithm->sign = UA_SymSig_Basic256_Sign; /* set the policy context */ retval = UA_Policy_Basic256_New_Context (policy, localPrivateKey, logger); if (retval != UA_STATUSCODE_GOOD) { UA_ByteString_clear (&policy->localCertificate); return retval; } policy->clear = UA_Policy_Basic256_Clear_Context; /* Use the same signature algorithm as the asymmetric component for certificate signing (see standard) */ policy->certificateSigningAlgorithm = policy->asymmetricModule.cryptoModule.signatureAlgorithm; return UA_STATUSCODE_GOOD; } #endif /**** amalgamated original file "/plugins/crypto/openssl/ua_openssl_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 2020 (c) Wind River Systems, Inc. * Copyright 2020 (c) basysKom GmbH */ #if defined(UA_ENABLE_ENCRYPTION_OPENSSL) || defined(UA_ENABLE_ENCRYPTION_LIBRESSL) #include #include #include #include #include #include #define UA_SHA256_LENGTH 32 /* 256 bit */ #define UA_SECURITYPOLICY_BASIC256SHA256_RSAPADDING_LEN 42 #define UA_SECURITYPOLICY_BASIC256SHA256_SYM_SIGNING_KEY_LENGTH 32 #define UA_SECURITYPOLICY_BASIC256SHA256_SYM_ENCRYPTION_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 { EVP_PKEY *localPrivateKey; UA_ByteString localCertThumbprint; const UA_Logger *logger; } Policy_Context_Basic256Sha256; typedef struct { UA_ByteString localSymSigningKey; UA_ByteString localSymEncryptingKey; UA_ByteString localSymIv; UA_ByteString remoteSymSigningKey; UA_ByteString remoteSymEncryptingKey; UA_ByteString remoteSymIv; Policy_Context_Basic256Sha256 *policyContext; UA_ByteString remoteCertificate; X509 *remoteCertificateX509; /* X509 */ } Channel_Context_Basic256Sha256; /* create the policy context */ static UA_StatusCode UA_Policy_New_Context(UA_SecurityPolicy * securityPolicy, const UA_ByteString localPrivateKey, const UA_Logger *logger) { Policy_Context_Basic256Sha256 *context = (Policy_Context_Basic256Sha256 *) UA_malloc(sizeof(Policy_Context_Basic256Sha256)); if(context == NULL) return UA_STATUSCODE_BADOUTOFMEMORY; context->localPrivateKey = UA_OpenSSL_LoadPrivateKey(&localPrivateKey); if(!context->localPrivateKey) { UA_free(context); return UA_STATUSCODE_BADINVALIDARGUMENT; } UA_StatusCode retval = UA_Openssl_X509_GetCertificateThumbprint(&securityPolicy->localCertificate, &context->localCertThumbprint, true); if(retval != UA_STATUSCODE_GOOD) { EVP_PKEY_free(context->localPrivateKey); UA_free(context); return retval; } context->logger = logger; securityPolicy->policyContext = context; return UA_STATUSCODE_GOOD; } /* Clear the policy context */ static void UA_Policy_Clear_Context(UA_SecurityPolicy *policy) { if(policy == NULL) return; UA_ByteString_clear(&policy->localCertificate); /* Delete all allocated members in the context */ Policy_Context_Basic256Sha256 *pc = (Policy_Context_Basic256Sha256 *) policy->policyContext; EVP_PKEY_free(pc->localPrivateKey); UA_ByteString_clear(&pc->localCertThumbprint); UA_free(pc); return; } /* create the channel context */ static UA_StatusCode UA_ChannelModule_New_Context(const UA_SecurityPolicy * securityPolicy, const UA_ByteString * remoteCertificate, void ** channelContext) { if(securityPolicy == NULL || remoteCertificate == NULL || channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Basic256Sha256 *context = (Channel_Context_Basic256Sha256 *) UA_malloc(sizeof(Channel_Context_Basic256Sha256)); if(context == NULL) return UA_STATUSCODE_BADOUTOFMEMORY; UA_ByteString_init(&context->localSymSigningKey); UA_ByteString_init(&context->localSymEncryptingKey); UA_ByteString_init(&context->localSymIv); UA_ByteString_init(&context->remoteSymSigningKey); UA_ByteString_init(&context->remoteSymEncryptingKey); UA_ByteString_init(&context->remoteSymIv); UA_StatusCode retval = UA_copyCertificate(&context->remoteCertificate, remoteCertificate); if(retval != UA_STATUSCODE_GOOD) { UA_free(context); return retval; } /* decode to X509 */ context->remoteCertificateX509 = UA_OpenSSL_LoadCertificate(&context->remoteCertificate); if(context->remoteCertificateX509 == NULL) { UA_ByteString_clear(&context->remoteCertificate); UA_free(context); return UA_STATUSCODE_BADINTERNALERROR; } context->policyContext = (Policy_Context_Basic256Sha256 *)securityPolicy->policyContext; *channelContext = context; UA_LOG_INFO(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "The basic256sha256 security policy channel with openssl is created."); return UA_STATUSCODE_GOOD; } /* delete the channel context */ static void UA_ChannelModule_Delete_Context(void * channelContext) { if(!channelContext) return; Channel_Context_Basic256Sha256 * cc = (Channel_Context_Basic256Sha256 *)channelContext; X509_free(cc->remoteCertificateX509); UA_ByteString_clear(&cc->remoteCertificate); UA_ByteString_clear(&cc->localSymSigningKey); UA_ByteString_clear(&cc->localSymEncryptingKey); UA_ByteString_clear(&cc->localSymIv); UA_ByteString_clear(&cc->remoteSymSigningKey); UA_ByteString_clear(&cc->remoteSymEncryptingKey); UA_ByteString_clear(&cc->remoteSymIv); UA_LOG_INFO(cc->policyContext->logger, UA_LOGCATEGORY_SECURITYPOLICY, "The basic256sha256 security policy channel with openssl is deleted."); UA_free(cc); } /* Verifies the signature of the message using the provided keys in the context. * AsymmetricSignatureAlgorithm_RSA-PKCS15-SHA2-256 */ static UA_StatusCode UA_AsySig_Basic256Sha256_Verify(void *channelContext, const UA_ByteString *message, const UA_ByteString *signature) { if(message == NULL || signature == NULL || channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Basic256Sha256 * cc = (Channel_Context_Basic256Sha256 *) channelContext; return UA_OpenSSL_RSA_PKCS1_V15_SHA256_Verify(message, cc->remoteCertificateX509, signature); } /* Compares the supplied certificate with the certificate * in the endpoint context */ static UA_StatusCode UA_compareCertificateThumbprint(const UA_SecurityPolicy * securityPolicy, const UA_ByteString * certificateThumbprint) { if(securityPolicy == NULL || certificateThumbprint == NULL) return UA_STATUSCODE_BADINVALIDARGUMENT; Policy_Context_Basic256Sha256 *pc = (Policy_Context_Basic256Sha256 *) securityPolicy->policyContext; if(!UA_ByteString_equal(certificateThumbprint, &pc->localCertThumbprint)) return UA_STATUSCODE_BADCERTIFICATEINVALID; return UA_STATUSCODE_GOOD; } /* Generates a thumbprint for the specified certificate */ static UA_StatusCode UA_makeCertificateThumbprint(const UA_SecurityPolicy * securityPolicy, const UA_ByteString * certificate, UA_ByteString * thumbprint) { return UA_Openssl_X509_GetCertificateThumbprint(certificate, thumbprint, false); } static UA_StatusCode UA_Asym_Basic256Sha256_Decrypt(void * channelContext, UA_ByteString * data) { if(channelContext == NULL || data == NULL) return UA_STATUSCODE_BADINVALIDARGUMENT; Channel_Context_Basic256Sha256 * cc = (Channel_Context_Basic256Sha256 *) channelContext; return UA_Openssl_RSA_Oaep_Decrypt(data, cc->policyContext->localPrivateKey); } static size_t UA_Asym_Basic256Sha256_getRemoteSignatureSize(const void *channelContext) { if(channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; const Channel_Context_Basic256Sha256 * cc = (const Channel_Context_Basic256Sha256 *) channelContext; UA_Int32 keyLen = 0; UA_Openssl_RSA_Public_GetKeyLength(cc->remoteCertificateX509, &keyLen); return (size_t) keyLen; } static size_t UA_AsySig_Basic256Sha256_getLocalSignatureSize(const void *channelContext) { if(channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; const Channel_Context_Basic256Sha256 *cc = (const Channel_Context_Basic256Sha256 *) channelContext; Policy_Context_Basic256Sha256 * pc = cc->policyContext; UA_Int32 keyLen = 0; UA_Openssl_RSA_Private_GetKeyLength(pc->localPrivateKey, &keyLen); return (size_t) keyLen; } static size_t UA_AsymEn_Basic256Sha256_getRemotePlainTextBlockSize(const void *channelContext) { if(channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; const Channel_Context_Basic256Sha256 *cc = (const Channel_Context_Basic256Sha256 *) channelContext; UA_Int32 keyLen = 0; UA_Openssl_RSA_Public_GetKeyLength(cc->remoteCertificateX509, &keyLen); return (size_t) keyLen - UA_SECURITYPOLICY_BASIC256SHA256_RSAPADDING_LEN; } static size_t UA_AsymEn_Basic256Sha256_getRemoteBlockSize(const void *channelContext) { if(channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; const Channel_Context_Basic256Sha256 * cc = (const Channel_Context_Basic256Sha256 *) channelContext; UA_Int32 keyLen = 0; UA_Openssl_RSA_Public_GetKeyLength(cc->remoteCertificateX509, &keyLen); return (size_t) keyLen; } static size_t UA_AsymEn_Basic256Sha256_getRemoteKeyLength(const void *channelContext) { if(channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; const Channel_Context_Basic256Sha256 *cc = (const Channel_Context_Basic256Sha256 *) channelContext; UA_Int32 keyLen = 0; UA_Openssl_RSA_Public_GetKeyLength(cc->remoteCertificateX509, &keyLen); return (size_t) keyLen * 8; } static UA_StatusCode UA_Sym_Basic256Sha256_generateNonce(void *policyContext, UA_ByteString *out) { UA_Int32 rc = RAND_bytes(out->data, (int) out->length); if(rc != 1) return UA_STATUSCODE_BADUNEXPECTEDERROR; return UA_STATUSCODE_GOOD; } static size_t UA_SymEn_Basic256Sha256_getLocalKeyLength(const void *channelContext) { /* 32 bytes 256 bits */ return UA_SECURITYPOLICY_BASIC256SHA256_SYM_ENCRYPTION_KEY_LENGTH; } static size_t UA_SymSig_Basic256Sha256_getLocalKeyLength(const void *channelContext) { /* 32 bytes 256 bits */ return UA_SECURITYPOLICY_BASIC256SHA256_SYM_SIGNING_KEY_LENGTH; } static UA_StatusCode UA_Sym_Basic256Sha256_generateKey(void *policyContext, const UA_ByteString *secret, const UA_ByteString *seed, UA_ByteString *out) { return UA_Openssl_Random_Key_PSHA256_Derive(secret, seed, out); } static UA_StatusCode UA_ChannelModule_Basic256Sha256_setLocalSymSigningKey(void * channelContext, const UA_ByteString * key) { if(key == NULL || channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Basic256Sha256 * cc = (Channel_Context_Basic256Sha256 *) channelContext; UA_ByteString_clear(&cc->localSymSigningKey); return UA_ByteString_copy(key, &cc->localSymSigningKey); } static UA_StatusCode UA_ChannelM_Basic256Sha256_setLocalSymEncryptingKey(void * channelContext, const UA_ByteString * key) { if(key == NULL || channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Basic256Sha256 * cc = (Channel_Context_Basic256Sha256 *) channelContext; UA_ByteString_clear(&cc->localSymEncryptingKey); return UA_ByteString_copy(key, &cc->localSymEncryptingKey); } static UA_StatusCode UA_ChannelM_Basic256Sha256_setLocalSymIv(void * channelContext, const UA_ByteString * iv) { if(iv == NULL || channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Basic256Sha256 * cc = (Channel_Context_Basic256Sha256 *) channelContext; UA_ByteString_clear(&cc->localSymIv); return UA_ByteString_copy(iv, &cc->localSymIv); } static size_t UA_SymEn_Basic256Sha256_getRemoteKeyLength(const void * channelContext) { /* 32 bytes 256 bits */ return UA_SECURITYPOLICY_BASIC256SHA256_SYM_ENCRYPTION_KEY_LENGTH; } static size_t UA_SymEn_Basic256Sha256_getBlockSize(const void *channelContext) { return UA_SECURITYPOLICY_BASIC256SHA256_SYM_ENCRYPTION_BLOCK_SIZE; } static size_t UA_SymSig_Basic256Sha256_getRemoteKeyLength(const void * channelContext) { /* 32 bytes 256 bits */ return UA_SECURITYPOLICY_BASIC256SHA256_SYM_SIGNING_KEY_LENGTH; } static UA_StatusCode UA_ChannelM_Basic256Sha256_setRemoteSymSigningKey(void *channelContext, const UA_ByteString * key) { if(key == NULL || channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Basic256Sha256 * cc = (Channel_Context_Basic256Sha256 *) channelContext; UA_ByteString_clear(&cc->remoteSymSigningKey); return UA_ByteString_copy(key, &cc->remoteSymSigningKey); } static UA_StatusCode UA_ChannelM_Basic256Sha256_setRemoteSymEncryptingKey(void *channelContext, const UA_ByteString * key) { if(key == NULL || channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Basic256Sha256 * cc = (Channel_Context_Basic256Sha256 *) channelContext; UA_ByteString_clear(&cc->remoteSymEncryptingKey); return UA_ByteString_copy(key, &cc->remoteSymEncryptingKey); } static UA_StatusCode UA_ChannelM_Basic256Sha256_setRemoteSymIv(void *channelContext, const UA_ByteString * key) { if(key == NULL || channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Basic256Sha256 * cc = (Channel_Context_Basic256Sha256 *) channelContext; UA_ByteString_clear(&cc->remoteSymIv); return UA_ByteString_copy(key, &cc->remoteSymIv); } static UA_StatusCode UA_AsySig_Basic256Sha256_sign(void *channelContext, const UA_ByteString * message, UA_ByteString *signature) { if(channelContext == NULL || message == NULL || signature == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Basic256Sha256 *cc = (Channel_Context_Basic256Sha256 *) channelContext; Policy_Context_Basic256Sha256 *pc = cc->policyContext; return UA_Openssl_RSA_PKCS1_V15_SHA256_Sign(message, pc->localPrivateKey, signature); } static UA_StatusCode UA_AsymEn_Basic256Sha256_encrypt(void *channelContext, UA_ByteString *data) { if(channelContext == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Basic256Sha256 * cc = (Channel_Context_Basic256Sha256 *) channelContext; return UA_Openssl_RSA_OAEP_Encrypt(data, UA_SECURITYPOLICY_BASIC256SHA256_RSAPADDING_LEN, cc->remoteCertificateX509); } static size_t UA_SymSig_Basic256Sha256_getRemoteSignatureSize(const void *channelContext) { return UA_SHA256_LENGTH; } static UA_StatusCode UA_SymSig_Basic256Sha256_verify(void *channelContext, const UA_ByteString *message, const UA_ByteString *signature) { if(channelContext == NULL || message == NULL || signature == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Basic256Sha256 * cc = (Channel_Context_Basic256Sha256 *) channelContext; return UA_OpenSSL_HMAC_SHA256_Verify(message, &cc->remoteSymSigningKey, signature); } static UA_StatusCode UA_SymSig_Basic256Sha256_sign(void *channelContext, const UA_ByteString *message, UA_ByteString *signature) { if(channelContext == NULL || message == NULL || signature == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Basic256Sha256 * cc = (Channel_Context_Basic256Sha256 *) channelContext; return UA_OpenSSL_HMAC_SHA256_Sign(message, &cc->localSymSigningKey, signature); } static size_t UA_SymSig_Basic256Sha256_getLocalSignatureSize(const void * channelContext) { return UA_SHA256_LENGTH; } static UA_StatusCode UA_SymEn_Basic256Sha256_decrypt(void *channelContext, UA_ByteString *data) { if(channelContext == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Basic256Sha256 * cc = (Channel_Context_Basic256Sha256 *) channelContext; return UA_OpenSSL_AES_256_CBC_Decrypt(&cc->remoteSymIv, &cc->remoteSymEncryptingKey, data); } static UA_StatusCode UA_SymEn_Basic256Sha256_encrypt(void *channelContext, UA_ByteString *data) { if(channelContext == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Basic256Sha256 * cc = (Channel_Context_Basic256Sha256 *) channelContext; return UA_OpenSSL_AES_256_CBC_Encrypt(&cc->localSymIv, &cc->localSymEncryptingKey, data); } static UA_StatusCode UA_ChannelM_Basic256Sha256_compareCertificate(const void *channelContext, const UA_ByteString *certificate) { if(channelContext == NULL || certificate == NULL) return UA_STATUSCODE_BADINTERNALERROR; const Channel_Context_Basic256Sha256 * cc = (const Channel_Context_Basic256Sha256 *) channelContext; return UA_OpenSSL_X509_compare(certificate, cc->remoteCertificateX509); } static size_t UA_AsymEn_Basic256Sha256_getLocalKeyLength(const void *channelContext) { if(channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; const Channel_Context_Basic256Sha256 * cc = (const Channel_Context_Basic256Sha256 *) channelContext; Policy_Context_Basic256Sha256 *pc = cc->policyContext; UA_Int32 keyLen = 0; UA_Openssl_RSA_Private_GetKeyLength(pc->localPrivateKey, &keyLen); return (size_t) keyLen * 8; } /* the main entry of Basic256Sha256 */ UA_StatusCode UA_SecurityPolicy_Basic256Sha256(UA_SecurityPolicy *policy, const UA_ByteString localCertificate, const UA_ByteString localPrivateKey, const UA_Logger *logger) { UA_SecurityPolicyAsymmetricModule *asymmetricModule = &policy->asymmetricModule; UA_SecurityPolicySymmetricModule *symmetricModule = &policy->symmetricModule; UA_SecurityPolicyChannelModule *channelModule = &policy->channelModule; UA_LOG_INFO(logger, UA_LOGCATEGORY_SECURITYPOLICY, "The basic256sha256 security policy with openssl is added."); UA_Openssl_Init(); memset(policy, 0, sizeof(UA_SecurityPolicy)); policy->logger = logger; policy->policyUri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256\0"); /* Set ChannelModule context */ channelModule->newContext = UA_ChannelModule_New_Context; channelModule->deleteContext = UA_ChannelModule_Delete_Context; channelModule->setLocalSymSigningKey = UA_ChannelModule_Basic256Sha256_setLocalSymSigningKey; channelModule->setLocalSymEncryptingKey = UA_ChannelM_Basic256Sha256_setLocalSymEncryptingKey; channelModule->setLocalSymIv = UA_ChannelM_Basic256Sha256_setLocalSymIv; channelModule->setRemoteSymSigningKey = UA_ChannelM_Basic256Sha256_setRemoteSymSigningKey; channelModule->setRemoteSymEncryptingKey = UA_ChannelM_Basic256Sha256_setRemoteSymEncryptingKey; channelModule->setRemoteSymIv = UA_ChannelM_Basic256Sha256_setRemoteSymIv; channelModule->compareCertificate = UA_ChannelM_Basic256Sha256_compareCertificate; UA_StatusCode retval = UA_OpenSSL_LoadLocalCertificate(&localCertificate, &policy->localCertificate); if(retval != UA_STATUSCODE_GOOD) return retval; /* AsymmetricModule - signature algorithm */ UA_SecurityPolicySignatureAlgorithm *asySigAlgorithm = &asymmetricModule->cryptoModule.signatureAlgorithm; asySigAlgorithm->uri = UA_STRING("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\0"); asySigAlgorithm->verify = UA_AsySig_Basic256Sha256_Verify; asySigAlgorithm->sign = UA_AsySig_Basic256Sha256_sign; asySigAlgorithm->getLocalSignatureSize = UA_AsySig_Basic256Sha256_getLocalSignatureSize; asySigAlgorithm->getRemoteSignatureSize = UA_Asym_Basic256Sha256_getRemoteSignatureSize; asySigAlgorithm->getLocalKeyLength = NULL; asySigAlgorithm->getRemoteKeyLength = NULL; /* AsymmetricModule encryption algorithm */ UA_SecurityPolicyEncryptionAlgorithm *asymEncryAlg = &asymmetricModule->cryptoModule.encryptionAlgorithm; asymEncryAlg->uri = UA_STRING("http://www.w3.org/2001/04/xmlenc#rsa-oaep\0"); asymEncryAlg->encrypt = UA_AsymEn_Basic256Sha256_encrypt; asymEncryAlg->decrypt = UA_Asym_Basic256Sha256_Decrypt; asymEncryAlg->getLocalKeyLength = UA_AsymEn_Basic256Sha256_getLocalKeyLength; asymEncryAlg->getRemoteKeyLength = UA_AsymEn_Basic256Sha256_getRemoteKeyLength; asymEncryAlg->getRemoteBlockSize = UA_AsymEn_Basic256Sha256_getRemoteBlockSize; asymEncryAlg->getRemotePlainTextBlockSize = UA_AsymEn_Basic256Sha256_getRemotePlainTextBlockSize; /* AsymmetricModule */ asymmetricModule->compareCertificateThumbprint = UA_compareCertificateThumbprint; asymmetricModule->makeCertificateThumbprint = UA_makeCertificateThumbprint; /* SymmetricModule */ symmetricModule->secureChannelNonceLength = 32; symmetricModule->generateNonce = UA_Sym_Basic256Sha256_generateNonce; symmetricModule->generateKey = UA_Sym_Basic256Sha256_generateKey; /* Symmetric encryption Algorithm */ UA_SecurityPolicyEncryptionAlgorithm *symEncryptionAlgorithm = &symmetricModule->cryptoModule.encryptionAlgorithm; symEncryptionAlgorithm->uri = UA_STRING("http://www.w3.org/2001/04/xmlenc#aes256-cbc\0"); symEncryptionAlgorithm->encrypt = UA_SymEn_Basic256Sha256_encrypt; symEncryptionAlgorithm->decrypt = UA_SymEn_Basic256Sha256_decrypt; symEncryptionAlgorithm->getLocalKeyLength = UA_SymEn_Basic256Sha256_getLocalKeyLength; symEncryptionAlgorithm->getRemoteKeyLength = UA_SymEn_Basic256Sha256_getRemoteKeyLength; symEncryptionAlgorithm->getRemoteBlockSize = UA_SymEn_Basic256Sha256_getBlockSize; symEncryptionAlgorithm->getRemotePlainTextBlockSize = UA_SymEn_Basic256Sha256_getBlockSize; /* Symmetric signature Algorithm */ UA_SecurityPolicySignatureAlgorithm *symSignatureAlgorithm = &symmetricModule->cryptoModule.signatureAlgorithm; symSignatureAlgorithm->uri = UA_STRING("http://www.w3.org/2000/09/xmldsig#hmac-sha2-256\0"); symSignatureAlgorithm->verify = UA_SymSig_Basic256Sha256_verify; symSignatureAlgorithm->sign = UA_SymSig_Basic256Sha256_sign; symSignatureAlgorithm->getLocalSignatureSize = UA_SymSig_Basic256Sha256_getLocalSignatureSize; symSignatureAlgorithm->getRemoteSignatureSize = UA_SymSig_Basic256Sha256_getRemoteSignatureSize; symSignatureAlgorithm->getLocalKeyLength = UA_SymSig_Basic256Sha256_getLocalKeyLength; symSignatureAlgorithm->getRemoteKeyLength = UA_SymSig_Basic256Sha256_getRemoteKeyLength; policy->clear = UA_Policy_Clear_Context; retval = UA_Policy_New_Context(policy, localPrivateKey, logger); if(retval != UA_STATUSCODE_GOOD) { UA_ByteString_clear(&policy->localCertificate); return retval; } /* Use the same signature algorithm as the asymmetric component for * certificate signing (see standard) */ policy->certificateSigningAlgorithm = policy->asymmetricModule.cryptoModule.signatureAlgorithm; return UA_STATUSCODE_GOOD; } #endif /**** amalgamated original file "/plugins/crypto/openssl/ua_openssl_aes128sha256rsaoaep.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 2020 (c) Wind River Systems, Inc. */ #if defined(UA_ENABLE_ENCRYPTION_OPENSSL) || defined(UA_ENABLE_ENCRYPTION_LIBRESSL) #include #include #include #include #include #include #define UA_SHA256_LENGTH 32 /* 256 bit */ #define UA_SECURITYPOLICY_AES128SHA256RSAOAEP_RSAPADDING_LEN 42 #define UA_SECURITYPOLICY_AES128SHA256RSAOAEP_SYM_SIGNING_KEY_LENGTH 32 #define UA_SECURITYPOLICY_AES128SHA256RSAOAEP_SYM_ENCRYPTION_KEY_LENGTH 16 #define UA_SECURITYPOLICY_AES128SHA256RSAOAEP_SYM_ENCRYPTION_BLOCK_SIZE 16 #define UA_SECURITYPOLICY_AES128SHA256RSAOAEP_SYM_PLAIN_TEXT_BLOCK_SIZE 16 #define UA_SECURITYPOLICY_AES128SHA256RSAOAEP_MINASYMKEYLENGTH 256 #define UA_SECURITYPOLICY_AES128SHA256RSAOAEP_MAXASYMKEYLENGTH 512 typedef struct { EVP_PKEY *localPrivateKey; UA_ByteString localCertThumbprint; const UA_Logger *logger; } Policy_Context_Aes128Sha256RsaOaep; typedef struct { UA_ByteString localSymSigningKey; UA_ByteString localSymEncryptingKey; UA_ByteString localSymIv; UA_ByteString remoteSymSigningKey; UA_ByteString remoteSymEncryptingKey; UA_ByteString remoteSymIv; Policy_Context_Aes128Sha256RsaOaep *policyContext; UA_ByteString remoteCertificate; X509 *remoteCertificateX509; /* X509 */ } Channel_Context_Aes128Sha256RsaOaep; /* create the policy context */ static UA_StatusCode UA_Policy_Aes128Sha256RsaOaep_New_Context(UA_SecurityPolicy *securityPolicy, const UA_ByteString localPrivateKey, const UA_Logger *logger) { Policy_Context_Aes128Sha256RsaOaep *context = (Policy_Context_Aes128Sha256RsaOaep *)UA_malloc( sizeof(Policy_Context_Aes128Sha256RsaOaep)); if(context == NULL) { return UA_STATUSCODE_BADOUTOFMEMORY; } context->localPrivateKey = UA_OpenSSL_LoadPrivateKey(&localPrivateKey); if (!context->localPrivateKey) { UA_free(context); return UA_STATUSCODE_BADINVALIDARGUMENT; } UA_StatusCode retval = UA_Openssl_X509_GetCertificateThumbprint( &securityPolicy->localCertificate, &context->localCertThumbprint, true); if(retval != UA_STATUSCODE_GOOD) { EVP_PKEY_free(context->localPrivateKey); UA_free(context); return retval; } context->logger = logger; securityPolicy->policyContext = context; return UA_STATUSCODE_GOOD; } /* clear the policy context */ static void UA_Policy_Aes128Sha256RsaOaep_Clear_Context(UA_SecurityPolicy *policy) { if(policy == NULL) return; UA_ByteString_clear(&policy->localCertificate); /* delete all allocated members in the context */ Policy_Context_Aes128Sha256RsaOaep *pc = (Policy_Context_Aes128Sha256RsaOaep *)policy->policyContext; if (pc == NULL) { return; } EVP_PKEY_free(pc->localPrivateKey); UA_ByteString_clear(&pc->localCertThumbprint); UA_free(pc); return; } /* create the channel context */ static UA_StatusCode UA_ChannelModule_Aes128Sha256RsaOaep_New_Context(const UA_SecurityPolicy *securityPolicy, const UA_ByteString *remoteCertificate, void **channelContext) { if(securityPolicy == NULL || remoteCertificate == NULL || channelContext == NULL) { return UA_STATUSCODE_BADINTERNALERROR; } Channel_Context_Aes128Sha256RsaOaep *context = (Channel_Context_Aes128Sha256RsaOaep *)UA_malloc( sizeof(Channel_Context_Aes128Sha256RsaOaep)); if(context == NULL) { return UA_STATUSCODE_BADOUTOFMEMORY; } UA_ByteString_init(&context->localSymSigningKey); UA_ByteString_init(&context->localSymEncryptingKey); UA_ByteString_init(&context->localSymIv); UA_ByteString_init(&context->remoteSymSigningKey); UA_ByteString_init(&context->remoteSymEncryptingKey); UA_ByteString_init(&context->remoteSymIv); UA_StatusCode retval = UA_copyCertificate(&context->remoteCertificate, remoteCertificate); if(retval != UA_STATUSCODE_GOOD) { UA_free(context); return retval; } /* decode to X509 */ context->remoteCertificateX509 = UA_OpenSSL_LoadCertificate(&context->remoteCertificate); if (context->remoteCertificateX509 == NULL) { UA_ByteString_clear (&context->remoteCertificate); UA_free (context); return UA_STATUSCODE_BADCERTIFICATECHAININCOMPLETE; } context->policyContext = (Policy_Context_Aes128Sha256RsaOaep *)(securityPolicy->policyContext); *channelContext = context; UA_LOG_INFO( securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "The Aes128Sha256RsaOaep security policy channel with openssl is created."); return UA_STATUSCODE_GOOD; } /* delete the channel context */ static void UA_ChannelModule_Aes128Sha256RsaOaep_Delete_Context(void *channelContext) { if(channelContext != NULL) { Channel_Context_Aes128Sha256RsaOaep *cc = (Channel_Context_Aes128Sha256RsaOaep *)channelContext; X509_free(cc->remoteCertificateX509); UA_ByteString_clear(&cc->remoteCertificate); UA_ByteString_clear(&cc->localSymSigningKey); UA_ByteString_clear(&cc->localSymEncryptingKey); UA_ByteString_clear(&cc->localSymIv); UA_ByteString_clear(&cc->remoteSymSigningKey); UA_ByteString_clear(&cc->remoteSymEncryptingKey); UA_ByteString_clear(&cc->remoteSymIv); UA_LOG_INFO( cc->policyContext->logger, UA_LOGCATEGORY_SECURITYPOLICY, "The Aes128Sha256RsaOaep security policy channel with openssl is deleted."); UA_free(cc); } } /* Verifies the signature of the message using the provided keys in the context. * AsymmetricSignatureAlgorithm_RSA-PKCS15-SHA2-256 */ static UA_StatusCode UA_AsySig_Aes128Sha256RsaOaep_Verify(void *channelContext, const UA_ByteString *message, const UA_ByteString *signature) { if(message == NULL || signature == NULL || channelContext == NULL) { return UA_STATUSCODE_BADINTERNALERROR; } Channel_Context_Aes128Sha256RsaOaep *cc = (Channel_Context_Aes128Sha256RsaOaep *)channelContext; UA_StatusCode retval = UA_OpenSSL_RSA_PKCS1_V15_SHA256_Verify( message, cc->remoteCertificateX509, signature); return retval; } /* Compares the supplied certificate with the certificate * in the endpoint context */ static UA_StatusCode UA_compareCertificateThumbprint_Aes128Sha256RsaOaep(const UA_SecurityPolicy *securityPolicy, const UA_ByteString *certificateThumbprint) { if(securityPolicy == NULL || certificateThumbprint == NULL) { return UA_STATUSCODE_BADINVALIDARGUMENT; } Policy_Context_Aes128Sha256RsaOaep *pc = (Policy_Context_Aes128Sha256RsaOaep *)securityPolicy->policyContext; if(!UA_ByteString_equal(certificateThumbprint, &pc->localCertThumbprint)) return UA_STATUSCODE_BADCERTIFICATEINVALID; return UA_STATUSCODE_GOOD; } /* Generates a thumbprint for the specified certificate */ static UA_StatusCode UA_makeCertificateThumbprint_Aes128Sha256RsaOaep(const UA_SecurityPolicy *securityPolicy, const UA_ByteString *certificate, UA_ByteString *thumbprint) { return UA_Openssl_X509_GetCertificateThumbprint(certificate, thumbprint, false); } static UA_StatusCode UA_Asym_Aes128Sha256RsaOaep_Decrypt(void *channelContext, UA_ByteString *data) { if(channelContext == NULL || data == NULL) return UA_STATUSCODE_BADINVALIDARGUMENT; Channel_Context_Aes128Sha256RsaOaep *cc = (Channel_Context_Aes128Sha256RsaOaep *)channelContext; UA_StatusCode ret = UA_Openssl_RSA_Oaep_Decrypt(data, cc->policyContext->localPrivateKey); return ret; } static size_t UA_Asym_Aes128Sha256RsaOaep_getRemoteSignatureSize(const void *channelContext) { if(channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; const Channel_Context_Aes128Sha256RsaOaep *cc = (const Channel_Context_Aes128Sha256RsaOaep *)channelContext; UA_Int32 keyLen = 0; UA_Openssl_RSA_Public_GetKeyLength(cc->remoteCertificateX509, &keyLen); return (size_t)keyLen; } static size_t UA_AsySig_Aes128Sha256RsaOaep_getLocalSignatureSize(const void *channelContext) { if(channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; const Channel_Context_Aes128Sha256RsaOaep *cc = (const Channel_Context_Aes128Sha256RsaOaep *)channelContext; Policy_Context_Aes128Sha256RsaOaep *pc = cc->policyContext; UA_Int32 keyLen = 0; UA_Openssl_RSA_Private_GetKeyLength(pc->localPrivateKey, &keyLen); return (size_t)keyLen; } static size_t UA_AsymEn_Aes128Sha256RsaOaep_getRemotePlainTextBlockSize(const void *channelContext) { if(channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; const Channel_Context_Aes128Sha256RsaOaep *cc = (const Channel_Context_Aes128Sha256RsaOaep *)channelContext; UA_Int32 keyLen = 0; UA_Openssl_RSA_Public_GetKeyLength(cc->remoteCertificateX509, &keyLen); return (size_t)keyLen - UA_SECURITYPOLICY_AES128SHA256RSAOAEP_RSAPADDING_LEN; } static size_t UA_AsymEn_Aes128Sha256RsaOaep_getRemoteBlockSize(const void *channelContext) { if(channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; const Channel_Context_Aes128Sha256RsaOaep *cc = (const Channel_Context_Aes128Sha256RsaOaep *)channelContext; UA_Int32 keyLen = 0; UA_Openssl_RSA_Public_GetKeyLength(cc->remoteCertificateX509, &keyLen); return (size_t)keyLen; } static size_t UA_AsymEn_Aes128Sha256RsaOaep_getRemoteKeyLength(const void *channelContext) { if(channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; const Channel_Context_Aes128Sha256RsaOaep *cc = (const Channel_Context_Aes128Sha256RsaOaep *)channelContext; UA_Int32 keyLen = 0; UA_Openssl_RSA_Public_GetKeyLength(cc->remoteCertificateX509, &keyLen); return (size_t)keyLen * 8; } static UA_StatusCode UA_Sym_Aes128Sha256RsaOaep_generateNonce(void *policyContext, UA_ByteString *out) { UA_Int32 rc = RAND_bytes(out->data, (int)out->length); if(rc != 1) { return UA_STATUSCODE_BADUNEXPECTEDERROR; } return UA_STATUSCODE_GOOD; } static size_t UA_SymEn_Aes128Sha256RsaOaep_getLocalKeyLength(const void *channelContext) { /* 32 bytes 256 bits */ return UA_SECURITYPOLICY_AES128SHA256RSAOAEP_SYM_ENCRYPTION_KEY_LENGTH; } static size_t UA_SymSig_Aes128Sha256RsaOaep_getLocalKeyLength(const void *channelContext) { /* 32 bytes 256 bits */ return UA_SECURITYPOLICY_AES128SHA256RSAOAEP_SYM_SIGNING_KEY_LENGTH; } static UA_StatusCode UA_Sym_Aes128Sha256RsaOaep_generateKey(void *policyContext, const UA_ByteString *secret, const UA_ByteString *seed, UA_ByteString *out) { return UA_Openssl_Random_Key_PSHA256_Derive(secret, seed, out); } static UA_StatusCode UA_ChannelModule_Aes128Sha256RsaOaep_setLocalSymSigningKey(void *channelContext, const UA_ByteString *key) { if(key == NULL || channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Aes128Sha256RsaOaep *cc = (Channel_Context_Aes128Sha256RsaOaep *)channelContext; UA_ByteString_clear(&cc->localSymSigningKey); return UA_ByteString_copy(key, &cc->localSymSigningKey); } static UA_StatusCode UA_ChannelM_Aes128Sha256RsaOaep_setLocalSymEncryptingKey(void *channelContext, const UA_ByteString *key) { if(key == NULL || channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Aes128Sha256RsaOaep *cc = (Channel_Context_Aes128Sha256RsaOaep *)channelContext; UA_ByteString_clear(&cc->localSymEncryptingKey); return UA_ByteString_copy(key, &cc->localSymEncryptingKey); } static UA_StatusCode UA_ChannelM_Aes128Sha256RsaOaep_setLocalSymIv(void *channelContext, const UA_ByteString *iv) { if(iv == NULL || channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Aes128Sha256RsaOaep *cc = (Channel_Context_Aes128Sha256RsaOaep *)channelContext; UA_ByteString_clear(&cc->localSymIv); return UA_ByteString_copy(iv, &cc->localSymIv); } static size_t UA_SymEn_Aes128Sha256RsaOaep_getRemoteKeyLength(const void *channelContext) { /* 32 bytes 256 bits */ return UA_SECURITYPOLICY_AES128SHA256RSAOAEP_SYM_ENCRYPTION_KEY_LENGTH; } static size_t UA_SymEn_Aes128Sha256RsaOaep_getBlockSize(const void *channelContext) { return UA_SECURITYPOLICY_AES128SHA256RSAOAEP_SYM_ENCRYPTION_BLOCK_SIZE; } static size_t UA_SymSig_Aes128Sha256RsaOaep_getRemoteKeyLength(const void *channelContext) { /* 32 bytes 256 bits */ return UA_SECURITYPOLICY_AES128SHA256RSAOAEP_SYM_SIGNING_KEY_LENGTH; } static UA_StatusCode UA_ChannelM_Aes128Sha256RsaOaep_setRemoteSymSigningKey(void *channelContext, const UA_ByteString *key) { if(key == NULL || channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Aes128Sha256RsaOaep *cc = (Channel_Context_Aes128Sha256RsaOaep *)channelContext; UA_ByteString_clear(&cc->remoteSymSigningKey); return UA_ByteString_copy(key, &cc->remoteSymSigningKey); } static UA_StatusCode UA_ChannelM_Aes128Sha256RsaOaep_setRemoteSymEncryptingKey(void *channelContext, const UA_ByteString *key) { if(key == NULL || channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Aes128Sha256RsaOaep *cc = (Channel_Context_Aes128Sha256RsaOaep *)channelContext; UA_ByteString_clear(&cc->remoteSymEncryptingKey); return UA_ByteString_copy(key, &cc->remoteSymEncryptingKey); } static UA_StatusCode UA_ChannelM_Aes128Sha256RsaOaep_setRemoteSymIv(void *channelContext, const UA_ByteString *key) { if(key == NULL || channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Aes128Sha256RsaOaep *cc = (Channel_Context_Aes128Sha256RsaOaep *)channelContext; UA_ByteString_clear(&cc->remoteSymIv); return UA_ByteString_copy(key, &cc->remoteSymIv); } static UA_StatusCode UA_AsySig_Aes128Sha256RsaOaep_sign(void *channelContext, const UA_ByteString *message, UA_ByteString *signature) { if(channelContext == NULL || message == NULL || signature == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Aes128Sha256RsaOaep *cc = (Channel_Context_Aes128Sha256RsaOaep *)channelContext; Policy_Context_Aes128Sha256RsaOaep *pc = cc->policyContext; return UA_Openssl_RSA_PKCS1_V15_SHA256_Sign(message, pc->localPrivateKey, signature); } static UA_StatusCode UA_AsymEn_Aes128Sha256RsaOaep_encrypt(void *channelContext, UA_ByteString *data) { if(channelContext == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Aes128Sha256RsaOaep *cc = (Channel_Context_Aes128Sha256RsaOaep *)channelContext; return UA_Openssl_RSA_OAEP_Encrypt( data, UA_SECURITYPOLICY_AES128SHA256RSAOAEP_RSAPADDING_LEN, cc->remoteCertificateX509); } static size_t UA_SymSig_Aes128Sha256RsaOaep_getRemoteSignatureSize(const void *channelContext) { return UA_SHA256_LENGTH; } static UA_StatusCode UA_SymSig_Aes128Sha256RsaOaep_verify(void *channelContext, const UA_ByteString *message, const UA_ByteString *signature) { if(channelContext == NULL || message == NULL || signature == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Aes128Sha256RsaOaep *cc = (Channel_Context_Aes128Sha256RsaOaep *)channelContext; return UA_OpenSSL_HMAC_SHA256_Verify(message, &cc->remoteSymSigningKey, signature); } static UA_StatusCode UA_SymSig_Aes128Sha256RsaOaep_sign(void *channelContext, const UA_ByteString *message, UA_ByteString *signature) { if(channelContext == NULL || message == NULL || signature == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Aes128Sha256RsaOaep *cc = (Channel_Context_Aes128Sha256RsaOaep *)channelContext; return UA_OpenSSL_HMAC_SHA256_Sign(message, &cc->localSymSigningKey, signature); } static size_t UA_SymSig_Aes128Sha256RsaOaep_getLocalSignatureSize(const void *channelContext) { return UA_SHA256_LENGTH; } static UA_StatusCode UA_SymEn_Aes128Sha256RsaOaep_decrypt(void *channelContext, UA_ByteString *data) { if(channelContext == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Aes128Sha256RsaOaep *cc = (Channel_Context_Aes128Sha256RsaOaep *)channelContext; return UA_OpenSSL_AES_128_CBC_Decrypt(&cc->remoteSymIv, &cc->remoteSymEncryptingKey, data); } static UA_StatusCode UA_SymEn_Aes128Sha256RsaOaep_encrypt(void *channelContext, UA_ByteString *data) { if(channelContext == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; Channel_Context_Aes128Sha256RsaOaep *cc = (Channel_Context_Aes128Sha256RsaOaep *)channelContext; return UA_OpenSSL_AES_128_CBC_Encrypt(&cc->localSymIv, &cc->localSymEncryptingKey, data); } static UA_StatusCode UA_ChannelM_Aes128Sha256RsaOaep_compareCertificate(const void *channelContext, const UA_ByteString *certificate) { if(channelContext == NULL || certificate == NULL) return UA_STATUSCODE_BADINTERNALERROR; const Channel_Context_Aes128Sha256RsaOaep *cc = (const Channel_Context_Aes128Sha256RsaOaep *)channelContext; return UA_OpenSSL_X509_compare(certificate, cc->remoteCertificateX509); } static size_t UA_AsymEn_Aes128Sha256RsaOaep_getLocalKeyLength(const void *channelContext) { if(channelContext == NULL) return UA_STATUSCODE_BADINTERNALERROR; const Channel_Context_Aes128Sha256RsaOaep *cc = (const Channel_Context_Aes128Sha256RsaOaep *)channelContext; Policy_Context_Aes128Sha256RsaOaep *pc = cc->policyContext; UA_Int32 keyLen = 0; UA_Openssl_RSA_Private_GetKeyLength(pc->localPrivateKey, &keyLen); return (size_t)keyLen * 8; } /* the main entry of Aes128Sha256RsaOaep */ UA_StatusCode UA_SecurityPolicy_Aes128Sha256RsaOaep(UA_SecurityPolicy *policy, const UA_ByteString localCertificate, const UA_ByteString localPrivateKey, const UA_Logger *logger) { UA_SecurityPolicyAsymmetricModule *const asymmetricModule = &policy->asymmetricModule; UA_SecurityPolicySymmetricModule *const symmetricModule = &policy->symmetricModule; UA_SecurityPolicyChannelModule *const channelModule = &policy->channelModule; UA_StatusCode retval; UA_LOG_INFO(logger, UA_LOGCATEGORY_SECURITYPOLICY, "The Aes128Sha256RsaOaep security policy with openssl is added."); UA_Openssl_Init(); memset(policy, 0, sizeof(UA_SecurityPolicy)); policy->logger = logger; policy->policyUri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Aes128_Sha256_RsaOaep\0"); /* set ChannelModule context */ channelModule->newContext = UA_ChannelModule_Aes128Sha256RsaOaep_New_Context; channelModule->deleteContext = UA_ChannelModule_Aes128Sha256RsaOaep_Delete_Context; channelModule->setLocalSymSigningKey = UA_ChannelModule_Aes128Sha256RsaOaep_setLocalSymSigningKey; channelModule->setLocalSymEncryptingKey = UA_ChannelM_Aes128Sha256RsaOaep_setLocalSymEncryptingKey; channelModule->setLocalSymIv = UA_ChannelM_Aes128Sha256RsaOaep_setLocalSymIv; channelModule->setRemoteSymSigningKey = UA_ChannelM_Aes128Sha256RsaOaep_setRemoteSymSigningKey; channelModule->setRemoteSymEncryptingKey = UA_ChannelM_Aes128Sha256RsaOaep_setRemoteSymEncryptingKey; channelModule->setRemoteSymIv = UA_ChannelM_Aes128Sha256RsaOaep_setRemoteSymIv; channelModule->compareCertificate = UA_ChannelM_Aes128Sha256RsaOaep_compareCertificate; /* Copy the certificate and add a NULL to the end */ retval = UA_copyCertificate(&policy->localCertificate, &localCertificate); if(retval != UA_STATUSCODE_GOOD) return retval; /* AsymmetricModule - signature algorithm */ UA_SecurityPolicySignatureAlgorithm *asySigAlgorithm = &asymmetricModule->cryptoModule.signatureAlgorithm; asySigAlgorithm->uri = UA_STRING("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\0"); asySigAlgorithm->verify = UA_AsySig_Aes128Sha256RsaOaep_Verify; asySigAlgorithm->getRemoteSignatureSize = UA_Asym_Aes128Sha256RsaOaep_getRemoteSignatureSize; asySigAlgorithm->getLocalSignatureSize = UA_AsySig_Aes128Sha256RsaOaep_getLocalSignatureSize; asySigAlgorithm->sign = UA_AsySig_Aes128Sha256RsaOaep_sign; asySigAlgorithm->getLocalKeyLength = NULL; asySigAlgorithm->getRemoteKeyLength = NULL; /* AsymmetricModule encryption algorithm */ UA_SecurityPolicyEncryptionAlgorithm *asymEncryAlg = &asymmetricModule->cryptoModule.encryptionAlgorithm; asymEncryAlg->uri = UA_STRING("http://www.w3.org/2001/04/xmlenc#rsa-oaep\0"); asymEncryAlg->decrypt = UA_Asym_Aes128Sha256RsaOaep_Decrypt; asymEncryAlg->getRemotePlainTextBlockSize = UA_AsymEn_Aes128Sha256RsaOaep_getRemotePlainTextBlockSize; asymEncryAlg->getRemoteBlockSize = UA_AsymEn_Aes128Sha256RsaOaep_getRemoteBlockSize; asymEncryAlg->getRemoteKeyLength = UA_AsymEn_Aes128Sha256RsaOaep_getRemoteKeyLength; asymEncryAlg->encrypt = UA_AsymEn_Aes128Sha256RsaOaep_encrypt; asymEncryAlg->getLocalKeyLength = UA_AsymEn_Aes128Sha256RsaOaep_getLocalKeyLength; /* asymmetricModule */ asymmetricModule->compareCertificateThumbprint = UA_compareCertificateThumbprint_Aes128Sha256RsaOaep; asymmetricModule->makeCertificateThumbprint = UA_makeCertificateThumbprint_Aes128Sha256RsaOaep; /* SymmetricModule */ symmetricModule->secureChannelNonceLength = 32; symmetricModule->generateNonce = UA_Sym_Aes128Sha256RsaOaep_generateNonce; symmetricModule->generateKey = UA_Sym_Aes128Sha256RsaOaep_generateKey; /* Symmetric encryption Algorithm */ UA_SecurityPolicyEncryptionAlgorithm *symEncryptionAlgorithm = &symmetricModule->cryptoModule.encryptionAlgorithm; symEncryptionAlgorithm->uri = UA_STRING("http://www.w3.org/2001/04/xmlenc#aes128-cbc\0"); symEncryptionAlgorithm->getLocalKeyLength = UA_SymEn_Aes128Sha256RsaOaep_getLocalKeyLength; symEncryptionAlgorithm->getRemoteKeyLength = UA_SymEn_Aes128Sha256RsaOaep_getRemoteKeyLength; symEncryptionAlgorithm->getRemoteBlockSize = UA_SymEn_Aes128Sha256RsaOaep_getBlockSize; symEncryptionAlgorithm->getRemotePlainTextBlockSize = UA_SymEn_Aes128Sha256RsaOaep_getBlockSize; symEncryptionAlgorithm->decrypt = UA_SymEn_Aes128Sha256RsaOaep_decrypt; symEncryptionAlgorithm->encrypt = UA_SymEn_Aes128Sha256RsaOaep_encrypt; /* Symmetric signature Algorithm */ UA_SecurityPolicySignatureAlgorithm *symSignatureAlgorithm = &symmetricModule->cryptoModule.signatureAlgorithm; symSignatureAlgorithm->uri = UA_STRING("http://www.w3.org/2000/09/xmldsig#hmac-sha2-256\0"); symSignatureAlgorithm->getLocalKeyLength = UA_SymSig_Aes128Sha256RsaOaep_getLocalKeyLength; symSignatureAlgorithm->getRemoteKeyLength = UA_SymSig_Aes128Sha256RsaOaep_getRemoteKeyLength; symSignatureAlgorithm->getRemoteSignatureSize = UA_SymSig_Aes128Sha256RsaOaep_getRemoteSignatureSize; symSignatureAlgorithm->verify = UA_SymSig_Aes128Sha256RsaOaep_verify; symSignatureAlgorithm->sign = UA_SymSig_Aes128Sha256RsaOaep_sign; symSignatureAlgorithm->getLocalSignatureSize = UA_SymSig_Aes128Sha256RsaOaep_getLocalSignatureSize; retval = UA_Policy_Aes128Sha256RsaOaep_New_Context(policy, localPrivateKey, logger); if(retval != UA_STATUSCODE_GOOD) { UA_ByteString_clear(&policy->localCertificate); return retval; } policy->clear = UA_Policy_Aes128Sha256RsaOaep_Clear_Context; /* Use the same signature algorithm as the asymmetric component for certificate signing (see standard) */ policy->certificateSigningAlgorithm = policy->asymmetricModule.cryptoModule.signatureAlgorithm; return UA_STATUSCODE_GOOD; } #endif /**** amalgamated original file "/plugins/crypto/openssl/ua_openssl_create_certificate.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 2021 (c) Christian von Arnim, ISW University of Stuttgart (for VDW and umati) * Copyright 2022 (c) Wind River Systems, Inc. * */ #if defined(UA_ENABLE_ENCRYPTION_OPENSSL) || defined(UA_ENABLE_ENCRYPTION_LIBRESSL) #include #include /** * Join an array of UA_String to a single NULL-Terminated UA_String * separated by character sep */ static UA_StatusCode join_string_with_sep(const UA_String *strings, size_t stringsSize, char sep, UA_String *out) { if(!out) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_String_clear(out); size_t totalSize = stringsSize; for(size_t iStr = 0; iStr < stringsSize; ++iStr) { totalSize += strings[iStr].length; } UA_ByteString_allocBuffer(out, totalSize); if(!out->data) { return UA_STATUSCODE_BADOUTOFMEMORY; } size_t pos = 0; for(size_t iStr = 0; iStr < stringsSize; ++iStr) { memcpy(&out->data[pos], strings[iStr].data, strings[iStr].length); pos += strings[iStr].length; out->data[pos] = (UA_Byte) sep; ++pos; } out->data[out->length-1] = 0; return UA_STATUSCODE_GOOD; } /** * Search for a character in a string (like strchr). * \todo Handle UTF-8 * * \return index of the character or -1 on case of an error. */ static UA_Int32 UA_String_chr(const UA_String *pUaStr, char needl) { UA_Byte byteNeedl = (UA_Byte)needl; for(size_t i = 0; (size_t)i < pUaStr->length; ++i) { if(pUaStr->data[i] == byteNeedl) { return (UA_Int32) i; } } return -1; } static UA_StatusCode add_x509V3ext(X509 *x509, int nid, const char *value) { X509_EXTENSION *ex; X509V3_CTX ctx; X509V3_set_ctx_nodb(&ctx); X509V3_set_ctx(&ctx, x509, x509, NULL, NULL, 0); ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value); if(!ex) return UA_STATUSCODE_BADINTERNALERROR; X509_add_ext(x509, ex, -1); X509_EXTENSION_free(ex); return UA_STATUSCODE_GOOD; } #if (OPENSSL_VERSION_NUMBER >= 0x30000000L) /* generate the RSA key */ static EVP_PKEY * UA_RSA_Generate_Key (size_t keySizeBits){ return EVP_RSA_gen(keySizeBits); } #endif UA_StatusCode UA_CreateCertificate(const UA_Logger *logger, const UA_String *subject, size_t subjectSize, const UA_String *subjectAltName, size_t subjectAltNameSize, size_t keySizeBits, UA_CertificateFormat certFormat, UA_ByteString *outPrivateKey, UA_ByteString *outCertificate) { if(!outPrivateKey || !outCertificate || !logger || !subjectAltName || !subject || subjectAltNameSize == 0 || subjectSize == 0 || (certFormat != UA_CERTIFICATEFORMAT_DER && certFormat != UA_CERTIFICATEFORMAT_PEM )) return UA_STATUSCODE_BADINVALIDARGUMENT; /* Use the maximum size */ if(keySizeBits == 0) keySizeBits = 4096; UA_ByteString_init(outPrivateKey); UA_ByteString_init(outCertificate); UA_String fullAltSubj = UA_STRING_NULL; UA_Int32 serial = 1; /** \TODO: Seed Random generator * See: (https://www.openssl.org/docs/man1.1.0/man3/RAND_add.html) */ BIO *memCert = NULL; BIO *memPKey = NULL; UA_StatusCode errRet = UA_STATUSCODE_GOOD; X509 *x509 = X509_new(); #if (OPENSSL_VERSION_NUMBER >= 0x30000000L) EVP_PKEY *pkey = UA_RSA_Generate_Key(keySizeBits); if((pkey == NULL) || (x509 == NULL)) { errRet = UA_STATUSCODE_BADOUTOFMEMORY; goto cleanup; } #else BIGNUM *exponent = BN_new(); EVP_PKEY *pkey = EVP_PKEY_new(); RSA *rsa = RSA_new(); if(!pkey || !x509 || !exponent || !rsa) { errRet = UA_STATUSCODE_BADOUTOFMEMORY; goto cleanup; } UA_LOG_INFO(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Generating RSA key. This may take a while."); if(BN_set_word(exponent, RSA_F4) != 1) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Setting RSA exponent failed."); errRet = UA_STATUSCODE_BADINTERNALERROR; goto cleanup; } if(RSA_generate_key_ex(rsa, (int) keySizeBits, exponent, NULL) != 1) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Generating RSA key failed."); errRet = UA_STATUSCODE_BADINTERNALERROR; goto cleanup; } if(EVP_PKEY_assign_RSA(pkey, rsa) != 1) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Assign RSA key failed."); errRet = UA_STATUSCODE_BADINTERNALERROR; goto cleanup; } /* rsa will be freed by pkey */ rsa = NULL; #endif /* end of OPENSSL_VERSION_NUMBER >= 0x30000000L */ /* x509v3 has version 2 * (https://www.openssl.org/docs/man1.1.0/man3/X509_set_version.html) */ if(X509_set_version(x509, 2) != 1) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Setting version failed."); errRet = UA_STATUSCODE_BADINTERNALERROR; goto cleanup; } if(ASN1_INTEGER_set(X509_get_serialNumber(x509), serial) != 1) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Setting serial number failed."); /* Only memory errors are possible */ errRet = UA_STATUSCODE_BADOUTOFMEMORY; goto cleanup; } if(X509_gmtime_adj(X509_get_notBefore(x509), 0) == NULL) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Setting 'not before' failed."); errRet = UA_STATUSCODE_BADINTERNALERROR; goto cleanup; } if(X509_gmtime_adj(X509_get_notAfter(x509), (UA_Int64) 60 * 60 * 24 * 365) == NULL) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Setting 'not before' failed."); errRet = UA_STATUSCODE_BADINTERNALERROR; goto cleanup; } if(X509_set_pubkey(x509, pkey) != 1) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Setting publik key failed."); errRet = UA_STATUSCODE_BADINTERNALERROR; goto cleanup; } X509_NAME *name = X509_get_subject_name(x509); if(name == NULL) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Getting name failed."); errRet = UA_STATUSCODE_BADINTERNALERROR; goto cleanup; } for(UA_UInt32 iSubject = 0; iSubject < subjectSize; ++iSubject) { UA_Int32 sep = UA_String_chr(&subject[iSubject], '='); char field[16]; if(sep == -1 || sep == 0 || ((size_t) sep == (subject[iSubject].length - 1)) || sep >= 15) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Subject must contain one '=' with " "content before and after."); errRet = UA_STATUSCODE_BADINTERNALERROR; goto cleanup; } memcpy(field, subject[iSubject].data, (size_t) sep); field[sep] = 0; UA_Byte* pData = &subject[iSubject].data[sep + 1]; if(X509_NAME_add_entry_by_txt( name, field, MBSTRING_ASC, (const unsigned char *)pData, (int) subject[iSubject].length - (int) sep - 1, -1, 0) != 1) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Setting subject failed."); errRet = UA_STATUSCODE_BADINTERNALERROR; goto cleanup; } } /* Self signed, so issuer == subject */ if(X509_set_issuer_name(x509, name) != 1) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Setting name failed."); errRet = UA_STATUSCODE_BADINTERNALERROR; goto cleanup; } errRet = add_x509V3ext(x509, NID_basic_constraints, "CA:FALSE"); if(errRet != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Setting 'Basic Constraints' failed."); goto cleanup; } /* See https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.3 for * possible values */ errRet = add_x509V3ext(x509, NID_key_usage, "digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyCertSign"); if(errRet != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Setting 'Key Usage' failed."); goto cleanup; } errRet = add_x509V3ext(x509, NID_ext_key_usage, "serverAuth,clientAuth"); if(errRet != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Setting 'Extended Key Usage' failed."); goto cleanup; } errRet = add_x509V3ext(x509, NID_subject_key_identifier, "hash"); if(errRet != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Setting 'Subject Key Identifier' failed."); goto cleanup; } errRet = join_string_with_sep(subjectAltName, subjectAltNameSize, ',', &fullAltSubj); if(errRet != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Joining altSubject failed."); goto cleanup; } errRet = add_x509V3ext(x509, NID_subject_alt_name, (const char*) fullAltSubj.data); if(errRet != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Setting 'Subject Alternative Name:' failed."); goto cleanup; } if(X509_sign(x509, pkey, EVP_sha256()) == 0) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Signing failed."); errRet = UA_STATUSCODE_BADINTERNALERROR; goto cleanup; } switch(certFormat) { case UA_CERTIFICATEFORMAT_DER: { int tmpLen = i2d_PrivateKey(pkey, &outPrivateKey->data); if(tmpLen <= 0) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Create private DER key failed."); errRet = UA_STATUSCODE_BADINTERNALERROR; goto cleanup; } outPrivateKey->length = (size_t) tmpLen; tmpLen = i2d_X509(x509, &outCertificate->data); if(tmpLen <= 0) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Create DER-certificate failed."); errRet = UA_STATUSCODE_BADINTERNALERROR; goto cleanup; } outCertificate->length = (size_t) tmpLen; break; } case UA_CERTIFICATEFORMAT_PEM: { /* Private Key */ memPKey = BIO_new(BIO_s_mem()); if(!memPKey) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Allocate Membuffer for PKey failed."); errRet = UA_STATUSCODE_BADOUTOFMEMORY; goto cleanup; } if(PEM_write_bio_PrivateKey(memPKey, pkey, NULL, NULL, 0, 0, NULL) != 1) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Generate PEM-PrivateKey failed."); errRet = UA_STATUSCODE_BADINTERNALERROR; goto cleanup; } UA_ByteString tmpPem = UA_BYTESTRING_NULL; tmpPem.length = (size_t) BIO_get_mem_data(memPKey, &tmpPem.data); errRet = UA_ByteString_copy(&tmpPem, outPrivateKey); if(errRet != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Copy PEM PKey failed."); goto cleanup; } /* Certificate */ memCert = BIO_new(BIO_s_mem()); if(!memCert) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Allocate Membuffer for Cert failed."); errRet = UA_STATUSCODE_BADOUTOFMEMORY; goto cleanup; } if(PEM_write_bio_X509(memCert, x509) != 1) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Generate PEM-Certifcate failed."); errRet = UA_STATUSCODE_BADINTERNALERROR; goto cleanup; } tmpPem.length = (size_t) BIO_get_mem_data(memCert, &tmpPem.data); errRet = UA_ByteString_copy(&tmpPem, outCertificate); if(errRet != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, "Create Certificate: Copy PEM Certificate failed."); goto cleanup; } break; } } cleanup: UA_String_clear(&fullAltSubj); #if (OPENSSL_VERSION_NUMBER < 0x30000000L) RSA_free(rsa); BN_free(exponent); #endif X509_free(x509); EVP_PKEY_free(pkey); BIO_free(memCert); BIO_free(memPKey); return errRet; } #endif /**** amalgamated original file "/plugins/crypto/openssl/ua_pki_openssl.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 2020 (c) Wind River Systems, Inc. * Copyright 2020 (c) basysKom GmbH */ #if defined(UA_ENABLE_ENCRYPTION_OPENSSL) || defined(UA_ENABLE_ENCRYPTION_LIBRESSL) #include #include #include #include /* 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*/ for(; l > 0; ++s, --l) { if(*s == ch) return s; } return NULL; } static const unsigned char * UA_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; } 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; STACK_OF(X509) * skIssue; STACK_OF(X509) * skTrusted; STACK_OF(X509_CRL) * skCrls; /* Revocation list*/ } CertContext; static UA_StatusCode UA_CertContext_sk_Init (CertContext * context) { context->skTrusted = sk_X509_new_null(); context->skIssue = sk_X509_new_null(); context->skCrls = sk_X509_CRL_new_null(); if (context->skTrusted == NULL || context->skIssue == NULL || context->skCrls == NULL) { return UA_STATUSCODE_BADOUTOFMEMORY; } return UA_STATUSCODE_GOOD; } static void UA_CertContext_sk_free (CertContext * context) { sk_X509_pop_free (context->skTrusted, X509_free); sk_X509_pop_free (context->skIssue, X509_free); sk_X509_CRL_pop_free (context->skCrls, X509_CRL_free); } static UA_StatusCode UA_CertContext_Init (CertContext * context) { (void) memset (context, 0, sizeof (CertContext)); UA_ByteString_init (&context->trustListFolder); UA_ByteString_init (&context->issuerListFolder); UA_ByteString_init (&context->revocationListFolder); return UA_CertContext_sk_Init (context); } static void UA_CertificateVerification_clear (UA_CertificateVerification * cv) { if (cv == NULL) { return ; } CertContext * context = (CertContext *) cv->context; if (context == NULL) { return; } UA_ByteString_clear (&context->trustListFolder); UA_ByteString_clear (&context->issuerListFolder); UA_ByteString_clear (&context->revocationListFolder); UA_CertContext_sk_free (context); UA_free (context); cv->context = NULL; return; } static UA_StatusCode UA_skTrusted_Cert2X509 (const UA_ByteString * certificateTrustList, size_t certificateTrustListSize, CertContext * ctx) { size_t i; for (i = 0; i < certificateTrustListSize; i++) { X509 * x509 = UA_OpenSSL_LoadCertificate(&certificateTrustList[i]); if (x509 == NULL) { return UA_STATUSCODE_BADINTERNALERROR; } sk_X509_push (ctx->skTrusted, x509); } return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_skIssuer_Cert2X509 (const UA_ByteString * certificateIssuerList, size_t certificateIssuerListSize, CertContext * ctx) { size_t i; for (i = 0; i < certificateIssuerListSize; i++) { X509 * x509 = UA_OpenSSL_LoadCertificate(&certificateIssuerList[i]); if (x509 == NULL) { return UA_STATUSCODE_BADINTERNALERROR; } sk_X509_push (ctx->skIssue, x509); } return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_skCrls_Cert2X509 (const UA_ByteString * certificateRevocationList, size_t certificateRevocationListSize, CertContext * ctx) { size_t i; const unsigned char * pData; for (i = 0; i < certificateRevocationListSize; i++) { pData = certificateRevocationList[i].data; X509_CRL * crl = NULL; if (certificateRevocationList[i].length > 1 && pData[0] == 0x30 && pData[1] == 0x82) { // Magic number for DER encoded files crl = d2i_X509_CRL (NULL, &pData, (long) certificateRevocationList[i].length); } else { BIO* bio = NULL; bio = BIO_new_mem_buf((void *) certificateRevocationList[i].data, (int) certificateRevocationList[i].length); crl = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL); BIO_free(bio); } if (crl == NULL) { return UA_STATUSCODE_BADINTERNALERROR; } sk_X509_CRL_push (ctx->skCrls, crl); } return UA_STATUSCODE_GOOD; } #ifdef __linux__ #include static int UA_Certificate_Filter_der_pem (const struct dirent * entry) { /* ignore hidden files */ if (entry->d_name[0] == '.') return 0; /* check file extension */ const char *pszFind = strrchr(entry->d_name, '.'); if (pszFind == 0) return 0; pszFind++; if (strcmp (pszFind, "der") == 0 || strcmp (pszFind, "pem") == 0) return 1; return 0; } static int UA_Certificate_Filter_crl (const struct dirent * entry) { /* ignore hidden files */ if (entry->d_name[0] == '.') return 0; /* check file extension */ const char *pszFind = strrchr(entry->d_name, '.'); if (pszFind == 0) return 0; pszFind++; if (strcmp (pszFind, "crl") == 0) return 1; return 0; } static UA_StatusCode UA_BuildFullPath (const char * path, const char * fileName, size_t fullPathBufferLength, char * fullPath) { size_t pathLen = strlen (path); size_t fileNameLen = strlen (fileName); if ((pathLen + fileNameLen + 2) > fullPathBufferLength) { return UA_STATUSCODE_BADINVALIDARGUMENT; } strcpy (fullPath, path); strcat (fullPath, "/"); strcat (fullPath, fileName); return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_loadCertFromFile (const char * fileName, UA_ByteString * cert) { FILE * fp = fopen(fileName, "rb"); if (fp == NULL) return UA_STATUSCODE_BADINTERNALERROR; fseek(fp, 0, SEEK_END); cert->length = (size_t) ftell(fp); if (UA_ByteString_allocBuffer (cert, cert->length) != UA_STATUSCODE_GOOD) { fclose (fp); return UA_STATUSCODE_BADOUTOFMEMORY; } fseek(fp, 0, SEEK_SET); size_t readLen = fread (cert->data, 1, cert->length, fp); if (readLen != cert->length) { UA_ByteString_clear (cert); cert->length = 0; fclose (fp); return UA_STATUSCODE_BADINTERNALERROR; } fclose (fp); return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_ReloadCertFromFolder (CertContext * ctx) { UA_StatusCode ret; struct dirent ** dirlist = NULL; int i; int numCertificates; char certFile[PATH_MAX]; UA_ByteString strCert; char folderPath[PATH_MAX]; UA_ByteString_init (&strCert); if (ctx->trustListFolder.length > 0) { UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Reloading the trust-list"); sk_X509_pop_free (ctx->skTrusted, X509_free); ctx->skTrusted = sk_X509_new_null(); if (ctx->skTrusted == NULL) { return UA_STATUSCODE_BADOUTOFMEMORY; } (void) memcpy (folderPath, ctx->trustListFolder.data, ctx->trustListFolder.length); folderPath[ctx->trustListFolder.length] = 0; numCertificates = scandir(folderPath, &dirlist, UA_Certificate_Filter_der_pem, alphasort); for (i = 0; i < numCertificates; i++) { if (UA_BuildFullPath (folderPath, dirlist[i]->d_name, PATH_MAX, certFile) != UA_STATUSCODE_GOOD) { continue; } ret = UA_loadCertFromFile (certFile, &strCert); if (ret != UA_STATUSCODE_GOOD) { UA_LOG_INFO (UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Failed to load the certificate file %s", certFile); continue; /* continue or return ? */ } if (UA_skTrusted_Cert2X509 (&strCert, 1, ctx) != UA_STATUSCODE_GOOD) { UA_LOG_INFO (UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Failed to decode the certificate file %s", certFile); UA_ByteString_clear (&strCert); continue; /* continue or return ? */ } UA_ByteString_clear (&strCert); } } if (ctx->issuerListFolder.length > 0) { UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Reloading the issuer-list"); sk_X509_pop_free (ctx->skIssue, X509_free); ctx->skIssue = sk_X509_new_null(); if (ctx->skIssue == NULL) { return UA_STATUSCODE_BADOUTOFMEMORY; } memcpy (folderPath, ctx->issuerListFolder.data, ctx->issuerListFolder.length); folderPath[ctx->issuerListFolder.length] = 0; numCertificates = scandir(folderPath, &dirlist, UA_Certificate_Filter_der_pem, alphasort); for (i = 0; i < numCertificates; i++) { if (UA_BuildFullPath (folderPath, dirlist[i]->d_name, PATH_MAX, certFile) != UA_STATUSCODE_GOOD) { continue; } ret = UA_loadCertFromFile (certFile, &strCert); if (ret != UA_STATUSCODE_GOOD) { UA_LOG_INFO (UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Failed to load the certificate file %s", certFile); continue; /* continue or return ? */ } if (UA_skIssuer_Cert2X509 (&strCert, 1, ctx) != UA_STATUSCODE_GOOD) { UA_LOG_INFO (UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Failed to decode the certificate file %s", certFile); UA_ByteString_clear (&strCert); continue; /* continue or return ? */ } UA_ByteString_clear (&strCert); } } if (ctx->revocationListFolder.length > 0) { UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Reloading the revocation-list"); sk_X509_CRL_pop_free (ctx->skCrls, X509_CRL_free); ctx->skCrls = sk_X509_CRL_new_null(); if (ctx->skCrls == NULL) { return UA_STATUSCODE_BADOUTOFMEMORY; } memcpy (folderPath, ctx->revocationListFolder.data, ctx->revocationListFolder.length); folderPath[ctx->revocationListFolder.length] = 0; numCertificates = scandir(folderPath, &dirlist, UA_Certificate_Filter_crl, alphasort); for (i = 0; i < numCertificates; i++) { if (UA_BuildFullPath (folderPath, dirlist[i]->d_name, PATH_MAX, certFile) != UA_STATUSCODE_GOOD) { continue; } ret = UA_loadCertFromFile (certFile, &strCert); if (ret != UA_STATUSCODE_GOOD) { UA_LOG_INFO (UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Failed to load the revocation file %s", certFile); continue; /* continue or return ? */ } if (UA_skCrls_Cert2X509 (&strCert, 1, ctx) != UA_STATUSCODE_GOOD) { UA_LOG_INFO (UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Failed to decode the revocation file %s", certFile); UA_ByteString_clear (&strCert); continue; /* continue or return ? */ } UA_ByteString_clear (&strCert); } } ret = UA_STATUSCODE_GOOD; return ret; } #endif /* end of __linux__ */ static UA_StatusCode UA_X509_Store_CTX_Error_To_UAError (int opensslErr) { UA_StatusCode ret; switch (opensslErr) { case X509_V_ERR_CERT_HAS_EXPIRED: case X509_V_ERR_CERT_NOT_YET_VALID: case X509_V_ERR_CRL_NOT_YET_VALID: case X509_V_ERR_CRL_HAS_EXPIRED: case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: ret = UA_STATUSCODE_BADCERTIFICATETIMEINVALID; break; case X509_V_ERR_CERT_REVOKED: ret = UA_STATUSCODE_BADCERTIFICATEREVOKED; break; case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: ret = UA_STATUSCODE_BADCERTIFICATEUNTRUSTED; break; case X509_V_ERR_CERT_SIGNATURE_FAILURE: case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: ret = UA_STATUSCODE_BADSECURITYCHECKSFAILED; break; case X509_V_ERR_UNABLE_TO_GET_CRL: ret = UA_STATUSCODE_BADCERTIFICATEREVOCATIONUNKNOWN; break; default: ret = UA_STATUSCODE_BADCERTIFICATEINVALID; break; } return ret; } static UA_StatusCode UA_CertificateVerification_Verify (void * verificationContext, const UA_ByteString * certificate) { X509_STORE_CTX* storeCtx; X509_STORE* store; CertContext * ctx; UA_StatusCode ret; int opensslRet; X509 * certificateX509 = NULL; if (verificationContext == NULL) { return UA_STATUSCODE_BADINTERNALERROR; } ctx = (CertContext *) verificationContext; store = X509_STORE_new(); storeCtx = X509_STORE_CTX_new(); if (store == NULL || storeCtx == NULL) { ret = UA_STATUSCODE_BADOUTOFMEMORY; goto cleanup; } #ifdef __linux__ ret = UA_ReloadCertFromFolder (ctx); if (ret != UA_STATUSCODE_GOOD) { goto cleanup; } #endif certificateX509 = UA_OpenSSL_LoadCertificate(certificate); if (certificateX509 == NULL) { ret = UA_STATUSCODE_BADCERTIFICATEINVALID; goto cleanup; } X509_STORE_set_flags(store, 0); opensslRet = X509_STORE_CTX_init (storeCtx, store, certificateX509, ctx->skIssue); if (opensslRet != 1) { ret = UA_STATUSCODE_BADINTERNALERROR; goto cleanup; } #if defined(OPENSSL_API_COMPAT) && OPENSSL_API_COMPAT < 0x10100000L (void) X509_STORE_CTX_trusted_stack (storeCtx, ctx->skTrusted); #else (void) X509_STORE_CTX_set0_trusted_stack (storeCtx, ctx->skTrusted); #endif /* Set crls to ctx */ if (sk_X509_CRL_num (ctx->skCrls) > 0) { X509_STORE_CTX_set0_crls (storeCtx, ctx->skCrls); } /* Set flag to check if the certificate has an invalid signature */ X509_STORE_CTX_set_flags (storeCtx, X509_V_FLAG_CHECK_SS_SIGNATURE); if (X509_STORE_CTX_get_check_issued(storeCtx) (storeCtx,certificateX509, certificateX509) != 1) { X509_STORE_CTX_set_flags (storeCtx, X509_V_FLAG_CRL_CHECK); } /* This condition will check whether the certificate is a User certificate or a CA certificate. * If the KU_KEY_CERT_SIGN and 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 */ /** \todo Can the ca-parameter of X509_check_purpose can be used? */ if(X509_check_purpose(certificateX509, X509_PURPOSE_CRL_SIGN, 0) && X509_check_ca(certificateX509)) { return UA_STATUSCODE_BADCERTIFICATEUSENOTALLOWED; } opensslRet = X509_verify_cert (storeCtx); if (opensslRet == 1) { ret = UA_STATUSCODE_GOOD; /* Check if the not trusted certificate has a CRL file. If there is no CRL file available for the corresponding * parent certificate then return status code UA_STATUSCODE_BADCERTIFICATEISSUERREVOCATIONUNKNOWN. Refer the test * case CTT/Security/Security Certificate Validation/002.js */ if (X509_STORE_CTX_get_check_issued (storeCtx) (storeCtx,certificateX509, certificateX509) != 1) { /* Free X509_STORE_CTX and reuse it for certification verification */ if (storeCtx != NULL) { X509_STORE_CTX_free(storeCtx); } /* Initialised X509_STORE_CTX sructure*/ storeCtx = X509_STORE_CTX_new(); /* Sets up X509_STORE_CTX structure for a subsequent verification operation */ X509_STORE_set_flags(store, 0); X509_STORE_CTX_init (storeCtx, store, certificateX509,ctx->skIssue); /* Set trust list to ctx */ (void) X509_STORE_CTX_trusted_stack (storeCtx, ctx->skTrusted); /* Set crls to ctx */ X509_STORE_CTX_set0_crls (storeCtx, ctx->skCrls); /* Set flags for CRL check */ X509_STORE_CTX_set_flags (storeCtx, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); opensslRet = X509_verify_cert (storeCtx); if (opensslRet != 1) { opensslRet = X509_STORE_CTX_get_error (storeCtx); if (opensslRet == X509_V_ERR_UNABLE_TO_GET_CRL) { ret = UA_STATUSCODE_BADCERTIFICATEISSUERREVOCATIONUNKNOWN; } } } } else { opensslRet = X509_STORE_CTX_get_error (storeCtx); /* Check the issued certificate of a CA that is not trusted but available */ if(opensslRet == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN){ int trusted_cert_len = sk_X509_num(ctx->skTrusted); int cmpVal; X509 *trusted_cert; const ASN1_OCTET_STRING *trusted_cert_keyid; const ASN1_OCTET_STRING *remote_cert_keyid; for (int i = 0; i < trusted_cert_len; i++) { trusted_cert = sk_X509_value(ctx->skTrusted, i); /* Fetch the Subject key identifier of the certificate in trust list */ trusted_cert_keyid = X509_get0_subject_key_id(trusted_cert); /* Fetch the Subject key identifier of the remote certificate */ remote_cert_keyid = X509_get0_subject_key_id(certificateX509); /* Check remote certificate is present in the trust list */ cmpVal = ASN1_OCTET_STRING_cmp(trusted_cert_keyid, remote_cert_keyid); if (cmpVal == 0){ ret = UA_STATUSCODE_GOOD; goto cleanup; } } } /* Return expected OPCUA error code */ ret = UA_X509_Store_CTX_Error_To_UAError (opensslRet); } cleanup: if (store != NULL) { X509_STORE_free (store); } if (storeCtx != NULL) { X509_STORE_CTX_free (storeCtx); } if (certificateX509 != NULL) { X509_free (certificateX509); } return ret; } static UA_StatusCode UA_CertificateVerification_VerifyApplicationURI (void * verificationContext, const UA_ByteString * certificate, const UA_String * applicationURI) { (void) verificationContext; const unsigned char * pData; X509 * certificateX509; UA_String subjectURI = UA_STRING_NULL; GENERAL_NAMES * pNames; int i; UA_StatusCode ret; pData = certificate->data; if (pData == NULL) { return UA_STATUSCODE_BADSECURITYCHECKSFAILED; } certificateX509 = UA_OpenSSL_LoadCertificate(certificate); if (certificateX509 == NULL) { return UA_STATUSCODE_BADSECURITYCHECKSFAILED; } pNames = (GENERAL_NAMES *) X509_get_ext_d2i(certificateX509, NID_subject_alt_name, NULL, NULL); if (pNames == NULL) { X509_free (certificateX509); return UA_STATUSCODE_BADSECURITYCHECKSFAILED; } UA_String_init(&subjectURI); for (i = 0; i < sk_GENERAL_NAME_num (pNames); i++) { GENERAL_NAME * value = sk_GENERAL_NAME_value (pNames, i); if (value->type == GEN_URI) { subjectURI.length = (size_t) (value->d.ia5->length); subjectURI.data = (UA_Byte *) UA_malloc (subjectURI.length); if (subjectURI.data == NULL) { X509_free (certificateX509); sk_GENERAL_NAME_pop_free(pNames, GENERAL_NAME_free); return UA_STATUSCODE_BADSECURITYCHECKSFAILED; } (void) memcpy (subjectURI.data, value->d.ia5->data, subjectURI.length); break; } } ret = UA_STATUSCODE_GOOD; if (UA_Bstrstr (subjectURI.data, subjectURI.length, applicationURI->data, applicationURI->length) == NULL) { ret = UA_STATUSCODE_BADCERTIFICATEURIINVALID; } X509_free (certificateX509); sk_GENERAL_NAME_pop_free(pNames, GENERAL_NAME_free); UA_String_clear (&subjectURI); return ret; } /* main entry */ 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) { UA_StatusCode ret; if (cv == NULL) { return UA_STATUSCODE_BADINTERNALERROR; } CertContext * context = (CertContext *) UA_malloc (sizeof (CertContext)); if (context == NULL) { return UA_STATUSCODE_BADOUTOFMEMORY; } ret = UA_CertContext_Init (context); if (ret != UA_STATUSCODE_GOOD) { return ret; } cv->verifyApplicationURI = UA_CertificateVerification_VerifyApplicationURI; cv->clear = UA_CertificateVerification_clear; cv->context = context; cv->verifyCertificate = UA_CertificateVerification_Verify; if (certificateTrustListSize > 0) { if (UA_skTrusted_Cert2X509 (certificateTrustList, certificateTrustListSize, context) != UA_STATUSCODE_GOOD) { ret = UA_STATUSCODE_BADINTERNALERROR; goto errout; } } if (certificateIssuerListSize > 0) { if (UA_skIssuer_Cert2X509 (certificateIssuerList, certificateIssuerListSize, context) != UA_STATUSCODE_GOOD) { ret = UA_STATUSCODE_BADINTERNALERROR; goto errout; } } if (certificateRevocationListSize > 0) { if (UA_skCrls_Cert2X509 (certificateRevocationList, certificateRevocationListSize, context) != UA_STATUSCODE_GOOD) { ret = UA_STATUSCODE_BADINTERNALERROR; goto errout; } } return UA_STATUSCODE_GOOD; errout: UA_CertificateVerification_clear (cv); return ret; } #ifdef __linux__ /* Linux only so far */ UA_StatusCode UA_CertificateVerification_CertFolders(UA_CertificateVerification * cv, const char * trustListFolder, const char * issuerListFolder, const char * revocationListFolder) { UA_StatusCode ret; if (cv == NULL) { return UA_STATUSCODE_BADINTERNALERROR; } CertContext * context = (CertContext *) UA_malloc (sizeof (CertContext)); if (context == NULL) { return UA_STATUSCODE_BADOUTOFMEMORY; } ret = UA_CertContext_Init (context); if (ret != UA_STATUSCODE_GOOD) { return ret; } cv->verifyApplicationURI = UA_CertificateVerification_VerifyApplicationURI; cv->clear = UA_CertificateVerification_clear; cv->context = context; cv->verifyCertificate = UA_CertificateVerification_Verify; /* Only set the folder paths. They will be reloaded during runtime. */ context->trustListFolder = UA_STRING_ALLOC(trustListFolder); context->issuerListFolder = UA_STRING_ALLOC(issuerListFolder); context->revocationListFolder = UA_STRING_ALLOC(revocationListFolder); return UA_STATUSCODE_GOOD; } #endif #endif /* end of defined(UA_ENABLE_ENCRYPTION_OPENSSL) || defined(UA_ENABLE_ENCRYPTION_LIBRESSL) */ /**** amalgamated original file "/plugins/crypto/mbedtls/securitypolicy_mbedtls_common.h" ****/ /* 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 2019 (c) Fraunhofer IOSB (Author: Julius Pfrommer) */ #if defined(UA_ENABLE_ENCRYPTION_MBEDTLS) || defined(UA_ENABLE_PUBSUB_ENCRYPTION) #include #include #include // MBEDTLS_ENTROPY_HARDWARE_ALT should be defined if your hardware does not supportd platform entropy #define UA_SHA1_LENGTH 20 _UA_BEGIN_DECLS void swapBuffers(UA_ByteString *const bufA, UA_ByteString *const bufB); void mbedtls_hmac(mbedtls_md_context_t *context, const UA_ByteString *key, const UA_ByteString *in, unsigned char *out); UA_StatusCode mbedtls_generateKey(mbedtls_md_context_t *context, const UA_ByteString *secret, const UA_ByteString *seed, UA_ByteString *out); UA_StatusCode mbedtls_verifySig_sha1(mbedtls_x509_crt *certificate, const UA_ByteString *message, const UA_ByteString *signature); UA_StatusCode mbedtls_sign_sha1(mbedtls_pk_context *localPrivateKey, mbedtls_ctr_drbg_context *drbgContext, const UA_ByteString *message, UA_ByteString *signature); UA_StatusCode mbedtls_thumbprint_sha1(const UA_ByteString *certificate, UA_ByteString *thumbprint); /* Set the hashing scheme before calling * E.g. mbedtls_rsa_set_padding(context, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA1); */ UA_StatusCode mbedtls_encrypt_rsaOaep(mbedtls_rsa_context *context, mbedtls_ctr_drbg_context *drbgContext, UA_ByteString *data, const size_t plainTextBlockSize); UA_StatusCode mbedtls_decrypt_rsaOaep(mbedtls_pk_context *localPrivateKey, mbedtls_ctr_drbg_context *drbgContext, UA_ByteString *data); int UA_mbedTLS_LoadPrivateKey(const UA_ByteString *key, mbedtls_pk_context *target, void *p_rng); UA_StatusCode UA_mbedTLS_LoadLocalCertificate(const UA_ByteString *certData, UA_ByteString *target); UA_ByteString UA_mbedTLS_CopyDataFormatAware(const UA_ByteString *data); _UA_END_DECLS #endif /**** amalgamated original file "/plugins/crypto/mbedtls/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-2019 (c) Mark Giraud, Fraunhofer IOSB * Copyright 2019 (c) Kalycito Infotech Private Limited * Copyright 2018 (c) HMS Industrial Networks AB (Author: Jonas Green) * Copyright 2020 (c) Wind River Systems, Inc. * Copyright 2020 (c) basysKom GmbH * */ #ifdef UA_ENABLE_ENCRYPTION_MBEDTLS #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 { 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(Basic128Rsa15_ChannelContext *cc, const UA_ByteString *message, const UA_ByteString *signature) { if(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(Basic128Rsa15_ChannelContext *cc, const UA_ByteString *message, UA_ByteString *signature) { if(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 Basic128Rsa15_ChannelContext *cc) { if(cc == NULL) return 0; #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 return mbedtls_pk_rsa(cc->policyContext->localPrivateKey)->len; #else return mbedtls_rsa_get_len(mbedtls_pk_rsa(cc->policyContext->localPrivateKey)); #endif } static size_t asym_getRemoteSignatureSize_sp_basic128rsa15(const Basic128Rsa15_ChannelContext *cc) { if(cc == NULL) return 0; #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 return mbedtls_pk_rsa(cc->remoteCertificate.pk)->len; #else return mbedtls_rsa_get_len(mbedtls_pk_rsa(cc->remoteCertificate.pk)); #endif } static UA_StatusCode asym_encrypt_sp_basic128rsa15(Basic128Rsa15_ChannelContext *cc, UA_ByteString *data) { if(cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; mbedtls_rsa_context *remoteRsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); mbedtls_rsa_set_padding(remoteRsaContext, MBEDTLS_RSA_PKCS_V15, MBEDTLS_MD_NONE); #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 size_t plainTextBlockSize = remoteRsaContext->len - UA_SECURITYPOLICY_BASIC128RSA15_RSAPADDING_LEN; #else size_t keylen = mbedtls_rsa_get_len(remoteRsaContext); size_t plainTextBlockSize = mbedtls_rsa_get_len(remoteRsaContext) - UA_SECURITYPOLICY_BASIC128RSA15_RSAPADDING_LEN; #endif if(data->length % plainTextBlockSize != 0) return UA_STATUSCODE_BADINTERNALERROR; size_t blocks = data->length / plainTextBlockSize; UA_ByteString encrypted; #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 UA_StatusCode retval = UA_ByteString_allocBuffer(&encrypted, blocks * remoteRsaContext->len); #else UA_StatusCode retval = UA_ByteString_allocBuffer(&encrypted, blocks * keylen); #endif 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_clear(&encrypted); return UA_STATUSCODE_BADINTERNALERROR; } inOffset += plainTextBlockSize; offset += outLength; lenDataToEncrypt -= plainTextBlockSize; } memcpy(data->data, encrypted.data, offset); UA_ByteString_clear(&encrypted); return UA_STATUSCODE_GOOD; } static UA_StatusCode asym_decrypt_sp_basic128rsa15(Basic128Rsa15_ChannelContext *cc, UA_ByteString *data) { if(cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; Basic128Rsa15_PolicyContext *pc = cc->policyContext; mbedtls_rsa_context *rsaContext = mbedtls_pk_rsa(pc->localPrivateKey); mbedtls_rsa_set_padding(rsaContext, MBEDTLS_RSA_PKCS_V15, MBEDTLS_MD_NONE); #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 size_t keylen = rsaContext->len; #else size_t keylen = mbedtls_rsa_get_len(rsaContext); #endif if(data->length % keylen != 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(&pc->localPrivateKey, data->data + inOffset, keylen, buf, &outLength, 512, mbedtls_ctr_drbg_random, &pc->drbgContext); if(mbedErr) return UA_STATUSCODE_BADSECURITYCHECKSFAILED; memcpy(data->data + outOffset, buf, outLength); inOffset += keylen; outOffset += outLength; } data->length = outOffset; return UA_STATUSCODE_GOOD; } static size_t asym_getLocalEncryptionKeyLength_sp_basic128rsa15(const Basic128Rsa15_ChannelContext *cc) { #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 mbedtls_rsa_context *const rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); return rsaContext->len; #else if(cc == NULL) return 0; return mbedtls_rsa_get_len(mbedtls_pk_rsa(cc->remoteCertificate.pk)); #endif } static size_t asym_getRemoteEncryptionKeyLength_sp_basic128rsa15(const Basic128Rsa15_ChannelContext *cc) { return mbedtls_pk_get_len(&cc->remoteCertificate.pk) * 8; } static size_t asym_getRemoteBlockSize_sp_basic128rsa15(const Basic128Rsa15_ChannelContext *cc) { #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 mbedtls_rsa_context *const rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); return rsaContext->len; #else if(cc == NULL) return 0; return mbedtls_rsa_get_len(mbedtls_pk_rsa(cc->remoteCertificate.pk)); #endif } static size_t asym_getRemotePlainTextBlockSize_sp_basic128rsa15(const Basic128Rsa15_ChannelContext *cc) { #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 mbedtls_rsa_context *const rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); return rsaContext->len - UA_SECURITYPOLICY_BASIC128RSA15_RSAPADDING_LEN; #else if(cc == NULL) return 0; return mbedtls_rsa_get_len(mbedtls_pk_rsa(cc->remoteCertificate.pk)) - UA_SECURITYPOLICY_BASIC128RSA15_RSAPADDING_LEN; #endif } 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(Basic128Rsa15_ChannelContext *cc, const UA_ByteString *message, const UA_ByteString *signature) { if(cc == NULL || message == NULL || signature == NULL) return UA_STATUSCODE_BADINTERNALERROR; /* Compute MAC */ if(signature->length != UA_SHA1_LENGTH) return UA_STATUSCODE_BADSECURITYCHECKSFAILED; Basic128Rsa15_PolicyContext *pc = cc->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 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 void *channelContext) { return UA_SHA1_LENGTH; } static size_t sym_getSigningKeyLength_sp_basic128rsa15(const void *const channelContext) { return UA_BASIC128RSA15_SYM_SIGNING_KEY_LENGTH; } static size_t sym_getEncryptionKeyLength_sp_basic128rsa15(const void *channelContext) { return UA_SECURITYPOLICY_BASIC128RSA15_SYM_KEY_LENGTH; } static size_t sym_getEncryptionBlockSize_sp_basic128rsa15(const void *const channelContext) { return UA_SECURITYPOLICY_BASIC128RSA15_SYM_ENCRYPTION_BLOCK_SIZE; } static size_t sym_getPlainTextBlockSize_sp_basic128rsa15(const void *const channelContext) { return UA_SECURITYPOLICY_BASIC128RSA15_SYM_PLAIN_TEXT_BLOCK_SIZE; } static UA_StatusCode sym_encrypt_sp_basic128rsa15(const Basic128Rsa15_ChannelContext *cc, UA_ByteString *data) { if(cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; if(cc->localSymIv.length != UA_SECURITYPOLICY_BASIC128RSA15_SYM_ENCRYPTION_BLOCK_SIZE) return UA_STATUSCODE_BADINTERNALERROR; size_t plainTextBlockSize = UA_SECURITYPOLICY_BASIC128RSA15_SYM_PLAIN_TEXT_BLOCK_SIZE; if(data->length % plainTextBlockSize != 0) 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_clear(&ivCopy); return retval; } static UA_StatusCode sym_decrypt_sp_basic128rsa15(const Basic128Rsa15_ChannelContext *cc, UA_ByteString *data) { if(cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; size_t encryptionBlockSize = UA_SECURITYPOLICY_BASIC128RSA15_SYM_ENCRYPTION_BLOCK_SIZE; if(cc->remoteSymIv.length != encryptionBlockSize) return UA_STATUSCODE_BADINTERNALERROR; if(data->length % encryptionBlockSize != 0) 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_clear(&ivCopy); return retval; } static UA_StatusCode sym_generateKey_sp_basic128rsa15(void *policyContext, const UA_ByteString *secret, const UA_ByteString *seed, UA_ByteString *out) { if(secret == NULL || seed == NULL || out == NULL) return UA_STATUSCODE_BADINTERNALERROR; Basic128Rsa15_PolicyContext *pc = (Basic128Rsa15_PolicyContext *)policyContext; return mbedtls_generateKey(&pc->sha1MdContext, secret, seed, out); } static UA_StatusCode sym_generateNonce_sp_basic128rsa15(void *policyContext, UA_ByteString *out) { if(out == NULL) return UA_STATUSCODE_BADINTERNALERROR; Basic128Rsa15_PolicyContext *pc = (Basic128Rsa15_PolicyContext *)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 */ #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 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; #else size_t keylen = mbedtls_rsa_get_len(mbedtls_pk_rsa(cc->remoteCertificate.pk)); if(keylen < UA_SECURITYPOLICY_BASIC128RSA15_MINASYMKEYLENGTH || keylen > UA_SECURITYPOLICY_BASIC128RSA15_MAXASYMKEYLENGTH) return UA_STATUSCODE_BADCERTIFICATEUSENOTALLOWED; #endif return UA_STATUSCODE_GOOD; } static void channelContext_deleteContext_sp_basic128rsa15(Basic128Rsa15_ChannelContext *cc) { UA_ByteString_clear(&cc->localSymSigningKey); UA_ByteString_clear(&cc->localSymEncryptingKey); UA_ByteString_clear(&cc->localSymIv); UA_ByteString_clear(&cc->remoteSymSigningKey); UA_ByteString_clear(&cc->remoteSymEncryptingKey); UA_ByteString_clear(&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_clear(&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_clear(&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_clear(&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_clear(&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_clear(&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_clear(&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 clear_sp_basic128rsa15(UA_SecurityPolicy *securityPolicy) { if(securityPolicy == NULL) return; UA_ByteString_clear(&securityPolicy->localCertificate); if(securityPolicy->policyContext == NULL) return; /* 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_clear(&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_clear(&securityPolicy->localCertificate); UA_StatusCode retval = UA_mbedTLS_LoadLocalCertificate(&newCertificate, &securityPolicy->localCertificate); if (retval != UA_STATUSCODE_GOOD) return retval; /* Set the new private key */ mbedtls_pk_free(&pc->localPrivateKey); mbedtls_pk_init(&pc->localPrivateKey); int mbedErr = UA_mbedTLS_LoadPrivateKey(&newPrivateKey, &pc->localPrivateKey, &pc->entropyContext); if(mbedErr) { retval = UA_STATUSCODE_BADSECURITYCHECKSFAILED; goto error; } retval = asym_makeThumbprint_sp_basic128rsa15(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) clear_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); /* 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; } mbedErr = mbedtls_entropy_self_test(0); 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 = UA_mbedTLS_LoadPrivateKey(&localPrivateKey, &pc->localPrivateKey, &pc->entropyContext); 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(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) clear_sp_basic128rsa15(securityPolicy); return retval; } UA_StatusCode UA_SecurityPolicy_Basic128Rsa15(UA_SecurityPolicy *policy, 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\0"); UA_SecurityPolicyAsymmetricModule *const asymmetricModule = &policy->asymmetricModule; UA_SecurityPolicySymmetricModule *const symmetricModule = &policy->symmetricModule; UA_SecurityPolicyChannelModule *const channelModule = &policy->channelModule; UA_StatusCode retval = UA_mbedTLS_LoadLocalCertificate(&localCertificate, &policy->localCertificate); if (retval != UA_STATUSCODE_GOOD) return retval; /* 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 (*)(void *, const UA_ByteString *, const UA_ByteString *))asym_verify_sp_basic128rsa15; asym_signatureAlgorithm->sign = (UA_StatusCode (*)(void *, const UA_ByteString *, UA_ByteString *))asym_sign_sp_basic128rsa15; asym_signatureAlgorithm->getLocalSignatureSize = (size_t (*)(const void *))asym_getLocalSignatureSize_sp_basic128rsa15; asym_signatureAlgorithm->getRemoteSignatureSize = (size_t (*)(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(*)(void *, UA_ByteString *))asym_encrypt_sp_basic128rsa15; asym_encryptionAlgorithm->decrypt = (UA_StatusCode(*)(void *, UA_ByteString *)) asym_decrypt_sp_basic128rsa15; asym_encryptionAlgorithm->getLocalKeyLength = (size_t (*)(const void *))asym_getLocalEncryptionKeyLength_sp_basic128rsa15; asym_encryptionAlgorithm->getRemoteKeyLength = (size_t (*)(const void *))asym_getRemoteEncryptionKeyLength_sp_basic128rsa15; asym_encryptionAlgorithm->getRemoteBlockSize = (size_t (*)(const void *))asym_getRemoteBlockSize_sp_basic128rsa15; asym_encryptionAlgorithm->getRemotePlainTextBlockSize = (size_t (*)(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 (*)(void *, const UA_ByteString *, const UA_ByteString *))sym_verify_sp_basic128rsa15; sym_signatureAlgorithm->sign = (UA_StatusCode (*)(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 void *))sym_getSigningKeyLength_sp_basic128rsa15; sym_signatureAlgorithm->getRemoteKeyLength = (size_t (*)(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(*)(void *, UA_ByteString *))sym_encrypt_sp_basic128rsa15; sym_encryptionAlgorithm->decrypt = (UA_StatusCode(*)(void *, UA_ByteString *))sym_decrypt_sp_basic128rsa15; sym_encryptionAlgorithm->getLocalKeyLength = sym_getEncryptionKeyLength_sp_basic128rsa15; sym_encryptionAlgorithm->getRemoteKeyLength = sym_getEncryptionKeyLength_sp_basic128rsa15; sym_encryptionAlgorithm->getRemoteBlockSize = (size_t (*)(const void *))sym_getEncryptionBlockSize_sp_basic128rsa15; sym_encryptionAlgorithm->getRemotePlainTextBlockSize = (size_t (*)(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->clear = clear_sp_basic128rsa15; UA_StatusCode res = policyContext_newContext_sp_basic128rsa15(policy, localPrivateKey); if(res != UA_STATUSCODE_GOOD) clear_sp_basic128rsa15(policy); return res; } #endif /**** amalgamated original file "/plugins/crypto/mbedtls/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 * Copyright 2020 (c) Wind River Systems, Inc. * Copyright 2020 (c) basysKom GmbH * */ #ifdef UA_ENABLE_ENCRYPTION_MBEDTLS #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 { 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(Basic256_ChannelContext *cc, const UA_ByteString *message, const UA_ByteString *signature) { if(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(Basic256_ChannelContext *cc, const UA_ByteString *message, UA_ByteString *signature) { if(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 Basic256_ChannelContext *cc) { if(cc == NULL) return 0; #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 return mbedtls_pk_rsa(cc->policyContext->localPrivateKey)->len; #else return mbedtls_rsa_get_len(mbedtls_pk_rsa(cc->policyContext->localPrivateKey)); #endif } static size_t asym_getRemoteSignatureSize_sp_basic256(const Basic256_ChannelContext *cc) { if(cc == NULL) return 0; #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 return mbedtls_pk_rsa(cc->remoteCertificate.pk)->len; #else return mbedtls_rsa_get_len(mbedtls_pk_rsa(cc->remoteCertificate.pk)); #endif } static size_t asym_getRemotePlainTextBlockSize_sp_basic256(const Basic256_ChannelContext *cc) { #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 mbedtls_rsa_context *const rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); return rsaContext->len - UA_SECURITYPOLICY_BASIC256SHA1_RSAPADDING_LEN; #else if(cc == NULL) return 0; return mbedtls_rsa_get_len(mbedtls_pk_rsa(cc->remoteCertificate.pk)) - UA_SECURITYPOLICY_BASIC256SHA1_RSAPADDING_LEN; #endif } /* AsymmetricEncryptionAlgorithm_RSA-OAEP-SHA1 */ static UA_StatusCode asym_encrypt_sp_basic256(Basic256_ChannelContext *cc, UA_ByteString *data) { if(cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; const size_t plainTextBlockSize = asym_getRemotePlainTextBlockSize_sp_basic256(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(Basic256_ChannelContext *cc, UA_ByteString *data) { if(cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; return mbedtls_decrypt_rsaOaep(&cc->policyContext->localPrivateKey, &cc->policyContext->drbgContext, data); } static size_t asym_getLocalEncryptionKeyLength_sp_basic256(const Basic256_ChannelContext *cc) { return mbedtls_pk_get_len(&cc->policyContext->localPrivateKey) * 8; } static size_t asym_getRemoteEncryptionKeyLength_sp_basic256(const Basic256_ChannelContext *cc) { return mbedtls_pk_get_len(&cc->remoteCertificate.pk) * 8; } static size_t asym_getRemoteBlockSize_sp_basic256(const Basic256_ChannelContext *cc) { #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 mbedtls_rsa_context *const rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); return rsaContext->len; #else if(cc == NULL) return 0; return mbedtls_rsa_get_len(mbedtls_pk_rsa(cc->remoteCertificate.pk)); #endif } 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(Basic256_ChannelContext *cc, const UA_ByteString *message, const UA_ByteString *signature) { if(cc == NULL || message == NULL || signature == NULL) return UA_STATUSCODE_BADINTERNALERROR; /* Compute MAC */ if(signature->length != UA_SHA1_LENGTH) return UA_STATUSCODE_BADSECURITYCHECKSFAILED; Basic256_PolicyContext *pc = cc->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 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 void *channelContext) { return UA_SHA1_LENGTH; } static size_t sym_getSigningKeyLength_sp_basic256(const void *const channelContext) { return UA_BASIC256_SYM_SIGNING_KEY_LENGTH; } static size_t sym_getEncryptionKeyLength_sp_basic256(const void *channelContext) { return UA_SECURITYPOLICY_BASIC256_SYM_KEY_LENGTH; } static size_t sym_getEncryptionBlockSize_sp_basic256(const void *const channelContext) { return UA_SECURITYPOLICY_BASIC256_SYM_ENCRYPTION_BLOCK_SIZE; } static size_t sym_getPlainTextBlockSize_sp_basic256(const void *const channelContext) { return UA_SECURITYPOLICY_BASIC256_SYM_PLAIN_TEXT_BLOCK_SIZE; } static UA_StatusCode sym_encrypt_sp_basic256(const Basic256_ChannelContext *cc, UA_ByteString *data) { if(cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; if(cc->localSymIv.length != UA_SECURITYPOLICY_BASIC256_SYM_ENCRYPTION_BLOCK_SIZE) return UA_STATUSCODE_BADINTERNALERROR; size_t plainTextBlockSize = UA_SECURITYPOLICY_BASIC256_SYM_PLAIN_TEXT_BLOCK_SIZE; if(data->length % plainTextBlockSize != 0) 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_clear(&ivCopy); return retval; } static UA_StatusCode sym_decrypt_sp_basic256(const Basic256_ChannelContext *cc, UA_ByteString *data) { if(cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; size_t encryptionBlockSize = UA_SECURITYPOLICY_BASIC256_SYM_ENCRYPTION_BLOCK_SIZE; if(cc->remoteSymIv.length != encryptionBlockSize) return UA_STATUSCODE_BADINTERNALERROR; if(data->length % encryptionBlockSize != 0) 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_clear(&ivCopy); return retval; } static UA_StatusCode sym_generateKey_sp_basic256(void *policyContext, const UA_ByteString *secret, const UA_ByteString *seed, UA_ByteString *out) { if(secret == NULL || seed == NULL || out == NULL) return UA_STATUSCODE_BADINTERNALERROR; Basic256_PolicyContext *pc = (Basic256_PolicyContext *)policyContext; return mbedtls_generateKey(&pc->sha1MdContext, secret, seed, out); } static UA_StatusCode sym_generateNonce_sp_basic256(void *policyContext, UA_ByteString *out) { if(out == NULL) return UA_STATUSCODE_BADINTERNALERROR; Basic256_PolicyContext *pc = (Basic256_PolicyContext *)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 */ #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 mbedtls_rsa_context *rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); if(rsaContext->len < UA_SECURITYPOLICY_BASIC256_MINASYMKEYLENGTH || rsaContext->len > UA_SECURITYPOLICY_BASIC256_MAXASYMKEYLENGTH) #else size_t keylen = mbedtls_rsa_get_len(mbedtls_pk_rsa(cc->remoteCertificate.pk)); if(keylen < UA_SECURITYPOLICY_BASIC256_MINASYMKEYLENGTH || keylen > UA_SECURITYPOLICY_BASIC256_MAXASYMKEYLENGTH) #endif return UA_STATUSCODE_BADCERTIFICATEUSENOTALLOWED; return UA_STATUSCODE_GOOD; } static void channelContext_deleteContext_sp_basic256(Basic256_ChannelContext *cc) { UA_ByteString_clear(&cc->localSymSigningKey); UA_ByteString_clear(&cc->localSymEncryptingKey); UA_ByteString_clear(&cc->localSymIv); UA_ByteString_clear(&cc->remoteSymSigningKey); UA_ByteString_clear(&cc->remoteSymEncryptingKey); UA_ByteString_clear(&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_clear(&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_clear(&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_clear(&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_clear(&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_clear(&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_clear(&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 clear_sp_basic256(UA_SecurityPolicy *securityPolicy) { if(securityPolicy == NULL) return; UA_ByteString_clear(&securityPolicy->localCertificate); if(securityPolicy->policyContext == NULL) return; /* 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_clear(&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_clear(&securityPolicy->localCertificate); UA_StatusCode retval = UA_mbedTLS_LoadLocalCertificate(&newCertificate, &securityPolicy->localCertificate); if (retval != UA_STATUSCODE_GOOD) return retval; /* Set the new private key */ mbedtls_pk_free(&pc->localPrivateKey); mbedtls_pk_init(&pc->localPrivateKey); int mbedErr = UA_mbedTLS_LoadPrivateKey(&newPrivateKey, &pc->localPrivateKey, &pc->entropyContext); if(mbedErr) { retval = UA_STATUSCODE_BADSECURITYCHECKSFAILED; goto error; } retval = asym_makeThumbprint_sp_basic256(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) clear_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); /* 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; } mbedErr = mbedtls_entropy_self_test(0); 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 = UA_mbedTLS_LoadPrivateKey(&localPrivateKey, &pc->localPrivateKey, &pc->entropyContext); 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(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) clear_sp_basic256(securityPolicy); return retval; } UA_StatusCode UA_SecurityPolicy_Basic256(UA_SecurityPolicy *policy, 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\0"); UA_SecurityPolicyAsymmetricModule *const asymmetricModule = &policy->asymmetricModule; UA_SecurityPolicySymmetricModule *const symmetricModule = &policy->symmetricModule; UA_SecurityPolicyChannelModule *const channelModule = &policy->channelModule; UA_StatusCode retval = UA_mbedTLS_LoadLocalCertificate(&localCertificate, &policy->localCertificate); if (retval != UA_STATUSCODE_GOOD) return retval; /* 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 (*)(void *, const UA_ByteString *, const UA_ByteString *))asym_verify_sp_basic256; asym_signatureAlgorithm->sign = (UA_StatusCode (*)(void *, const UA_ByteString *, UA_ByteString *))asym_sign_sp_basic256; asym_signatureAlgorithm->getLocalSignatureSize = (size_t (*)(const void *))asym_getLocalSignatureSize_sp_basic256; asym_signatureAlgorithm->getRemoteSignatureSize = (size_t (*)(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(*)(void *, UA_ByteString *))asym_encrypt_sp_basic256; asym_encryptionAlgorithm->decrypt = (UA_StatusCode(*)(void *, UA_ByteString *))asym_decrypt_sp_basic256; asym_encryptionAlgorithm->getLocalKeyLength = (size_t (*)(const void *))asym_getLocalEncryptionKeyLength_sp_basic256; asym_encryptionAlgorithm->getRemoteKeyLength = (size_t (*)(const void *))asym_getRemoteEncryptionKeyLength_sp_basic256; asym_encryptionAlgorithm->getRemoteBlockSize = (size_t (*)(const void *))asym_getRemoteBlockSize_sp_basic256; asym_encryptionAlgorithm->getRemotePlainTextBlockSize = (size_t (*)(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 (*)(void *, const UA_ByteString *, const UA_ByteString *))sym_verify_sp_basic256; sym_signatureAlgorithm->sign = (UA_StatusCode (*)(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 void *))sym_getSigningKeyLength_sp_basic256; sym_signatureAlgorithm->getRemoteKeyLength = (size_t (*)(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(*)(void *, UA_ByteString *))sym_encrypt_sp_basic256; sym_encryptionAlgorithm->decrypt = (UA_StatusCode(*)(void *, UA_ByteString *))sym_decrypt_sp_basic256; sym_encryptionAlgorithm->getLocalKeyLength = sym_getEncryptionKeyLength_sp_basic256; sym_encryptionAlgorithm->getRemoteKeyLength = sym_getEncryptionKeyLength_sp_basic256; sym_encryptionAlgorithm->getRemoteBlockSize = (size_t (*)(const void *))sym_getEncryptionBlockSize_sp_basic256; sym_encryptionAlgorithm->getRemotePlainTextBlockSize = (size_t (*)(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->clear = clear_sp_basic256; UA_StatusCode res = policyContext_newContext_sp_basic256(policy, localPrivateKey); if(res != UA_STATUSCODE_GOOD) clear_sp_basic256(policy); return res; } #endif /**** amalgamated original file "/plugins/crypto/mbedtls/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 * Copyright 2018 (c) HMS Industrial Networks AB (Author: Jonas Green) * Copyright 2020 (c) Wind River Systems, Inc. * Copyright 2020 (c) basysKom GmbH */ #ifdef UA_ENABLE_ENCRYPTION_MBEDTLS #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 { 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(Basic256Sha256_ChannelContext *cc, const UA_ByteString *message, const UA_ByteString *signature) { if(message == NULL || signature == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; unsigned char hash[UA_SHA256_LENGTH]; #if MBEDTLS_VERSION_NUMBER >= 0x02070000 && MBEDTLS_VERSION_NUMBER < 0x03000000 // 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(Basic256Sha256_ChannelContext *cc, const UA_ByteString *message, UA_ByteString *signature) { if(message == NULL || signature == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; unsigned char hash[UA_SHA256_LENGTH]; #if MBEDTLS_VERSION_NUMBER >= 0x02070000 && MBEDTLS_VERSION_NUMBER < 0x03000000 // 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, #if MBEDTLS_VERSION_NUMBER >= 0x03000000 signature->length, #endif &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 Basic256Sha256_ChannelContext *cc) { if(cc == NULL) return 0; #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 return mbedtls_pk_rsa(cc->policyContext->localPrivateKey)->len; #else return mbedtls_rsa_get_len(mbedtls_pk_rsa(cc->policyContext->localPrivateKey)); #endif } static size_t asym_getRemoteSignatureSize_sp_basic256sha256(const Basic256Sha256_ChannelContext *cc) { if(cc == NULL) return 0; #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 return mbedtls_pk_rsa(cc->remoteCertificate.pk)->len; #else return mbedtls_rsa_get_len(mbedtls_pk_rsa(cc->remoteCertificate.pk)); #endif } static size_t asym_getRemoteBlockSize_sp_basic256sha256(const Basic256Sha256_ChannelContext *cc) { #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 mbedtls_rsa_context *const rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); return rsaContext->len; #else if(cc == NULL) return 0; return mbedtls_rsa_get_len(mbedtls_pk_rsa(cc->remoteCertificate.pk)); #endif } static size_t asym_getRemotePlainTextBlockSize_sp_basic256sha256(const Basic256Sha256_ChannelContext *cc) { #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 mbedtls_rsa_context *const rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); return rsaContext->len - UA_SECURITYPOLICY_BASIC256SHA256_RSAPADDING_LEN; #else if(cc == NULL) return 0; return mbedtls_rsa_get_len(mbedtls_pk_rsa(cc->remoteCertificate.pk)) - UA_SECURITYPOLICY_BASIC256SHA256_RSAPADDING_LEN; #endif } /* AsymmetricEncryptionAlgorithm_RSA-OAEP-SHA1 */ static UA_StatusCode asym_encrypt_sp_basic256sha256(Basic256Sha256_ChannelContext *cc, UA_ByteString *data) { if(cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; const size_t plainTextBlockSize = asym_getRemotePlainTextBlockSize_sp_basic256sha256(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(Basic256Sha256_ChannelContext *cc, UA_ByteString *data) { if(cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; return mbedtls_decrypt_rsaOaep(&cc->policyContext->localPrivateKey, &cc->policyContext->drbgContext, data); } static size_t asym_getLocalEncryptionKeyLength_sp_basic256sha256(const Basic256Sha256_ChannelContext *cc) { return mbedtls_pk_get_len(&cc->policyContext->localPrivateKey) * 8; } static size_t asym_getRemoteEncryptionKeyLength_sp_basic256sha256(const Basic256Sha256_ChannelContext *cc) { return mbedtls_pk_get_len(&cc->remoteCertificate.pk) * 8; } 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(Basic256Sha256_ChannelContext *cc, const UA_ByteString *message, const UA_ByteString *signature) { if(cc == NULL || message == NULL || signature == NULL) return UA_STATUSCODE_BADINTERNALERROR; /* Compute MAC */ if(signature->length != UA_SHA256_LENGTH) return UA_STATUSCODE_BADSECURITYCHECKSFAILED; Basic256Sha256_PolicyContext *pc = cc->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 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 void *channelContext) { return UA_SHA256_LENGTH; } static size_t sym_getSigningKeyLength_sp_basic256sha256(const void *channelContext) { return UA_BASIC256SHA256_SYM_SIGNING_KEY_LENGTH; } static size_t sym_getEncryptionKeyLength_sp_basic256sha256(const void *channelContext) { return UA_SECURITYPOLICY_BASIC256SHA256_SYM_KEY_LENGTH; } static size_t sym_getEncryptionBlockSize_sp_basic256sha256(const void *channelContext) { return UA_SECURITYPOLICY_BASIC256SHA256_SYM_ENCRYPTION_BLOCK_SIZE; } static size_t sym_getPlainTextBlockSize_sp_basic256sha256(const void *channelContext) { return UA_SECURITYPOLICY_BASIC256SHA256_SYM_PLAIN_TEXT_BLOCK_SIZE; } static UA_StatusCode sym_encrypt_sp_basic256sha256(const Basic256Sha256_ChannelContext *cc, UA_ByteString *data) { if(cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; if(cc->localSymIv.length != UA_SECURITYPOLICY_BASIC256SHA256_SYM_ENCRYPTION_BLOCK_SIZE) return UA_STATUSCODE_BADINTERNALERROR; size_t plainTextBlockSize = UA_SECURITYPOLICY_BASIC256SHA256_SYM_PLAIN_TEXT_BLOCK_SIZE; if(data->length % plainTextBlockSize != 0) 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_clear(&ivCopy); return retval; } static UA_StatusCode sym_decrypt_sp_basic256sha256(const Basic256Sha256_ChannelContext *cc, UA_ByteString *data) { if(cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; size_t encryptionBlockSize = UA_SECURITYPOLICY_BASIC256SHA256_SYM_ENCRYPTION_BLOCK_SIZE; if(cc->remoteSymIv.length != encryptionBlockSize) return UA_STATUSCODE_BADINTERNALERROR; if(data->length % encryptionBlockSize != 0) 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_clear(&ivCopy); return retval; } static UA_StatusCode sym_generateKey_sp_basic256sha256(void *policyContext, const UA_ByteString *secret, const UA_ByteString *seed, UA_ByteString *out) { if(secret == NULL || seed == NULL || out == NULL) return UA_STATUSCODE_BADINTERNALERROR; Basic256Sha256_PolicyContext *pc = (Basic256Sha256_PolicyContext *)policyContext; return mbedtls_generateKey(&pc->sha256MdContext, secret, seed, out); } static UA_StatusCode sym_generateNonce_sp_basic256sha256(void *policyContext, UA_ByteString *out) { if(out == NULL) return UA_STATUSCODE_BADINTERNALERROR; Basic256Sha256_PolicyContext *pc = (Basic256Sha256_PolicyContext *)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 */ #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 mbedtls_rsa_context *rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); if(rsaContext->len < UA_SECURITYPOLICY_BASIC256SHA256_MINASYMKEYLENGTH || rsaContext->len > UA_SECURITYPOLICY_BASIC256SHA256_MAXASYMKEYLENGTH) #else size_t keylen = mbedtls_rsa_get_len(mbedtls_pk_rsa(cc->remoteCertificate.pk)); if(keylen < UA_SECURITYPOLICY_BASIC256SHA256_MINASYMKEYLENGTH || keylen > UA_SECURITYPOLICY_BASIC256SHA256_MAXASYMKEYLENGTH) #endif return UA_STATUSCODE_BADCERTIFICATEUSENOTALLOWED; return UA_STATUSCODE_GOOD; } static void channelContext_deleteContext_sp_basic256sha256(Basic256Sha256_ChannelContext *cc) { UA_ByteString_clear(&cc->localSymSigningKey); UA_ByteString_clear(&cc->localSymEncryptingKey); UA_ByteString_clear(&cc->localSymIv); UA_ByteString_clear(&cc->remoteSymSigningKey); UA_ByteString_clear(&cc->remoteSymEncryptingKey); UA_ByteString_clear(&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_clear(&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_clear(&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_clear(&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_clear(&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_clear(&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_clear(&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 clear_sp_basic256sha256(UA_SecurityPolicy *securityPolicy) { if(securityPolicy == NULL) return; UA_ByteString_clear(&securityPolicy->localCertificate); if(securityPolicy->policyContext == NULL) return; /* 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_clear(&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_clear(&securityPolicy->localCertificate); UA_StatusCode retval = UA_mbedTLS_LoadLocalCertificate(&newCertificate, &securityPolicy->localCertificate); if (retval != UA_STATUSCODE_GOOD) return retval; /* Set the new private key */ mbedtls_pk_free(&pc->localPrivateKey); mbedtls_pk_init(&pc->localPrivateKey); int mbedErr = UA_mbedTLS_LoadPrivateKey(&newPrivateKey, &pc->localPrivateKey, &pc->entropyContext); if(mbedErr) { retval = UA_STATUSCODE_BADSECURITYCHECKSFAILED; goto error; } retval = asym_makeThumbprint_sp_basic256sha256(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) clear_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); /* 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; } mbedErr = mbedtls_entropy_self_test(0); 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 = UA_mbedTLS_LoadPrivateKey(&localPrivateKey, &pc->localPrivateKey, &pc->entropyContext); 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(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) clear_sp_basic256sha256(securityPolicy); return retval; } UA_StatusCode UA_SecurityPolicy_Basic256Sha256(UA_SecurityPolicy *policy, 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; UA_StatusCode retval = UA_mbedTLS_LoadLocalCertificate(&localCertificate, &policy->localCertificate); if (retval != UA_STATUSCODE_GOOD) return retval; /* 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 (*)(void *, const UA_ByteString *, const UA_ByteString *))asym_verify_sp_basic256sha256; asym_signatureAlgorithm->sign = (UA_StatusCode (*)(void *, const UA_ByteString *, UA_ByteString *))asym_sign_sp_basic256sha256; asym_signatureAlgorithm->getLocalSignatureSize = (size_t (*)(const void *))asym_getLocalSignatureSize_sp_basic256sha256; asym_signatureAlgorithm->getRemoteSignatureSize = (size_t (*)(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(*)(void *, UA_ByteString *))asym_encrypt_sp_basic256sha256; asym_encryptionAlgorithm->decrypt = (UA_StatusCode(*)(void *, UA_ByteString *)) asym_decrypt_sp_basic256sha256; asym_encryptionAlgorithm->getLocalKeyLength = (size_t (*)(const void *))asym_getLocalEncryptionKeyLength_sp_basic256sha256; asym_encryptionAlgorithm->getRemoteKeyLength = (size_t (*)(const void *))asym_getRemoteEncryptionKeyLength_sp_basic256sha256; asym_encryptionAlgorithm->getRemoteBlockSize = (size_t (*)(const void *))asym_getRemoteBlockSize_sp_basic256sha256; asym_encryptionAlgorithm->getRemotePlainTextBlockSize = (size_t (*)(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 (*)(void *, const UA_ByteString *, const UA_ByteString *))sym_verify_sp_basic256sha256; sym_signatureAlgorithm->sign = (UA_StatusCode (*)(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 void *))sym_getSigningKeyLength_sp_basic256sha256; sym_signatureAlgorithm->getRemoteKeyLength = (size_t (*)(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#aes256-cbc"); sym_encryptionAlgorithm->encrypt = (UA_StatusCode(*)(void *, UA_ByteString *))sym_encrypt_sp_basic256sha256; sym_encryptionAlgorithm->decrypt = (UA_StatusCode(*)(void *, UA_ByteString *))sym_decrypt_sp_basic256sha256; sym_encryptionAlgorithm->getLocalKeyLength = sym_getEncryptionKeyLength_sp_basic256sha256; sym_encryptionAlgorithm->getRemoteKeyLength = sym_getEncryptionKeyLength_sp_basic256sha256; sym_encryptionAlgorithm->getRemoteBlockSize = (size_t (*)(const void *))sym_getEncryptionBlockSize_sp_basic256sha256; sym_encryptionAlgorithm->getRemotePlainTextBlockSize = (size_t (*)(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->clear = clear_sp_basic256sha256; UA_StatusCode res = policyContext_newContext_sp_basic256sha256(policy, localPrivateKey); if(res != UA_STATUSCODE_GOOD) clear_sp_basic256sha256(policy); return res; } #endif /**** amalgamated original file "/plugins/crypto/mbedtls/ua_securitypolicy_aes128sha256rsaoaep.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 2018 (c) HMS Industrial Networks AB (Author: Jonas Green) * Copyright 2020 (c) Wind River Systems, Inc. */ #ifdef UA_ENABLE_ENCRYPTION_MBEDTLS #include #include #include #include #include #include #include #include #include /* Notes: * mbedTLS' AES allows in-place encryption and decryption. So 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_AES128SHA256RSAOAEP_RSAPADDING_LEN 42 #define UA_SHA1_LENGTH 20 #define UA_SHA256_LENGTH 32 #define UA_AES128SHA256RSAOAEP_SYM_SIGNING_KEY_LENGTH 32 #define UA_SECURITYPOLICY_AES128SHA256RSAOAEP_SYM_KEY_LENGTH 16 #define UA_SECURITYPOLICY_AES128SHA256RSAOAEP_SYM_ENCRYPTION_BLOCK_SIZE 16 #define UA_SECURITYPOLICY_AES128SHA256RSAOAEP_SYM_PLAIN_TEXT_BLOCK_SIZE 16 #define UA_SECURITYPOLICY_AES128SHA256RSAOAEP_MINASYMKEYLENGTH 256 #define UA_SECURITYPOLICY_AES128SHA256RSAOAEP_MAXASYMKEYLENGTH 512 typedef struct { UA_ByteString localCertThumbprint; mbedtls_ctr_drbg_context drbgContext; mbedtls_entropy_context entropyContext; mbedtls_md_context_t sha256MdContext; mbedtls_pk_context localPrivateKey; } Aes128Sha256PsaOaep_PolicyContext; typedef struct { Aes128Sha256PsaOaep_PolicyContext *policyContext; UA_ByteString localSymSigningKey; UA_ByteString localSymEncryptingKey; UA_ByteString localSymIv; UA_ByteString remoteSymSigningKey; UA_ByteString remoteSymEncryptingKey; UA_ByteString remoteSymIv; mbedtls_x509_crt remoteCertificate; } Aes128Sha256PsaOaep_ChannelContext; /********************/ /* AsymmetricModule */ /********************/ /* VERIFY AsymmetricSignatureAlgorithm_RSA-PKCS15-SHA2-256 */ static UA_StatusCode asym_verify_sp_aes128sha256rsaoaep(Aes128Sha256PsaOaep_ChannelContext *cc, const UA_ByteString *message, const UA_ByteString *signature) { if(message == NULL || signature == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; unsigned char hash[UA_SHA256_LENGTH]; #if MBEDTLS_VERSION_NUMBER >= 0x02070000 && MBEDTLS_VERSION_NUMBER < 0x03000000 // 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_aes128sha256rsaoaep(Aes128Sha256PsaOaep_ChannelContext *cc, const UA_ByteString *message, UA_ByteString *signature) { if(message == NULL || signature == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; unsigned char hash[UA_SHA256_LENGTH]; #if MBEDTLS_VERSION_NUMBER >= 0x02070000 && MBEDTLS_VERSION_NUMBER < 0x03000000 // TODO check return status mbedtls_sha256_ret(message->data, message->length, hash, 0); #else mbedtls_sha256(message->data, message->length, hash, 0); #endif Aes128Sha256PsaOaep_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, #if MBEDTLS_VERSION_NUMBER >= 0x03000000 signature->length, #endif &sigLen, mbedtls_ctr_drbg_random, &pc->drbgContext); if(mbedErr) return UA_STATUSCODE_BADINTERNALERROR; return UA_STATUSCODE_GOOD; } static size_t asym_getLocalSignatureSize_sp_aes128sha256rsaoaep(const Aes128Sha256PsaOaep_ChannelContext *cc) { #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 return mbedtls_pk_rsa(cc->policyContext->localPrivateKey)->len; #else if(cc == NULL) return 0; return mbedtls_rsa_get_len(mbedtls_pk_rsa(cc->policyContext->localPrivateKey)); #endif } static size_t asym_getRemoteSignatureSize_sp_aes128sha256rsaoaep(const Aes128Sha256PsaOaep_ChannelContext *cc) { #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 return mbedtls_pk_rsa(cc->remoteCertificate.pk)->len; #else if(cc == NULL) return 0; return mbedtls_rsa_get_len(mbedtls_pk_rsa(cc->remoteCertificate.pk)); #endif } static size_t asym_getRemoteBlockSize_sp_aes128sha256rsaoaep(const Aes128Sha256PsaOaep_ChannelContext *cc) { #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 return mbedtls_pk_rsa(cc->remoteCertificate.pk)->len; #else if(cc == NULL) return 0; return mbedtls_rsa_get_len(mbedtls_pk_rsa(cc->remoteCertificate.pk)); #endif } static size_t asym_getRemotePlainTextBlockSize_sp_aes128sha256rsaoaep(const Aes128Sha256PsaOaep_ChannelContext *cc) { #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 mbedtls_rsa_context *const rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); return rsaContext->len - UA_SECURITYPOLICY_AES128SHA256RSAOAEP_RSAPADDING_LEN; #else if(cc == NULL) return 0; return mbedtls_rsa_get_len(mbedtls_pk_rsa(cc->remoteCertificate.pk)) - UA_SECURITYPOLICY_AES128SHA256RSAOAEP_RSAPADDING_LEN; #endif } /* AsymmetricEncryptionAlgorithm_RSA-OAEP-SHA1 */ static UA_StatusCode asym_encrypt_sp_aes128sha256rsaoaep(Aes128Sha256PsaOaep_ChannelContext *cc, UA_ByteString *data) { if(cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; const size_t plainTextBlockSize = asym_getRemotePlainTextBlockSize_sp_aes128sha256rsaoaep(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_aes128sha256rsaoaep(Aes128Sha256PsaOaep_ChannelContext *cc, UA_ByteString *data) { if(cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; return mbedtls_decrypt_rsaOaep(&cc->policyContext->localPrivateKey, &cc->policyContext->drbgContext, data); } static size_t asym_getLocalEncryptionKeyLength_sp_aes128sha256rsaoaep(const Aes128Sha256PsaOaep_ChannelContext *cc) { return mbedtls_pk_get_len(&cc->policyContext->localPrivateKey) * 8; } static size_t asym_getRemoteEncryptionKeyLength_sp_aes128sha256rsaoaep(const Aes128Sha256PsaOaep_ChannelContext *cc) { return mbedtls_pk_get_len(&cc->remoteCertificate.pk) * 8; } static UA_StatusCode asym_makeThumbprint_sp_aes128sha256rsaoaep(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_aes128sha256rsaoaep(const UA_SecurityPolicy *securityPolicy, const UA_ByteString *certificateThumbprint) { if(securityPolicy == NULL || certificateThumbprint == NULL) return UA_STATUSCODE_BADINTERNALERROR; Aes128Sha256PsaOaep_PolicyContext *pc = (Aes128Sha256PsaOaep_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_aes128sha256rsaoaep(Aes128Sha256PsaOaep_ChannelContext *cc, const UA_ByteString *message, const UA_ByteString *signature) { if(cc == NULL || message == NULL || signature == NULL) return UA_STATUSCODE_BADINTERNALERROR; /* Compute MAC */ if(signature->length != UA_SHA256_LENGTH) return UA_STATUSCODE_BADSECURITYCHECKSFAILED; Aes128Sha256PsaOaep_PolicyContext *pc = cc->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_aes128sha256rsaoaep(const Aes128Sha256PsaOaep_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_aes128sha256rsaoaep(const void *channelContext) { return UA_SHA256_LENGTH; } static size_t sym_getSigningKeyLength_sp_aes128sha256rsaoaep(const void *channelContext) { return UA_AES128SHA256RSAOAEP_SYM_SIGNING_KEY_LENGTH; } static size_t sym_getEncryptionKeyLength_sp_aes128sha256rsaoaep(const void *channelContext) { return UA_SECURITYPOLICY_AES128SHA256RSAOAEP_SYM_KEY_LENGTH; } static size_t sym_getEncryptionBlockSize_sp_aes128sha256rsaoaep(const void *channelContext) { return UA_SECURITYPOLICY_AES128SHA256RSAOAEP_SYM_ENCRYPTION_BLOCK_SIZE; } static size_t sym_getPlainTextBlockSize_sp_aes128sha256rsaoaep(const void *channelContext) { return UA_SECURITYPOLICY_AES128SHA256RSAOAEP_SYM_PLAIN_TEXT_BLOCK_SIZE; } static UA_StatusCode sym_encrypt_sp_aes128sha256rsaoaep(const Aes128Sha256PsaOaep_ChannelContext *cc, UA_ByteString *data) { if(cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; if(cc->localSymIv.length != UA_SECURITYPOLICY_AES128SHA256RSAOAEP_SYM_ENCRYPTION_BLOCK_SIZE) return UA_STATUSCODE_BADINTERNALERROR; size_t plainTextBlockSize = UA_SECURITYPOLICY_AES128SHA256RSAOAEP_SYM_PLAIN_TEXT_BLOCK_SIZE; if(data->length % plainTextBlockSize != 0) 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_clear(&ivCopy); return retval; } static UA_StatusCode sym_decrypt_sp_aes128sha256rsaoaep(const Aes128Sha256PsaOaep_ChannelContext *cc, UA_ByteString *data) { if(cc == NULL || data == NULL) return UA_STATUSCODE_BADINTERNALERROR; size_t encryptionBlockSize = UA_SECURITYPOLICY_AES128SHA256RSAOAEP_SYM_ENCRYPTION_BLOCK_SIZE; if(cc->remoteSymIv.length != encryptionBlockSize) return UA_STATUSCODE_BADINTERNALERROR; if(data->length % encryptionBlockSize != 0) 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_clear(&ivCopy); return retval; } static UA_StatusCode sym_generateKey_sp_aes128sha256rsaoaep(void *policyContext, const UA_ByteString *secret, const UA_ByteString *seed, UA_ByteString *out) { if(secret == NULL || seed == NULL || out == NULL) return UA_STATUSCODE_BADINTERNALERROR; Aes128Sha256PsaOaep_PolicyContext *pc = (Aes128Sha256PsaOaep_PolicyContext *)policyContext; return mbedtls_generateKey(&pc->sha256MdContext, secret, seed, out); } static UA_StatusCode sym_generateNonce_sp_aes128sha256rsaoaep(void *policyContext, UA_ByteString *out) { if(out == NULL) return UA_STATUSCODE_BADINTERNALERROR; Aes128Sha256PsaOaep_PolicyContext *pc = (Aes128Sha256PsaOaep_PolicyContext *)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_aes128sha256rsaoaep(Aes128Sha256PsaOaep_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 */ #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 mbedtls_rsa_context *rsaContext = mbedtls_pk_rsa(cc->remoteCertificate.pk); if(rsaContext->len < UA_SECURITYPOLICY_AES128SHA256RSAOAEP_MINASYMKEYLENGTH || rsaContext->len > UA_SECURITYPOLICY_AES128SHA256RSAOAEP_MAXASYMKEYLENGTH) #else size_t keylen = mbedtls_rsa_get_len(mbedtls_pk_rsa(cc->remoteCertificate.pk)); if(keylen < UA_SECURITYPOLICY_AES128SHA256RSAOAEP_MINASYMKEYLENGTH || keylen > UA_SECURITYPOLICY_AES128SHA256RSAOAEP_MAXASYMKEYLENGTH) #endif return UA_STATUSCODE_BADCERTIFICATEUSENOTALLOWED; return UA_STATUSCODE_GOOD; } static void channelContext_deleteContext_sp_aes128sha256rsaoaep(Aes128Sha256PsaOaep_ChannelContext *cc) { UA_ByteString_clear(&cc->localSymSigningKey); UA_ByteString_clear(&cc->localSymEncryptingKey); UA_ByteString_clear(&cc->localSymIv); UA_ByteString_clear(&cc->remoteSymSigningKey); UA_ByteString_clear(&cc->remoteSymEncryptingKey); UA_ByteString_clear(&cc->remoteSymIv); mbedtls_x509_crt_free(&cc->remoteCertificate); UA_free(cc); } static UA_StatusCode channelContext_newContext_sp_aes128sha256rsaoaep(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(Aes128Sha256PsaOaep_ChannelContext)); if(*pp_contextData == NULL) return UA_STATUSCODE_BADOUTOFMEMORY; Aes128Sha256PsaOaep_ChannelContext *cc = (Aes128Sha256PsaOaep_ChannelContext *)*pp_contextData; /* Initialize the channel context */ cc->policyContext = (Aes128Sha256PsaOaep_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_aes128sha256rsaoaep(cc, remoteCertificate); if(retval != UA_STATUSCODE_GOOD) { channelContext_deleteContext_sp_aes128sha256rsaoaep(cc); *pp_contextData = NULL; } return retval; } static UA_StatusCode channelContext_setLocalSymEncryptingKey_sp_aes128sha256rsaoaep(Aes128Sha256PsaOaep_ChannelContext *cc, const UA_ByteString *key) { if(key == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_clear(&cc->localSymEncryptingKey); return UA_ByteString_copy(key, &cc->localSymEncryptingKey); } static UA_StatusCode channelContext_setLocalSymSigningKey_sp_aes128sha256rsaoaep(Aes128Sha256PsaOaep_ChannelContext *cc, const UA_ByteString *key) { if(key == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_clear(&cc->localSymSigningKey); return UA_ByteString_copy(key, &cc->localSymSigningKey); } static UA_StatusCode channelContext_setLocalSymIv_sp_aes128sha256rsaoaep(Aes128Sha256PsaOaep_ChannelContext *cc, const UA_ByteString *iv) { if(iv == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_clear(&cc->localSymIv); return UA_ByteString_copy(iv, &cc->localSymIv); } static UA_StatusCode channelContext_setRemoteSymEncryptingKey_sp_aes128sha256rsaoaep(Aes128Sha256PsaOaep_ChannelContext *cc, const UA_ByteString *key) { if(key == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_clear(&cc->remoteSymEncryptingKey); return UA_ByteString_copy(key, &cc->remoteSymEncryptingKey); } static UA_StatusCode channelContext_setRemoteSymSigningKey_sp_aes128sha256rsaoaep(Aes128Sha256PsaOaep_ChannelContext *cc, const UA_ByteString *key) { if(key == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_clear(&cc->remoteSymSigningKey); return UA_ByteString_copy(key, &cc->remoteSymSigningKey); } static UA_StatusCode channelContext_setRemoteSymIv_sp_aes128sha256rsaoaep(Aes128Sha256PsaOaep_ChannelContext *cc, const UA_ByteString *iv) { if(iv == NULL || cc == NULL) return UA_STATUSCODE_BADINTERNALERROR; UA_ByteString_clear(&cc->remoteSymIv); return UA_ByteString_copy(iv, &cc->remoteSymIv); } static UA_StatusCode channelContext_compareCertificate_sp_aes128sha256rsaoaep(const Aes128Sha256PsaOaep_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 clear_sp_aes128sha256rsaoaep(UA_SecurityPolicy *securityPolicy) { if(securityPolicy == NULL) return; UA_ByteString_clear(&securityPolicy->localCertificate); if(securityPolicy->policyContext == NULL) return; /* delete all allocated members in the context */ Aes128Sha256PsaOaep_PolicyContext *pc = (Aes128Sha256PsaOaep_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_clear(&pc->localCertThumbprint); UA_LOG_DEBUG(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY, "Deleted members of EndpointContext for sp_aes128sha256rsaoaep"); UA_free(pc); securityPolicy->policyContext = NULL; } static UA_StatusCode updateCertificateAndPrivateKey_sp_aes128sha256rsaoaep(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; Aes128Sha256PsaOaep_PolicyContext *pc = (Aes128Sha256PsaOaep_PolicyContext *) securityPolicy->policyContext; UA_ByteString_clear(&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); #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 int mbedErr = mbedtls_pk_parse_key(&pc->localPrivateKey, newPrivateKey.data, newPrivateKey.length, NULL, 0); #else int mbedErr = mbedtls_pk_parse_key(&pc->localPrivateKey, newPrivateKey.data, newPrivateKey.length, NULL, 0, mbedtls_entropy_func, &pc->drbgContext); #endif if(mbedErr) { retval = UA_STATUSCODE_BADSECURITYCHECKSFAILED; goto error; } retval = asym_makeThumbprint_sp_aes128sha256rsaoaep(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) clear_sp_aes128sha256rsaoaep(securityPolicy); return retval; } static UA_StatusCode policyContext_newContext_sp_aes128sha256rsaoaep(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; } Aes128Sha256PsaOaep_PolicyContext *pc = (Aes128Sha256PsaOaep_PolicyContext *) UA_malloc(sizeof(Aes128Sha256PsaOaep_PolicyContext)); securityPolicy->policyContext = (void *)pc; if(!pc) { retval = UA_STATUSCODE_BADOUTOFMEMORY; goto error; } /* Initialize the PolicyContext */ memset(pc, 0, sizeof(Aes128Sha256PsaOaep_PolicyContext)); mbedtls_ctr_drbg_init(&pc->drbgContext); mbedtls_entropy_init(&pc->entropyContext); mbedtls_pk_init(&pc->localPrivateKey); mbedtls_md_init(&pc->sha256MdContext); /* 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; } mbedErr = mbedtls_entropy_self_test(0); 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 = UA_mbedTLS_LoadPrivateKey(&localPrivateKey, &pc->localPrivateKey, &pc->entropyContext); 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_aes128sha256rsaoaep(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) clear_sp_aes128sha256rsaoaep(securityPolicy); return retval; } UA_StatusCode UA_SecurityPolicy_Aes128Sha256RsaOaep(UA_SecurityPolicy *policy, 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#Aes128_Sha256_RsaOaep"); UA_SecurityPolicyAsymmetricModule *const asymmetricModule = &policy->asymmetricModule; UA_SecurityPolicySymmetricModule *const symmetricModule = &policy->symmetricModule; UA_SecurityPolicyChannelModule *const channelModule = &policy->channelModule; UA_StatusCode retval = UA_mbedTLS_LoadLocalCertificate(&localCertificate, &policy->localCertificate); if (retval != UA_STATUSCODE_GOOD) return retval; /* 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 (*)(void *, const UA_ByteString *, const UA_ByteString *))asym_verify_sp_aes128sha256rsaoaep; asym_signatureAlgorithm->sign = (UA_StatusCode (*)(void *, const UA_ByteString *, UA_ByteString *))asym_sign_sp_aes128sha256rsaoaep; asym_signatureAlgorithm->getLocalSignatureSize = (size_t (*)(const void *))asym_getLocalSignatureSize_sp_aes128sha256rsaoaep; asym_signatureAlgorithm->getRemoteSignatureSize = (size_t (*)(const void *))asym_getRemoteSignatureSize_sp_aes128sha256rsaoaep; 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(*)(void *, UA_ByteString *))asym_encrypt_sp_aes128sha256rsaoaep; asym_encryptionAlgorithm->decrypt = (UA_StatusCode(*)(void *, UA_ByteString *)) asym_decrypt_sp_aes128sha256rsaoaep; asym_encryptionAlgorithm->getLocalKeyLength = (size_t (*)(const void *))asym_getLocalEncryptionKeyLength_sp_aes128sha256rsaoaep; asym_encryptionAlgorithm->getRemoteKeyLength = (size_t (*)(const void *))asym_getRemoteEncryptionKeyLength_sp_aes128sha256rsaoaep; asym_encryptionAlgorithm->getRemoteBlockSize = (size_t (*)(const void *))asym_getRemoteBlockSize_sp_aes128sha256rsaoaep; asym_encryptionAlgorithm->getRemotePlainTextBlockSize = (size_t (*)(const void *))asym_getRemotePlainTextBlockSize_sp_aes128sha256rsaoaep; asymmetricModule->makeCertificateThumbprint = asym_makeThumbprint_sp_aes128sha256rsaoaep; asymmetricModule->compareCertificateThumbprint = asymmetricModule_compareCertificateThumbprint_sp_aes128sha256rsaoaep; /* SymmetricModule */ symmetricModule->generateKey = sym_generateKey_sp_aes128sha256rsaoaep; symmetricModule->generateNonce = sym_generateNonce_sp_aes128sha256rsaoaep; 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 (*)(void *, const UA_ByteString *, const UA_ByteString *))sym_verify_sp_aes128sha256rsaoaep; sym_signatureAlgorithm->sign = (UA_StatusCode (*)(void *, const UA_ByteString *, UA_ByteString *))sym_sign_sp_aes128sha256rsaoaep; sym_signatureAlgorithm->getLocalSignatureSize = sym_getSignatureSize_sp_aes128sha256rsaoaep; sym_signatureAlgorithm->getRemoteSignatureSize = sym_getSignatureSize_sp_aes128sha256rsaoaep; sym_signatureAlgorithm->getLocalKeyLength = (size_t (*)(const void *))sym_getSigningKeyLength_sp_aes128sha256rsaoaep; sym_signatureAlgorithm->getRemoteKeyLength = (size_t (*)(const void *))sym_getSigningKeyLength_sp_aes128sha256rsaoaep; 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(*)(void *, UA_ByteString *))sym_encrypt_sp_aes128sha256rsaoaep; sym_encryptionAlgorithm->decrypt = (UA_StatusCode(*)(void *, UA_ByteString *))sym_decrypt_sp_aes128sha256rsaoaep; sym_encryptionAlgorithm->getLocalKeyLength = sym_getEncryptionKeyLength_sp_aes128sha256rsaoaep; sym_encryptionAlgorithm->getRemoteKeyLength = sym_getEncryptionKeyLength_sp_aes128sha256rsaoaep; sym_encryptionAlgorithm->getRemoteBlockSize = (size_t (*)(const void *))sym_getEncryptionBlockSize_sp_aes128sha256rsaoaep; sym_encryptionAlgorithm->getRemotePlainTextBlockSize = (size_t (*)(const void *))sym_getPlainTextBlockSize_sp_aes128sha256rsaoaep; 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_aes128sha256rsaoaep; channelModule->deleteContext = (void (*)(void *)) channelContext_deleteContext_sp_aes128sha256rsaoaep; channelModule->setLocalSymEncryptingKey = (UA_StatusCode (*)(void *, const UA_ByteString *)) channelContext_setLocalSymEncryptingKey_sp_aes128sha256rsaoaep; channelModule->setLocalSymSigningKey = (UA_StatusCode (*)(void *, const UA_ByteString *)) channelContext_setLocalSymSigningKey_sp_aes128sha256rsaoaep; channelModule->setLocalSymIv = (UA_StatusCode (*)(void *, const UA_ByteString *)) channelContext_setLocalSymIv_sp_aes128sha256rsaoaep; channelModule->setRemoteSymEncryptingKey = (UA_StatusCode (*)(void *, const UA_ByteString *)) channelContext_setRemoteSymEncryptingKey_sp_aes128sha256rsaoaep; channelModule->setRemoteSymSigningKey = (UA_StatusCode (*)(void *, const UA_ByteString *)) channelContext_setRemoteSymSigningKey_sp_aes128sha256rsaoaep; channelModule->setRemoteSymIv = (UA_StatusCode (*)(void *, const UA_ByteString *)) channelContext_setRemoteSymIv_sp_aes128sha256rsaoaep; channelModule->compareCertificate = (UA_StatusCode (*)(const void *, const UA_ByteString *)) channelContext_compareCertificate_sp_aes128sha256rsaoaep; policy->updateCertificateAndPrivateKey = updateCertificateAndPrivateKey_sp_aes128sha256rsaoaep; policy->clear = clear_sp_aes128sha256rsaoaep; UA_StatusCode res = policyContext_newContext_sp_aes128sha256rsaoaep(policy, localPrivateKey); if(res != UA_STATUSCODE_GOOD) clear_sp_aes128sha256rsaoaep(policy); return res; } #endif /**** amalgamated original file "/plugins/crypto/mbedtls/ua_pki_mbedtls.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_MBEDTLS #include #include #include #include #define REMOTECERTIFICATETRUSTED 1 #define ISSUERKNOWN 2 #define DUALPARENT 3 #define PARENTFOUND 4 /* 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*/ for(; l > 0; ++s, --l) { if(*s == ch) return s; } return NULL; } static const unsigned char * UA_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; } // mbedTLS expects PEM data to be null terminated // The data length parameter must include the null terminator static UA_ByteString copyDataFormatAware(const UA_ByteString *data) { UA_ByteString result; UA_ByteString_init(&result); if (!data->length) return result; if (data->length && data->data[0] == '-') { UA_ByteString_allocBuffer(&result, data->length + 1); memcpy(result.data, data->data, data->length); result.data[data->length] = '\0'; } else { UA_ByteString_copy(data, &result); } return result; } 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]; char *res = realpath(buf, buf2); if(!res) { closedir(dir); return UA_STATUSCODE_BADINTERNALERROR; } 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; int internalErrorFlag = 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, mbedTLS error: %s (error code: %d)", f, errBuff, err); internalErrorFlag = 1; } } /* 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 { char errBuff[300]; mbedtls_strerror(err, errBuff, 300); UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Failed to load certificate from %.*s, mbedTLS error: %s (error code: %d)", (int)paths[i].length, paths[i].data, errBuff, err); internalErrorFlag = 1; } } 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 { char errBuff[300]; mbedtls_strerror(err, errBuff, 300); UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Failed to load certificate from %s, mbedTLS error: %s (error code: %d)", f, errBuff, err); internalErrorFlag = 1; } } if(internalErrorFlag) { retval = UA_STATUSCODE_BADINTERNALERROR; } 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 */ UA_StatusCode certFlag = reloadCertificates(ci); if(certFlag != UA_STATUSCODE_GOOD) { return certFlag; } #endif if(ci->trustListFolder.length == 0 && ci->issuerListFolder.length == 0 && ci->revocationListFolder.length == 0 && ci->certificateTrustList.raw.len == 0 && ci->certificateIssuerList.raw.len == 0 && ci->certificateRevocationList.raw.len == 0) { UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "PKI plugin unconfigured. Accepting the certificate."); return UA_STATUSCODE_GOOD; } /* Parse the certificate */ mbedtls_x509_crt remoteCertificate; /* Temporary Object to parse the trustList */ mbedtls_x509_crt *tempCert = NULL; /* Temporary Object to parse the revocationList */ mbedtls_x509_crl *tempCrl = NULL; /* Temporary Object to identify the parent CA when there is no intermediate CA */ mbedtls_x509_crt *parentCert = NULL; /* Temporary Object to identify the parent CA when there is intermediate CA */ mbedtls_x509_crt *parentCert_2 = NULL; /* 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; 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 MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 if((remoteCertificate.key_usage & MBEDTLS_X509_KU_KEY_CERT_SIGN) && (remoteCertificate.key_usage & MBEDTLS_X509_KU_CRL_SIGN)) { return UA_STATUSCODE_BADCERTIFICATEUSENOTALLOWED; } #else if((remoteCertificate.private_key_usage & MBEDTLS_X509_KU_KEY_CERT_SIGN) && (remoteCertificate.private_key_usage & MBEDTLS_X509_KU_CRL_SIGN)) { return UA_STATUSCODE_BADCERTIFICATEUSENOTALLOWED; } #endif UA_StatusCode retval = UA_STATUSCODE_GOOD; if(mbedErr) { #if UA_LOGLEVEL <= 400 char buff[100]; int len = mbedtls_x509_crt_verify_info(buff, 100, "", flags); UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_SECURITYPOLICY, "Verifying the certificate failed with error: %.*s", len-1, buff); #endif 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; } 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(UA_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_clear(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; cv->verifyCertificate = certificateVerification_verify; cv->clear = certificateVerification_clear; cv->verifyApplicationURI = certificateVerification_verifyApplicationURI; int err; UA_ByteString data; UA_ByteString_init(&data); for(size_t i = 0; i < certificateTrustListSize; i++) { data = copyDataFormatAware(&certificateTrustList[i]); err = mbedtls_x509_crt_parse(&ci->certificateTrustList, data.data, data.length); UA_ByteString_clear(&data); if(err) goto error; } for(size_t i = 0; i < certificateIssuerListSize; i++) { data = copyDataFormatAware(&certificateIssuerList[i]); err = mbedtls_x509_crt_parse(&ci->certificateIssuerList, data.data, data.length); UA_ByteString_clear(&data); if(err) goto error; } for(size_t i = 0; i < certificateRevocationListSize; i++) { data = copyDataFormatAware(&certificateRevocationList[i]); err = mbedtls_x509_crl_parse(&ci->certificateRevocationList, data.data, data.length); UA_ByteString_clear(&data); if(err) goto error; } return UA_STATUSCODE_GOOD; error: certificateVerification_clear(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->clear = certificateVerification_clear; cv->verifyApplicationURI = certificateVerification_verifyApplicationURI; return UA_STATUSCODE_GOOD; } #endif #endif /**** amalgamated original file "/plugins/crypto/mbedtls/securitypolicy_mbedtls_common.c" ****/ #if defined(UA_ENABLE_ENCRYPTION_MBEDTLS) || defined(UA_ENABLE_PUBSUB_ENCRYPTION) #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) { #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 size_t hashLen = (size_t)mbedtls_md_get_size(context->md_info); #else size_t hashLen = (size_t)mbedtls_md_get_size(context->private_md_info); #endif 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_clear(&A_and_seed); UA_ByteString_clear(&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_clear(&outSegment); UA_ByteString_clear(&A_and_seed); UA_ByteString_clear(&ANext_and_seed); return retval; } if(bufferAllocated) { memcpy(out->data + offset, outSegment.data, out->length - offset); UA_ByteString_clear(&outSegment); } swapBuffers(&ANext_and_seed, &A_and_seed); swapBuffers(&ANext, &A); } UA_ByteString_clear(&A_and_seed); UA_ByteString_clear(&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_VERSION_NUMBER < 0x03000000 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, MBEDTLS_MD_NONE); /* 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_VERSION_NUMBER < 0x03000000 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, MBEDTLS_MD_NONE); size_t sigLen = 0; int mbedErr = mbedtls_pk_sign(localPrivateKey, MBEDTLS_MD_SHA1, hash, UA_SHA1_LENGTH, signature->data, #if MBEDTLS_VERSION_NUMBER >= 0x03000000 signature->length, #endif &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_VERSION_NUMBER < 0x03000000 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; #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 UA_StatusCode retval = UA_ByteString_allocBuffer(&encrypted, max_blocks * context->len); #else size_t keylen = mbedtls_rsa_get_len(context); UA_StatusCode retval = UA_ByteString_allocBuffer(&encrypted, max_blocks * keylen); #endif 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) { #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 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); #else int mbedErr = mbedtls_rsa_rsaes_oaep_encrypt(context, mbedtls_ctr_drbg_random, drbgContext, label, 0, plainTextBlockSize, data->data + inOffset, encrypted.data + offset); #endif if(mbedErr) { UA_ByteString_clear(&encrypted); return UA_STATUSCODE_BADINTERNALERROR; } inOffset += plainTextBlockSize; #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 offset += context->len; #else offset += keylen; #endif lenDataToEncrypt -= plainTextBlockSize; } memcpy(data->data, encrypted.data, offset); UA_ByteString_clear(&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 MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 if(data->length % rsaContext->len != 0) return UA_STATUSCODE_BADINTERNALERROR; #else size_t keylen = mbedtls_rsa_get_len(rsaContext); if(data->length % keylen != 0) return UA_STATUSCODE_BADINTERNALERROR; #endif size_t inOffset = 0; size_t outOffset = 0; size_t outLength = 0; unsigned char buf[512]; while(inOffset < data->length) { #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 int mbedErr = mbedtls_rsa_rsaes_oaep_decrypt(rsaContext, mbedtls_ctr_drbg_random, drbgContext, MBEDTLS_RSA_PRIVATE, NULL, 0, &outLength, data->data + inOffset, buf, 512); #else int mbedErr = mbedtls_rsa_rsaes_oaep_decrypt(rsaContext, mbedtls_ctr_drbg_random, drbgContext, NULL, 0, &outLength, data->data + inOffset, buf, 512); #endif if(mbedErr) return UA_STATUSCODE_BADSECURITYCHECKSFAILED; memcpy(data->data + outOffset, buf, outLength); #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 inOffset += rsaContext->len; #else inOffset += keylen; #endif outOffset += outLength; } data->length = outOffset; return UA_STATUSCODE_GOOD; } int UA_mbedTLS_LoadPrivateKey(const UA_ByteString *key, mbedtls_pk_context *target, void *p_rng) { UA_ByteString data = UA_mbedTLS_CopyDataFormatAware(key); #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 int mbedErr = mbedtls_pk_parse_key(target, data.data, data.length, NULL, 0); #else int mbedErr = mbedtls_pk_parse_key(target, data.data, data.length, NULL, 0, mbedtls_entropy_func, p_rng); #endif UA_ByteString_clear(&data); return mbedErr; } UA_StatusCode UA_mbedTLS_LoadLocalCertificate(const UA_ByteString *certData, UA_ByteString *target) { UA_ByteString data = UA_mbedTLS_CopyDataFormatAware(certData); mbedtls_x509_crt cert; mbedtls_x509_crt_init(&cert); int mbedErr = mbedtls_x509_crt_parse(&cert, data.data, data.length); UA_StatusCode result = UA_STATUSCODE_BADINVALIDARGUMENT; if (!mbedErr) { UA_ByteString tmp; tmp.data = cert.raw.p; tmp.length = cert.raw.len; result = UA_ByteString_copy(&tmp, target); } else { UA_ByteString_init(target); } UA_ByteString_clear(&data); mbedtls_x509_crt_free(&cert); return result; } // mbedTLS expects PEM data to be null terminated // The data length parameter must include the null terminator UA_ByteString UA_mbedTLS_CopyDataFormatAware(const UA_ByteString *data) { UA_ByteString result; UA_ByteString_init(&result); if (!data->length) return result; if (data->length && data->data[0] == '-') { UA_ByteString_allocBuffer(&result, data->length + 1); memcpy(result.data, data->data, data->length); result.data[data->length] = '\0'; } else { UA_ByteString_copy(data, &result); } return result; } #endif /**** amalgamated original file "/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 /* 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 static const char *logLevelNames[6] = {"trace", "debug", ANSI_COLOR_GREEN "info", ANSI_COLOR_YELLOW "warn", ANSI_COLOR_RED "error", ANSI_COLOR_MAGENTA "fatal"}; static const char *logCategoryNames[7] = {"network", "channel", "session", "server", "client", "userland", "securitypolicy"}; typedef struct { UA_LogLevel minlevel; #if UA_MULTITHREADING >= 100 UA_Lock lock; #endif } LogContext; #ifdef __clang__ __attribute__((__format__(__printf__, 4 , 0))) #endif static void UA_Log_Stdout_log(void *context, UA_LogLevel level, UA_LogCategory category, const char *msg, va_list args) { LogContext *logContext = (LogContext*)context; if(logContext) { if(logContext->minlevel > level) return; UA_LOCK(&logContext->lock); } UA_Int64 tOffset = UA_DateTime_localTimeUtcOffset(); UA_DateTimeStruct dts = UA_DateTime_toStruct(UA_DateTime_now() + tOffset); 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); if(logContext) { UA_UNLOCK(&logContext->lock); } } static void UA_Log_Stdout_clear(void *context) { if(!context) return; UA_LOCK_DESTROY(&((LogContext*)context)->lock); UA_free(context); } const UA_Logger UA_Log_Stdout_ = {UA_Log_Stdout_log, NULL, UA_Log_Stdout_clear}; const UA_Logger *UA_Log_Stdout = &UA_Log_Stdout_; UA_Logger UA_Log_Stdout_withLevel(UA_LogLevel minlevel) { LogContext *context = (LogContext*)UA_calloc(1, sizeof(LogContext)); if(context) { UA_LOCK_INIT(&context->lock); context->minlevel = minlevel; } UA_Logger logger = {UA_Log_Stdout_log, (void*)context, UA_Log_Stdout_clear}; return logger; } /**** amalgamated original file "/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 * Copyright 2019 (c) HMS Industrial Networks AB (Author: Jonas Green) */ /* Example access control management. Anonymous and username / password login. * The access rights are maximally permissive. * * FOR PRODUCTION USE, THIS EXAMPLE PLUGIN SHOULD BE REPLACED WITH LESS * PERMISSIVE ACCESS CONTROL. * * For TransferSubscriptions, we check whether the transfer happens between * Sessions for the same user. */ typedef struct { UA_Boolean allowAnonymous; size_t usernamePasswordLoginSize; UA_UsernamePasswordLogin *usernamePasswordLogin; UA_UsernamePasswordLoginCallback loginCallback; void *loginContext; UA_CertificateVerification verifyX509; } AccessControlContext; #define ANONYMOUS_POLICY "open62541-anonymous-policy" #define CERTIFICATE_POLICY "open62541-certificate-policy" #define USERNAME_POLICY "open62541-username-policy" const UA_String anonymous_policy = UA_STRING_STATIC(ANONYMOUS_POLICY); const UA_String certificate_policy = UA_STRING_STATIC(CERTIFICATE_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; if(context->loginCallback) { if(context->loginCallback(&userToken->userName, &userToken->password, context->usernamePasswordLoginSize, context->usernamePasswordLogin, sessionContext, context->loginContext) == UA_STATUSCODE_GOOD) match = true; } else { 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; /* For the CTT, recognize whether two sessions are */ UA_ByteString *username = UA_ByteString_new(); if(username) UA_ByteString_copy(&userToken->userName, username); *sessionContext = username; return UA_STATUSCODE_GOOD; } /* x509 certificate */ if(userIdentityToken->content.decoded.type == &UA_TYPES[UA_TYPES_X509IDENTITYTOKEN]) { const UA_X509IdentityToken *userToken = (UA_X509IdentityToken*) userIdentityToken->content.decoded.data; if(!UA_String_equal(&userToken->policyId, &certificate_policy)) return UA_STATUSCODE_BADIDENTITYTOKENINVALID; if(!context->verifyX509.verifyCertificate) return UA_STATUSCODE_BADIDENTITYTOKENINVALID; return context->verifyX509. verifyCertificate(context->verifyX509.context, &userToken->certificateData); } /* Unsupported token type */ return UA_STATUSCODE_BADIDENTITYTOKENINVALID; } static void closeSession_default(UA_Server *server, UA_AccessControl *ac, const UA_NodeId *sessionId, void *sessionContext) { if(sessionContext) UA_ByteString_delete((UA_ByteString*)sessionContext); } 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; } static UA_Boolean allowBrowseNode_default(UA_Server *server, UA_AccessControl *ac, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, void *nodeContext) { return true; } #ifdef UA_ENABLE_SUBSCRIPTIONS static UA_Boolean allowTransferSubscription_default(UA_Server *server, UA_AccessControl *ac, const UA_NodeId *oldSessionId, void *oldSessionContext, const UA_NodeId *newSessionId, void *newSessionContext) { if(oldSessionContext == newSessionContext) return true; if(oldSessionContext && newSessionContext) return UA_ByteString_equal((UA_ByteString*)oldSessionContext, (UA_ByteString*)newSessionContext); return false; } #endif #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 clear_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_clear(&context->usernamePasswordLogin[i].username); UA_String_clear(&context->usernamePasswordLogin[i].password); } if(context->usernamePasswordLoginSize > 0) UA_free(context->usernamePasswordLogin); if(context->verifyX509.clear) context->verifyX509.clear(&context->verifyX509); UA_free(ac->context); ac->context = NULL; } } UA_StatusCode UA_AccessControl_default(UA_ServerConfig *config, UA_Boolean allowAnonymous, UA_CertificateVerification *verifyX509, const UA_ByteString *userTokenPolicyUri, size_t usernamePasswordLoginSize, const UA_UsernamePasswordLogin *usernamePasswordLogin) { UA_LOG_WARNING(&config->logger, UA_LOGCATEGORY_SERVER, "AccessControl: Unconfigured AccessControl. Users have all permissions."); UA_AccessControl *ac = &config->accessControl; if(ac->clear) clear_default(ac); ac->clear = clear_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; ac->allowBrowseNode = allowBrowseNode_default; #ifdef UA_ENABLE_SUBSCRIPTIONS ac->allowTransferSubscription = allowTransferSubscription_default; #endif #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; if(allowAnonymous) { UA_LOG_INFO(&config->logger, UA_LOGCATEGORY_SERVER, "AccessControl: Anonymous login is enabled"); } /* Allow x509 certificates? Move the plugin over. */ if(verifyX509) { context->verifyX509 = *verifyX509; memset(verifyX509, 0, sizeof(UA_CertificateVerification)); } else { memset(&context->verifyX509, 0, sizeof(UA_CertificateVerification)); UA_LOG_INFO(&config->logger, UA_LOGCATEGORY_SERVER, "AccessControl: x509 certificate user authentication is enabled"); } /* 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(verifyX509) 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); policies++; } if(verifyX509) { ac->userTokenPolicies[policies].tokenType = UA_USERTOKENTYPE_CERTIFICATE; ac->userTokenPolicies[policies].policyId = UA_STRING_ALLOC(CERTIFICATE_POLICY); #if UA_LOGLEVEL <= 400 if(UA_ByteString_equal(userTokenPolicyUri, &UA_SECURITY_POLICY_NONE_URI)) { UA_LOG_WARNING(&config->logger, UA_LOGCATEGORY_SERVER, "x509 Certificate Authentication configured, " "but no encrypting SecurityPolicy. " "This can leak credentials on the network."); } #endif UA_ByteString_copy(userTokenPolicyUri, &ac->userTokenPolicies[policies].securityPolicyUri); policies++; } if(usernamePasswordLoginSize > 0) { ac->userTokenPolicies[policies].tokenType = UA_USERTOKENTYPE_USERNAME; ac->userTokenPolicies[policies].policyId = UA_STRING_ALLOC(USERNAME_POLICY); #if UA_LOGLEVEL <= 400 if(UA_ByteString_equal(userTokenPolicyUri, &UA_SECURITY_POLICY_NONE_URI)) { UA_LOG_WARNING(&config->logger, UA_LOGCATEGORY_SERVER, "Username/Password Authentication configured, " "but no encrypting SecurityPolicy. " "This can leak credentials on the network."); } #endif UA_ByteString_copy(userTokenPolicyUri, &ac->userTokenPolicies[policies].securityPolicyUri); } return UA_STATUSCODE_GOOD; } UA_StatusCode UA_AccessControl_defaultWithLoginCallback(UA_ServerConfig *config, UA_Boolean allowAnonymous, UA_CertificateVerification *verifyX509, const UA_ByteString *userTokenPolicyUri, size_t usernamePasswordLoginSize, const UA_UsernamePasswordLogin *usernamePasswordLogin, UA_UsernamePasswordLoginCallback loginCallback, void *loginContext) { AccessControlContext *context; UA_StatusCode sc; sc = UA_AccessControl_default(config, allowAnonymous, verifyX509, userTokenPolicyUri, usernamePasswordLoginSize, usernamePasswordLogin); if (sc != UA_STATUSCODE_GOOD) return sc; context = (AccessControlContext *)config->accessControl.context; context->loginCallback = loginCallback; context->loginContext = loginContext; return UA_STATUSCODE_GOOD; } /**** amalgamated original file "/plugins/ua_nodestore_ziptree.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 container_of #define container_of(ptr, type, member) \ (type *)((uintptr_t)ptr - offsetof(type,member)) #endif 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; /* Maps ReferenceTypeIndex to the NodeId of the ReferenceType */ UA_NodeId referenceTypeIds[UA_REFERENCETYPESET_MAX]; UA_Byte referenceTypeCounter; } ZipContext; ZIP_FUNCTIONS(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->head.nodeClass = nodeClass; return entry; } static void deleteEntry(NodeEntry *entry) { UA_Node_clear((UA_Node*)&entry->nodeId); UA_free(entry); } static void cleanupEntry(NodeEntry *entry) { if(entry->refCount > 0) return; if(entry->deleted) { deleteEntry(entry); return; } UA_NodeHead *head = (UA_NodeHead*)&entry->nodeId; for(size_t i = 0; i < head->referencesSize; i++) { UA_NodeReferenceKind *rk = &head->references[i]; if(rk->targetsSize > 16 && !rk->hasRefTree) UA_NodeReferenceKind_switch(rk); } } /***********************/ /* Interface functions */ /***********************/ /* Not yet inserted into the ZipContext */ static UA_Node * zipNsNewNode(void *nsCtx, UA_NodeClass nodeClass) { NodeEntry *entry = newEntry(nodeClass); if(!entry) return NULL; return (UA_Node*)&entry->nodeId; } /* Not yet inserted into the ZipContext */ static void zipNsDeleteNode(void *nsCtx, UA_Node *node) { deleteEntry(container_of(node, NodeEntry, nodeId)); } static const UA_Node * zipNsGetNode(void *nsCtx, const UA_NodeId *nodeId) { ZipContext *ns = (ZipContext*)nsCtx; NodeEntry dummy; dummy.nodeIdHash = UA_NodeId_hash(nodeId); dummy.nodeId = *nodeId; NodeEntry *entry = ZIP_FIND(NodeTree, &ns->root, &dummy); if(!entry) return NULL; ++entry->refCount; return (const UA_Node*)&entry->nodeId; } static void zipNsReleaseNode(void *nsCtx, const UA_Node *node) { if(!node) return; NodeEntry *entry = container_of(node, NodeEntry, nodeId); UA_assert(entry->refCount > 0); --entry->refCount; cleanupEntry(entry); } static UA_StatusCode zipNsGetNodeCopy(void *nsCtx, const UA_NodeId *nodeId, UA_Node **outNode) { /* Find the node */ const UA_Node *node = zipNsGetNode(nsCtx, nodeId); if(!node) return UA_STATUSCODE_BADNODEIDUNKNOWN; /* Create the new entry */ NodeEntry *ne = newEntry(node->head.nodeClass); if(!ne) { zipNsReleaseNode(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); zipNsReleaseNode(nsCtx, node); if(retval != UA_STATUSCODE_GOOD) { deleteEntry(ne); return retval; } ne->orig = container_of(node, NodeEntry, nodeId); *outNode = nnode; return UA_STATUSCODE_GOOD; } static UA_StatusCode zipNsInsertNode(void *nsCtx, UA_Node *node, UA_NodeId *addedNodeId) { NodeEntry *entry = container_of(node, NodeEntry, nodeId); ZipContext *ns = (ZipContext*)nsCtx; /* Ensure that the NodeId is unique */ NodeEntry dummy; memset(&dummy, 0, sizeof(NodeEntry)); dummy.nodeId = node->head.nodeId; if(node->head.nodeId.identifierType == UA_NODEIDTYPE_NUMERIC && node->head.nodeId.identifier.numeric == 0) { do { /* Create a random nodeid until we find an unoccupied id */ UA_UInt32 numId = UA_UInt32_random(); #if SIZE_MAX <= UA_UINT32_MAX /* The compressed "immediate" representation of nodes does not * support the full range on 32bit systems. Generate smaller * identifiers as they can be stored more compactly. */ if(numId >= (0x01 << 24)) numId = numId % (0x01 << 24); #endif node->head.nodeId.identifier.numeric = numId; dummy.nodeId.identifier.numeric = numId; dummy.nodeIdHash = UA_NodeId_hash(&node->head.nodeId); } while(ZIP_FIND(NodeTree, &ns->root, &dummy)); } else { dummy.nodeIdHash = UA_NodeId_hash(&node->head.nodeId); if(ZIP_FIND(NodeTree, &ns->root, &dummy)) { /* The nodeid exists */ deleteEntry(entry); return UA_STATUSCODE_BADNODEIDEXISTS; } } /* Copy the NodeId */ if(addedNodeId) { UA_StatusCode retval = UA_NodeId_copy(&node->head.nodeId, addedNodeId); if(retval != UA_STATUSCODE_GOOD) { deleteEntry(entry); return retval; } } /* For new ReferencetypeNodes add to the index map */ if(node->head.nodeClass == UA_NODECLASS_REFERENCETYPE) { UA_ReferenceTypeNode *refNode = &node->referenceTypeNode; if(ns->referenceTypeCounter >= UA_REFERENCETYPESET_MAX) { deleteEntry(entry); return UA_STATUSCODE_BADINTERNALERROR; } UA_StatusCode retval = UA_NodeId_copy(&node->head.nodeId, &ns->referenceTypeIds[ns->referenceTypeCounter]); if(retval != UA_STATUSCODE_GOOD) { deleteEntry(entry); return UA_STATUSCODE_BADINTERNALERROR; } /* Assign the ReferenceTypeIndex to the new ReferenceTypeNode */ refNode->referenceTypeIndex = ns->referenceTypeCounter; refNode->subTypes = UA_REFTYPESET(ns->referenceTypeCounter); ns->referenceTypeCounter++; } /* Insert the node */ entry->nodeIdHash = dummy.nodeIdHash; ZIP_INSERT(NodeTree, &ns->root, entry, UA_UInt32_random()); return UA_STATUSCODE_GOOD; } static UA_StatusCode zipNsReplaceNode(void *nsCtx, UA_Node *node) { /* Find the node */ const UA_Node *oldNode = zipNsGetNode(nsCtx, &node->head.nodeId); if(!oldNode) { deleteEntry(container_of(node, NodeEntry, nodeId)); 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); zipNsReleaseNode(nsCtx, oldNode); return UA_STATUSCODE_BADINTERNALERROR; } /* Replace */ ZipContext *ns = (ZipContext*)nsCtx; ZIP_REMOVE(NodeTree, &ns->root, oldEntry); entry->nodeIdHash = oldEntry->nodeIdHash; ZIP_INSERT(NodeTree, &ns->root, entry, ZIP_RANK(entry, zipfields)); oldEntry->deleted = true; zipNsReleaseNode(nsCtx, oldNode); return UA_STATUSCODE_GOOD; } static UA_StatusCode zipNsRemoveNode(void *nsCtx, const UA_NodeId *nodeId) { ZipContext *ns = (ZipContext*)nsCtx; NodeEntry dummy; dummy.nodeIdHash = UA_NodeId_hash(nodeId); dummy.nodeId = *nodeId; NodeEntry *entry = ZIP_FIND(NodeTree, &ns->root, &dummy); if(!entry) return UA_STATUSCODE_BADNODEIDUNKNOWN; ZIP_REMOVE(NodeTree, &ns->root, entry); entry->deleted = true; cleanupEntry(entry); return UA_STATUSCODE_GOOD; } static const UA_NodeId * zipNsGetReferenceTypeId(void *nsCtx, UA_Byte refTypeIndex) { ZipContext *ns = (ZipContext*)nsCtx; if(refTypeIndex >= ns->referenceTypeCounter) return NULL; return &ns->referenceTypeIds[refTypeIndex]; } 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); } static void zipNsIterate(void *nsCtx, UA_NodestoreVisitor visitor, void *visitorCtx) { struct VisitorData d; d.visitor = visitor; d.visitorContext = visitorCtx; ZipContext *ns = (ZipContext*)nsCtx; ZIP_ITER(NodeTree, &ns->root, nodeVisitor, &d); } static void deleteNodeVisitor(NodeEntry *entry, void *data) { deleteEntry(entry); } /***********************/ /* Nodestore Lifecycle */ /***********************/ static void zipNsClear(void *nsCtx) { if (!nsCtx) return; ZipContext *ns = (ZipContext*)nsCtx; ZIP_ITER(NodeTree, &ns->root, deleteNodeVisitor, NULL); /* Clean up the ReferenceTypes index array */ for(size_t i = 0; i < ns->referenceTypeCounter; i++) UA_NodeId_clear(&ns->referenceTypeIds[i]); UA_free(ns); } UA_StatusCode UA_Nodestore_ZipTree(UA_Nodestore *ns) { /* Allocate and initialize the context */ ZipContext *ctx = (ZipContext*)UA_malloc(sizeof(ZipContext)); if(!ctx) return UA_STATUSCODE_BADOUTOFMEMORY; ZIP_INIT(&ctx->root); ctx->referenceTypeCounter = 0; /* Populate the nodestore */ ns->context = (void*)ctx; ns->clear = zipNsClear; ns->newNode = zipNsNewNode; ns->deleteNode = zipNsDeleteNode; ns->getNode = zipNsGetNode; ns->releaseNode = zipNsReleaseNode; ns->getNodeCopy = zipNsGetNodeCopy; ns->insertNode = zipNsInsertNode; ns->replaceNode = zipNsReplaceNode; ns->removeNode = zipNsRemoveNode; ns->getReferenceTypeId = zipNsGetReferenceTypeId; ns->iterate = zipNsIterate; return UA_STATUSCODE_GOOD; } /**** amalgamated original file "/plugins/ua_nodestore_hashmap.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-2019 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2017 (c) Julian Grothoff * Copyright 2017 (c) Stefan Profanter, fortiss GmbH */ #ifndef container_of #define container_of(ptr, type, member) \ (type *)((uintptr_t)ptr - offsetof(type,member)) #endif /* The default Nodestore is simply a hash-map from NodeIds to Nodes. To find an * entry, iterate over candidate positions according to the NodeId hash. * * - Tombstone or non-matching NodeId: continue searching * - Matching NodeId: Return the entry * - NULL: Abort the search */ typedef struct UA_NodeMapEntry { struct UA_NodeMapEntry *orig; /* the version this is a copy from (or NULL) */ 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 */ UA_Node node; } UA_NodeMapEntry; #define UA_NODEMAP_MINSIZE 64 #define UA_NODEMAP_TOMBSTONE ((UA_NodeMapEntry*)0x01) typedef struct { UA_NodeMapEntry *entry; UA_UInt32 nodeIdHash; } UA_NodeMapSlot; typedef struct { UA_NodeMapSlot *slots; UA_UInt32 size; UA_UInt32 count; UA_UInt32 sizePrimeIndex; /* Maps ReferenceTypeIndex to the NodeId of the ReferenceType */ UA_NodeId referenceTypeIds[UA_REFERENCETYPESET_MAX]; UA_Byte referenceTypeCounter; } UA_NodeMap; /*********************/ /* HashMap Utilities */ /*********************/ /* The size of the hash-map is always a prime number. They are chosen to be * close to the next power of 2. So the size ca. doubles with each prime. */ static UA_UInt32 const primes[] = { 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139, 524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393, 67108859, 134217689, 268435399, 536870909, 1073741789, 2147483647, 4294967291 }; static UA_UInt32 mod(UA_UInt32 h, UA_UInt32 size) { return h % size; } static UA_UInt32 mod2(UA_UInt32 h, UA_UInt32 size) { return 1 + (h % (size - 2)); } static UA_UInt16 higher_prime_index(UA_UInt32 n) { UA_UInt16 low = 0; UA_UInt16 high = (UA_UInt16)(sizeof(primes) / sizeof(UA_UInt32)); while(low != high) { UA_UInt16 mid = (UA_UInt16)(low + ((high - low) / 2)); if(n > primes[mid]) low = (UA_UInt16)(mid + 1); else high = mid; } return low; } /* Returns an empty slot or null if the nodeid exists or if no empty slot is found. */ static UA_NodeMapSlot * findFreeSlot(const UA_NodeMap *ns, const UA_NodeId *nodeid) { UA_UInt32 h = UA_NodeId_hash(nodeid); UA_UInt32 size = ns->size; UA_UInt64 idx = mod(h, size); /* Use 64bit container to avoid overflow */ UA_UInt32 startIdx = (UA_UInt32)idx; UA_UInt32 hash2 = mod2(h, size); UA_NodeMapSlot *candidate = NULL; do { UA_NodeMapSlot *slot = &ns->slots[(UA_UInt32)idx]; if(slot->entry > UA_NODEMAP_TOMBSTONE) { /* A Node with the NodeId does already exist */ if(slot->nodeIdHash == h && UA_NodeId_equal(&slot->entry->node.head.nodeId, nodeid)) return NULL; } else { /* Found a candidate node */ if(!candidate) candidate = slot; /* No matching node can come afterwards */ if(slot->entry == NULL) return candidate; } idx += hash2; if(idx >= size) idx -= size; } while((UA_UInt32)idx != startIdx); return candidate; } /* The occupancy of the table after the call will be about 50% */ static UA_StatusCode expand(UA_NodeMap *ns) { UA_UInt32 osize = ns->size; UA_UInt32 count = ns->count; /* Resize only when table after removal of unused elements is either too full or too empty */ if(count * 2 < osize && (count * 8 > osize || osize <= UA_NODEMAP_MINSIZE)) return UA_STATUSCODE_GOOD; UA_NodeMapSlot *oslots = ns->slots; UA_UInt32 nindex = higher_prime_index(count * 2); UA_UInt32 nsize = primes[nindex]; UA_NodeMapSlot *nslots= (UA_NodeMapSlot*)UA_calloc(nsize, sizeof(UA_NodeMapSlot)); if(!nslots) return UA_STATUSCODE_BADOUTOFMEMORY; ns->slots = nslots; ns->size = nsize; ns->sizePrimeIndex = nindex; /* recompute the position of every entry and insert the pointer */ for(size_t i = 0, j = 0; i < osize && j < count; ++i) { if(oslots[i].entry <= UA_NODEMAP_TOMBSTONE) continue; UA_NodeMapSlot *s = findFreeSlot(ns, &oslots[i].entry->node.head.nodeId); UA_assert(s); *s = oslots[i]; ++j; } UA_free(oslots); return UA_STATUSCODE_GOOD; } static UA_NodeMapEntry * createEntry(UA_NodeClass nodeClass) { size_t size = sizeof(UA_NodeMapEntry) - sizeof(UA_Node); 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; } UA_NodeMapEntry *entry = (UA_NodeMapEntry*)UA_calloc(1, size); if(!entry) return NULL; entry->node.head.nodeClass = nodeClass; return entry; } static void deleteNodeMapEntry(UA_NodeMapEntry *entry) { UA_Node_clear(&entry->node); UA_free(entry); } static void cleanupNodeMapEntry(UA_NodeMapEntry *entry) { if(entry->refCount > 0) return; if(entry->deleted) { deleteNodeMapEntry(entry); return; } for(size_t i = 0; i < entry->node.head.referencesSize; i++) { UA_NodeReferenceKind *rk = &entry->node.head.references[i]; if(rk->targetsSize > 16 && !rk->hasRefTree) UA_NodeReferenceKind_switch(rk); } } static UA_NodeMapSlot * findOccupiedSlot(const UA_NodeMap *ns, const UA_NodeId *nodeid) { UA_UInt32 h = UA_NodeId_hash(nodeid); UA_UInt32 size = ns->size; UA_UInt64 idx = mod(h, size); /* Use 64bit container to avoid overflow */ UA_UInt32 hash2 = mod2(h, size); UA_UInt32 startIdx = (UA_UInt32)idx; do { UA_NodeMapSlot *slot= &ns->slots[(UA_UInt32)idx]; if(slot->entry > UA_NODEMAP_TOMBSTONE) { if(slot->nodeIdHash == h && UA_NodeId_equal(&slot->entry->node.head.nodeId, nodeid)) return slot; } else { if(slot->entry == NULL) return NULL; /* No further entry possible */ } idx += hash2; if(idx >= size) idx -= size; } while((UA_UInt32)idx != startIdx); return NULL; } /***********************/ /* Interface functions */ /***********************/ static UA_Node * UA_NodeMap_newNode(void *context, UA_NodeClass nodeClass) { UA_NodeMapEntry *entry = createEntry(nodeClass); if(!entry) return NULL; return &entry->node; } static void UA_NodeMap_deleteNode(void *context, UA_Node *node) { UA_NodeMapEntry *entry = container_of(node, UA_NodeMapEntry, node); UA_assert(&entry->node == node); deleteNodeMapEntry(entry); } static const UA_Node * UA_NodeMap_getNode(void *context, const UA_NodeId *nodeid) { UA_NodeMap *ns = (UA_NodeMap*)context; UA_NodeMapSlot *slot = findOccupiedSlot(ns, nodeid); if(!slot) return NULL; ++slot->entry->refCount; return &slot->entry->node; } static void UA_NodeMap_releaseNode(void *context, const UA_Node *node) { if (!node) return; UA_NodeMapEntry *entry = container_of(node, UA_NodeMapEntry, node); UA_assert(&entry->node == node); UA_assert(entry->refCount > 0); --entry->refCount; cleanupNodeMapEntry(entry); } static UA_StatusCode UA_NodeMap_getNodeCopy(void *context, const UA_NodeId *nodeid, UA_Node **outNode) { UA_NodeMap *ns = (UA_NodeMap*)context; UA_NodeMapSlot *slot = findOccupiedSlot(ns, nodeid); if(!slot) return UA_STATUSCODE_BADNODEIDUNKNOWN; UA_NodeMapEntry *entry = slot->entry; UA_NodeMapEntry *newItem = createEntry(entry->node.head.nodeClass); if(!newItem) return UA_STATUSCODE_BADOUTOFMEMORY; UA_StatusCode retval = UA_Node_copy(&entry->node, &newItem->node); if(retval == UA_STATUSCODE_GOOD) { newItem->orig = entry; /* Store the pointer to the original */ *outNode = &newItem->node; } else { deleteNodeMapEntry(newItem); } return retval; } static UA_StatusCode UA_NodeMap_removeNode(void *context, const UA_NodeId *nodeid) { UA_NodeMap *ns = (UA_NodeMap*)context; UA_NodeMapSlot *slot = findOccupiedSlot(ns, nodeid); if(!slot) return UA_STATUSCODE_BADNODEIDUNKNOWN; UA_NodeMapEntry *entry = slot->entry; slot->entry = UA_NODEMAP_TOMBSTONE; UA_atomic_sync(); /* Set the tombstone before cleaning up. E.g. if the * nodestore is accessed from an interrupt. */ entry->deleted = true; cleanupNodeMapEntry(entry); --ns->count; /* Downsize the hashmap if it is very empty */ if(ns->count * 8 < ns->size && ns->size > UA_NODEMAP_MINSIZE) expand(ns); /* Can fail. Just continue with the bigger hashmap. */ return UA_STATUSCODE_GOOD; } /* * If this function fails in any way, the node parameter is deleted here, * so the caller function does not need to take care of it anymore */ static UA_StatusCode UA_NodeMap_insertNode(void *context, UA_Node *node, UA_NodeId *addedNodeId) { UA_NodeMap *ns = (UA_NodeMap*)context; if(ns->size * 3 <= ns->count * 4) { if(expand(ns) != UA_STATUSCODE_GOOD){ deleteNodeMapEntry(container_of(node, UA_NodeMapEntry, node)); return UA_STATUSCODE_BADINTERNALERROR; } } UA_NodeMapSlot *slot; if(node->head.nodeId.identifierType == UA_NODEIDTYPE_NUMERIC && node->head.nodeId.identifier.numeric == 0) { /* Create a random nodeid: Start at least with 50,000 to make sure we * don not conflict with nodes from the spec. If we find a conflict, we * just try another identifier until we have tried all possible * identifiers. Since the size is prime and we don't change the increase * val, we will reach the starting id again. E.g. adding a nodeset will * create children while there are still other nodes which need to be * created. Thus the node ids may collide. */ UA_UInt32 size = ns->size; UA_UInt64 identifier = mod(50000 + size+1, UA_UINT32_MAX); /* Use 64bit to * avoid overflow */ UA_UInt32 increase = mod2(ns->count+1, size); UA_UInt32 startId = (UA_UInt32)identifier; /* mod ensures us that the id * is a valid 32 bit integer */ do { node->head.nodeId.identifier.numeric = (UA_UInt32)identifier; slot = findFreeSlot(ns, &node->head.nodeId); if(slot) break; identifier += increase; if(identifier >= size) identifier -= size; #if SIZE_MAX <= UA_UINT32_MAX /* The compressed "immediate" representation of nodes does not * support the full range on 32bit systems. Generate smaller * identifiers as they can be stored more compactly. */ if(identifier >= (0x01 << 24)) identifier = identifier % (0x01 << 24); #endif } while((UA_UInt32)identifier != startId); } else { slot = findFreeSlot(ns, &node->head.nodeId); } if(!slot) { deleteNodeMapEntry(container_of(node, UA_NodeMapEntry, node)); return UA_STATUSCODE_BADNODEIDEXISTS; } /* Copy the NodeId */ UA_StatusCode retval = UA_STATUSCODE_GOOD; if(addedNodeId) { retval = UA_NodeId_copy(&node->head.nodeId, addedNodeId); if(retval != UA_STATUSCODE_GOOD) { deleteNodeMapEntry(container_of(node, UA_NodeMapEntry, node)); return retval; } } /* For new ReferencetypeNodes add to the index map */ if(node->head.nodeClass == UA_NODECLASS_REFERENCETYPE) { UA_ReferenceTypeNode *refNode = &node->referenceTypeNode; if(ns->referenceTypeCounter >= UA_REFERENCETYPESET_MAX) { deleteNodeMapEntry(container_of(node, UA_NodeMapEntry, node)); return UA_STATUSCODE_BADINTERNALERROR; } retval = UA_NodeId_copy(&node->head.nodeId, &ns->referenceTypeIds[ns->referenceTypeCounter]); if(retval != UA_STATUSCODE_GOOD) { deleteNodeMapEntry(container_of(node, UA_NodeMapEntry, node)); return UA_STATUSCODE_BADINTERNALERROR; } /* Assign the ReferenceTypeIndex to the new ReferenceTypeNode */ refNode->referenceTypeIndex = ns->referenceTypeCounter; refNode->subTypes = UA_REFTYPESET(ns->referenceTypeCounter); ns->referenceTypeCounter++; } /* Insert the node */ UA_NodeMapEntry *newEntry = container_of(node, UA_NodeMapEntry, node); slot->nodeIdHash = UA_NodeId_hash(&node->head.nodeId); UA_atomic_sync(); /* Set the hash first */ slot->entry = newEntry; ++ns->count; return retval; } static UA_StatusCode UA_NodeMap_replaceNode(void *context, UA_Node *node) { UA_NodeMap *ns = (UA_NodeMap*)context; UA_NodeMapEntry *newEntry = container_of(node, UA_NodeMapEntry, node); /* Find the node */ UA_NodeMapSlot *slot = findOccupiedSlot(ns, &node->head.nodeId); if(!slot) { deleteNodeMapEntry(newEntry); return UA_STATUSCODE_BADNODEIDUNKNOWN; } /* The node was already updated since the copy was made? */ UA_NodeMapEntry *oldEntry = slot->entry; if(oldEntry != newEntry->orig) { deleteNodeMapEntry(newEntry); return UA_STATUSCODE_BADINTERNALERROR; } /* Replace the entry */ slot->entry = newEntry; UA_atomic_sync(); oldEntry->deleted = true; cleanupNodeMapEntry(oldEntry); return UA_STATUSCODE_GOOD; } static const UA_NodeId * UA_NodeMap_getReferenceTypeId(void *nsCtx, UA_Byte refTypeIndex) { UA_NodeMap *ns = (UA_NodeMap*)nsCtx; if(refTypeIndex >= ns->referenceTypeCounter) return NULL; return &ns->referenceTypeIds[refTypeIndex]; } static void UA_NodeMap_iterate(void *context, UA_NodestoreVisitor visitor, void *visitorContext) { UA_NodeMap *ns = (UA_NodeMap*)context; for(UA_UInt32 i = 0; i < ns->size; ++i) { UA_NodeMapSlot *slot = &ns->slots[i]; if(slot->entry > UA_NODEMAP_TOMBSTONE) { /* The visitor can delete the node. So refcount here. */ slot->entry->refCount++; visitor(visitorContext, &slot->entry->node); slot->entry->refCount--; cleanupNodeMapEntry(slot->entry); } } } static void UA_NodeMap_delete(void *context) { /* Already cleaned up? */ if(!context) return; UA_NodeMap *ns = (UA_NodeMap*)context; UA_UInt32 size = ns->size; UA_NodeMapSlot *slots = ns->slots; for(UA_UInt32 i = 0; i < size; ++i) { if(slots[i].entry > UA_NODEMAP_TOMBSTONE) { /* On debugging builds, check that all nodes were release */ UA_assert(slots[i].entry->refCount == 0); /* Delete the node */ deleteNodeMapEntry(slots[i].entry); } } UA_free(ns->slots); /* Clean up the ReferenceTypes index array */ for(size_t i = 0; i < ns->referenceTypeCounter; i++) UA_NodeId_clear(&ns->referenceTypeIds[i]); UA_free(ns); } UA_StatusCode UA_Nodestore_HashMap(UA_Nodestore *ns) { /* Allocate and initialize the nodemap */ UA_NodeMap *nodemap = (UA_NodeMap*)UA_malloc(sizeof(UA_NodeMap)); if(!nodemap) return UA_STATUSCODE_BADOUTOFMEMORY; nodemap->sizePrimeIndex = higher_prime_index(UA_NODEMAP_MINSIZE); nodemap->size = primes[nodemap->sizePrimeIndex]; nodemap->count = 0; nodemap->slots = (UA_NodeMapSlot*) UA_calloc(nodemap->size, sizeof(UA_NodeMapSlot)); if(!nodemap->slots) { UA_free(nodemap); return UA_STATUSCODE_BADOUTOFMEMORY; } nodemap->referenceTypeCounter = 0; /* Populate the nodestore */ ns->context = nodemap; ns->clear = UA_NodeMap_delete; ns->newNode = UA_NodeMap_newNode; ns->deleteNode = UA_NodeMap_deleteNode; ns->getNode = UA_NodeMap_getNode; ns->releaseNode = UA_NodeMap_releaseNode; ns->getNodeCopy = UA_NodeMap_getNodeCopy; ns->insertNode = UA_NodeMap_insertNode; ns->replaceNode = UA_NodeMap_replaceNode; ns->removeNode = UA_NodeMap_removeNode; ns->getReferenceTypeId = UA_NodeMap_getReferenceTypeId; ns->iterate = UA_NodeMap_iterate; return UA_STATUSCODE_GOOD; } /**** amalgamated original file "/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 * Copyright 2017-2020 (c) HMS Industrial Networks AB (Author: Jonas Green) * Copyright 2020 (c) Wind River Systems, Inc. */ #ifdef UA_ENABLE_WEBSOCKET_SERVER #endif /* 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; } UA_Server * UA_Server_new(void) { UA_ServerConfig config; memset(&config, 0, sizeof(UA_ServerConfig)); /* Set a default logger and NodeStore for the initialization */ config.logger = UA_Log_Stdout_; if(UA_STATUSCODE_GOOD != UA_Nodestore_HashMap(&config.nodestore)) { return NULL; } return UA_Server_newWithConfig(&config); } /*******************************/ /* Default Connection Settings */ /*******************************/ const UA_ConnectionConfig UA_ConnectionConfig_default = { 0, /* .protocolVersion */ 2 << 16, /* .sendBufferSize, 64k per chunk */ 2 << 16, /* .recvBufferSize, 64k per chunk */ 2 << 29, /* .localMaxMessageSize, 512 MB */ 2 << 29, /* .remoteMaxMessageSize, 512 MB */ 2 << 14, /* .localMaxChunkCount, 16k */ 2 << 14 /* .remoteMaxChunkCount, 16k */ }; /***************************/ /* 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){ UA_String_clear(&endpoint->securityPolicyUri); UA_String_clear(&endpoint->transportProfileUri); 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; if(conf->nodestore.context == NULL) UA_Nodestore_HashMap(&conf->nodestore); /* --> Start setting the default static config <-- */ /* Allow user to set his own logger */ if(!conf->logger.log) conf->logger = UA_Log_Stdout_withLevel(UA_LOGLEVEL_INFO); conf->shutdownDelay = 0.0; /* Server Description */ UA_BuildInfo_clear(&conf->buildInfo); 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(); UA_ApplicationDescription_clear(&conf->applicationDescription); 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_clear(&conf->mdnsConfig); conf->mdnsInterfaceIP = UA_STRING_NULL; # if !defined(UA_HAS_GETIFADDR) conf->mdnsIpAddressList = NULL; conf->mdnsIpAddressListSize = 0; # endif #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; */ conf->modellingRulesOnInstances = UA_TRUE; /* 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 */ #ifdef UA_ENABLE_SUBSCRIPTIONS /* 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); #endif #ifdef UA_ENABLE_DISCOVERY conf->discoveryCleanupTimeout = 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 #if UA_MULTITHREADING >= 100 conf->maxAsyncOperationQueueSize = 0; conf->asyncOperationTimeout = 120000; /* Async Operation Timeout in ms (2 minutes) */ #endif /* --> Finish setting the default static config <-- */ return UA_STATUSCODE_GOOD; } UA_EXPORT UA_StatusCode UA_ServerConfig_setBasics(UA_ServerConfig* conf) { UA_StatusCode res = setDefaultConfig(conf); UA_LOG_WARNING(&conf->logger, UA_LOGCATEGORY_USERLAND, "AcceptAll Certificate Verification. " "Any remote certificate will be accepted."); return res; } static UA_StatusCode addDefaultNetworkLayers(UA_ServerConfig *conf, UA_UInt16 portNumber, UA_UInt32 sendBufferSize, UA_UInt32 recvBufferSize) { return UA_ServerConfig_addNetworkLayerTCP(conf, portNumber, sendBufferSize, recvBufferSize); } #ifdef UA_ENABLE_WEBSOCKET_SERVER UA_EXPORT UA_StatusCode UA_ServerConfig_addNetworkLayerWS(UA_ServerConfig *conf, UA_UInt16 portNumber, UA_UInt32 sendBufferSize, UA_UInt32 recvBufferSize, const UA_ByteString* certificate, const UA_ByteString* privateKey) { /* 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_ServerNetworkLayerWS(config, portNumber, certificate, privateKey); if(!conf->networkLayers[conf->networkLayersSize].handle) return UA_STATUSCODE_BADOUTOFMEMORY; conf->networkLayersSize++; return UA_STATUSCODE_GOOD; } #endif 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, 0); 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) { /* 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; UA_StatusCode retval = UA_SecurityPolicy_None(&config->securityPolicies[config->securityPoliciesSize], localCertificate, &config->logger); if(retval != UA_STATUSCODE_GOOD) { if(config->securityPoliciesSize == 0) { UA_free(config->securityPolicies); config->securityPolicies = NULL; } 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) { /* 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 */ UA_StatusCode 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) { /* 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)) { UA_StatusCode retval = createEndpoint(config, &config->endpoints[config->endpointsSize], &config->securityPolicies[i], UA_MESSAGESECURITYMODE_NONE); if(retval != UA_STATUSCODE_GOOD) return retval; config->endpointsSize++; } else { UA_StatusCode 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, NULL, &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; } UA_LOG_WARNING(&config->logger, UA_LOGCATEGORY_USERLAND, "AcceptAll Certificate Verification. " "Any remote certificate will be accepted."); 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) { /* 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; UA_StatusCode retval = UA_SecurityPolicy_Basic128Rsa15(&config->securityPolicies[config->securityPoliciesSize], localCertificate, localPrivateKey, &config->logger); if(retval != UA_STATUSCODE_GOOD) { if(config->securityPoliciesSize == 0) { UA_free(config->securityPolicies); config->securityPolicies = NULL; } 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) { /* 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; UA_StatusCode retval = UA_SecurityPolicy_Basic256(&config->securityPolicies[config->securityPoliciesSize], localCertificate, localPrivateKey, &config->logger); if(retval != UA_STATUSCODE_GOOD) { if(config->securityPoliciesSize == 0) { UA_free(config->securityPolicies); config->securityPolicies = NULL; } 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) { /* 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; UA_StatusCode retval = UA_SecurityPolicy_Basic256Sha256(&config->securityPolicies[config->securityPoliciesSize], localCertificate, localPrivateKey, &config->logger); if(retval != UA_STATUSCODE_GOOD) { if(config->securityPoliciesSize == 0) { UA_free(config->securityPolicies); config->securityPolicies = NULL; } return retval; } config->securityPoliciesSize++; return UA_STATUSCODE_GOOD; } UA_EXPORT UA_StatusCode UA_ServerConfig_addSecurityPolicyAes128Sha256RsaOaep(UA_ServerConfig *config, const UA_ByteString *certificate, const UA_ByteString *privateKey) { /* 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; UA_StatusCode retval = UA_SecurityPolicy_Aes128Sha256RsaOaep(&config->securityPolicies[config->securityPoliciesSize], localCertificate, localPrivateKey, &config->logger); if(retval != UA_STATUSCODE_GOOD) { if(config->securityPoliciesSize == 0) { UA_free(config->securityPolicies); config->securityPolicies = NULL; } return retval; } config->securityPoliciesSize++; return UA_STATUSCODE_GOOD; } /* Always returns UA_STATUSCODE_GOOD. Logs a warning if policies could not be added. */ UA_EXPORT UA_StatusCode UA_ServerConfig_addAllSecurityPolicies(UA_ServerConfig *config, const UA_ByteString *certificate, const UA_ByteString *privateKey) { /* Populate the SecurityPolicies */ UA_ByteString localCertificate = UA_BYTESTRING_NULL; UA_ByteString localPrivateKey = UA_BYTESTRING_NULL; if(certificate) localCertificate = *certificate; if(privateKey) localPrivateKey = *privateKey; UA_StatusCode retval = UA_ServerConfig_addSecurityPolicyNone(config, &localCertificate); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&config->logger, UA_LOGCATEGORY_USERLAND, "Could not add SecurityPolicy#None with error code %s", UA_StatusCode_name(retval)); } retval = UA_ServerConfig_addSecurityPolicyBasic128Rsa15(config, &localCertificate, &localPrivateKey); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&config->logger, UA_LOGCATEGORY_USERLAND, "Could not add SecurityPolicy#Basic128Rsa15 with error code %s", UA_StatusCode_name(retval)); } retval = UA_ServerConfig_addSecurityPolicyBasic256(config, &localCertificate, &localPrivateKey); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&config->logger, UA_LOGCATEGORY_USERLAND, "Could not add SecurityPolicy#Basic256 with error code %s", UA_StatusCode_name(retval)); } retval = UA_ServerConfig_addSecurityPolicyBasic256Sha256(config, &localCertificate, &localPrivateKey); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&config->logger, UA_LOGCATEGORY_USERLAND, "Could not add SecurityPolicy#Basic256Sha256 with error code %s", UA_StatusCode_name(retval)); } retval = UA_ServerConfig_addSecurityPolicyAes128Sha256RsaOaep(config, &localCertificate, &localPrivateKey); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(&config->logger, UA_LOGCATEGORY_USERLAND, "Could not add SecurityPolicy#Aes128Sha256RsaOaep with error code %s", UA_StatusCode_name(retval)); } return UA_STATUSCODE_GOOD; } 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; 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; } UA_CertificateVerification accessControlVerification; retval = UA_CertificateVerification_Trustlist(&accessControlVerification, trustList, trustListSize, issuerList, issuerListSize, revocationList, revocationListSize); retval |= UA_AccessControl_default(conf, true, &accessControlVerification, &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 */ /***************************/ UA_Client * UA_Client_new(void) { UA_ClientConfig config; memset(&config, 0, sizeof(UA_ClientConfig)); config.logger = UA_Log_Stdout_withLevel(UA_LOGLEVEL_INFO); return UA_Client_newWithConfig(&config); } UA_StatusCode UA_ClientConfig_setDefault(UA_ClientConfig *config) { config->timeout = 5000; config->secureChannelLifeTime = 10 * 60 * 1000; /* 10 minutes */ if(!config->logger.log) { config->logger = UA_Log_Stdout_withLevel(UA_LOGLEVEL_INFO); } if (config->sessionLocaleIdsSize > 0 && config->sessionLocaleIds) { UA_Array_delete(config->sessionLocaleIds, config->sessionLocaleIdsSize, &UA_TYPES[UA_TYPES_LOCALEID]); } config->sessionLocaleIds = NULL; config->sessionLocaleIds = 0; config->localConnectionConfig = UA_ConnectionConfig_default; /* Certificate Verification that accepts every certificate. Can be * overwritten when the policy is specialized. */ UA_CertificateVerification_AcceptAll(&config->certificateVerification); UA_LOG_WARNING(&config->logger, UA_LOGCATEGORY_USERLAND, "AcceptAll Certificate Verification. " "Any remote certificate will be accepted."); /* 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, UA_BYTESTRING_NULL, &config->logger); if(retval != UA_STATUSCODE_GOOD) { UA_free(config->securityPolicies); config->securityPolicies = NULL; return retval; } config->securityPoliciesSize = 1; config->initConnectionFunc = UA_ClientConnectionTCP_init; /* for async client */ config->pollConnectionFunc = UA_ClientConnectionTCP_poll; /* 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) * 5); if(!sp) return UA_STATUSCODE_BADOUTOFMEMORY; config->securityPolicies = sp; retval = UA_SecurityPolicy_Basic128Rsa15(&config->securityPolicies[config->securityPoliciesSize], localCertificate, privateKey, &config->logger); if(retval == UA_STATUSCODE_GOOD) { ++config->securityPoliciesSize; } else { UA_LOG_WARNING(&config->logger, UA_LOGCATEGORY_USERLAND, "Could not add SecurityPolicy#Basic128Rsa15 with error code %s", UA_StatusCode_name(retval)); } retval = UA_SecurityPolicy_Basic256(&config->securityPolicies[config->securityPoliciesSize], localCertificate, privateKey, &config->logger); if(retval == UA_STATUSCODE_GOOD) { ++config->securityPoliciesSize; } else { UA_LOG_WARNING(&config->logger, UA_LOGCATEGORY_USERLAND, "Could not add SecurityPolicy#Basic256 with error code %s", UA_StatusCode_name(retval)); } retval = UA_SecurityPolicy_Basic256Sha256(&config->securityPolicies[config->securityPoliciesSize], localCertificate, privateKey, &config->logger); if(retval == UA_STATUSCODE_GOOD) { ++config->securityPoliciesSize; } else { UA_LOG_WARNING(&config->logger, UA_LOGCATEGORY_USERLAND, "Could not add SecurityPolicy#Basic256Sha256 with error code %s", UA_StatusCode_name(retval)); } retval = UA_SecurityPolicy_Aes128Sha256RsaOaep(&config->securityPolicies[config->securityPoliciesSize], localCertificate, privateKey, &config->logger); if(retval == UA_STATUSCODE_GOOD) { ++config->securityPoliciesSize; } else { UA_LOG_WARNING(&config->logger, UA_LOGCATEGORY_USERLAND, "Could not add SecurityPolicy#Aes128Sha256RsaOaep with error code %s", UA_StatusCode_name(retval)); } if(config->securityPoliciesSize == 0) { UA_free(config->securityPolicies); config->securityPolicies = NULL; } return UA_STATUSCODE_GOOD; } #endif /**** amalgamated original file "/plugins/crypto/ua_pki_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 2020 (c) Julius Pfrommer, Fraunhofer IOSB */ 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 clearVerifyAllowAll(UA_CertificateVerification *cv) { } void UA_CertificateVerification_AcceptAll(UA_CertificateVerification *cv) { cv->verifyCertificate = verifyCertificateAllowAll; cv->verifyApplicationURI = verifyApplicationURIAllowAll; cv->clear = clearVerifyAllowAll; } /**** amalgamated original file "/plugins/crypto/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 */ #ifdef UA_ENABLE_ENCRYPTION_MBEDTLS #endif #if defined(UA_ENABLE_ENCRYPTION_OPENSSL) || defined(UA_ENABLE_ENCRYPTION_LIBRESSL) #endif static UA_StatusCode verify_none(void *channelContext, const UA_ByteString *message, const UA_ByteString *signature) { return UA_STATUSCODE_GOOD; } static UA_StatusCode sign_none(void *channelContext, const UA_ByteString *message, UA_ByteString *signature) { return UA_STATUSCODE_GOOD; } static size_t length_none(const void *channelContext) { return 0; } static UA_StatusCode encrypt_none(void *channelContext, UA_ByteString *data) { return UA_STATUSCODE_GOOD; } static UA_StatusCode decrypt_none(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(void *policyContext, 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(void *policyContext, UA_ByteString *out) { if(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 randNumber = UA_UInt32_random(); memcpy(&out->data[i], &randNumber, 4); i = i+4; } /* Fill the remaining byte */ UA_UInt32 randNumber = UA_UInt32_random(); memcpy(&out->data[i], &randNumber, 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_clear(&policy->localCertificate); UA_ByteString_copy(&newCertificate, &policy->localCertificate); return UA_STATUSCODE_GOOD; } static void policy_clear_none(UA_SecurityPolicy *policy) { UA_ByteString_clear(&policy->localCertificate); } UA_StatusCode UA_SecurityPolicy_None(UA_SecurityPolicy *policy, 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; #ifdef UA_ENABLE_ENCRYPTION_MBEDTLS UA_mbedTLS_LoadLocalCertificate(&localCertificate, &policy->localCertificate); #elif defined(UA_ENABLE_ENCRYPTION_OPENSSL) || defined(UA_ENABLE_ENCRYPTION_LIBRESSL) UA_OpenSSL_LoadLocalCertificate(&localCertificate, &policy->localCertificate); #else UA_ByteString_copy(&localCertificate, &policy->localCertificate); #endif 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->uri = UA_STRING_NULL; sym_encryptionAlgorithm->encrypt = encrypt_none; sym_encryptionAlgorithm->decrypt = decrypt_none; sym_encryptionAlgorithm->getLocalKeyLength = length_none; sym_encryptionAlgorithm->getRemoteKeyLength = length_none; sym_encryptionAlgorithm->getRemoteBlockSize = 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->clear = policy_clear_none; return UA_STATUSCODE_GOOD; } /**** amalgamated original file "/plugins/ua_log_syslog.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 2020 (c) Fraunhofer IOSB (Author: Julius Pfrommer) */ #if defined(__linux__) || defined(__unix__) #include const char *syslogLevelNames[6] = {"trace", "debug", "info", "warn", "error", "fatal"}; const char *syslogCategoryNames[7] = {"network", "channel", "session", "server", "client", "userland", "securitypolicy"}; #ifdef __clang__ __attribute__((__format__(__printf__, 4 , 0))) #endif static void UA_Log_Syslog_log(void *context, UA_LogLevel level, UA_LogCategory category, const char *msg, va_list args) { /* Assume that context is casted to UA_LogLevel */ if(context != NULL && (UA_LogLevel)(uintptr_t)context > level) return; int priority = LOG_INFO; switch(level) { case UA_LOGLEVEL_DEBUG: priority = LOG_DEBUG; break; case UA_LOGLEVEL_INFO: priority = LOG_INFO; break; case UA_LOGLEVEL_WARNING: priority = LOG_WARNING; break; case UA_LOGLEVEL_ERROR: priority = LOG_ERR; break; case UA_LOGLEVEL_FATAL: priority = LOG_CRIT; break; case UA_LOGLEVEL_TRACE: default: return; } #define LOGBUFSIZE 512 char logbuf[LOGBUFSIZE]; int pos = snprintf(logbuf, LOGBUFSIZE, "[%s/%s] ", syslogLevelNames[level], syslogCategoryNames[category]); if(pos < 0) { syslog(LOG_WARNING, "Log message too long for syslog"); return; } pos = vsnprintf(&logbuf[pos], LOGBUFSIZE - (size_t)pos, msg, args); if(pos < 0) { syslog(LOG_WARNING, "Log message too long for syslog"); return; } syslog(priority, "%s", logbuf); } static void UA_Log_Syslog_clear(void *logContext) { /* closelog is optional. We don't use it as several loggers might be * instantiated in parallel. */ /* closelog(); */ } UA_Logger UA_Log_Syslog(void) { return UA_Log_Syslog_withLevel(UA_LOGLEVEL_TRACE); } UA_Logger UA_Log_Syslog_withLevel(UA_LogLevel minlevel) { UA_Logger logger = {UA_Log_Syslog_log, (void*)minlevel, UA_Log_Syslog_clear}; return logger; } #endif /**** amalgamated original file "/plugins/historydata/ua_history_data_backend_memory.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) basysKom GmbH (Author: Peter Rustler) */ #include #include typedef struct { UA_DateTime timestamp; UA_DataValue value; } UA_DataValueMemoryStoreItem; static void UA_DataValueMemoryStoreItem_clear(UA_DataValueMemoryStoreItem* item) { UA_DateTime_clear(&item->timestamp); UA_DataValue_clear(&item->value); } typedef struct { UA_NodeId nodeId; UA_DataValueMemoryStoreItem **dataStore; size_t storeEnd; size_t storeSize; } UA_NodeIdStoreContextItem_backend_memory; static void UA_NodeIdStoreContextItem_clear(UA_NodeIdStoreContextItem_backend_memory* item) { UA_NodeId_clear(&item->nodeId); for (size_t i = 0; i < item->storeEnd; ++i) { UA_DataValueMemoryStoreItem_clear(item->dataStore[i]); UA_free(item->dataStore[i]); } UA_free(item->dataStore); } typedef struct { UA_NodeIdStoreContextItem_backend_memory *dataStore; size_t storeEnd; size_t storeSize; size_t initialStoreSize; } UA_MemoryStoreContext; static void UA_MemoryStoreContext_clear(UA_MemoryStoreContext* ctx) { for (size_t i = 0; i < ctx->storeEnd; ++i) { UA_NodeIdStoreContextItem_clear(&ctx->dataStore[i]); } UA_free(ctx->dataStore); memset(ctx, 0, sizeof(UA_MemoryStoreContext)); } static UA_NodeIdStoreContextItem_backend_memory * getNewNodeIdContext_backend_memory(UA_MemoryStoreContext* context, UA_Server *server, const UA_NodeId *nodeId) { UA_MemoryStoreContext *ctx = (UA_MemoryStoreContext*)context; if (ctx->storeEnd >= ctx->storeSize) { size_t newStoreSize = ctx->storeSize * 2; if (newStoreSize == 0) return NULL; ctx->dataStore = (UA_NodeIdStoreContextItem_backend_memory*)UA_realloc(ctx->dataStore, (newStoreSize * sizeof(UA_NodeIdStoreContextItem_backend_memory))); if (!ctx->dataStore) { ctx->storeSize = 0; return NULL; } ctx->storeSize = newStoreSize; } UA_NodeIdStoreContextItem_backend_memory *item = &ctx->dataStore[ctx->storeEnd]; UA_NodeId_copy(nodeId, &item->nodeId); UA_DataValueMemoryStoreItem ** store = (UA_DataValueMemoryStoreItem **)UA_calloc(ctx->initialStoreSize, sizeof(UA_DataValueMemoryStoreItem*)); if (!store) { UA_NodeIdStoreContextItem_clear(item); return NULL; } item->dataStore = store; item->storeSize = ctx->initialStoreSize; item->storeEnd = 0; ++ctx->storeEnd; return item; } static UA_NodeIdStoreContextItem_backend_memory * getNodeIdStoreContextItem_backend_memory(UA_MemoryStoreContext* context, UA_Server *server, const UA_NodeId *nodeId) { for (size_t i = 0; i < context->storeEnd; ++i) { if (UA_NodeId_equal(nodeId, &context->dataStore[i].nodeId)) { return &context->dataStore[i]; } } return getNewNodeIdContext_backend_memory(context, server, nodeId); } static UA_Boolean binarySearch_backend_memory(const UA_NodeIdStoreContextItem_backend_memory* item, const UA_DateTime timestamp, size_t *index) { if (item->storeEnd == 0) { *index = item->storeEnd; return false; } size_t min = 0; size_t max = item->storeEnd - 1; while (min <= max) { *index = (min + max) / 2; if (item->dataStore[*index]->timestamp == timestamp) { return true; } else if (item->dataStore[*index]->timestamp < timestamp) { if (*index == item->storeEnd - 1) { *index = item->storeEnd; return false; } min = *index + 1; } else { if (*index == 0) return false; max = *index - 1; } } *index = min; return false; } static size_t resultSize_backend_memory(UA_Server *server, void *context, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId * nodeId, size_t startIndex, size_t endIndex) { const UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId); if (item->storeEnd == 0 || startIndex == item->storeEnd || endIndex == item->storeEnd) return 0; return endIndex - startIndex + 1; } static size_t getDateTimeMatch_backend_memory(UA_Server *server, void *context, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId * nodeId, const UA_DateTime timestamp, const MatchStrategy strategy) { const UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId); size_t current; UA_Boolean retval = binarySearch_backend_memory(item, timestamp, ¤t); if ((strategy == MATCH_EQUAL || strategy == MATCH_EQUAL_OR_AFTER || strategy == MATCH_EQUAL_OR_BEFORE) && retval) return current; switch (strategy) { case MATCH_AFTER: if (retval) return current+1; return current; case MATCH_EQUAL_OR_AFTER: return current; case MATCH_EQUAL_OR_BEFORE: // retval == true aka "equal" is handled before // Fall through if !retval case MATCH_BEFORE: if (current > 0) return current-1; else return item->storeEnd; default: break; } return item->storeEnd; } static UA_StatusCode serverSetHistoryData_backend_memory(UA_Server *server, void *context, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId * nodeId, UA_Boolean historizing, const UA_DataValue *value) { UA_NodeIdStoreContextItem_backend_memory *item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId); if (item->storeEnd >= item->storeSize) { size_t newStoreSize = item->storeSize == 0 ? INITIAL_MEMORY_STORE_SIZE : item->storeSize * 2; item->dataStore = (UA_DataValueMemoryStoreItem **)UA_realloc(item->dataStore, (newStoreSize * sizeof(UA_DataValueMemoryStoreItem*))); if (!item->dataStore) { item->storeSize = 0; return UA_STATUSCODE_BADOUTOFMEMORY; } item->storeSize = newStoreSize; } UA_DateTime timestamp = 0; if (value->hasSourceTimestamp) { timestamp = value->sourceTimestamp; } else if (value->hasServerTimestamp) { timestamp = value->serverTimestamp; } else { timestamp = UA_DateTime_now(); } UA_DataValueMemoryStoreItem *newItem = (UA_DataValueMemoryStoreItem *)UA_calloc(1, sizeof(UA_DataValueMemoryStoreItem)); newItem->timestamp = timestamp; UA_DataValue_copy(value, &newItem->value); size_t index = getDateTimeMatch_backend_memory(server, context, NULL, NULL, nodeId, timestamp, MATCH_EQUAL_OR_AFTER); if (item->storeEnd > 0 && index < item->storeEnd) { memmove(&item->dataStore[index+1], &item->dataStore[index], sizeof(UA_DataValueMemoryStoreItem*) * (item->storeEnd - index)); } item->dataStore[index] = newItem; ++item->storeEnd; return UA_STATUSCODE_GOOD; } static void UA_MemoryStoreContext_delete(UA_MemoryStoreContext* ctx) { UA_MemoryStoreContext_clear(ctx); UA_free(ctx); } static size_t getEnd_backend_memory(UA_Server *server, void *context, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId * nodeId) { const UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId); return item->storeEnd; } static size_t lastIndex_backend_memory(UA_Server *server, void *context, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId * nodeId) { const UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId); if (item->storeEnd == 0) return 0; return item->storeEnd - 1; } static size_t firstIndex_backend_memory(UA_Server *server, void *context, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId * nodeId) { return 0; } static UA_Boolean boundSupported_backend_memory(UA_Server *server, void *context, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId * nodeId) { return true; } static UA_Boolean timestampsToReturnSupported_backend_memory(UA_Server *server, void *context, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, const UA_TimestampsToReturn timestampsToReturn) { const UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId); if (item->storeEnd == 0) { return true; } if (timestampsToReturn == UA_TIMESTAMPSTORETURN_NEITHER || timestampsToReturn == UA_TIMESTAMPSTORETURN_INVALID || (timestampsToReturn == UA_TIMESTAMPSTORETURN_SERVER && !item->dataStore[0]->value.hasServerTimestamp) || (timestampsToReturn == UA_TIMESTAMPSTORETURN_SOURCE && !item->dataStore[0]->value.hasSourceTimestamp) || (timestampsToReturn == UA_TIMESTAMPSTORETURN_BOTH && !(item->dataStore[0]->value.hasSourceTimestamp && item->dataStore[0]->value.hasServerTimestamp))) { return false; } return true; } static const UA_DataValue* getDataValue_backend_memory(UA_Server *server, void *context, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId * nodeId, size_t index) { const UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId); return &item->dataStore[index]->value; } static UA_StatusCode UA_DataValue_backend_copyRange(const UA_DataValue *src, UA_DataValue *dst, const UA_NumericRange range) { memcpy(dst, src, sizeof(UA_DataValue)); if (src->hasValue) return UA_Variant_copyRange(&src->value, &dst->value, range); return UA_STATUSCODE_BADDATAUNAVAILABLE; } static UA_StatusCode copyDataValues_backend_memory(UA_Server *server, void *context, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId * nodeId, size_t startIndex, size_t endIndex, UA_Boolean reverse, size_t maxValues, UA_NumericRange range, UA_Boolean releaseContinuationPoints, const UA_ByteString *continuationPoint, UA_ByteString *outContinuationPoint, size_t * providedValues, UA_DataValue * values) { size_t skip = 0; if (continuationPoint->length > 0) { if (continuationPoint->length == sizeof(size_t)) { skip = *((size_t*)(continuationPoint->data)); } else { return UA_STATUSCODE_BADCONTINUATIONPOINTINVALID; } } const UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)context, server, nodeId); size_t index = startIndex; size_t counter = 0; size_t skipedValues = 0; if (reverse) { while (index >= endIndex && index < item->storeEnd && counter < maxValues) { if (skipedValues++ >= skip) { if (range.dimensionsSize > 0) { UA_DataValue_backend_copyRange(&item->dataStore[index]->value, &values[counter], range); } else { UA_DataValue_copy(&item->dataStore[index]->value, &values[counter]); } ++counter; } --index; } } else { while (index <= endIndex && counter < maxValues) { if (skipedValues++ >= skip) { if (range.dimensionsSize > 0) { UA_DataValue_backend_copyRange(&item->dataStore[index]->value, &values[counter], range); } else { UA_DataValue_copy(&item->dataStore[index]->value, &values[counter]); } ++counter; } ++index; } } if (providedValues) *providedValues = counter; if ((!reverse && (endIndex-startIndex-skip+1) > counter) || (reverse && (startIndex-endIndex-skip+1) > counter)) { outContinuationPoint->length = sizeof(size_t); size_t t = sizeof(size_t); outContinuationPoint->data = (UA_Byte*)UA_malloc(t); *((size_t*)(outContinuationPoint->data)) = skip + counter; } return UA_STATUSCODE_GOOD; } static UA_StatusCode insertDataValue_backend_memory(UA_Server *server, void *hdbContext, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, const UA_DataValue *value) { if (!value->hasSourceTimestamp && !value->hasServerTimestamp) return UA_STATUSCODE_BADINVALIDTIMESTAMP; const UA_DateTime timestamp = value->hasSourceTimestamp ? value->sourceTimestamp : value->serverTimestamp; UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)hdbContext, server, nodeId); size_t index = getDateTimeMatch_backend_memory(server, hdbContext, sessionId, sessionContext, nodeId, timestamp, MATCH_EQUAL_OR_AFTER); if (item->storeEnd != index && item->dataStore[index]->timestamp == timestamp) return UA_STATUSCODE_BADENTRYEXISTS; if (item->storeEnd >= item->storeSize) { size_t newStoreSize = item->storeSize == 0 ? INITIAL_MEMORY_STORE_SIZE : item->storeSize * 2; item->dataStore = (UA_DataValueMemoryStoreItem **)UA_realloc(item->dataStore, (newStoreSize * sizeof(UA_DataValueMemoryStoreItem*))); if (!item->dataStore) { item->storeSize = 0; return UA_STATUSCODE_BADOUTOFMEMORY; } item->storeSize = newStoreSize; } UA_DataValueMemoryStoreItem *newItem = (UA_DataValueMemoryStoreItem *)UA_calloc(1, sizeof(UA_DataValueMemoryStoreItem)); newItem->timestamp = timestamp; UA_DataValue_copy(value, &newItem->value); if (item->storeEnd > 0 && index < item->storeEnd) { memmove(&item->dataStore[index+1], &item->dataStore[index], sizeof(UA_DataValueMemoryStoreItem*) * (item->storeEnd - index)); } item->dataStore[index] = newItem; ++item->storeEnd; return UA_STATUSCODE_GOOD; } static UA_StatusCode replaceDataValue_backend_memory(UA_Server *server, void *hdbContext, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, const UA_DataValue *value) { if (!value->hasSourceTimestamp && !value->hasServerTimestamp) return UA_STATUSCODE_BADINVALIDTIMESTAMP; const UA_DateTime timestamp = value->hasSourceTimestamp ? value->sourceTimestamp : value->serverTimestamp; UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)hdbContext, server, nodeId); size_t index = getDateTimeMatch_backend_memory(server, hdbContext, sessionId, sessionContext, nodeId, timestamp, MATCH_EQUAL); if (index == item->storeEnd) return UA_STATUSCODE_BADNOENTRYEXISTS; UA_DataValue_clear(&item->dataStore[index]->value); UA_DataValue_copy(value, &item->dataStore[index]->value); return UA_STATUSCODE_GOOD; } static UA_StatusCode updateDataValue_backend_memory(UA_Server *server, void *hdbContext, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, const UA_DataValue *value) { // we first try to replace, because it is cheap UA_StatusCode ret = replaceDataValue_backend_memory(server, hdbContext, sessionId, sessionContext, nodeId, value); if (ret == UA_STATUSCODE_GOOD) return UA_STATUSCODE_GOODENTRYREPLACED; ret = insertDataValue_backend_memory(server, hdbContext, sessionId, sessionContext, nodeId, value); if (ret == UA_STATUSCODE_GOOD) return UA_STATUSCODE_GOODENTRYINSERTED; return ret; } static UA_StatusCode removeDataValue_backend_memory(UA_Server *server, void *hdbContext, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, UA_DateTime startTimestamp, UA_DateTime endTimestamp) { UA_NodeIdStoreContextItem_backend_memory* item = getNodeIdStoreContextItem_backend_memory((UA_MemoryStoreContext*)hdbContext, server, nodeId); size_t storeEnd = item->storeEnd; // The first index which will be deleted size_t index1; // the first index which is not deleted size_t index2; if (startTimestamp > endTimestamp) { return UA_STATUSCODE_BADTIMESTAMPNOTSUPPORTED; } if (startTimestamp == endTimestamp) { index1 = getDateTimeMatch_backend_memory(server, hdbContext, sessionId, sessionContext, nodeId, startTimestamp, MATCH_EQUAL); if (index1 == storeEnd) return UA_STATUSCODE_BADNODATA; index2 = index1 + 1; } else { index1 = getDateTimeMatch_backend_memory(server, hdbContext, sessionId, sessionContext, nodeId, startTimestamp, MATCH_EQUAL_OR_AFTER); index2 = getDateTimeMatch_backend_memory(server, hdbContext, sessionId, sessionContext, nodeId, endTimestamp, MATCH_BEFORE); if (index2 == storeEnd || index1 == storeEnd || index1 > index2 ) return UA_STATUSCODE_BADNODATA; ++index2; } #ifndef __clang_analyzer__ for (size_t i = index1; i < index2; ++i) { UA_DataValueMemoryStoreItem_clear(item->dataStore[i]); UA_free(item->dataStore[i]); } memmove(&item->dataStore[index1], &item->dataStore[index2], sizeof(UA_DataValueMemoryStoreItem*) * (item->storeEnd - index2)); item->storeEnd -= index2 - index1; #else (void)index1; (void)index2; #endif return UA_STATUSCODE_GOOD; } static void deleteMembers_backend_memory(UA_HistoryDataBackend *backend) { if (backend == NULL || backend->context == NULL) return; UA_MemoryStoreContext_clear((UA_MemoryStoreContext*)backend->context); UA_free(backend->context); } UA_HistoryDataBackend UA_HistoryDataBackend_Memory(size_t initialNodeIdStoreSize, size_t initialDataStoreSize) { if (initialNodeIdStoreSize == 0) initialNodeIdStoreSize = 1; if (initialDataStoreSize == 0) initialDataStoreSize = 1; UA_HistoryDataBackend result; memset(&result, 0, sizeof(UA_HistoryDataBackend)); UA_MemoryStoreContext *ctx = (UA_MemoryStoreContext *)UA_calloc(1, sizeof(UA_MemoryStoreContext)); if (!ctx) return result; ctx->dataStore = (UA_NodeIdStoreContextItem_backend_memory*)UA_calloc(initialNodeIdStoreSize, sizeof(UA_NodeIdStoreContextItem_backend_memory)); ctx->initialStoreSize = initialDataStoreSize; ctx->storeSize = initialNodeIdStoreSize; ctx->storeEnd = 0; result.serverSetHistoryData = &serverSetHistoryData_backend_memory; result.resultSize = &resultSize_backend_memory; result.getEnd = &getEnd_backend_memory; result.lastIndex = &lastIndex_backend_memory; result.firstIndex = &firstIndex_backend_memory; result.getDateTimeMatch = &getDateTimeMatch_backend_memory; result.copyDataValues = ©DataValues_backend_memory; result.getDataValue = &getDataValue_backend_memory; result.boundSupported = &boundSupported_backend_memory; result.timestampsToReturnSupported = ×tampsToReturnSupported_backend_memory; result.insertDataValue = &insertDataValue_backend_memory; result.updateDataValue = &updateDataValue_backend_memory; result.replaceDataValue = &replaceDataValue_backend_memory; result.removeDataValue = &removeDataValue_backend_memory; result.deleteMembers = &deleteMembers_backend_memory; result.getHistoryData = NULL; result.context = ctx; return result; } void UA_HistoryDataBackend_Memory_clear(UA_HistoryDataBackend *backend) { UA_MemoryStoreContext *ctx = (UA_MemoryStoreContext*)backend->context; UA_MemoryStoreContext_delete(ctx); memset(backend, 0, sizeof(UA_HistoryDataBackend)); } /**** amalgamated original file "/plugins/historydata/ua_history_data_gathering_default.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) basysKom GmbH (Author: Peter Rustler) */ #include typedef struct { UA_NodeId nodeId; UA_HistorizingNodeIdSettings setting; UA_MonitoredItemCreateResult monitoredResult; } UA_NodeIdStoreContextItem_gathering_default; typedef struct { UA_NodeIdStoreContextItem_gathering_default *dataStore; size_t storeEnd; size_t storeSize; } UA_NodeIdStoreContext; static void dataChangeCallback_gathering_default(UA_Server *server, UA_UInt32 monitoredItemId, void *monitoredItemContext, const UA_NodeId *nodeId, void *nodeContext, UA_UInt32 attributeId, const UA_DataValue *value) { UA_NodeIdStoreContextItem_gathering_default *context = (UA_NodeIdStoreContextItem_gathering_default*)monitoredItemContext; context->setting.historizingBackend.serverSetHistoryData(server, context->setting.historizingBackend.context, NULL, NULL, nodeId, UA_TRUE, value); } static UA_NodeIdStoreContextItem_gathering_default* getNodeIdStoreContextItem_gathering_default(UA_NodeIdStoreContext *context, const UA_NodeId *nodeId) { for (size_t i = 0; i < context->storeEnd; ++i) { if (UA_NodeId_equal(&context->dataStore[i].nodeId, nodeId)) { return &context->dataStore[i]; } } return NULL; } static UA_StatusCode startPoll(UA_Server *server, UA_NodeIdStoreContextItem_gathering_default *item) { UA_MonitoredItemCreateRequest monitorRequest = UA_MonitoredItemCreateRequest_default(item->nodeId); monitorRequest.requestedParameters.samplingInterval = (double)item->setting.pollingInterval; monitorRequest.monitoringMode = UA_MONITORINGMODE_REPORTING; item->monitoredResult = UA_Server_createDataChangeMonitoredItem(server, UA_TIMESTAMPSTORETURN_BOTH, monitorRequest, item, &dataChangeCallback_gathering_default); return item->monitoredResult.statusCode; } static UA_StatusCode stopPoll(UA_Server *server, UA_NodeIdStoreContextItem_gathering_default *item) { UA_StatusCode retval = UA_Server_deleteMonitoredItem(server, item->monitoredResult.monitoredItemId); UA_MonitoredItemCreateResult_init(&item->monitoredResult); return retval; } static UA_StatusCode stopPoll_gathering_default(UA_Server *server, void *context, const UA_NodeId *nodeId) { UA_NodeIdStoreContext *ctx = (UA_NodeIdStoreContext *)context; UA_NodeIdStoreContextItem_gathering_default *item = getNodeIdStoreContextItem_gathering_default(ctx, nodeId); if (!item) { return UA_STATUSCODE_BADNODEIDUNKNOWN; } if (item->setting.historizingUpdateStrategy != UA_HISTORIZINGUPDATESTRATEGY_POLL) return UA_STATUSCODE_BADNODEIDINVALID; if (item->monitoredResult.monitoredItemId == 0) return UA_STATUSCODE_BADMONITOREDITEMIDINVALID; return stopPoll(server, item); } static UA_StatusCode startPoll_gathering_default(UA_Server *server, void *context, const UA_NodeId *nodeId) { UA_NodeIdStoreContext *ctx = (UA_NodeIdStoreContext *)context; UA_NodeIdStoreContextItem_gathering_default *item = getNodeIdStoreContextItem_gathering_default(ctx, nodeId); if (!item) { return UA_STATUSCODE_BADNODEIDUNKNOWN; } if (item->setting.historizingUpdateStrategy != UA_HISTORIZINGUPDATESTRATEGY_POLL) return UA_STATUSCODE_BADNODEIDINVALID; if (item->monitoredResult.monitoredItemId > 0) return UA_STATUSCODE_BADMONITOREDITEMIDINVALID; return startPoll(server, item); } static UA_StatusCode registerNodeId_gathering_default(UA_Server *server, void *context, const UA_NodeId *nodeId, const UA_HistorizingNodeIdSettings setting) { UA_NodeIdStoreContext *ctx = (UA_NodeIdStoreContext*)context; if (getNodeIdStoreContextItem_gathering_default(ctx, nodeId)) { return UA_STATUSCODE_BADNODEIDEXISTS; } if (ctx->storeEnd >= ctx->storeSize) { size_t newStoreSize = ctx->storeSize * 2; ctx->dataStore = (UA_NodeIdStoreContextItem_gathering_default*)UA_realloc(ctx->dataStore, (newStoreSize * sizeof(UA_NodeIdStoreContextItem_gathering_default))); if (!ctx->dataStore) { ctx->storeSize = 0; return UA_STATUSCODE_BADOUTOFMEMORY; } memset(&ctx->dataStore[ctx->storeSize], 0, (newStoreSize - ctx->storeSize) * sizeof(UA_NodeIdStoreContextItem_gathering_default)); ctx->storeSize = newStoreSize; } UA_NodeId_copy(nodeId, &ctx->dataStore[ctx->storeEnd].nodeId); size_t current = ctx->storeEnd; ctx->dataStore[current].setting = setting; ++ctx->storeEnd; return UA_STATUSCODE_GOOD; } static const UA_HistorizingNodeIdSettings* getHistorizingSetting_gathering_default(UA_Server *server, void *context, const UA_NodeId *nodeId) { UA_NodeIdStoreContext *ctx = (UA_NodeIdStoreContext*)context; UA_NodeIdStoreContextItem_gathering_default *item = getNodeIdStoreContextItem_gathering_default(ctx, nodeId); if (item) { return &item->setting; } return NULL; } static void deleteMembers_gathering_default(UA_HistoryDataGathering *gathering) { if (gathering == NULL || gathering->context == NULL) return; UA_NodeIdStoreContext *ctx = (UA_NodeIdStoreContext*)gathering->context; for (size_t i = 0; i < ctx->storeEnd; ++i) { UA_NodeId_clear(&ctx->dataStore[i].nodeId); // There is still a monitored item present for this gathering // You need to remove it with UA_Server_deleteMonitoredItem UA_assert(ctx->dataStore[i].monitoredResult.monitoredItemId == 0); } UA_free(ctx->dataStore); UA_free(gathering->context); } static UA_Boolean updateNodeIdSetting_gathering_default(UA_Server *server, void *context, const UA_NodeId *nodeId, const UA_HistorizingNodeIdSettings setting) { UA_NodeIdStoreContext *ctx = (UA_NodeIdStoreContext*)context; UA_NodeIdStoreContextItem_gathering_default *item = getNodeIdStoreContextItem_gathering_default(ctx, nodeId); if (!item) { return false; } stopPoll_gathering_default(server, context, nodeId); item->setting = setting; return true; } static void setValue_gathering_default(UA_Server *server, void *context, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, UA_Boolean historizing, const UA_DataValue *value) { UA_NodeIdStoreContext *ctx = (UA_NodeIdStoreContext*)context; UA_NodeIdStoreContextItem_gathering_default *item = getNodeIdStoreContextItem_gathering_default(ctx, nodeId); if (!item) { return; } if (item->setting.historizingUpdateStrategy == UA_HISTORIZINGUPDATESTRATEGY_VALUESET) { item->setting.historizingBackend.serverSetHistoryData(server, item->setting.historizingBackend.context, sessionId, sessionContext, nodeId, historizing, value); } } UA_HistoryDataGathering UA_HistoryDataGathering_Default(size_t initialNodeIdStoreSize) { UA_HistoryDataGathering gathering; memset(&gathering, 0, sizeof(UA_HistoryDataGathering)); gathering.setValue = &setValue_gathering_default; gathering.getHistorizingSetting = &getHistorizingSetting_gathering_default; gathering.registerNodeId = ®isterNodeId_gathering_default; gathering.startPoll = &startPoll_gathering_default; gathering.stopPoll = &stopPoll_gathering_default; gathering.deleteMembers = &deleteMembers_gathering_default; gathering.updateNodeIdSetting = &updateNodeIdSetting_gathering_default; UA_NodeIdStoreContext *context = (UA_NodeIdStoreContext*)UA_calloc(1, sizeof(UA_NodeIdStoreContext)); context->storeEnd = 0; context->storeSize = initialNodeIdStoreSize; context->dataStore = (UA_NodeIdStoreContextItem_gathering_default*)UA_calloc(initialNodeIdStoreSize, sizeof(UA_NodeIdStoreContextItem_gathering_default)); gathering.context = context; return gathering; } /**** amalgamated original file "/plugins/historydata/ua_history_database_default.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) basysKom GmbH (Author: Peter Rustler) */ #include typedef struct { UA_HistoryDataGathering gathering; } UA_HistoryDatabaseContext_default; static size_t getResultSize_service_default(const UA_HistoryDataBackend* backend, UA_Server *server, const UA_NodeId *sessionId, void* sessionContext, const UA_NodeId *nodeId, UA_DateTime start, UA_DateTime end, UA_UInt32 numValuesPerNode, UA_Boolean returnBounds, size_t *startIndex, size_t *endIndex, UA_Boolean *addFirst, UA_Boolean *addLast, UA_Boolean *reverse) { size_t storeEnd = backend->getEnd(server, backend->context, sessionId, sessionContext, nodeId); size_t firstIndex = backend->firstIndex(server, backend->context, sessionId, sessionContext, nodeId); size_t lastIndex = backend->lastIndex(server, backend->context, sessionId, sessionContext, nodeId); *startIndex = storeEnd; *endIndex = storeEnd; *addFirst = false; *addLast = false; if (end == LLONG_MIN) { *reverse = false; } else if (start == LLONG_MIN) { *reverse = true; } else { *reverse = end < start; } UA_Boolean equal = start == end; size_t size = 0; if (lastIndex != storeEnd) { if (equal) { if (returnBounds) { *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_EQUAL_OR_BEFORE); if (*startIndex == storeEnd) { *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_AFTER); *addFirst = true; } *endIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_AFTER); size = backend->resultSize(server, backend->context, sessionId, sessionContext, nodeId, *startIndex, *endIndex); } else { *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_EQUAL); *endIndex = *startIndex; if (*startIndex == storeEnd) size = 0; else size = 1; } } else if (start == LLONG_MIN) { *endIndex = firstIndex; if (returnBounds) { *addLast = true; *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_EQUAL_OR_AFTER); if (*startIndex == storeEnd) { *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_EQUAL_OR_BEFORE); *addFirst = true; } } else { *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_EQUAL_OR_BEFORE); } size = backend->resultSize(server, backend->context, sessionId, sessionContext, nodeId, *endIndex, *startIndex); } else if (end == LLONG_MIN) { *endIndex = lastIndex; if (returnBounds) { *addLast = true; *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_EQUAL_OR_BEFORE); if (*startIndex == storeEnd) { *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_AFTER); *addFirst = true; } } else { *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_EQUAL_OR_AFTER); } size = backend->resultSize(server, backend->context, sessionId, sessionContext, nodeId, *startIndex, *endIndex); } else if (*reverse) { if (returnBounds) { *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_EQUAL_OR_AFTER); if (*startIndex == storeEnd) { *addFirst = true; *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_BEFORE); } *endIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_EQUAL_OR_BEFORE); if (*endIndex == storeEnd) { *addLast = true; *endIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_AFTER); } } else { *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_EQUAL_OR_BEFORE); *endIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_AFTER); } size = backend->resultSize(server, backend->context, sessionId, sessionContext, nodeId, *endIndex, *startIndex); } else { if (returnBounds) { *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_EQUAL_OR_BEFORE); if (*startIndex == storeEnd) { *addFirst = true; *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_AFTER); } *endIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_EQUAL_OR_AFTER); if (*endIndex == storeEnd) { *addLast = true; *endIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_BEFORE); } } else { *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_EQUAL_OR_AFTER); *endIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_BEFORE); } size = backend->resultSize(server, backend->context, sessionId, sessionContext, nodeId, *startIndex, *endIndex); } } else if (returnBounds) { *addLast = true; *addFirst = true; } if (*addLast) ++size; if (*addFirst) ++size; if (numValuesPerNode > 0 && size > numValuesPerNode) { size = numValuesPerNode; *addLast = false; } return size; } static UA_StatusCode getHistoryData_service_default(const UA_HistoryDataBackend* backend, const UA_DateTime start, const UA_DateTime end, UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId* nodeId, size_t maxSize, UA_UInt32 numValuesPerNode, UA_Boolean returnBounds, UA_TimestampsToReturn timestampsToReturn, UA_NumericRange range, UA_Boolean releaseContinuationPoints, const UA_ByteString *continuationPoint, UA_ByteString *outContinuationPoint, size_t *resultSize, UA_DataValue ** result) { size_t skip = 0; UA_ByteString backendContinuationPoint; UA_ByteString_init(&backendContinuationPoint); if (continuationPoint->length > 0) { if (continuationPoint->length >= sizeof(size_t)) { skip = *((size_t*)(continuationPoint->data)); if (continuationPoint->length > 0) { backendContinuationPoint.length = continuationPoint->length - sizeof(size_t); backendContinuationPoint.data = continuationPoint->data + sizeof(size_t); } } else { return UA_STATUSCODE_BADCONTINUATIONPOINTINVALID; } } size_t storeEnd = backend->getEnd(server, backend->context, sessionId, sessionContext, nodeId); size_t startIndex; size_t endIndex; UA_Boolean addFirst; UA_Boolean addLast; UA_Boolean reverse; size_t _resultSize = getResultSize_service_default(backend, server, sessionId, sessionContext, nodeId, start, end, numValuesPerNode == 0 ? 0 : numValuesPerNode + (UA_UInt32)skip, returnBounds, &startIndex, &endIndex, &addFirst, &addLast, &reverse); *resultSize = _resultSize - skip; if (*resultSize > maxSize) { *resultSize = maxSize; } UA_DataValue *outResult= (UA_DataValue*)UA_Array_new(*resultSize, &UA_TYPES[UA_TYPES_DATAVALUE]); if (!outResult) { *resultSize = 0; return UA_STATUSCODE_BADOUTOFMEMORY; } *result = outResult; size_t counter = 0; if (addFirst) { if (skip == 0) { outResult[counter].hasStatus = true; outResult[counter].status = UA_STATUSCODE_BADBOUNDNOTFOUND; outResult[counter].hasSourceTimestamp = true; if (start == LLONG_MIN) { outResult[counter].sourceTimestamp = end; } else { outResult[counter].sourceTimestamp = start; } ++counter; } } UA_ByteString backendOutContinuationPoint; UA_ByteString_init(&backendOutContinuationPoint); if (endIndex != storeEnd && startIndex != storeEnd) { size_t retval = 0; size_t valueSize = *resultSize - counter; if (valueSize + skip > _resultSize - addFirst - addLast) { if (skip == 0) { valueSize = _resultSize - addFirst - addLast; } else { valueSize = _resultSize - skip - addLast; } } UA_StatusCode ret = UA_STATUSCODE_GOOD; if (valueSize > 0) ret = backend->copyDataValues(server, backend->context, sessionId, sessionContext, nodeId, startIndex, endIndex, reverse, valueSize, range, releaseContinuationPoints, &backendContinuationPoint, &backendOutContinuationPoint, &retval, &outResult[counter]); if (ret != UA_STATUSCODE_GOOD) { UA_Array_delete(outResult, *resultSize, &UA_TYPES[UA_TYPES_DATAVALUE]); *result = NULL; *resultSize = 0; return ret; } counter += retval; } if (addLast && counter < *resultSize) { outResult[counter].hasStatus = true; outResult[counter].status = UA_STATUSCODE_BADBOUNDNOTFOUND; outResult[counter].hasSourceTimestamp = true; if (start == LLONG_MIN && storeEnd != backend->firstIndex(server, backend->context, sessionId, sessionContext, nodeId)) { outResult[counter].sourceTimestamp = backend->getDataValue(server, backend->context, sessionId, sessionContext, nodeId, endIndex)->sourceTimestamp - UA_DATETIME_SEC; } else if (end == LLONG_MIN && storeEnd != backend->firstIndex(server, backend->context, sessionId, sessionContext, nodeId)) { outResult[counter].sourceTimestamp = backend->getDataValue(server, backend->context, sessionId, sessionContext, nodeId, endIndex)->sourceTimestamp + UA_DATETIME_SEC; } else { outResult[counter].sourceTimestamp = end; } } // there are more values if (skip + *resultSize < _resultSize // there are not more values for this request, but there are more values in database || (backendOutContinuationPoint.length > 0 && numValuesPerNode != 0) // we deliver just one value which is a FIRST/LAST value || (skip == 0 && addFirst == true && *resultSize == 1)) { if(UA_ByteString_allocBuffer(outContinuationPoint, backendOutContinuationPoint.length + sizeof(size_t)) != UA_STATUSCODE_GOOD) { return UA_STATUSCODE_BADOUTOFMEMORY; } *((size_t*)(outContinuationPoint->data)) = skip + *resultSize; if(backendOutContinuationPoint.length > 0) memcpy(outContinuationPoint->data + sizeof(size_t), backendOutContinuationPoint.data, backendOutContinuationPoint.length); } UA_ByteString_clear(&backendOutContinuationPoint); return UA_STATUSCODE_GOOD; } static void updateData_service_default(UA_Server *server, void *hdbContext, const UA_NodeId *sessionId, void *sessionContext, const UA_RequestHeader *requestHeader, const UA_UpdateDataDetails *details, UA_HistoryUpdateResult *result) { UA_HistoryDatabaseContext_default *ctx = (UA_HistoryDatabaseContext_default*)hdbContext; UA_Byte accessLevel = 0; UA_Server_readAccessLevel(server, details->nodeId, &accessLevel); if (!(accessLevel & UA_ACCESSLEVELMASK_HISTORYWRITE)) { result->statusCode = UA_STATUSCODE_BADUSERACCESSDENIED; return; } UA_Boolean historizing = false; UA_Server_readHistorizing(server, details->nodeId, &historizing); if (!historizing) { result->statusCode = UA_STATUSCODE_BADHISTORYOPERATIONINVALID; return; } const UA_HistorizingNodeIdSettings *setting = ctx->gathering.getHistorizingSetting( server, ctx->gathering.context, &details->nodeId); if (!setting) { result->statusCode = UA_STATUSCODE_BADHISTORYOPERATIONINVALID; return; } UA_ServerConfig *config = UA_Server_getConfig(server); result->operationResultsSize = details->updateValuesSize; result->operationResults = (UA_StatusCode*)UA_Array_new(result->operationResultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]); for (size_t i = 0; i < details->updateValuesSize; ++i) { if (config->accessControl.allowHistoryUpdateUpdateData && !config->accessControl.allowHistoryUpdateUpdateData(server, &config->accessControl, sessionId, sessionContext, &details->nodeId, details->performInsertReplace, &details->updateValues[i])) { result->operationResults[i] = UA_STATUSCODE_BADUSERACCESSDENIED; continue; } switch (details->performInsertReplace) { case UA_PERFORMUPDATETYPE_INSERT: if (!setting->historizingBackend.insertDataValue) { result->operationResults[i] = UA_STATUSCODE_BADHISTORYOPERATIONUNSUPPORTED; continue; } result->operationResults[i] = setting->historizingBackend.insertDataValue(server, setting->historizingBackend.context, sessionId, sessionContext, &details->nodeId, &details->updateValues[i]); continue; case UA_PERFORMUPDATETYPE_REPLACE: if (!setting->historizingBackend.replaceDataValue) { result->operationResults[i] = UA_STATUSCODE_BADHISTORYOPERATIONUNSUPPORTED; continue; } result->operationResults[i] = setting->historizingBackend.replaceDataValue(server, setting->historizingBackend.context, sessionId, sessionContext, &details->nodeId, &details->updateValues[i]); continue; case UA_PERFORMUPDATETYPE_UPDATE: if (!setting->historizingBackend.updateDataValue) { result->operationResults[i] = UA_STATUSCODE_BADHISTORYOPERATIONUNSUPPORTED; continue; } result->operationResults[i] = setting->historizingBackend.updateDataValue(server, setting->historizingBackend.context, sessionId, sessionContext, &details->nodeId, &details->updateValues[i]); continue; default: result->operationResults[i] = UA_STATUSCODE_BADHISTORYOPERATIONINVALID; continue; } } } static void deleteRawModified_service_default(UA_Server *server, void *hdbContext, const UA_NodeId *sessionId, void *sessionContext, const UA_RequestHeader *requestHeader, const UA_DeleteRawModifiedDetails *details, UA_HistoryUpdateResult *result) { if (details->isDeleteModified) { result->statusCode = UA_STATUSCODE_BADHISTORYOPERATIONUNSUPPORTED; return; } UA_HistoryDatabaseContext_default *ctx = (UA_HistoryDatabaseContext_default*)hdbContext; UA_Byte accessLevel = 0; UA_Server_readAccessLevel(server, details->nodeId, &accessLevel); if (!(accessLevel & UA_ACCESSLEVELMASK_HISTORYWRITE)) { result->statusCode = UA_STATUSCODE_BADUSERACCESSDENIED; return; } UA_Boolean historizing = false; UA_Server_readHistorizing(server, details->nodeId, &historizing); if (!historizing) { result->statusCode = UA_STATUSCODE_BADHISTORYOPERATIONINVALID; return; } const UA_HistorizingNodeIdSettings *setting = ctx->gathering.getHistorizingSetting( server, ctx->gathering.context, &details->nodeId); if (!setting) { result->statusCode = UA_STATUSCODE_BADHISTORYOPERATIONINVALID; return; } if (!setting->historizingBackend.removeDataValue) { result->statusCode = UA_STATUSCODE_BADHISTORYOPERATIONUNSUPPORTED; return; } UA_ServerConfig *config = UA_Server_getConfig(server); if (config->accessControl.allowHistoryUpdateDeleteRawModified && !config->accessControl.allowHistoryUpdateDeleteRawModified(server, &config->accessControl, sessionId, sessionContext, &details->nodeId, details->startTime, details->endTime, details->isDeleteModified)) { result->statusCode = UA_STATUSCODE_BADUSERACCESSDENIED; return; } result->statusCode = setting->historizingBackend.removeDataValue(server, setting->historizingBackend.context, sessionId, sessionContext, &details->nodeId, details->startTime, details->endTime); } static void readRaw_service_default(UA_Server *server, void *context, const UA_NodeId *sessionId, void *sessionContext, const UA_RequestHeader *requestHeader, const UA_ReadRawModifiedDetails *historyReadDetails, UA_TimestampsToReturn timestampsToReturn, UA_Boolean releaseContinuationPoints, size_t nodesToReadSize, const UA_HistoryReadValueId *nodesToRead, UA_HistoryReadResponse *response, UA_HistoryData * const * const historyData) { UA_HistoryDatabaseContext_default *ctx = (UA_HistoryDatabaseContext_default*)context; for (size_t i = 0; i < nodesToReadSize; ++i) { UA_Byte accessLevel = 0; UA_Server_readAccessLevel(server, nodesToRead[i].nodeId, &accessLevel); if (!(accessLevel & UA_ACCESSLEVELMASK_HISTORYREAD)) { response->results[i].statusCode = UA_STATUSCODE_BADUSERACCESSDENIED; continue; } UA_Boolean historizing = false; UA_Server_readHistorizing(server, nodesToRead[i].nodeId, &historizing); if (!historizing) { response->results[i].statusCode = UA_STATUSCODE_BADHISTORYOPERATIONINVALID; continue; } const UA_HistorizingNodeIdSettings *setting = ctx->gathering.getHistorizingSetting( server, ctx->gathering.context, &nodesToRead[i].nodeId); if (!setting) { response->results[i].statusCode = UA_STATUSCODE_BADHISTORYOPERATIONINVALID; continue; } if (historyReadDetails->returnBounds && !setting->historizingBackend.boundSupported( server, setting->historizingBackend.context, sessionId, sessionContext, &nodesToRead[i].nodeId)) { response->results[i].statusCode = UA_STATUSCODE_BADBOUNDNOTSUPPORTED; continue; } if (!setting->historizingBackend.timestampsToReturnSupported( server, setting->historizingBackend.context, sessionId, sessionContext, &nodesToRead[i].nodeId, timestampsToReturn)) { response->results[i].statusCode = UA_STATUSCODE_BADTIMESTAMPNOTSUPPORTED; continue; } UA_NumericRange range; range.dimensionsSize = 0; range.dimensions = NULL; if (nodesToRead[i].indexRange.length > 0) { UA_StatusCode rangeParseResult = UA_NumericRange_parse(&range, nodesToRead[i].indexRange); if (rangeParseResult != UA_STATUSCODE_GOOD) { response->results[i].statusCode = rangeParseResult; continue; } } UA_StatusCode getHistoryDataStatusCode; if (setting->historizingBackend.getHistoryData) { getHistoryDataStatusCode = setting->historizingBackend.getHistoryData( server, sessionId, sessionContext, &setting->historizingBackend, historyReadDetails->startTime, historyReadDetails->endTime, &nodesToRead[i].nodeId, setting->maxHistoryDataResponseSize, historyReadDetails->numValuesPerNode, historyReadDetails->returnBounds, timestampsToReturn, range, releaseContinuationPoints, &nodesToRead[i].continuationPoint, &response->results[i].continuationPoint, historyData[i]); } else { getHistoryDataStatusCode = getHistoryData_service_default( &setting->historizingBackend, historyReadDetails->startTime, historyReadDetails->endTime, server, sessionId, sessionContext, &nodesToRead[i].nodeId, setting->maxHistoryDataResponseSize, historyReadDetails->numValuesPerNode, historyReadDetails->returnBounds, timestampsToReturn, range, releaseContinuationPoints, &nodesToRead[i].continuationPoint, &response->results[i].continuationPoint, &historyData[i]->dataValuesSize, &historyData[i]->dataValues); } if (getHistoryDataStatusCode != UA_STATUSCODE_GOOD) { response->results[i].statusCode = getHistoryDataStatusCode; continue; } } response->responseHeader.serviceResult = UA_STATUSCODE_GOOD; return; } static void setValue_service_default(UA_Server *server, void *context, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, UA_Boolean historizing, const UA_DataValue *value) { UA_HistoryDatabaseContext_default *ctx = (UA_HistoryDatabaseContext_default*)context; if (ctx->gathering.setValue) ctx->gathering.setValue(server, ctx->gathering.context, sessionId, sessionContext, nodeId, historizing, value); } static void clear_service_default(UA_HistoryDatabase *hdb) { if (hdb == NULL || hdb->context == NULL) return; UA_HistoryDatabaseContext_default *ctx = (UA_HistoryDatabaseContext_default*)hdb->context; ctx->gathering.deleteMembers(&ctx->gathering); UA_free(ctx); } UA_HistoryDatabase UA_HistoryDatabase_default(UA_HistoryDataGathering gathering) { UA_HistoryDatabase hdb; memset(&hdb, 0, sizeof(UA_HistoryDatabase)); UA_HistoryDatabaseContext_default *context = (UA_HistoryDatabaseContext_default*) UA_calloc(1, sizeof(UA_HistoryDatabaseContext_default)); context->gathering = gathering; hdb.context = context; hdb.readRaw = &readRaw_service_default; hdb.setValue = &setValue_service_default; hdb.updateData = &updateData_service_default; hdb.deleteRawModified = &deleteRawModified_service_default; hdb.clear = clear_service_default; return hdb; } /**** amalgamated original file "/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 "/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 UA_EXPORT UA_THREAD_LOCAL void * (*UA_mallocSingleton)(size_t size) = malloc; UA_EXPORT UA_THREAD_LOCAL void (*UA_freeSingleton)(void *ptr) = free; UA_EXPORT UA_THREAD_LOCAL void * (*UA_callocSingleton)(size_t nelem, size_t elsize) = calloc; UA_EXPORT UA_THREAD_LOCAL void * (*UA_reallocSingleton)(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 "/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; #ifdef __CODEGEARC__ gmtime_s(&rawtime, &ptm); #else gmtime_s(&ptm, &rawtime); #endif // 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 "/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 /* Global malloc singletons */ #ifdef UA_ENABLE_MALLOC_SINGLETON UA_EXPORT UA_THREAD_LOCAL void * (*UA_mallocSingleton)(size_t size) = malloc; UA_EXPORT UA_THREAD_LOCAL void (*UA_freeSingleton)(void *ptr) = free; UA_EXPORT UA_THREAD_LOCAL void * (*UA_callocSingleton)(size_t nelem, size_t elsize) = calloc; UA_EXPORT UA_THREAD_LOCAL void * (*UA_reallocSingleton)(void *ptr, size_t size) = realloc; #endif 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 "/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 * Copyright 2020 (c) HMS Industrial Networks AB (Author: Jonas Green) */ #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) { UA_SecureChannel *channel = connection->channel; if(channel && channel->config.sendBufferSize < length) return UA_STATUSCODE_BADCOMMUNICATIONERROR; return UA_ByteString_allocBuffer(buf, length); } static void connection_releasesendbuffer(UA_Connection *connection, UA_ByteString *buf) { UA_ByteString_clear(buf); } static void connection_releaserecvbuffer(UA_Connection *connection, UA_ByteString *buf) { UA_ByteString_clear(buf); } static UA_StatusCode connection_write(UA_Connection *connection, UA_ByteString *buf) { if(connection->state == UA_CONNECTIONSTATE_CLOSED) { UA_ByteString_clear(buf); return UA_STATUSCODE_BADCONNECTIONCLOSED; } /* Prevent OS signals when sending to a closed socket */ int flags = 0; flags |= MSG_NOSIGNAL; struct pollfd poll_fd[1]; poll_fd[0].fd = connection->sockfd; poll_fd[0].events = UA_POLLOUT; /* 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) { if(UA_ERRNO != UA_INTERRUPTED && UA_ERRNO != UA_AGAIN) { connection->close(connection); UA_ByteString_clear(buf); return UA_STATUSCODE_BADCONNECTIONCLOSED; } int poll_ret; do { poll_ret = UA_poll (poll_fd, 1, 1000); } while (poll_ret == 0 || (poll_ret < 0 && UA_ERRNO == UA_INTERRUPTED)); } } while(n < 0); nWritten += (size_t)n; } while(nWritten < buf->length); /* Free the buffer */ UA_ByteString_clear(buf); return UA_STATUSCODE_GOOD; } static UA_StatusCode connection_recv(UA_Connection *connection, UA_ByteString *response, UA_UInt32 timeout) { if(connection->state == UA_CONNECTIONSTATE_CLOSED) return UA_STATUSCODE_BADCONNECTIONCLOSED; /* Listen on the socket for the given timeout until a message arrives */ 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. Act as if it timed out. */ if(UA_ERRNO == UA_INTERRUPTED) return UA_STATUSCODE_GOODNONCRITICALTIMEOUT; /* The error cannot be recovered. Close the connection. */ connection->close(connection); return UA_STATUSCODE_BADCONNECTIONCLOSED; } UA_Boolean internallyAllocated = !response->length; /* Allocate the buffer */ if(internallyAllocated) { size_t bufferSize = 16384; /* Use as default for a new SecureChannel */ UA_SecureChannel *channel = connection->channel; if(channel && channel->config.recvBufferSize > 0) bufferSize = channel->config.recvBufferSize; UA_StatusCode res = UA_ByteString_allocBuffer(response, bufferSize); if(res != UA_STATUSCODE_GOOD) return res; } /* Get the received packet(s) */ ssize_t ret = UA_recv(connection->sockfd, (char*)response->data, response->length, 0); /* The remote side closed the connection */ if(ret == 0) { if(internallyAllocated) UA_ByteString_clear(response); connection->close(connection); return UA_STATUSCODE_BADCONNECTIONCLOSED; } /* Error case */ if(ret < 0) { if(internallyAllocated) UA_ByteString_clear(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; } /* Set the length of the received buffer */ response->length = (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_UInt16 maxConnections; UA_SOCKET serverSockets[FD_SETSIZE]; UA_UInt16 serverSocketsSize; LIST_HEAD(, ConnectionEntry) connections; UA_UInt16 connectionsSize; } ServerNetworkLayerTCP; static void ServerNetworkLayerTCP_freeConnection(UA_Connection *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_CONNECTIONSTATE_CLOSED) return; UA_shutdown((UA_SOCKET)connection->sockfd, 2); connection->state = UA_CONNECTIONSTATE_CLOSED; } static UA_Boolean purgeFirstConnectionWithoutChannel(ServerNetworkLayerTCP *layer) { ConnectionEntry *e; LIST_FOREACH(e, &layer->connections, pointers) { if(e->connection.channel == NULL) { LIST_REMOVE(e, pointers); layer->connectionsSize--; UA_close(e->connection.sockfd); e->connection.free(&e->connection); return true; } } return false; } static UA_StatusCode ServerNetworkLayerTCP_add(UA_ServerNetworkLayer *nl, ServerNetworkLayerTCP *layer, UA_Int32 newsockfd, struct sockaddr_storage *remote) { if(layer->maxConnections && layer->connectionsSize >= layer->maxConnections && !purgeFirstConnectionWithoutChannel(layer)) { return UA_STATUSCODE_BADTCPNOTENOUGHRESOURCES; } /* 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) { return UA_STATUSCODE_BADOUTOFMEMORY; } UA_Connection *c = &e->connection; memset(c, 0, sizeof(UA_Connection)); c->sockfd = newsockfd; c->handle = layer; 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_CONNECTIONSTATE_OPENING; c->openingDate = UA_DateTime_nowMonotonic(); layer->connectionsSize++; /* Add to the linked list */ LIST_INSERT_HEAD(&layer->connections, e, pointers); if(nl->statistics) { nl->statistics->currentConnectionCount++; nl->statistics->cumulatedConnectionCount++; } return UA_STATUSCODE_GOOD; } static UA_StatusCode 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_SOCKET_ERRNO_WRAP( UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Error opening the server socket: %s", errno_str)); return UA_STATUSCODE_BADCOMMUNICATIONERROR; } /* 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 UA_STATUSCODE_BADCOMMUNICATIONERROR; } #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 UA_STATUSCODE_BADCOMMUNICATIONERROR; } 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 UA_STATUSCODE_BADCOMMUNICATIONERROR; } /* Bind socket to address */ int ret = UA_bind(newsock, ai->ai_addr, (socklen_t)ai->ai_addrlen); if(ret < 0) { /* If bind to specific address failed, try to bind *-socket */ if(ai->ai_family == AF_INET) { struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr; if(sin->sin_addr.s_addr != htonl(INADDR_ANY)) { sin->sin_addr.s_addr = 0; ret = 0; } } #if UA_IPV6 else if(ai->ai_family == AF_INET6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr; if(!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { memset(&sin6->sin6_addr, 0, sizeof(sin6->sin6_addr)); sin6->sin6_scope_id = 0; ret = 0; } } #endif // UA_IPV6 if(ret == 0) { ret = UA_bind(newsock, ai->ai_addr, (socklen_t)ai->ai_addrlen); if(ret == 0) { /* The second bind fixed the issue, inform the user. */ UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK, "Server socket bound to unspecified address"); } } } if(ret < 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 UA_STATUSCODE_BADCOMMUNICATIONERROR; } /* 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 UA_STATUSCODE_BADCOMMUNICATIONERROR; } 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++; return UA_STATUSCODE_GOOD; } static UA_StatusCode ServerNetworkLayerTCP_start(UA_ServerNetworkLayer *nl, const UA_Logger *logger, const UA_String *customHostname) { UA_initialize_architecture_network(); ServerNetworkLayerTCP *layer = (ServerNetworkLayerTCP *)nl->handle; layer->logger = logger; /* Get addrinfo of the server and create server sockets */ char hostname[512]; if(customHostname->length) { if(customHostname->length >= sizeof(hostname)) return UA_STATUSCODE_BADOUTOFMEMORY; memcpy(hostname, customHostname->data, customHostname->length); hostname[customHostname->length] = '\0'; } char portno[6]; UA_snprintf(portno, 6, "%d", layer->port); struct addrinfo hints, *res; memset(&hints, 0, sizeof hints); #if UA_IPV6 hints.ai_family = AF_UNSPEC; /* allow IPv4 and IPv6 */ #else hints.ai_family = AF_INET; /* enforce IPv4 only */ #endif hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; #ifdef AI_ADDRCONFIG hints.ai_flags |= AI_ADDRCONFIG; #endif hints.ai_protocol = IPPROTO_TCP; int retcode = UA_getaddrinfo(customHostname->length ? hostname : NULL, portno, &hints, &res); if(retcode != 0) { UA_LOG_SOCKET_ERRNO_GAI_WRAP(UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "getaddrinfo lookup of %s failed with error %d - %s", hostname, retcode, errno_str)); 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); if(layer->serverSocketsSize == 0) { return UA_STATUSCODE_BADCOMMUNICATIONERROR; } /* 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(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])); if(ServerNetworkLayerTCP_add(nl, layer, (UA_Int32)newsockfd, &remote) != UA_STATUSCODE_GOOD) { UA_close(newsockfd); } } /* 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_CONNECTIONSTATE_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); layer->connectionsSize--; UA_close(e->connection.sockfd); UA_Server_removeConnection(server, &e->connection); if(nl->statistics) { nl->statistics->connectionTimeoutCount++; nl->statistics->currentConnectionCount--; } 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); layer->connectionsSize--; UA_close(e->connection.sockfd); UA_Server_removeConnection(server, &e->connection); if(nl->statistics) { nl->statistics->currentConnectionCount--; } } } 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_clear(UA_ServerNetworkLayer *nl) { ServerNetworkLayerTCP *layer = (ServerNetworkLayerTCP *)nl->handle; UA_String_clear(&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); layer->connectionsSize--; UA_close(e->connection.sockfd); UA_free(e); if(nl->statistics) { nl->statistics->currentConnectionCount--; } } /* Free the layer */ UA_free(layer); } UA_ServerNetworkLayer UA_ServerNetworkLayerTCP(UA_ConnectionConfig config, UA_UInt16 port, UA_UInt16 maxConnections) { UA_ServerNetworkLayer nl; memset(&nl, 0, sizeof(UA_ServerNetworkLayer)); nl.clear = ServerNetworkLayerTCP_clear; 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->port = port; layer->maxConnections = maxConnections; return nl; } typedef struct TCPClientConnection { struct addrinfo hints, *server; UA_DateTime connStart; UA_String endpointUrl; UA_UInt32 timeout; } TCPClientConnection; /***************************/ /* Client NetworkLayer TCP */ /***************************/ static void ClientNetworkLayerTCP_close(UA_Connection *connection) { if(connection->state == UA_CONNECTIONSTATE_CLOSED) return; if(connection->sockfd != UA_INVALID_SOCKET) { UA_shutdown(connection->sockfd, 2); UA_close(connection->sockfd); } connection->state = UA_CONNECTIONSTATE_CLOSED; } static void ClientNetworkLayerTCP_free(UA_Connection *connection) { if(!connection->handle) return; TCPClientConnection *tcpConnection = (TCPClientConnection *)connection->handle; if(tcpConnection->server) UA_freeaddrinfo(tcpConnection->server); UA_String_clear(&tcpConnection->endpointUrl); UA_free(tcpConnection); connection->handle = NULL; } UA_StatusCode UA_ClientConnectionTCP_poll(UA_Connection *connection, UA_UInt32 timeout, const UA_Logger *logger) { if(connection->state == UA_CONNECTIONSTATE_CLOSED) return UA_STATUSCODE_BADDISCONNECT; if(connection->state == UA_CONNECTIONSTATE_ESTABLISHED) return UA_STATUSCODE_GOOD; /* Connection timeout? */ TCPClientConnection *tcpConnection = (TCPClientConnection*) connection->handle; if(tcpConnection == NULL) { connection->state = UA_CONNECTIONSTATE_CLOSED; return UA_STATUSCODE_BADDISCONNECT; // some thing is wrong } if((UA_Double) (UA_DateTime_nowMonotonic() - tcpConnection->connStart) > (UA_Double) tcpConnection->timeout * UA_DATETIME_MSEC ) { UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, "Timed out"); ClientNetworkLayerTCP_close(connection); return UA_STATUSCODE_BADDISCONNECT; } /* Get a socket and connect (only once) if not already done in a previous * call. On win32, calling connect multiple times is not recommended on * non-blocking sockets * (https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect). * On posix it is also not necessary to call connect multiple times. * * Identification of successfull connection is done using select (writeable/errorfd) * and getsockopt using SO_ERROR on win32 and posix. */ if(connection->sockfd == UA_INVALID_SOCKET) { connection->sockfd = UA_socket(tcpConnection->server->ai_family, tcpConnection->server->ai_socktype, tcpConnection->server->ai_protocol); if(connection->sockfd == UA_INVALID_SOCKET) { UA_LOG_WARNING(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(connection->sockfd) != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, "Could not set the client socket to nonblocking"); ClientNetworkLayerTCP_close(connection); return UA_STATUSCODE_BADDISCONNECT; } /* Don't have the socket create interrupt signals */ #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(logger, UA_LOGCATEGORY_NETWORK, "Couldn't set SO_NOSIGPIPE"); #endif int error = UA_connect(connection->sockfd, tcpConnection->server->ai_addr, tcpConnection->server->ai_addrlen); /* Connection successful */ if(error == 0) { connection->state = UA_CONNECTIONSTATE_ESTABLISHED; return UA_STATUSCODE_GOOD; } /* The connection failed */ if((UA_ERRNO != UA_ERR_CONNECTION_PROGRESS)) { UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, "Connection to %.*s failed with error: %s", (int)tcpConnection->endpointUrl.length, tcpConnection->endpointUrl.data, strerror(UA_ERRNO)); ClientNetworkLayerTCP_close(connection); return UA_STATUSCODE_BADDISCONNECT; } } /* Use select to wait until connected. Return with a half-opened connection * after a timeout. */ UA_UInt32 timeout_usec = timeout * 1000; #ifdef _OS9000 /* OS-9 cannot use select for checking write sockets. Therefore, we need to * use connect until success or failed */ 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(connection->sockfd, 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 /* Wait in a select-call until the connection fully opens or the timeout * happens */ /* On windows select both writing and error fdset */ fd_set writing_fdset; FD_ZERO(&writing_fdset); UA_fd_set(connection->sockfd, &writing_fdset); fd_set error_fdset; FD_ZERO(&error_fdset); #ifdef _WIN32 UA_fd_set(connection->sockfd, &error_fdset); #endif struct timeval tmptv = {(long int)(timeout_usec / 1000000), (int)(timeout_usec % 1000000)}; int ret = UA_select((UA_Int32)(connection->sockfd + 1), NULL, &writing_fdset, &error_fdset, &tmptv); // When select fails abort connection if(ret == -1) { UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, "Connection to %.*s failed with error: %s", (int)tcpConnection->endpointUrl.length, tcpConnection->endpointUrl.data, strerror(UA_ERRNO)); ClientNetworkLayerTCP_close(connection); return UA_STATUSCODE_BADDISCONNECT; } else if (timeout && ret == 0) { UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, "Connection to %.*s timed out", (int)tcpConnection->endpointUrl.length, tcpConnection->endpointUrl.data); ClientNetworkLayerTCP_close(connection); return UA_STATUSCODE_BADTIMEOUT; } int resultsize = UA_fd_isset(connection->sockfd, &writing_fdset); #endif /* Any errors on the socket reported? */ OPTVAL_TYPE so_error = 0; socklen_t len = sizeof(so_error); ret = UA_getsockopt(connection->sockfd, SOL_SOCKET, SO_ERROR, &so_error, &len); if(ret != 0 || so_error != 0) { // no UA_LOG_SOCKET_ERRNO_GAI_WRAP because of so_error #ifndef _WIN32 char *errno_str = strerror(ret == 0 ? so_error : UA_ERRNO); #elif defined(UNDER_CE) LPVOID errno_str = NULL; FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, ret == 0 ? so_error : WSAGetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&errno_str, 0, NULL); #else char *errno_str = NULL; FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, ret == 0 ? so_error : WSAGetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&errno_str, 0, NULL); #endif UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, "Connection to %.*s failed with error: %s", (int)tcpConnection->endpointUrl.length, tcpConnection->endpointUrl.data, errno_str); #ifdef _WIN32 LocalFree(errno_str); #endif ClientNetworkLayerTCP_close(connection); return UA_STATUSCODE_BADDISCONNECT; } /* The connection is fully opened. Otherwise, select has timed out. But we * can retry. */ if(resultsize > 0) connection->state = UA_CONNECTIONSTATE_ESTABLISHED; return UA_STATUSCODE_GOOD; } UA_Connection UA_ClientConnectionTCP_init(UA_ConnectionConfig config, const UA_String endpointUrl, UA_UInt32 timeout, const UA_Logger *logger) { UA_initialize_architecture_network(); UA_Connection connection; memset(&connection, 0, sizeof(UA_Connection)); connection.state = UA_CONNECTIONSTATE_OPENING; connection.sockfd = UA_INVALID_SOCKET; 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)); if(!tcpClientConnection) { connection.state = UA_CONNECTIONSTATE_CLOSED; return connection; } memset(tcpClientConnection, 0, 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_String_copy(&endpointUrl, &tcpClientConnection->endpointUrl); 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_CONNECTIONSTATE_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 %" PRIu16, 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 %d - %s", hostname, error, errno_str)); connection.state = UA_CONNECTIONSTATE_CLOSED; return connection; } /* Return connection with state UA_CONNECTIONSTATE_OPENING */ return connection; }