From 2d16b66f1992e29712de2cb3686075b2bede57d9 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Fri, 5 Apr 2013 12:33:09 +0300 Subject: tools: Add unit test for dnsproxy --- tools/dnsproxy-unit.c | 463 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 463 insertions(+) create mode 100644 tools/dnsproxy-unit.c (limited to 'tools') diff --git a/tools/dnsproxy-unit.c b/tools/dnsproxy-unit.c new file mode 100644 index 00000000..1d0e66d2 --- /dev/null +++ b/tools/dnsproxy-unit.c @@ -0,0 +1,463 @@ +/* + * + * Connection Manager + * + * Copyright (C) 2013 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#define DEBUG +#endif +#ifdef DEBUG +#include + +#define LOG(fmt, arg...) do { \ + fprintf(stdout, "%s:%s() " fmt "\n", \ + __FILE__, __func__ , ## arg); \ +} while (0) +#else +#define LOG(fmt, arg...) +#endif + +unsigned char msg[] = { + 0x00, 0x1c, /* len 28 */ + 0x31, 0x82, /* tran id */ + 0x01, 0x00, /* flags (recursion required) */ + 0x00, 0x01, /* questions (1) */ + 0x00, 0x00, /* answer rr */ + 0x00, 0x00, /* authority rr */ + 0x00, 0x00, /* additional rr */ + 0x06, 0x6c, 0x6f, 0x6c, 0x67, 0x65, 0x30, /* lolge0, just some name */ + 0x03, 0x63, 0x6f, 0x6d, /* com */ + 0x00, /* null terminator */ + 0x00, 0x01, /* type A */ + 0x00, 0x01, /* class IN */ +}; + +unsigned char msg2[] = { + 0x00, 0x1c, /* len 28 */ + 0x31, 0x83, /* tran id */ + 0x01, 0x00, /* flags (recursion required) */ + 0x00, 0x01, /* questions (1) */ + 0x00, 0x00, /* answer rr */ + 0x00, 0x00, /* authority rr */ + 0x00, 0x00, /* additional rr */ + 0x06, 0x6c, 0x6f, 0x67, 0x6c, 0x67, 0x65, /* loelge */ + 0x03, 0x63, 0x6f, 0x6d, /* com */ + 0x00, /* null terminator */ + 0x00, 0x01, /* type A */ + 0x00, 0x01, /* class IN */ + + 0x00, 0x1c, /* len 28 */ + 0x31, 0x84, /* tran id */ + 0x01, 0x00, /* flags (recursion required) */ + 0x00, 0x01, /* questions (1) */ + 0x00, 0x00, /* answer rr */ + 0x00, 0x00, /* authority rr */ + 0x00, 0x00, /* additional rr */ + 0x06, 0x6c, 0x6f, 0x67, 0x6c, 0x67, 0x65, /* loelge */ + 0x03, 0x6e, 0x65, 0x74, /* net */ + 0x00, /* null terminator */ + 0x00, 0x01, /* type A */ + 0x00, 0x01, /* class IN */ +}; + +unsigned char msg_invalid[] = { + 0x00, 0x1c, /* len 28 */ + 0x31, 0xC0, /* tran id */ +}; + +static int create_tcp_socket(int family) +{ + int sk, err; + + sk = socket(family, SOCK_STREAM, IPPROTO_TCP); + if (sk < 0) { + err = errno; + LOG("Failed to create TCP socket %d/%s", err, strerror(err)); + return -err; + } + + return sk; +} + +static int create_udp_socket(int family) +{ + int sk, err; + + sk = socket(family, SOCK_DGRAM, IPPROTO_UDP); + if (sk < 0) { + err = errno; + LOG("Failed to create UDP socket %d/%s", err, strerror(err)); + return -err; + } + + return sk; +} + +static int connect_tcp_socket(char *server) +{ + int sk; + int err = 0; + + struct addrinfo hints, *rp; + + memset(&hints, 0, sizeof(hints)); + + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_NUMERICSERV | AI_NUMERICHOST; + getaddrinfo(server, "53", &hints, &rp); + + sk = create_tcp_socket(rp->ai_family); + err = sk; + + LOG("sk %d family %d", sk, rp->ai_family); + + if (sk >= 0 && connect(sk, rp->ai_addr, rp->ai_addrlen) < 0) { + err = -errno; + fprintf(stderr, "Failed to connect to DNS server at %s " + "errno %d/%s\n", + server, -err, strerror(-err)); + } + + if (sk >= 0) + fcntl(sk, F_SETFL, O_NONBLOCK); + + freeaddrinfo(rp); + + return err; +} + +static int send_msg(int sk, unsigned char *msg, unsigned int len, + int sleep_between) +{ + unsigned int i; + int err; + + for (i = 0; i < len; i++) { + err = write(sk, &msg[i], 1); + if (err < 0) { + err = -errno; + LOG("write failed errno %d/%s", -err, strerror(-err)); + } + + g_assert_cmpint(err, >=, 0); + if (err < 0) + return err; + + if (sleep_between) + usleep(1000); + } + + return 0; +} + +static int connect_udp_socket(char *server, struct sockaddr *sa, + socklen_t *sa_len) +{ + int sk; + int err = 0; + + struct addrinfo hints, *rp; + + memset(&hints, 0, sizeof(hints)); + + hints.ai_socktype = SOCK_DGRAM; + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_NUMERICSERV | AI_NUMERICHOST; + getaddrinfo(server, "53", &hints, &rp); + + sk = create_udp_socket(rp->ai_family); + err = sk; + + LOG("sk %d family %d", sk, rp->ai_family); + + if (sk < 0) { + err = -errno; + fprintf(stderr, "Failed to connect to DNS server at %s " + "errno %d/%s\n", + server, -err, strerror(-err)); + } else { + memcpy(sa, rp->ai_addr, *sa_len); + *sa_len = rp->ai_addrlen; + } + + freeaddrinfo(rp); + + return err; +} + +static int sendto_msg(int sk, struct sockaddr *sa, socklen_t salen, + unsigned char *msg, unsigned int len) +{ + int err; + + err = sendto(sk, msg, len, MSG_NOSIGNAL, sa, salen); + if (err < 0) { + err = -errno; + LOG("sendto failed errno %d/%s", -err, strerror(-err)); + } + + g_assert_cmpint(err, >=, 0); + if (err < 0) + return err; + + return 0; +} + +static unsigned short get_id() +{ + return random(); +} + +static void change_msg(unsigned char *msg, unsigned int tranid, + unsigned int host_loc, unsigned char host_count) +{ + unsigned short id = get_id(); + + msg[tranid] = id >> 8; + msg[tranid+1] = id; + msg[host_loc] = host_count; +} + +static void change_msg2(unsigned char *msg, unsigned char host_count) +{ + change_msg(msg, 2, 20, host_count); + change_msg(msg, 32, 50, host_count+1); +} + +static int receive_message(int sk, int timeout, int expected, int *server_closed) +{ + int ret, received = 0; + unsigned char buf[512]; + + while (timeout > 0) { + ret = read(sk, buf, sizeof(buf)); + if (ret > 0) { + LOG("received %d bytes from server %d", ret, sk); + received += ret; + if (received >= expected) + break; + } else if (ret == 0) { + LOG("server %d closed socket", sk); + if (server_closed) + *server_closed = 1; + break; + } else { + if (errno != EAGAIN && errno != EWOULDBLOCK) + break; + } + + sleep(1); + timeout--; + } + + return received; +} + +static int receive_from_message(int sk, struct sockaddr *sa, socklen_t len, + int timeout, int expected) +{ + int ret, received = 0; + unsigned char buf[512]; + + fcntl(sk, F_SETFL, O_NONBLOCK); + + while (timeout > 0) { + ret = recvfrom(sk, buf, sizeof(buf), 0, sa, &len); + if (ret > 0) { + LOG("received %d bytes from server %d", ret, sk); + received += ret; + if (received >= expected) + break; + } else if (ret < 0) { + if (errno != EAGAIN && errno != EWOULDBLOCK) + break; + } + + sleep(1); + timeout--; + } + + return received; +} + +static void test_ipv4_udp_msg(void) +{ + int sk, received = 0; + struct sockaddr_in sa; + socklen_t len = sizeof(sa); + + sk = connect_udp_socket("127.0.0.1", (struct sockaddr *)&sa, &len); + g_assert_cmpint(sk, >=, 0); + change_msg(msg, 2, 20, '1'); + sendto_msg(sk, (struct sockaddr *)&sa, len, msg, sizeof(msg)); + received = receive_from_message(sk, (struct sockaddr *)&sa, len, + 10, sizeof(msg)); + close(sk); + + g_assert_cmpint(received, >=, sizeof(msg)); +} + +static void test_ipv6_udp_msg(void) +{ + int sk, received = 0; + struct sockaddr_in6 sa; + socklen_t len = sizeof(sa); + + sk = connect_udp_socket("::1", (struct sockaddr *)&sa, &len); + g_assert_cmpint(sk, >=, 0); + change_msg(msg, 2, 20, '1'); + sendto_msg(sk, (struct sockaddr *)&sa, len, msg, sizeof(msg)); + received = receive_from_message(sk, (struct sockaddr *)&sa, len, + 10, sizeof(msg)); + close(sk); + + g_assert_cmpint(received, >=, sizeof(msg)); +} + +static void test_partial_ipv4_tcp_msg(void) +{ + int sk, received = 0; + + sk = connect_tcp_socket("127.0.0.1"); + g_assert_cmpint(sk, >=, 0); + change_msg(msg, 2, 20, '1'); + send_msg(sk, msg, sizeof(msg), 1); + received = receive_message(sk, 10, sizeof(msg), NULL); + close(sk); + + g_assert_cmpint(received, >=, sizeof(msg)); +} + +static void test_partial_ipv6_tcp_msg(void) +{ + int sk, received = 0; + + sk = connect_tcp_socket("::1"); + g_assert_cmpint(sk, >=, 0); + change_msg(msg, 2, 20, '2'); + send_msg(sk, msg, sizeof(msg), 1); + received = receive_message(sk, 10, sizeof(msg), NULL); + close(sk); + + g_assert_cmpint(received, >=, sizeof(msg)); +} + +static void test_multiple_ipv4_tcp_msg(void) +{ + int sk, received = 0, server_closed = 0; + + sk = connect_tcp_socket("127.0.0.1"); + g_assert_cmpint(sk, >=, 0); + change_msg2(msg2, '1'); + send_msg(sk, msg2, sizeof(msg2), 0); + received = receive_message(sk, 35, sizeof(msg2), &server_closed); + close(sk); + + /* If the final DNS server refuses to serve us, then do not consider + * it an error. This happens very often with unbound. + */ + if (server_closed == 0) + g_assert_cmpint(received, >=, sizeof(msg2)); +} + +static void test_multiple_ipv6_tcp_msg(void) +{ + int sk, received = 0, server_closed = 0; + + sk = connect_tcp_socket("::1"); + g_assert_cmpint(sk, >=, 0); + change_msg2(msg2, '2'); + send_msg(sk, msg2, sizeof(msg2), 0); + received = receive_message(sk, 35, sizeof(msg2), &server_closed); + close(sk); + + if (server_closed == 0) + g_assert_cmpint(received, >=, sizeof(msg2)); +} + +static void test_failure_tcp_msg(void) +{ + int sk, received = 0; + + sk = connect_tcp_socket("127.0.0.1"); + g_assert_cmpint(sk, >=, 0); + send_msg(sk, msg_invalid, sizeof(msg_invalid), 0); + received = receive_message(sk, 10, 0, NULL); + close(sk); + + g_assert_cmpint(received, ==, 0); +} + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/dnsproxy/ipv4 udp msg", + test_ipv4_udp_msg); + + g_test_add_func("/dnsproxy/ipv6 udp msg", + test_ipv6_udp_msg); + + g_test_add_func("/dnsproxy/failure tcp msg ", + test_failure_tcp_msg); + + g_test_add_func("/dnsproxy/partial ipv4 tcp msg", + test_partial_ipv4_tcp_msg); + + g_test_add_func("/dnsproxy/partial ipv6 tcp msg", + test_partial_ipv6_tcp_msg); + + g_test_add_func("/dnsproxy/partial ipv4 tcp msg from cache", + test_partial_ipv4_tcp_msg); + + g_test_add_func("/dnsproxy/partial ipv6 tcp msg from cache", + test_partial_ipv6_tcp_msg); + + g_test_add_func("/dnsproxy/multiple ipv4 tcp msg", + test_multiple_ipv4_tcp_msg); + + g_test_add_func("/dnsproxy/multiple ipv6 tcp msg", + test_multiple_ipv6_tcp_msg); + + g_test_add_func("/dnsproxy/multiple ipv4 tcp msg from cache", + test_multiple_ipv4_tcp_msg); + + g_test_add_func("/dnsproxy/multiple ipv6 tcp msg from cache", + test_multiple_ipv6_tcp_msg); + + return g_test_run(); +} -- cgit v1.2.3