Browse Source

Refactor UA_parseEndpointUrl to work with UA_String; Create ua_util.c for utility functions

Julius Pfrommer 7 years ago
parent
commit
3a74a089ae
7 changed files with 191 additions and 212 deletions
  1. 7 0
      CHANGELOG
  2. 1 0
      CMakeLists.txt
  3. 14 14
      include/ua_connection.h
  4. 12 12
      plugins/ua_network_tcp.c
  5. 0 134
      src/ua_connection.c
  6. 88 0
      src/ua_util.c
  7. 69 52
      tests/check_utils.c

+ 7 - 0
CHANGELOG

@@ -1,6 +1,13 @@
 The changelog tracks changes to the public API.
 The changelog tracks changes to the public API.
 Internal refactorings and bug fixes are not reported here.
 Internal refactorings and bug fixes are not reported here.
 
 
+2017-04-16 jpfr <julius.pfrommer@web.de>
+
+    * Refactor UA_parseEndpointUrl to work with UA_String
+
+      The output hostname and path now point into the original
+      endpointUrl with an appropriate length.
+
 2017-04-14 jpfr <julius.pfrommer@web.de>
 2017-04-14 jpfr <julius.pfrommer@web.de>
 
 
     * Auto-instantiate only child nodes marked as mandatory (fixes #1004)
     * Auto-instantiate only child nodes marked as mandatory (fixes #1004)

+ 1 - 0
CMakeLists.txt

@@ -252,6 +252,7 @@ set(lib_sources ${PROJECT_SOURCE_DIR}/src/ua_types.c
                 ${PROJECT_SOURCE_DIR}/src/ua_types_encoding_binary.c
                 ${PROJECT_SOURCE_DIR}/src/ua_types_encoding_binary.c
                 ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.c
                 ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.c
                 ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated.c
                 ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated.c
+                ${PROJECT_SOURCE_DIR}/src/ua_util.c
                 ${PROJECT_SOURCE_DIR}/src/ua_connection.c
                 ${PROJECT_SOURCE_DIR}/src/ua_connection.c
                 ${PROJECT_SOURCE_DIR}/src/ua_securechannel.c
                 ${PROJECT_SOURCE_DIR}/src/ua_securechannel.c
                 ${PROJECT_SOURCE_DIR}/src/ua_session.c
                 ${PROJECT_SOURCE_DIR}/src/ua_session.c

+ 14 - 14
include/ua_connection.h

@@ -108,21 +108,21 @@ void UA_EXPORT UA_Connection_deleteMembers(UA_Connection *connection);
 /**
 /**
  * EndpointURL Helper
  * EndpointURL Helper
  * ^^^^^^^^^^^^^^^^^^ */
  * ^^^^^^^^^^^^^^^^^^ */
-/* Split the given endpoint url into hostname and port
- * @param endpointUrl The endpoint URL to split up
- * @param hostname the target array for hostname. Has to be at least 256 size.
- *        If an IPv6 address is given, hostname contains e.g.
- *        '[2001:0db8:85a3::8a2e:0370:7334]'
- * @param port set to the port of the url or 0
- * @param path pointing to the end of given endpointUrl or to NULL if no
- *        path given. The starting '/' is NOT included in path
- * @return UA_STATUSCODE_BADOUTOFRANGE if url too long,
- *         UA_STATUSCODE_BADATTRIBUTEIDINVALID if url not starting with
- *         'opc.tcp://', UA_STATUSCODE_GOOD on success
- */
+/* Split the given endpoint url into hostname, port and path. All arguments must
+ * be non-NULL.
+ *
+ * @param endpointUrl The endpoint URL.
+ * @param outHostname Set to the parsed hostname. The string points into the
+ *        original endpointUrl, so no memory is allocated. If an IPv6 address is
+ *        given, hostname contains e.g. '[2001:0db8:85a3::8a2e:0370:7334]'
+ * @param outPort Set to the port of the url or left unchanged.
+ * @param outPath Set to the path if one is present in the endpointUrl. The
+ *        starting (and trailing) '/' is NOT included in the path. The string
+ *        points into the original endpointUrl, so no memory is allocated.
+ * @return Returns UA_STATUSCODE_BADTCPENDPOINTURLINVALID if parsing failed. */
 UA_StatusCode UA_EXPORT
 UA_StatusCode UA_EXPORT
-UA_EndpointUrl_split(const char *endpointUrl, char *hostname,
-                     UA_UInt16 * port, const char ** path);
+UA_parseEndpointUrl(const UA_String *endpointUrl, UA_String *outHostname,
+                    UA_UInt16 *outPort, UA_String *outPath);
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 } // extern "C"
 } // extern "C"

+ 12 - 12
plugins/ua_network_tcp.c

@@ -635,26 +635,26 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf, const char *endpointUrl,
     connection.releaseSendBuffer = ClientNetworkLayerReleaseBuffer;
     connection.releaseSendBuffer = ClientNetworkLayerReleaseBuffer;
     connection.releaseRecvBuffer = ClientNetworkLayerReleaseBuffer;
     connection.releaseRecvBuffer = ClientNetworkLayerReleaseBuffer;
 
 
-    char hostname[512];
+    UA_String endpointUrlString = UA_STRING((char*)(uintptr_t)endpointUrl);
+    UA_String hostnameString = UA_STRING_NULL;
+    UA_String pathString = UA_STRING_NULL;
     UA_UInt16 port = 0;
     UA_UInt16 port = 0;
-    const char *path = NULL;
-
-    UA_StatusCode parse_retval = UA_EndpointUrl_split(endpointUrl, hostname, &port, &path);
-    if(parse_retval != UA_STATUSCODE_GOOD) {
-        if(parse_retval == UA_STATUSCODE_BADOUTOFRANGE)
-            UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK,
-                           "Server url is invalid: %s", endpointUrl);
-        else if(parse_retval == UA_STATUSCODE_BADATTRIBUTEIDINVALID)
-            UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK,
-                           "Server url does not begin with 'opc.tcp://'  '%s'", endpointUrl);
+    char hostname[512];
+
+    UA_StatusCode parse_retval =
+        UA_parseEndpointUrl(&endpointUrlString, &hostnameString, &port, &pathString);
+    if(parse_retval != UA_STATUSCODE_GOOD || hostnameString.length > 511) {
+        UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK,
+                       "Server url is invalid: %s", endpointUrl);
         return connection;
         return connection;
     }
     }
-
     if(port == 0) {
     if(port == 0) {
         port = 4840;
         port = 4840;
         UA_LOG_INFO(logger, UA_LOGCATEGORY_NETWORK,
         UA_LOG_INFO(logger, UA_LOGCATEGORY_NETWORK,
                     "No port defined, using standard port %d", port);
                     "No port defined, using standard port %d", port);
     }
     }
+    memcpy(hostname, hostnameString.data, hostnameString.length);
+    hostname[hostnameString.length] = 0;
 
 
     struct addrinfo hints, *server;
     struct addrinfo hints, *server;
     memset(&hints, 0, sizeof(hints));
     memset(&hints, 0, sizeof(hints));

+ 0 - 134
src/ua_connection.c

@@ -195,137 +195,3 @@ UA_Connection_attachSecureChannel(UA_Connection *connection, UA_SecureChannel *c
     if(UA_atomic_cmpxchg((void**)&channel->connection, NULL, connection) == NULL)
     if(UA_atomic_cmpxchg((void**)&channel->connection, NULL, connection) == NULL)
         UA_atomic_xchg((void**)&connection->channel, (void*)channel);
         UA_atomic_xchg((void**)&connection->channel, (void*)channel);
 }
 }
-
-UA_StatusCode
-UA_EndpointUrl_split_ptr(const char *endpointUrl, char *hostname,
-                         const char ** port, const char **path) {
-    if (!endpointUrl || !hostname)
-        return UA_STATUSCODE_BADINVALIDARGUMENT;
-
-    size_t urlLength = strlen(endpointUrl);
-    if(urlLength < 10 || urlLength >= 256)
-        return UA_STATUSCODE_BADOUTOFRANGE;
-
-    if(strncmp(endpointUrl, "opc.tcp://", 10) != 0)
-        return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
-
-    if (urlLength == 10) {
-        hostname[0] = '\0';
-        port = NULL;
-        *path = NULL;
-    }
-
-    /* where does the port begin? */
-    size_t portpos = 10;
-    // opc.tcp://[2001:0db8:85a3::8a2e:0370:7334]:1234/path
-    // if ip6, then end not found, otherwise we are fine
-    UA_Boolean ip6_end_found = endpointUrl[portpos] != '[';
-    for(; portpos < urlLength; ++portpos) {
-        if (!ip6_end_found) {
-            if (endpointUrl[portpos] == ']')
-                ip6_end_found = UA_TRUE;
-            continue;
-        }
-
-        if(endpointUrl[portpos] == ':' || endpointUrl[portpos] == '/')
-            break;
-    }
-
-    memcpy(hostname, &endpointUrl[10], portpos - 10);
-    hostname[portpos-10] = 0;
-
-    if(port) {
-        if (portpos < urlLength - 1) {
-            if (endpointUrl[portpos] == '/')
-                *port = NULL;
-            else
-                *port = &endpointUrl[portpos + 1];
-        } else {
-            *port = NULL;
-        }
-    }
-
-    if(path) {
-        size_t pathpos = portpos < urlLength ? portpos : 10;
-        for(; pathpos < urlLength; ++pathpos) {
-            if(endpointUrl[pathpos] == '/')
-                break;
-        }
-        if (pathpos < urlLength-1)
-            *path = &endpointUrl[pathpos+1]; // do not include slash in path
-        else
-            *path = NULL;
-    }
-
-    return UA_STATUSCODE_GOOD;
-}
-
-
-UA_StatusCode
-UA_EndpointUrl_split(const char *endpointUrl, char *hostname,
-                     UA_UInt16 * port, const char ** path) {
-    const char* portTmp = NULL;
-    const char* pathTmp = NULL;
-    UA_StatusCode retval = UA_EndpointUrl_split_ptr(endpointUrl, hostname, &portTmp, &pathTmp);
-    if(retval != UA_STATUSCODE_GOOD) {
-        if(hostname)
-            hostname[0] = '\0';
-        return retval;
-    }
-    if(!port && !path)
-        return UA_STATUSCODE_GOOD;
-
-    char portStr[6];
-    portStr[0] = '\0';
-    if(portTmp) {
-        size_t portLen;
-        if (pathTmp)
-            portLen = (size_t)(pathTmp-portTmp-1);
-        else
-            portLen = strlen(portTmp);
-
-        if (portLen > 5) // max is 65535
-            return UA_STATUSCODE_BADOUTOFRANGE;
-
-        memcpy(portStr, portTmp, portLen);
-        portStr[portLen]='\0';
-
-        if(port) {
-            for (size_t i=0; i<6; ++i) {
-                if (portStr[i] == 0)
-                    break;
-                if (portStr[i] < '0' || portStr[i] > '9')
-                    return UA_STATUSCODE_BADOUTOFRANGE;
-            }
-
-            UA_UInt32 p;
-            UA_readNumber((UA_Byte*)portStr, 6, &p);
-            if (p>65535)
-                return UA_STATUSCODE_BADOUTOFRANGE;
-            *port = (UA_UInt16)p;
-        }
-    } else {
-        if (port)
-            *port = 0;
-    }
-    if (path)
-        *path = pathTmp;
-    return UA_STATUSCODE_GOOD;
-}
-
-size_t UA_readNumber(UA_Byte *buf, size_t buflen, UA_UInt32 *number) {
-    UA_assert(buf);
-    UA_assert(number);
-    UA_UInt32 n = 0;
-    size_t progress = 0;
-    /* read numbers until the end or a non-number character appears */
-    while(progress < buflen) {
-        UA_Byte c = buf[progress];
-        if(c < '0' || c > '9')
-            break;
-        n = (n*10) + (UA_UInt32)(c-'0');
-        ++progress;
-    }
-    *number = n;
-    return progress;
-}

+ 88 - 0
src/ua_util.c

@@ -0,0 +1,88 @@
+/* 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_util.h"
+#include "ua_connection.h"
+
+size_t
+UA_readNumber(UA_Byte *buf, size_t buflen, UA_UInt32 *number) {
+    UA_assert(buf);
+    UA_assert(number);
+    UA_UInt32 n = 0;
+    size_t progress = 0;
+    /* read numbers until the end or a non-number character appears */
+    while(progress < buflen) {
+        UA_Byte c = buf[progress];
+        if(c < '0' || c > '9')
+            break;
+        n = (n*10) + (UA_UInt32)(c-'0');
+        ++progress;
+    }
+    *number = n;
+    return progress;
+}
+
+UA_StatusCode
+UA_parseEndpointUrl(const UA_String *endpointUrl, UA_String *outHostname,
+                    UA_UInt16 *outPort, UA_String *outPath) {
+    /* Url must begin with "opc.tcp://" */
+    if(endpointUrl->length < 11 || strncmp((char*)endpointUrl->data, "opc.tcp://", 10) != 0)
+        return UA_STATUSCODE_BADTCPENDPOINTURLINVALID;
+
+    /* Where does the hostname end? */
+    size_t pos = 10;
+    if(endpointUrl->data[pos] == '[') {
+        /* IPv6: opc.tcp://[2001:0db8:85a3::8a2e:0370:7334]:1234/path */
+        for(; pos < endpointUrl->length; ++pos) {
+            if(endpointUrl->data[pos] == ']')
+                break;
+        }
+        if(pos == endpointUrl->length)
+            return UA_STATUSCODE_BADTCPENDPOINTURLINVALID;
+        pos++;
+    } else {
+        /* IPv4 or hostname: opc.tcp://something.something:1234/path */
+        for(; pos < endpointUrl->length; ++pos) {
+            if(endpointUrl->data[pos] == ':' || endpointUrl->data[pos] == '/')
+                break;
+        }
+    }
+
+    /* Set the hostname */
+    outHostname->data = &endpointUrl->data[10];
+    outHostname->length = pos - 10;
+    if(pos == endpointUrl->length)
+        return UA_STATUSCODE_GOOD;
+
+    /* Set the port */
+    if(endpointUrl->data[pos] == ':') {
+        if(++pos == endpointUrl->length)
+            return UA_STATUSCODE_BADTCPENDPOINTURLINVALID;
+        UA_UInt32 largeNum;
+        size_t progress = UA_readNumber(&endpointUrl->data[pos], endpointUrl->length - pos, &largeNum);
+        if(progress == 0 || largeNum > 65535)
+            return UA_STATUSCODE_BADTCPENDPOINTURLINVALID;
+        /* Test if the end of a valid port was reached */
+        pos += progress;
+        if(pos == endpointUrl->length || endpointUrl->data[pos] == '/')
+            *outPort = (UA_UInt16)largeNum;
+        if(pos == endpointUrl->length)
+            return UA_STATUSCODE_GOOD;
+    }
+
+    /* Set the path */
+    UA_assert(pos < endpointUrl->length);
+    if(endpointUrl->data[pos] != '/')
+        return UA_STATUSCODE_BADTCPENDPOINTURLINVALID;
+    if(++pos == endpointUrl->length)
+        return UA_STATUSCODE_GOOD;
+    outPath->data = &endpointUrl->data[pos];
+    outPath->length = endpointUrl->length - pos;
+
+    /* Remove trailing slash from the path */
+    if(endpointUrl->data[endpointUrl->length - 1] == '/')
+        outPath->length--;
+
+    return UA_STATUSCODE_GOOD;
+}

+ 69 - 52
tests/check_utils.c

@@ -10,95 +10,112 @@
 #include "check.h"
 #include "check.h"
 
 
 START_TEST(EndpointUrl_split) {
 START_TEST(EndpointUrl_split) {
-    // check for null
-    ck_assert_uint_eq(UA_EndpointUrl_split(NULL, NULL, NULL, NULL), UA_STATUSCODE_BADINVALIDARGUMENT);
-
-    char hostname[256];
-    UA_UInt16 port;
-    const char* path;
-
-    // check for max url length
-    // this string has 256 chars
-    char *overlength = "wEgfH2Sqe8AtFcUqX6VnyvZz6A4AZtbKRvGwQWvtPLrt7aaLb6wtqFzqQ2dLYLhTwJpAuVbsRTGfjvP2kvsVSYQLLeGuPjJyYnMt5e8TqtmYuPTb78uuAx7KyQB9ce95eacs3Jp32KMNtb7BTuKjQ236MnMX3mFWYAkALcj5axpQnFaGyU3HvpYrX24FTEztuZ3zpNnqBWQyHPVa6efGTzmUXMADxjw3AbG5sTGzDca7rucsfQRAZby8ZWKm66pV";
-    ck_assert_uint_eq(UA_EndpointUrl_split(overlength, hostname, &port, &path), UA_STATUSCODE_BADOUTOFRANGE);
+    UA_String hostname = UA_STRING_NULL;
+    UA_String path = UA_STRING_NULL;
+    UA_UInt16 port = 0;
 
 
     // check for too short url
     // check for too short url
-    ck_assert_uint_eq(UA_EndpointUrl_split("inv.ali:/", hostname, &port, &path), UA_STATUSCODE_BADOUTOFRANGE);
+    UA_String endPointUrl = UA_STRING("inv.ali:/");
+    ck_assert_uint_eq(UA_parseEndpointUrl(&endPointUrl, &hostname, &port, &path),
+                      UA_STATUSCODE_BADTCPENDPOINTURLINVALID);
 
 
     // check for opc.tcp:// protocol
     // check for opc.tcp:// protocol
-    ck_assert_uint_eq(UA_EndpointUrl_split("inv.ali://", hostname, &port, &path), UA_STATUSCODE_BADATTRIBUTEIDINVALID);
+    endPointUrl = UA_STRING("inv.ali://");
+    ck_assert_uint_eq(UA_parseEndpointUrl(&endPointUrl, &hostname, &port, &path),
+                      UA_STATUSCODE_BADTCPENDPOINTURLINVALID);
 
 
     // empty url
     // empty url
-    ck_assert_uint_eq(UA_EndpointUrl_split("opc.tcp://", hostname, &port, &path), UA_STATUSCODE_GOOD);
-    ck_assert_uint_eq(strlen(hostname), 0);
+    endPointUrl = UA_STRING("opc.tcp://");
+    ck_assert_uint_eq(UA_parseEndpointUrl(&endPointUrl, &hostname, &port, &path),
+                      UA_STATUSCODE_BADTCPENDPOINTURLINVALID);
+    ck_assert(UA_String_equal(&hostname, &UA_STRING_NULL));
     ck_assert_uint_eq(port, 0);
     ck_assert_uint_eq(port, 0);
-    ck_assert_ptr_eq(path, NULL);
+    ck_assert(UA_String_equal(&path, &UA_STRING_NULL));
 
 
     // only hostname
     // only hostname
-    ck_assert_uint_eq(UA_EndpointUrl_split("opc.tcp://hostname", hostname, &port, &path), UA_STATUSCODE_GOOD);
-    ck_assert_str_eq(hostname,"hostname");
+    endPointUrl = UA_STRING("opc.tcp://hostname");
+    ck_assert_uint_eq(UA_parseEndpointUrl(&endPointUrl, &hostname, &port, &path), UA_STATUSCODE_GOOD);
+    UA_String expected = UA_STRING("hostname");
+    ck_assert(UA_String_equal(&hostname, &expected));
     ck_assert_uint_eq(port, 0);
     ck_assert_uint_eq(port, 0);
-    ck_assert_ptr_eq(path, NULL);
+    ck_assert(UA_String_equal(&path, &UA_STRING_NULL));
 
 
     // empty port
     // empty port
-    ck_assert_uint_eq(UA_EndpointUrl_split("opc.tcp://hostname:", hostname, &port, &path), UA_STATUSCODE_GOOD);
-    ck_assert_str_eq(hostname,"hostname");
+    endPointUrl = UA_STRING("opc.tcp://hostname:");
+    ck_assert_uint_eq(UA_parseEndpointUrl(&endPointUrl, &hostname, &port, &path),
+                      UA_STATUSCODE_BADTCPENDPOINTURLINVALID);
+    ck_assert(UA_String_equal(&hostname, &expected));
     ck_assert_uint_eq(port, 0);
     ck_assert_uint_eq(port, 0);
-    ck_assert_ptr_eq(path, NULL);
+    ck_assert(UA_String_equal(&path, &UA_STRING_NULL));
 
 
     // specific port
     // specific port
-    ck_assert_uint_eq(UA_EndpointUrl_split("opc.tcp://hostname:1234", hostname, &port, &path), UA_STATUSCODE_GOOD);
-    ck_assert_str_eq(hostname,"hostname");
+    endPointUrl = UA_STRING("opc.tcp://hostname:1234");
+    ck_assert_uint_eq(UA_parseEndpointUrl(&endPointUrl, &hostname, &port, &path),
+                      UA_STATUSCODE_GOOD);
+    ck_assert(UA_String_equal(&hostname, &expected));
     ck_assert_uint_eq(port, 1234);
     ck_assert_uint_eq(port, 1234);
-    ck_assert_ptr_eq(path, NULL);
+    ck_assert(UA_String_equal(&path, &UA_STRING_NULL));
 
 
     // IPv6
     // IPv6
-    ck_assert_uint_eq(UA_EndpointUrl_split("opc.tcp://[2001:0db8:85a3::8a2e:0370:7334]:1234/path", hostname, &port, &path), UA_STATUSCODE_GOOD);
-    ck_assert_str_eq(hostname,"[2001:0db8:85a3::8a2e:0370:7334]");
+    endPointUrl = UA_STRING("opc.tcp://[2001:0db8:85a3::8a2e:0370:7334]:1234/path");
+    ck_assert_uint_eq(UA_parseEndpointUrl(&endPointUrl, &hostname, &port, &path), UA_STATUSCODE_GOOD);
+    expected = UA_STRING("[2001:0db8:85a3::8a2e:0370:7334]");
+    UA_String expectedPath = UA_STRING("path");
+    ck_assert(UA_String_equal(&hostname, &expected));
     ck_assert_uint_eq(port, 1234);
     ck_assert_uint_eq(port, 1234);
-    ck_assert_str_eq(path, "path");
+    ck_assert(UA_String_equal(&path, &expectedPath));
 
 
     // empty hostname
     // empty hostname
-    ck_assert_uint_eq(UA_EndpointUrl_split("opc.tcp://:", hostname, &port, &path), UA_STATUSCODE_GOOD);
-    ck_assert_uint_eq(strlen(hostname),0);
+    endPointUrl = UA_STRING("opc.tcp://:");
+    port = 0;
+    path = UA_STRING_NULL;
+    ck_assert_uint_eq(UA_parseEndpointUrl(&endPointUrl, &hostname, &port, &path),
+                      UA_STATUSCODE_BADTCPENDPOINTURLINVALID);
+    ck_assert(UA_String_equal(&hostname, &UA_STRING_NULL));
     ck_assert_uint_eq(port, 0);
     ck_assert_uint_eq(port, 0);
-    ck_assert_ptr_eq(path, NULL);
+    ck_assert(UA_String_equal(&path, &UA_STRING_NULL));
 
 
     // empty hostname and no port
     // empty hostname and no port
-    ck_assert_uint_eq(UA_EndpointUrl_split("opc.tcp:///", hostname, &port, &path), UA_STATUSCODE_GOOD);
-    ck_assert_uint_eq(strlen(hostname),0);
+    endPointUrl = UA_STRING("opc.tcp:///");
+    ck_assert_uint_eq(UA_parseEndpointUrl(&endPointUrl, &hostname, &port, &path), UA_STATUSCODE_GOOD);
+    ck_assert(UA_String_equal(&hostname, &UA_STRING_NULL));
     ck_assert_uint_eq(port, 0);
     ck_assert_uint_eq(port, 0);
-    ck_assert_ptr_eq(path,0);
+    ck_assert(UA_String_equal(&path, &UA_STRING_NULL));
 
 
     // overlength port
     // overlength port
-    ck_assert_uint_eq(UA_EndpointUrl_split("opc.tcp://hostname:12345678", hostname, &port, &path), UA_STATUSCODE_BADOUTOFRANGE);
-
-    // too high port
-    ck_assert_uint_eq(UA_EndpointUrl_split("opc.tcp://hostname:65536", hostname, &port, &path), UA_STATUSCODE_BADOUTOFRANGE);
+    endPointUrl = UA_STRING("opc.tcp://hostname:12345678");
+    ck_assert_uint_eq(UA_parseEndpointUrl(&endPointUrl, &hostname, &port, &path),
+                      UA_STATUSCODE_BADTCPENDPOINTURLINVALID);
 
 
     // port not a number
     // port not a number
-    ck_assert_uint_eq(UA_EndpointUrl_split("opc.tcp://hostname:6x6", hostname, &port, &path), UA_STATUSCODE_BADOUTOFRANGE);
+    endPointUrl = UA_STRING("opc.tcp://hostname:6x6");
+    ck_assert_uint_eq(UA_parseEndpointUrl(&endPointUrl, &hostname, &port, &path),
+                      UA_STATUSCODE_BADTCPENDPOINTURLINVALID);
+    expected = UA_STRING("hostname");
+    ck_assert(UA_String_equal(&hostname, &expected));
+    ck_assert_uint_eq(port, 0);
+    ck_assert(UA_String_equal(&path, &UA_STRING_NULL));
 
 
     // no port but path
     // no port but path
-    ck_assert_uint_eq(UA_EndpointUrl_split("opc.tcp://hostname/", hostname, &port, &path), UA_STATUSCODE_GOOD);
-    ck_assert_str_eq(hostname,"hostname");
+    endPointUrl = UA_STRING("opc.tcp://hostname/");
+    ck_assert_uint_eq(UA_parseEndpointUrl(&endPointUrl, &hostname, &port, &path), UA_STATUSCODE_GOOD);
+    ck_assert(UA_String_equal(&hostname, &expected));
     ck_assert_uint_eq(port, 0);
     ck_assert_uint_eq(port, 0);
-    ck_assert_ptr_eq(path, 0);
+    ck_assert(UA_String_equal(&path, &UA_STRING_NULL));
 
 
     // port and path
     // port and path
-    ck_assert_uint_eq(UA_EndpointUrl_split("opc.tcp://hostname:1234/path", hostname, &port, &path), UA_STATUSCODE_GOOD);
-    ck_assert_str_eq(hostname,"hostname");
+    endPointUrl = UA_STRING("opc.tcp://hostname:1234/path");
+    ck_assert_uint_eq(UA_parseEndpointUrl(&endPointUrl, &hostname, &port, &path), UA_STATUSCODE_GOOD);
+    ck_assert(UA_String_equal(&hostname, &expected));
     ck_assert_uint_eq(port, 1234);
     ck_assert_uint_eq(port, 1234);
-    ck_assert_str_eq(path, "path");
-
-    // full url, but only hostname required
-    ck_assert_uint_eq(UA_EndpointUrl_split("opc.tcp://hostname:1234/path", hostname, NULL, NULL), UA_STATUSCODE_GOOD);
-    ck_assert_str_eq(hostname,"hostname");
+    ck_assert(UA_String_equal(&path, &expectedPath));
 
 
-    // full url, but only hostname and port required
-    ck_assert_uint_eq(UA_EndpointUrl_split("opc.tcp://hostname:1234/path", hostname, &port, NULL), UA_STATUSCODE_GOOD);
-    ck_assert_str_eq(hostname,"hostname");
+    // port and path with a slash
+    endPointUrl = UA_STRING("opc.tcp://hostname:1234/path/");
+    ck_assert_uint_eq(UA_parseEndpointUrl(&endPointUrl, &hostname, &port, &path), UA_STATUSCODE_GOOD);
+    ck_assert(UA_String_equal(&hostname, &expected));
     ck_assert_uint_eq(port, 1234);
     ck_assert_uint_eq(port, 1234);
+    ck_assert(UA_String_equal(&path, &expectedPath));
 }
 }
 END_TEST
 END_TEST