diff --git a/include/net/net_ip.h b/include/net/net_ip.h index 0db514095a866..e68b9e93ffb87 100644 --- a/include/net/net_ip.h +++ b/include/net/net_ip.h @@ -869,6 +869,30 @@ int net_addr_pton(sa_family_t family, const char *src, void *dst); char *net_addr_ntop(sa_family_t family, const void *src, char *dst, size_t size); +/** + * @brief Parse a string that contains either IPv4 or IPv6 address + * and optional port, and store the information in user supplied + * sockaddr struct. + * + * @details Syntax of the IP address string: + * 192.0.2.1:80 + * 192.0.2.42 + * [2001:db8::1]:8080 + * [2001:db8::2] + * 2001:db::42 + * Note that the str_len parameter is used to restrict the amount of + * characters that are checked. If the string does not contain port + * number, then the port number in sockaddr is not modified. + * + * @param str String that contains the IP address. + * @param str_len Length of the string to be parsed. + * @param addr Pointer to user supplied struct sockaddr. + * + * @return True if parsing could be done, false otherwise. + */ +bool net_ipaddr_parse(const char *str, size_t str_len, + struct sockaddr *addr); + /** * @brief Compare TCP sequence numbers. * diff --git a/subsys/net/ip/utils.c b/subsys/net/ip/utils.c index 6741d7b49c50b..e4ad0ec664db2 100644 --- a/subsys/net/ip/utils.c +++ b/subsys/net/ip/utils.c @@ -518,3 +518,206 @@ bool net_header_fits(struct net_pkt *pkt, u8_t *hdr, size_t hdr_size) return false; } + +#if defined(CONFIG_NET_IPV6) || defined(CONFIG_NET_IPV4) +static bool convert_port(const char *buf, u16_t *port) +{ + unsigned long tmp; + char *endptr; + + tmp = strtoul(buf, &endptr, 10); + if ((endptr == buf && tmp == 0) || + !(*buf != '\0' && *endptr == '\0') || + ((unsigned long)(unsigned short)tmp != tmp)) { + return false; + } + + *port = tmp; + + return true; +} +#endif /* CONFIG_NET_IPV6 || CONFIG_NET_IPV4 */ + +#if defined(CONFIG_NET_IPV6) +static bool parse_ipv6(const char *str, size_t str_len, + struct sockaddr *addr, bool has_port) +{ + char *ptr = NULL; + struct in6_addr *addr6; + char ipaddr[INET6_ADDRSTRLEN + 1]; + int end, len, ret, i; + u16_t port; + + len = min(INET6_ADDRSTRLEN, str_len); + + for (i = 0; i < len; i++) { + if (!str[i]) { + len = i; + break; + } + } + + if (has_port) { + /* IPv6 address with port number */ + ptr = memchr(str, ']', len); + if (!ptr) { + return false; + } + + end = min(len, ptr - (str + 1)); + memcpy(ipaddr, str + 1, end); + } else { + end = len; + memcpy(ipaddr, str, end); + } + + ipaddr[end] = '\0'; + + addr6 = &net_sin6(addr)->sin6_addr; + + ret = net_addr_pton(AF_INET6, ipaddr, addr6); + if (ret < 0) { + return false; + } + + net_sin6(addr)->sin6_family = AF_INET6; + + if (!has_port) { + return true; + } + + if ((ptr + 1) < (str + str_len) && *(ptr + 1) == ':') { + len = str_len - end; + + /* Re-use the ipaddr buf for port conversion */ + memcpy(ipaddr, ptr + 2, len); + ipaddr[len] = '\0'; + + ret = convert_port(ipaddr, &port); + if (!ret) { + return false; + } + + net_sin6(addr)->sin6_port = htons(port); + + NET_DBG("IPv6 host %s port %d", + net_addr_ntop(AF_INET6, addr6, + ipaddr, sizeof(ipaddr) - 1), + port); + } else { + NET_DBG("IPv6 host %s", + net_addr_ntop(AF_INET6, addr6, + ipaddr, sizeof(ipaddr) - 1)); + } + + return true; +} +#endif /* CONFIG_NET_IPV6 */ + +#if defined(CONFIG_NET_IPV4) +static bool parse_ipv4(const char *str, size_t str_len, + struct sockaddr *addr, bool has_port) +{ + char *ptr = NULL; + char ipaddr[NET_IPV4_ADDR_LEN + 1]; + struct in_addr *addr4; + int end, len, ret, i; + u16_t port; + + len = min(NET_IPV4_ADDR_LEN, str_len); + + for (i = 0; i < len; i++) { + if (!str[i]) { + len = i; + break; + } + } + + if (has_port) { + /* IPv4 address with port number */ + ptr = memchr(str, ':', len); + if (!ptr) { + return false; + } + + end = min(len, ptr - str); + } else { + end = len; + } + + memcpy(ipaddr, str, end); + ipaddr[end] = '\0'; + + addr4 = &net_sin(addr)->sin_addr; + + ret = net_addr_pton(AF_INET, ipaddr, addr4); + if (ret < 0) { + return false; + } + + net_sin(addr)->sin_family = AF_INET; + + if (!has_port) { + return true; + } + + memcpy(ipaddr, ptr + 1, str_len - end); + ipaddr[str_len - end] = '\0'; + + ret = convert_port(ipaddr, &port); + if (!ret) { + return false; + } + + net_sin(addr)->sin_port = htons(port); + + NET_DBG("IPv4 host %s port %d", + net_addr_ntop(AF_INET, addr4, + ipaddr, sizeof(ipaddr) - 1), + port); + return true; +} +#endif /* CONFIG_NET_IPV4 */ + +bool net_ipaddr_parse(const char *str, size_t str_len, struct sockaddr *addr) +{ + int i, count; + + if (*str == '[') { +#if defined(CONFIG_NET_IPV6) + return parse_ipv6(str, str_len, addr, true); +#else + return false; +#endif /* CONFIG_NET_IPV6 */ + } + + for (count = i = 0; str[i] && i < str_len; i++) { + if (str[i] == ':') { + count++; + } + } + + if (count == 1) { +#if defined(CONFIG_NET_IPV4) + return parse_ipv4(str, str_len, addr, true); +#else + return false; +#endif /* CONFIG_NET_IPV4 */ + } + +#if defined(CONFIG_NET_IPV4) && defined(CONFIG_NET_IPV6) + if (!parse_ipv4(str, str_len, addr, false)) { + return parse_ipv6(str, str_len, addr, false); + } + + return true; +#endif + +#if defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_IPV6) + return parse_ipv4(str, str_len, addr, false); +#endif + +#if defined(CONFIG_NET_IPV6) && !defined(CONFIG_NET_IPV4) + return parse_ipv6(str, str_len, addr, false); +#endif +} diff --git a/subsys/net/lib/dns/resolve.c b/subsys/net/lib/dns/resolve.c index 555be1b77858c..e74fd29ac0099 100644 --- a/subsys/net/lib/dns/resolve.c +++ b/subsys/net/lib/dns/resolve.c @@ -88,7 +88,6 @@ int dns_resolve_init(struct dns_resolve_context *ctx, const char *servers[]) struct sockaddr *local_addr = NULL; socklen_t addr_len = 0; int i = 0, idx = 0; - u16_t port = 0; int ret, count; if (!ctx) { @@ -106,177 +105,26 @@ int dns_resolve_init(struct dns_resolve_context *ctx, const char *servers[]) memset(ctx, 0, sizeof(*ctx)); for (i = 0; i < CONFIG_DNS_RESOLVER_MAX_SERVERS && servers[i]; i++) { - int j; - char *ptr; - - if (*servers[i] == '[') { -#if defined(CONFIG_NET_IPV6) - /* IPv6 address with port number */ - struct in6_addr *addr; - char server[INET6_ADDRSTRLEN + 1]; - int end; - - ptr = strstr(servers[i], "]:"); - if (!ptr) { - continue; - } - - end = min(INET6_ADDRSTRLEN, ptr - (servers[i] + 1)); - memcpy(server, servers[i] + 1, end); - server[end] = '\0'; - - addr = &net_sin6(&ctx->servers[idx].dns_server)-> - sin6_addr; - - ret = net_addr_pton(AF_INET6, server, addr); - if (ret < 0 && ret != -EINVAL) { - return ret; - } - - if (ret == -EINVAL) { - continue; - } - - port = strtol(ptr + 2, NULL, 10); - - net_sin6(&ctx->servers[idx].dns_server)->sin6_port = - htons(port); - - NET_DBG("[%d] IPv6 server %s port %d", idx, server, - port); - - ctx->servers[idx++].dns_server.sa_family = AF_INET6; -#endif /* CONFIG_NET_IPV6 */ - - continue; - } - - count = j = 0; - while (servers[i][j]) { - if (servers[i][j] == ':') { - count++; - } - - j++; - } - - if (count == 1) { -#if defined(CONFIG_NET_IPV4) - /* IPv4 address with port number */ - char server[NET_IPV4_ADDR_LEN + 1]; - struct in_addr *addr; - int end; - - ptr = strstr(servers[i], ":"); - if (!ptr) { - continue; - } - - end = min(NET_IPV4_ADDR_LEN, ptr - servers[i]); - memcpy(server, servers[i], end); - server[end] = '\0'; - - addr = &net_sin(&ctx->servers[idx].dns_server)-> - sin_addr; - ret = net_addr_pton(AF_INET, server, addr); - if (ret < 0 && ret != -EINVAL) { - NET_ERR("Cannot set DNS server %s", server); - return ret; - } - - if (ret == -EINVAL) { - continue; - } - - port = strtol(ptr + 1, NULL, 10); - - net_sin(&ctx->servers[idx].dns_server)->sin_port = - htons(port); + struct sockaddr *addr = &ctx->servers[idx].dns_server; - NET_DBG("[%d] IPv4 server %s port %d", idx, server, - port); - - ctx->servers[idx++].dns_server.sa_family = AF_INET; -#endif /* CONFIG_NET_IPV4 */ + memset(addr, 0, sizeof(*addr)); + ret = net_ipaddr_parse(servers[i], strlen(servers[i]), addr); + if (!ret) { continue; } -#if defined(CONFIG_NET_IPV4) && defined(CONFIG_NET_IPV6) - /* First try IPv4 address */ - ret = net_addr_pton(AF_INET, servers[i], - &net_sin(&ctx->servers[idx].dns_server)-> - sin_addr); - if (ret < 0 && ret != -EINVAL) { - return ret; - } - - if (!ret) { - net_sin(&ctx->servers[idx].dns_server)->sin_port = - htons(53); - - NET_DBG("[%d] IPv4 server %s port %d", idx, servers[i], - 53); - - ctx->servers[idx++].dns_server.sa_family = AF_INET; - - } else if (ret == -EINVAL) { - /* Then the address must be IPv6 based */ - ret = net_addr_pton(AF_INET6, servers[i], - &net_sin6(&ctx->servers[idx].dns_server)-> - sin6_addr); - if (ret < 0 && ret != -EINVAL) { - return ret; + if (addr->sa_family == AF_INET) { + if (net_sin(addr)->sin_port == 0) { + net_sin(addr)->sin_port = htons(53); } - - if (ret == -EINVAL) { - continue; + } else { + if (net_sin6(addr)->sin6_port == 0) { + net_sin6(addr)->sin6_port = htons(53); } - - net_sin6(&ctx->servers[idx].dns_server)->sin6_port = - htons(53); - - NET_DBG("[%d] IPv6 server %s port %d", idx, servers[i], - 53); - - ctx->servers[idx++].dns_server.sa_family = AF_INET6; - } -#endif - -#if defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_IPV6) - ret = net_addr_pton(AF_INET, servers[i], - &net_sin(&ctx->servers[idx].dns_server)-> - sin_addr); - if (ret < 0 && ret != -EINVAL) { - return ret; } - if (ret == -EINVAL) { - continue; - } - - NET_DBG("[%d] IPv4 server %s port %d", idx, servers[i], 53); - - net_sin(&ctx->servers[idx].dns_server)->sin_port = htons(53); - ctx->servers[idx++].dns_server.sa_family = AF_INET; -#endif /* IPv4 && !IPv6 */ - -#if defined(CONFIG_NET_IPV6) && !defined(CONFIG_NET_IPV4) - ret = net_addr_pton(AF_INET6, servers[i], - &net_sin6(&ctx->servers[idx].dns_server)->sin6_addr); - if (ret < 0 && ret != -EINVAL) { - return ret; - } - - if (ret == -EINVAL) { - continue; - } - - NET_DBG("[%d] IPv6 server %s port %d", idx, servers[i], 53); - - net_sin6(&ctx->servers[idx].dns_server)->sin6_port = htons(53); - ctx->servers[idx++].dns_server.sa_family = AF_INET6; -#endif /* IPv6 && !IPv4 */ + idx++; } for (i = 0, count = 0; i < CONFIG_DNS_RESOLVER_MAX_SERVERS && @@ -762,6 +610,7 @@ int dns_resolve_name(struct dns_resolve_context *ctx, { struct net_buf *dns_data; struct net_buf *dns_qname = NULL; + struct sockaddr addr; int ret, i, j = 0; int failure = 0; @@ -775,6 +624,36 @@ int dns_resolve_name(struct dns_resolve_context *ctx, return -EINVAL; } + ret = net_ipaddr_parse(query, strlen(query), &addr); + if (ret) { + /* The query name was already in numeric form, no + * need to continue further. + */ + struct dns_addrinfo info = { 0 }; + + if (type == DNS_QUERY_TYPE_A) { + memcpy(net_sin(&info.ai_addr), net_sin(&addr), + sizeof(struct sockaddr_in)); + info.ai_family = AF_INET; + info.ai_addr.sa_family = AF_INET; + info.ai_addrlen = sizeof(struct sockaddr_in); + } else if (type == DNS_QUERY_TYPE_AAAA) { + memcpy(net_sin6(&info.ai_addr), net_sin6(&addr), + sizeof(struct sockaddr_in6)); + info.ai_family = AF_INET6; + info.ai_addr.sa_family = AF_INET6; + info.ai_addrlen = sizeof(struct sockaddr_in6); + } else { + goto try_resolve; + } + + cb(DNS_EAI_INPROGRESS, &info, user_data); + cb(DNS_EAI_ALLDONE, NULL, user_data); + + return 0; + } + +try_resolve: i = get_cb_slot(ctx); if (i < 0) { return -EAGAIN; diff --git a/tests/net/lib/dns_resolve/prj.conf b/tests/net/lib/dns_resolve/prj.conf index 1f0867622323f..7c0c58e2cb9ef 100644 --- a/tests/net/lib/dns_resolve/prj.conf +++ b/tests/net/lib/dns_resolve/prj.conf @@ -16,7 +16,7 @@ CONFIG_DNS_SERVER3="192.0.2.2:5353" CONFIG_DNS_SERVER4="[2001:db8::2]:5353" CONFIG_NET_LOG=y -CONFIG_SYS_LOG_NET_LEVEL=4 +CONFIG_SYS_LOG_NET_LEVEL=2 CONFIG_SYS_LOG_SHOW_COLOR=y #CONFIG_NET_DEBUG_DNS_RESOLVE=y @@ -29,3 +29,5 @@ CONFIG_NET_ARP=n CONFIG_PRINTK=y CONFIG_ZTEST=y + +CONFIG_MAIN_STACK_SIZE=1344 diff --git a/tests/net/lib/dns_resolve/src/main.c b/tests/net/lib/dns_resolve/src/main.c index dae6f04e185f8..423d64ac72c7a 100644 --- a/tests/net/lib/dns_resolve/src/main.c +++ b/tests/net/lib/dns_resolve/src/main.c @@ -30,6 +30,8 @@ #define NAME4 "4.zephyr.test" #define NAME6 "6.zephyr.test" +#define NAME_IPV4 "192.0.2.1" +#define NAME_IPV6 "2001:db8::1" #define DNS_TIMEOUT 500 /* ms */ @@ -37,6 +39,8 @@ /* Interface 1 addresses */ static struct in6_addr my_addr1 = { { { 0x20, 0x01, 0x0d, 0xb8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1 } } }; +static struct in6_addr my_addr3 = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0x1 } } }; /* Extra address is assigned to ll_addr */ static struct in6_addr ll_addr = { { { 0xfe, 0x80, 0x43, 0xb8, 0, 0, 0, 0, @@ -637,6 +641,101 @@ static void dns_query_ipv6(void) } } +struct expected_addr_status { + struct sockaddr addr; + int status1; + int status2; + const char *caller; +}; + +void dns_result_numeric_cb(enum dns_resolve_status status, + struct dns_addrinfo *info, + void *user_data) +{ + struct expected_addr_status *expected = user_data; + + if (status != expected->status1 && status != expected->status2) { + DBG("Result status %d\n", status); + DBG("Expected status1 %d\n", expected->status1); + DBG("Expected status2 %d\n", expected->status2); + DBG("Caller %s\n", expected->caller); + + zassert_true(false, "Invalid status"); + } + + if (info && info->ai_family == AF_INET) { + if (net_ipv4_addr_cmp(&net_sin(&info->ai_addr)->sin_addr, + &my_addr2) != true) { + zassert_true(false, "IPv4 address does not match"); + } + } + + if (info && info->ai_family == AF_INET6) { + if (net_ipv6_addr_cmp(&net_sin6(&info->ai_addr)->sin6_addr, + &my_addr3) != true) { + zassert_true(false, "IPv6 address does not match"); + } + } + + k_sem_give(&wait_data2); +} + +static void dns_query_ipv4_numeric(void) +{ + struct expected_addr_status status = { + .status1 = DNS_EAI_INPROGRESS, + .status2 = DNS_EAI_ALLDONE, + .caller = __func__, + }; + int ret; + + timeout_query = false; + + ret = dns_get_addr_info(NAME_IPV4, + DNS_QUERY_TYPE_A, + ¤t_dns_id, + dns_result_numeric_cb, + &status, + DNS_TIMEOUT); + zassert_equal(ret, 0, "Cannot create IPv4 numeric query"); + + DBG("Query id %u\n", current_dns_id); + + k_yield(); /* mandatory so that net_if send func gets to run */ + + if (k_sem_take(&wait_data2, WAIT_TIME)) { + zassert_true(false, "Timeout while waiting data"); + } +} + +static void dns_query_ipv6_numeric(void) +{ + struct expected_addr_status status = { + .status1 = DNS_EAI_INPROGRESS, + .status2 = DNS_EAI_ALLDONE, + .caller = __func__, + }; + int ret; + + timeout_query = false; + + ret = dns_get_addr_info(NAME_IPV6, + DNS_QUERY_TYPE_AAAA, + ¤t_dns_id, + dns_result_numeric_cb, + &status, + DNS_TIMEOUT); + zassert_equal(ret, 0, "Cannot create IPv6 query"); + + DBG("Query id %u\n", current_dns_id); + + k_yield(); /* mandatory so that net_if send func gets to run */ + + if (k_sem_take(&wait_data2, WAIT_TIME)) { + zassert_true(false, "Timeout while waiting data"); + } +} + void test_main(void) { ztest_test_suite(dns_tests, @@ -654,7 +753,9 @@ void test_main(void) ztest_unit_test(dns_query_ipv4_cancel), ztest_unit_test(dns_query_ipv6_cancel), ztest_unit_test(dns_query_ipv4), - ztest_unit_test(dns_query_ipv6)); + ztest_unit_test(dns_query_ipv6), + ztest_unit_test(dns_query_ipv4_numeric), + ztest_unit_test(dns_query_ipv6_numeric)); ztest_run_test_suite(dns_tests); } diff --git a/tests/net/utils/src/main.c b/tests/net/utils/src/main.c index 47d73ac1482be..ffc1e032e6d32 100644 --- a/tests/net/utils/src/main.c +++ b/tests/net/utils/src/main.c @@ -26,6 +26,7 @@ #define NET_LOG_ENABLED 1 #include "net_private.h" +#if defined(CONFIG_NET_IPV6) /* ICMPv6 frame (104 bytes) */ static const unsigned char pkt1[104] = { /* IPv6 header starts here */ @@ -103,7 +104,9 @@ static const unsigned char pkt3[199] = { 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* ........ */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96 /* ....... */ }; +#endif /* CONFIG_NET_IPV6 */ +#if defined(CONFIG_NET_IPV4) /* ICMP reply (98 bytes) */ static const unsigned char pkt4[98] = { /* Ethernet header starts here */ @@ -147,6 +150,7 @@ static const unsigned char pkt5[98] = { 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ 0x36, 0x37 /* 67 */ }; +#endif /* CONFIG_NET_IPV4 */ void run_tests(void) { @@ -155,7 +159,10 @@ void run_tests(void) struct net_pkt *pkt; struct net_buf *frag; u16_t chksum, orig_chksum; - int hdr_len, i, chunk, datalen, total = 0; + int hdr_len; + +#if defined(CONFIG_NET_IPV6) + int i, chunk, datalen, total = 0; /* Packet fits to one fragment */ pkt = net_pkt_get_reserve_rx(0, K_FOREVER); @@ -309,10 +316,12 @@ void run_tests(void) zassert_true(0, "exiting"); } net_pkt_unref(pkt); +#endif /* CONFIG_NET_IPV6 */ /* Another packet that fits to one fragment. * This one has ethernet header before IPv4 data. */ +#if defined(CONFIG_NET_IPV4) pkt = net_pkt_get_reserve_rx(0, K_FOREVER); frag = net_pkt_get_reserve_rx_data(sizeof(struct net_eth_hdr), K_FOREVER); @@ -367,6 +376,7 @@ void run_tests(void) zassert_true(0, "exiting"); } net_pkt_unref(pkt); +#endif /* CONFIG_NET_IPV4 */ } struct net_addr_test_data { @@ -743,10 +753,440 @@ void run_net_addr_tests(void) zassert_equal(pass, ARRAY_SIZE(tests), "test_net_addr error"); } +void run_addr_parse_tests(void) +{ + struct sockaddr addr; + bool ret; + int i; +#if defined(CONFIG_NET_IPV4) + static const struct { + const char *address; + int len; + struct sockaddr_in result; + bool verdict; + } parse_ipv4_entries[] = { + { + .address = "192.0.2.1:80", + .len = sizeof("192.0.2.1:80") - 1, + .result = { + .sin_family = AF_INET, + .sin_port = htons(80), + .sin_addr = { + .s4_addr[0] = 192, + .s4_addr[1] = 0, + .s4_addr[2] = 2, + .s4_addr[3] = 1 + } + }, + .verdict = true + }, + { + .address = "192.0.2.2", + .len = sizeof("192.0.2.2") - 1, + .result = { + .sin_family = AF_INET, + .sin_port = 0, + .sin_addr = { + .s4_addr[0] = 192, + .s4_addr[1] = 0, + .s4_addr[2] = 2, + .s4_addr[3] = 2 + } + }, + .verdict = true + }, + { + .address = "192.0.2.3/foobar", + .len = sizeof("192.0.2.3/foobar") - 8, + .result = { + .sin_family = AF_INET, + .sin_port = 0, + .sin_addr = { + .s4_addr[0] = 192, + .s4_addr[1] = 0, + .s4_addr[2] = 2, + .s4_addr[3] = 3 + } + }, + .verdict = true + }, + { + .address = "255.255.255.255:0", + .len = sizeof("255.255.255.255:0") - 1, + .result = { + .sin_family = AF_INET, + .sin_port = 0, + .sin_addr = { + .s4_addr[0] = 255, + .s4_addr[1] = 255, + .s4_addr[2] = 255, + .s4_addr[3] = 255 + } + }, + .verdict = true + }, + { + .address = "127.0.0.42:65535", + .len = sizeof("127.0.0.42:65535") - 1, + .result = { + .sin_family = AF_INET, + .sin_port = htons(65535), + .sin_addr = { + .s4_addr[0] = 127, + .s4_addr[1] = 0, + .s4_addr[2] = 0, + .s4_addr[3] = 42 + } + }, + .verdict = true + }, + { + .address = "192.0.2.3:80/foobar", + .len = sizeof("192.0.2.3:80/foobar") - 1, + .verdict = false + }, + { + .address = "192.168.1.1:65536/foobar", + .len = sizeof("192.168.1.1:65536") - 1, + .verdict = false + }, + { + .address = "192.0.2.3:80/foobar", + .len = sizeof("192.0.2.3") - 1, + .result = { + .sin_family = AF_INET, + .sin_port = 0, + .sin_addr = { + .s4_addr[0] = 192, + .s4_addr[1] = 0, + .s4_addr[2] = 2, + .s4_addr[3] = 3 + } + }, + .verdict = true + }, + { + .address = "192.0.2.3/foobar", + .len = sizeof("192.0.2.3/foobar") - 1, + .verdict = false + }, + { + .address = "192.0.2.3:80:80", + .len = sizeof("192.0.2.3:80:80") - 1, + .verdict = false + }, + { + .address = "192.0.2.1:80000", + .len = sizeof("192.0.2.1:80000") - 1, + .verdict = false + }, + { + .address = "192.168.0.1", + .len = sizeof("192.168.0.1:80000") - 1, + .result = { + .sin_family = AF_INET, + .sin_port = 0, + .sin_addr = { + .s4_addr[0] = 192, + .s4_addr[1] = 168, + .s4_addr[2] = 0, + .s4_addr[3] = 1 + } + }, + .verdict = true + }, + { + .address = "a.b.c.d", + .verdict = false + }, + }; +#endif +#if defined(CONFIG_NET_IPV6) + static const struct { + const char *address; + int len; + struct sockaddr_in6 result; + bool verdict; + } parse_ipv6_entries[] = { + { + .address = "[2001:db8::2]:80", + .len = sizeof("[2001:db8::2]:80") - 1, + .result = { + .sin6_family = AF_INET6, + .sin6_port = htons(80), + .sin6_addr = { + .s6_addr16[0] = ntohs(0x2001), + .s6_addr16[1] = ntohs(0xdb8), + .s6_addr16[3] = 0, + .s6_addr16[4] = 0, + .s6_addr16[5] = 0, + .s6_addr16[6] = 0, + .s6_addr16[7] = ntohs(2) + } + }, + .verdict = true + }, + { + .address = "[2001:db8::a]/barfoo", + .len = sizeof("[2001:db8::a]/barfoo") - 8, + .result = { + .sin6_family = AF_INET6, + .sin6_port = 0, + .sin6_addr = { + .s6_addr16[0] = ntohs(0x2001), + .s6_addr16[1] = ntohs(0xdb8), + .s6_addr16[3] = 0, + .s6_addr16[4] = 0, + .s6_addr16[5] = 0, + .s6_addr16[6] = 0, + .s6_addr16[7] = ntohs(0xa) + } + }, + .verdict = true + }, + { + .address = "[2001:db8::a]", + .len = sizeof("[2001:db8::a]") - 1, + .result = { + .sin6_family = AF_INET6, + .sin6_port = 0, + .sin6_addr = { + .s6_addr16[0] = ntohs(0x2001), + .s6_addr16[1] = ntohs(0xdb8), + .s6_addr16[3] = 0, + .s6_addr16[4] = 0, + .s6_addr16[5] = 0, + .s6_addr16[6] = 0, + .s6_addr16[7] = ntohs(0xa) + } + }, + .verdict = true + }, + { + .address = "[2001:db8:3:4:5:6:7:8]:65535", + .len = sizeof("[2001:db8:3:4:5:6:7:8]:65535") - 1, + .result = { + .sin6_family = AF_INET6, + .sin6_port = 65535, + .sin6_addr = { + .s6_addr16[0] = ntohs(0x2001), + .s6_addr16[1] = ntohs(0xdb8), + .s6_addr16[2] = ntohs(3), + .s6_addr16[3] = ntohs(4), + .s6_addr16[4] = ntohs(5), + .s6_addr16[5] = ntohs(6), + .s6_addr16[6] = ntohs(7), + .s6_addr16[7] = ntohs(8), + } + }, + .verdict = true + }, + { + .address = "[::]:0", + .len = sizeof("[::]:0") - 1, + .result = { + .sin6_family = AF_INET6, + .sin6_port = 0, + .sin6_addr = { + .s6_addr16[0] = 0, + .s6_addr16[1] = 0, + .s6_addr16[2] = 0, + .s6_addr16[3] = 0, + .s6_addr16[4] = 0, + .s6_addr16[5] = 0, + .s6_addr16[6] = 0, + .s6_addr16[7] = 0, + } + }, + .verdict = true + }, + { + .address = "2001:db8::42", + .len = sizeof("2001:db8::42") - 1, + .result = { + .sin6_family = AF_INET6, + .sin6_port = 0, + .sin6_addr = { + .s6_addr16[0] = ntohs(0x2001), + .s6_addr16[1] = ntohs(0xdb8), + .s6_addr16[3] = 0, + .s6_addr16[4] = 0, + .s6_addr16[5] = 0, + .s6_addr16[6] = 0, + .s6_addr16[7] = ntohs(0x42) + } + }, + .verdict = true + }, + { + .address = "[2001:db8::192.0.2.1]:80000", + .len = sizeof("[2001:db8::192.0.2.1]:80000") - 1, + .verdict = false + }, + { + .address = "[2001:db8::1]:80", + .len = sizeof("[2001:db8::1") - 1, + .verdict = false + }, + { + .address = "[2001:db8::1]:65536", + .len = sizeof("[2001:db8::1]:65536") - 1, + .verdict = false + }, + { + .address = "[2001:db8::1]:80", + .len = sizeof("2001:db8::1") - 1, + .verdict = false + }, + { + .address = "[2001:db8::1]:a", + .len = sizeof("[2001:db8::1]:a") - 1, + .verdict = false + }, + { + .address = "[2001:db8::1]:10-12", + .len = sizeof("[2001:db8::1]:10-12") - 1, + .verdict = false + }, + { + .address = "[2001:db8::]:80/url/continues", + .len = sizeof("[2001:db8::]") - 1, + .result = { + .sin6_family = AF_INET6, + .sin6_port = 0, + .sin6_addr = { + .s6_addr16[0] = ntohs(0x2001), + .s6_addr16[1] = ntohs(0xdb8), + .s6_addr16[3] = 0, + .s6_addr16[4] = 0, + .s6_addr16[5] = 0, + .s6_addr16[6] = 0, + .s6_addr16[7] = 0, + } + }, + .verdict = true + }, + { + .address = "[2001:db8::200]:080", + .len = sizeof("[2001:db8:433:2]:80000") - 1, + .result = { + .sin6_family = AF_INET6, + .sin6_port = htons(80), + .sin6_addr = { + .s6_addr16[0] = ntohs(0x2001), + .s6_addr16[1] = ntohs(0xdb8), + .s6_addr16[3] = 0, + .s6_addr16[4] = 0, + .s6_addr16[5] = 0, + .s6_addr16[6] = 0, + .s6_addr16[7] = ntohs(0x200) + } + }, + .verdict = true + }, + { + .address = "[2001:db8::]:8080/another/url", + .len = sizeof("[2001:db8::]:8080/another/url") - 1, + .verdict = false + }, + { + .address = "[2001:db8::1", + .len = sizeof("[2001:db8::1") - 1, + .verdict = false + }, + { + .address = "[2001:db8::1]:-1", + .len = sizeof("[2001:db8::1]:-1") - 1, + .verdict = false + }, + { + /* Valid although user probably did not mean this */ + .address = "2001:db8::1:80", + .len = sizeof("2001:db8::1:80") - 1, + .result = { + .sin6_family = AF_INET6, + .sin6_port = 0, + .sin6_addr = { + .s6_addr16[0] = ntohs(0x2001), + .s6_addr16[1] = ntohs(0xdb8), + .s6_addr16[3] = 0, + .s6_addr16[4] = 0, + .s6_addr16[5] = 0, + .s6_addr16[6] = ntohs(0x01), + .s6_addr16[7] = ntohs(0x80) + } + }, + .verdict = true + }, + }; +#endif + +#if defined(CONFIG_NET_IPV4) + for (i = 0; i < ARRAY_SIZE(parse_ipv4_entries) - 1; i++) { + memset(&addr, 0, sizeof(addr)); + + ret = net_ipaddr_parse( + parse_ipv4_entries[i].address, + parse_ipv4_entries[i].len, + &addr); + if (ret != parse_ipv4_entries[i].verdict) { + printk("IPv4 entry [%d] \"%s\" failed\n", i, + parse_ipv4_entries[i].address); + zassert_true(false, "failure"); + } + + if (ret == true) { + zassert_true( + net_ipv4_addr_cmp( + &net_sin(&addr)->sin_addr, + &parse_ipv4_entries[i].result.sin_addr), + parse_ipv4_entries[i].address); + zassert_true(net_sin(&addr)->sin_port == + parse_ipv4_entries[i].result.sin_port, + "IPv4 port"); + zassert_true(net_sin(&addr)->sin_family == + parse_ipv4_entries[i].result.sin_family, + "IPv4 family"); + } + } +#endif +#if defined(CONFIG_NET_IPV6) + for (i = 0; i < ARRAY_SIZE(parse_ipv6_entries) - 1; i++) { + memset(&addr, 0, sizeof(addr)); + + ret = net_ipaddr_parse( + parse_ipv6_entries[i].address, + parse_ipv6_entries[i].len, + &addr); + if (ret != parse_ipv6_entries[i].verdict) { + printk("IPv6 entry [%d] \"%s\" failed\n", i, + parse_ipv6_entries[i].address); + zassert_true(false, "failure"); + } + + if (ret == true) { + zassert_true( + net_ipv6_addr_cmp( + &net_sin6(&addr)->sin6_addr, + &parse_ipv6_entries[i].result.sin6_addr), + parse_ipv6_entries[i].address); + zassert_true(net_sin6(&addr)->sin6_port == + parse_ipv6_entries[i].result.sin6_port, + "IPv6 port"); + zassert_true(net_sin6(&addr)->sin6_family == + parse_ipv6_entries[i].result.sin6_family, + "IPv6 family"); + } + } +#endif +} + + void test_main(void) { ztest_test_suite(test_utils_fn, - ztest_unit_test(run_tests), - ztest_unit_test(run_net_addr_tests)); + ztest_unit_test(run_tests), + ztest_unit_test(run_net_addr_tests), + ztest_unit_test(run_addr_parse_tests)); ztest_run_test_suite(test_utils_fn); }