You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

71784 lines
2.7 MiB

/* THIS IS A SINGLE-FILE DISTRIBUTION CONCATENATED FROM THE OPEN62541 SOURCES
* visit http://open62541.org/ for information about this software
* Git-Revision: v1.2.2-506-g998e2523-dirty
*/
/*
* 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.
*/
/*
* Copyright (c) 2021 AIIT XUOS Lab
* XiUOS is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
/**
* @file open62541.c
* @brief Support OPCUA protocol
* @version 1.0
* @author AIIT XUOS Lab
* @date 2021.12.15
*/
#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"
#define ua_print KPrintf
#define ua_trace() KPrintf("ua: [%s] line %d checked!\n", __func__, __LINE__)
#if LWIP_DNS
#else
/* input flags for struct addrinfo */
#define AI_PASSIVE 0x01
#define AI_CANONNAME 0x02
#define AI_NUMERICHOST 0x04
#define AI_NUMERICSERV 0x08
#define AI_V4MAPPED 0x10
#define AI_ALL 0x20
#define AI_ADDRCONFIG 0x40
struct addrinfo {
int ai_flags; /* Input flags. */
int ai_family; /* Address family of socket. */
int ai_socktype; /* Socket type. */
int ai_protocol; /* Protocol of socket. */
socklen_t ai_addrlen; /* Length of socket address. */
struct sockaddr *ai_addr; /* Socket address of socket. */
char *ai_canonname; /* Canonical name of service location. */
struct addrinfo *ai_next; /* Pointer to next in list. */
};
#endif
/**** 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 <oneill@pcg-random.org>
*
* 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 <stddef.h>
/**
* 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_freeRTOS/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_freeRTOS/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_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;
#ifdef UA_ENABLE_SUBSCRIPTIONS
size_t subscriptionsSize;
TAILQ_HEAD(, UA_Subscription) subscriptions; /* Late subscriptions that do eventually
* publish are moved to the tail. So that
* other late subscriptions are not
* starved. */
SIMPLEQ_HEAD(, UA_PublishResponseEntry) responseQueue;
UA_UInt32 numPublishReq;
size_t totalRetransmissionQueueSize; /* Retransmissions of all subscriptions */
#endif
} UA_Session;
/**
* Session Lifecycle
* ----------------- */
void UA_Session_init(UA_Session *session);
void UA_Session_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);
void
UA_Session_detachSubscription(UA_Server *server, UA_Session *session,
UA_Subscription *sub);
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 { \
UA_String idString = UA_STRING_NULL; \
UA_UInt32 channelId = 0; \
if(SESSION) { \
UA_NodeId_print(&(SESSION)->sessionId, &idString); \
channelId = (SESSION)->header.channel ? \
(SESSION)->header.channel->securityToken.channelId : 0; \
} \
UA_LOG_##LEVEL(LOGGER, UA_LOGCATEGORY_SESSION, \
"SecureChannel %" PRIu32 " | Session %.*s | " MSG "%.0s", \
channelId, (int)idString.length, idString.data, __VA_ARGS__); \
UA_String_clear(&idString); \
} while(0)
#if UA_LOGLEVEL <= 100
# define UA_LOG_TRACE_SESSION(LOGGER, SESSION, ...) \
UA_MACRO_EXPAND(UA_LOG_SESSION_INTERNAL(LOGGER, TRACE, SESSION, __VA_ARGS__, ""))
#else
# define UA_LOG_TRACE_SESSION(LOGGER, SESSION, ...)
#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 (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 */
/*****************/
struct UA_MonitoredItem {
UA_TimerEntry delayedFreePointers;
LIST_ENTRY(UA_MonitoredItem) listEntry; /* Linked list in the Subscription */
UA_MonitoredItem *next; /* Linked list of MonitoredItems directly attached
* to a Node. Initialized to ~0 to indicate that the
* MonitoredItem is not added to a node. */
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 sampleCallbackIsRegistered;
UA_Boolean registered; /* Registered in the server / Subscription */
UA_DateTime triggeredUntil; /* If the MonitoringMode is SAMPLING, a
* triggered MonitoredItem puts the next
* Notification into the global queue (of the
* Subscription) for publishing. But triggering
* is only active for the duration of one
* publishin cycle. */
/* 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 Callback */
UA_UInt64 sampleCallbackId;
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;
TAILQ_ENTRY(UA_Subscription) sessionListEntry; /* Only set if session != NULL */
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_UInt32 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;
/* Global list of notifications from the MonitoredItems */
TAILQ_HEAD(, UA_Notification) notificationQueue;
UA_UInt32 notificationQueueSize; /* Total queue size */
UA_UInt32 dataChangeNotifications;
UA_UInt32 eventNotifications;
/* Notifications to be sent out now (already late). In a regular publish
* callback, all queued notifications are sent out. In a late publish
* response, only the notifications left from the last regular publish
* callback are sent. */
UA_UInt32 readyNotifications;
/* Retransmission Queue */
NotificationMessageQueue retransmissionQueue;
size_t retransmissionQueueSize;
};
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_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 { \
UA_String idString = UA_STRING_NULL; \
if((SUB) && (SUB)->session) { \
UA_NodeId_print(&(SUB)->session->sessionId, &idString); \
UA_LOG_##LEVEL(LOGGER, UA_LOGCATEGORY_SESSION, \
"SecureChannel %" PRIu32 " | Session %.*s | Subscription %" PRIu32 " | " MSG "%.0s", \
((SUB)->session->header.channel ? \
(SUB)->session->header.channel->securityToken.channelId : 0), \
(int)idString.length, idString.data, (SUB)->subscriptionId, __VA_ARGS__); \
UA_String_clear(&idString); \
} 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;
/**
* 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);
void
UA_NetworkMessage_delete(UA_NetworkMessage* p);
#ifdef UA_ENABLE_JSON_ENCODING
UA_StatusCode
UA_NetworkMessage_encodeJson(const UA_NetworkMessage *src,
UA_Byte **bufPos, const UA_Byte **bufEnd, UA_String *namespaces,
size_t namespaceSize, UA_String *serverUris,
size_t serverUriSize, UA_Boolean useReversible);
size_t
UA_NetworkMessage_calcSizeJson(const UA_NetworkMessage *src,
UA_String *namespaces, size_t namespaceSize,
UA_String *serverUris, size_t serverUriSize,
UA_Boolean useReversible);
UA_StatusCode UA_NetworkMessage_decodeJson(UA_NetworkMessage *dst, const UA_ByteString *src);
#endif
_UA_END_DECLS
/**** amalgamated original file "/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