Provide support for inet_ntop() and inet_pton().
String are handling in FLL via f_string_static_t and f_string_dynamic_t.
The functions f_network_to_ip_string() and f_network_from_ip_string() are written to convert to these string types.
This implements a custom f_network_family_ip_t type to pass the digit IP address.
The idea here is to help reduce how much the FLL API is locked into the "libc" or POSIX API.
The structures are allocated using "{ 0 }" to take advantage of the supposed relatively new feature that treats this as an initialize all to NULL.
build_sources_headers iki.h iki/common.h iki/data.h
build_sources_headers limit.h limit/set.h limit/value.h
build_sources_headers memory.h memory/structure.h memory/common.h
-build_sources_headers network.h
+build_sources_headers network.h network/common.h
build_sources_headers parse.h parse/utf.h
build_sources_headers path.h path/common.h
build_sources_headers pipe.h
build_sources_headers level_0/iki.h level_0/iki/common.h level_0/iki/data.h
build_sources_headers level_0/limit.h level_0/limit/set.h level_0/limit/value.h
build_sources_headers level_0/memory.h level_0/memory/structure.h level_0/memory/common.h
-build_sources_headers level_0/network.h
+build_sources_headers level_0/network.h network/common.h
build_sources_headers level_0/parse.h level_0/parse/utf.h
build_sources_headers level_0/path.h level_0/path/common.h
build_sources_headers level_0/pipe.h
#endif
#ifndef _di_f_network_from_host_long_
- f_status_t f_network_from_host_long(const uint32_t from, uint32_t *to) {
+ f_status_t f_network_from_host_long(const uint32_t from, uint32_t * const to) {
#ifndef _di_level_0_parameter_checking_
if (!to) return F_status_set_error(F_parameter);
#endif // _di_level_0_parameter_checking_
#endif // _di_f_network_from_host_long_
#ifndef _di_f_network_from_host_short_
- f_status_t f_network_from_host_short(const uint16_t from, uint16_t *to) {
+ f_status_t f_network_from_host_short(const uint16_t from, uint16_t * const to) {
#ifndef _di_level_0_parameter_checking_
if (!to) return F_status_set_error(F_parameter);
#endif // _di_level_0_parameter_checking_
}
#endif // _di_f_network_from_host_short_
+#ifndef _di_f_network_from_ip_string_
+ f_status_t f_network_from_ip_string(const f_string_static_t from, f_network_family_ip_t * const to) {
+ #ifndef _di_level_0_parameter_checking_
+ if (!to) return F_status_set_error(F_parameter);
+ #endif // _di_level_0_parameter_checking_
+
+ if (!from.used || to->type == f_network_family_none_e) return F_data_not;
+
+ if (inet_pton(to->type == f_network_family_ip_4_e ? AF_INET : AF_INET6, from.string, (void *) & to->address) == -1) {
+ if (errno == EAFNOSUPPORT) return F_status_set_error(F_support_not);
+
+ return F_status_set_error(F_failure);
+ }
+
+ return F_none;
+ }
+#endif // _di_f_network_from_ip_string_
+
#ifndef _di_f_network_to_host_long_
- f_status_t f_network_to_host_long(const uint32_t from, uint32_t *to) {
+ f_status_t f_network_to_host_long(const uint32_t from, uint32_t * const to) {
#ifndef _di_level_0_parameter_checking_
if (!to) return F_status_set_error(F_parameter);
#endif // _di_level_0_parameter_checking_
#endif // _di_f_network_to_host_long_
#ifndef _di_f_network_to_host_short_
- f_status_t f_network_to_host_short(const uint16_t from, uint16_t *to) {
+ f_status_t f_network_to_host_short(const uint16_t from, uint16_t * const to) {
#ifndef _di_level_0_parameter_checking_
if (!to) return F_status_set_error(F_parameter);
#endif // _di_level_0_parameter_checking_
}
#endif // _di_f_network_to_host_short_
+#ifndef _di_f_network_to_ip_string_
+ f_status_t f_network_to_ip_string(const f_network_family_ip_t from, f_string_dynamic_t * const to) {
+ #ifndef _di_level_0_parameter_checking_
+ if (!to) return F_status_set_error(F_parameter);
+ #endif // _di_level_0_parameter_checking_
+
+ if (!(from.type == f_network_family_ip_4_e || from.type == f_network_family_ip_6_e)) return F_data_not;
+
+ {
+ const f_status_t status = f_string_dynamic_increase_by((from.type == f_network_family_ip_4_e ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN) + 1, to);
+ if (F_status_is_error(status)) return status;
+ }
+
+ if (!inet_ntop(from.type == f_network_family_ip_4_e ? AF_INET : AF_INET6, (void *) & from.address, to->string + to->used, from.type == f_network_family_ip_4_e ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN)) {
+ if (errno == EAFNOSUPPORT) return F_status_set_error(F_support_not);
+ if (errno == ENOSPC) return F_status_set_error(F_space_not);
+
+ return F_status_set_error(F_failure);
+ }
+
+ while (to->used < to->size && to->string[to->used]) {
+ ++to->used;
+ } // while
+
+ // The string should be NULL terminated, but in case it is not, ensure one always exists.
+ to->string[to->used] = 0;
+
+ return F_none;
+ }
+#endif // _di_f_network_to_ip_string_
+
#ifdef __cplusplus
} // extern "C"
#endif
// FLL-0 includes.
#include <fll/level_0/type.h>
#include <fll/level_0/status.h>
+#include <fll/level_0/string.h>
// FLL-0 network includes.
+#include <fll/level_0/network/common.h>
#ifdef __cplusplus
extern "C" {
* @see htonl()
*/
#ifndef _di_f_network_from_host_long_
- extern f_status_t f_network_from_host_long(const uint32_t from, uint32_t *to);
+ extern f_status_t f_network_from_host_long(const uint32_t from, uint32_t * const to);
#endif // _di_f_network_from_host_long_
/**
* @see htons()
*/
#ifndef _di_f_network_from_host_short_
- extern f_status_t f_network_from_host_short(const uint16_t from, uint16_t *to);
+ extern f_status_t f_network_from_host_short(const uint16_t from, uint16_t * const to);
#endif // _di_f_network_from_host_short_
/**
+ * Convert from a human-friendly string into a network IP address digit.
+ *
+ * This is for the ip address and is not for the domain name.
+ *
+ * @param from
+ * The human-friendly ip address string.
+ * @param to
+ * The converted IP version 4 or version 6 family integer.
+ *
+ * @return
+ * F_none on success.
+ * F_data_not on success but there is nothing to convert (to.type is f_network_family_none_e or from.used is 0).
+ *
+ * F_parameter (with error bit) if a parameter is invalid.
+ * F_space_not (with error bit) if not enough space is available in to.string.
+ * F_support_not (with error bit) if an invalid address family type is passed to inet_pton().
+ * F_failure (with error bit) on any other error.
+ *
+ * @see inet_pton()
+ */
+#ifndef _di_f_network_from_ip_string_
+ extern f_status_t f_network_from_ip_string(const f_string_static_t from, f_network_family_ip_t * const to);
+#endif // _di_f_network_from_ip_string_
+
+/**
* Convert from network byte order to host byte order for an unsigned long integer.
*
* @param from
* @see ntohl()
*/
#ifndef _di_f_network_to_host_long_
- extern f_status_t f_network_to_host_long(const uint32_t from, uint32_t *to);
+ extern f_status_t f_network_to_host_long(const uint32_t from, uint32_t * const to);
#endif // _di_f_network_to_host_long_
/**
* @see ntohs()
*/
#ifndef _di_f_network_to_host_short_
- extern f_status_t f_network_to_host_short(const uint16_t from, uint16_t *to);
+ extern f_status_t f_network_to_host_short(const uint16_t from, uint16_t * const to);
#endif // _di_f_network_to_host_short_
+/**
+ * Convert from a network IP address digit into a human-friendly string.
+ *
+ * This is for the ip address and is not for the domain name.
+ *
+ * @param from
+ * The IP version 4 or version 6 family integer.
+ * @param to
+ * The converted human-friendly ip address string.
+ *
+ * @return
+ * F_none on success.
+ * F_data_not on success but there is nothing to convert (from.type is f_network_family_none_e).
+ *
+ * F_parameter (with error bit) if a parameter is invalid.
+ * F_space_not (with error bit) if not enough space is available in to.string.
+ * F_support_not (with error bit) if an invalid address family type is passed to inet_ntop().
+ * F_failure (with error bit) on any other error.
+ *
+ * Errors (with error bit) from: f_string_dynamic_increase_by()
+ *
+ * @see inet_ntop()
+ *
+ * @see f_string_dynamic_increase_by()
+ */
+#ifndef _di_f_network_to_ip_string_
+ extern f_status_t f_network_to_ip_string(const f_network_family_ip_t from, f_string_dynamic_t * const to);
+#endif // _di_f_network_to_ip_string_
+
#ifdef __cplusplus
} // extern "C"
#endif
--- /dev/null
+/**
+ * FLL - Level 0
+ *
+ * Project: Network
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Defines network common data.
+ *
+ * This is auto-included by network.h and should not need to be explicitly included.
+ */
+#ifndef _F_network_common_h
+#define _F_network_common_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Designate an ip address family.
+ *
+ * f_network_family_*_e:
+ * - none: Represents address family is not set.
+ * - ip_4: Represents IP address family version 4, similar to AF_INET.
+ * - ip_6: Represents IP address family version 6, similar to AF_INET6.
+ */
+#ifndef _di_f_network_family_e_
+ enum {
+ f_network_family_none_e = 0,
+ f_network_family_ip_4_e,
+ f_network_family_ip_6_e,
+ }; // enum
+#endif // _di_f_network_family_e_
+
+/**
+ * A union for representing either an IP version 4 or IP version 6 structure.
+ *
+ * v4: The IP version 4.
+ * v6: The IP version 6.
+ *
+ * macro_f_network_family_ip_4_or_6_t_*:
+ * - initialize_1: Specifically initialize the IP address version 4.
+ * - initialize_2: Specifically initialize the IP address version 6.
+ * - clear: Clear the union values.
+ */
+#ifndef _di_f_network_family_ip_4_or_6_t_
+ typedef union {
+ struct in_addr v4;
+ struct in6_addr v6;
+ } f_network_family_ip_4_or_6_t;
+
+ #define f_network_family_ip_4_or_6_t_initialize { 0 }
+
+ #define macro_f_network_family_ip_4_or_6_t_initialize_1(v4) { .v4 = v4 }
+ #define macro_f_network_family_ip_4_or_6_t_initialize_2(v6) { .v6 = v6 }
+
+ #define f_network_family_ip_4_or_6_t_clear(family_ip_4_or_6) \
+ family_ip_4_or_6.v4 = 0; \
+ family_ip_4_or_6.v6 = 0;
+#endif // _di_f_network_family_ip_4_or_6_t_
+
+/**
+ * A structure for managing an IP address version 4 or 6.
+ *
+ * type: The type, usually either one of f_network_family_ip_4_e or f_network_family_ip_6_e.
+ * address: The address data.
+ *
+ * macro_f_network_family_ip_4_or_6_t_*:
+ * - initialize_1: Specifically initialize the IP address version 4.
+ * - initialize_2: Specifically initialize the IP address version 6.
+ * - clear: Clear the union values.
+ */
+#ifndef _di_f_network_family_ip_t_
+ typedef struct {
+ uint8_t type;
+ f_network_family_ip_4_or_6_t address;
+ } f_network_family_ip_t;
+
+ #define f_network_family_ip_t_initialize { 0 }
+
+ #define macro_f_network_family_ip_t_initialize_1(v4) { f_network_family_ip_4_e, macro_f_network_family_ip_4_or_6_t_initialize_1(v4) }
+ #define macro_f_network_family_ip_t_initialize_2(v6) { f_network_family_ip_6_e, macro_f_network_family_ip_4_or_6_t_initialize_2(v6) }
+
+ #define f_network_family_ip_t_clear(family_ip) \
+ family_ip.type = f_network_family_none_e; \
+ f_network_family_ip_t_clear(family_ip.address)
+#endif // _di_f_network_family_ip_t_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _F_network_common_h
f_type
f_status
+f_memory
+f_string
build_language c
build_libraries -lc
-build_libraries-individual
+build_libraries-individual -lf_memory -lf_string
build_sources_library network.c
-build_sources_headers network.h
+build_sources_headers network.h network/common.h
build_script yes
build_shared yes
build_sources_library network.c ../../tests/unit/c/mock-network.c
-build_sources_headers network.h
+build_sources_headers network.h network/common.h
build_script yes
build_shared yes
# Inject mocks.
flags -Wl,--wrap=htonl
flags -Wl,--wrap=htons
+flags -Wl,--wrap=inet_ntop
+flags -Wl,--wrap=inet_pton
flags -Wl,--wrap=ntohl
flags -Wl,--wrap=ntohs
build_language c
build_libraries -lc -lcmocka
-build_libraries-individual -lf_network
+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-to_host_long.c test-network-to_host_short.c
+build_sources_program test-network-from_host_long.c test-network-from_host_short.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.c
build_script no
return mock_type(uint16_t);
}
+const char *__wrap_inet_ntop(int af, const void *src, char *dst, socklen_t size) {
+
+ const bool failure = mock_type(bool);
+
+ if (failure) {
+ errno = mock_type(int);
+
+ return 0;
+ }
+
+ char *string = mock_type(char *);
+ size_t total = mock_type(size_t);
+
+ if (dst) {
+ memcpy(dst, string, total);
+ dst[total] = 0;
+ }
+
+ return dst;
+}
+
uint32_t __wrap_ntohl(uint32_t netlong) {
return mock_type(uint32_t);
return mock_type(uint16_t);
}
+int __wrap_inet_pton(int af, const char *src, void *dst) {
+
+ const bool failure = mock_type(bool);
+
+ if (failure) {
+ errno = mock_type(int);
+
+ return -1;
+ }
+
+ return 0;
+}
+
#ifdef __cplusplus
} // extern "C"
#endif
#include <stddef.h>
#include <setjmp.h>
#include <stdint.h>
+#include <string.h>
// cmocka includes.
#include <cmocka.h>
extern uint32_t __wrap_htonl(uint32_t hostlong);
extern uint16_t __wrap_htons(uint16_t hostshort);
+const char *__wrap_inet_ntop(int af, const void *src, char *dst, socklen_t size);
extern uint32_t __wrap_ntohl(uint32_t netlong);
extern uint16_t __wrap_ntohs(uint16_t netshort);
+extern int __wrap_inet_pton(int af, const char *src, void *dst);
#ifdef __cplusplus
} // extern "C"
/**
* FLL - Level 0
*
- * Project: Limit
+ * Project: Network
* API Version: 0.7
* Licenses: lgpl-2.1-or-later
*
--- /dev/null
+#include "test-network.h"
+#include "test-network-from_ip_string.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void test__f_network_from_ip_string__fails(void **state) {
+
+ int errnos[] = {
+ EAFNOSUPPORT,
+ mock_errno_generic,
+ };
+
+ f_status_t statuss[] = {
+ F_support_not,
+ F_failure,
+ };
+
+ for (uint8_t i = 0; i < 2; ++i) {
+
+ f_network_family_ip_t family = f_network_family_ip_t_initialize;
+
+ will_return(__wrap_inet_pton, true);
+ will_return(__wrap_inet_pton, errnos[i]);
+
+ const f_status_t status = f_network_from_ip_string(f_string_empty_s, &family);
+
+ assert_int_equal(status, F_status_set_error(statuss[i]));
+ } // for
+}
+
+void test__f_network_from_ip_string__parameter_checking(void **state) {
+
+ {
+ const f_status_t status = f_network_from_ip_string(f_string_empty_s, 0);
+
+ assert_int_equal(status, F_status_set_error(F_parameter));
+ }
+}
+
+void test__f_network_from_ip_string__returns_data_not(void **state) {
+
+ f_network_family_ip_t family = f_network_family_ip_t_initialize;
+
+ const f_string_static_t ip = macro_f_string_static_t_initialize_1("127.0.0.1", 0, 9);
+
+ {
+ const f_status_t status = f_network_from_ip_string(ip, &family);
+
+ assert_int_equal(status, F_data_not);
+ }
+
+ family.type = f_network_family_ip_4_e;
+
+ {
+ const f_status_t status = f_network_from_ip_string(f_string_empty_s, &family);
+
+ assert_int_equal(status, F_data_not);
+ }
+}
+
+void test__f_network_from_ip_string__works(void **state) {
+
+ f_network_family_ip_t family = f_network_family_ip_t_initialize;
+ family.type = f_network_family_ip_4_e;
+
+ const f_string_static_t ip = macro_f_string_static_t_initialize_1("127.0.0.1", 0, 9);
+
+ {
+ will_return(__wrap_inet_pton, false);
+
+ const f_status_t status = f_network_from_ip_string(ip, &family);
+
+ assert_int_equal(status, F_none);
+ }
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 0
+ *
+ * Project: Network
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Test the network project.
+ */
+#ifndef _TEST__F_network_from_ip_string_h
+#define _TEST__F_network_from_ip_string_h
+
+/**
+ * Test that function fails.
+ *
+ * @see f_network_from_ip_string()
+ */
+extern void test__f_network_from_ip_string__fails(void **state);
+
+/**
+ * Test that parameter checking works as expected.
+ *
+ * @see f_network_from_ip_string()
+ */
+extern void test__f_network_from_ip_string__parameter_checking(void **state);
+
+/**
+ * Test that function works but the string is empty or the address.type is 0.
+ *
+ * @see f_network_from_ip_string()
+ */
+extern void test__f_network_from_ip_string__returns_data_not(void **state);
+
+/**
+ * Test that function works.
+ *
+ * @see f_network_from_ip_string()
+ */
+extern void test__f_network_from_ip_string__works(void **state);
+
+#endif // _TEST__F_network_from_ip_string_h
--- /dev/null
+#include "test-network.h"
+#include "test-network-to_ip_string.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void test__f_network_to_ip_string__fails(void **state) {
+
+ f_network_family_ip_t family = f_network_family_ip_t_initialize;
+ f_string_dynamic_t ip = f_string_dynamic_t_initialize;
+
+ int errnos[] = {
+ EAFNOSUPPORT,
+ ENOSPC,
+ mock_errno_generic,
+ };
+
+ f_status_t statuss[] = {
+ F_support_not,
+ F_space_not,
+ F_failure,
+ };
+
+ for (uint8_t i = 0; i < 3; ++i) {
+
+ will_return(__wrap_inet_pton, true);
+ will_return(__wrap_inet_pton, errnos[i]);
+
+ const f_status_t status = f_network_to_ip_string(family, &ip);
+
+ assert_int_equal(status, F_status_set_error(statuss[i]));
+ } // for
+
+ free(ip.string);
+}
+
+void test__f_network_to_ip_string__parameter_checking(void **state) {
+
+ f_network_family_ip_t family = f_network_family_ip_t_initialize;
+
+ {
+ const f_status_t status = f_network_to_ip_string(family, 0);
+
+ assert_int_equal(status, F_status_set_error(F_parameter));
+ }
+}
+
+void test__f_network_to_ip_string__returns_data_not(void **state) {
+
+ f_network_family_ip_t family = f_network_family_ip_t_initialize;
+ f_string_dynamic_t ip = f_string_dynamic_t_initialize;
+
+ {
+ const f_status_t status = f_network_to_ip_string(family, &ip);
+
+ assert_int_equal(status, F_data_not);
+ }
+
+ free(ip.string);
+}
+
+void test__f_network_to_ip_string__works(void **state) {
+
+ f_network_family_ip_t family = f_network_family_ip_t_initialize;
+ family.type = f_network_family_ip_4_e;
+
+ f_string_dynamic_t ip = f_string_dynamic_t_initialize;
+
+ const f_string_static_t expect = macro_f_string_static_t_initialize_1("127.0.0.1", 0, 9);
+
+ {
+ will_return(__wrap_inet_ntop, false);
+ will_return(__wrap_inet_ntop, expect.string);
+ will_return(__wrap_inet_ntop, expect.used);
+
+ const f_status_t status = f_network_to_ip_string(family, &ip);
+
+ assert_int_equal(status, F_none);
+ assert_int_equal(ip.used, expect.used);
+ assert_string_equal(ip.string, expect.string);
+ }
+
+ free(ip.string);
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 0
+ *
+ * Project: Network
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Test the network project.
+ */
+#ifndef _TEST__F_network_to_ip_string_h
+#define _TEST__F_network_to_ip_string_h
+
+/**
+ * Test that function fails.
+ *
+ * @see f_network_to_ip_string()
+ */
+extern void test__f_network_to_ip_string__fails(void **state);
+
+/**
+ * Test that parameter checking works as expected.
+ *
+ * @see f_network_to_ip_string()
+ */
+extern void test__f_network_to_ip_string__parameter_checking(void **state);
+
+/**
+ * Test that function works but the address.type is 0.
+ *
+ * @see f_network_to_ip_string()
+ */
+extern void test__f_network_to_ip_string__returns_data_not(void **state);
+
+/**
+ * Test that function works.
+ *
+ * @see f_network_to_ip_string()
+ */
+extern void test__f_network_to_ip_string__works(void **state);
+
+#endif // _TEST__F_network_to_ip_string_h
int main(void) {
const struct CMUnitTest tests[] = {
+ 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),
cmocka_unit_test(test__f_network_from_host_short__works),
+ cmocka_unit_test(test__f_network_from_ip_string__works),
cmocka_unit_test(test__f_network_to_host_long__works),
cmocka_unit_test(test__f_network_to_host_short__works),
+ cmocka_unit_test(test__f_network_to_ip_string__works),
#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_string__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),
#endif // _di_level_0_parameter_checking_
};
#include <cmocka.h>
// FLL-0 includes.
+#include <fll/level_0/string.h>
#include <fll/level_0/network.h>
// Mock includes.
// Test includes.
#include "test-network-from_host_long.h"
#include "test-network-from_host_short.h"
+#include "test-network-from_ip_string.h"
#include "test-network-to_host_long.h"
#include "test-network-to_host_short.h"
+#include "test-network-to_ip_string.h"
#ifdef __cplusplus
extern "C" {