tests: net: getaddrinfo: Fix test to check the query is sent
As the getaddrinfo() test is suppose to be self contained, then we will need to install handler for the sent DNS query. After we receive the query in process_dns() thread, we verify that the query was sent properly. We do not return DNS reply back to the caller in this case as we do not want to create DNS server just for this simple test application. This can be changed later if we get DNS server functionality in Zephyr network stack. Fixes #15193 Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
This commit is contained in:
parent
3d8fe9a8fa
commit
960e6d9504
|
@ -4,5 +4,7 @@ cmake_minimum_required(VERSION 3.13.1)
|
|||
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
|
||||
project(getaddrinfo)
|
||||
|
||||
target_include_directories(app PRIVATE $ENV{ZEPHYR_BASE}/subsys/net/ip)
|
||||
target_include_directories(app PRIVATE $ENV{ZEPHYR_BASE}/subsys/net/lib/dns)
|
||||
FILE(GLOB app_sources src/*.c)
|
||||
target_sources(app PRIVATE ${app_sources})
|
||||
|
|
|
@ -15,16 +15,16 @@ CONFIG_TEST_RANDOM_GENERATOR=y
|
|||
|
||||
# Network address config
|
||||
CONFIG_NET_CONFIG_SETTINGS=y
|
||||
CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
|
||||
CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1"
|
||||
CONFIG_NET_CONFIG_MY_IPV4_ADDR="127.0.0.1"
|
||||
CONFIG_NET_CONFIG_MY_IPV6_ADDR="::1"
|
||||
|
||||
# Enable the DNS resolver
|
||||
CONFIG_DNS_RESOLVER=y
|
||||
CONFIG_DNS_SERVER_IP_ADDRESSES=y
|
||||
|
||||
# Use local dnsmasq server for testing
|
||||
CONFIG_DNS_SERVER1="[2001:db8::2]:15353"
|
||||
CONFIG_DNS_SERVER2="192.0.2.2:15353"
|
||||
# Use local server for testing.
|
||||
CONFIG_DNS_SERVER1="[::1]:15353"
|
||||
CONFIG_DNS_SERVER2="127.0.0.1:15353"
|
||||
|
||||
CONFIG_MAIN_STACK_SIZE=2048
|
||||
CONFIG_ZTEST=y
|
||||
|
@ -32,3 +32,8 @@ CONFIG_ZTEST=y
|
|||
# User mode requirements
|
||||
CONFIG_TEST_USERSPACE=y
|
||||
CONFIG_HEAP_MEM_POOL_SIZE=128
|
||||
|
||||
# We do not need neighbor discovery etc for this test
|
||||
CONFIG_NET_IPV6_DAD=n
|
||||
CONFIG_NET_IPV6_ND=n
|
||||
CONFIG_NET_IPV6_MLD=n
|
||||
|
|
|
@ -9,22 +9,235 @@ LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL);
|
|||
|
||||
#include <stdio.h>
|
||||
#include <ztest_assert.h>
|
||||
|
||||
#include <misc/mutex.h>
|
||||
#include <net/socket.h>
|
||||
#include <net/dns_resolve.h>
|
||||
#include <net/buf.h>
|
||||
|
||||
#include "../../socket_helpers.h"
|
||||
|
||||
#include "dns_pack.h"
|
||||
|
||||
#define QUERY_HOST "www.zephyrproject.org"
|
||||
|
||||
#define ANY_PORT 0
|
||||
#define MAX_BUF_SIZE 128
|
||||
#define STACK_SIZE 1024
|
||||
#define THREAD_PRIORITY K_PRIO_COOP(8)
|
||||
#define WAIT_TIME 250
|
||||
|
||||
static u8_t recv_buf[MAX_BUF_SIZE];
|
||||
|
||||
static int sock_v4;
|
||||
static int sock_v6;
|
||||
|
||||
static struct sockaddr_in addr_v4;
|
||||
static struct sockaddr_in6 addr_v6;
|
||||
|
||||
/* The mutex is there to wait the data to be received. */
|
||||
static ZTEST_BMEM SYS_MUTEX_DEFINE(wait_data);
|
||||
|
||||
NET_BUF_POOL_DEFINE(test_dns_msg_pool, 1, 512, 0, NULL);
|
||||
|
||||
static bool check_dns_query(u8_t *buf, int buf_len)
|
||||
{
|
||||
struct dns_msg_t dns_msg;
|
||||
struct net_buf *result;
|
||||
enum dns_rr_type qtype;
|
||||
enum dns_class qclass;
|
||||
int ret, queries;
|
||||
|
||||
/* Store the DNS query name into a temporary net_buf as that is
|
||||
* expected by dns_unpack_query() function. In this test we are
|
||||
* currently not sending any DNS response back as that is not
|
||||
* really needed by these tests.
|
||||
*/
|
||||
result = net_buf_alloc(&test_dns_msg_pool, K_FOREVER);
|
||||
if (!result) {
|
||||
ret = -ENOMEM;
|
||||
return false;
|
||||
}
|
||||
|
||||
dns_msg.msg = buf;
|
||||
dns_msg.msg_size = buf_len;
|
||||
|
||||
ret = mdns_unpack_query_header(&dns_msg, NULL);
|
||||
if (ret < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
queries = ret;
|
||||
|
||||
NET_DBG("Received %d %s", queries,
|
||||
queries > 1 ? "queries" : "query");
|
||||
|
||||
(void)memset(result->data, 0, net_buf_tailroom(result));
|
||||
result->len = 0U;
|
||||
|
||||
ret = dns_unpack_query(&dns_msg, result, &qtype, &qclass);
|
||||
if (ret < 0) {
|
||||
net_buf_unref(result);
|
||||
return false;
|
||||
}
|
||||
|
||||
NET_DBG("[%d] query %s/%s label %s (%d bytes)", queries,
|
||||
qtype == DNS_RR_TYPE_A ? "A" : "AAAA", "IN",
|
||||
log_strdup(result->data), ret);
|
||||
|
||||
/* In this test we are just checking if the query came to us in correct
|
||||
* form, we are not creating a DNS server implementation here.
|
||||
*/
|
||||
if (strncmp(result->data + 1, QUERY_HOST,
|
||||
sizeof(QUERY_HOST) - 1)) {
|
||||
net_buf_unref(result);
|
||||
return false;
|
||||
}
|
||||
|
||||
net_buf_unref(result);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int process_dns(void)
|
||||
{
|
||||
struct pollfd pollfds[2];
|
||||
struct sockaddr *addr;
|
||||
socklen_t addr_len;
|
||||
int ret, idx;
|
||||
|
||||
sys_mutex_lock(&wait_data, K_FOREVER);
|
||||
|
||||
NET_DBG("Waiting for IPv4 DNS packets on port %d",
|
||||
ntohs(addr_v4.sin_port));
|
||||
NET_DBG("Waiting for IPv6 DNS packets on port %d",
|
||||
ntohs(addr_v6.sin6_port));
|
||||
|
||||
while (true) {
|
||||
memset(pollfds, 0, sizeof(pollfds));
|
||||
pollfds[0].fd = sock_v4;
|
||||
pollfds[0].events = POLLIN;
|
||||
pollfds[1].fd = sock_v6;
|
||||
pollfds[1].events = POLLIN;
|
||||
|
||||
NET_DBG("Polling...");
|
||||
|
||||
ret = poll(pollfds, ARRAY_SIZE(pollfds), -1);
|
||||
if (ret <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(pollfds); idx++) {
|
||||
if (pollfds[idx].revents & POLLIN) {
|
||||
if (pollfds[idx].fd == sock_v4) {
|
||||
addr_len = sizeof(addr_v4);
|
||||
addr = (struct sockaddr *)&addr_v4;
|
||||
} else {
|
||||
addr_len = sizeof(addr_v6);
|
||||
addr = (struct sockaddr *)&addr_v6;
|
||||
}
|
||||
|
||||
ret = recvfrom(pollfds[idx].fd,
|
||||
recv_buf, sizeof(recv_buf), 0,
|
||||
addr, &addr_len);
|
||||
if (ret < 0) {
|
||||
/* Socket error */
|
||||
NET_ERR("DNS: Connection error (%d)",
|
||||
errno);
|
||||
break;
|
||||
}
|
||||
|
||||
NET_DBG("Received DNS query");
|
||||
|
||||
ret = check_dns_query(recv_buf,
|
||||
sizeof(recv_buf));
|
||||
if (ret) {
|
||||
sys_mutex_unlock(&wait_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
K_THREAD_DEFINE(dns_server_thread_id, STACK_SIZE,
|
||||
process_dns, NULL, NULL, NULL,
|
||||
THREAD_PRIORITY, 0, K_FOREVER);
|
||||
|
||||
void test_getaddrinfo_setup(void)
|
||||
{
|
||||
char str[INET6_ADDRSTRLEN], *addr_str;
|
||||
struct sockaddr addr;
|
||||
int ret;
|
||||
|
||||
ret = net_ipaddr_parse(CONFIG_DNS_SERVER1,
|
||||
sizeof(CONFIG_DNS_SERVER1) - 1,
|
||||
&addr);
|
||||
zassert_true(ret, "Cannot parse IP address %s", CONFIG_DNS_SERVER1);
|
||||
|
||||
if (addr.sa_family == AF_INET) {
|
||||
memcpy(&addr_v4, net_sin(&addr), sizeof(struct sockaddr_in));
|
||||
} else if (addr.sa_family == AF_INET6) {
|
||||
memcpy(&addr_v6, net_sin6(&addr), sizeof(struct sockaddr_in6));
|
||||
}
|
||||
|
||||
ret = net_ipaddr_parse(CONFIG_DNS_SERVER2,
|
||||
sizeof(CONFIG_DNS_SERVER2) - 1,
|
||||
&addr);
|
||||
zassert_true(ret, "Cannot parse IP address %s", CONFIG_DNS_SERVER2);
|
||||
|
||||
if (addr.sa_family == AF_INET) {
|
||||
memcpy(&addr_v4, net_sin(&addr), sizeof(struct sockaddr_in));
|
||||
} else if (addr.sa_family == AF_INET6) {
|
||||
memcpy(&addr_v6, net_sin6(&addr), sizeof(struct sockaddr_in6));
|
||||
}
|
||||
|
||||
addr_str = inet_ntop(AF_INET, &addr_v4.sin_addr, str, sizeof(str));
|
||||
NET_DBG("v4: [%s]:%d", addr_str, ntohs(addr_v4.sin_port));
|
||||
|
||||
sock_v4 = prepare_listen_sock_udp_v4(&addr_v4);
|
||||
zassert_true(sock_v4 >= 0, "Invalid IPv4 socket");
|
||||
|
||||
addr_str = inet_ntop(AF_INET6, &addr_v6.sin6_addr, str, sizeof(str));
|
||||
NET_DBG("v6: [%s]:%d", addr_str, ntohs(addr_v6.sin6_port));
|
||||
|
||||
sock_v6 = prepare_listen_sock_udp_v6(&addr_v6);
|
||||
zassert_true(sock_v6 >= 0, "Invalid IPv6 socket");
|
||||
|
||||
k_thread_start(dns_server_thread_id);
|
||||
|
||||
k_yield();
|
||||
}
|
||||
|
||||
void test_getaddrinfo_ok(void)
|
||||
{
|
||||
struct addrinfo *res = NULL;
|
||||
|
||||
/* This check simulates a local query that we will catch
|
||||
* in dns_process() function. So we do not check the res variable
|
||||
* as that will currently not contain anything useful. We just check
|
||||
* that the query triggered a function call to dns_process() function
|
||||
* and that it could parse the DNS query.
|
||||
*/
|
||||
(void)getaddrinfo(QUERY_HOST, NULL, NULL, &res);
|
||||
|
||||
if (sys_mutex_lock(&wait_data, WAIT_TIME)) {
|
||||
zassert_true(false, "Timeout DNS query not received");
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
}
|
||||
|
||||
void test_getaddrinfo_cancelled(void)
|
||||
{
|
||||
struct addrinfo *res = NULL;
|
||||
int ret;
|
||||
|
||||
ret = getaddrinfo("www.zephyrproject.org", NULL, NULL, &res);
|
||||
ret = getaddrinfo(QUERY_HOST, NULL, NULL, &res);
|
||||
|
||||
/* Without a local dnsmasq server this request will be canceled. */
|
||||
/* Without a local DNS server this request will be canceled. */
|
||||
zassert_equal(ret, DNS_EAI_CANCELED, "Invalid result");
|
||||
|
||||
/* With a local dnsmasq server this request shall return 0. */
|
||||
/* zassert_equal(ret, 0, "Invalid result"); */
|
||||
|
||||
freeaddrinfo(res);
|
||||
}
|
||||
|
||||
|
@ -47,7 +260,9 @@ void test_main(void)
|
|||
k_thread_system_pool_assign(k_current_get());
|
||||
|
||||
ztest_test_suite(socket_getaddrinfo,
|
||||
ztest_unit_test(test_getaddrinfo_setup),
|
||||
ztest_user_unit_test(test_getaddrinfo_ok),
|
||||
ztest_user_unit_test(test_getaddrinfo_cancelled),
|
||||
ztest_user_unit_test(test_getaddrinfo_no_host));
|
||||
|
||||
ztest_run_test_suite(socket_getaddrinfo);
|
||||
|
|
|
@ -4,4 +4,4 @@ common:
|
|||
tests:
|
||||
net.socket:
|
||||
min_ram: 21
|
||||
tags: net socket
|
||||
tags: net socket getaddrinfo
|
||||
|
|
|
@ -10,6 +10,44 @@
|
|||
|
||||
#define clear_buf(buf) memset(buf, 0, sizeof(buf))
|
||||
|
||||
static inline int prepare_listen_sock_udp_v4(struct sockaddr_in *addr)
|
||||
{
|
||||
int ret, sock;
|
||||
|
||||
zassert_not_null(addr, "null sockaddr");
|
||||
|
||||
ret = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
zassert_true(ret >= 0, "socket open failed");
|
||||
|
||||
sock = ret;
|
||||
|
||||
zassert_equal(addr->sin_family, AF_INET, "Invalid family");
|
||||
|
||||
ret = bind(sock, (struct sockaddr *)addr, sizeof(*addr));
|
||||
zassert_equal(ret, 0, "bind failed (%d/%d)", ret, errno);
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
static inline int prepare_listen_sock_udp_v6(struct sockaddr_in6 *addr)
|
||||
{
|
||||
int ret, sock;
|
||||
|
||||
zassert_not_null(addr, "null sockaddr");
|
||||
|
||||
ret = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
|
||||
zassert_true(ret >= 0, "socket open failed");
|
||||
|
||||
sock = ret;
|
||||
|
||||
zassert_equal(addr->sin6_family, AF_INET6, "Invalid family");
|
||||
|
||||
ret = bind(sock, (struct sockaddr *)addr, sizeof(*addr));
|
||||
zassert_equal(ret, 0, "bind failed (%d/%d)", ret, errno);
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
static inline void prepare_sock_udp_v4(const char *addr, u16_t port,
|
||||
int *sock, struct sockaddr_in *sockaddr)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue