diff --git a/sys/include/net/utils.h b/sys/include/net/utils.h index 662c3704b0..6a776fad8f 100644 --- a/sys/include/net/utils.h +++ b/sys/include/net/utils.h @@ -16,6 +16,7 @@ * @brief Common network interface API definitions * * @author Benjamin Valentin + * @author Hendrik van Essen */ #ifndef NET_UTILS_H @@ -24,6 +25,7 @@ #include #include +#include "net/ipv4/addr.h" #include "net/ipv6/addr.h" #include "net/netif.h" @@ -31,6 +33,18 @@ extern "C" { #endif +/** + * @brief Parse an IPv4 address / hostname string. + * If the @ref net_sock_dns module is used, this will + * attempt to resolve hostnames via DNS to IPv4 addresses. + * + * @param[out] addr IPv4 address of the host + * @param[in] hostname IPv4 address string or hostname + * + * @return 0 on success, error otherwise + */ +int netutils_get_ipv4(ipv4_addr_t *addr, const char *hostname); + /** * @brief Parse an IPv6 address / hostname string. * If the @ref net_sock_dns module is used, this will diff --git a/sys/net/netutils/util.c b/sys/net/netutils/util.c index ccfcdf86b3..defc428086 100644 --- a/sys/net/netutils/util.c +++ b/sys/net/netutils/util.c @@ -11,6 +11,7 @@ * * @file * @author Benjamin Valentin + * @author Hendrik van Essen */ #include @@ -34,6 +35,40 @@ static bool _netif_get(netif_t **current_netif) return netif_iter(netif); } +int netutils_get_ipv4(ipv4_addr_t *addr, const char *hostname) +{ + if (hostname == NULL) { + return -EINVAL; + } + + for (size_t i = 0; i < strlen(hostname); i++) { + bool is_not_ipv4 = (hostname[i] < '0' || hostname[i] > '9') && hostname[i] != '.'; + +#ifdef MODULE_SOCK_DNS + /* once we see an invalid character for an IPv4 address try to + * resolve the hostname by DNS */ + if (is_not_ipv4) { + int res = sock_dns_query(hostname, addr, AF_INET); + if (res < 0) { + return res; + } + return 0; + } +#else + if (is_not_ipv4) { + return -EINVAL; + } +#endif + } + + size_t len = strlen(hostname); + if (ipv4_addr_from_buf(addr, hostname, len) == NULL) { + return -EINVAL; + } + + return 0; +} + int netutils_get_ipv6(ipv6_addr_t *addr, netif_t **netif, const char *hostname) { *netif = NULL; @@ -71,7 +106,7 @@ int netutils_get_ipv6(ipv6_addr_t *addr, netif_t **netif, const char *hostname) } if (ipv6_addr_from_buf(addr, hostname, len) == NULL) { - return -EINVAL; + return -EINVAL; } return 0; diff --git a/tests/netutils/Makefile b/tests/netutils/Makefile index 10c661b057..35734852c4 100644 --- a/tests/netutils/Makefile +++ b/tests/netutils/Makefile @@ -8,6 +8,9 @@ USEMODULE += embunit USEMODULE += gnrc_sock_udp USEMODULE += gnrc_ipv6 +USEMODULE += ipv4_addr +USEMODULE += ipv6_addr + # pretend to include sock_dns CFLAGS += -DMODULE_SOCK_DNS=1 diff --git a/tests/netutils/main.c b/tests/netutils/main.c index 728427f8f4..c026b095ea 100644 --- a/tests/netutils/main.c +++ b/tests/netutils/main.c @@ -121,7 +121,73 @@ static void test_ipv6_addr_from_str__success5(void) TEST_ASSERT(ipv6_addr_equal(&a, &address)); } -Test *tests_netutils_tests(void) +static void test_ipv4_addr_from_str__missing_parts(void) +{ + ipv4_addr_t address; + + TEST_ASSERT_EQUAL_INT(netutils_get_ipv4(&address, "1"), -EINVAL); + TEST_ASSERT_EQUAL_INT(netutils_get_ipv4(&address, "1."), -EINVAL); + TEST_ASSERT_EQUAL_INT(netutils_get_ipv4(&address, "1.2"), -EINVAL); + TEST_ASSERT_EQUAL_INT(netutils_get_ipv4(&address, "1.2."), -EINVAL); + TEST_ASSERT_EQUAL_INT(netutils_get_ipv4(&address, "1.2.3"), -EINVAL); + TEST_ASSERT_EQUAL_INT(netutils_get_ipv4(&address, "1.2.3."), -EINVAL); +} + +static void test_ipv4_addr_from_str__illegal_chars(void) +{ + ipv4_addr_t address; + + TEST_ASSERT_EQUAL_INT(netutils_get_ipv4(&address, ":-D"), -ENOTSUP); +} + +static void test_ipv4_addr_from_str__addr_NULL(void) +{ + ipv4_addr_t address; + + TEST_ASSERT_EQUAL_INT(netutils_get_ipv4(&address, NULL), -EINVAL); +} + +static void test_ipv4_addr_from_str__address_NULL(void) +{ + TEST_ASSERT_NULL(ipv4_addr_from_str(NULL, "1.2.3.4")); +} + +static void test_ipv4_addr_from_str__success(void) +{ + static const ipv4_addr_t a = { { 0x01, 0x02, 0x03, 0x04 } }; + ipv4_addr_t address; + + TEST_ASSERT_EQUAL_INT(netutils_get_ipv4(&address, "1.2.3.4"), 0); + TEST_ASSERT(ipv4_addr_equal(&a, &address)); +} + +static void test_ipv4_addr_from_str__success2(void) +{ + static const ipv4_addr_t a = { { 0x5d, 0xb8, 0xd8, 0x22 } }; + ipv4_addr_t address; + + TEST_ASSERT_EQUAL_INT(netutils_get_ipv4(&address, "example.com"), 0); + TEST_ASSERT(ipv4_addr_equal(&a, &address)); +} + +Test *tests_netutils_ipv4_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + /* IPv4 tests */ + new_TestFixture(test_ipv4_addr_from_str__missing_parts), + new_TestFixture(test_ipv4_addr_from_str__illegal_chars), + new_TestFixture(test_ipv4_addr_from_str__addr_NULL), + new_TestFixture(test_ipv4_addr_from_str__address_NULL), + new_TestFixture(test_ipv4_addr_from_str__success), + new_TestFixture(test_ipv4_addr_from_str__success2), + }; + + EMB_UNIT_TESTCALLER(ipv4_addr_tests, NULL, NULL, fixtures); + + return (Test *)&ipv4_addr_tests; +} + +Test *tests_netutils_ipv6_tests(void) { for (unsigned i = 0; i < ARRAY_SIZE(dummy_netif); ++i) { netif_register(&dummy_netif[i].netif); @@ -149,7 +215,8 @@ Test *tests_netutils_tests(void) int main(void) { TESTS_START(); - TESTS_RUN(tests_netutils_tests()); + TESTS_RUN(tests_netutils_ipv4_tests()); + TESTS_RUN(tests_netutils_ipv6_tests()); TESTS_END(); return 0; diff --git a/tests/netutils/mock_dns.c b/tests/netutils/mock_dns.c index 994047f1c2..35644005eb 100644 --- a/tests/netutils/mock_dns.c +++ b/tests/netutils/mock_dns.c @@ -17,26 +17,33 @@ #include #include "net/af.h" +#include "net/ipv4/addr.h" #include "net/ipv6/addr.h" #include "net/sock/dns.h" int sock_dns_query(const char *domain_name, void *addr_out, int family) { - const ipv6_addr_t a = { { + const ipv4_addr_t addr_ipv4 = { { 0x5d, 0xb8, 0xd8, 0x22 } }; + + const ipv6_addr_t addr_ipv6 = { { 0x26, 0x06, 0x28, 0x00, 0x02, 0x20, 0x00, 0x01, 0x02, 0x48, 0x18, 0x93, 0x25, 0xc8, 0x19, 0x46 } }; - if (family != AF_INET6) { - return -EAFNOSUPPORT; - } - if (strcmp(domain_name, "example.com")) { return -ENOTSUP; } - memcpy(addr_out, &a, sizeof(a)); - return 0; + switch (family) { + case AF_INET: + memcpy(addr_out, &addr_ipv4, sizeof(addr_ipv4)); + return 0; + case AF_INET6: + memcpy(addr_out, &addr_ipv6, sizeof(addr_ipv6)); + return 0; + default: + return -EAFNOSUPPORT; + } } /** @} */