/* * * 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 static 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 */ }; static 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 */ }; static 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; close(sk); sk = -1; 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(void) { 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(); }