]> Kevux Git Server - fll/commitdiff
Update: Network additions and improvements, minor documentation comment clean ups.
authorKevin Day <kevin@kevux.org>
Sun, 9 Jul 2023 16:33:46 +0000 (11:33 -0500)
committerKevin Day <kevin@kevux.org>
Sun, 9 Jul 2023 16:33:46 +0000 (11:33 -0500)
Add f_network_is_ip_address() along with appropriate unit tests.
The unit test are not as thorough as they could be.
I did not fully review the IPv6 standard, which has a rather absurd design.
There are likely things that I do not yet know about that are not supported.
Specifically do not support port number on any IPv6 address without brackets.
(The IPv6 address notation standard is horribly written.)

level_0/f_network/c/network.c
level_0/f_network/c/network.h
level_0/f_network/data/build/settings-tests
level_0/f_network/tests/unit/c/test-network-is_ip_address.c [new file with mode: 0644]
level_0/f_network/tests/unit/c/test-network-is_ip_address.h [new file with mode: 0644]
level_0/f_network/tests/unit/c/test-network.c
level_0/f_network/tests/unit/c/test-network.h

index e802489087ce4685f125f00c1d36c8ab4673d2ae..d52bb0def4c5663e9e8ead1f0ffcccecf21fea30 100644 (file)
@@ -102,6 +102,326 @@ extern "C" {
   }
 #endif // _di_f_network_from_ip_string_
 
+#ifndef _di_f_network_is_ip_address_
+  void f_network_is_ip_address(const f_string_static_t address, f_number_unsigned_t * const port, f_state_t * const state) {
+
+    if (!state) return;
+
+    if (!address.used) {
+      state->status = F_data_not;
+
+      return;
+    }
+
+    f_number_unsigned_t i = 0;
+    uint8_t flag = 0x0; // 0x1 == is IPv6.
+    uint8_t set = 0;
+    uint8_t count = 0;
+
+    for (; i < address.used; ++i) {
+
+      if (state->interrupt) {
+        state->interrupt((void *) state, 0);
+        if (F_status_set_fine(state->status) == F_interrupt) return;
+      }
+
+      if (!address.string[i]) continue;
+
+      if (isxdigit(address.string[i])) {
+        if (address.string[i] < f_string_ascii_0_s.string[0] || address.string[i] > f_string_ascii_9_s.string[0]) {
+          flag = 0x1;
+        }
+
+        ++count;
+
+        continue;
+      }
+
+      if (address.string[i] == f_string_ascii_period_s.string[0]) {
+
+        // An IPv4 looking address with a hexidecimal letter.
+        if (flag & 0x1) {
+          state->status = F_network_version_four_not;
+
+          return;
+        }
+
+        break;
+      }
+      else if (address.string[i] == f_string_ascii_colon_s.string[0]) {
+        flag = 0x1;
+
+        break;
+      }
+      else if (address.string[i] == f_string_ascii_bracket_open_s.string[0]) {
+        if (flag) {
+          state->status = F_false;
+
+          return;
+        }
+
+        flag = 0x3;
+
+        break;
+      }
+
+      state->status = F_false;
+
+      return;
+    } // for
+
+    if (i >= address.used) {
+      state->status = F_false;
+
+      return;
+    }
+
+    f_number_unsigned_t j = 0;
+
+    if (flag & 0x1) {
+
+      // IPv6 flag additions: 0x2 == has bracket open, 0x4 == has forward slash, 0x8 = has double-colon, 0x10 = has no leading digit and no bracket open.
+      if (flag & 0x2) {
+        ++i;
+      }
+
+      state->status = F_network_version_six_not;
+
+      if (count > 4) return;
+
+      if (!count) {
+        for (j = i; i < address.used && !address.string[j]; ++j) {
+
+          if (state->interrupt) {
+            state->interrupt((void *) state, 0);
+            if (F_status_set_fine(state->status) == F_interrupt) return;
+            state->status = F_network_version_six_not;
+          }
+        } // for
+
+        if (j == address.used) return;
+        if (!(flag & 0x2)) flag |= 0x10;
+      }
+
+      for (; i < address.used; ++i) {
+
+        if (state->interrupt) {
+          state->interrupt((void *) state, 0);
+          if (F_status_set_fine(state->status) == F_interrupt) return;
+          state->status = F_network_version_six_not;
+        }
+
+        if (!address.string[i]) continue;
+
+        if (isxdigit(address.string[i])) {
+          if (++count > 4) return;
+
+          if (flag & 0x4) {
+            if (address.string[i] < f_string_ascii_0_s.string[0] || address.string[i] > f_string_ascii_9_s.string[0] || count > 3) return;
+          }
+        }
+        else if (address.string[i] == f_string_ascii_colon_s.string[0]) {
+          if (flag & 0x4) return;
+
+          j = i + 1;
+
+          if (j < address.used) {
+            for (; j < address.used && !address.string[j]; ++j) {
+
+              if (state->interrupt) {
+                state->interrupt((void *) state, 0);
+                if (F_status_set_fine(state->status) == F_interrupt) return;
+                state->status = F_network_version_six_not;
+              }
+            } // for
+
+            if (address.string[j] == f_string_ascii_colon_s.string[0]) {
+              if (flag & 0x8) return;
+
+              i = j;
+              flag |= 0x8;
+
+              for (; i < address.used && !address.string[i]; ++i) {
+
+                if (state->interrupt) {
+                  state->interrupt((void *) state, 0);
+                  if (F_status_set_fine(state->status) == F_interrupt) return;
+                  state->status = F_network_version_six_not;
+                }
+              } // for
+
+              // Must not end on a double-colon.
+              if (i + 1 == address.used) return;
+            }
+
+            // Colons must be followed by a hexidecimal digit.
+            if (!isxdigit(address.string[i + 1])) return;
+          }
+          else {
+
+            // Must not end on a colon.
+            return;
+          }
+
+          if (++set > 7) return;
+
+          count = 0;
+        }
+        else if (address.string[i] == f_string_ascii_slash_forward_s.string[0]) {
+          if (flag & 0x4) return;
+
+          flag |= 0x4;
+        }
+        else if (address.string[i] == f_string_ascii_bracket_close_s.string[0]) {
+          if (!(flag & 0x2)) return;
+
+          for (j = i + 1; j < address.used && !address.string[j]; ++j) {
+
+            if (state->interrupt) {
+              state->interrupt((void *) state, 0);
+              if (F_status_set_fine(state->status) == F_interrupt) return;
+              state->status = F_network_version_six_not;
+            }
+          } // for
+
+          if (j < address.used) {
+            if (address.string[j] == f_string_ascii_colon_s.string[0]) {
+              while (++j < address.used && !address.string[j]) {
+
+                if (state->interrupt) {
+                  state->interrupt((void *) state, 0);
+                  if (F_status_set_fine(state->status) == F_interrupt) return;
+                  state->status = F_network_version_six_not;
+                }
+              } // while
+
+              // The colon used to designate the port number must be followed by a (base-10) number.
+              for (i = j; j < address.used; ++j) {
+
+                if (state->interrupt) {
+                  state->interrupt((void *) state, 0);
+                  if (F_status_set_fine(state->status) == F_interrupt) return;
+                  state->status = F_network_version_six_not;
+                }
+
+                if (!address.string[j]) continue;
+                if (!isdigit(address.string[j])) return;
+              } // for
+
+              // The double colon either must exist when set is smaller than 7 or the double colon must not exist at all.
+              if (set < 7 && !(flag & 0x8) || set == 7 && (flag & 0x8)) return;
+
+              state->status = F_network_version_six;
+
+              if (port) *port = i;
+            }
+            else {
+
+              // Only a colon (and port number) may follow a valid close bracket.
+              return;
+            }
+          }
+          else if (count || set) {
+
+            // The double colon either must exist when set is smaller than 7 or the double colon must not exist at all.
+            if (set < 7 && !(flag & 0x8) || set == 7 && (flag & 0x8)) return;
+
+            state->status = F_network_version_six;
+
+            if (port) *port = 0;
+          }
+
+          return;
+        }
+        else {
+          return;
+        }
+      } // for
+
+      // The double colon either must exist when set is smaller than 7 or the double colon must not exist at all.
+      if (set < 7 && !(flag & 0x8) || set == 7 && (flag & 0x8)) return;
+
+      // A close bracket must exist if there is an open bracket.
+      if (flag & 0x2) return;
+
+      if (!(flag & 0x10) || set || count) {
+        state->status = F_network_version_six;
+
+        if (port) *port = 0;
+      }
+
+      return;
+    }
+
+    // IPv4 flag additions: 0x2 == has port number (and all 4 proper sets), 0x4 == has forward slash, 0x8 = finished forward slash.
+    state->status = F_network_version_four_not;
+
+    if (count > 3) return;
+
+    for (; i < address.used; ++i) {
+
+      if (state->interrupt) {
+        state->interrupt((void *) state, 0);
+        if (F_status_set_fine(state->status) == F_interrupt) return;
+        state->status = F_network_version_four_not;
+      }
+
+      if (!address.string[i]) continue;
+
+      if (isdigit(address.string[i])) {
+        if (flag & 0x4) {
+          if (!(flag & 0x8) && ++count > 3) return;
+        }
+        else if (!(flag & 0x2)) {
+          if (++count > 3) return;
+        }
+      }
+      else if (address.string[i] == f_string_ascii_period_s.string[0]) {
+        if ((flag & 0x6) || !count || ++set > 3) return;
+
+        count = 0;
+      }
+      else if (address.string[i] == f_string_ascii_colon_s.string[0]) {
+        if (set != 3 || (flag & 0x2)) return;
+
+        if (flag & 0x4) {
+          flag |= 0x8;
+        }
+
+        for (; i < address.used && !address.string[i]; ++i) {
+
+          if (state->interrupt) {
+            state->interrupt((void *) state, 0);
+            if (F_status_set_fine(state->status) == F_interrupt) return;
+            state->status = F_network_version_four_not;
+          }
+        } // for
+
+        flag |= 0x2;
+        j = i + 1; // Save the position that might represent the start of the port number.
+      }
+      else if (address.string[i] == f_string_ascii_slash_forward_s.string[0]) {
+        if ((flag & 0x4) || set != 3) return;
+
+        flag |= 0x4;
+        count = 0;
+      }
+      else {
+        return;
+      }
+    } // for
+
+    if (set == 3) {
+      state->status = F_network_version_four;
+
+      if (port) *port = j;
+    }
+    else {
+      state->status = F_network_version_four_not;
+    }
+  }
+#endif // _di_f_network_is_ip_address_
+
 #ifndef _di_f_network_to_host_long_
   f_status_t f_network_to_host_long(const uint32_t from, uint32_t * const to) {
     #ifndef _di_level_0_parameter_checking_
index 58d4034f54f43bab4b1163799fea9fd186174367..0230957e564c6fad4e5a55981cbc67f7d1713d26 100644 (file)
@@ -12,6 +12,7 @@
 
 // Libc includes.
 #include <arpa/inet.h>
+#include <ctype.h>
 #include <netdb.h>
 #include <string.h>
 #include <sys/socket.h>
@@ -152,6 +153,46 @@ extern "C" {
 #endif // _di_f_network_from_ip_string_
 
 /**
+ * Identify whether or not a string is a valid IPv4 or IPv6 address, including the optional port number.
+ *
+ * This does not fully validate the numbers.
+ * This only checks that the address is in the proper form.
+ *
+ * For IPv6, this only accepts port numbers when the IPv6 address is wrapped in brackets ('[' (U+005B) and ']' (U+005D)).
+ *
+ * @param address
+ *   The string to parse.
+ * @param port
+ *   (optional) This gets updated with the location where the first digit of the port number begins.
+ *   This is set to 0 if there is no port number.
+ *   On any error, this value is not changed.
+ *
+ *   Set to NULL to disable.
+ * @param state
+ *   A state for providing flags and handling interrupts during long running operations.
+ *   There is no state.handle().
+ *   There is no "callbacks" structure.
+ *   There is no data structure passed to these functions.
+ *
+ *   When state.interrupt() returns, only F_interrupt and F_interrupt_not are processed.
+ *   Error bit designates an error but must be passed along with F_interrupt.
+ *   All other statuses are ignored.
+ *
+ *   This alters state.status:
+ *     F_data_not on success but there is nothing to process (address.used is 0).
+ *     F_false on success, but this is not an IP Address.
+ *     F_network_version_four_not on success, but this is not an IP Address but looks close to a IPv4 address.
+ *     F_network_version_six_not on success, but this is not an IP Address but looks close to a IPv6 address.
+ *     F_network_version_four on success and this is an IPv4 address.
+ *     F_network_version_six on success and this is an IPv6 address.
+ *
+ *     F_interrupt (with or without error bit) if stopping due to an interrupt.
+ */
+#ifndef _di_f_network_is_ip_address_
+  extern void f_network_is_ip_address(const f_string_static_t address, f_number_unsigned_t * const port, f_state_t * const state);
+#endif // _di_f_network_is_ip_address_
+
+/**
  * Convert from network byte order to host byte order for an unsigned long integer.
  *
  * @param from
index 07ed04c3fdabe0c6c5afbabac95deaf3833fde91..ed1c664e826dd379f9cb75db410060fcfd76e09c 100644 (file)
@@ -25,7 +25,7 @@ build_language c
 build_libraries -lc -lcmocka
 build_libraries-individual -lf_memory -lf_string -lf_network
 
-build_sources_program test-network-from_host_long.c test-network-from_host_short.c test-network-from_ip_address.c test-network-from_ip_name.c test-network-from_ip_string.c test-network-to_host_long.c test-network-to_host_short.c test-network-to_ip_string.c
+build_sources_program test-network-from_host_long.c test-network-from_host_short.c test-network-from_ip_address.c test-network-from_ip_name.c test-network-from_ip_string.c test-network-is_ip_address.c test-network-to_host_long.c test-network-to_host_short.c test-network-to_ip_string.c
 build_sources_program test-network.c
 
 build_script no
diff --git a/level_0/f_network/tests/unit/c/test-network-is_ip_address.c b/level_0/f_network/tests/unit/c/test-network-is_ip_address.c
new file mode 100644 (file)
index 0000000..a0dde5b
--- /dev/null
@@ -0,0 +1,334 @@
+#include "test-network.h"
+#include "test-network-is_ip_address.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void test__f_network_is_ip_address__returns_data_not(void **state) {
+
+  f_state_t state_data = f_state_t_initialize;
+
+  {
+    f_network_is_ip_address(f_string_empty_s, 0, &state_data);
+
+    assert_int_equal(state_data.status, F_data_not);
+  }
+}
+
+void test__f_network_is_ip_address__returns_false(void **state) {
+
+  f_state_t state_data = f_state_t_initialize;
+
+  const f_string_static_t ips[] = {
+    macro_f_string_static_t_initialize_1("127", 0, 3),
+    macro_f_string_static_t_initialize_1("a", 0, 1),
+    macro_f_string_static_t_initialize_1("www.example.com", 0, 15),
+    macro_f_string_static_t_initialize_1("www.example.com:80", 0, 18),
+    macro_f_string_static_t_initialize_1("localhost", 0, 9),
+    macro_f_string_static_t_initialize_1("___", 0, 3),
+    macro_f_string_static_t_initialize_1("]", 0, 1),
+  };
+
+  for (uint8_t i = 0; i < 7; ++i) {
+
+    state_data.status = F_none;
+
+    f_network_is_ip_address(ips[i], 0, &state_data);
+
+    assert_int_equal(state_data.status, F_false);
+  } // for
+}
+
+void test__f_network_is_ip_address__returns_network_version_four(void **state) {
+
+  f_state_t state_data = f_state_t_initialize;
+
+  const f_string_static_t ips[] = {
+    macro_f_string_static_t_initialize_1("127.0.0.1", 0, 9),
+    macro_f_string_static_t_initialize_1("127.0.0.1/24", 0, 12),
+    macro_f_string_static_t_initialize_1("127.0.0.1:80", 0, 12),
+    macro_f_string_static_t_initialize_1("127.0.0.1/24:80", 0, 15),
+  };
+
+  const f_number_unsigned_t ports[] = {
+    0,
+    0,
+    10,
+    13,
+  };
+
+  f_number_unsigned_t port = 0;
+
+  for (uint8_t i = 0; i < 4; ++i) {
+
+    state_data.status = F_none;
+    port = 100000;
+
+    f_network_is_ip_address(ips[i], &port, &state_data);
+
+    assert_int_equal(state_data.status, F_network_version_four);
+    assert_int_equal(port, ports[i]);
+  } // for
+}
+
+void test__f_network_is_ip_address__returns_network_version_four_not(void **state) {
+
+  f_state_t state_data = f_state_t_initialize;
+
+  const f_string_static_t ips[] = {
+    macro_f_string_static_t_initialize_1("127.", 0, 4),
+    macro_f_string_static_t_initialize_1("127./24", 0, 7),
+    macro_f_string_static_t_initialize_1("127./2a", 0, 7),
+    macro_f_string_static_t_initialize_1("0..", 0, 3),
+    macro_f_string_static_t_initialize_1("0.1111", 0, 6),
+    macro_f_string_static_t_initialize_1("0.1.2.3.4.", 0, 10),
+    macro_f_string_static_t_initialize_1("0.1.2.3.4.5.6", 0, 13),
+    macro_f_string_static_t_initialize_1("1274.", 0, 5),
+    macro_f_string_static_t_initialize_1("127.1234", 0, 8),
+    macro_f_string_static_t_initialize_1("127.hello", 0, 9),
+    macro_f_string_static_t_initialize_1("a.0.0.1", 0, 7),
+    macro_f_string_static_t_initialize_1("a.b", 0, 9),
+    macro_f_string_static_t_initialize_1("127.0.0.1/1234", 0, 14),
+    macro_f_string_static_t_initialize_1("127.0.0.1/2a", 0, 12),
+    macro_f_string_static_t_initialize_1(".", 0, 1),
+    macro_f_string_static_t_initialize_1("..", 0, 2),
+    macro_f_string_static_t_initialize_1(".127.0.0.1", 0, 10),
+  };
+
+  for (uint8_t i = 0; i < 17; ++i) {
+
+    state_data.status = F_none;
+
+    f_network_is_ip_address(ips[i], 0, &state_data);
+
+    assert_int_equal(state_data.status, F_network_version_four_not);
+  } // for
+}
+
+void test__f_network_is_ip_address__returns_network_version_six_not(void **state) {
+
+  f_state_t state_data = f_state_t_initialize;
+
+  const f_string_static_t ips[] = {
+    macro_f_string_static_t_initialize_1(":", 0, 1),
+    macro_f_string_static_t_initialize_1("::", 0, 2),
+    macro_f_string_static_t_initialize_1(":/24", 0, 4),
+    macro_f_string_static_t_initialize_1("::/24", 0, 5),
+    macro_f_string_static_t_initialize_1(":/2a", 0, 4),
+    macro_f_string_static_t_initialize_1("::/2a", 0, 5),
+    macro_f_string_static_t_initialize_1(":12345", 0, 6),
+    macro_f_string_static_t_initialize_1("::12345", 0, 7),
+    macro_f_string_static_t_initialize_1(":12345/24", 0, 9),
+    macro_f_string_static_t_initialize_1("::12345/24", 0, 10),
+    macro_f_string_static_t_initialize_1(":12345/2a", 0, 9),
+    macro_f_string_static_t_initialize_1("::12345/2a", 0, 10),
+    macro_f_string_static_t_initialize_1("::1/1234", 0, 8),
+    macro_f_string_static_t_initialize_1("::1/12a", 0, 7),
+    macro_f_string_static_t_initialize_1(":example", 0, 8),
+    macro_f_string_static_t_initialize_1("::example", 0, 9),
+    macro_f_string_static_t_initialize_1(":example/24", 0, 11),
+    macro_f_string_static_t_initialize_1("::example/24", 0, 12),
+    macro_f_string_static_t_initialize_1(":example/2a", 0, 11),
+    macro_f_string_static_t_initialize_1("::example/2a", 0, 12),
+    macro_f_string_static_t_initialize_1("[:", 0, 1),
+    macro_f_string_static_t_initialize_1("[::", 0, 2),
+    macro_f_string_static_t_initialize_1("[:/24", 0, 4),
+    macro_f_string_static_t_initialize_1("[::/24", 0, 5),
+    macro_f_string_static_t_initialize_1("[:/2a", 0, 4),
+    macro_f_string_static_t_initialize_1("[::/2a", 0, 5),
+    macro_f_string_static_t_initialize_1("[:1/24", 0, 5),
+    macro_f_string_static_t_initialize_1("[::1/24", 0, 6),
+    macro_f_string_static_t_initialize_1("[:12345", 0, 6),
+    macro_f_string_static_t_initialize_1("[::12345", 0, 7),
+    macro_f_string_static_t_initialize_1("[:12345/24", 0, 9),
+    macro_f_string_static_t_initialize_1("[::12345/24", 0, 10),
+    macro_f_string_static_t_initialize_1("[::1/1234", 0, 8),
+    macro_f_string_static_t_initialize_1("[::1", 0, 4),
+    macro_f_string_static_t_initialize_1("[::1/24", 0, 7),
+    macro_f_string_static_t_initialize_1("[:example", 0, 9),
+    macro_f_string_static_t_initialize_1("[::example", 0, 10),
+    macro_f_string_static_t_initialize_1("[:example/24", 0, 12),
+    macro_f_string_static_t_initialize_1("[::example/24", 0, 13),
+    macro_f_string_static_t_initialize_1("[:example/2a", 0, 12),
+    macro_f_string_static_t_initialize_1("[::example/2a", 0, 13),
+    macro_f_string_static_t_initialize_1("[:]", 0, 1),
+    macro_f_string_static_t_initialize_1("[:1/24]", 0, 7),
+    macro_f_string_static_t_initialize_1("[:1]/24", 0, 7),
+    macro_f_string_static_t_initialize_1("[:1/2a]", 0, 7),
+    macro_f_string_static_t_initialize_1("[:1]/2a", 0, 7),
+    macro_f_string_static_t_initialize_1("[::]", 0, 2),
+    macro_f_string_static_t_initialize_1("[::1]/24", 0, 8),
+    macro_f_string_static_t_initialize_1("[::1]/2a", 0, 8),
+    macro_f_string_static_t_initialize_1("[:/24]", 0, 4),
+    macro_f_string_static_t_initialize_1("[:/2a]", 0, 4),
+    macro_f_string_static_t_initialize_1("[::/24]", 0, 5),
+    macro_f_string_static_t_initialize_1("[::/2a]", 0, 5),
+    macro_f_string_static_t_initialize_1("[:12345]", 0, 6),
+    macro_f_string_static_t_initialize_1("[::12345]", 0, 7),
+    macro_f_string_static_t_initialize_1("[:12345/24]", 0, 9),
+    macro_f_string_static_t_initialize_1("[::12345/24]", 0, 10),
+    macro_f_string_static_t_initialize_1("[:12345/2a]", 0, 9),
+    macro_f_string_static_t_initialize_1("[::12345/2a]", 0, 10),
+    macro_f_string_static_t_initialize_1("[::1/1234]", 0, 8),
+    macro_f_string_static_t_initialize_1("[:example]", 0, 10),
+    macro_f_string_static_t_initialize_1("[::example]", 0, 11),
+    macro_f_string_static_t_initialize_1("[:example/24]", 0, 13),
+    macro_f_string_static_t_initialize_1("[::example/24]", 0, 14),
+    macro_f_string_static_t_initialize_1("[:example]/24", 0, 13),
+    macro_f_string_static_t_initialize_1("[::example]/24", 0, 14),
+    macro_f_string_static_t_initialize_1("[:example/2a]", 0, 13),
+    macro_f_string_static_t_initialize_1("[::example/2a]", 0, 14),
+    macro_f_string_static_t_initialize_1("[:example]/2a", 0, 13),
+    macro_f_string_static_t_initialize_1("[::example]/2a", 0, 14),
+    macro_f_string_static_t_initialize_1("1234::example", 0, 13),
+    macro_f_string_static_t_initialize_1("1234:example", 0, 12),
+    macro_f_string_static_t_initialize_1("1234:1234:1234:1234:1234:1234:1234:1234:1234", 0, 44),
+    macro_f_string_static_t_initialize_1("1234::1234:1234:1234:1234::1234:1234:1234", 0, 41),
+    macro_f_string_static_t_initialize_1("1234::1234::1234", 0, 16),
+    macro_f_string_static_t_initialize_1("a234:1234:1234:1234:1234:1234:1234:1234:1234", 0, 44),
+    macro_f_string_static_t_initialize_1("a234::1234:1234:1234:1234::1234:1234:1234", 0, 41),
+    macro_f_string_static_t_initialize_1("a234::1234::1234", 0, 16),
+    macro_f_string_static_t_initialize_1("1234:aaaa:1234:1234:1234:1234:1234:1234:1234", 0, 44),
+    macro_f_string_static_t_initialize_1("1234::aaaa:1234:1234:1234::1234:1234:1234", 0, 41),
+    macro_f_string_static_t_initialize_1("1234::aaaa::1234", 0, 16),
+    macro_f_string_static_t_initialize_1("1234::www.example.com", 0, 21),
+    macro_f_string_static_t_initialize_1("2001:0DB8:0:CD30:123:4567:89AB:CDEF/1234", 0, 40),
+    macro_f_string_static_t_initialize_1("2001:0DB8:0:CD30:123:4567:89AB:CDEF/2a", 0, 38),
+    macro_f_string_static_t_initialize_1("2001:0DB83:0:CD30:123:4567:89AB:CDEF", 0, 36),
+    macro_f_string_static_t_initialize_1("2001:0DB83:0:CD30:123:4567:89AB:CDEF/1234", 0, 41),
+    macro_f_string_static_t_initialize_1("[2001:0DB8:0:CD30:123:4567:89AB:CDEF/1234]", 0, 42),
+    macro_f_string_static_t_initialize_1("2001:0DB83:0:CD30:123:4567:89AB:CDEF/2a", 0, 39),
+    macro_f_string_static_t_initialize_1("[2001:0DB8:0:CD30:123:4567:89AB:CDEF/2a]", 0, 40),
+    macro_f_string_static_t_initialize_1("[2001:0DB83:0:CD30:123:4567:89AB:CDEF]", 0, 38),
+    macro_f_string_static_t_initialize_1("[2001:0DB83:0:CD30:123:4567:89AB:CDEF/1234]", 0, 43),
+    macro_f_string_static_t_initialize_1("[2001:0DB83:0:CD30:123:4567:89AB:CDEF/2a]", 0, 41),
+    macro_f_string_static_t_initialize_1("[2001:0DB8:0:CD30:123:4567:89AB:CDEF]a", 0, 38),
+    macro_f_string_static_t_initialize_1("[2001:0DB8:0:CD30:123:4567:89AB:CDEF]1", 0, 38),
+    macro_f_string_static_t_initialize_1("[2001:0DB8:0:CD30:123:4567:89AB:CDEF]hello", 0, 42),
+    macro_f_string_static_t_initialize_1("[2001:0DB8:0:CD30:123:4567:89AB:CDEF]/24", 0, 40),
+    macro_f_string_static_t_initialize_1("[2001:0DB8:0:CD30:123:4567:89AB:CDEF]/2a", 0, 40),
+    macro_f_string_static_t_initialize_1("[2001:0DB8:0:CD30:123:4567:89AB:CDEF]/1234", 0, 42),
+  };
+
+  for (uint8_t i = 0; i < 98; ++i) {
+
+    state_data.status = F_none;
+
+    f_network_is_ip_address(ips[i], 0, &state_data);
+
+    assert_int_equal(state_data.status, F_network_version_six_not);
+  } // for
+}
+
+void test__f_network_is_ip_address__returns_network_version_six(void **state) {
+
+  f_state_t state_data = f_state_t_initialize;
+
+  const f_string_static_t ips[] = {
+    macro_f_string_static_t_initialize_1("::1", 0, 3),
+    macro_f_string_static_t_initialize_1("[::1]", 0, 5),
+    macro_f_string_static_t_initialize_1("[::1]:80", 0, 8),
+    macro_f_string_static_t_initialize_1("::1/24", 0, 6),
+    macro_f_string_static_t_initialize_1("[::1/24]", 0, 8),
+    macro_f_string_static_t_initialize_1("[::1/24]:80", 0, 11),
+    macro_f_string_static_t_initialize_1("2001:0DB8:0:CD30:123:4567:89AB:CDEF", 0, 35),
+    macro_f_string_static_t_initialize_1("[2001:0DB8:0:CD30:123:4567:89AB:CDEF]", 0, 37),
+    macro_f_string_static_t_initialize_1("[2001:0DB8:0:CD30:123:4567:89AB:CDEF]:80", 0, 40),
+    macro_f_string_static_t_initialize_1("2001:0DB8:0:CD30:123:4567::CDEF", 0, 31),
+    macro_f_string_static_t_initialize_1("[2001:0DB8:0:CD30:123:4567::CDEF]", 0, 33),
+    macro_f_string_static_t_initialize_1("[2001:0DB8:0:CD30:123:4567::CDEF]:80", 0, 36),
+    macro_f_string_static_t_initialize_1("2001:0DB8:0:CD30:123::CDEF", 0, 26),
+    macro_f_string_static_t_initialize_1("[2001:0DB8:0:CD30:123::CDEF]", 0, 28),
+    macro_f_string_static_t_initialize_1("[2001:0DB8:0:CD30:123::CDEF]:80", 0, 31),
+    macro_f_string_static_t_initialize_1("2001:0DB8:0:CD30::CDEF", 0, 22),
+    macro_f_string_static_t_initialize_1("[2001:0DB8:0:CD30::CDEF]", 0, 24),
+    macro_f_string_static_t_initialize_1("[2001:0DB8:0:CD30::CDEF]:80", 0, 27),
+    macro_f_string_static_t_initialize_1("2001:0DB8:0::CDEF", 0, 17),
+    macro_f_string_static_t_initialize_1("[2001:0DB8:0::CDEF]", 0, 19),
+    macro_f_string_static_t_initialize_1("[2001:0DB8:0::CDEF]:80", 0, 22),
+    macro_f_string_static_t_initialize_1("2001:0DB8::CDEF", 0, 15),
+    macro_f_string_static_t_initialize_1("[2001:0DB8::CDEF]", 0, 17),
+    macro_f_string_static_t_initialize_1("[2001:0DB8::CDEF]:80", 0, 20),
+    macro_f_string_static_t_initialize_1("2001::CDEF", 0, 10),
+    macro_f_string_static_t_initialize_1("[2001::CDEF]", 0, 12),
+    macro_f_string_static_t_initialize_1("[2001::CDEF]:80", 0, 15),
+    macro_f_string_static_t_initialize_1("2001::0DB8:CDEF", 0, 15),
+    macro_f_string_static_t_initialize_1("[2001::0DB8:CDEF]", 0, 17),
+    macro_f_string_static_t_initialize_1("[2001::0DB8:CDEF]:80", 0, 20),
+    macro_f_string_static_t_initialize_1("2001:0DB8::0:CDEF", 0, 17),
+    macro_f_string_static_t_initialize_1("[2001:0DB8::0:CDEF]", 0, 19),
+    macro_f_string_static_t_initialize_1("[2001:0DB8::0:CDEF]:80", 0, 22),
+    macro_f_string_static_t_initialize_1("2001::0DB8:0:CD30:CDEF", 0, 22),
+    macro_f_string_static_t_initialize_1("[2001::0DB8:0:CD30:CDEF]", 0, 24),
+    macro_f_string_static_t_initialize_1("[2001::0DB8:0:CD30:CDEF]:80", 0, 27),
+    macro_f_string_static_t_initialize_1("2001:0DB8:0:CD30::123:CDEF", 0, 26),
+    macro_f_string_static_t_initialize_1("[2001:0DB8:0:CD30::123:CDEF]", 0, 28),
+    macro_f_string_static_t_initialize_1("[2001:0DB8:0:CD30::123:CDEF]:80", 0, 31),
+    macro_f_string_static_t_initialize_1("2001:0DB8:0:CD30::123:4567:CDEF", 0, 31),
+    macro_f_string_static_t_initialize_1("[2001:0DB8:0:CD30::123:4567:CDEF]", 0, 33),
+    macro_f_string_static_t_initialize_1("[2001:0DB8:0:CD30::123:4567:CDEF]:80", 0, 36),
+  };
+
+  const f_number_unsigned_t ports[] = {
+    0,
+    0,
+    6,
+    0,
+    0,
+    9,
+    0,
+    0,
+    38,
+    0,
+    0,
+    34,
+    0,
+    0,
+    29,
+    0,
+    0,
+    25,
+    0,
+    0,
+    20,
+    0,
+    0,
+    18,
+    0,
+    0,
+    13,
+    0,
+    0,
+    18,
+    0,
+    0,
+    20,
+    0,
+    0,
+    25,
+    0,
+    0,
+    29,
+    0,
+    0,
+    34,
+  };
+
+  f_number_unsigned_t port = 0;
+
+  for (uint8_t i = 0; i < 42; ++i) {
+
+    state_data.status = F_none;
+    port = 100000;
+
+    f_network_is_ip_address(ips[i], &port, &state_data);
+
+    assert_int_equal(state_data.status, F_network_version_six);
+    assert_int_equal(port, ports[i]);
+  } // for
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/level_0/f_network/tests/unit/c/test-network-is_ip_address.h b/level_0/f_network/tests/unit/c/test-network-is_ip_address.h
new file mode 100644 (file)
index 0000000..b27b3d8
--- /dev/null
@@ -0,0 +1,55 @@
+/**
+ * FLL - Level 0
+ *
+ * Project: Network
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Test the network project.
+ */
+#ifndef _TEST__F_network_is_ip_address_h
+#define _TEST__F_network_is_ip_address_h
+
+/**
+ * Test that function works but address.used is 0.
+ *
+ * @see f_network_is_ip_address()
+ */
+extern void test__f_network_is_ip_address__returns_data_not(void **state);
+
+/**
+ * Test that function works and returns F_false.
+ *
+ * @see f_network_is_ip_address()
+ */
+extern void test__f_network_is_ip_address__returns_false(void **state);
+
+/**
+ * Test that function works and returns F_network_version_four.
+ *
+ * @see f_network_is_ip_address()
+ */
+extern void test__f_network_is_ip_address__returns_network_version_four(void **state);
+
+/**
+ * Test that function works and returns F_network_version_four_not.
+ *
+ * @see f_network_is_ip_address()
+ */
+extern void test__f_network_is_ip_address__returns_network_version_four_not(void **state);
+
+/**
+ * Test that function works and returns F_network_version_six.
+ *
+ * @see f_network_is_ip_address()
+ */
+extern void test__f_network_is_ip_address__returns_network_version_six(void **state);
+
+/**
+ * Test that function works and returns F_network_version_six_not.
+ *
+ * @see f_network_is_ip_address()
+ */
+extern void test__f_network_is_ip_address__returns_network_version_six_not(void **state);
+
+#endif // _TEST__F_network_is_ip_address_h
index 797dbf51d18fb14a4315b9097e0122ff9d94a478..c56d1b521d44bc675e9fdbee9ab01e1aefb9316c 100644 (file)
@@ -22,7 +22,6 @@ int main(void) {
     cmocka_unit_test(test__f_network_from_ip_address__returns_data_not),
     cmocka_unit_test(test__f_network_from_ip_name__returns_data_not),
     cmocka_unit_test(test__f_network_from_ip_string__returns_data_not),
-
     cmocka_unit_test(test__f_network_to_ip_string__returns_data_not),
 
     cmocka_unit_test(test__f_network_from_host_long__works),
@@ -35,12 +34,20 @@ int main(void) {
     cmocka_unit_test(test__f_network_to_host_short__works),
     cmocka_unit_test(test__f_network_to_ip_string__works),
 
+    cmocka_unit_test(test__f_network_is_ip_address__returns_data_not),
+    cmocka_unit_test(test__f_network_is_ip_address__returns_false),
+    cmocka_unit_test(test__f_network_is_ip_address__returns_network_version_four),
+    cmocka_unit_test(test__f_network_is_ip_address__returns_network_version_four_not),
+    cmocka_unit_test(test__f_network_is_ip_address__returns_network_version_six),
+    cmocka_unit_test(test__f_network_is_ip_address__returns_network_version_six_not),
+
     #ifndef _di_level_0_parameter_checking_
       cmocka_unit_test(test__f_network_from_host_long__parameter_checking),
       cmocka_unit_test(test__f_network_from_host_short__parameter_checking),
       cmocka_unit_test(test__f_network_from_ip_address__parameter_checking),
       cmocka_unit_test(test__f_network_from_ip_name__parameter_checking),
       cmocka_unit_test(test__f_network_from_ip_string__parameter_checking),
+      // f_network_is_ip_address() doesn't use parameter checking.
       cmocka_unit_test(test__f_network_to_host_long__parameter_checking),
       cmocka_unit_test(test__f_network_to_host_short__parameter_checking),
       cmocka_unit_test(test__f_network_to_ip_string__parameter_checking),
index ecb3d18cd836cd30dd50990cbbca033da685492d..da66efc5274405ef3e2b6e1e58bb69bd9559de37 100644 (file)
@@ -33,6 +33,7 @@
 #include "test-network-from_ip_address.h"
 #include "test-network-from_ip_name.h"
 #include "test-network-from_ip_string.h"
+#include "test-network-is_ip_address.h"
 #include "test-network-to_host_long.h"
 #include "test-network-to_host_short.h"
 #include "test-network-to_ip_string.h"