Browse Source

use securitypolicy API in the server

Infinity95 7 years ago
parent
commit
8b685b6540

+ 1 - 5
CMakeLists.txt

@@ -260,11 +260,6 @@ set(exported_headers ${PROJECT_BINARY_DIR}/src_generated/ua_config.h
                      ${PROJECT_SOURCE_DIR}/include/ua_server_config.h
                      ${PROJECT_SOURCE_DIR}/include/ua_client.h
                      ${PROJECT_SOURCE_DIR}/include/ua_client_highlevel.h
-                     ${PROJECT_SOURCE_DIR}/plugins/ua_network_tcp.h
-                     ${PROJECT_SOURCE_DIR}/plugins/ua_accesscontrol_default.h
-                     ${PROJECT_SOURCE_DIR}/plugins/ua_log_stdout.h
-                     ${PROJECT_SOURCE_DIR}/plugins/ua_config_default.h
-                     ${PROJECT_SOURCE_DIR}/plugins/ua_securitypolicy_none.h
 )
 set(internal_headers ${PROJECT_SOURCE_DIR}/deps/queue.h
                      ${PROJECT_SOURCE_DIR}/deps/pcg_basic.h
@@ -320,6 +315,7 @@ set(lib_sources ${PROJECT_SOURCE_DIR}/src/ua_types.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_discovery_multicast.c
                 # client
                 ${PROJECT_SOURCE_DIR}/src/client/ua_client.c
+                ${PROJECT_SOURCE_DIR}/src/client/ua_client_connect.c
                 ${PROJECT_SOURCE_DIR}/src/client/ua_client_discovery.c
                 ${PROJECT_SOURCE_DIR}/src/client/ua_client_highlevel.c
                 ${PROJECT_SOURCE_DIR}/src/client/ua_client_highlevel_subscriptions.c

+ 52 - 54
include/ua_client.h

@@ -51,40 +51,67 @@ typedef struct UA_ClientConfig {
 /**
  * Client Lifecycle
  * ---------------- */
+
 typedef enum {
-     UA_CLIENTSTATE_READY,     /* The client is not connected but initialized
-                                  and ready to use. */
-     UA_CLIENTSTATE_CONNECTED, /* The client is connected to a server. */
-     UA_CLIENTSTATE_FAULTED,   /* An error has occured that might have
-                                  influenced the connection state. A successfull
-                                  service call or renewal of the secure channel
-                                  will reset the state to CONNECTED. */
-     UA_CLIENTSTATE_ERRORED    /* A non-recoverable error has occured and the
-                                  connection is no longer reliable. The client
-                                  needs to be disconnected and reinitialized to
-                                  recover into a CONNECTED state. */
+    UA_CLIENTSTATE_DISCONNECTED,        /* The client is not connected */
+    UA_CLIENTSTATE_CONNECTED,           /* A TCP connection to the server is open */
+    UA_CLIENTSTATE_SECURECHANNEL,       /* A SecureChannel to the server is open */
+    UA_CLIENTSTATE_SESSION,             /* A session with the server is open */
+    UA_CLIENTSTATE_SESSION_DISCONNECTED /* A session with the server is open.
+                                         * But the SecureChannel was lost. Try
+                                         * to establish a new SecureChannel and
+                                         * reattach the existing session. */
 } UA_ClientState;
 
 struct UA_Client;
 typedef struct UA_Client UA_Client;
 
-/* Create a new client
- *
- * @param config for the new client. You can use UA_ClientConfig_default
- *        which has sane defaults
- * @param logger function pointer to a logger function. See
- *        examples/logger_stdout.c for a simple implementation
- * @return return the new Client object */
-UA_Client UA_EXPORT * UA_Client_new(UA_ClientConfig config);
+/* Create a new client */
+UA_Client UA_EXPORT *
+UA_Client_new(UA_ClientConfig config);
 
 /* Get the client connection status */
-UA_ClientState UA_EXPORT UA_Client_getState(UA_Client *client);
+UA_ClientState UA_EXPORT
+UA_Client_getState(UA_Client *client);
 
 /* Reset a client */
-void UA_EXPORT UA_Client_reset(UA_Client *client);
+void UA_EXPORT
+UA_Client_reset(UA_Client *client);
 
 /* Delete a client */
-void UA_EXPORT UA_Client_delete(UA_Client *client);
+void UA_EXPORT
+UA_Client_delete(UA_Client *client);
+
+/**
+ * Connect to a Server
+ * ------------------- */
+
+/* Connect to the server
+ *
+ * @param client to use
+ * @param endpointURL to connect (for example "opc.tcp://localhost:16664")
+ * @return Indicates whether the operation succeeded or returns an error code */
+UA_StatusCode UA_EXPORT
+UA_Client_connect(UA_Client *client, const char *endpointUrl);
+
+/* Connect to the selected server with the given username and password
+ *
+ * @param client to use
+ * @param endpointURL to connect (for example "opc.tcp://localhost:16664")
+ * @param username
+ * @param password
+ * @return Indicates whether the operation succeeded or returns an error code */
+UA_StatusCode UA_EXPORT
+UA_Client_connect_username(UA_Client *client, const char *endpointUrl,
+                           const char *username, const char *password);
+
+/* Close a connection to the selected server */
+UA_StatusCode UA_EXPORT
+UA_Client_disconnect(UA_Client *client);
+
+/* Renew the underlying secure channel */
+UA_StatusCode UA_EXPORT
+UA_Client_manuallyRenewSecureChannel(UA_Client *client);
 
 /**
  * Discovery
@@ -151,38 +178,9 @@ UA_Client_findServers(UA_Client *client, const char *serverUrl,
  * @return Indicates whether the operation succeeded or returns an error code */
 UA_StatusCode UA_EXPORT
 UA_Client_findServersOnNetwork(UA_Client *client, const char *serverUrl,
-                     UA_UInt32 startingRecordId, UA_UInt32 maxRecordsToReturn,
-                     size_t serverCapabilityFilterSize, UA_String *serverCapabilityFilter,
-                     size_t *serverOnNetworkSize, UA_ServerOnNetwork **serverOnNetwork);
-
-/**
- * Manage the Connection
- * --------------------- */
-
-/* Connect to the selected server
- *
- * @param client to use
- * @param endpointURL to connect (for example "opc.tcp://localhost:16664")
- * @return Indicates whether the operation succeeded or returns an error code */
-UA_StatusCode UA_EXPORT
-UA_Client_connect(UA_Client *client, const char *endpointUrl);
-
-/* Connect to the selected server with the given username and password
- *
- * @param client to use
- * @param endpointURL to connect (for example "opc.tcp://localhost:16664")
- * @param username
- * @param password
- * @return Indicates whether the operation succeeded or returns an error code */
-UA_StatusCode UA_EXPORT
-UA_Client_connect_username(UA_Client *client, const char *endpointUrl,
-                           const char *username, const char *password);
-
-/* Close a connection to the selected server */
-UA_StatusCode UA_EXPORT UA_Client_disconnect(UA_Client *client);
-
-/* Renew the underlying secure channel */
-UA_StatusCode UA_EXPORT UA_Client_manuallyRenewSecureChannel(UA_Client *client);
+                               UA_UInt32 startingRecordId, UA_UInt32 maxRecordsToReturn,
+                               size_t serverCapabilityFilterSize, UA_String *serverCapabilityFilter,
+                               size_t *serverOnNetworkSize, UA_ServerOnNetwork **serverOnNetwork);
 
 /**
  * .. _client-services:

+ 32 - 20
include/ua_plugin_securitypolicy.h

@@ -11,6 +11,7 @@ extern "C" {
 
 #include "ua_types.h"
 #include "ua_types_generated.h"
+#include "ua_plugin_log.h"
 
 extern const UA_ByteString UA_SECURITY_POLICY_NONE_URI;
 
@@ -18,6 +19,8 @@ struct UA_SecurityPolicy;
 typedef struct UA_SecurityPolicy UA_SecurityPolicy;
 
 typedef struct {
+    UA_String signatureAlgorithmUri;
+
     /* Verifies the signature of the message using the provided keys in the context.
      *
      * @param securityPolicy the securityPolicy the function is invoked on.
@@ -46,7 +49,7 @@ typedef struct {
                           const UA_ByteString *message,
                           UA_ByteString *signature);
 
-    /* Gets the signature size that depends on the local private key.
+    /* Gets the signature size that depends on the local (private) key.
      *
      * @param securityPolicy the securityPolicy the function is invoked on.
      * @param channelContext the channelContext that contains the
@@ -56,20 +59,17 @@ typedef struct {
     size_t (*getLocalSignatureSize)(const UA_SecurityPolicy *securityPolicy,
                                     const void *channelContext);
 
-    /* Gets the signature size that depends on the remote public key.
+    /* Gets the signature size that depends on the remote (public) key.
      *
      * @param securityPolicy the securityPolicy the function is invoked on.
      * @param channelContext the context to retrieve data from.
-     * @return the size of the remote asymmetric signature. Returns 0 if no
+     * @return the size of the remote signature. Returns 0 if no
      *         remote certificate was set previousely. */
     size_t (*getRemoteSignatureSize)(const UA_SecurityPolicy *securityPolicy,
                                      const void *channelContext);
 
-    /* The signature size in bytes */
-    UA_String signatureAlgorithmUri;
-} UA_SecurityPolicySigningModule;
+    UA_String encryptionAlgorithmUri;
 
-typedef struct {
     /* Encrypt the given data in place using an asymmetric algorithm and keys.
      *
      * @param securityPolicy the securityPolicy the function is invoked on.
@@ -90,7 +90,25 @@ typedef struct {
     UA_StatusCode(*decrypt)(const UA_SecurityPolicy *securityPolicy,
                             const void *channelContext,
                             UA_ByteString *data);
-} UA_SecurityPolicyEncryptingModule;
+
+    /* Returns the length of the key used locally to encrypt messages in bits
+     *
+     * @param securityPolicy the securityPolicy the function is invoked on.
+     * @param channelContext the context to retrieve data from.
+     * @return the length of the local key. Returns 0 if no
+     *         key length is known. */
+    size_t (*getLocalEncryptionKeyLength)(const UA_SecurityPolicy *securityPolicy,
+                                          const void *channelContext);
+
+    /* Returns the length of the key used remotely to encrypt messages in bits
+     *
+     * @param securityPolicy the securityPolicy the function is invoked on.
+     * @param channelContext the context to retrieve data from.
+     * @return the length of the remote key. Returns 0 if no
+     *         key length is known. */
+    size_t (*getRemoteEncryptionKeyLength)(const UA_SecurityPolicy *securityPolicy,
+                                           const void *channelContext);
+} UA_SecurityPolicyCryptoModule;
 
 typedef struct {
     /* Generates a thumprint for the specified certificate.
@@ -115,12 +133,7 @@ typedef struct {
     UA_StatusCode (*compareCertificateThumbprint)(const UA_SecurityPolicy *securityPolicy,
                                                   const UA_ByteString *certificateThumbprint);
 
-    size_t minAsymmetricKeyLength;
-    size_t maxAsymmetricKeyLength;
-    size_t thumbprintLength;
-
-    UA_SecurityPolicyEncryptingModule encryptingModule;
-    UA_SecurityPolicySigningModule signingModule;
+    UA_SecurityPolicyCryptoModule cryptoModule;
 } UA_SecurityPolicyAsymmetricModule;
 
 typedef struct {
@@ -149,12 +162,9 @@ typedef struct {
     UA_StatusCode (*generateNonce)(const UA_SecurityPolicy *securityPolicy,
                                    UA_ByteString *out);
 
-    UA_SecurityPolicyEncryptingModule encryptingModule;
-    UA_SecurityPolicySigningModule signingModule;
-
+    UA_SecurityPolicyCryptoModule cryptoModule;
+    size_t encryptionBlockSize;
     size_t signingKeyLength;
-    size_t encryptingKeyLength;
-    size_t encryptingBlockSize;
 } UA_SecurityPolicySymmetricModule;
 
 typedef struct {
@@ -243,7 +253,7 @@ typedef struct {
 
     /* Gets the number of bytes that are needed by the encryption function in
      * addition to the length of the plaintext message. This is needed, since
-     * most rsa encryption methods have their own padding mechanism included.
+     * most RSA encryption methods have their own padding mechanism included.
      * This makes the encrypted message larger than the plainText, so we need to
      * have enough room in the buffer for the overhead.
      *
@@ -270,6 +280,8 @@ struct UA_SecurityPolicy {
     UA_SecurityPolicySymmetricModule symmetricModule;
     UA_SecurityPolicyChannelModule channelModule;
 
+    UA_Logger logger;
+
     /* Deletes the dynamic content of the policy */
     void (*deleteMembers)(UA_SecurityPolicy *policy);
 };

+ 1 - 1
plugins/ua_config_default.c

@@ -217,7 +217,7 @@ UA_ServerConfig_new_minimal(UA_UInt16 portNumber,
     UA_ByteString localCertificate = UA_BYTESTRING_NULL;
     if(certificate)
         localCertificate = *certificate;
-    UA_StatusCode retval =
+    retval =
         createSecurityPolicyNoneEndpoint(conf, &conf->endpoints[0], localCertificate);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_ServerConfig_delete(conf);

+ 3 - 0
plugins/ua_log_stdout.c

@@ -21,6 +21,9 @@ const char *LogCategoryNames[7] = {"network", "channel", "session", "server", "c
 # pragma GCC diagnostic ignored "-Wformat-nonliteral"
 #endif
 
+#ifdef __clang__
+__attribute__((__format__(__printf__, 3 , 0)))
+#endif
 void
 UA_Log_Stdout(UA_LogLevel level, UA_LogCategory category,
               const char *msg, va_list args) {

+ 163 - 304
plugins/ua_securitypolicy_none.c

@@ -1,304 +1,163 @@
-/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
- * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
-
-#include "ua_securitypolicy_none.h"
-#include "ua_types_generated_handling.h"
-
-/*******************************/
-/* Asymmetric module functions */
-/*******************************/
-
-static UA_StatusCode
-asym_verify_sp_none(const UA_SecurityPolicy *const securityPolicy,
-                    const void *const channelContext,
-                    const UA_ByteString *const message,
-                    const UA_ByteString *const signature) {
-    return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode
-asym_sign_sp_none(const UA_SecurityPolicy *const securityPolicy,
-                  const void *const channelContext,
-                  const UA_ByteString *const message,
-                  UA_ByteString *const signature) {
-    return UA_STATUSCODE_GOOD;
-}
-
-static size_t
-asym_getLocalSignatureSize_sp_none(const UA_SecurityPolicy *const securityPolicy,
-                                   const void *const channelContext) {
-    return 0;
-}
-
-static size_t
-asym_getRemoteSignatureSize_sp_none(const UA_SecurityPolicy *const securityPolicy,
-                                    const void *const channelContext) {
-    return 0;
-}
-
-static UA_StatusCode
-asym_encrypt_sp_none(const UA_SecurityPolicy *const securityPolicy,
-                     const void *const channelContext,
-                     UA_ByteString *const data) {
-    return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode
-asym_decrypt_sp_none(const UA_SecurityPolicy *const securityPolicy,
-                     const void *const channelContext,
-                     UA_ByteString *const data) {
-    return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode
-asym_makeThumbprint_sp_none(const UA_SecurityPolicy *const securityPolicy,
-                            const UA_ByteString *const certificate,
-                            UA_ByteString *const thumbprint) {
-    if(certificate == NULL || thumbprint == NULL)
-        return UA_STATUSCODE_BADINTERNALERROR;
-    return UA_STATUSCODE_GOOD;
-}
-
-/* Only a stub function for now */
-static UA_StatusCode
-asym_compareThumbprint_sp_none(const UA_SecurityPolicy *securityPolicy,
-                               const UA_ByteString *certificateThumbprint) {
-    if(!securityPolicy || !certificateThumbprint)
-        return UA_STATUSCODE_BADINTERNALERROR;
-
-    // The certificatethumbprint doesn't exist if the security policy is none
-    if(certificateThumbprint->length != 0)
-        return UA_STATUSCODE_BADCERTIFICATEINVALID;
-
-    return UA_STATUSCODE_GOOD;
-}
-
-/******************************/
-/* Symmetric module functions */
-/******************************/
-
-static UA_StatusCode
-sym_verify_sp_none(const UA_SecurityPolicy *const securityPolicy,
-                   const void *const channelContext,
-                   const UA_ByteString *const message,
-                   const UA_ByteString *const signature) {
-    return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode
-sym_sign_sp_none(const UA_SecurityPolicy *const securityPolicy,
-                 const void *const channelContext,
-                 const UA_ByteString *const message,
-                 UA_ByteString *const signature) {
-    return UA_STATUSCODE_GOOD;
-}
-
-static size_t
-sym_getLocalSignatureSize_sp_none(const UA_SecurityPolicy *const securityPolicy,
-                                  const void *const channelContext) {
-    return 0;
-}
-
-static size_t
-sym_getRemoteSignatureSize_sp_none(const UA_SecurityPolicy *const securityPolicy,
-                                   const void *const channelContext) {
-    return 0;
-}
-
-static UA_StatusCode
-sym_encrypt_sp_none(const UA_SecurityPolicy *const securityPolicy,
-                    const void *const channelContext,
-                    UA_ByteString *const data) {
-    return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode
-sym_decrypt_sp_none(const UA_SecurityPolicy *const securityPolicy,
-                    const void *const channelContext,
-                    UA_ByteString *const data) {
-    return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode
-sym_generateKey_sp_none(const UA_SecurityPolicy *const securityPolicy,
-                        const UA_ByteString *const secret,
-                        const UA_ByteString *const seed,
-                        UA_ByteString *const out) {
-    return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode
-sym_generateNonce_sp_none(const UA_SecurityPolicy *const securityPolicy,
-                          UA_ByteString *const out) {
-    out->length = securityPolicy->symmetricModule.encryptingKeyLength;
-    out->data[0] = 'a';
-    return UA_STATUSCODE_GOOD;
-}
-
-/***************************/
-/* ChannelModule functions */
-/***************************/
-
-// this is not really needed in security policy none because no context is required
-// it is there to serve as a small example for policies that need context per channel
-typedef struct {
-    const UA_SecurityPolicy *securityPolicy;
-    int callCounter;
-} UA_SP_NONE_ChannelContextData;
-
-static UA_StatusCode
-channelContext_newContext_sp_none(const UA_SecurityPolicy *securityPolicy,
-                                  const UA_ByteString *remoteCertificate,
-                                  void **channelContext) {
-    if(channelContext == NULL)
-        return UA_STATUSCODE_BADINTERNALERROR;
-
-    *channelContext = UA_malloc(sizeof(UA_SP_NONE_ChannelContextData));
-    if(*channelContext == NULL)
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-
-    // Initialize the channelcontext data here to sensible values
-    UA_SP_NONE_ChannelContextData* const data = (UA_SP_NONE_ChannelContextData*)*channelContext;
-
-    data->callCounter = 0;
-    data->securityPolicy = securityPolicy;
-
-    return UA_STATUSCODE_GOOD;
-}
-
-static void
-channelContext_deleteContext_sp_none(void *const channelContext) {
-    if(channelContext != NULL)
-        UA_free(channelContext);
-}
-
-static UA_StatusCode
-channelContext_setLocalSymEncryptingKey_sp_none(void *const channelContext,
-                                                const UA_ByteString *const key) {
-    UA_SP_NONE_ChannelContextData* const data = (UA_SP_NONE_ChannelContextData*)channelContext;
-    data->callCounter++;
-    return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode
-channelContext_setLocalSymSigningKey_sp_none(void *const channelContext,
-                                             const UA_ByteString *const key) {
-    UA_SP_NONE_ChannelContextData* const data = (UA_SP_NONE_ChannelContextData*)channelContext;
-    data->callCounter++;
-    return UA_STATUSCODE_GOOD;
-}
-
-
-static UA_StatusCode
-channelContext_setLocalSymIv_sp_none(void *const channelContext,
-                                     const UA_ByteString *const iv) {
-    UA_SP_NONE_ChannelContextData* const data = (UA_SP_NONE_ChannelContextData*)channelContext;
-    data->callCounter++;
-    return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode
-channelContext_setRemoteSymEncryptingKey_sp_none(void *const channelContext,
-                                                 const UA_ByteString *const key) {
-    UA_SP_NONE_ChannelContextData* const data = (UA_SP_NONE_ChannelContextData*)channelContext;
-    data->callCounter++;
-    return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode
-channelContext_setRemoteSymSigningKey_sp_none(void *const channelContext,
-                                              const UA_ByteString *const key) {
-    UA_SP_NONE_ChannelContextData* const data = (UA_SP_NONE_ChannelContextData*)channelContext;
-    data->callCounter++;
-    return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode
-channelContext_setRemoteSymIv_sp_none(void *const channelContext,
-                                      const UA_ByteString *const iv) {
-    UA_SP_NONE_ChannelContextData* const data = (UA_SP_NONE_ChannelContextData*)channelContext;
-    data->callCounter++;
-    return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode
-channelContext_compareCertificate_sp_none(const void *const channelContext,
-                                          const UA_ByteString *const certificate) {
-    if(channelContext == NULL || certificate == NULL)
-        return UA_STATUSCODE_BADINTERNALERROR;
-
-    return UA_STATUSCODE_GOOD;
-}
-
-static size_t
-channelContext_getRemoteAsymPlainTextBlockSize_sp_none(const void *const channelContext) {
-    return 0;
-}
-
-static size_t
-channelContext_getRemoteAsymEncryptionBufferLengthOverhead_sp_none(const void *const channelContext,
-                                                                   const size_t maxEncryptionLength) {
-    return 0;
-}
-
-/*****************************/
-/* Security Policy functions */
-/*****************************/
-
-static void
-policy_deletemembers_sp_none(UA_SecurityPolicy *policy) {
-    UA_ByteString_deleteMembers(&policy->localCertificate);
-}
-
-UA_StatusCode
-UA_SecurityPolicy_None(UA_SecurityPolicy *policy,
-                       const UA_ByteString localCertificate,
-                       UA_Logger logger) {
-    policy->policyContext = (void*)(uintptr_t)logger;
-    policy->policyUri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None");
-    UA_ByteString_copy(&localCertificate, &policy->localCertificate);
-
-    policy->asymmetricModule.makeCertificateThumbprint = asym_makeThumbprint_sp_none;
-    policy->asymmetricModule.compareCertificateThumbprint = asym_compareThumbprint_sp_none;
-    policy->asymmetricModule.minAsymmetricKeyLength = 0;
-    policy->asymmetricModule.maxAsymmetricKeyLength = 0;
-    policy->asymmetricModule.thumbprintLength = 20;
-    policy->asymmetricModule.encryptingModule.encrypt = asym_encrypt_sp_none;
-    policy->asymmetricModule.encryptingModule.decrypt = asym_decrypt_sp_none;
-    policy->asymmetricModule.signingModule.verify = asym_verify_sp_none;
-    policy->asymmetricModule.signingModule.sign = asym_sign_sp_none;
-    policy->asymmetricModule.signingModule.getLocalSignatureSize = asym_getLocalSignatureSize_sp_none;
-    policy->asymmetricModule.signingModule.getRemoteSignatureSize = asym_getRemoteSignatureSize_sp_none;
-    policy->asymmetricModule.signingModule.signatureAlgorithmUri = UA_STRING_NULL;
-
-    policy->symmetricModule.generateKey = sym_generateKey_sp_none;
-    policy->symmetricModule.generateNonce = sym_generateNonce_sp_none;
-    policy->symmetricModule.encryptingModule.encrypt = sym_encrypt_sp_none;
-    policy->symmetricModule.encryptingModule.decrypt = sym_decrypt_sp_none;
-    policy->symmetricModule.signingModule.verify = sym_verify_sp_none;
-    policy->symmetricModule.signingModule.sign = sym_sign_sp_none;
-    policy->symmetricModule.signingModule.getLocalSignatureSize = sym_getLocalSignatureSize_sp_none;
-    policy->symmetricModule.signingModule.getRemoteSignatureSize = sym_getRemoteSignatureSize_sp_none;
-    policy->symmetricModule.signingModule.signatureAlgorithmUri = UA_STRING_NULL;
-    policy->symmetricModule.signingKeyLength = 0;
-    policy->symmetricModule.encryptingKeyLength = 1;
-    policy->symmetricModule.encryptingBlockSize = 0;
-
-    policy->channelModule.newContext = channelContext_newContext_sp_none;
-    policy->channelModule.deleteContext = channelContext_deleteContext_sp_none;
-    policy->channelModule.setLocalSymEncryptingKey = channelContext_setLocalSymEncryptingKey_sp_none;
-    policy->channelModule.setLocalSymSigningKey = channelContext_setLocalSymSigningKey_sp_none;
-    policy->channelModule.setLocalSymIv = channelContext_setLocalSymIv_sp_none;
-    policy->channelModule.setRemoteSymEncryptingKey = channelContext_setRemoteSymEncryptingKey_sp_none;
-    policy->channelModule.setRemoteSymSigningKey = channelContext_setRemoteSymSigningKey_sp_none;
-    policy->channelModule.setRemoteSymIv = channelContext_setRemoteSymIv_sp_none;
-    policy->channelModule.compareCertificate = channelContext_compareCertificate_sp_none;
-    policy->channelModule.getRemoteAsymPlainTextBlockSize =
-        channelContext_getRemoteAsymPlainTextBlockSize_sp_none;
-    policy->channelModule.getRemoteAsymEncryptionBufferLengthOverhead =
-        channelContext_getRemoteAsymEncryptionBufferLengthOverhead_sp_none;
-
-    policy->deleteMembers = policy_deletemembers_sp_none;
-
-    return UA_STATUSCODE_GOOD;
-}
+/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+
+#include "ua_securitypolicy_none.h"
+#include "ua_types_generated_handling.h"
+
+static UA_StatusCode
+verify_none(const UA_SecurityPolicy *securityPolicy,
+            const void *channelContext,
+            const UA_ByteString *message,
+            const UA_ByteString *signature) {
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode
+sign_none(const UA_SecurityPolicy *securityPolicy,
+          const void *channelContext,
+          const UA_ByteString *message,
+          UA_ByteString *signature) {
+    return UA_STATUSCODE_GOOD;
+}
+
+static size_t
+length_none(const UA_SecurityPolicy *securityPolicy,
+            const void *channelContext) {
+    return 0;
+}
+
+static UA_StatusCode
+encrypt_none(const UA_SecurityPolicy *securityPolicy,
+             const void *channelContext,
+             UA_ByteString *data) {
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode
+decrypt_none(const UA_SecurityPolicy *securityPolicy,
+             const void *channelContext,
+             UA_ByteString *data) {
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode
+makeThumbprint_none(const UA_SecurityPolicy *securityPolicy,
+                    const UA_ByteString *certificate,
+                    UA_ByteString *thumbprint) {
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode
+compareThumbprint_none(const UA_SecurityPolicy *securityPolicy,
+                       const UA_ByteString *certificateThumbprint) {
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode
+generateKey_none(const UA_SecurityPolicy *securityPolicy,
+                 const UA_ByteString *secret,
+                 const UA_ByteString *seed,
+                 UA_ByteString *out) {
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode
+generateNonce_none(const UA_SecurityPolicy *securityPolicy,
+                   UA_ByteString *out) {
+    out->data = UA_Byte_new();
+    if(!out->data)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    out->length = 1;
+    out->data[0] = 'a';
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode
+newContext_none(const UA_SecurityPolicy *securityPolicy,
+                const UA_ByteString *remoteCertificate,
+                void **channelContext) {
+    return UA_STATUSCODE_GOOD;
+}
+
+static void
+deleteContext_none(void *const channelContext) {
+}
+
+static UA_StatusCode
+setContextValue_none(void *channelContext,
+                     const UA_ByteString *key) {
+    return UA_STATUSCODE_GOOD;
+}
+
+static size_t
+getRemoteAsymPlainTextBlockSize_none(const void *const channelContext) {
+    return 0;
+}
+
+static size_t
+getRemoteAsymEncryptionBufferLengthOverhead_none(const void *const channelContext,
+                                                 const size_t maxEncryptionLength) {
+    return 0;
+}
+
+static UA_StatusCode
+compareCertificate_none(const void *channelContext,
+                        const UA_ByteString *certificate) {
+    return UA_STATUSCODE_GOOD;
+}
+
+static void
+policy_deletemembers_none(UA_SecurityPolicy *policy) {
+    UA_ByteString_deleteMembers(&policy->localCertificate);
+}
+
+UA_StatusCode
+UA_SecurityPolicy_None(UA_SecurityPolicy *policy, const UA_ByteString localCertificate,
+                       UA_Logger logger) {
+    policy->policyContext = (void*)(uintptr_t)logger;
+    policy->policyUri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None");
+    policy->logger = logger;
+    UA_ByteString_copy(&localCertificate, &policy->localCertificate);
+
+    policy->asymmetricModule.makeCertificateThumbprint = makeThumbprint_none;
+    policy->asymmetricModule.compareCertificateThumbprint = compareThumbprint_none;
+    policy->asymmetricModule.cryptoModule.signatureAlgorithmUri = UA_STRING_NULL;
+    policy->asymmetricModule.cryptoModule.verify = verify_none;
+    policy->asymmetricModule.cryptoModule.sign = sign_none;
+    policy->asymmetricModule.cryptoModule.getLocalSignatureSize = length_none;
+    policy->asymmetricModule.cryptoModule.getRemoteSignatureSize = length_none;
+    policy->asymmetricModule.cryptoModule.encrypt = encrypt_none;
+    policy->asymmetricModule.cryptoModule.decrypt = decrypt_none;
+    policy->asymmetricModule.cryptoModule.getLocalEncryptionKeyLength = length_none;
+    policy->asymmetricModule.cryptoModule.getRemoteEncryptionKeyLength = length_none;
+
+    policy->symmetricModule.generateKey = generateKey_none;
+    policy->symmetricModule.generateNonce = generateNonce_none;
+    policy->symmetricModule.cryptoModule.signatureAlgorithmUri = UA_STRING_NULL;
+    policy->symmetricModule.cryptoModule.verify = verify_none;
+    policy->symmetricModule.cryptoModule.sign = sign_none;
+    policy->symmetricModule.cryptoModule.getLocalSignatureSize = length_none;
+    policy->symmetricModule.cryptoModule.getRemoteSignatureSize = length_none;
+    policy->symmetricModule.cryptoModule.encrypt = encrypt_none;
+    policy->symmetricModule.cryptoModule.decrypt = decrypt_none;
+    policy->symmetricModule.cryptoModule.getLocalEncryptionKeyLength = length_none;
+    policy->symmetricModule.cryptoModule.getRemoteEncryptionKeyLength = length_none;
+    policy->symmetricModule.encryptionBlockSize = 0;
+    policy->symmetricModule.signingKeyLength = 0;
+
+    policy->channelModule.newContext = newContext_none;
+    policy->channelModule.deleteContext = deleteContext_none;
+    policy->channelModule.setLocalSymEncryptingKey = setContextValue_none;
+    policy->channelModule.setLocalSymSigningKey = setContextValue_none;
+    policy->channelModule.setLocalSymIv = setContextValue_none;
+    policy->channelModule.setRemoteSymEncryptingKey = setContextValue_none;
+    policy->channelModule.setRemoteSymSigningKey = setContextValue_none;
+    policy->channelModule.setRemoteSymIv = setContextValue_none;
+    policy->channelModule.compareCertificate = compareCertificate_none;
+    policy->channelModule.getRemoteAsymPlainTextBlockSize = getRemoteAsymPlainTextBlockSize_none;
+    policy->channelModule.getRemoteAsymEncryptionBufferLengthOverhead =
+        getRemoteAsymEncryptionBufferLengthOverhead_none;
+    policy->deleteMembers = policy_deletemembers_none;
+
+    return UA_STATUSCODE_GOOD;
+}

+ 98 - 689
src/client/ua_client.c

@@ -7,31 +7,36 @@
 #include "ua_connection_internal.h"
 #include "ua_types_encoding_binary.h"
 #include "ua_types_generated_encoding_binary.h"
-#include "ua_transport_generated.h"
-#include "ua_transport_generated_handling.h"
-#include "ua_transport_generated_encoding_binary.h"
 #include "ua_util.h"
+#include "ua_securitypolicy_none.h"
 
-/*********************/
-/* Create and Delete */
-/*********************/
+ /********************/
+ /* Client Lifecycle */
+ /********************/
 
-static void UA_Client_init(UA_Client* client, UA_ClientConfig config) {
+static void
+UA_Client_init(UA_Client* client, UA_ClientConfig config) {
     memset(client, 0, sizeof(UA_Client));
-    client->channel.connection = &client->connection;
+    /* TODO: Select policy according to the endpoint */
+    UA_SecurityPolicy_None(&client->securityPolicy, UA_BYTESTRING_NULL, config.logger);
+    client->channel.securityPolicy = &client->securityPolicy;
+    client->channel.securityMode = UA_MESSAGESECURITYMODE_NONE;
     client->config = config;
 }
 
-UA_Client * UA_Client_new(UA_ClientConfig config) {
-    UA_Client *client = (UA_Client*)UA_calloc(1, sizeof(UA_Client));
+UA_Client *
+UA_Client_new(UA_ClientConfig config) {
+    UA_Client *client = (UA_Client*)UA_malloc(sizeof(UA_Client));
     if(!client)
         return NULL;
     UA_Client_init(client, config);
     return client;
 }
 
-static void UA_Client_deleteMembers(UA_Client* client) {
+static void
+UA_Client_deleteMembers(UA_Client* client) {
     UA_Client_disconnect(client);
+    client->securityPolicy.deleteMembers(&client->securityPolicy);
     UA_SecureChannel_deleteMembersCleanup(&client->channel);
     UA_Connection_deleteMembers(&client->connection);
     if(client->endpointUrl.data)
@@ -63,616 +68,42 @@ static void UA_Client_deleteMembers(UA_Client* client) {
 #endif
 }
 
-void UA_Client_reset(UA_Client* client){
+void
+UA_Client_reset(UA_Client* client) {
     UA_Client_deleteMembers(client);
     UA_Client_init(client, client->config);
 }
 
-void UA_Client_delete(UA_Client* client){
+void
+UA_Client_delete(UA_Client* client) {
     UA_Client_deleteMembers(client);
     UA_free(client);
 }
 
-UA_ClientState UA_Client_getState(UA_Client *client) {
-    if(!client)
-        return UA_CLIENTSTATE_ERRORED;
+UA_ClientState
+UA_Client_getState(UA_Client *client) {
     return client->state;
 }
 
-/*************************/
-/* Manage the Connection */
-/*************************/
-
-#define UA_MINMESSAGESIZE 8192
-
-static void
-processHelResponse(void *application, UA_Connection *connection,
-                   const UA_ByteString *chunk) {
-    UA_Client *client = (UA_Client*)application;
-
-    /* Decode the message */
-    size_t offset = 0;
-    UA_StatusCode retval;
-    UA_TcpMessageHeader messageHeader;
-    UA_TcpAcknowledgeMessage ackMessage;
-    retval = UA_TcpMessageHeader_decodeBinary(chunk, &offset, &messageHeader);
-    retval |= UA_TcpAcknowledgeMessage_decodeBinary(chunk, &offset, &ackMessage);
-
-    /* Store remote connection settings and adjust local configuration to not
-     * exceed the limits */
-    if(retval == UA_STATUSCODE_GOOD) {
-        UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_NETWORK, "Received ACK message");
-        connection->remoteConf.maxChunkCount = ackMessage.maxChunkCount; /* may be zero -> unlimited */
-        connection->remoteConf.maxMessageSize = ackMessage.maxMessageSize; /* may be zero -> unlimited */
-        connection->remoteConf.protocolVersion = ackMessage.protocolVersion;
-        connection->remoteConf.sendBufferSize = ackMessage.sendBufferSize;
-        connection->remoteConf.recvBufferSize = ackMessage.receiveBufferSize;
-        if(connection->remoteConf.recvBufferSize < connection->localConf.sendBufferSize)
-            connection->localConf.sendBufferSize = connection->remoteConf.recvBufferSize;
-        if(connection->remoteConf.sendBufferSize < connection->localConf.recvBufferSize)
-            connection->localConf.recvBufferSize = connection->remoteConf.sendBufferSize;
-        connection->state = UA_CONNECTION_ESTABLISHED;
-    } else {
-        UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_NETWORK, "Decoding ACK message failed");
-    }
-    UA_TcpAcknowledgeMessage_deleteMembers(&ackMessage);
-}
-
-static UA_StatusCode
-HelAckHandshake(UA_Client *client) {
-    /* Get a buffer */
-    UA_ByteString message;
-    UA_Connection *conn = &client->connection;
-    UA_StatusCode retval = conn->getSendBuffer(conn, UA_MINMESSAGESIZE, &message);
-    if(retval != UA_STATUSCODE_GOOD)
-        return retval;
-
-    /* Prepare the HEL message and encode at offset 8 */
-    UA_TcpHelloMessage hello;
-    UA_String_copy(&client->endpointUrl, &hello.endpointUrl); /* must be less than 4096 bytes */
-    hello.maxChunkCount = conn->localConf.maxChunkCount;
-    hello.maxMessageSize = conn->localConf.maxMessageSize;
-    hello.protocolVersion = conn->localConf.protocolVersion;
-    hello.receiveBufferSize = conn->localConf.recvBufferSize;
-    hello.sendBufferSize = conn->localConf.sendBufferSize;
-
-    UA_Byte *bufPos = &message.data[8]; /* skip the header */
-    const UA_Byte *bufEnd = &message.data[message.length];
-    retval = UA_TcpHelloMessage_encodeBinary(&hello, &bufPos, &bufEnd);
-    UA_TcpHelloMessage_deleteMembers(&hello);
-
-    /* Encode the message header at offset 0 */
-    UA_TcpMessageHeader messageHeader;
-    messageHeader.messageTypeAndChunkType = UA_CHUNKTYPE_FINAL + UA_MESSAGETYPE_HEL;
-    messageHeader.messageSize = (UA_UInt32)((uintptr_t)bufPos - (uintptr_t)message.data);
-    bufPos = message.data;
-    retval |= UA_TcpMessageHeader_encodeBinary(&messageHeader, &bufPos, &bufEnd);
-    if(retval != UA_STATUSCODE_GOOD) {
-        conn->releaseSendBuffer(conn, &message);
-        return retval;
-    }
-
-    /* Send the HEL message */
-    message.length = messageHeader.messageSize;
-    retval = conn->send(conn, &message);
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_NETWORK,
-                    "Sending HEL failed");
-        return retval;
-    }
-    UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_NETWORK,
-                 "Sent HEL message");
-
-    /* Loop until we have a complete chunk */
-    retval = UA_Connection_receiveChunksBlocking(conn, client, processHelResponse,
-                                                 client->config.timeout);
-    if(retval != UA_STATUSCODE_GOOD)
-        UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_NETWORK,
-                    "Receiving ACK message failed");
-    return retval;
-}
-
-struct OPNResponseData {
-    UA_Client *client;
-    UA_Boolean renew;
-};
-
-static void
-processOPNResponse(void *data, UA_Connection *connection,
-                   const UA_ByteString *chunk) {
-    struct OPNResponseData *rd = (struct OPNResponseData*)data;
-    UA_Client *client = rd->client;
-
-    /* Decode the header */
-    size_t offset = 0;
-    UA_StatusCode retval;
-    UA_SecureConversationMessageHeader messageHeader;
-    UA_AsymmetricAlgorithmSecurityHeader asymHeader;
-    UA_SequenceHeader seqHeader;
-    UA_NodeId requestType;
-    retval = UA_SecureConversationMessageHeader_decodeBinary(chunk, &offset, &messageHeader);
-    retval |= UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(chunk, &offset, &asymHeader);
-    retval |= UA_SequenceHeader_decodeBinary(chunk, &offset, &seqHeader);
-    retval |= UA_NodeId_decodeBinary(chunk, &offset, &requestType);
-    UA_NodeId expectedRequest =
-        UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE].binaryEncodingId);
-    if(retval != UA_STATUSCODE_GOOD || !UA_NodeId_equal(&requestType, &expectedRequest)) {
-        UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
-        UA_NodeId_deleteMembers(&requestType);
-        UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_CLIENT,
-                     "Chunk answers the wrong request. Expected OpenSecureChannelResponse.");
-        client->state = UA_CLIENTSTATE_ERRORED;
-    }
-
-    /* Save the sequence number from server */
-    client->channel.receiveSequenceNumber = seqHeader.sequenceNumber;
-
-    /* Decode the response */
-    UA_OpenSecureChannelResponse response;
-    retval = UA_OpenSecureChannelResponse_decodeBinary(chunk, &offset, &response);
-
-    /* Results in either the StatusCode of decoding or the service */
-    retval |= response.responseHeader.serviceResult;
-
-    if(retval == UA_STATUSCODE_GOOD) {
-        /* Response.securityToken.revisedLifetime is UInt32 we need to cast it
-         * to DateTime=Int64 we take 75% of lifetime to start renewing as
-         *  described in standard */
-        client->nextChannelRenewal = UA_DateTime_nowMonotonic() +
-            (UA_DateTime)(response.securityToken.revisedLifetime * (UA_Double)UA_MSEC_TO_DATETIME * 0.75);
-
-        /* Replace the old nonce */
-        UA_ChannelSecurityToken_deleteMembers(&client->channel.securityToken);
-        UA_ChannelSecurityToken_copy(&response.securityToken, &client->channel.securityToken);
-        UA_ByteString_deleteMembers(&client->channel.serverNonce);
-        UA_ByteString_copy(&response.serverNonce, &client->channel.serverNonce);
-
-        if(rd->renew)
-            UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
-                         "SecureChannel renewed");
-        else
-            UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
-                         "SecureChannel opened");
-    } else {
-        if(rd->renew)
-            UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
-                        "SecureChannel could not be renewed "
-                        "with error code %s", UA_StatusCode_name(retval));
-        else
-            UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
-                        "SecureChannel could not be opened "
-                        "with error code %s", UA_StatusCode_name(retval));
-    }
-
-    /* Clean up */
-    UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
-    UA_OpenSecureChannelResponse_deleteMembers(&response);
-}
-
-static UA_StatusCode
-SecureChannelHandshake(UA_Client *client, UA_Boolean renew) {
-    /* Check if sc is still valid */
-    if(renew && client->nextChannelRenewal - UA_DateTime_nowMonotonic() > 0)
-        return UA_STATUSCODE_GOOD;
-
-    UA_Connection *conn = &client->connection;
-    if(conn->state != UA_CONNECTION_ESTABLISHED)
-        return UA_STATUSCODE_BADSERVERNOTCONNECTED;
-
-    UA_ByteString message;
-    UA_StatusCode retval = conn->getSendBuffer(conn, conn->remoteConf.recvBufferSize, &message);
-    if(retval != UA_STATUSCODE_GOOD)
-        return retval;
-
-    /* Jump over the messageHeader that will be encoded last */
-    UA_Byte *bufPos = &message.data[12];
-    const UA_Byte *bufEnd = &message.data[message.length];
-
-    /* Encode the Asymmetric Security Header */
-    UA_AsymmetricAlgorithmSecurityHeader asymHeader;
-    UA_AsymmetricAlgorithmSecurityHeader_init(&asymHeader);
-    asymHeader.securityPolicyUri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None");
-    retval = UA_AsymmetricAlgorithmSecurityHeader_encodeBinary(&asymHeader, &bufPos, &bufEnd);
-
-    /* Encode the sequence header */
-    UA_SequenceHeader seqHeader;
-    seqHeader.sequenceNumber = ++client->channel.sendSequenceNumber;
-    seqHeader.requestId = ++client->requestId;
-    retval |= UA_SequenceHeader_encodeBinary(&seqHeader, &bufPos, &bufEnd);
-
-    /* Encode the NodeId of the OpenSecureChannel Service */
-    UA_NodeId requestType =
-        UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_OPENSECURECHANNELREQUEST].binaryEncodingId);
-    retval |= UA_NodeId_encodeBinary(&requestType, &bufPos, &bufEnd);
-
-    /* Encode the OpenSecureChannelRequest */
-    UA_OpenSecureChannelRequest opnSecRq;
-    UA_OpenSecureChannelRequest_init(&opnSecRq);
-    opnSecRq.requestHeader.timestamp = UA_DateTime_now();
-    opnSecRq.requestHeader.authenticationToken = client->authenticationToken;
-    if(renew) {
-        opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_RENEW;
-        UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
-                     "Requesting to renew the SecureChannel");
-    } else {
-        opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_ISSUE;
-        UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
-                     "Requesting to open a SecureChannel");
-    }
-    opnSecRq.securityMode = UA_MESSAGESECURITYMODE_NONE;
-    opnSecRq.clientNonce = client->channel.clientNonce;
-    opnSecRq.requestedLifetime = client->config.secureChannelLifeTime;
-    retval |= UA_OpenSecureChannelRequest_encodeBinary(&opnSecRq, &bufPos, &bufEnd);
-
-    /* Encode the message header at the beginning */
-    size_t length = (uintptr_t)(bufPos - message.data);
-    bufPos = message.data;
-    UA_SecureConversationMessageHeader messageHeader;
-    messageHeader.messageHeader.messageTypeAndChunkType = UA_MESSAGETYPE_OPN + UA_CHUNKTYPE_FINAL;
-    messageHeader.messageHeader.messageSize = (UA_UInt32)length;
-    if(renew)
-        messageHeader.secureChannelId = client->channel.securityToken.channelId;
-    else
-        messageHeader.secureChannelId = 0;
-    retval |= UA_SecureConversationMessageHeader_encodeBinary(&messageHeader, &bufPos, &bufEnd);
-
-    /* Clean up and return if encoding the message failed */
-    if(retval != UA_STATUSCODE_GOOD) {
-        client->connection.releaseSendBuffer(&client->connection, &message);
-        return retval;
-    }
-
-    /* Send the message */
-    message.length = length;
-    retval = conn->send(conn, &message);
-    if(retval != UA_STATUSCODE_GOOD)
-        return retval;
-
-    /* Receive the response */
-    struct OPNResponseData rd;
-    rd.client = client;
-    rd.renew = renew;
-    retval = UA_Connection_receiveChunksBlocking(conn, &rd, processOPNResponse,
-                                                 client->config.timeout);
-    if(retval != UA_STATUSCODE_GOOD)
-        UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
-                     "Receiving OpenSecureChannelResponse failed");
-    return retval;
-}
-
-static UA_StatusCode
-ActivateSession(UA_Client *client) {
-    UA_ActivateSessionRequest request;
-    UA_ActivateSessionRequest_init(&request);
-    request.requestHeader.requestHandle = ++client->requestHandle;
-    request.requestHeader.timestamp = UA_DateTime_now();
-    request.requestHeader.timeoutHint = 600000;
-
-    //manual ExtensionObject encoding of the identityToken
-    if(client->authenticationMethod == UA_CLIENTAUTHENTICATION_NONE) {
-        UA_AnonymousIdentityToken* identityToken = UA_AnonymousIdentityToken_new();
-        UA_AnonymousIdentityToken_init(identityToken);
-        UA_String_copy(&client->token.policyId, &identityToken->policyId);
-        request.userIdentityToken.encoding = UA_EXTENSIONOBJECT_DECODED;
-        request.userIdentityToken.content.decoded.type = &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN];
-        request.userIdentityToken.content.decoded.data = identityToken;
-    } else {
-        UA_UserNameIdentityToken* identityToken = UA_UserNameIdentityToken_new();
-        UA_UserNameIdentityToken_init(identityToken);
-        UA_String_copy(&client->token.policyId, &identityToken->policyId);
-        UA_String_copy(&client->username, &identityToken->userName);
-        UA_String_copy(&client->password, &identityToken->password);
-        request.userIdentityToken.encoding = UA_EXTENSIONOBJECT_DECODED;
-        request.userIdentityToken.content.decoded.type = &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN];
-        request.userIdentityToken.content.decoded.data = identityToken;
-    }
-
-    UA_ActivateSessionResponse response;
-    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST],
-                        &response, &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE]);
-
-    if(response.responseHeader.serviceResult) {
-        UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
-                     "ActivateSession failed with error code %s",
-                     UA_StatusCode_name(response.responseHeader.serviceResult));
-    }
-
-    UA_StatusCode retval = response.responseHeader.serviceResult;
-    UA_ActivateSessionRequest_deleteMembers(&request);
-    UA_ActivateSessionResponse_deleteMembers(&response);
-    return retval;
-}
-
-/* Gets a list of endpoints. Memory is allocated for endpointDescription array */
-UA_StatusCode
-__UA_Client_getEndpoints(UA_Client *client, size_t* endpointDescriptionsSize,
-                         UA_EndpointDescription** endpointDescriptions) {
-    UA_GetEndpointsRequest request;
-    UA_GetEndpointsRequest_init(&request);
-    request.requestHeader.timestamp = UA_DateTime_now();
-    request.requestHeader.timeoutHint = 10000;
-    // assume the endpointurl outlives the service call
-    request.endpointUrl = client->endpointUrl; 
-
-    UA_GetEndpointsResponse response;
-    UA_GetEndpointsResponse_init(&response);
-    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST],
-                        &response, &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE]);
-
-    if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
-        UA_StatusCode retval = response.responseHeader.serviceResult;
-        UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
-                     "GetEndpointRequest failed with error code %s",
-                     UA_StatusCode_name(retval));
-        UA_GetEndpointsResponse_deleteMembers(&response);
-        return retval;
-    }
-    *endpointDescriptions = response.endpoints;
-    *endpointDescriptionsSize = response.endpointsSize;
-    response.endpoints = NULL;
-    response.endpointsSize = 0;
-    UA_GetEndpointsResponse_deleteMembers(&response);
-    return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode
-EndpointsHandshake(UA_Client *client) {
-    UA_EndpointDescription* endpointArray = NULL;
-    size_t endpointArraySize = 0;
-    UA_StatusCode retval = __UA_Client_getEndpoints(client, &endpointArraySize, &endpointArray);
-    if(retval != UA_STATUSCODE_GOOD)
-        return retval;
-
-    UA_Boolean endpointFound = false;
-    UA_Boolean tokenFound = false;
-    UA_String securityNone = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None");
-    UA_String binaryTransport = UA_STRING("http://opcfoundation.org/UA-Profile/"
-                                          "Transport/uatcp-uasc-uabinary");
-
-    //TODO: compare endpoint information with client->endpointUri
-    for(size_t i = 0; i < endpointArraySize; ++i) {
-        UA_EndpointDescription* endpoint = &endpointArray[i];
-        /* look out for binary transport endpoints */
-        /* Note: Siemens returns empty ProfileUrl, we will accept it as binary */
-        if(endpoint->transportProfileUri.length != 0 &&
-           !UA_String_equal(&endpoint->transportProfileUri, &binaryTransport))
-            continue;
-        /* look out for an endpoint without security */
-        if(!UA_String_equal(&endpoint->securityPolicyUri, &securityNone))
-            continue;
-        
-        /* endpoint with no security found */
-        endpointFound = true;
-       
-        /* look for a user token policy with an anonymous token */
-        for(size_t j = 0; j < endpoint->userIdentityTokensSize; ++j) {
-            UA_UserTokenPolicy* userToken = &endpoint->userIdentityTokens[j];
-
-            /* Usertokens also have a security policy... */
-            if(userToken->securityPolicyUri.length > 0 &&
-               !UA_String_equal(&userToken->securityPolicyUri, &securityNone))
-                continue;
-
-            /* UA_CLIENTAUTHENTICATION_NONE == UA_USERTOKENTYPE_ANONYMOUS
-             * UA_CLIENTAUTHENTICATION_USERNAME == UA_USERTOKENTYPE_USERNAME
-             * TODO: Check equivalence for other types when adding the support */
-            if((int)client->authenticationMethod != (int)userToken->tokenType)
-                continue;
-
-            /* Endpoint with matching usertokenpolicy found */
-            tokenFound = true;
-            UA_UserTokenPolicy_copy(userToken, &client->token);
-            break;
-        }
-    }
-
-    UA_Array_delete(endpointArray, endpointArraySize,
-                    &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
-
-    if(!endpointFound) {
-        UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
-                     "No suitable endpoint found");
-        retval = UA_STATUSCODE_BADINTERNALERROR;
-    } else if(!tokenFound) {
-        UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
-                     "No suitable UserTokenPolicy found for the possible endpoints");
-        retval = UA_STATUSCODE_BADINTERNALERROR;
-    }
-    return retval;
-}
-
-static UA_StatusCode
-SessionHandshake(UA_Client *client) {
-    UA_CreateSessionRequest request;
-    UA_CreateSessionRequest_init(&request);
-
-    request.requestHeader.timestamp = UA_DateTime_now();
-    request.requestHeader.timeoutHint = 10000;
-    UA_ByteString_copy(&client->channel.clientNonce, &request.clientNonce);
-    request.requestedSessionTimeout = 1200000;
-    request.maxResponseMessageSize = UA_INT32_MAX;
-    UA_String_copy(&client->endpointUrl, &request.endpointUrl);
-
-    UA_CreateSessionResponse response;
-    UA_CreateSessionResponse_init(&response);
-    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST],
-                        &response, &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE]);
-
-    UA_NodeId_copy(&response.authenticationToken, &client->authenticationToken);
-
-    UA_StatusCode retval = response.responseHeader.serviceResult;
-    UA_CreateSessionRequest_deleteMembers(&request);
-    UA_CreateSessionResponse_deleteMembers(&response);
-    return retval;
-}
-
-static UA_StatusCode
-CloseSession(UA_Client *client) {
-    UA_CloseSessionRequest request;
-    UA_CloseSessionRequest_init(&request);
-
-    request.requestHeader.timestamp = UA_DateTime_now();
-    request.requestHeader.timeoutHint = 10000;
-    request.deleteSubscriptions = true;
-    UA_CloseSessionResponse response;
-    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST],
-                        &response, &UA_TYPES[UA_TYPES_CLOSESESSIONRESPONSE]);
-
-    UA_StatusCode retval = response.responseHeader.serviceResult;
-    UA_CloseSessionRequest_deleteMembers(&request);
-    UA_CloseSessionResponse_deleteMembers(&response);
-    return retval;
-}
-
-static UA_StatusCode
-CloseSecureChannel(UA_Client *client) {
-    UA_SecureChannel *channel = &client->channel;
-    UA_CloseSecureChannelRequest request;
-    UA_CloseSecureChannelRequest_init(&request);
-    request.requestHeader.requestHandle = ++client->requestHandle;
-    request.requestHeader.timestamp = UA_DateTime_now();
-    request.requestHeader.timeoutHint = 10000;
-    UA_NodeId_copy(&client->authenticationToken,
-                   &request.requestHeader.authenticationToken);
-
-    UA_SecureConversationMessageHeader msgHeader;
-    msgHeader.messageHeader.messageTypeAndChunkType = UA_MESSAGETYPE_CLO + UA_CHUNKTYPE_FINAL;
-    msgHeader.secureChannelId = channel->securityToken.channelId;
-
-    UA_SymmetricAlgorithmSecurityHeader symHeader;
-    symHeader.tokenId = channel->securityToken.tokenId;
-
-    UA_SequenceHeader seqHeader;
-    seqHeader.sequenceNumber = ++channel->sendSequenceNumber;
-    seqHeader.requestId = ++client->requestId;
-
-    UA_NodeId typeId =
-        UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_CLOSESECURECHANNELREQUEST].binaryEncodingId);
-
-    UA_ByteString message;
-    UA_Connection *conn = &client->connection;
-    UA_StatusCode retval = conn->getSendBuffer(conn, conn->remoteConf.recvBufferSize, &message);
-    if(retval != UA_STATUSCODE_GOOD){
-        UA_CloseSecureChannelRequest_deleteMembers(&request);
-        return retval;
-    }
-
-    /* Jump over the header */
-    UA_Byte *bufPos = &message.data[12];
-    const UA_Byte *bufEnd = &message.data[message.length];
-    
-    /* Encode some more headers and the CloseSecureChannelRequest */
-    retval |= UA_SymmetricAlgorithmSecurityHeader_encodeBinary(&symHeader, &bufPos, &bufEnd);
-    retval |= UA_SequenceHeader_encodeBinary(&seqHeader, &bufPos, &bufEnd);
-    retval |= UA_NodeId_encodeBinary(&typeId, &bufPos, &bufEnd);
-    retval |= UA_encodeBinary(&request, &UA_TYPES[UA_TYPES_CLOSESECURECHANNELREQUEST],
-                              &bufPos, &bufEnd, NULL, NULL);
-
-    /* Encode the header */
-    msgHeader.messageHeader.messageSize = (UA_UInt32)((uintptr_t)bufPos - (uintptr_t)message.data);
-    bufPos = message.data;
-    retval |= UA_SecureConversationMessageHeader_encodeBinary(&msgHeader, &bufPos, &bufEnd);
-
-    if(retval == UA_STATUSCODE_GOOD) {
-        message.length = msgHeader.messageHeader.messageSize;
-        retval = conn->send(conn, &message);
-    } else {
-        conn->releaseSendBuffer(conn, &message);
-    }
-    conn->close(conn);
-    UA_CloseSecureChannelRequest_deleteMembers(&request);
-    return retval;
-}
-
-UA_StatusCode
-UA_Client_connect_username(UA_Client *client, const char *endpointUrl,
-                           const char *username, const char *password){
-    client->authenticationMethod=UA_CLIENTAUTHENTICATION_USERNAME;
-    client->username = UA_STRING_ALLOC(username);
-    client->password = UA_STRING_ALLOC(password);
-    return UA_Client_connect(client, endpointUrl);
-}
-
-UA_StatusCode
-__UA_Client_connect(UA_Client *client, const char *endpointUrl,
-                    UA_Boolean endpointsHandshake, UA_Boolean createSession) {
-    if(client->state == UA_CLIENTSTATE_CONNECTED)
-        return UA_STATUSCODE_GOOD;
-    if(client->state == UA_CLIENTSTATE_ERRORED) {
-        UA_Client_reset(client);
-    }
-
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    client->connection =
-        client->config.connectionFunc(client->config.localConnectionConfig, endpointUrl, client->config.timeout);
-    if(client->connection.state != UA_CONNECTION_OPENING) {
-        retval = UA_STATUSCODE_BADCONNECTIONCLOSED;
-        goto cleanup;
-    }
-
-    client->endpointUrl = UA_STRING_ALLOC(endpointUrl);
-    if(!client->endpointUrl.data) {
-        retval = UA_STATUSCODE_BADOUTOFMEMORY;
-        goto cleanup;
-    }
-
-    client->connection.localConf = client->config.localConnectionConfig;
-    retval = HelAckHandshake(client);
-    if(retval == UA_STATUSCODE_GOOD)
-        retval = SecureChannelHandshake(client, false);
-    if(endpointsHandshake && retval == UA_STATUSCODE_GOOD)
-        retval = EndpointsHandshake(client);
-    if(endpointsHandshake && createSession && retval == UA_STATUSCODE_GOOD)
-        retval = SessionHandshake(client);
-    if(endpointsHandshake && createSession && retval == UA_STATUSCODE_GOOD)
-        retval = ActivateSession(client);
-    if(retval == UA_STATUSCODE_GOOD) {
-        client->connection.state = UA_CONNECTION_ESTABLISHED;
-        client->state = UA_CLIENTSTATE_CONNECTED;
-    } else {
-        goto cleanup;
-    }
-    return retval;
-
- cleanup:
-    UA_Client_reset(client);
-    return retval;
-}
-
-UA_StatusCode
-UA_Client_connect(UA_Client *client, const char *endpointUrl) {
-    return __UA_Client_connect(client, endpointUrl, UA_TRUE, UA_TRUE);
-}
-
-UA_StatusCode UA_Client_disconnect(UA_Client *client) {
-    if(client->state == UA_CLIENTSTATE_READY)
-        return UA_STATUSCODE_BADNOTCONNECTED;
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    /* Is a session established? */
-    if(client->connection.state == UA_CONNECTION_ESTABLISHED &&
-       !UA_NodeId_equal(&client->authenticationToken, &UA_NODEID_NULL))
-        retval = CloseSession(client);
-    /* Is a secure channel established? */
-    if(client->connection.state == UA_CONNECTION_ESTABLISHED)
-        retval |= CloseSecureChannel(client);
-    return retval;
-}
-
-UA_StatusCode UA_Client_manuallyRenewSecureChannel(UA_Client *client) {
-    UA_StatusCode retval = SecureChannelHandshake(client, true);
-    if(retval == UA_STATUSCODE_GOOD)
-        client->state = UA_CLIENTSTATE_CONNECTED;
-    return retval;
-}
-
 /****************/
 /* Raw Services */
 /****************/
 
+/* For synchronous service calls. Execute async responses with a callback. When
+ * the response with the correct requestId turns up, return it via the
+ * SyncResponseDescription pointer. */
+typedef struct {
+    UA_Client *client;
+    UA_Boolean received;
+    UA_UInt32 requestId;
+    void *response;
+    const UA_DataType *responseType;
+} SyncResponseDescription;
+
 /* For both synchronous and asynchronous service calls */
 static UA_StatusCode
-sendServiceRequest(UA_Client *client, const void *request,
-                   const UA_DataType *requestType, UA_UInt32 *requestId) {
+sendSymmetricServiceRequest(UA_Client *client, const void *request,
+                            const UA_DataType *requestType, UA_UInt32 *requestId) {
     /* Make sure we have a valid session */
     UA_StatusCode retval = UA_Client_manuallyRenewSecureChannel(client);
     if(retval != UA_STATUSCODE_GOOD)
@@ -689,8 +120,9 @@ sendServiceRequest(UA_Client *client, const void *request,
     UA_UInt32 rqId = ++client->requestId;
     UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_CLIENT,
                  "Sending a request of type %i", requestType->typeId.identifier.numeric);
-    retval = UA_SecureChannel_sendBinaryMessage(&client->channel, rqId, rr, requestType);
-    UA_NodeId_init(&rr->authenticationToken);
+    retval = UA_SecureChannel_sendSymmetricMessage(&client->channel, rqId, UA_MESSAGETYPE_MSG,
+                                                   rr, requestType);
+    UA_NodeId_init(&rr->authenticationToken); /* Do not return the token to the user */
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
 
@@ -700,9 +132,8 @@ sendServiceRequest(UA_Client *client, const void *request,
 
 /* Look for the async callback in the linked list, execute and delete it */
 static UA_StatusCode
-processAsyncResponse(UA_Client *client, UA_UInt32 requestId,
-                  UA_NodeId *responseTypeId, UA_ByteString *responseMessage,
-                  size_t *offset) {
+processAsyncResponse(UA_Client *client, UA_UInt32 requestId, UA_NodeId *responseTypeId,
+                     const UA_ByteString *responseMessage, size_t *offset) {
     /* Find the callback */
     AsyncServiceCall *ac;
     LIST_FOREACH(ac, &client->asyncServiceCalls, pointers) {
@@ -732,57 +163,32 @@ processAsyncResponse(UA_Client *client, UA_UInt32 requestId,
     return retval;
 }
 
-/* For synchronous service calls. Execute async responses until the response
- * with the correct requestId turns up. Then, the response is decoded into the
- * "response" pointer. If the responseType is NULL, just process responses as
- * async. */
-typedef struct {
-    UA_Client *client;
-    UA_Boolean received;
-    UA_UInt32 requestId;
-    void *response;
-    const UA_DataType *responseType;
-} SyncResponseDescription;
-
-static void
-processServiceResponse(SyncResponseDescription *rd, UA_SecureChannel *channel,
+/* Processes the received service response. Either with an async callback or by
+ * decoding the message and returning it "upwards" in the
+ * SyncResponseDescription. */
+static UA_StatusCode
+processServiceResponse(void *application, UA_SecureChannel *channel,
                        UA_MessageType messageType, UA_UInt32 requestId,
-                       UA_ByteString *message) {
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+                       const UA_ByteString *message) {
+    SyncResponseDescription *rd = (SyncResponseDescription*)application;
+
+    /* Must be OPN or MSG */
+    if(messageType != UA_MESSAGETYPE_OPN &&
+       messageType != UA_MESSAGETYPE_MSG) {
+        UA_LOG_TRACE_CHANNEL(rd->client->config.logger, channel,
+                             "Invalid message type");
+        return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID;
+    }
+
+    /* Forward declaration for the goto */
     UA_NodeId expectedNodeId;
     const UA_NodeId serviceFaultNodeId =
         UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_SERVICEFAULT].binaryEncodingId);
 
-    UA_ResponseHeader *respHeader = (UA_ResponseHeader*)rd->response;
-
-    /* Forward declaration for the goto */
+    /* Decode the data type identifier of the response */
     size_t offset = 0;
     UA_NodeId responseId;
-    UA_NodeId_init(&responseId);
-
-    /* Got an error.
-     * TODO: Return as part of the response header */
-    if(messageType == UA_MESSAGETYPE_ERR) {
-        UA_TcpErrorMessage *msg = (UA_TcpErrorMessage*)message;
-        UA_LOG_ERROR(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
-                     "Server replied with an error message: %s %.*s",
-                     UA_StatusCode_name(msg->error), (int)msg->reason.length,
-                     msg->reason.data);
-        retval = msg->error;
-        goto finish;
-    }
-
-    /* Unexpected response type.
-     * TODO: How to process valid OPN responses? */
-    if(messageType != UA_MESSAGETYPE_MSG) {
-        UA_LOG_ERROR(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
-                     "Server replied with the wrong message type");
-        retval = UA_STATUSCODE_BADTCPMESSAGETYPEINVALID;
-        goto finish;
-    }
-
-    /* Decode the data type identifier of the response */
-    retval = UA_NodeId_decodeBinary(message, &offset, &responseId);
+    UA_StatusCode retval = UA_NodeId_decodeBinary(message, &offset, &responseId);
     if(retval != UA_STATUSCODE_GOOD)
         goto finish;
 
@@ -794,33 +200,32 @@ processServiceResponse(SyncResponseDescription *rd, UA_SecureChannel *channel,
     }
 
     /* Got the synchronous response */
-    rd->received= true;
+    rd->received = true;
 
     /* Check that the response type matches */
     expectedNodeId = UA_NODEID_NUMERIC(0, rd->responseType->binaryEncodingId);
-    if(!UA_NodeId_equal(&responseId, &expectedNodeId)) {
+    if(UA_NodeId_equal(&responseId, &expectedNodeId)) {
+        /* Decode the response */
+        retval = UA_decodeBinary(message, &offset, rd->response, rd->responseType,
+                                 rd->client->config.customDataTypesSize,
+                                 rd->client->config.customDataTypes);
+    } else {
+        UA_LOG_ERROR(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
+                     "Reply contains the wrong service response");
         if(UA_NodeId_equal(&responseId, &serviceFaultNodeId)) {
-            /* Take the statuscode from the servicefault */
+            /* Decode only the message header with the servicefault */
             retval = UA_decodeBinary(message, &offset, rd->response,
                                      &UA_TYPES[UA_TYPES_SERVICEFAULT], 0, NULL);
         } else {
-            UA_LOG_ERROR(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
-                         "Reply answers the wrong request. Expected ns=%i,i=%i."
-                         "But retrieved ns=%i,i=%i", expectedNodeId.namespaceIndex,
-                         expectedNodeId.identifier.numeric, responseId.namespaceIndex,
-                         responseId.identifier.numeric);
-            retval = UA_STATUSCODE_BADINTERNALERROR;
+            /* Close the connection */
+            retval = UA_STATUSCODE_BADCOMMUNICATIONERROR;
         }
-        goto finish;
     }
 
-    /* Decode the response */
-    retval = UA_decodeBinary(message, &offset, rd->response, rd->responseType,
-                             rd->client->config.customDataTypesSize,
-                             rd->client->config.customDataTypes);
 
- finish:
+finish:
     UA_NodeId_deleteMembers(&responseId);
+
     if(retval == UA_STATUSCODE_GOOD) {
         UA_LOG_DEBUG(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
                      "Received a response of type %i", responseId.identifier.numeric);
@@ -828,49 +233,53 @@ processServiceResponse(SyncResponseDescription *rd, UA_SecureChannel *channel,
         if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED)
             retval = UA_STATUSCODE_BADRESPONSETOOLARGE;
         UA_LOG_INFO(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
-                    "Error receiving the response");
+                    "Error receiving the response with status code %s",
+                    UA_StatusCode_name(retval));
+        UA_ResponseHeader *respHeader = (UA_ResponseHeader*)rd->response;
         respHeader->serviceResult = retval;
     }
+    return retval;
 }
 
-static void
-processChunk(void *application, UA_Connection *connection,
-             const UA_ByteString *chunk) {
+/* Forward complete chunks directly to the securechannel */
+static UA_StatusCode
+client_processChunk(void *application, UA_Connection *connection, UA_ByteString *chunk) {
     SyncResponseDescription *rd = (SyncResponseDescription*)application;
-    UA_SecureChannel_processChunk(&rd->client->channel, chunk,
-                                  (UA_ProcessMessageCallback*)processServiceResponse, rd);
+    return UA_SecureChannel_processChunk(&rd->client->channel, chunk,
+                                         processServiceResponse,
+                                         rd);
 }
 
+/* Receive and process messages until a synchronous message arrives or the
+ * timout finishes */
 static UA_StatusCode
 receiveServiceResponse(UA_Client *client, void *response, const UA_DataType *responseType,
                        UA_DateTime maxDate, UA_UInt32 *synchronousRequestId) {
     /* Prepare the response and the structure we give into processServiceResponse */
-    SyncResponseDescription rd = {client, false, 0, response, responseType};
+    SyncResponseDescription rd = { client, false, 0, response, responseType };
 
     /* Return upon receiving the synchronized response. All other responses are
      * processed with a callback "in the background". */
     if(synchronousRequestId)
         rd.requestId = *synchronousRequestId;
 
+    UA_StatusCode retval;
     do {
-        /* Retrieve complete chunks */
         UA_DateTime now = UA_DateTime_nowMonotonic();
         if(now > maxDate)
             return UA_STATUSCODE_GOODNONCRITICALTIMEOUT;
         UA_UInt32 timeout = (UA_UInt32)((maxDate - now) / UA_MSEC_TO_DATETIME);
-        UA_StatusCode retval =
-            UA_Connection_receiveChunksBlocking(&client->connection, &rd,
-                                                processChunk, timeout);
+        retval = UA_Connection_receiveChunksBlocking(&client->connection, &rd,
+                                                     client_processChunk, timeout);
         if(retval != UA_STATUSCODE_GOOD) {
-            if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED) {
-                /* set client to error state so that call to connect will force
-                 * a new connection attempt */
-                client->state = UA_CLIENTSTATE_ERRORED;
-            }
-            return retval;
+            if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED)
+                client->state = UA_CLIENTSTATE_DISCONNECTED;
+            else
+                UA_Client_disconnect(client);
+            break;
         }
     } while(!rd.received);
-    return UA_STATUSCODE_GOOD;
+    return retval;
 }
 
 void
@@ -882,13 +291,13 @@ __UA_Client_Service(UA_Client *client, const void *request,
 
     /* Send the request */
     UA_UInt32 requestId;
-    UA_StatusCode retval = sendServiceRequest(client, request, requestType, &requestId);
+    UA_StatusCode retval = sendSymmetricServiceRequest(client, request, requestType, &requestId);
     if(retval != UA_STATUSCODE_GOOD) {
         if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED)
             respHeader->serviceResult = UA_STATUSCODE_BADREQUESTTOOLARGE;
         else
             respHeader->serviceResult = retval;
-        client->state = UA_CLIENTSTATE_FAULTED;
+        UA_Client_disconnect(client);
         return;
     }
 
@@ -915,7 +324,7 @@ __UA_Client_AsyncService(UA_Client *client, const void *request,
     ac->userdata = userdata;
 
     /* Call the service and set the requestId */
-    UA_StatusCode retval = sendServiceRequest(client, request, requestType, &ac->requestId);
+    UA_StatusCode retval = sendSymmetricServiceRequest(client, request, requestType, &ac->requestId);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_free(ac);
         return retval;

+ 535 - 0
src/client/ua_client_connect.c

@@ -0,0 +1,535 @@
+/* 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/. */
+
+#include "ua_client.h"
+#include "ua_client_internal.h"
+#include "ua_transport_generated.h"
+#include "ua_transport_generated_handling.h"
+#include "ua_transport_generated_encoding_binary.h"
+#include "ua_types_encoding_binary.h"
+#include "ua_types_generated_encoding_binary.h"
+
+#define UA_MINMESSAGESIZE 8192
+
+ /***********************/
+ /* Open the Connection */
+ /***********************/
+
+static UA_StatusCode
+processACKResponse(void *application, UA_Connection *connection, UA_ByteString *chunk) {
+    UA_Client *client = (UA_Client*)application;
+
+    /* Decode the message */
+    size_t offset = 0;
+    UA_StatusCode retval;
+    UA_TcpMessageHeader messageHeader;
+    UA_TcpAcknowledgeMessage ackMessage;
+    retval = UA_TcpMessageHeader_decodeBinary(chunk, &offset, &messageHeader);
+    retval |= UA_TcpAcknowledgeMessage_decodeBinary(chunk, &offset, &ackMessage);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_NETWORK,
+                    "Decoding ACK message failed");
+        return retval;
+    }
+
+    /* Store remote connection settings and adjust local configuration to not
+     * exceed the limits */
+    UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_NETWORK, "Received ACK message");
+    connection->remoteConf.maxChunkCount = ackMessage.maxChunkCount; /* may be zero -> unlimited */
+    connection->remoteConf.maxMessageSize = ackMessage.maxMessageSize; /* may be zero -> unlimited */
+    connection->remoteConf.protocolVersion = ackMessage.protocolVersion;
+    connection->remoteConf.sendBufferSize = ackMessage.sendBufferSize;
+    connection->remoteConf.recvBufferSize = ackMessage.receiveBufferSize;
+    if(connection->remoteConf.recvBufferSize < connection->localConf.sendBufferSize)
+        connection->localConf.sendBufferSize = connection->remoteConf.recvBufferSize;
+    if(connection->remoteConf.sendBufferSize < connection->localConf.recvBufferSize)
+        connection->localConf.recvBufferSize = connection->remoteConf.sendBufferSize;
+    connection->state = UA_CONNECTION_ESTABLISHED;
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode
+HelAckHandshake(UA_Client *client) {
+    /* Get a buffer */
+    UA_ByteString message;
+    UA_Connection *conn = &client->connection;
+    UA_StatusCode retval = conn->getSendBuffer(conn, UA_MINMESSAGESIZE, &message);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    /* Prepare the HEL message and encode at offset 8 */
+    UA_TcpHelloMessage hello;
+    UA_String_copy(&client->endpointUrl, &hello.endpointUrl); /* must be less than 4096 bytes */
+    hello.maxChunkCount = conn->localConf.maxChunkCount;
+    hello.maxMessageSize = conn->localConf.maxMessageSize;
+    hello.protocolVersion = conn->localConf.protocolVersion;
+    hello.receiveBufferSize = conn->localConf.recvBufferSize;
+    hello.sendBufferSize = conn->localConf.sendBufferSize;
+
+    UA_Byte *bufPos = &message.data[8]; /* skip the header */
+    const UA_Byte *bufEnd = &message.data[message.length];
+    retval = UA_TcpHelloMessage_encodeBinary(&hello, &bufPos, &bufEnd);
+    UA_TcpHelloMessage_deleteMembers(&hello);
+
+    /* Encode the message header at offset 0 */
+    UA_TcpMessageHeader messageHeader;
+    messageHeader.messageTypeAndChunkType = UA_CHUNKTYPE_FINAL + UA_MESSAGETYPE_HEL;
+    messageHeader.messageSize = (UA_UInt32)((uintptr_t)bufPos - (uintptr_t)message.data);
+    bufPos = message.data;
+    retval |= UA_TcpMessageHeader_encodeBinary(&messageHeader, &bufPos, &bufEnd);
+    if(retval != UA_STATUSCODE_GOOD) {
+        conn->releaseSendBuffer(conn, &message);
+        return retval;
+    }
+
+    /* Send the HEL message */
+    message.length = messageHeader.messageSize;
+    retval = conn->send(conn, &message);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_NETWORK,
+                    "Sending HEL failed");
+        return retval;
+    }
+    UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_NETWORK,
+                 "Sent HEL message");
+
+    /* Loop until we have a complete chunk */
+    retval = UA_Connection_receiveChunksBlocking(conn, client, processACKResponse,
+                                                 client->config.timeout);
+    if(retval != UA_STATUSCODE_GOOD)
+        UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_NETWORK,
+                    "Receiving ACK message failed");
+    return retval;
+}
+
+static UA_StatusCode
+processDecodedOPNResponse(void *application, UA_SecureChannel *channel,
+                          UA_MessageType messageType, UA_UInt32 requestId,
+                          const UA_ByteString *message) {
+    /* Does the request id match? */
+    UA_Client *client = (UA_Client*)application;
+    if(requestId != client->requestId)
+        return UA_STATUSCODE_BADCOMMUNICATIONERROR;
+
+    /* Is the content of the expected type? */
+    size_t offset = 0;
+    UA_NodeId responseId;
+    UA_NodeId expectedId =
+        UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE].binaryEncodingId);
+    UA_StatusCode retval = UA_NodeId_decodeBinary(message, &offset, &responseId);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+    if(!UA_NodeId_equal(&responseId, &expectedId)) {
+        UA_NodeId_deleteMembers(&responseId);
+        return UA_STATUSCODE_BADCOMMUNICATIONERROR;
+    }
+    UA_NodeId_deleteMembers(&responseId);
+
+    /* Decode the response */
+    UA_OpenSecureChannelResponse response;
+    retval = UA_OpenSecureChannelResponse_decodeBinary(message, &offset, &response);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    /* Response.securityToken.revisedLifetime is UInt32 we need to cast it to
+     * DateTime=Int64 we take 75% of lifetime to start renewing as described in
+     * standard */
+    client->nextChannelRenewal = UA_DateTime_nowMonotonic() +
+        (UA_DateTime)(response.securityToken.revisedLifetime * (UA_Double)UA_MSEC_TO_DATETIME * 0.75);
+
+    /* Replace the token and nonce */
+    UA_ChannelSecurityToken_deleteMembers(&client->channel.securityToken);
+    UA_ByteString_deleteMembers(&client->channel.remoteNonce);
+    client->channel.securityToken = response.securityToken;
+    client->channel.remoteNonce = response.serverNonce;
+    UA_ResponseHeader_deleteMembers(&response.responseHeader); /* the other members were moved */
+    if(client->channel.state == UA_SECURECHANNELSTATE_OPEN)
+        UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
+                     "SecureChannel renewed");
+    else
+        UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
+                     "SecureChannel opened");
+    client->channel.state = UA_SECURECHANNELSTATE_OPEN;
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode
+processOPNResponse(void *application, UA_Connection *connection, UA_ByteString *chunk) {
+    UA_Client *client = (UA_Client*)application;
+    return UA_SecureChannel_processChunk(&client->channel, chunk,
+                                         processDecodedOPNResponse,
+                                         client);
+}
+
+/* OPN messges to renew the channel are sent asynchronous */
+static UA_StatusCode
+openSecureChannel(UA_Client *client, UA_Boolean renew) {
+    /* Check if sc is still valid */
+    if(renew && client->nextChannelRenewal - UA_DateTime_nowMonotonic() > 0)
+        return UA_STATUSCODE_GOOD;
+
+    UA_Connection *conn = &client->connection;
+    if(conn->state != UA_CONNECTION_ESTABLISHED)
+        return UA_STATUSCODE_BADSERVERNOTCONNECTED;
+
+    /* Prepare the OpenSecureChannelRequest */
+    UA_OpenSecureChannelRequest opnSecRq;
+    UA_OpenSecureChannelRequest_init(&opnSecRq);
+    opnSecRq.requestHeader.timestamp = UA_DateTime_now();
+    opnSecRq.requestHeader.authenticationToken = client->authenticationToken;
+    if(renew) {
+        opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_RENEW;
+        UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
+                     "Requesting to renew the SecureChannel");
+    } else {
+        opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_ISSUE;
+        UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
+                     "Requesting to open a SecureChannel");
+    }
+    opnSecRq.securityMode = UA_MESSAGESECURITYMODE_NONE;
+    opnSecRq.clientNonce = client->channel.localNonce;
+    opnSecRq.requestedLifetime = client->config.secureChannelLifeTime;
+
+    /* Prepare the entry for the linked list */
+    UA_UInt32 requestId = ++client->requestId;
+    AsyncServiceCall *ac = NULL;
+    if(renew) {
+        ac = (AsyncServiceCall*)UA_malloc(sizeof(AsyncServiceCall));
+        if(!ac)
+            return UA_STATUSCODE_BADOUTOFMEMORY;
+        ac->callback = (UA_ClientAsyncServiceCallback)processDecodedOPNResponse;
+        ac->responseType = &UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE];
+        ac->requestId = requestId;
+        ac->userdata = NULL;
+    }
+
+    /* Send the OPN message */
+    UA_StatusCode retval =
+        UA_SecureChannel_sendAsymmetricOPNMessage(&client->channel, requestId,
+                                                  &opnSecRq, &UA_TYPES[UA_TYPES_OPENSECURECHANNELREQUEST]);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
+                     "Sending OPN message failed with error %s", UA_StatusCode_name(retval));
+        UA_Client_disconnect(client);
+        if(renew)
+            UA_free(ac);
+        return retval;
+    }
+
+    UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "OPN message sent");
+
+    /* Store the entry for async processing and return */
+    if(renew) {
+        LIST_INSERT_HEAD(&client->asyncServiceCalls, ac, pointers);
+        return retval;
+    }
+
+    return UA_Connection_receiveChunksBlocking(&client->connection, client,
+                                               processOPNResponse, client->config.timeout);
+}
+
+static UA_StatusCode
+activateSession(UA_Client *client) {
+    UA_ActivateSessionRequest request;
+    UA_ActivateSessionRequest_init(&request);
+    request.requestHeader.requestHandle = ++client->requestHandle;
+    request.requestHeader.timestamp = UA_DateTime_now();
+    request.requestHeader.timeoutHint = 600000;
+
+    //manual ExtensionObject encoding of the identityToken
+    if(client->authenticationMethod == UA_CLIENTAUTHENTICATION_NONE) {
+        UA_AnonymousIdentityToken* identityToken = UA_AnonymousIdentityToken_new();
+        UA_AnonymousIdentityToken_init(identityToken);
+        UA_String_copy(&client->token.policyId, &identityToken->policyId);
+        request.userIdentityToken.encoding = UA_EXTENSIONOBJECT_DECODED;
+        request.userIdentityToken.content.decoded.type = &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN];
+        request.userIdentityToken.content.decoded.data = identityToken;
+    } else {
+        UA_UserNameIdentityToken* identityToken = UA_UserNameIdentityToken_new();
+        UA_UserNameIdentityToken_init(identityToken);
+        UA_String_copy(&client->token.policyId, &identityToken->policyId);
+        UA_String_copy(&client->username, &identityToken->userName);
+        UA_String_copy(&client->password, &identityToken->password);
+        request.userIdentityToken.encoding = UA_EXTENSIONOBJECT_DECODED;
+        request.userIdentityToken.content.decoded.type = &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN];
+        request.userIdentityToken.content.decoded.data = identityToken;
+    }
+
+    UA_ActivateSessionResponse response;
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE]);
+
+    if(response.responseHeader.serviceResult) {
+        UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                     "ActivateSession failed with error code %s",
+                     UA_StatusCode_name(response.responseHeader.serviceResult));
+    }
+
+    UA_StatusCode retval = response.responseHeader.serviceResult;
+    UA_ActivateSessionRequest_deleteMembers(&request);
+    UA_ActivateSessionResponse_deleteMembers(&response);
+    return retval;
+}
+
+/* Gets a list of endpoints. Memory is allocated for endpointDescription array */
+UA_StatusCode
+UA_Client_getEndpointsInternal(UA_Client *client, size_t* endpointDescriptionsSize,
+                               UA_EndpointDescription** endpointDescriptions) {
+    UA_GetEndpointsRequest request;
+    UA_GetEndpointsRequest_init(&request);
+    request.requestHeader.timestamp = UA_DateTime_now();
+    request.requestHeader.timeoutHint = 10000;
+    // assume the endpointurl outlives the service call
+    request.endpointUrl = client->endpointUrl;
+
+    UA_GetEndpointsResponse response;
+    UA_GetEndpointsResponse_init(&response);
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE]);
+
+    if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
+        UA_StatusCode retval = response.responseHeader.serviceResult;
+        UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                     "GetEndpointRequest failed with error code %s",
+                     UA_StatusCode_name(retval));
+        UA_GetEndpointsResponse_deleteMembers(&response);
+        return retval;
+    }
+    *endpointDescriptions = response.endpoints;
+    *endpointDescriptionsSize = response.endpointsSize;
+    response.endpoints = NULL;
+    response.endpointsSize = 0;
+    UA_GetEndpointsResponse_deleteMembers(&response);
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode
+getEndpoints(UA_Client *client) {
+    UA_EndpointDescription* endpointArray = NULL;
+    size_t endpointArraySize = 0;
+    UA_StatusCode retval =
+        UA_Client_getEndpointsInternal(client, &endpointArraySize, &endpointArray);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    UA_Boolean endpointFound = false;
+    UA_Boolean tokenFound = false;
+    UA_String securityNone = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None");
+    UA_String binaryTransport = UA_STRING("http://opcfoundation.org/UA-Profile/"
+                                          "Transport/uatcp-uasc-uabinary");
+
+    // TODO: compare endpoint information with client->endpointUri
+    for(size_t i = 0; i < endpointArraySize; ++i) {
+        UA_EndpointDescription* endpoint = &endpointArray[i];
+        /* look out for binary transport endpoints */
+        /* Note: Siemens returns empty ProfileUrl, we will accept it as binary */
+        if(endpoint->transportProfileUri.length != 0 &&
+           !UA_String_equal(&endpoint->transportProfileUri, &binaryTransport))
+            continue;
+        /* look out for an endpoint without security */
+        if(!UA_String_equal(&endpoint->securityPolicyUri, &securityNone))
+            continue;
+
+        /* endpoint with no security found */
+        endpointFound = true;
+
+        /* look for a user token policy with an anonymous token */
+        for(size_t j = 0; j < endpoint->userIdentityTokensSize; ++j) {
+            UA_UserTokenPolicy* userToken = &endpoint->userIdentityTokens[j];
+
+            /* Usertokens also have a security policy... */
+            if(userToken->securityPolicyUri.length > 0 &&
+               !UA_String_equal(&userToken->securityPolicyUri, &securityNone))
+                continue;
+
+            /* UA_CLIENTAUTHENTICATION_NONE == UA_USERTOKENTYPE_ANONYMOUS
+             * UA_CLIENTAUTHENTICATION_USERNAME == UA_USERTOKENTYPE_USERNAME
+             * TODO: Check equivalence for other types when adding the support */
+            if((int)client->authenticationMethod != (int)userToken->tokenType)
+                continue;
+
+            /* Endpoint with matching usertokenpolicy found */
+            tokenFound = true;
+            UA_UserTokenPolicy_deleteMembers(&client->token);
+            UA_UserTokenPolicy_copy(userToken, &client->token);
+            break;
+        }
+    }
+
+    UA_Array_delete(endpointArray, endpointArraySize,
+                    &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
+
+    if(!endpointFound) {
+        UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                     "No suitable endpoint found");
+        retval = UA_STATUSCODE_BADINTERNALERROR;
+    } else if(!tokenFound) {
+        UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                     "No suitable UserTokenPolicy found for the possible endpoints");
+        retval = UA_STATUSCODE_BADINTERNALERROR;
+    }
+    return retval;
+}
+
+static UA_StatusCode
+createSession(UA_Client *client) {
+    UA_CreateSessionRequest request;
+    UA_CreateSessionRequest_init(&request);
+
+    request.requestHeader.timestamp = UA_DateTime_now();
+    request.requestHeader.timeoutHint = 10000;
+    UA_ByteString_copy(&client->channel.localNonce, &request.clientNonce);
+    request.requestedSessionTimeout = 1200000;
+    request.maxResponseMessageSize = UA_INT32_MAX;
+    UA_String_copy(&client->endpointUrl, &request.endpointUrl);
+
+    UA_CreateSessionResponse response;
+    UA_CreateSessionResponse_init(&response);
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE]);
+
+    UA_NodeId_copy(&response.authenticationToken, &client->authenticationToken);
+
+    UA_StatusCode retval = response.responseHeader.serviceResult;
+    UA_CreateSessionRequest_deleteMembers(&request);
+    UA_CreateSessionResponse_deleteMembers(&response);
+    return retval;
+}
+
+UA_StatusCode
+UA_Client_connectInternal(UA_Client *client, const char *endpointUrl,
+                          UA_Boolean endpointsHandshake, UA_Boolean createNewSession) {
+    UA_ChannelSecurityToken_init(&client->channel.securityToken);
+    client->channel.state = UA_SECURECHANNELSTATE_FRESH;
+
+    if(client->state >= UA_CLIENTSTATE_CONNECTED)
+        return UA_STATUSCODE_GOOD;
+
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    client->connection =
+        client->config.connectionFunc(client->config.localConnectionConfig,
+                                      endpointUrl, client->config.timeout);
+    if(client->connection.state != UA_CONNECTION_OPENING) {
+        retval = UA_STATUSCODE_BADCONNECTIONCLOSED;
+        goto cleanup;
+    }
+
+    UA_String_deleteMembers(&client->endpointUrl);
+    client->endpointUrl = UA_STRING_ALLOC(endpointUrl);
+    if(!client->endpointUrl.data) {
+        retval = UA_STATUSCODE_BADOUTOFMEMORY;
+        goto cleanup;
+    }
+
+    /* Open a TCP connection */
+    client->connection.localConf = client->config.localConnectionConfig;
+    retval = HelAckHandshake(client);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto cleanup;
+    client->state = UA_CLIENTSTATE_CONNECTED;
+
+    /* Open a SecureChannel. TODO: Select with endpoint  */
+    client->channel.connection = &client->connection;
+    retval = openSecureChannel(client, false);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto cleanup;
+    client->state = UA_CLIENTSTATE_SECURECHANNEL;
+
+    /* Get Endpoints */
+    if(endpointsHandshake) {
+        retval = getEndpoints(client);
+        if(retval != UA_STATUSCODE_GOOD)
+            goto cleanup;
+    }
+
+    /* Open a Session */
+    if(createNewSession) {
+        retval = createSession(client);
+        if(retval != UA_STATUSCODE_GOOD)
+            goto cleanup;
+        retval = activateSession(client);
+        if(retval != UA_STATUSCODE_GOOD)
+            goto cleanup;
+        client->state = UA_CLIENTSTATE_SESSION;
+    }
+    return retval;
+
+cleanup:
+    UA_Client_disconnect(client);
+    return retval;
+}
+
+UA_StatusCode
+UA_Client_connect(UA_Client *client, const char *endpointUrl) {
+    return UA_Client_connectInternal(client, endpointUrl, UA_TRUE, UA_TRUE);
+}
+
+UA_StatusCode
+UA_Client_connect_username(UA_Client *client, const char *endpointUrl,
+                           const char *username, const char *password) {
+    client->authenticationMethod = UA_CLIENTAUTHENTICATION_USERNAME;
+    client->username = UA_STRING_ALLOC(username);
+    client->password = UA_STRING_ALLOC(password);
+    return UA_Client_connect(client, endpointUrl);
+}
+
+UA_StatusCode
+UA_Client_manuallyRenewSecureChannel(UA_Client *client) {
+    UA_StatusCode retval = openSecureChannel(client, true);
+    if(retval != UA_STATUSCODE_GOOD)
+        client->state = UA_CLIENTSTATE_DISCONNECTED;
+    return retval;
+}
+
+/************************/
+/* Close the Connection */
+/************************/
+
+static void
+sendCloseSession(UA_Client *client) {
+    UA_CloseSessionRequest request;
+    UA_CloseSessionRequest_init(&request);
+
+    request.requestHeader.timestamp = UA_DateTime_now();
+    request.requestHeader.timeoutHint = 10000;
+    request.deleteSubscriptions = true;
+    UA_CloseSessionResponse response;
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_CLOSESESSIONRESPONSE]);
+    UA_CloseSessionRequest_deleteMembers(&request);
+    UA_CloseSessionResponse_deleteMembers(&response);
+}
+
+static void
+sendCloseSecureChannel(UA_Client *client) {
+    UA_SecureChannel *channel = &client->channel;
+    UA_CloseSecureChannelRequest request;
+    UA_CloseSecureChannelRequest_init(&request);
+    request.requestHeader.requestHandle = ++client->requestHandle;
+    request.requestHeader.timestamp = UA_DateTime_now();
+    request.requestHeader.timeoutHint = 10000;
+    request.requestHeader.authenticationToken = client->authenticationToken;
+    UA_SecureChannel_sendSymmetricMessage(channel, ++client->requestId,
+                                          UA_MESSAGETYPE_CLO, &request,
+                                          &UA_TYPES[UA_TYPES_CLOSESECURECHANNELREQUEST]);
+    UA_SecureChannel_deleteMembersCleanup(&client->channel);
+}
+
+UA_StatusCode
+UA_Client_disconnect(UA_Client *client) {
+    /* Is a session established? */
+    if(client->state == UA_CLIENTSTATE_SESSION)
+        sendCloseSession(client);
+
+    /* Is a secure channel established? */
+    if(client->state >= UA_CLIENTSTATE_SECURECHANNEL)
+        sendCloseSecureChannel(client);
+
+    /* Close the TCP connection */
+    if(client->state >= UA_CLIENTSTATE_CONNECTED)
+        client->connection.close(&client->connection);
+
+    client->state = UA_CLIENTSTATE_DISCONNECTED;
+    return UA_STATUSCODE_GOOD;
+}

+ 37 - 30
src/client/ua_client_discovery.c

@@ -9,18 +9,23 @@ UA_StatusCode
 UA_Client_getEndpoints(UA_Client *client, const char *serverUrl,
                        size_t* endpointDescriptionsSize,
                        UA_EndpointDescription** endpointDescriptions) {
-    if(client->state == UA_CLIENTSTATE_CONNECTED &&
-        strncmp((const char*)client->endpointUrl.data, serverUrl, client->endpointUrl.length) != 0) {
-        // client is already connected but to a different endpoint url.
+    UA_Boolean connected = (client->state > UA_CLIENTSTATE_DISCONNECTED);
+    /* Client is already connected to a different server */
+    if(connected && strncmp((const char*)client->endpointUrl.data, serverUrl,
+                            client->endpointUrl.length) != 0) {
         return UA_STATUSCODE_BADINVALIDARGUMENT;
     }
 
-    UA_StatusCode retval = __UA_Client_connect(client, serverUrl, UA_FALSE, UA_FALSE);
-    if(retval == UA_STATUSCODE_GOOD)
-        retval = __UA_Client_getEndpoints(client, endpointDescriptionsSize, endpointDescriptions);
+    UA_StatusCode retval;
+    if(!connected) {
+        retval = UA_Client_connectInternal(client, serverUrl, UA_FALSE, UA_FALSE);
+        if(retval != UA_STATUSCODE_GOOD)
+            return retval;
+    }
+    retval = UA_Client_getEndpointsInternal(client, endpointDescriptionsSize, endpointDescriptions);
 
-    UA_Client_disconnect(client);
-    UA_Client_reset(client);
+    if(!connected)
+        UA_Client_disconnect(client);
     return retval;
 }
 
@@ -30,16 +35,17 @@ UA_Client_findServers(UA_Client *client, const char *serverUrl,
                       size_t localeIdsSize, UA_String *localeIds,
                       size_t *registeredServersSize,
                       UA_ApplicationDescription **registeredServers) {
-    /* Client is already connected but to a different endpoint url */
-    if(client->state == UA_CLIENTSTATE_CONNECTED &&
-       strncmp((const char*)client->endpointUrl.data, serverUrl, client->endpointUrl.length) != 0)
+    UA_Boolean connected = (client->state > UA_CLIENTSTATE_DISCONNECTED);
+    /* Client is already connected to a different server */
+    if(connected && strncmp((const char*)client->endpointUrl.data, serverUrl,
+                            client->endpointUrl.length) != 0) {
         return UA_STATUSCODE_BADINVALIDARGUMENT;
+    }
 
-    UA_StatusCode retval = __UA_Client_connect(client, serverUrl, UA_TRUE, UA_FALSE);
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_Client_disconnect(client);
-        UA_Client_reset(client);
-        return retval;
+    if(!connected) {
+        UA_StatusCode retval = UA_Client_connectInternal(client, serverUrl, UA_TRUE, UA_FALSE);
+        if(retval != UA_STATUSCODE_GOOD)
+            return retval;
     }
 
     /* Prepare the request */
@@ -57,7 +63,7 @@ UA_Client_findServers(UA_Client *client, const char *serverUrl,
                         &response, &UA_TYPES[UA_TYPES_FINDSERVERSRESPONSE]);
 
     /* Process the response */
-    retval = response.responseHeader.serviceResult;
+    UA_StatusCode retval = response.responseHeader.serviceResult;
     if(retval == UA_STATUSCODE_GOOD) {
         *registeredServersSize = response.serversSize;
         *registeredServers = response.servers;
@@ -70,8 +76,8 @@ UA_Client_findServers(UA_Client *client, const char *serverUrl,
 
     /* Clean up */
     UA_FindServersResponse_deleteMembers(&response);
-    UA_Client_disconnect(client);
-    UA_Client_reset(client);
+    if(!connected)
+        UA_Client_disconnect(client);
     return retval;
 }
 
@@ -80,16 +86,17 @@ UA_Client_findServersOnNetwork(UA_Client *client, const char *serverUrl,
                                UA_UInt32 startingRecordId, UA_UInt32 maxRecordsToReturn,
                                size_t serverCapabilityFilterSize, UA_String *serverCapabilityFilter,
                                size_t *serverOnNetworkSize, UA_ServerOnNetwork **serverOnNetwork) {
-    /* Client is already connected but to a different endpoint url */
-    if(client->state == UA_CLIENTSTATE_CONNECTED &&
-        strncmp((const char*)client->endpointUrl.data, serverUrl, client->endpointUrl.length) != 0)
+    UA_Boolean connected = (client->state > UA_CLIENTSTATE_DISCONNECTED);
+    /* Client is already connected to a different server */
+    if(connected && strncmp((const char*)client->endpointUrl.data, serverUrl,
+                            client->endpointUrl.length) != 0) {
         return UA_STATUSCODE_BADINVALIDARGUMENT;
+    }
 
-    UA_StatusCode retval = __UA_Client_connect(client, serverUrl, UA_TRUE, UA_FALSE);
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_Client_disconnect(client);
-        UA_Client_reset(client);
-        return retval;
+    if(!connected) {
+        UA_StatusCode retval = UA_Client_connectInternal(client, serverUrl, UA_TRUE, UA_FALSE);
+        if(retval != UA_STATUSCODE_GOOD)
+            return retval;
     }
 
     /* Prepare the request */
@@ -107,7 +114,7 @@ UA_Client_findServersOnNetwork(UA_Client *client, const char *serverUrl,
                         &response, &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKRESPONSE]);
 
     /* Process the response */
-    retval = response.responseHeader.serviceResult;
+    UA_StatusCode retval = response.responseHeader.serviceResult;
     if(retval == UA_STATUSCODE_GOOD) {
         *serverOnNetworkSize = response.serversSize;
         *serverOnNetwork = response.servers;
@@ -120,7 +127,7 @@ UA_Client_findServersOnNetwork(UA_Client *client, const char *serverUrl,
 
     /* Clean up */
     UA_FindServersOnNetworkResponse_deleteMembers(&response);
-    UA_Client_disconnect(client);
-    UA_Client_reset(client);
+    if(!connected)
+        UA_Client_disconnect(client);
     return retval;
 }

+ 3 - 3
src/client/ua_client_highlevel_subscriptions.c

@@ -399,7 +399,7 @@ UA_Client_processPublishResponse(UA_Client *client, UA_PublishRequest *request,
 
 UA_StatusCode
 UA_Client_Subscriptions_manuallySendPublishRequest(UA_Client *client) {
-    if (client->state == UA_CLIENTSTATE_ERRORED)
+    if(client->state < UA_CLIENTSTATE_SESSION)
         return UA_STATUSCODE_BADSERVERNOTCONNECTED;
 
     UA_Boolean moreNotifications = true;
@@ -412,8 +412,8 @@ UA_Client_Subscriptions_manuallySendPublishRequest(UA_Client *client) {
         LIST_FOREACH(ack, &client->pendingNotificationsAcks, listEntry)
             ++request.subscriptionAcknowledgementsSize;
         if(request.subscriptionAcknowledgementsSize > 0) {
-            request.subscriptionAcknowledgements =
-                (UA_SubscriptionAcknowledgement *)UA_malloc(sizeof(UA_SubscriptionAcknowledgement) * request.subscriptionAcknowledgementsSize);
+            request.subscriptionAcknowledgements = (UA_SubscriptionAcknowledgement*)
+                UA_malloc(sizeof(UA_SubscriptionAcknowledgement) * request.subscriptionAcknowledgementsSize);
             if(!request.subscriptionAcknowledgements)
                 return UA_STATUSCODE_BADOUTOFMEMORY;
         }

+ 11 - 19
src/client/ua_client_internal.h

@@ -8,9 +8,9 @@
 #include "ua_securechannel.h"
 #include "queue.h"
 
-/**************************/
-/* Subscriptions Handling */
-/**************************/
+ /**************************/
+ /* Subscriptions Handling */
+ /**************************/
 
 #ifdef UA_ENABLE_SUBSCRIPTIONS
 
@@ -29,9 +29,9 @@ typedef struct UA_Client_MonitoredItem {
     UA_Double samplingInterval;
     UA_UInt32 queueSize;
     UA_Boolean discardOldest;
-    void (*handler)(UA_UInt32 monId, UA_DataValue *value, void *context);
+    void(*handler)(UA_UInt32 monId, UA_DataValue *value, void *context);
     void *handlerContext;
-    void (*handlerEvents)(const UA_UInt32 monId, const size_t nEventFields, const UA_Variant *eventFields, void *context);
+    void(*handlerEvents)(const UA_UInt32 monId, const size_t nEventFields, const UA_Variant *eventFields, void *context);
     void *handlerEventsContext;
 } UA_Client_MonitoredItem;
 
@@ -77,6 +77,7 @@ struct UA_Client {
     UA_String endpointUrl;
 
     /* SecureChannel */
+    UA_SecurityPolicy securityPolicy;
     UA_SecureChannel channel;
     UA_UInt32 requestId;
     UA_DateTime nextChannelRenewal;
@@ -93,7 +94,7 @@ struct UA_Client {
 
     /* Async Service */
     LIST_HEAD(ListOfAsyncServiceCall, AsyncServiceCall) asyncServiceCalls;
-    
+
     /* Subscriptions */
 #ifdef UA_ENABLE_SUBSCRIPTIONS
     UA_UInt32 monitoredItemHandles;
@@ -102,21 +103,12 @@ struct UA_Client {
 #endif
 };
 
-/* Connect to the selected server.
- * This will not create a session.
- *
- * @param client to use
- * @param endpointURL to connect (for example "opc.tcp://localhost:16664")
- * @return Indicates whether the operation succeeded or returns an error code */
-UA_StatusCode UA_EXPORT
-UA_Client_connect_no_session(UA_Client *client, const char *endpointUrl);
-
 UA_StatusCode
-__UA_Client_connect(UA_Client *client, const char *endpointUrl,
-                    UA_Boolean endpointsHandshake, UA_Boolean createSession);
+UA_Client_connectInternal(UA_Client *client, const char *endpointUrl,
+                          UA_Boolean endpointsHandshake, UA_Boolean createNewSession);
 
 UA_StatusCode
-__UA_Client_getEndpoints(UA_Client *client, size_t* endpointDescriptionsSize,
-                         UA_EndpointDescription** endpointDescriptions);
+UA_Client_getEndpointsInternal(UA_Client *client, size_t* endpointDescriptionsSize,
+                               UA_EndpointDescription** endpointDescriptions);
 
 #endif /* UA_CLIENT_INTERNAL_H_ */

+ 101 - 55
src/server/ua_securechannel_manager.c

@@ -5,13 +5,15 @@
 #include "ua_securechannel_manager.h"
 #include "ua_session.h"
 #include "ua_server_internal.h"
+#include "ua_transport_generated_handling.h"
 
 #define STARTCHANNELID 1
 #define STARTTOKENID 1
 
 UA_StatusCode
-UA_SecureChannelManager_init(UA_SecureChannelManager *cm, UA_Server *server) {
+UA_SecureChannelManager_init(UA_SecureChannelManager* cm, UA_Server* server) {
     LIST_INIT(&cm->channels);
+    // TODO: use an ID that is likely to be unique after a restart
     cm->lastChannelId = STARTCHANNELID;
     cm->lastTokenId = STARTTOKENID;
     cm->currentChannelCount = 0;
@@ -19,7 +21,7 @@ UA_SecureChannelManager_init(UA_SecureChannelManager *cm, UA_Server *server) {
     return UA_STATUSCODE_GOOD;
 }
 
-void UA_SecureChannelManager_deleteMembers(UA_SecureChannelManager *cm) {
+void UA_SecureChannelManager_deleteMembers(UA_SecureChannelManager* cm) {
     channel_list_entry *entry, *temp;
     LIST_FOREACH_SAFE(entry, &cm->channels, pointers, temp) {
         LIST_REMOVE(entry, pointers);
@@ -36,7 +38,7 @@ removeSecureChannelCallback(UA_Server *server, void *entry) {
 }
 
 static UA_StatusCode
-removeSecureChannel(UA_SecureChannelManager *cm, channel_list_entry *entry){
+removeSecureChannel(UA_SecureChannelManager *cm, channel_list_entry *entry) {
     /* Add a delayed callback to remove the channel when the currently
      * scheduled jobs have completed */
     UA_StatusCode retval = UA_Server_delayedCallback(cm->server, removeSecureChannelCallback, entry);
@@ -64,17 +66,18 @@ UA_SecureChannelManager_cleanupTimedOut(UA_SecureChannelManager *cm, UA_DateTime
             UA_LOG_INFO_CHANNEL(cm->server->config.logger, &entry->channel,
                                 "SecureChannel has timed out");
             removeSecureChannel(cm, entry);
-        } else if(entry->channel.nextSecurityToken.tokenId > 0) {
+        }
+        else if(entry->channel.nextSecurityToken.tokenId > 0) {
             UA_SecureChannel_revolveTokens(&entry->channel);
         }
     }
 }
 
 /* remove the first channel that has no session attached */
-static UA_Boolean purgeFirstChannelWithoutSession(UA_SecureChannelManager *cm) {
-    channel_list_entry *entry;
+static UA_Boolean purgeFirstChannelWithoutSession(UA_SecureChannelManager* cm) {
+    channel_list_entry* entry;
     LIST_FOREACH(entry, &cm->channels, pointers) {
-        if(LIST_EMPTY(&(entry->channel.sessions))){
+        if(LIST_EMPTY(&(entry->channel.sessions))) {
             UA_LOG_DEBUG_CHANNEL(cm->server->config.logger, &entry->channel,
                                  "Channel was purged since maxSecureChannels was "
                                  "reached and channel had no session attached");
@@ -87,60 +90,97 @@ static UA_Boolean purgeFirstChannelWithoutSession(UA_SecureChannelManager *cm) {
 }
 
 UA_StatusCode
-UA_SecureChannelManager_open(UA_SecureChannelManager *cm, UA_Connection *conn,
-                             const UA_OpenSecureChannelRequest *request,
-                             UA_OpenSecureChannelResponse *response) {
-    if(request->securityMode != UA_MESSAGESECURITYMODE_NONE)
-        return UA_STATUSCODE_BADSECURITYMODEREJECTED;
+UA_SecureChannelManager_create(UA_SecureChannelManager *const cm, UA_Connection *const connection,
+                               const UA_SecurityPolicy *const securityPolicy,
+                               const UA_AsymmetricAlgorithmSecurityHeader *const asymHeader) {
+    /* connection already has a channel attached. */
+    if(connection->channel != NULL)
+        return UA_STATUSCODE_BADINTERNALERROR;
 
-    //check if there exists a free SC, otherwise try to purge one SC without a session
-    //the purge has been introduced to pass CTT, it is not clear what strategy is expected here
-    if(cm->currentChannelCount >= cm->server->config.maxSecureChannels && !purgeFirstChannelWithoutSession(cm)){
+    /* Check if there exists a free SC, otherwise try to purge one SC without a
+     * session the purge has been introduced to pass CTT, it is not clear what
+     * strategy is expected here */
+    if(cm->currentChannelCount >= cm->server->config.maxSecureChannels &&
+       !purgeFirstChannelWithoutSession(cm))
         return UA_STATUSCODE_BADOUTOFMEMORY;
-    }
 
-    /* Set up the channel */
-    channel_list_entry *entry = (channel_list_entry*)UA_malloc(sizeof(channel_list_entry));
+    UA_LOG_INFO(cm->server->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
+                "Creating a new SecureChannel");
+
+    channel_list_entry* entry = (channel_list_entry*)UA_malloc(sizeof(channel_list_entry));
     if(!entry)
         return UA_STATUSCODE_BADOUTOFMEMORY;
-    UA_SecureChannel_init(&entry->channel);
-    entry->channel.securityToken.channelId = cm->lastChannelId++;
+
+    /* Create the channel context and parse the sender (remote) certificate used for the
+     * secureChannel. */
+    UA_StatusCode retval = UA_SecureChannel_init(&entry->channel, securityPolicy,
+                                                 &asymHeader->senderCertificate);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_free(entry);
+        return retval;
+    }
+
+    /* Channel state is fresh (0) */
+    entry->channel.securityToken.channelId = 0;
     entry->channel.securityToken.tokenId = cm->lastTokenId++;
     entry->channel.securityToken.createdAt = UA_DateTime_now();
-    entry->channel.securityToken.revisedLifetime =
-        (request->requestedLifetime > cm->server->config.maxSecurityTokenLifetime) ?
-        cm->server->config.maxSecurityTokenLifetime : request->requestedLifetime;
-    if(entry->channel.securityToken.revisedLifetime == 0) /* lifetime 0 -> set the maximum possible */
-        entry->channel.securityToken.revisedLifetime = cm->server->config.maxSecurityTokenLifetime;
-    UA_ByteString_copy(&request->clientNonce, &entry->channel.clientNonce);
-    entry->channel.serverAsymAlgSettings.securityPolicyUri =
-        UA_STRING_ALLOC("http://opcfoundation.org/UA/SecurityPolicy#None");
-    UA_SecureChannel_generateNonce(&entry->channel.serverNonce);
-
-    /* Set the response */
-    UA_ByteString_copy(&entry->channel.serverNonce, &response->serverNonce);
-    UA_ChannelSecurityToken_copy(&entry->channel.securityToken, &response->securityToken);
-    response->responseHeader.timestamp = UA_DateTime_now();
-
-    /* Now overwrite the creation date with the internal monotonic clock */
-    entry->channel.securityToken.createdAt = UA_DateTime_nowMonotonic();
+    entry->channel.securityToken.revisedLifetime = cm->server->config.maxSecurityTokenLifetime;
 
-    /* Set all the pointers internally */
-    UA_Connection_attachSecureChannel(conn, &entry->channel);
     LIST_INSERT_HEAD(&cm->channels, entry, pointers);
     UA_atomic_add(&cm->currentChannelCount, 1);
+    UA_Connection_attachSecureChannel(connection, &entry->channel);
     return UA_STATUSCODE_GOOD;
 }
 
 UA_StatusCode
-UA_SecureChannelManager_renew(UA_SecureChannelManager *cm, UA_Connection *conn,
-                              const UA_OpenSecureChannelRequest *request,
-                              UA_OpenSecureChannelResponse *response) {
-    UA_SecureChannel *channel = conn->channel;
-    if(!channel)
+UA_SecureChannelManager_open(UA_SecureChannelManager* cm, UA_SecureChannel *channel,
+                             const UA_OpenSecureChannelRequest* request,
+                             UA_OpenSecureChannelResponse* response) {
+    if(channel->state != UA_SECURECHANNELSTATE_FRESH) {
+        UA_LOG_ERROR_CHANNEL(cm->server->config.logger, channel,
+                             "Called open on already open or closed channel");
         return UA_STATUSCODE_BADINTERNALERROR;
+    }
+
+    if(request->securityMode != UA_MESSAGESECURITYMODE_NONE &&
+       UA_ByteString_equal(&channel->securityPolicy->policyUri, &UA_SECURITY_POLICY_NONE_URI)) {
+        return UA_STATUSCODE_BADSECURITYMODEREJECTED;
+    }
+
+    channel->securityToken.channelId = cm->lastChannelId++;
+    channel->securityToken.createdAt = UA_DateTime_now();
+    channel->securityToken.revisedLifetime =
+        (request->requestedLifetime > cm->server->config.maxSecurityTokenLifetime) ?
+        cm->server->config.maxSecurityTokenLifetime : request->requestedLifetime;
+    if(channel->securityToken.revisedLifetime == 0) // lifetime 0 -> set the maximum possible
+        channel->securityToken.revisedLifetime = cm->server->config.maxSecurityTokenLifetime;
+    UA_ByteString_copy(&request->clientNonce, &channel->remoteNonce);
+    channel->securityMode = request->securityMode;
+    const size_t keyLength = channel->securityPolicy->symmetricModule.cryptoModule.
+        getLocalEncryptionKeyLength(channel->securityPolicy, channel->channelContext);
+    UA_SecureChannel_generateNonce(channel,
+                                   keyLength,
+                                   &channel->localNonce);
+
+    UA_SecureChannel_generateNewKeys(channel);
+
+    // Set the response
+    UA_ByteString_copy(&channel->localNonce, &response->serverNonce);
+    UA_ChannelSecurityToken_copy(&channel->securityToken, &response->securityToken);
+    response->responseHeader.timestamp = UA_DateTime_now();
+
+    // Now overwrite the creation date with the internal monotonic clock
+    channel->securityToken.createdAt = UA_DateTime_nowMonotonic();
 
-    /* if no security token is already issued */
+    channel->state = UA_SECURECHANNELSTATE_OPEN;
+    return UA_STATUSCODE_GOOD;
+}
+
+UA_StatusCode
+UA_SecureChannelManager_renew(UA_SecureChannelManager* cm, UA_SecureChannel *channel,
+                              const UA_OpenSecureChannelRequest* request,
+                              UA_OpenSecureChannelResponse* response) {
+    /* If no security token is already issued */
     if(channel->nextSecurityToken.tokenId == 0) {
         channel->nextSecurityToken.channelId = channel->securityToken.channelId;
         channel->nextSecurityToken.tokenId = cm->lastTokenId++;
@@ -153,23 +193,29 @@ UA_SecureChannelManager_renew(UA_SecureChannelManager *cm, UA_Connection *conn,
     }
 
     /* invalidate the old nonce */
-    if(channel->clientNonce.data)
-        UA_ByteString_deleteMembers(&channel->clientNonce);
+    if(channel->remoteNonce.data)
+        UA_ByteString_deleteMembers(&channel->remoteNonce);
+    if(channel->localNonce.data)
+        UA_ByteString_deleteMembers(&channel->localNonce);
 
     /* set the response */
-    UA_ByteString_copy(&request->clientNonce, &channel->clientNonce);
-    UA_ByteString_copy(&channel->serverNonce, &response->serverNonce);
+    UA_ByteString_copy(&request->clientNonce, &channel->remoteNonce);
+    const size_t keyLength = channel->securityPolicy->symmetricModule.cryptoModule.
+        getLocalEncryptionKeyLength(channel->securityPolicy, channel->channelContext);
+    UA_SecureChannel_generateNonce(channel,
+                                   keyLength,
+                                   &channel->localNonce);
+    UA_ByteString_copy(&channel->localNonce, &response->serverNonce);
     UA_ChannelSecurityToken_copy(&channel->nextSecurityToken, &response->securityToken);
 
     /* reset the creation date to the monotonic clock */
     channel->nextSecurityToken.createdAt = UA_DateTime_nowMonotonic();
-
     return UA_STATUSCODE_GOOD;
 }
 
-UA_SecureChannel *
-UA_SecureChannelManager_get(UA_SecureChannelManager *cm, UA_UInt32 channelId) {
-    channel_list_entry *entry;
+UA_SecureChannel*
+UA_SecureChannelManager_get(UA_SecureChannelManager* cm, UA_UInt32 channelId) {
+    channel_list_entry* entry;
     LIST_FOREACH(entry, &cm->channels, pointers) {
         if(entry->channel.securityToken.channelId == channelId)
             return &entry->channel;
@@ -178,8 +224,8 @@ UA_SecureChannelManager_get(UA_SecureChannelManager *cm, UA_UInt32 channelId) {
 }
 
 UA_StatusCode
-UA_SecureChannelManager_close(UA_SecureChannelManager *cm, UA_UInt32 channelId) {
-    channel_list_entry *entry;
+UA_SecureChannelManager_close(UA_SecureChannelManager* cm, UA_UInt32 channelId) {
+    channel_list_entry* entry;
     LIST_FOREACH(entry, &cm->channels, pointers) {
         if(entry->channel.securityToken.channelId == channelId)
             break;

+ 7 - 2
src/server/ua_securechannel_manager.h

@@ -41,12 +41,17 @@ UA_SecureChannelManager_cleanupTimedOut(UA_SecureChannelManager *cm,
                                         UA_DateTime nowMonotonic);
 
 UA_StatusCode
-UA_SecureChannelManager_open(UA_SecureChannelManager *cm, UA_Connection *conn,
+UA_SecureChannelManager_create(UA_SecureChannelManager *const cm, UA_Connection *const connection,
+                               const UA_SecurityPolicy *const securityPolicy,
+                               const UA_AsymmetricAlgorithmSecurityHeader *const asymHeader);
+
+UA_StatusCode
+UA_SecureChannelManager_open(UA_SecureChannelManager *cm, UA_SecureChannel *channel,
                              const UA_OpenSecureChannelRequest *request,
                              UA_OpenSecureChannelResponse *response);
 
 UA_StatusCode
-UA_SecureChannelManager_renew(UA_SecureChannelManager *cm, UA_Connection *conn,
+UA_SecureChannelManager_renew(UA_SecureChannelManager *cm, UA_SecureChannel *channel,
                               const UA_OpenSecureChannelRequest *request,
                               UA_OpenSecureChannelResponse *response);
 

+ 209 - 286
src/server/ua_server_binary.c

@@ -11,28 +11,34 @@
 #include "ua_transport_generated.h"
 #include "ua_transport_generated_handling.h"
 #include "ua_transport_generated_encoding_binary.h"
+#include "ua_types_generated_handling.h"
+#include "ua_securitypolicy_none.h"
 
 /********************/
 /* Helper Functions */
 /********************/
 
-static void
-sendError(UA_SecureChannel *channel, const UA_ByteString *msg,
-          size_t offset, const UA_DataType *responseType,
-          UA_UInt32 requestId, UA_StatusCode error) {
+ /* This is not an ERR message, the connection is not closed afterwards */
+static UA_StatusCode
+sendServiceFault(UA_SecureChannel *channel, const UA_ByteString *msg,
+                 size_t offset, const UA_DataType *responseType,
+                 UA_UInt32 requestId, UA_StatusCode error) {
     UA_RequestHeader requestHeader;
     UA_StatusCode retval = UA_RequestHeader_decodeBinary(msg, &offset, &requestHeader);
     if(retval != UA_STATUSCODE_GOOD)
-        return;
+        return retval;
     void *response = UA_alloca(responseType->memSize);
     UA_init(response, responseType);
     UA_ResponseHeader *responseHeader = (UA_ResponseHeader*)response;
     responseHeader->requestHandle = requestHeader.requestHandle;
     responseHeader->timestamp = UA_DateTime_now();
     responseHeader->serviceResult = error;
-    UA_SecureChannel_sendBinaryMessage(channel, requestId, response, responseType);
+
+    // Send error message. Message type is MSG and not ERR, since we are on a securechanenl!
+    retval = UA_SecureChannel_sendSymmetricMessage(channel, requestId, UA_MESSAGETYPE_MSG,
+                                                   response, responseType);
     UA_RequestHeader_deleteMembers(&requestHeader);
-    UA_ResponseHeader_deleteMembers(responseHeader);
+    return retval;
 }
 
 static void
@@ -219,16 +225,13 @@ getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
 /*************************/
 
 /* HEL -> Open up the connection */
-static void
-processHEL(UA_Server *server, UA_Connection *connection, const UA_ByteString *msg, size_t *offset) {
+static UA_StatusCode
+processHEL(UA_Server *server, UA_Connection *connection,
+           const UA_ByteString *msg, size_t *offset) {
     UA_TcpHelloMessage helloMessage;
-    if(UA_TcpHelloMessage_decodeBinary(msg, offset, &helloMessage) != UA_STATUSCODE_GOOD) {
-        UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                    "Connection %i | Could not decode the HEL message. Closing the connection.",
-                    connection->sockfd);
-        connection->close(connection);
-        return;
-    }
+    UA_StatusCode retval = UA_TcpHelloMessage_decodeBinary(msg, offset, &helloMessage);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
 
     /* Parameterize the connection */
     connection->remoteConf.maxChunkCount = helloMessage.maxChunkCount; /* zero -> unlimited */
@@ -240,17 +243,18 @@ processHEL(UA_Server *server, UA_Connection *connection, const UA_ByteString *ms
     connection->remoteConf.sendBufferSize = helloMessage.sendBufferSize;
     if(connection->localConf.recvBufferSize > helloMessage.sendBufferSize)
         connection->localConf.recvBufferSize = helloMessage.sendBufferSize;
-    connection->state = UA_CONNECTION_ESTABLISHED;
-    UA_TcpHelloMessage_deleteMembers(&helloMessage);
+    UA_String_deleteMembers(&helloMessage.endpointUrl);
 
-    if (connection->remoteConf.recvBufferSize == 0) {
+    if(connection->remoteConf.recvBufferSize == 0) {
         UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                    "Connection %i | Remote end indicated a receive buffer size of 0. Not able to send any messages.",
+                    "Connection %i | Remote end indicated a receive buffer size of 0. "
+                    "Not able to send any messages.",
                     connection->sockfd);
-        connection->close(connection);
-        return;
+        return UA_STATUSCODE_BADINTERNALERROR;
     }
 
+    connection->state = UA_CONNECTION_ESTABLISHED;
+
     /* Build acknowledge response */
     UA_TcpAcknowledgeMessage ackMessage;
     ackMessage.protocolVersion = connection->localConf.protocolVersion;
@@ -258,7 +262,6 @@ processHEL(UA_Server *server, UA_Connection *connection, const UA_ByteString *ms
     ackMessage.sendBufferSize = connection->localConf.sendBufferSize;
     ackMessage.maxMessageSize = connection->localConf.maxMessageSize;
     ackMessage.maxChunkCount = connection->localConf.maxChunkCount;
-
     UA_TcpMessageHeader ackHeader;
     ackHeader.messageTypeAndChunkType = UA_MESSAGETYPE_ACK + UA_CHUNKTYPE_FINAL;
     ackHeader.messageSize = 8 + 20; /* ackHeader + ackMessage */
@@ -266,10 +269,10 @@ processHEL(UA_Server *server, UA_Connection *connection, const UA_ByteString *ms
     /* Get the send buffer from the network layer */
     UA_ByteString ack_msg;
     UA_ByteString_init(&ack_msg);
-    UA_StatusCode retval =
-        connection->getSendBuffer(connection, connection->localConf.sendBufferSize, &ack_msg);
+    retval = connection->getSendBuffer(connection, connection->localConf.sendBufferSize,
+                                       &ack_msg);
     if(retval != UA_STATUSCODE_GOOD)
-        return;
+        return retval;
 
     /* Encode and send the response */
     UA_Byte *bufPos = ack_msg.data;
@@ -277,147 +280,75 @@ processHEL(UA_Server *server, UA_Connection *connection, const UA_ByteString *ms
     UA_TcpMessageHeader_encodeBinary(&ackHeader, &bufPos, &bufEnd);
     UA_TcpAcknowledgeMessage_encodeBinary(&ackMessage, &bufPos, &bufEnd);
     ack_msg.length = ackHeader.messageSize;
-    connection->send(connection, &ack_msg);
+    return connection->send(connection, &ack_msg);
 }
 
 /* OPN -> Open up/renew the securechannel */
-static void
-processOPN(UA_Server *server, UA_Connection *connection,
-           UA_UInt32 channelId, const UA_ByteString *msg) {
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    /* Called before HEL */
-    if(connection->state != UA_CONNECTION_ESTABLISHED)
-        retval = UA_STATUSCODE_BADCOMMUNICATIONERROR;
-    /* Opening up a channel with a channelid already set */
-    if(!connection->channel && channelId != 0)
-        retval = UA_STATUSCODE_BADCOMMUNICATIONERROR;
-    /* Renew a channel with the wrong channelid */
-    if(connection->channel && channelId != connection->channel->securityToken.channelId)
-        retval = UA_STATUSCODE_BADCOMMUNICATIONERROR;
-
+static UA_StatusCode
+processOPN(UA_Server *server, UA_SecureChannel *channel,
+           const UA_UInt32 requestId, const UA_ByteString *msg) {
     /* Decode the request */
-    UA_AsymmetricAlgorithmSecurityHeader asymHeader;
-    UA_SequenceHeader seqHeader;
-    UA_NodeId requestType;
-    UA_OpenSecureChannelRequest r;
     size_t offset = 0;
-    retval |= UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(msg, &offset, &asymHeader);
-    retval |= UA_SequenceHeader_decodeBinary(msg, &offset, &seqHeader);
+    UA_NodeId requestType;
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    UA_OpenSecureChannelRequest openSecureChannelRequest;
     retval |= UA_NodeId_decodeBinary(msg, &offset, &requestType);
-    retval |= UA_OpenSecureChannelRequest_decodeBinary(msg, &offset, &r);
+    retval |= UA_OpenSecureChannelRequest_decodeBinary(msg, &offset, &openSecureChannelRequest);
 
     /* Error occured */
-    if(retval != UA_STATUSCODE_GOOD || requestType.identifier.numeric != 446) {
-        UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
+    if(retval != UA_STATUSCODE_GOOD ||
+       requestType.identifier.numeric != UA_TYPES[UA_TYPES_OPENSECURECHANNELREQUEST].binaryEncodingId) {
         UA_NodeId_deleteMembers(&requestType);
-        UA_OpenSecureChannelRequest_deleteMembers(&r);
-        UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                    "Connection %i | Could not decode the OPN message. Closing the connection.",
-                    connection->sockfd);
-        connection->close(connection);
-        return;
+        UA_OpenSecureChannelRequest_deleteMembers(&openSecureChannelRequest);
+        UA_LOG_INFO_CHANNEL(server->config.logger, channel,
+                            "Could not decode the OPN message. Closing the connection.");
+        UA_SecureChannelManager_close(&server->secureChannelManager, channel->securityToken.channelId);
+        return retval;
     }
 
     /* Call the service */
-    UA_OpenSecureChannelResponse p;
-    UA_OpenSecureChannelResponse_init(&p);
-    Service_OpenSecureChannel(server, connection, &r, &p);
-    UA_OpenSecureChannelRequest_deleteMembers(&r);
-
-    /* Opening the channel failed */
-    UA_SecureChannel *channel = connection->channel;
-    if(!channel) {
-        UA_OpenSecureChannelResponse_deleteMembers(&p);
-        UA_NodeId_deleteMembers(&requestType);
-        UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
-        UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                    "Connection %i | Could not open a SecureChannel. "
-                    "Closing the connection.", connection->sockfd);
-        connection->close(connection);
-        return;
-    }
-
-    // every message contains secureconversationmessageheader. Thus its size has to be
-    // the minimum buffer size
-    if (connection->localConf.sendBufferSize <= 12) {
-        UA_OpenSecureChannelResponse_deleteMembers(&p);
-        UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
-        UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                    "Connection %i | Response too large for client configuration. %s", connection->sockfd, UA_StatusCode_name(UA_STATUSCODE_BADRESPONSETOOLARGE));
-        connection->close(connection);
-        return;
-    }
-
-    /* Set the starting sequence number */
-    channel->receiveSequenceNumber = seqHeader.sequenceNumber;
-
-    /* Allocate the return message */
-    UA_ByteString resp_msg;
-    UA_ByteString_init(&resp_msg);
-    retval = connection->getSendBuffer(connection, connection->localConf.sendBufferSize, &resp_msg);
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_OpenSecureChannelResponse_deleteMembers(&p);
-        UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
-        UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                    "Connection %i | Could not obtain a buffer to answer the OPN message. "
-                    "Closing the connection.", connection->sockfd);
-        connection->close(connection);
-        return;
+    UA_OpenSecureChannelResponse openScResponse;
+    UA_OpenSecureChannelResponse_init(&openScResponse);
+    Service_OpenSecureChannel(server, channel, &openSecureChannelRequest, &openScResponse);
+    UA_OpenSecureChannelRequest_deleteMembers(&openSecureChannelRequest);
+    if(openScResponse.responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
+        UA_LOG_INFO_CHANNEL(server->config.logger, channel, "Could not open a SecureChannel. "
+                            "Closing the connection.");
+        UA_SecureChannelManager_close(&server->secureChannelManager,
+                                      channel->securityToken.channelId);
+        return openScResponse.responseHeader.serviceResult;
     }
 
-    /* Encode the message after the secureconversationmessageheader */
-    UA_Byte *bufPos = &resp_msg.data[12]; /* skip the header */
-    const UA_Byte *bufEnd = &resp_msg.data[resp_msg.length];
-    seqHeader.sequenceNumber = UA_atomic_add(&channel->sendSequenceNumber, 1);
-    retval |= UA_AsymmetricAlgorithmSecurityHeader_encodeBinary(&asymHeader, &bufPos, &bufEnd); // just mirror back
-    retval |= UA_SequenceHeader_encodeBinary(&seqHeader, &bufPos, &bufEnd);
-    UA_NodeId responseType = UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE].binaryEncodingId);
-    retval |= UA_NodeId_encodeBinary(&responseType, &bufPos, &bufEnd);
-    retval |= UA_OpenSecureChannelResponse_encodeBinary(&p, &bufPos, &bufEnd);
-
+    /* Send the response */
+    retval = UA_SecureChannel_sendAsymmetricOPNMessage(channel, requestId, &openScResponse,
+                                                       &UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE]);
+    UA_OpenSecureChannelResponse_deleteMembers(&openScResponse);
     if(retval != UA_STATUSCODE_GOOD) {
-        UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                    "Connection %i | Could not encode the OPN message. "
-                    "Closing the connection. %s", connection->sockfd, UA_StatusCode_name(retval));
-        connection->releaseSendBuffer(connection, &resp_msg);
-        UA_OpenSecureChannelResponse_deleteMembers(&p);
-        UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
-        connection->close(connection);
-        return;
+        UA_LOG_INFO_CHANNEL(server->config.logger, channel,
+                            "Could not send the OPN answer with error code %s",
+                            UA_StatusCode_name(retval));
+        UA_SecureChannelManager_close(&server->secureChannelManager,
+                                      channel->securityToken.channelId);
     }
-
-    /* Encode the secureconversationmessageheader (cannot fail) and send */
-    UA_SecureConversationMessageHeader respHeader;
-    respHeader.messageHeader.messageTypeAndChunkType = UA_MESSAGETYPE_OPN + UA_CHUNKTYPE_FINAL;
-    respHeader.messageHeader.messageSize = (u32)((uintptr_t)bufPos - (uintptr_t)resp_msg.data);
-    respHeader.secureChannelId = p.securityToken.channelId;
-    bufPos = resp_msg.data;
-    UA_SecureConversationMessageHeader_encodeBinary(&respHeader, &bufPos, &bufEnd);
-    resp_msg.length = respHeader.messageHeader.messageSize;
-    connection->send(connection, &resp_msg);
-
-    /* Clean up */
-    UA_OpenSecureChannelResponse_deleteMembers(&p);
-    UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
+    return retval;
 }
 
-static void
+static UA_StatusCode
 processMSG(UA_Server *server, UA_SecureChannel *channel,
            UA_UInt32 requestId, const UA_ByteString *msg) {
     /* At 0, the nodeid starts... */
-    size_t ppos = 0;
-    size_t *offset = &ppos;
+    size_t offset = 0;
 
     /* Decode the nodeid */
     UA_NodeId requestTypeId;
-    UA_StatusCode retval = UA_NodeId_decodeBinary(msg, offset, &requestTypeId);
+    UA_StatusCode retval = UA_NodeId_decodeBinary(msg, &offset, &requestTypeId);
     if(retval != UA_STATUSCODE_GOOD)
-        return;
+        return retval;
     if(requestTypeId.identifierType != UA_NODEIDTYPE_NUMERIC)
         UA_NodeId_deleteMembers(&requestTypeId); /* leads to badserviceunsupported */
 
     /* Store the start-position of the request */
-    size_t requestPos = *offset;
+    size_t requestPos = offset;
 
     /* Get the service pointers */
     UA_Service service = NULL;
@@ -436,23 +367,21 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
                                 "Unknown request with type identifier %i",
                                 requestTypeId.identifier.numeric);
         }
-        sendError(channel, msg, requestPos, &UA_TYPES[UA_TYPES_SERVICEFAULT],
-                  requestId, UA_STATUSCODE_BADSERVICEUNSUPPORTED);
-        return;
+        return sendServiceFault(channel, msg, requestPos, &UA_TYPES[UA_TYPES_SERVICEFAULT],
+                                requestId, UA_STATUSCODE_BADSERVICEUNSUPPORTED);
     }
     UA_assert(responseType);
 
     /* Decode the request */
     void *request = UA_alloca(requestType->memSize);
     UA_RequestHeader *requestHeader = (UA_RequestHeader*)request;
-    retval = UA_decodeBinary(msg, offset, request, requestType,
+    retval = UA_decodeBinary(msg, &offset, request, requestType,
                              server->config.customDataTypesSize,
                              server->config.customDataTypes);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_DEBUG_CHANNEL(server->config.logger, channel,
                              "Could not decode the request");
-        sendError(channel, msg, requestPos, responseType, requestId, retval);
-        return;
+        return sendServiceFault(channel, msg, requestPos, responseType, requestId, retval);
     }
 
     /* Prepare the respone */
@@ -463,7 +392,7 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
     /* CreateSession doesn't need a session */
     if(requestType == &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST]) {
         Service_CreateSession(server, channel,
-                              (const UA_CreateSessionRequest *)request,
+            (const UA_CreateSessionRequest *)request,
                               (UA_CreateSessionResponse *)response);
         goto send_response;
     }
@@ -479,13 +408,12 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
             UA_LOG_DEBUG_CHANNEL(server->config.logger, channel,
                                  "Trying to activate a session that is " \
                                  "not known in the server");
-            sendError(channel, msg, requestPos, responseType,
-                      requestId, UA_STATUSCODE_BADSESSIONIDINVALID);
             UA_deleteMembers(request, requestType);
-            return;
+            return sendServiceFault(channel, msg, requestPos, responseType,
+                                    requestId, UA_STATUSCODE_BADSESSIONIDINVALID);
         }
         Service_ActivateSession(server, channel, session,
-                                (const UA_ActivateSessionRequest*)request,
+            (const UA_ActivateSessionRequest*)request,
                                 (UA_ActivateSessionResponse*)response);
         goto send_response;
     }
@@ -497,10 +425,9 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
             UA_LOG_INFO_CHANNEL(server->config.logger, channel,
                                 "Service request %i without a valid session",
                                 requestType->binaryEncodingId);
-            sendError(channel, msg, requestPos, responseType,
-                      requestId, UA_STATUSCODE_BADSESSIONIDINVALID);
             UA_deleteMembers(request, requestType);
-            return;
+            return sendServiceFault(channel, msg, requestPos, responseType,
+                                    requestId, UA_STATUSCODE_BADSESSIONIDINVALID);
         }
         UA_Session_init(&anonymousSession);
         anonymousSession.sessionId = UA_NODEID_GUID(0, UA_GUID_NULL);
@@ -513,22 +440,20 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
         UA_LOG_INFO_SESSION(server->config.logger, session,
                             "Calling service %i on a non-activated session",
                             requestType->binaryEncodingId);
-        sendError(channel, msg, requestPos, responseType,
-                  requestId, UA_STATUSCODE_BADSESSIONNOTACTIVATED);
         UA_SessionManager_removeSession(&server->sessionManager,
                                         &session->authenticationToken);
         UA_deleteMembers(request, requestType);
-        return;
+        return sendServiceFault(channel, msg, requestPos, responseType,
+                                requestId, UA_STATUSCODE_BADSESSIONNOTACTIVATED);
     }
 
     /* The session is bound to another channel */
     if(session->channel != channel) {
         UA_LOG_DEBUG_CHANNEL(server->config.logger, channel,
                              "Client tries to use an obsolete securechannel");
-        sendError(channel, msg, requestPos, responseType,
-                  requestId, UA_STATUSCODE_BADSECURECHANNELIDINVALID);
         UA_deleteMembers(request, requestType);
-        return;
+        return sendServiceFault(channel, msg, requestPos, responseType,
+                                requestId, UA_STATUSCODE_BADSECURECHANNELIDINVALID);
     }
 
     /* Update the session lifetime */
@@ -538,9 +463,9 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
     /* The publish request is not answered immediately */
     if(requestType == &UA_TYPES[UA_TYPES_PUBLISHREQUEST]) {
         Service_Publish(server, session,
-                        (const UA_PublishRequest*)request, requestId);
+            (const UA_PublishRequest*)request, requestId);
         UA_deleteMembers(request, requestType);
-        return;
+        return UA_STATUSCODE_GOOD;
     }
 #endif
 
@@ -548,11 +473,12 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
     UA_assert(service); /* For all services besides publish, the service pointer is non-NULL*/
     service(server, session, request, response);
 
- send_response:
+send_response:
     /* Send the response */
     ((UA_ResponseHeader*)response)->requestHandle = requestHeader->requestHandle;
     ((UA_ResponseHeader*)response)->timestamp = UA_DateTime_now();
-    retval = UA_SecureChannel_sendBinaryMessage(channel, requestId, response, responseType);
+    retval = UA_SecureChannel_sendSymmetricMessage(channel, requestId, UA_MESSAGETYPE_MSG,
+                                                   response, responseType);
 
     if(retval != UA_STATUSCODE_GOOD)
         UA_LOG_INFO_CHANNEL(server->config.logger, channel,
@@ -562,167 +488,167 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
     /* Clean up */
     UA_deleteMembers(request, requestType);
     UA_deleteMembers(response, responseType);
+    return retval;
 }
 
-/* ERR -> Error from the remote connection */
-static void processERR(UA_Server *server, UA_Connection *connection, const UA_ByteString *msg, size_t *offset) {
-    UA_TcpErrorMessage errorMessage;
-    if (UA_TcpErrorMessage_decodeBinary(msg, offset, &errorMessage) != UA_STATUSCODE_GOOD) {
-        UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                    "Connection %i | Could not decide the ERR message. "
-                    "Closing the connection.", connection->sockfd);
-        connection->close(connection);
-        return;
-    }
-
-    UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                 "Connection %i | Client replied with an error message: %s %.*s",
-                 connection->sockfd, UA_StatusCode_name(errorMessage.error),
-                 (int)errorMessage.reason.length, errorMessage.reason.data);
-
-    UA_TcpErrorMessage_deleteMembers(&errorMessage);
-}
-
-/* Takes decoded messages starting at the nodeid of the content type. Only OPN
- * messages start at the asymmetricalgorithmsecurityheader and are not
- * decoded. */
-static void
-UA_Server_processSecureChannelMessage(UA_Server *server, UA_SecureChannel *channel,
-                                      UA_MessageType messagetype, UA_UInt32 requestId,
-                                      const UA_ByteString *message) {
-    UA_assert(channel);
-    UA_assert(channel->connection);
+/* Takes decoded messages starting at the nodeid of the content type. */
+static UA_StatusCode
+processSecureChannelMessage(void *application, UA_SecureChannel *channel,
+                            UA_MessageType messagetype, UA_UInt32 requestId,
+                            const UA_ByteString *message) {
+    UA_Server *server = (UA_Server*)application;
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
     switch(messagetype) {
-    case UA_MESSAGETYPE_ERR: {
-        const UA_TcpErrorMessage *msg = (const UA_TcpErrorMessage *) message;
-        UA_LOG_ERROR_CHANNEL(server->config.logger, channel,
-                             "Client replied with an error message: %s %.*s",
-                             UA_StatusCode_name(msg->error), (int)msg->reason.length,
-                             msg->reason.data);
-        break;
-    }
-    case UA_MESSAGETYPE_HEL:
-        UA_LOG_TRACE_CHANNEL(server->config.logger, channel,
-                             "Cannot process a HEL on an open channel");
-        break;
     case UA_MESSAGETYPE_OPN:
         UA_LOG_TRACE_CHANNEL(server->config.logger, channel,
                              "Process an OPN on an open channel");
-        processOPN(server, channel->connection, channel->securityToken.channelId, message);
+        retval = processOPN(server, channel, requestId, message);
         break;
     case UA_MESSAGETYPE_MSG:
         UA_LOG_TRACE_CHANNEL(server->config.logger, channel, "Process a MSG");
-        processMSG(server, channel, requestId, message);
+        retval = processMSG(server, channel, requestId, message);
         break;
     case UA_MESSAGETYPE_CLO:
         UA_LOG_TRACE_CHANNEL(server->config.logger, channel, "Process a CLO");
         Service_CloseSecureChannel(server, channel);
         break;
     default:
-        UA_LOG_TRACE_CHANNEL(server->config.logger, channel,
-                             "Unknown message type");
+        UA_LOG_TRACE_CHANNEL(server->config.logger, channel, "Invalid message type");
+        retval = UA_STATUSCODE_BADTCPMESSAGETYPEINVALID;
+        break;
     }
+    return retval;
 }
 
-static void
-processCompleteChunk(UA_Server *server, UA_Connection *connection,
-                     UA_ByteString *message) {
+static UA_StatusCode
+createSecureChannel(void *application, UA_Connection *connection,
+                    UA_AsymmetricAlgorithmSecurityHeader *asymHeader) {
+    UA_Server *server = (UA_Server*)application;
+
+    /* Iterate over available endpoints and choose the correct one */
+    UA_Endpoint *endpoint = NULL;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    UA_SecureChannel *channel = connection->channel; 
-    if(!channel) {
-        /* Process chunk without a channel; must be OPN */
+    for(size_t i = 0; i < server->config.endpointsSize; ++i) {
+        UA_Endpoint *endpointCandidate = &server->config.endpoints[i];
+        if(!UA_ByteString_equal(&asymHeader->securityPolicyUri,
+                                &endpointCandidate->securityPolicy.policyUri))
+            continue;
+        retval = endpointCandidate->securityPolicy.asymmetricModule.
+            compareCertificateThumbprint(&endpointCandidate->securityPolicy,
+                                         &asymHeader->receiverCertificateThumbprint);
+        if(retval != UA_STATUSCODE_GOOD)
+            continue;
+
+        /* We found the correct endpoint (except for security mode) The endpoint
+         * needs to be changed by the client / server to match the security
+         * mode. The server does this in the securechannel manager */
+        endpoint = endpointCandidate;
+        break;
+    }
+
+    if(!endpoint)
+        return UA_STATUSCODE_BADSECURITYPOLICYREJECTED;
+
+    /* Create a new channel */
+    return UA_SecureChannelManager_create(&server->secureChannelManager, connection,
+                                          &endpoint->securityPolicy, asymHeader);
+}
+
+static UA_StatusCode
+processCompleteChunkWithoutChannel(UA_Server *server, UA_Connection *connection,
+                                   UA_ByteString *message) {
+    /* Process chunk without a channel; must be OPN */
+    UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
+                 "Connection %i | No channel attached to the connection. "
+                 "Process the chunk directly", connection->sockfd);
+    size_t offset = 0;
+    UA_TcpMessageHeader tcpMessageHeader;
+    UA_StatusCode retval =
+        UA_TcpMessageHeader_decodeBinary(message, &offset, &tcpMessageHeader);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    // Only HEL and OPN messages possible without a channel (on the server side)
+    switch(tcpMessageHeader.messageTypeAndChunkType & 0x00ffffff) {
+    case UA_MESSAGETYPE_HEL:
+        retval = processHEL(server, connection, message, &offset);
+        break;
+    case UA_MESSAGETYPE_OPN:
+    {
         UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                     "Connection %i | No channel attached to the connection. "
-                     "Process the chunk directly", connection->sockfd);
-        size_t offset = 0;
-        UA_TcpMessageHeader tcpMessageHeader;
-        retval = UA_TcpMessageHeader_decodeBinary(message, &offset, &tcpMessageHeader);
-        if(retval != UA_STATUSCODE_GOOD) {
-            UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                        "Connection %i | Could not decode the TCP message header. "
-                        "Closing the connection.", connection->sockfd);
-            connection->close(connection);
-            return;
-        }
+                     "Connection %i | Process OPN message", connection->sockfd);
 
-        /* Dispatch according to the message type */
-        switch(tcpMessageHeader.messageTypeAndChunkType & 0x00ffffff) {
-        case UA_MESSAGETYPE_ERR:
-            UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                         "Connection %i | Process ERR message", connection->sockfd);
-            processERR(server, connection, message, &offset);
+        /* Called before HEL */
+        if(connection->state != UA_CONNECTION_ESTABLISHED) {
+            retval = UA_STATUSCODE_BADCOMMUNICATIONERROR;
             break;
-        case UA_MESSAGETYPE_HEL:
-            UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                         "Connection %i | Process HEL message", connection->sockfd);
-            processHEL(server, connection, message, &offset);
+        }
+
+        // Decode the asymmetric algorithm security header since it is not encrypted and
+        // needed to decide what security policy to use.
+        UA_AsymmetricAlgorithmSecurityHeader asymHeader;
+        UA_AsymmetricAlgorithmSecurityHeader_init(&asymHeader);
+        size_t messageHeaderOffset = UA_SECURE_CONVERSATION_MESSAGE_HEADER_LENGTH;
+        retval = UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(message,
+                                                                   &messageHeaderOffset,
+                                                                   &asymHeader);
+        if(retval != UA_STATUSCODE_GOOD)
             break;
-        case UA_MESSAGETYPE_OPN: {
-            UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                         "Connection %i | Process OPN message", connection->sockfd);
-            UA_UInt32 channelId = 0;
-            retval = UA_UInt32_decodeBinary(message, &offset, &channelId);
-            if(retval != UA_STATUSCODE_GOOD) {
-                UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                            "Connection %i | Could not decode the channel ID for an OPN call. "
-                            "Closing the connection.", connection->sockfd);
-                connection->close(connection);
-                break;
-            }
-            UA_ByteString offsetMessage;
-            offsetMessage.data = message->data + 12;
-            offsetMessage.length = message->length - 12;
-            processOPN(server, connection, channelId, &offsetMessage);
-            break; }
-        case UA_MESSAGETYPE_MSG:
-            UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                         "Connection %i | Processing a MSG message not possible "
-                         "without a SecureChannel", connection->sockfd);
-            connection->close(connection);
+
+        retval = createSecureChannel(server, connection, &asymHeader);
+        UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
+        if(retval != UA_STATUSCODE_GOOD)
             break;
-        case UA_MESSAGETYPE_CLO:
-            UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                         "Connection %i | Processing a CLO message not possible "
-                         "without a SecureChannel", connection->sockfd);
-            connection->close(connection);
+
+        retval = UA_SecureChannel_processChunk(connection->channel, message,
+                                               processSecureChannelMessage,
+                                               server);
+        if(retval != UA_STATUSCODE_GOOD)
             break;
-        default:
-            UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                         "Connection %i | Unknown message type", connection->sockfd);
-            connection->close(connection);
-        }
-        return;
+        break;
+    }
+    default:
+        UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
+                     "Connection %i | Expected OPN or HEL message on a connection "
+                     "without a SecureChannel", connection->sockfd);
+        retval = UA_STATUSCODE_BADTCPMESSAGETYPEINVALID;
+        break;
     }
+    return retval;
+}
 
-    /* Process (and decode) chunk in the securechannel and process complete messages */
-    retval = UA_SecureChannel_processChunk(channel, message, (UA_ProcessMessageCallback*)
-                                           UA_Server_processSecureChannelMessage, server);
-    if(retval != UA_STATUSCODE_GOOD)
-        UA_LOG_TRACE_CHANNEL(server->config.logger, channel, "Procesing chunks "
-                             "resulted in error code %s", UA_StatusCode_name(retval));
+static UA_StatusCode
+processCompleteChunk(void *const application,
+                     UA_Connection *const connection,
+                     UA_ByteString *const chunk) {
+    UA_Server *const server = (UA_Server*)application;
+    if(!connection->channel)
+        return processCompleteChunkWithoutChannel(server, connection, chunk);
+    return UA_SecureChannel_processChunk(connection->channel, chunk,
+                                         processSecureChannelMessage,
+                                         server);
 }
 
-/* Takes the raw message from the network layer */
 static void
 processBinaryMessage(UA_Server *server, UA_Connection *connection,
                      UA_ByteString *message) {
     UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
                  "Connection %i | Received a packet.", connection->sockfd);
-
 #ifdef UA_DEBUG_DUMP_PKGS
     UA_dump_hex_pkg(message->data, message->length);
 #endif
 
-    UA_StatusCode retval =
-        UA_Connection_processChunks(connection, server,
-                                    (UA_Connection_processChunk)processCompleteChunk,
-                                    message);
-
-    /* Failed to complete a chunk */
+    UA_StatusCode retval = UA_Connection_processChunks(connection, server,
+                                                       processCompleteChunk, message);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                    "Connection %i | Failed to complete a chunk. "
-                    "Closing the connection.", connection->sockfd);
+                    "Connection %i | Processing the message failed with "
+                    "error %s", connection->sockfd, UA_StatusCode_name(retval));
+        /* Send an ERR message and close the connection */
+        UA_TcpErrorMessage error;
+        error.error = retval;
+        error.reason = UA_STRING_NULL;
+        UA_Connection_sendError(connection, &error);
         connection->close(connection);
     }
 }
@@ -766,9 +692,6 @@ UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection,
     UA_Server_workerCallback(server, (UA_ServerCallback)workerProcessBinaryMessage, cm);
 }
 
-#endif
-
-#ifdef UA_ENABLE_MULTITHREADING
 static void
 deleteConnectionTrampoline(UA_Server *server, void *data) {
     UA_Connection *connection = (UA_Connection*)data;

+ 1 - 1
src/server/ua_services.h

@@ -108,7 +108,7 @@ void Service_RegisterServer2(UA_Server *server, UA_Session *session,
  * ^^^^^^^^^^^^^^^^^^^^^^^^^
  * Open or renew a SecureChannel that can be used to ensure Confidentiality and
  * Integrity for Message exchange during a Session. */
-void Service_OpenSecureChannel(UA_Server *server, UA_Connection *connection,
+void Service_OpenSecureChannel(UA_Server *server, UA_SecureChannel* channel,
                                const UA_OpenSecureChannelRequest *request,
                                UA_OpenSecureChannelResponse *response);
 

+ 14 - 23
src/server/ua_services_securechannel.c

@@ -6,28 +6,22 @@
 #include "ua_services.h"
 #include "ua_securechannel_manager.h"
 
-void
-Service_OpenSecureChannel(UA_Server *server, UA_Connection *connection,
-                          const UA_OpenSecureChannelRequest *request,
-                          UA_OpenSecureChannelResponse *response) {
-    /* TODO: if(request->clientProtocolVersion != protocolVersion) */
-
+void Service_OpenSecureChannel(UA_Server *server, UA_SecureChannel* channel,
+                               const UA_OpenSecureChannelRequest *request,
+                               UA_OpenSecureChannelResponse *response) {
     if(request->requestType == UA_SECURITYTOKENREQUESTTYPE_RENEW) {
         /* Renew the channel */
         response->responseHeader.serviceResult =
             UA_SecureChannelManager_renew(&server->secureChannelManager,
-                                          connection, request, response);
+                                          channel, request, response);
 
         /* Logging */
         if(response->responseHeader.serviceResult == UA_STATUSCODE_GOOD) {
-            UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
-                         "Connection %i | SecureChannel %i | "
-                         "SecureChannel renewed", connection->sockfd,
-                         response->securityToken.channelId);
+            UA_LOG_DEBUG_CHANNEL(server->config.logger, channel,
+                                 "SecureChannel renewed");
         } else {
-            UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
-                         "Connection %i | Renewing SecureChannel failed",
-                         connection->sockfd);
+            UA_LOG_DEBUG_CHANNEL(server->config.logger, channel,
+                                 "Renewing SecureChannel failed");
         }
         return;
     }
@@ -40,19 +34,16 @@ Service_OpenSecureChannel(UA_Server *server, UA_Connection *connection,
 
     /* Open the channel */
     response->responseHeader.serviceResult =
-        UA_SecureChannelManager_open(&server->secureChannelManager,
-                                     connection, request, response);
+        UA_SecureChannelManager_open(&server->secureChannelManager, channel,
+                                     request, response);
 
     /* Logging */
     if(response->responseHeader.serviceResult == UA_STATUSCODE_GOOD) {
-        UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
-                    "Connection %i | SecureChannel %i | "
-                    "Opened SecureChannel", connection->sockfd,
-                    response->securityToken.channelId);
+        UA_LOG_INFO_CHANNEL(server->config.logger, channel,
+                            "Opened SecureChannel");
     } else {
-        UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
-                     "Connection %i | Opening a SecureChannel failed",
-                     connection->sockfd);
+        UA_LOG_INFO_CHANNEL(server->config.logger, channel,
+                            "Opening a SecureChannel failed");
     }
 }
 

+ 144 - 11
src/server/ua_services_session.c

@@ -5,17 +5,90 @@
 #include "ua_services.h"
 #include "ua_server_internal.h"
 #include "ua_session_manager.h"
-#include "ua_types_generated_encoding_binary.h"
+#include "ua_types_generated_handling.h"
+
+/* Create a signed nonce */
+static UA_StatusCode
+nonceAndSignCreateSessionResponse(UA_Server *server, UA_SecureChannel *channel,
+                                  UA_Session *session,
+                                  const UA_CreateSessionRequest *request,
+                                  UA_CreateSessionResponse *response) {
+    if(channel->securityMode != UA_MESSAGESECURITYMODE_SIGN &&
+       channel->securityMode != UA_MESSAGESECURITYMODE_SIGNANDENCRYPT)
+        return UA_STATUSCODE_GOOD;
+
+    const UA_SecurityPolicy *const securityPolicy = channel->securityPolicy;
+    UA_SignatureData *signatureData = &response->serverSignature;
+
+    /* Generate Nonce
+     * FIXME: remove magic number??? */
+    UA_StatusCode retval = UA_SecureChannel_generateNonce(channel, 32, &response->serverNonce);
+    retval |= UA_ByteString_copy(&response->serverNonce, &session->serverNonce);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_SessionManager_removeSession(&server->sessionManager, &session->authenticationToken);
+        return retval;
+    }
+
+    size_t signatureSize = securityPolicy->asymmetricModule.cryptoModule.
+        getLocalSignatureSize(securityPolicy, channel->channelContext);
+
+    retval |= UA_ByteString_allocBuffer(&signatureData->signature, signatureSize);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_SessionManager_removeSession(&server->sessionManager, &session->authenticationToken);
+        return retval;
+    }
+
+    UA_ByteString dataToSign;
+    retval |= UA_ByteString_allocBuffer(&dataToSign,
+                                        request->clientCertificate.length +
+                                        request->clientNonce.length);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_SignatureData_deleteMembers(signatureData);
+        UA_SessionManager_removeSession(&server->sessionManager, &session->authenticationToken);
+        return retval;
+    }
+
+    memcpy(dataToSign.data, request->clientCertificate.data, request->clientCertificate.length);
+    memcpy(dataToSign.data + request->clientCertificate.length,
+           request->clientNonce.data, request->clientNonce.length);
+
+    retval |= UA_String_copy(&securityPolicy->asymmetricModule.cryptoModule.
+                             signatureAlgorithmUri, &signatureData->algorithm);
+    retval |= securityPolicy->asymmetricModule.cryptoModule.
+        sign(securityPolicy, channel->channelContext, &dataToSign, &signatureData->signature);
+
+    UA_ByteString_deleteMembers(&dataToSign);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_SignatureData_deleteMembers(signatureData);
+        UA_SessionManager_removeSession(&server->sessionManager, &session->authenticationToken);
+    }
+    return retval;
+}
 
 void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
                            const UA_CreateSessionRequest *request,
                            UA_CreateSessionResponse *response) {
+    if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGN ||
+       channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) {
+        if(!UA_ByteString_equal(&request->clientCertificate,
+                                &channel->remoteCertificate)) {
+            response->responseHeader.serviceResult = UA_STATUSCODE_BADCERTIFICATEINVALID;
+            return;
+        }
+    }
     if(channel->securityToken.channelId == 0) {
         response->responseHeader.serviceResult =
             UA_STATUSCODE_BADSECURECHANNELIDINVALID;
         return;
     }
 
+    if(!UA_ByteString_equal(&channel->securityPolicy->policyUri,
+                            &UA_SECURITY_POLICY_NONE_URI) &&
+       request->clientNonce.length < 32) {
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADNONCEINVALID;
+        return;
+    }
+
     /* Allocate the response */
     response->serverEndpoints = (UA_EndpointDescription*)
         UA_Array_new(server->config.endpointsSize,
@@ -31,6 +104,8 @@ void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
         response->responseHeader.serviceResult |=
             UA_EndpointDescription_copy(&server->config.endpoints[0].endpointDescription,
                                         &response->serverEndpoints[i]);
+    if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD)
+        return;
 
     /* Mirror back the endpointUrl */
     for(size_t i = 0; i < response->serverEndpointsSize; ++i) {
@@ -63,16 +138,21 @@ void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
     response->authenticationToken = newSession->authenticationToken;
     response->responseHeader.serviceResult =
         UA_String_copy(&request->sessionName, &newSession->sessionName);
-    if(server->config.endpointsSize > 0)
-        response->responseHeader.serviceResult |=
-            UA_ByteString_copy(&server->config.endpoints[0].endpointDescription.serverCertificate,
-                               &response->serverCertificate);
 
+    /* Todo: Copy from the session's endpoint */
+    /* if(server->config.endpointsSize > 0) */
+    /*     response->responseHeader.serviceResult |= */
+    /*     UA_ByteString_copy(&channel->endpoint->endpointDescription.serverCertificate, */
+    /*                        &response->serverCertificate); */
+
+    /* Create a signed nonce */
+    response->responseHeader.serviceResult =
+        nonceAndSignCreateSessionResponse(server, channel, newSession, request, response);
+    
     /* Failure -> remove the session */
     if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
-        UA_SessionManager_removeSession(&server->sessionManager,
-                                        &newSession->authenticationToken);
-         return;
+        UA_SessionManager_removeSession(&server->sessionManager, &newSession->authenticationToken);
+        return;
     }
 
     UA_LOG_DEBUG_CHANNEL(server->config.logger, channel,
@@ -80,10 +160,59 @@ void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
            UA_PRINTF_GUID_DATA(newSession->sessionId.identifier.guid));
 }
 
+static void
+checkSignature(const UA_Server *server,
+               const UA_SecureChannel *channel,
+               UA_Session *session,
+               const UA_ActivateSessionRequest *request,
+               UA_ActivateSessionResponse *response) {
+    if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGN ||
+       channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) {
+        const UA_SecurityPolicy *const securityPolicy = channel->securityPolicy;
+        const UA_ByteString *const localCertificate = &securityPolicy->localCertificate;
+
+        UA_ByteString dataToVerify;
+        UA_StatusCode retval = UA_ByteString_allocBuffer(&dataToVerify,
+                                                         localCertificate->length + session->serverNonce.length);
+        if(retval != UA_STATUSCODE_GOOD) {
+            response->responseHeader.serviceResult = retval;
+            UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                                 "Failed to allocate buffer for signature verification! %#10x", retval);
+            return;
+        }
+
+        memcpy(dataToVerify.data, localCertificate->data, localCertificate->length);
+        memcpy(dataToVerify.data + localCertificate->length,
+               session->serverNonce.data, session->serverNonce.length);
+
+        retval = securityPolicy->asymmetricModule.cryptoModule.
+            verify(securityPolicy, channel->channelContext, &dataToVerify,
+                   &request->clientSignature.signature);
+        if(retval != UA_STATUSCODE_GOOD) {
+            response->responseHeader.serviceResult = retval;
+            UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                                 "Failed to verify the client signature! %#10x", retval);
+            UA_ByteString_deleteMembers(&dataToVerify);
+            return;
+        }
+
+        retval  = UA_SecureChannel_generateNonce(channel, 32, &response->serverNonce);
+        retval |= UA_ByteString_copy(&response->serverNonce, &session->serverNonce);
+        if(retval != UA_STATUSCODE_GOOD) {
+            response->responseHeader.serviceResult = retval;
+            UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                                 "Failed to generate a new nonce! %#10x", retval);
+            UA_ByteString_deleteMembers(&dataToVerify);
+            return;
+        }
+
+        UA_ByteString_deleteMembers(&dataToVerify);
+    }
+}
+
 void
-Service_ActivateSession(UA_Server *server,
-                        UA_SecureChannel *channel, UA_Session *session,
-                        const UA_ActivateSessionRequest *request,
+Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
+                        UA_Session *session, const UA_ActivateSessionRequest *request,
                         UA_ActivateSessionResponse *response) {
     if(session->validTill < UA_DateTime_nowMonotonic()) {
         UA_LOG_INFO_SESSION(server->config.logger, session,
@@ -95,6 +224,10 @@ Service_ActivateSession(UA_Server *server,
         return;
     }
 
+    checkSignature(server, channel, session, request, response);
+    if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD)
+        return;
+
     /* Callback into userland access control */
     response->responseHeader.serviceResult =
         server->config.accessControl.activateSession(&session->sessionId,

+ 2 - 2
src/server/ua_services_subscription.c

@@ -404,8 +404,8 @@ subscriptionSendError(UA_SecureChannel *channel, UA_UInt32 requestHandle,
     err_response.responseHeader.requestHandle = requestHandle;
     err_response.responseHeader.timestamp = UA_DateTime_now();
     err_response.responseHeader.serviceResult = error;
-    UA_SecureChannel_sendBinaryMessage(channel, requestId, &err_response,
-                                       &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
+    UA_SecureChannel_sendSymmetricMessage(channel, requestId, UA_MESSAGETYPE_MSG,
+                                          &err_response, &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
 }
 
 void

+ 6 - 6
src/server/ua_subscription.c

@@ -305,9 +305,9 @@ UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
                          "Subscription %u | Sending out a publish response "
                          "with %u notifications", sub->subscriptionID,
                          (UA_UInt32)notifications);
-    UA_SecureChannel_sendBinaryMessage(sub->session->channel,
-                                       pre->requestId, response,
-                                       &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
+    UA_SecureChannel_sendSymmetricMessage(sub->session->channel, pre->requestId,
+                                          UA_MESSAGETYPE_MSG, response,
+                                          &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
 
     /* Reset subscription state to normal. */
     sub->state = UA_SUBSCRIPTIONSTATE_NORMAL;
@@ -379,9 +379,9 @@ UA_Subscription_answerPublishRequestsNoSubscription(UA_Server *server,
         UA_PublishResponse *response = &pre->response;
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOSUBSCRIPTION;
         response->responseHeader.timestamp = UA_DateTime_now();
-        UA_SecureChannel_sendBinaryMessage(session->channel,
-                                           pre->requestId, response,
-                                           &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
+        UA_SecureChannel_sendSymmetricMessage(session->channel, pre->requestId,
+                                              UA_MESSAGETYPE_MSG, response,
+                                              &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
         UA_PublishResponse_deleteMembers(response);
         UA_free(pre);
     }

+ 71 - 29
src/ua_connection.c

@@ -7,12 +7,56 @@
 #include "ua_types_encoding_binary.h"
 #include "ua_types_generated_encoding_binary.h"
 #include "ua_types_generated_handling.h"
+#include "ua_transport_generated_encoding_binary.h"
 #include "ua_securechannel.h"
 
 void UA_Connection_deleteMembers(UA_Connection *connection) {
     UA_ByteString_deleteMembers(&connection->incompleteMessage);
 }
 
+/* Hides somme errors before sending them to a client according to the
+ * standard. */
+static void
+hideErrors(UA_TcpErrorMessage *const error) {
+    switch(error->error) {
+    case UA_STATUSCODE_BADCERTIFICATEUNTRUSTED:
+        error->error = UA_STATUSCODE_BADSECURITYCHECKSFAILED;
+        error->reason = UA_STRING_NULL;
+        break;
+    case UA_STATUSCODE_BADCERTIFICATEREVOKED:
+        error->error = UA_STATUSCODE_BADSECURITYCHECKSFAILED;
+        error->reason = UA_STRING_NULL;
+        break;
+        // TODO: Check if these are all cases that need to be covered.
+    default:
+        break;
+    }
+}
+
+void
+UA_Connection_sendError(UA_Connection *connection, UA_TcpErrorMessage *error) {
+    hideErrors(error);
+
+    UA_TcpMessageHeader header;
+    header.messageTypeAndChunkType = UA_MESSAGETYPE_ERR + UA_CHUNKTYPE_FINAL;
+    // Header + ErrorMessage (error + reasonLength_field + length)
+    header.messageSize = 8 + (4 + 4 + (UA_UInt32)error->reason.length);
+
+    /* Get the send buffer from the network layer */
+    UA_ByteString msg = UA_BYTESTRING_NULL;
+    UA_StatusCode retval = connection->getSendBuffer(connection, header.messageSize, &msg);
+    if(retval != UA_STATUSCODE_GOOD)
+        return;
+
+    /* Encode and send the response */
+    UA_Byte *bufPos = msg.data;
+    const UA_Byte *bufEnd = &msg.data[msg.length];
+    UA_TcpMessageHeader_encodeBinary(&header, &bufPos, &bufEnd);
+    UA_TcpErrorMessage_encodeBinary(error, &bufPos, &bufEnd);
+    msg.length = header.messageSize;
+    connection->send(connection, &msg);
+}
+
 static UA_StatusCode
 prependIncompleteChunk(UA_Connection *connection, UA_ByteString *message) {
     /* Allocate the new message buffer */
@@ -41,62 +85,58 @@ bufferIncompleteChunk(UA_Connection *connection, const UA_Byte *pos, const UA_By
     return UA_STATUSCODE_GOOD;
 }
 
-static UA_Boolean
+static UA_StatusCode
 processChunk(UA_Connection *connection, void *application,
              UA_Connection_processChunk processCallback,
-             const UA_Byte **posp, const UA_Byte *end) {
+             const UA_Byte **posp, const UA_Byte *end, UA_Boolean *done) {
     const UA_Byte *pos = *posp;
     size_t length = (uintptr_t)end - (uintptr_t)pos;
 
     /* At least 8 byte needed for the header. Wait for the next chunk. */
     if(length < 8) {
         bufferIncompleteChunk(connection, pos, end);
-        return true;
+        *done = true;
+        return UA_STATUSCODE_GOOD;
     }
 
     /* Check the message type */
-    UA_UInt32 msgtype = (UA_UInt32)pos[0] +
-                       ((UA_UInt32)pos[1] << 8) +
-                       ((UA_UInt32)pos[2] << 16);
-    if(msgtype != ('M' + ('S' << 8) + ('G' << 16)) &&
-       msgtype != ('E' + ('R' << 8) + ('R' << 16)) &&
-       msgtype != ('O' + ('P' << 8) + ('N' << 16)) &&
-       msgtype != ('H' + ('E' << 8) + ('L' << 16)) &&
-       msgtype != ('A' + ('C' << 8) + ('K' << 16)) &&
-       msgtype != ('C' + ('L' << 8) + ('O' << 16))) {
+    UA_MessageType msgtype = (UA_MessageType)((UA_UInt32)pos[0] + ((UA_UInt32)pos[1] << 8) +
+        ((UA_UInt32)pos[2] << 16));
+    if(msgtype != UA_MESSAGETYPE_MSG && msgtype != UA_MESSAGETYPE_ERR &&
+       msgtype != UA_MESSAGETYPE_OPN && msgtype != UA_MESSAGETYPE_HEL &&
+       msgtype != UA_MESSAGETYPE_ACK && msgtype != UA_MESSAGETYPE_CLO) {
         /* The message type is not recognized */
-        return true;
+        return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID;
     }
 
     UA_Byte isFinal = pos[3];
     if(isFinal != 'C' && isFinal != 'F' && isFinal != 'A') {
         /* The message type is not recognized */
-        return true;
+        return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID;
     }
 
     UA_UInt32 chunk_length = 0;
-    UA_ByteString temp = {8, (UA_Byte*)(uintptr_t)pos}; /* At least 8 byte left */
+    UA_ByteString temp = { 8, (UA_Byte*)(uintptr_t)pos }; /* At least 8 byte left */
     size_t temp_offset = 4;
     /* Decoding the UInt32 cannot fail */
     UA_UInt32_decodeBinary(&temp, &temp_offset, &chunk_length);
 
     /* The message size is not allowed */
     if(chunk_length < 16 || chunk_length > connection->localConf.recvBufferSize)
-        return true;
+        return UA_STATUSCODE_BADTCPMESSAGETOOLARGE;
 
     /* Wait for the next packet to process the complete chunk */
     if(chunk_length > length) {
         bufferIncompleteChunk(connection, pos, end);
-        return true;
+        *done = true;
+        return UA_STATUSCODE_GOOD;
     }
 
-    /* Process the chunk */
+    /* Process the chunk; forward the position pointer */
     temp.length = chunk_length;
-    processCallback(application, connection, &temp);
-
-    /* Continue to the next chunk */
     *posp += chunk_length;
-    return false;
+    *done = false;
+    return processCallback(application, connection, &temp);
 }
 
 UA_StatusCode
@@ -108,8 +148,9 @@ UA_Connection_processChunks(UA_Connection *connection, void *application,
      * message and the buffer is released if allocating the memory fails. */
     UA_Boolean realloced = false;
     UA_ByteString message = *packet;
+    UA_StatusCode retval;
     if(connection->incompleteMessage.length > 0) {
-        UA_StatusCode retval = prependIncompleteChunk(connection, &message);
+        retval = prependIncompleteChunk(connection, &message);
         if(retval != UA_STATUSCODE_GOOD)
             return retval;
         realloced = true;
@@ -118,14 +159,15 @@ UA_Connection_processChunks(UA_Connection *connection, void *application,
     /* Loop over the received chunks. pos is increased with each chunk. */
     const UA_Byte *pos = message.data;
     const UA_Byte *end = &message.data[message.length];
-    UA_Boolean done;
+    UA_Boolean done = true;
     do {
-        done = processChunk(connection, application, processCallback, &pos, end);
-    } while(!done);
+        retval = processChunk(connection, application, processCallback,
+                              &pos, end, &done);
+    } while(!done && retval == UA_STATUSCODE_GOOD);
 
     if(realloced)
         UA_ByteString_deleteMembers(&message);
-    return UA_STATUSCODE_GOOD;
+    return retval;
 }
 
 /* In order to know whether a chunk was processed, we insert an indirection into
@@ -136,13 +178,13 @@ struct completeChunkTrampolineData {
     UA_Connection_processChunk processCallback;
 };
 
-static void
+static UA_StatusCode
 completeChunkTrampoline(void *application, UA_Connection *connection,
                         UA_ByteString *chunk) {
     struct completeChunkTrampolineData *data =
         (struct completeChunkTrampolineData*)application;
     data->called = true;
-    data->processCallback(data->application, connection, chunk);
+    return data->processCallback(data->application, connection, chunk);
 }
 
 UA_StatusCode

+ 13 - 3
src/ua_connection_internal.h

@@ -10,11 +10,12 @@ extern "C" {
 #endif
 
 #include "ua_plugin_network.h"
+#include "ua_transport_generated.h"
 
 /* The application can be the client or the server */
-typedef void (*UA_Connection_processChunk)(void *application,
-                                           UA_Connection *connection,
-                                           UA_ByteString *chunk);
+typedef UA_StatusCode (*UA_Connection_processChunk)(void *application,
+                                                    UA_Connection *connection,
+                                                    UA_ByteString *chunk);
 
 /* The network layer may receive chopped up messages since TCP is a streaming
  * protocol. This method calls the processChunk callback on all full chunks that
@@ -52,6 +53,15 @@ UA_Connection_receiveChunksBlocking(UA_Connection *connection, void *application
                                     UA_Connection_processChunk processCallback,
                                     UA_UInt32 timeout);
 
+/* 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);

File diff suppressed because it is too large
+ 693 - 205
src/ua_securechannel.c


+ 96 - 27
src/ua_securechannel.h

@@ -13,8 +13,12 @@ extern "C" {
 #include "ua_types.h"
 #include "ua_transport_generated.h"
 #include "ua_connection_internal.h"
+#include "ua_plugin_securitypolicy.h"
+#include "ua_plugin_log.h"
 #include "ua_util.h"
 
+#define UA_SECURE_CONVERSATION_MESSAGE_HEADER_LENGTH 12
+
 struct UA_Session;
 typedef struct UA_Session UA_Session;
 
@@ -30,58 +34,123 @@ struct ChunkEntry {
     UA_ByteString bytes;
 };
 
-/* For chunked responses */
-typedef struct {
-    UA_SecureChannel *channel;
-    UA_UInt32 requestId;
-    UA_UInt32 messageType;
-    UA_UInt16 chunksSoFar;
-    size_t messageSizeSoFar;
-    UA_Boolean final;
-    UA_StatusCode errorCode;
-    UA_ByteString buffer;
-} UA_ChunkInfo;
+typedef enum {
+    UA_SECURECHANNELSTATE_FRESH,
+    UA_SECURECHANNELSTATE_OPEN,
+    UA_SECURECHANNELSTATE_CLOSED
+} UA_SecureChannelState;
 
 struct UA_SecureChannel {
+    UA_SecureChannelState   state;
     UA_MessageSecurityMode  securityMode;
-    UA_ChannelSecurityToken securityToken; // the channelId is contained in the securityToken
-    UA_ChannelSecurityToken nextSecurityToken; // the channelId is contained in the securityToken
-    UA_AsymmetricAlgorithmSecurityHeader clientAsymAlgSettings;
-    UA_AsymmetricAlgorithmSecurityHeader serverAsymAlgSettings;
-    UA_ByteString  clientNonce;
-    UA_ByteString  serverNonce;
-    UA_UInt32      receiveSequenceNumber;
-    UA_UInt32      sendSequenceNumber;
+    UA_ChannelSecurityToken securityToken; /* the channelId is contained in the securityToken */
+    UA_ChannelSecurityToken nextSecurityToken;
+
+    /* 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 thumprint of the remote certificate */
+
+    /* Symmetric encryption info */
+    UA_ByteString remoteNonce;
+    UA_ByteString localNonce;
+
+    UA_UInt32 receiveSequenceNumber;
+    UA_UInt32 sendSequenceNumber;
+
     LIST_HEAD(session_pointerlist, SessionEntry) sessions;
     LIST_HEAD(chunk_pointerlist, ChunkEntry) chunks;
 };
 
-void UA_SecureChannel_init(UA_SecureChannel *channel);
+UA_StatusCode
+UA_SecureChannel_init(UA_SecureChannel *channel,
+                      const UA_SecurityPolicy *securityPolicy,
+                      const UA_ByteString *remoteCertificate);
 void UA_SecureChannel_deleteMembersCleanup(UA_SecureChannel *channel);
 
-UA_StatusCode UA_SecureChannel_generateNonce(UA_ByteString *nonce);
+/* Generates new keys and sets them in the channel context */
+UA_StatusCode UA_SecureChannel_generateNewKeys(UA_SecureChannel* const channel);
+
+/* Wrapper function for generating nonces for the supplied channel.
+ *
+ * Uses the random generator of the channels security policy to allocate
+ * and generate a nonce with the specified length.
+ *
+ * \param channel the channel to use.
+ * \param nonceLength the length of the nonce to be generated.
+ * \param nonce will contain the nonce after being successfully called.
+ */
+UA_StatusCode UA_SecureChannel_generateNonce(const UA_SecureChannel *const channel,
+                                             const size_t nonceLength,
+                                             UA_ByteString *const nonce);
 
 void UA_SecureChannel_attachSession(UA_SecureChannel *channel, UA_Session *session);
 void UA_SecureChannel_detachSession(UA_SecureChannel *channel, UA_Session *session);
 UA_Session * UA_SecureChannel_getSession(UA_SecureChannel *channel, UA_NodeId *token);
 
-UA_StatusCode UA_SecureChannel_sendBinaryMessage(UA_SecureChannel *channel, UA_UInt32 requestId,
-                                                  const void *content, const UA_DataType *contentType);
+UA_StatusCode UA_SecureChannel_revolveTokens(UA_SecureChannel *channel);
 
-void UA_SecureChannel_revolveTokens(UA_SecureChannel *channel);
+UA_StatusCode
+UA_SecureChannel_sendSymmetricMessage(UA_SecureChannel *channel, UA_UInt32 requestId,
+                                      UA_MessageType messageType, const void *content,
+                                      const UA_DataType *contentType);
+
+UA_StatusCode
+UA_SecureChannel_sendAsymmetricOPNMessage(UA_SecureChannel *channel, UA_UInt32 requestId,
+                                          const void *content, const UA_DataType *contentType);
 
 /**
  * Chunking
  * -------- */
-typedef void
+
+/* For sending responses in multiple chunks */
+typedef struct {
+    UA_SecureChannel *channel;
+    UA_UInt32 requestId;
+    UA_UInt32 messageType;
+
+    UA_UInt16 chunksSoFar;
+    size_t messageSizeSoFar;
+
+    UA_ByteString messageBuffer;
+    UA_StatusCode errorCode;
+    UA_Boolean final;
+} UA_ChunkInfo;
+
+typedef UA_StatusCode
 (UA_ProcessMessageCallback)(void *application, UA_SecureChannel *channel,
                             UA_MessageType messageType, UA_UInt32 requestId,
                             const UA_ByteString *message);
 
+typedef UA_StatusCode
+(UA_AsymHeaderCallback)(void *application, UA_SecureChannel *channel,
+                        UA_AsymmetricAlgorithmSecurityHeader *asymHeader);
+
+typedef UA_StatusCode
+(UA_SymHeaderCallback)(void *application, UA_SecureChannel *channel,
+                       UA_UInt32 tokenId);
+
+/* Process a single chunk. This also decrypts the chunk if required. The
+ * callback function is called with the complete message body if the message is
+ * complete.
+ * 
+ * Symmetric calback is ERR, MSG, CLO only
+ * Asymmetric callback is OPN only
+ *
+ * @param channel the channel the chunks were recieved on.
+ * @param chunks the memory region where the chunks are stored.
+ * @param callback the callback function that gets called with the complete
+ *                 message body, once a final chunk is processed.
+ * @param application data pointer to application specific data that gets passed
+ *                    on to the callback function. */
 UA_StatusCode
-UA_SecureChannel_processChunk(UA_SecureChannel *channel, const UA_ByteString *chunk,
-                              UA_ProcessMessageCallback callback, void *application);
+UA_SecureChannel_processChunk(UA_SecureChannel *channel, UA_ByteString *chunk,
+                              UA_ProcessMessageCallback callback,
+                              void *application);
 
 /**
  * Log Helper

+ 1 - 1
tests/check_client_highlevel.c

@@ -58,7 +58,7 @@ static void teardown(void) {
 
 START_TEST(Misc_State) {
     UA_ClientState state = UA_Client_getState(client);
-    ck_assert_uint_eq(state, UA_CLIENTSTATE_CONNECTED);
+    ck_assert_uint_eq(state, UA_CLIENTSTATE_SESSION);
 }
 END_TEST
 

+ 1 - 0
tests/check_server_binary_messages.c

@@ -42,6 +42,7 @@ START_TEST(processMessage) {
         UA_Server_processBinaryMessage(server, &c, &msg);
         UA_ByteString_deleteMembers(&msg);
     }
+    UA_Server_run_shutdown(server);
     UA_Server_delete(server);
     UA_ServerConfig_delete(config);
     UA_Connection_deleteMembers(&c);