Browse Source

Add UA_parseEndpointUrlEthernet; Decode numbers with any base

Rudolf Hoyler 6 years ago
parent
commit
3678b867ad
4 changed files with 178 additions and 4 deletions
  1. 18 0
      include/ua_util.h
  2. 75 4
      src/ua_util.c
  3. 3 0
      src/ua_util_internal.h
  4. 82 0
      tests/check_utils.c

+ 18 - 0
include/ua_util.h

@@ -37,6 +37,24 @@ UA_StatusCode UA_EXPORT
 UA_parseEndpointUrl(const UA_String *endpointUrl, UA_String *outHostname,
                     UA_UInt16 *outPort, UA_String *outPath);
 
+/* Split the given endpoint url into hostname, vid and pcp. All arguments must
+ * be non-NULL. EndpointUrls have the form "opc.eth://<host>[:<VID>[.PCP]]".
+ * The host is a MAC address, an IP address or a registered name like a
+ * hostname. The format of a MAC address is six groups of hexadecimal digits,
+ * separated by hyphens (e.g. 01-23-45-67-89-ab). A system may also accept
+ * hostnames and/or IP addresses if it provides means to resolve it to a MAC
+ * address (e.g. DNS and Reverse-ARP).
+ *
+ * Note: currently only parsing MAC address is supported.
+ *
+ * @param endpointUrl The endpoint URL.
+ * @param vid Set to VLAN ID.
+ * @param pcp Set to Priority Code Point.
+ * @return Returns UA_STATUSCODE_BADINTERNALERROR if parsing failed. */
+UA_StatusCode UA_EXPORT
+UA_parseEndpointUrlEthernet(const UA_String *endpointUrl, UA_String *target,
+                            UA_UInt16 *vid, UA_Byte *pcp);
+
 /**
  * Convenience macros for complex types
  * ------------------------------------ */

+ 75 - 4
src/ua_util.c

@@ -13,7 +13,7 @@
 #include "base64.h"
 
 size_t
-UA_readNumber(u8 *buf, size_t buflen, u32 *number) {
+UA_readNumberWithBase(u8 *buf, size_t buflen, u32 *number, u8 base) {
     UA_assert(buf);
     UA_assert(number);
     u32 n = 0;
@@ -21,15 +21,26 @@ UA_readNumber(u8 *buf, size_t buflen, u32 *number) {
     /* read numbers until the end or a non-number character appears */
     while(progress < buflen) {
         u8 c = buf[progress];
-        if(c < '0' || c > '9')
-            break;
-        n = (n*10) + (u32)(c-'0');
+        if(c >= '0' && c <= '9' && c <= '0' + (base-1))
+           n = (n * base) + c - '0';
+        else if(base > 9 && c >= 'a' && c <= 'z' && c <= 'a' + (base-11))
+           n = (n * base) + c-'a' + 10;
+        else if(base > 9 && c >= 'A' && c <= 'Z' && c <= 'A' + (base-11))
+           n = (n * base) + c-'A' + 10;
+        else
+           break;
         ++progress;
     }
     *number = n;
     return progress;
 }
 
+size_t
+UA_readNumber(u8 *buf, size_t buflen, u32 *number)
+{
+    return UA_readNumberWithBase(buf, buflen, number, 10);
+}
+
 UA_StatusCode
 UA_parseEndpointUrl(const UA_String *endpointUrl, UA_String *outHostname,
                     u16 *outPort, UA_String *outPath) {
@@ -103,6 +114,66 @@ UA_parseEndpointUrl(const UA_String *endpointUrl, UA_String *outHostname,
     return UA_STATUSCODE_GOOD;
 }
 
+UA_StatusCode
+UA_parseEndpointUrlEthernet(const UA_String *endpointUrl, UA_String *target,
+                            UA_UInt16 *vid, UA_Byte *prio) {
+    /* Url must begin with "opc.eth://" */
+    if(endpointUrl->length < 11) {
+        return UA_STATUSCODE_BADINTERNALERROR;
+    } else if(strncmp((char*) endpointUrl->data, "opc.eth://", 10) != 0) {
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
+
+    /* Where does the host address end? */
+    size_t curr = 10;
+    for(; curr < endpointUrl->length; ++curr) {
+        if(endpointUrl->data[curr] == ':') {
+           break;
+        }
+    }
+
+    /* set host address */
+    target->data = &endpointUrl->data[10];
+    target->length = curr - 10;
+    if(curr == endpointUrl->length) {
+        return UA_STATUSCODE_GOOD;
+    }
+
+    /* Set VLAN */
+    u32 value = 0;
+    curr++;  /* skip ':' */
+    size_t progress = UA_readNumber(&endpointUrl->data[curr],
+                                    endpointUrl->length - curr, &value);
+    if(progress == 0 || value > 4096) {
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
+    curr += progress;
+    if(curr == endpointUrl->length || endpointUrl->data[curr] == '.') {
+        *vid = (UA_UInt16) value;
+    }
+    if(curr == endpointUrl->length) {
+        return UA_STATUSCODE_GOOD;
+    }
+
+    /* Set priority */
+    if(endpointUrl->data[curr] != '.') {
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
+    curr++;  /* skip '.' */
+    progress = UA_readNumber(&endpointUrl->data[curr],
+                             endpointUrl->length - curr, &value);
+    if(progress == 0 || value > 7) {
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
+    curr += progress;
+    if(curr != endpointUrl->length) {
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
+    *prio = (UA_Byte) value;
+
+    return UA_STATUSCODE_GOOD;
+}
+
 UA_StatusCode UA_ByteString_toBase64String(const UA_ByteString *byteString, UA_String *str) {
     if (str->length != 0) {
         UA_free(str->data);

+ 3 - 0
src/ua_util_internal.h

@@ -148,6 +148,9 @@ UA_atomic_subSize(volatile size_t *addr, size_t decrease) {
  * up to that point. */
 size_t UA_readNumber(u8 *buf, size_t buflen, u32 *number);
 
+/* Same as UA_ReadNumber but with a base parameter */
+size_t UA_readNumberWithBase(u8 *buf, size_t buflen, u32 *number, u8 base);
+
 #ifndef UA_MIN
 #define UA_MIN(A,B) (A > B ? B : A)
 #endif

+ 82 - 0
tests/check_utils.c

@@ -120,6 +120,66 @@ START_TEST(EndpointUrl_split) {
 }
 END_TEST
 
+START_TEST(EndpointUrl_ethernet) {
+    UA_String target;
+    UA_UInt16 vid = 0;
+    UA_Byte pcp = 0;
+    UA_String expected;
+
+    // check for too short url
+    UA_String endPointUrl = UA_STRING("inv.ali:/");
+    ck_assert_uint_eq(UA_parseEndpointUrlEthernet(&endPointUrl, &target, &vid, &pcp),
+                      UA_STATUSCODE_BADINTERNALERROR);
+    ck_assert_uint_eq(vid, 0);
+    ck_assert_uint_eq(pcp, 0);
+
+    // valid without vid and pcp but leading ':'
+    endPointUrl = UA_STRING("opc.eth://target:");
+    ck_assert_uint_eq(UA_parseEndpointUrlEthernet(&endPointUrl, &target, &vid, &pcp),
+                      UA_STATUSCODE_BADINTERNALERROR);
+    ck_assert_uint_eq(vid, 0);
+    ck_assert_uint_eq(pcp, 0);
+
+    // without pcp and vid as non decimal
+    endPointUrl = UA_STRING("opc.eth://target:abc");
+    ck_assert_uint_eq(UA_parseEndpointUrlEthernet(&endPointUrl, &target, &vid, &pcp),
+                      UA_STATUSCODE_BADINTERNALERROR);
+    ck_assert_uint_eq(vid, 0);
+    ck_assert_uint_eq(pcp, 0);
+
+    // pcp as non decimal
+    endPointUrl = UA_STRING("opc.eth://target:100.abc");
+    ck_assert_uint_eq(UA_parseEndpointUrlEthernet(&endPointUrl, &target, &vid, &pcp),
+                      UA_STATUSCODE_BADINTERNALERROR);
+    ck_assert_uint_eq(vid, 100);
+    ck_assert_uint_eq(pcp, 0);
+
+    // valid without pcp but leading '.'
+    endPointUrl = UA_STRING("opc.eth://target:100.");
+    ck_assert_uint_eq(UA_parseEndpointUrlEthernet(&endPointUrl, &target, &vid, &pcp),
+                      UA_STATUSCODE_BADINTERNALERROR);
+    ck_assert_uint_eq(vid, 100);
+    ck_assert_uint_eq(pcp, 0);
+
+    // valid without pcp
+    endPointUrl = UA_STRING("opc.eth://target:100");
+    ck_assert_uint_eq(UA_parseEndpointUrlEthernet(&endPointUrl, &target, &vid, &pcp),
+                      UA_STATUSCODE_GOOD);
+    expected = UA_STRING("target");
+    ck_assert(UA_String_equal(&target, &expected));
+    ck_assert_uint_eq(vid, 100);
+    ck_assert_uint_eq(pcp, 0);
+
+    // valid
+    endPointUrl = UA_STRING("opc.eth://target:100.7");
+    ck_assert_uint_eq(UA_parseEndpointUrlEthernet(&endPointUrl, &target, &vid, &pcp),
+                      UA_STATUSCODE_GOOD);
+    expected = UA_STRING("target");
+    ck_assert(UA_String_equal(&target, &expected));
+    ck_assert_uint_eq(vid, 100);
+    ck_assert_uint_eq(pcp, 7);
+}
+END_TEST
 
 START_TEST(readNumber) {
     UA_UInt32 result;
@@ -133,6 +193,24 @@ START_TEST(readNumber) {
 }
 END_TEST
 
+START_TEST(readNumberWithBase) {
+    UA_UInt32 result;
+    ck_assert_uint_eq(UA_readNumberWithBase((UA_Byte*)"g", 1, &result, 16), 0);
+
+    ck_assert_uint_eq(UA_readNumberWithBase((UA_Byte*)"f", 1, &result, 16), 1);
+    ck_assert_uint_eq(result, 15);
+
+    ck_assert_uint_eq(UA_readNumberWithBase((UA_Byte*)"1x", 2, &result, 16), 1);
+    ck_assert_uint_eq(result, 1);
+
+    ck_assert_uint_eq(UA_readNumberWithBase((UA_Byte*)"12345678", 9, &result, 16), 8);
+    ck_assert_uint_eq(result, 0x12345678);
+
+    ck_assert_uint_eq(UA_readNumberWithBase((UA_Byte*)"123456789", 9, &result, 8), 7);
+    ck_assert_uint_eq(result, 01234567);
+}
+END_TEST
+
 
 START_TEST(StatusCode_msg) {
 #ifndef UA_ENABLE_STATUSCODE_DESCRIPTIONS
@@ -304,8 +382,12 @@ static Suite* testSuite_Utils(void) {
     TCase *tc_endpointUrl_split = tcase_create("EndpointUrl_split");
     tcase_add_test(tc_endpointUrl_split, EndpointUrl_split);
     suite_add_tcase(s,tc_endpointUrl_split);
+    TCase *tc_endpointUrl_ethernet = tcase_create("EndpointUrl_ethernet");
+    tcase_add_test(tc_endpointUrl_ethernet, EndpointUrl_ethernet);
+    suite_add_tcase(s,tc_endpointUrl_ethernet);
     TCase *tc_utils = tcase_create("Utils");
     tcase_add_test(tc_utils, readNumber);
+    tcase_add_test(tc_utils, readNumberWithBase);
     tcase_add_test(tc_utils, StatusCode_msg);
     suite_add_tcase(s,tc_utils);