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:
Jukka Rissanen 2019-04-05 14:11:44 +03:00 committed by Andrew Boie
parent 3d8fe9a8fa
commit 960e6d9504
5 changed files with 272 additions and 12 deletions

View file

@ -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})

View file

@ -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

View file

@ -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);

View file

@ -4,4 +4,4 @@ common:
tests:
net.socket:
min_ram: 21
tags: net socket
tags: net socket getaddrinfo

View file

@ -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)
{