Browse Source

Merge remote-tracking branch '1.0' into master

Stefan Profanter 5 years ago
parent
commit
01fae7b5b4

+ 2 - 0
.dockerignore

@@ -0,0 +1,2 @@
+Dockerfile
+/build/*

+ 3 - 0
.travis.yml

@@ -6,6 +6,9 @@ env:
     # GITAUTH:
     - secure: nSunY54Wp5HkQCHHbKwlwpbaKyqRVIu/0EnhaoJSwhM1wqerQV/E5d/2JelO9/tZgbungAO7wk/fjutRMVc7d378RTIPwS8vHpvZfEoGhCFsLoTOlqESzsZFBup2H5t1lpQ23jRHDOxlLdJy2lz5U+zd1YnYgDXqdDFjegsIYdo=
     - PYTHON=python2
+    # For tag builds we do not have the base branch in TRAVIS_BRANCH, therefore store it in BRANCH_FOR_TAG
+    - BRANCH_FOR_TAG=$(git ls-remote origin | sed -n "\|$TRAVIS_COMMIT\s\+refs/heads/|{s///p}")
+
 
 dist: trusty
 

+ 2 - 2
CMakeLists.txt

@@ -456,7 +456,7 @@ if(NOT UA_COMPILE_AS_CXX AND (CMAKE_COMPILER_IS_GNUCC OR "x${CMAKE_C_COMPILER_ID
     set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") # cmake sets -rdynamic by default
 
     # Debug
-    if(BUILD_TYPE_LOWER_CASE STREQUAL "debug")
+    if(BUILD_TYPE_LOWER_CASE STREQUAL "debug" AND UNIX)
         if("x${CMAKE_C_COMPILER_ID}" STREQUAL "xClang" AND NOT UA_ENABLE_UNIT_TESTS_MEMCHECK)
             # Add default sanitizer settings when using clang and Debug build.
             # This allows e.g. CLion to find memory locations for SegFaults
@@ -467,7 +467,7 @@ if(NOT UA_COMPILE_AS_CXX AND (CMAKE_COMPILER_IS_GNUCC OR "x${CMAKE_C_COMPILER_ID
         endif()
     endif()
 
-    if(UA_ENABLE_HARDENING AND (CMAKE_BUILD_TYPE STREQUAL "Release") OR (CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
+    if(UA_ENABLE_HARDENING AND ((CMAKE_BUILD_TYPE STREQUAL "Release") OR (CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")))
         check_cc_flag("-D_FORTIFY_SOURCE=2") # run-time buffer overflow detection (needs at least -O1)
     endif()
 

+ 33 - 9
Dockerfile

@@ -1,10 +1,34 @@
-FROM alpine:3.5
-RUN apk add --no-cache cmake gcc g++ musl-dev python py-pip make && rm -rf /var/cache/apk/*
-ADD . /tmp/open62541
-WORKDIR /tmp/open62541/build
-RUN cmake -DUA_ENABLE_AMALGAMATION=true  \
-          -DBUILD_SHARED_LIBS=true \
-          /tmp/open62541 
+FROM alpine:3.10
+RUN apk add --no-cache cmake gcc git g++ musl-dev mbedtls-dev python py-pip make && rm -rf /var/cache/apk/*
+ADD . /opt/open62541
+
+# Get all the git tags to make sure we detect the correct version with git describe
+WORKDIR /opt/open62541
+RUN git remote add github-upstream https://github.com/open62541/open62541.git
+RUN git fetch --tags github-upstream
+
+WORKDIR /opt/open62541/build
+RUN cmake -DBUILD_SHARED_LIBS=ON \
+		-DCMAKE_BUILD_TYPE=Debug \
+		-DUA_BUILD_EXAMPLES=ON \
+		# Hardening needs to be disabled, otherwise the docker build takes too long and travis fails
+		-DUA_ENABLE_HARDENING=OFF \
+        -DUA_ENABLE_ENCRYPTION=ON \
+        -DUA_ENABLE_SUBSCRIPTIONS=ON \
+        -DUA_ENABLE_SUBSCRIPTIONS_EVENTS=ON \
+		-DUA_NAMESPACE_ZERO=FULL \
+         /opt/open62541
 RUN make -j
-RUN cp *.h /usr/include/ && \
-    cp bin/*.so /usr/lib
+RUN make install
+WORKDIR /opt/open62541
+RUN rm -rf /opt/open62541/build
+
+# Generate certificates
+RUN apk add --no-cache python-dev linux-headers openssl && rm -rf /var/cache/apk/*
+RUN pip install netifaces==0.10.9
+RUN mkdir -p /opt/open62541/pki/created
+RUN python /opt/open62541/tools/certs/create_self-signed.py /opt/open62541/pki/created
+
+
+EXPOSE 4840
+CMD ["/opt/open62541/build/bin/examples/server_ctt" , "/opt/open62541/pki/created/server_cert.der", "/opt/open62541/pki/created/server_key.der", "--enableUnencrypted", "--enableAnonymous"]

+ 32 - 2
README.md

@@ -11,7 +11,9 @@ The library is [available](https://github.com/open62541/open62541/releases) in s
 Build Status:
 
 [![Build Status](https://img.shields.io/travis/open62541/open62541/master.svg)](https://travis-ci.org/open62541/open62541)
+[![Build Status](https://dev.azure.com/open62541/open62541/_apis/build/status/open62541.open62541?branchName=master)](https://dev.azure.com/open62541/open62541/_build/latest?definitionId=1&branchName=master)
 [![Build Status](https://ci.appveyor.com/api/projects/status/github/open62541/open62541?branch=master&svg=true)](https://ci.appveyor.com/project/open62541/open62541/branch/master)
+[![Build Status](https://img.shields.io/docker/cloud/build/open62541/open62541)](https://cloud.docker.com/u/open62541/repository/docker/open62541/open62541)
 
 Code Quality:
 
@@ -125,11 +127,39 @@ A list of projects and companies using our open62541 stack can be found in our W
 
 https://github.com/open62541/open62541/wiki/References-to-open62541
 
+## Installation and code usage
+
+For every release, we provide some pre-packed release packages which you can directly use in your compile infrastructure.
+
+Have a look at the [release page](https://github.com/open62541/open62541/releases) and the corresponding attached assets.
+
+A more detailed explanation on how to install the open62541 SDK is given in our [documentation](https://open62541.org/doc/current/installing.html).
+
+You can not directly download a .zip package from the main branches using the Github UI, since then some of the submodules and version strings are missing.
+Therefore you have three options to install and use this stack:
+
+- **Recommended:** Use any of the prepared packages attached to every release or in the package repository of your distro (if available).  
+  Please check the install guide for more info.
+  
+- Download a .zip package of special `pack/` branches.  
+  These pack branches are up-to-date with the corresponding base branches, but already have the submodules in-place and the version string set correctly.  
+  Here are some direct download links for the current pack branches:  
+  - [pack/master.zip](https://github.com/open62541/open62541/archive/pack/master.zip)
+  - [pack/1.0.zip](https://github.com/open62541/open62541/archive/pack/1.0.zip)
+   
+- Clone this repository and initialize all the submodules using `git submodule update --init --recursive`. Then either use `make install` or setup your CMake project correspondingly.
+
 ## Examples
 
+A complete list of examples can be found in the [examples directory](https://github.com/open62541/open62541/tree/master/examples).
+
+To build the examples, we recommend to install the open62541 project as mentioned in previous section.
+
 ### Example Server Implementation
-Compile the examples with the single-file distribution `open62541.h/.c` header and source file.
-Using the GCC compiler, just run ```gcc -std=c99 -DUA_ARCHITECTURE_POSIX <server.c> open62541.c -o server``` (under Windows you may need to add ``` -lws2_32``` 
+
+The following simple server example can be built using gcc, after you installed open62541 on your system.
+
+Using the GCC compiler, just run ```gcc -std=c99 -lopen62541 -DUA_ARCHITECTURE_POSIX <server.c> -o server``` (under Windows you may need to add ``` -lws2_32``` 
 and change `-DUA_ARCHITECTURE_POSIX` to `-DUA_ARCHITECTURE_WIN32`).
 ```c
 #include <signal.h>

+ 2 - 1
arch/network_tcp.c

@@ -367,7 +367,6 @@ ServerNetworkLayerTCP_start(UA_ServerNetworkLayer *nl, const UA_String *customHo
     /* Get the discovery url from the hostname */
     UA_String du = UA_STRING_NULL;
     char discoveryUrlBuffer[256];
-    char hostnameBuffer[256];
     if (customHostname->length) {
         du.length = (size_t)UA_snprintf(discoveryUrlBuffer, 255, "opc.tcp://%.*s:%d/",
                                         (int)customHostname->length,
@@ -375,12 +374,14 @@ ServerNetworkLayerTCP_start(UA_ServerNetworkLayer *nl, const UA_String *customHo
                                         layer->port);
         du.data = (UA_Byte*)discoveryUrlBuffer;
     }else{
+        char hostnameBuffer[256];
         if(UA_gethostname(hostnameBuffer, 255) == 0) {
             du.length = (size_t)UA_snprintf(discoveryUrlBuffer, 255, "opc.tcp://%s:%d/",
                                             hostnameBuffer, layer->port);
             du.data = (UA_Byte*)discoveryUrlBuffer;
         } else {
             UA_LOG_ERROR(layer->logger, UA_LOGCATEGORY_NETWORK, "Could not get the hostname");
+            return UA_STATUSCODE_BADINTERNALERROR;
         }
     }
     UA_String_copy(&du, &nl->discoveryUrl);

+ 3 - 2
azure-pipelines.yml

@@ -57,7 +57,8 @@ jobs:
     displayName: Install Requirements
   - powershell: ./tools/azure-devops/build.ps1
     displayName: "Build: $(CC_NAME)"
-    errorActionPreference: continue # Nodeset Compiler prints to stderror
+    errorActionPreference: continue # If set to Stop, we only get a truncated exception message. Error is handled by checking exit code
+
 
   - task: PublishBuildArtifacts@1
     inputs:
@@ -87,7 +88,7 @@ jobs:
     displayName: Install Requirements
   - powershell: ./tools/azure-devops/build.ps1
     displayName: "Build: $(CC_NAME)"
-    errorActionPreference: continue # Nodeset Compiler prints to stderror
+    errorActionPreference: continue # If set to Stop, we only get a truncated exception message. Error is handled by checking exit code
 
   - task: PublishBuildArtifacts@1
     inputs:

+ 1 - 1
debian/control-template

@@ -16,7 +16,7 @@ Package: libopen62541-<soname>-dev
 Section: libdevel
 Architecture: any
 Multi-Arch: same
-Depends: libopen62541-<soname> (= ${binary:Version}), ${misc:Depends}
+Depends: libopen62541-<soname> (= ${binary:Version}), ${misc:Depends}, python
 Description: Development header files for open62541
  open62541 is an open source C (C99) implementation of the OPC UA standard
 

+ 88 - 163
deps/base64.c

@@ -1,179 +1,104 @@
 /*
-
-  https://github.com/superwills/NibbleAndAHalf
-  base64.h -- Fast base64 encoding and decoding.
-  version 1.0.0, April 17, 2013 143a
-
-  Copyright (C) 2013 William Sherif
-
-  This software is provided 'as-is', without any express or implied
-  warranty.  In no event will the authors be held liable for any damages
-  arising from the use of this software.
-
-  Permission is granted to anyone to use this software for any purpose,
-  including commercial applications, and to alter it and redistribute it
-  freely, subject to the following restrictions:
-
-  1. The origin of this software must not be misrepresented; you must not
-     claim that you wrote the original software. If you use this software
-     in a product, an acknowledgment in the product documentation would be
-     appreciated but is not required.
-  2. Altered source versions must be plainly marked as such, and must not be
-     misrepresented as being the original software.
-  3. This notice may not be removed or altered from any source distribution.
-
-  William Sherif
-  will.sherif@gmail.com
-
-  YWxsIHlvdXIgYmFzZSBhcmUgYmVsb25nIHRvIHVz
-
-*/
+ * Base64 encoding: Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
+ * This software may be distributed under the terms of the BSD license.
+ *
+ * Base64 decoding: Copyright (c) 2016, polfosol
+ * Posted at https://stackoverflow.com/a/37109258 under the CC-BY-SA Creative
+ * Commons license.
+ */
 
 #include "base64.h"
+#include <open62541/types.h>
 
-#include <stdio.h>
-#include <stdlib.h>
-
-static const char* b64="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ;
-
-// maps A=>0,B=>1..
-static const unsigned char unb64[]={
-    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //10
-    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //20
-    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //30
-    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //40
-    0,   0,   0,  62,   0,   0,   0,  63,  52,  53, //50
-    54,  55,  56,  57,  58,  59,  60,  61,   0,   0, //60
-    0,   0,   0,   0,   0,   0,   1,   2,   3,   4, //70
-    5,   6,   7,   8,   9,  10,  11,  12,  13,  14, //80
-    15,  16,  17,  18,  19,  20,  21,  22,  23,  24, //90
-    25,   0,   0,   0,   0,   0,   0,  26,  27,  28, //100
-    29,  30,  31,  32,  33,  34,  35,  36,  37,  38, //110
-    39,  40,  41,  42,  43,  44,  45,  46,  47,  48, //120
-    49,  50,  51,   0,   0,   0,   0,   0,   0,   0, //130
-    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //140
-    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //150
-    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //160
-    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //170
-    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //180
-    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //190
-    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //200
-    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //210
-    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //220
-    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //230
-    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //240
-    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //250
-    0,   0,   0,   0,   0,   0,
-}; // This array has 256 elements
-
-// Converts binary data of length=len to base64 characters.
-// Length of the resultant string is stored in flen
-// (you must pass pointer flen).
-char* UA_base64( const void* binaryData, int len, int *flen )
-{
-    const unsigned char* bin = (const unsigned char*) binaryData ;
-    char* res ;
-
-    int rc = 0 ; // result counter
-    int byteNo ; // I need this after the loop
-
-    int modulusLen = len % 3 ;
-    int pad = ((modulusLen&1)<<1) + ((modulusLen&2)>>1) ; // 2 gives 1 and 1 gives 2, but 0 gives 0.
+static const unsigned char base64_table[65] =
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
-    *flen = 4*(len + pad)/3 ;
-    res = (char*) malloc( (size_t)(*flen + 1) ) ; // and one for the null
-    if( !res )
-    {
-        puts( "ERROR: base64 could not allocate enough memory." ) ;
-        puts( "I must stop because I could not get enough" ) ;
-        return 0;
+unsigned char *
+UA_base64(const unsigned char *src, size_t len, size_t *out_len) {
+    if(len == 0) {
+        *out_len = 0;
+        return (unsigned char*)UA_EMPTY_ARRAY_SENTINEL;
     }
 
-    for( byteNo = 0 ; byteNo <= len-3 ; byteNo+=3 )
-    {
-        unsigned char BYTE0=bin[byteNo];
-        unsigned char BYTE1=bin[byteNo+1];
-        unsigned char BYTE2=bin[byteNo+2];
-        res[rc++]  = b64[ BYTE0 >> 2 ] ;
-        res[rc++]  = b64[ ((0x3&BYTE0)<<4) + (BYTE1 >> 4) ] ;
-        res[rc++]  = b64[ ((0x0f&BYTE1)<<2) + (BYTE2>>6) ] ;
-        res[rc++]  = b64[ 0x3f&BYTE2 ] ;
-    }
-
-    if( pad==2 )
-    {
-        res[rc++] = b64[ bin[byteNo] >> 2 ] ;
-        res[rc++] = b64[ (0x3&bin[byteNo])<<4 ] ;
-        res[rc++] = '=';
-        res[rc++] = '=';
-    }
-    else if( pad==1 )
-    {
-        res[rc++]  = b64[ bin[byteNo] >> 2 ] ;
-        res[rc++]  = b64[ ((0x3&bin[byteNo])<<4)   +   (bin[byteNo+1] >> 4) ] ;
-        res[rc++]  = b64[ (0x0f&bin[byteNo+1])<<2 ] ;
-        res[rc++] = '=';
-    }
-
-    res[rc]=0; // NULL TERMINATOR! ;)
-    return res ;
+	size_t olen = 4*((len + 2) / 3); /* 3-byte blocks to 4-byte */
+	if(olen < len)
+		return NULL; /* integer overflow */
+
+	unsigned char *out = (unsigned char*)UA_malloc(olen);
+	if(!out)
+		return NULL;
+
+	const unsigned char *end = src + len;
+	const unsigned char *in = src;
+	unsigned char *pos = out;
+	while(end - in >= 3) {
+		*pos++ = base64_table[in[0] >> 2];
+		*pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
+		*pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
+		*pos++ = base64_table[in[2] & 0x3f];
+		in += 3;
+	}
+
+	if(end - in) {
+		*pos++ = base64_table[in[0] >> 2];
+		if(end - in == 1) {
+			*pos++ = base64_table[(in[0] & 0x03) << 4];
+			*pos++ = '=';
+		} else {
+			*pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
+			*pos++ = base64_table[(in[1] & 0x0f) << 2];
+		}
+		*pos++ = '=';
+	}
+
+    *out_len = (size_t)(pos - out);
+	return out;
 }
 
-unsigned char* UA_unbase64( const char* ascii, int len, int *flen )
-{
-    const unsigned char *safeAsciiPtr = (const unsigned char*)ascii ;
-    unsigned char *bin ;
-    int cb=0;
-    int charNo;
-    int pad = 0 ;
-
-    if( len < 2 ) { // 2 accesses below would be OOB.
-        // catch empty string, return NULL as result.
-        puts( "ERROR: You passed an invalid base64 string (too short). You get NULL back." ) ;
-        *flen=0;
-        return 0 ;
+static const uint32_t from_b64[256] = {
+    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  62, 63, 62, 62, 63,
+    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0,  0,  0,  0,  0,  0,
+    0,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14,
+    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0,  0,  0,  0,  63,
+    0,  26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51};
+
+unsigned char *
+UA_unbase64(const unsigned char *src, size_t len, size_t *out_len) {
+    if(len == 0) {
+        *out_len = 0;
+        return (unsigned char*)UA_EMPTY_ARRAY_SENTINEL;
     }
-    if( safeAsciiPtr[ len-1 ]=='=' )  ++pad ;
-    if( safeAsciiPtr[ len-2 ]=='=' )  ++pad ;
 
-    *flen = 3*len/4 - pad ;
-    bin = (unsigned char*)malloc( (size_t) (*flen) ) ;
-    if( !bin )
-    {
-        puts( "ERROR: unbase64 could not allocate enough memory." ) ;
-        puts( "I must stop because I could not get enough" ) ;
-        return 0;
+    const unsigned char *p = src;
+    size_t pad1 = len % 4 || p[len - 1] == '=';
+    size_t pad2 = pad1 && (len % 4 > 2 || p[len - 2] != '=');
+    const size_t last = (len - pad1) / 4 << 2;
+
+    unsigned char *str = (unsigned char*)UA_malloc(last / 4 * 3 + pad1 + pad2);
+    if(!str)
+        return NULL;
+
+    unsigned char *pos = str;
+    for(size_t i = 0; i < last; i += 4) {
+        uint32_t n = from_b64[p[i]] << 18 | from_b64[p[i + 1]] << 12 |
+                     from_b64[p[i + 2]] << 6 | from_b64[p[i + 3]];
+        *pos++ = (unsigned char)(n >> 16);
+        *pos++ = (unsigned char)(n >> 8 & 0xFF);
+        *pos++ = (unsigned char)(n & 0xFF);
     }
 
-    for( charNo=0; charNo <= len - 4 - pad ; charNo+=4 )
-    {
-        int A=unb64[safeAsciiPtr[charNo]];
-        int B=unb64[safeAsciiPtr[charNo+1]];
-        int C=unb64[safeAsciiPtr[charNo+2]];
-        int D=unb64[safeAsciiPtr[charNo+3]];
-
-        bin[cb++] = (unsigned char)((A<<2) | (B>>4)) ;
-        bin[cb++] = (unsigned char)((B<<4) | (C>>2)) ;
-        bin[cb++] = (unsigned char)((C<<6) | (D)) ;
+    if(pad1) {
+        uint32_t n = from_b64[p[last]] << 18 | from_b64[p[last + 1]] << 12;
+        *pos++ = (unsigned char)(n >> 16);
+        if(pad2) {
+            n |= from_b64[p[last + 2]] << 6;
+            *pos++ = (unsigned char)(n >> 8 & 0xFF);
+        }
     }
 
-    if( pad==1 )
-    {
-        int A=unb64[safeAsciiPtr[charNo]];
-        int B=unb64[safeAsciiPtr[charNo+1]];
-        int C=unb64[safeAsciiPtr[charNo+2]];
-
-        bin[cb++] = (unsigned char)((A<<2) | (B>>4)) ;
-        bin[cb++] = (unsigned char)((B<<4) | (C>>2)) ;
-    }
-    else if( pad==2 )
-    {
-        int A=unb64[safeAsciiPtr[charNo]];
-        int B=unb64[safeAsciiPtr[charNo+1]];
-
-        bin[cb++] = (unsigned char)((A<<2) | (B>>4)) ;
-    }
-
-    return bin ;
+    *out_len = (uintptr_t)(pos - str);
+    return str;
 }
-

+ 27 - 41
deps/base64.h

@@ -1,46 +1,32 @@
-/*
-
-  https://github.com/superwills/NibbleAndAHalf
-  base64.h -- Fast base64 encoding and decoding.
-  version 1.0.0, April 17, 2013 143a
-
-  Copyright (C) 2013 William Sherif
-
-  This software is provided 'as-is', without any express or implied
-  warranty.  In no event will the authors be held liable for any damages
-  arising from the use of this software.
-
-  Permission is granted to anyone to use this software for any purpose,
-  including commercial applications, and to alter it and redistribute it
-  freely, subject to the following restrictions:
-
-  1. The origin of this software must not be misrepresented; you must not
-     claim that you wrote the original software. If you use this software
-     in a product, an acknowledgment in the product documentation would be
-     appreciated but is not required.
-  2. Altered source versions must be plainly marked as such, and must not be
-     misrepresented as being the original software.
-  3. This notice may not be removed or altered from any source distribution.
-
-  William Sherif
-  will.sherif@gmail.com
-
-  YWxsIHlvdXIgYmFzZSBhcmUgYmVsb25nIHRvIHVz
-
-*/
 #ifndef UA_BASE64_H_
 #define UA_BASE64_H_
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-char* UA_base64( const void* binaryData, int len, int *flen );
-
-unsigned char* UA_unbase64( const char* ascii, int len, int *flen );
-
-#ifdef __cplusplus
-}
-#endif
+#include <open62541/config.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_ */

+ 78 - 80
deps/libc_time.c

@@ -82,89 +82,87 @@ int __secs_to_tm(long long t, struct mytm *tm) {
     return 0;
 }
 
-int __month_to_secs(int month, int is_leap)
-{
-	static const int secs_through_month[] = {
-		0, 31*86400, 59*86400, 90*86400,
-		120*86400, 151*86400, 181*86400, 212*86400,
-		243*86400, 273*86400, 304*86400, 334*86400 };
-	int t = secs_through_month[month];
-	if (is_leap && month >= 2) t+=86400;
-	return t;
+int __month_to_secs(int month, int is_leap) {
+    static const int secs_through_month[] =
+        {0, 31*86400, 59*86400, 90*86400,
+         120*86400, 151*86400, 181*86400, 212*86400,
+         243*86400, 273*86400, 304*86400, 334*86400 };
+    int t = secs_through_month[month];
+    if (is_leap && month >= 2)
+        t+=86400;
+    return t;
 }
 
-long long __year_to_secs(long long year, int *is_leap)
-{
-	if (year-(int)2ULL <= 136) {
-		int y = (int)year;
-		int leaps = (y-68)>>2;
-		if (!((y-68)&3)) {
-			leaps--;
-			if (is_leap) *is_leap = 1;
-		} else if (is_leap) *is_leap = 0;
-		return 31536000*(y-70) + 86400*leaps;
-	}
-
-	int cycles, centuries, leaps, rem;
-
-        //if (!is_leap) is_leap = &(int){0};
-        int is_leap_val = 0;
-	if (!is_leap){
-            is_leap = &is_leap_val;
+long long __year_to_secs(long long year, int *is_leap) {
+    if (year-(int)2ULL <= 136) {
+        long long y = (int)year;
+        long long leaps = (y-68)>>2;
+        if (!((y-8)&3)) {
+            leaps--;
+            if (is_leap) *is_leap = 1;
+        } else if (is_leap) *is_leap = 0;
+        return 31536000*(y-70) + 86400*leaps;
+    }
+
+    int cycles, centuries, leaps, rem;
+
+    //if (!is_leap) is_leap = &(int){0};
+    int is_leap_val = 0;
+    if (!is_leap) {
+        is_leap = &is_leap_val;
+    }
+    cycles = (int)((year-100) / 400);
+    rem = (int)((year-100) % 400);
+    /* Comparison is always false because rem >= 0.
+    if (rem < 0) {
+        cycles--;
+        rem += 400;
+    } */
+    if (!rem) {
+        *is_leap = 1;
+        centuries = 0;
+        leaps = 0;
+    } else {
+        if (rem >= 200) {
+            if (rem >= 300) centuries = 3, rem -= 300;
+            else centuries = 2, rem -= 200;
+        } else {
+            if (rem >= 100) centuries = 1, rem -= 100;
+            else centuries = 0;
+        }
+        if (!rem) {
+            *is_leap = 0;
+            leaps = 0;
+        } else {
+            leaps = (rem / (int)4U);
+            rem %= (int)4U;
+            *is_leap = !rem;
         }
-	cycles = (int)((year-100) / 400);
-	rem = (int)((year-100) % 400);
-	/* Comparison is always false because rem >= 0.
-	if (rem < 0) {
-		cycles--;
-		rem += 400;
-	} */
-	if (!rem) {
-		*is_leap = 1;
-		centuries = 0;
-		leaps = 0;
-	} else {
-		if (rem >= 200) {
-			if (rem >= 300) centuries = 3, rem -= 300;
-			else centuries = 2, rem -= 200;
-		} else {
-			if (rem >= 100) centuries = 1, rem -= 100;
-			else centuries = 0;
-		}
-		if (!rem) {
-			*is_leap = 0;
-			leaps = 0;
-		} else {
-			leaps = (rem / (int)4U);
-			rem %= (int)4U;
-			*is_leap = !rem;
-		}
-	}
-
-	leaps += 97*cycles + 24*centuries - *is_leap;
-
-	return (year-100) * 31536000LL + leaps * 86400LL + 946684800 + 86400;
+    }
+
+    leaps += 97*cycles + 24*centuries - *is_leap;
+
+    return (year-100) * 31536000LL + leaps * 86400LL + 946684800 + 86400;
 }
 
-long long __tm_to_secs(const struct mytm *tm)
-{
-	int is_leap;
-	long long year = tm->tm_year;
-	int month = tm->tm_mon;
-	if (month >= 12 || month < 0) {
-		int adj = month / 12;
-		month %= 12;
-		if (month < 0) {
-			adj--;
-			month += 12;
-		}
-		year += adj;
-	}
-	long long t = __year_to_secs(year, &is_leap);
-	t += __month_to_secs(month, is_leap);
-	t += 86400LL * (tm->tm_mday-1);
-	t += 3600LL * tm->tm_hour;
-	t += 60LL * tm->tm_min;
-	t += tm->tm_sec;
-	return t;
+long long __tm_to_secs(const struct mytm *tm) {
+    int is_leap;
+    long long year = tm->tm_year;
+    int month = tm->tm_mon;
+    if (month >= 12 || month < 0) {
+        int adj = month / 12;
+        month %= 12;
+        if (month < 0) {
+            adj--;
+            month += 12;
+        }
+        year += adj;
+    }
+    long long t = __year_to_secs(year, &is_leap);
+    t += __month_to_secs(month, is_leap);
+    t += 86400LL * (tm->tm_mday-1);
+    t += 3600LL * tm->tm_hour;
+    t += 60LL * tm->tm_min;
+    t += tm->tm_sec;
+    return t;
 }

+ 0 - 3
deps/libc_time.h

@@ -10,9 +10,6 @@ struct mytm {
     int tm_year;
     int tm_wday;
     int tm_yday;
-    int tm_isdst;
-    /* long __tm_gmtoff; */
-    /* const char *__tm_zone; */
 };
 
 int __secs_to_tm(long long t, struct mytm *tm);

+ 17 - 0
doc/installing.rst

@@ -61,6 +61,23 @@ A full list of enabled features during build time is stored in the CMake Variabl
 Prebuilt packages
 -----------------
 
+Pack branches
+^^^^^^^^^^^^^
+
+Github allows you to download a specific branch as .zip package. Just using this .zip package for open62541 will likely fail:
+
+ * CMake uses ``git describe --tags`` to automatically detect the version string. The .zip package does not include any git information
+ * Specific options during the build stack require additional git submodules which are not inlined in the .zip
+
+Therefore we provide packaging branches. They have the prefix `pack/` and are automatically updated to match the referenced branch.
+
+Here are some examples:
+
+ * `pack/master.zip <https://github.com/open62541/open62541/archive/pack/master.zip>`_
+ * `pack/1.0.zip <https://github.com/open62541/open62541/archive/pack/1.0.zip>`_
+
+These pack branches have inlined submodules and the version string is hardcoded. If you need to build from source but do not want to use git,
+use these specific pack versions.
 
 Prebuild binaries
 ^^^^^^^^^^^^^^^^^

+ 1 - 1
plugins/include/open62541/plugin/pki_default.h

@@ -29,7 +29,7 @@ UA_CertificateVerification_Trustlist(UA_CertificateVerification *cv,
                                      const UA_ByteString *certificateRevocationList,
                                      size_t certificateRevocationListSize);
 
-#if __linux__ /* Linux only so far */
+#ifdef __linux__ /* Linux only so far */
 UA_EXPORT UA_StatusCode
 UA_CertificateVerification_CertFolders(UA_CertificateVerification *cv,
                                        const char *trustListFolder,

+ 7 - 1
plugins/securityPolicies/ua_securitypolicy_basic128rsa15.c

@@ -659,6 +659,12 @@ policyContext_newContext_sp_basic128rsa15(UA_SecurityPolicy *securityPolicy,
     if(securityPolicy == NULL)
         return UA_STATUSCODE_BADINTERNALERROR;
 
+    if (localPrivateKey.length == 0) {
+        UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY,
+                     "Can not initialize security policy. Private key is empty.");
+        return UA_STATUSCODE_BADINVALIDARGUMENT;
+    }
+
     Basic128Rsa15_PolicyContext *pc = (Basic128Rsa15_PolicyContext *)
         UA_malloc(sizeof(Basic128Rsa15_PolicyContext));
     securityPolicy->policyContext = (void *)pc;
@@ -725,7 +731,7 @@ policyContext_newContext_sp_basic128rsa15(UA_SecurityPolicy *securityPolicy,
 
 error:
     UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY,
-                 "Could not create securityContext");
+                 "Could not create securityContext: %s", UA_StatusCode_name(retval));
     if(securityPolicy->policyContext != NULL)
         deleteMembers_sp_basic128rsa15(securityPolicy);
     return retval;

+ 7 - 1
plugins/securityPolicies/ua_securitypolicy_basic256.c

@@ -610,6 +610,12 @@ policyContext_newContext_sp_basic256(UA_SecurityPolicy *securityPolicy,
     if(securityPolicy == NULL)
         return UA_STATUSCODE_BADINTERNALERROR;
 
+    if (localPrivateKey.length == 0) {
+        UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY,
+                     "Can not initialize security policy. Private key is empty.");
+        return UA_STATUSCODE_BADINVALIDARGUMENT;
+    }
+
     Basic256_PolicyContext *pc = (Basic256_PolicyContext *)
         UA_malloc(sizeof(Basic256_PolicyContext));
     securityPolicy->policyContext = (void *)pc;
@@ -675,7 +681,7 @@ policyContext_newContext_sp_basic256(UA_SecurityPolicy *securityPolicy,
 
 error:
     UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY,
-                 "Could not create securityContext");
+                 "Could not create securityContext: %s", UA_StatusCode_name(retval));
     if(securityPolicy->policyContext != NULL)
         deleteMembers_sp_basic256(securityPolicy);
     return retval;

+ 7 - 1
plugins/securityPolicies/ua_securitypolicy_basic256sha256.c

@@ -650,6 +650,12 @@ policyContext_newContext_sp_basic256sha256(UA_SecurityPolicy *securityPolicy,
     if(securityPolicy == NULL)
         return UA_STATUSCODE_BADINTERNALERROR;
 
+    if (localPrivateKey.length == 0) {
+        UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY,
+                     "Can not initialize security policy. Private key is empty.");
+        return UA_STATUSCODE_BADINVALIDARGUMENT;
+    }
+
     Basic256Sha256_PolicyContext *pc = (Basic256Sha256_PolicyContext *)
         UA_malloc(sizeof(Basic256Sha256_PolicyContext));
     securityPolicy->policyContext = (void *)pc;
@@ -715,7 +721,7 @@ policyContext_newContext_sp_basic256sha256(UA_SecurityPolicy *securityPolicy,
 
 error:
     UA_LOG_ERROR(securityPolicy->logger, UA_LOGCATEGORY_SECURITYPOLICY,
-                 "Could not create securityContext");
+                 "Could not create securityContext: %s", UA_StatusCode_name(retval));
     if(securityPolicy->policyContext != NULL)
         deleteMembers_sp_basic256sha256(securityPolicy);
     return retval;

+ 0 - 34
plugins/ua_config_default.c

@@ -232,28 +232,6 @@ addDefaultNetworkLayers(UA_ServerConfig *conf, UA_UInt16 portNumber,
     return UA_ServerConfig_addNetworkLayerTCP(conf, portNumber, sendBufferSize, recvBufferSize);
 }
 
-static UA_StatusCode
-addDiscoveryUrl(UA_ServerConfig *config, UA_UInt16 portNumber) {
-    config->applicationDescription.discoveryUrlsSize = 1;
-    UA_String *discurl = (UA_String *) UA_Array_new(1, &UA_TYPES[UA_TYPES_STRING]);
-    char discoveryUrlBuffer[220];
-    if (config->customHostname.length) {
-        UA_snprintf(discoveryUrlBuffer, 220, "opc.tcp://%.*s:%d/",
-                    (int)config->customHostname.length,
-                    config->customHostname.data,
-                    portNumber);
-    } else {
-        char hostnameBuffer[200];
-        if(UA_gethostname(hostnameBuffer, 200) == 0) {
-            UA_snprintf(discoveryUrlBuffer, 220, "opc.tcp://%s:%d/", hostnameBuffer, portNumber);
-        } else {
-            UA_LOG_ERROR(&config->logger, UA_LOGCATEGORY_NETWORK, "Could not get the hostname");
-        }
-    }
-    discurl[0] = UA_String_fromChars(discoveryUrlBuffer);
-    config->applicationDescription.discoveryUrls = discurl;
-    return UA_STATUSCODE_GOOD;
-}
 
 
 #ifdef UA_ENABLE_WEBSOCKET_SERVER
@@ -426,12 +404,6 @@ UA_ServerConfig_setMinimalCustomBuffer(UA_ServerConfig *config, UA_UInt16 portNu
         return retval;
     }
 
-    retval = addDiscoveryUrl(config, portNumber);
-    if (retval != UA_STATUSCODE_GOOD) {
-        UA_ServerConfig_clean(config);
-        return retval;
-    }
-
     /* Allocate the SecurityPolicies */
     retval = UA_ServerConfig_addSecurityPolicyNone(config, certificate);
     if(retval != UA_STATUSCODE_GOOD) {
@@ -636,12 +608,6 @@ UA_ServerConfig_setDefaultWithSecurityPolicies(UA_ServerConfig *conf,
         return retval;
     }
 
-    retval = addDiscoveryUrl(conf, portNumber);
-    if (retval != UA_STATUSCODE_GOOD) {
-        UA_ServerConfig_clean(conf);
-        return retval;
-    }
-
     retval = UA_ServerConfig_addAllSecurityPolicies(conf, certificate, privateKey);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_ServerConfig_clean(conf);

+ 3 - 3
plugins/ua_pki_default.c

@@ -63,7 +63,7 @@ typedef struct {
     mbedtls_x509_crl certificateRevocationList;
 } CertInfo;
 
-#if __linux__ /* Linux only so far */
+#ifdef __linux__ /* Linux only so far */
 
 #include <dirent.h>
 #include <limits.h>
@@ -196,7 +196,7 @@ certificateVerification_verify(void *verificationContext,
     if(!ci)
         return UA_STATUSCODE_BADINTERNALERROR;
 
-#if __linux__ /* Reload certificates if folder paths are specified */
+#ifdef __linux__ /* Reload certificates if folder paths are specified */
     reloadCertificates(ci);
 #endif
 
@@ -553,7 +553,7 @@ error:
     return UA_STATUSCODE_BADINTERNALERROR;
 }
 
-#if __linux__ /* Linux only so far */
+#ifdef __linux__ /* Linux only so far */
 
 UA_StatusCode
 UA_CertificateVerification_CertFolders(UA_CertificateVerification *cv,

+ 2 - 2
src/client/ua_client.c

@@ -525,8 +525,8 @@ UA_Client_sendAsyncRequest(UA_Client *client, const void *request,
                            UA_UInt32 *requestId) {
     if (UA_Client_getState(client) < UA_CLIENTSTATE_SECURECHANNEL) {
         UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT,
-                    "Cient must be connected to send high-level requests");
-        return UA_STATUSCODE_GOOD;
+				"Client must be connected to send high-level requests");
+		return UA_STATUSCODE_BADSERVERNOTCONNECTED;
     }
     return __UA_Client_AsyncService(client, request, requestType, callback,
                                     responseType, userdata, requestId);

+ 2 - 2
src/pubsub/ua_pubsub.c

@@ -771,7 +771,7 @@ UA_StatusCode UA_Server_DataSetReader_addTargetVariables(UA_Server *server, UA_N
             vAttr.displayName.text = pDataSetReader->config.dataSetMetaData.fields[iteratorField].name;
             if(pDataSetReader->config.dataSetMetaData.fields[iteratorField].name.length < slen) {
                 slen = (UA_UInt16)pDataSetReader->config.dataSetMetaData.fields[iteratorField].name.length;
-                UA_snprintf(szTmpName, sizeof(szTmpName), "%s", (const char*)pDataSetReader->config.dataSetMetaData.fields[iteratorField].name.data);
+                UA_snprintf(szTmpName, sizeof(szTmpName), "%.*s", (int)slen, (const char*)pDataSetReader->config.dataSetMetaData.fields[iteratorField].name.data);
             }
 
             szTmpName[slen] = '\0';
@@ -792,7 +792,7 @@ UA_StatusCode UA_Server_DataSetReader_addTargetVariables(UA_Server *server, UA_N
             UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_USERLAND, "addVariableNode %s succeeded", szTmpName);
         }
         else {
-            UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_USERLAND, "addVariableNode: error 0x%x", retval);
+            UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_USERLAND, "addVariableNode: error 0x%x", retval);
         }
 
         UA_FieldTargetDataType_init(&targetVars.targetVariables[iteratorField]);

+ 16 - 0
src/server/ua_server.c

@@ -500,6 +500,22 @@ UA_Server_run_startup(UA_Server *server) {
         result |= nl->start(nl, &server->config.customHostname);
     }
 
+    /* Update the application description to match the previously added discovery urls.
+     * We can only do this after the network layer is started since it inits the discovery url */
+    if (server->config.applicationDescription.discoveryUrlsSize != 0) {
+        UA_Array_delete(server->config.applicationDescription.discoveryUrls, server->config.applicationDescription.discoveryUrlsSize, &UA_TYPES[UA_TYPES_STRING]);
+        server->config.applicationDescription.discoveryUrlsSize = 0;
+    }
+    server->config.applicationDescription.discoveryUrls = (UA_String *) UA_Array_new(server->config.networkLayersSize, &UA_TYPES[UA_TYPES_STRING]);
+    if (!server->config.applicationDescription.discoveryUrls) {
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    }
+    server->config.applicationDescription.discoveryUrlsSize = server->config.networkLayersSize;
+    for (size_t i=0; i< server->config.applicationDescription.discoveryUrlsSize; i++) {
+        UA_ServerNetworkLayer *nl = &server->config.networkLayers[i];
+        UA_String_copy(&nl->discoveryUrl, &server->config.applicationDescription.discoveryUrls[i]);
+    }
+
     /* Spin up the worker threads */
 #if UA_MULTITHREADING >= 200
     UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER,

+ 1 - 2
src/server/ua_services_attribute.c

@@ -487,8 +487,7 @@ Service_Read(UA_Server *server, UA_Session *session,
     UA_LOCK_ASSERT(server->serviceMutex, 1);
 
     /* Check if the timestampstoreturn is valid */
-    if(request->timestampsToReturn < 0 ||
-       request->timestampsToReturn > UA_TIMESTAMPSTORETURN_NEITHER) {
+    if(request->timestampsToReturn > UA_TIMESTAMPSTORETURN_NEITHER) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADTIMESTAMPSTORETURNINVALID;
         return;
     }

+ 6 - 12
src/ua_types_encoding_json.c

@@ -656,19 +656,13 @@ ENCODE_JSON(ByteString) {
     }
 
     status ret = writeJsonQuote(ctx);
-    int flen;
-    char *ba64 = UA_base64(src->data, (int)src->length, &flen);
+    size_t flen = 0;
+    unsigned char *ba64 = UA_base64(src->data, src->length, &flen);
     
     /* Not converted, no mem */
     if(!ba64)
         return UA_STATUSCODE_BADENCODINGERROR;
 
-    /* Check if negative... (TODO: Why is base64 3rd argument type int?) */
-    if(flen < 0) {
-        UA_free(ba64);
-        return UA_STATUSCODE_BADENCODINGERROR;
-    }
-
     if(ctx->pos + flen > ctx->end) {
         UA_free(ba64);
         return UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED;
@@ -676,7 +670,7 @@ ENCODE_JSON(ByteString) {
     
     /* Copy flen bytes to output stream. */
     if(!ctx->calcOnly)
-        memcpy(ctx->pos, ba64, (size_t)flen);
+        memcpy(ctx->pos, ba64, flen);
     ctx->pos += flen;
 
     /* Base64 result no longer needed */
@@ -2205,13 +2199,13 @@ DECODE_JSON(ByteString) {
         return UA_STATUSCODE_GOOD;
     }
 
-    int flen;
-    unsigned char* unB64 = UA_unbase64(tokenData, (int)tokenSize, &flen);
+    size_t flen = 0;
+    unsigned char* unB64 = UA_unbase64((unsigned char*)tokenData, tokenSize, &flen);
     if(unB64 == 0)
         return UA_STATUSCODE_BADDECODINGERROR;
 
     dst->data = (u8*)unB64;
-    dst->length = (size_t)flen;
+    dst->length = flen;
     
     if(moveToken)
         parseCtx->index++;

+ 3 - 4
src/ua_util.c

@@ -190,10 +190,9 @@ UA_StatusCode UA_ByteString_toBase64String(const UA_ByteString *byteString, UA_S
     if (byteString == str)
         return UA_STATUSCODE_BADINVALIDARGUMENT;
 
-    int resSize = 0;
-    str->data = (UA_Byte*)UA_base64(byteString->data, (int)byteString->length, &resSize);
-    str->length = (size_t) resSize;
-    if (str->data == NULL)
+    str->data = (UA_Byte*)UA_base64(byteString->data,
+                                    byteString->length, &str->length);
+    if(str->data == NULL)
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
     return UA_STATUSCODE_GOOD;

+ 2 - 4
tests/fuzz/fuzz_json_decode_encode.cc

@@ -69,12 +69,10 @@ LLVMFuzzerTestOneInput(uint8_t *data, size_t size) {
 		UA_ByteString_deleteMembers(&buf3);
 		return 0;
 	}
-    if (memcmp(buf2.data, buf3.data, buf.length) != 0) {
-    	// ignore
-    }
 
+    UA_assert(buf2.length == buf3.length);
+    UA_assert(memcmp(buf2.data, buf3.data, buf2.length) == 0);
     UA_ByteString_deleteMembers(&buf2);
     UA_ByteString_deleteMembers(&buf3);
-
     return 0;
 }

+ 11 - 4
tests/pubsub/check_pubsub_subscribe.c

@@ -471,9 +471,9 @@ START_TEST(AddTargetVariableWithValidConfiguration) {
         UA_DataSetMetaDataType *pMetaData = &dataSetReaderConfig.dataSetMetaData;
         UA_DataSetMetaDataType_init (pMetaData);
         pMetaData->name = UA_STRING ("DataSet Test");
-        /* Static definition of number of fields size to 4 to create four different
-         * targetVariables of distinct datatype */
-        pMetaData->fieldsSize = 1;
+        /* Static definition of number of fields size to 2 to create targetVariables
+         * with DateTime and ByteString datatype */
+        pMetaData->fieldsSize = 2;
         pMetaData->fields = (UA_FieldMetaData*)UA_Array_new (pMetaData->fieldsSize,
                              &UA_TYPES[UA_TYPES_FIELDMETADATA]);
 
@@ -484,6 +484,13 @@ START_TEST(AddTargetVariableWithValidConfiguration) {
         pMetaData->fields[0].builtInType = UA_NS0ID_DATETIME;
         pMetaData->fields[0].valueRank = -1; /* scalar */
 
+        /* ByteString DataType */
+        UA_FieldMetaData_init (&pMetaData->fields[1]);
+        UA_NodeId_copy (&UA_TYPES[UA_TYPES_BYTESTRING].typeId,
+                        &pMetaData->fields[1].dataType);
+        pMetaData->fields[1].builtInType = UA_NS0ID_BYTESTRING;
+        pMetaData->fields[1].valueRank = -1; /* scalar */
+
         retVal |= UA_Server_addDataSetReader(server, localreaderGroupIdentifier, &dataSetReaderConfig, &localDataSetreaderIdentifier);
 
         ck_assert_int_eq(retVal, UA_STATUSCODE_GOOD);
@@ -494,8 +501,8 @@ START_TEST(AddTargetVariableWithValidConfiguration) {
                                  folderBrowseName, UA_NODEID_NUMERIC (0,
                                  UA_NS0ID_BASEOBJECTTYPE), oAttr, NULL, &folderId);
         retVal |=  UA_Server_DataSetReader_addTargetVariables(server, &folderId, localDataSetreaderIdentifier, UA_PUBSUB_SDS_TARGET);
-        UA_free(pMetaData->fields);
         ck_assert_int_eq(retVal, UA_STATUSCODE_GOOD);
+        UA_free(pMetaData->fields);
     } END_TEST
 
 START_TEST(SinglePublishSubscribeDateTime) {

+ 3 - 0
tests/server/check_server_userspace.c

@@ -146,6 +146,7 @@ START_TEST(Server_set_customHostname) {
 
     // TODO when we have more network layers, extend this
     ck_assert_uint_ge(config->networkLayersSize, 1);
+    ck_assert_uint_eq(config->applicationDescription.discoveryUrlsSize, config->networkLayersSize);
 
 
     for (size_t i=0; i<config->networkLayersSize; i++) {
@@ -153,7 +154,9 @@ START_TEST(Server_set_customHostname) {
         char discoveryUrl[256];
         int len = snprintf(discoveryUrl, 255, "opc.tcp://%.*s:%d/", (int)customHost.length, customHost.data, port);
         ck_assert_int_eq(nl->discoveryUrl.length, len);
+        ck_assert_int_eq(config->applicationDescription.discoveryUrls[i].length, len);
         ck_assert(strncmp(discoveryUrl, (char*)nl->discoveryUrl.data, len)==0);
+        ck_assert(strncmp(discoveryUrl, (char*)config->applicationDescription.discoveryUrls[i].data, len)==0);
     }
     UA_Server_run_shutdown(server);
     UA_Server_delete(server);

+ 2 - 1
tools/azure-devops/build.ps1

@@ -68,8 +68,9 @@ try {
     Write-Host -ForegroundColor Green "`n##### Testing $env:CC_NAME with full NS0 #####`n"
     New-Item -ItemType directory -Path "build"
     cd build
+    # Use build type Debug here, to force `-Werror`
     & cmake $cmake_cnf `
-            -DCMAKE_BUILD_TYPE=RelWithDebInfo `
+            -DCMAKE_BUILD_TYPE=Debug `
             -DUA_BUILD_EXAMPLES:BOOL=ON `
             -DUA_ENABLE_DA:BOOL=ON `
             -DUA_ENABLE_JSON_ENCODING:BOOL=ON `

+ 7 - 12
tools/nodeset_compiler/nodes.py

@@ -517,8 +517,7 @@ class DataTypeNode(Node):
 
             return self.__baseTypeEncoding__
 
-        isEnum = True
-        isSubType = True
+        isEnum = False
         # An option set is at the same time also an enum, at least for the encoding below
         isOptionSet = parentType is not None and parentType.id.ns == 0 and parentType.id.i==12755
 
@@ -540,26 +539,22 @@ class DataTypeNode(Node):
                         fdtype = str(av)
                         if fdtype in nodeset.aliases:
                             fdtype = nodeset.aliases[fdtype]
-                        isEnum = False
                     elif at == "Name":
                         fname = str(av)
-                    #elif at == "SymbolicName":
+                    elif at == "SymbolicName":
+                        # ignore
+                        continue
                     #    symbolicName = str(av)
                     elif at == "Value":
                         enumVal = int(av)
-                        isSubType = False
+                        isEnum = True
                     elif at == "ValueRank":
                         valueRank = int(av)
                     else:
                         logger.warn("Unknown Field Attribute " + str(at))
                 # This can either be an enumeration OR a structure, not both.
                 # Figure out which of the dictionaries gets the newly read value pair
-                if isEnum == isSubType:
-                    # This is an error
-                    logger.warn("DataType contains both enumeration and subtype (or neither)")
-                    self.__encodable__ = False
-                    break
-                elif isEnum:
+                if isEnum:
                     # This is an enumeration
                     enumDict.append((fname, enumVal))
                     continue
@@ -585,7 +580,7 @@ class DataTypeNode(Node):
                                                   namespaceMapping=namespaceMapping)
                     self.__baseTypeEncoding__ = self.__baseTypeEncoding__ + [[fname, subenc, valueRank]]
                     if not dtnode.isEncodable():
-                        # If we inherit an encoding from an unencodable not, this node is
+                        # If we inherit an encoding from an unencodable node, this node is
                         # also not encodable
                         self.__encodable__ = False
                         break

+ 0 - 2
tools/nodeset_compiler/nodeset.py

@@ -321,8 +321,6 @@ class NodeSet(object):
             if not isinstance(dataTypeNode, DataTypeNode):
                 logger.error("Node id " + str(dataType) + " is not reference a valid dataType.")
                 return None
-            if not dataTypeNode.isEncodable():
-                logger.warn("DataType " + str(dataTypeNode.browseName) + " is not encodable.")
             return dataTypeNode
         return None
 

+ 6 - 0
tools/nodeset_compiler/nodeset_compiler.py

@@ -12,6 +12,7 @@
 
 import logging
 import argparse
+import sys
 from datatypes import NodeId
 from nodeset import *
 
@@ -79,6 +80,9 @@ parser.add_argument('--backend',
 args = parser.parse_args()
 
 # Set up logging
+# By default logging outputs to stderr. We want to redirect it to stdout, otherwise build output from cmake
+# is in stdout and nodeset compiler in stderr
+logging.basicConfig(stream=sys.stdout)
 logger = logging.getLogger(__name__)
 logger.setLevel(logging.INFO)
 verbosity = 0
@@ -95,6 +99,8 @@ elif (verbosity >= 4):
 else:
     logging.basicConfig(level=logging.CRITICAL)
 
+# Set up logging
+logger = logging.getLogger(__name__)
 # Create a new nodeset. The nodeset name is not significant.
 # Parse the XML files
 ns = NodeSet()

+ 16 - 5
tools/travis/travis_linux_script.sh

@@ -28,7 +28,7 @@ fi
 # Docker build test
 if ! [ -z ${DOCKER+x} ]; then
     docker build -t open62541 .
-    docker run -d -p 127.0.0.1:80:80 --name open62541 open62541 /bin/sh
+    docker run -d -p 127.0.0.1:4840:4840 --name open62541 open62541 /bin/sh
     docker ps | grep -q open62541
     # disabled since it randomly fails
     # docker ps | grep -q open62541
@@ -518,12 +518,19 @@ if [ "$CC" != "tcc" ]; then
         echo -en 'travis_fold:end:script.build.coveralls\\r'
 
 		if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then
-			if [ "${TRAVIS_BRANCH}" = "master" ] || [ "${TRAVIS_BRANCH}" = "1.0" ]; then
+			REAL_BRANCH="${TRAVIS_BRANCH}"
+       		echo -en "== Checking branch for packing: BRANCH_FOR_TAG='$BRANCH_FOR_TAG' and TRAVIS_TAG='${TRAVIS_TAG}'. \n"
+			if [ "${TRAVIS_TAG}" = "${TRAVIS_BRANCH}" ]; then
+				REAL_BRANCH="$BRANCH_FOR_TAG"
+        		echo -en "== Commit is tag build for '${TRAVIS_TAG}'. Detected branch for tag = '$BRANCH_FOR_TAG' \n"
+			fi
+
+			if [ "${REAL_BRANCH}" = "master" ] || [ "${REAL_BRANCH}" = "1.0" ]; then
 				# Create a separate branch with the `pack/` prefix. This branch has the correct debian/changelog set, and
 				# The submodules are directly copied
-				echo -e "\r\n== Pushing 'pack/${TRAVIS_BRANCH}' branch =="  && echo -en 'travis_fold:start:script.build.pack-branch\\r'
+				echo -e "\r\n== Pushing 'pack/${REAL_BRANCH}' branch =="  && echo -en 'travis_fold:start:script.build.pack-branch\\r'
 
-				git checkout -b pack-tmp/${TRAVIS_BRANCH}
+				git checkout -b pack-tmp/${REAL_BRANCH}
 				cp -r deps/mdnsd deps/mdnsd_back
 				cp -r deps/ua-nodeset deps/ua-nodeset_back
 				git rm -rf --cached deps/mdnsd
@@ -538,10 +545,14 @@ if [ "$CC" != "tcc" ]; then
 				git add CMakeLists.txt
 				git commit -m "[ci skip] Pack with inline submodules"
 				git remote add origin-auth https://$GITAUTH@github.com/${TRAVIS_REPO_SLUG}
-				git push -uf origin-auth pack-tmp/${TRAVIS_BRANCH}:pack/${TRAVIS_BRANCH}
+				git push -uf origin-auth pack-tmp/${REAL_BRANCH}:pack/${REAL_BRANCH}
 
 				echo -en 'travis_fold:end:script.build.pack-branch\\r'
+			else
+				echo -en "== Skipping push to pack/ because branch does not match: REAL_BRANCH='${REAL_BRANCH}' \n"
 			fi
+        else
+        	echo -en "== Skipping push to pack/ because TRAVIS_PULL_REQUEST=false \n"
         fi