/* SSSD Async resolver tests Authors: Martin Nagy <mnagy@redhat.com> Jakub Hrozek <jhrozek@redhat.com> Copyright (C) Red Hat, Inc 2009 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. 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, see <http://www.gnu.org/licenses/>. */ #include <stdlib.h> #include <check.h> #include <talloc.h> #include <tevent.h> #include <popt.h> #include <arpa/inet.h> #include "util/util.h" /* Interface under test */ #include "resolv/async_resolv.h" int use_net_test; struct resolv_test_ctx { struct tevent_context *ev; struct resolv_ctx *resolv; int error; bool done; }; static int setup_resolv_test(struct resolv_test_ctx **ctx) { struct resolv_test_ctx *test_ctx; int ret; test_ctx = talloc_zero(NULL, struct resolv_test_ctx); if (test_ctx == NULL) { fail("Could not allocate memory for test context"); talloc_free(test_ctx); return ENOMEM; } test_ctx->ev = tevent_context_init(NULL); if (test_ctx->ev == NULL) { fail("Could not init tevent context"); talloc_free(test_ctx); return EFAULT; } ret = resolv_init(NULL, test_ctx->ev, &test_ctx->resolv); if (ret != EOK) { fail("Could not init resolv context"); talloc_free(test_ctx); return ret; } *ctx = test_ctx; return EOK; } static int test_loop(struct resolv_test_ctx *data) { while (!data->done) tevent_loop_once(data->ev); return data->error; } static void test_localhost(struct tevent_req *req) { int recv_status; int status; const struct hostent *hostent; int i; struct resolv_test_ctx *test_ctx = tevent_req_callback_data(req, struct resolv_test_ctx); test_ctx->done = true; recv_status = resolv_gethostbyname_recv(req, &status, NULL, &hostent); if (recv_status != EOK) { DEBUG(2, ("resolv_gethostbyname_recv failed: %d\n", recv_status)); test_ctx->error = recv_status; return; } DEBUG(7, ("resolv_gethostbyname_recv status: %d\n", status)); test_ctx->error = ENOENT; for (i = 0; hostent->h_addr_list[i]; i++) { char addr_buf[256]; inet_ntop(hostent->h_addrtype, hostent->h_addr_list[i], addr_buf, sizeof(addr_buf)); /* test that localhost resolves to 127.0.0.1 or ::1 */ if (strcmp(addr_buf, "127.0.0.1") == 0 || strcmp(addr_buf, "::1") == 0) { test_ctx->error = EOK; } } } START_TEST(test_resolv_localhost) { struct resolv_test_ctx *test_ctx; int ret = EOK; struct tevent_req *req; const char *hostname = "localhost.localdomain"; ret = setup_resolv_test(&test_ctx); if (ret != EOK) { fail("Could not set up test"); return; } req = resolv_gethostbyname_send(test_ctx->ev, test_ctx->ev, test_ctx->resolv, hostname, AF_INET); DEBUG(7, ("Sent resolv_gethostbyname\n")); if (req == NULL) { ret = ENOMEM; } if (ret == EOK) { tevent_req_set_callback(req, test_localhost, test_ctx); ret = test_loop(test_ctx); } fail_unless(ret == EOK); talloc_zfree(test_ctx); } END_TEST static void test_negative(struct tevent_req *req) { int recv_status; int status; const struct hostent *hostent; struct resolv_test_ctx *test_ctx; test_ctx = tevent_req_callback_data(req, struct resolv_test_ctx); test_ctx->done = true; recv_status = resolv_gethostbyname_recv(req, &status, NULL, &hostent); if (recv_status == EOK) { DEBUG(7, ("resolv_gethostbyname_recv succeeded in a negative test")); return; } test_ctx->error = status; DEBUG(2, ("resolv_gethostbyname_recv status: %d: %s\n", status, resolv_strerror(status))); } START_TEST(test_resolv_negative) { int ret = EOK; struct tevent_req *req; const char *hostname = "sssd.foo"; struct resolv_test_ctx *test_ctx; ret = setup_resolv_test(&test_ctx); if (ret != EOK) { fail("Could not set up test"); return; } req = resolv_gethostbyname_send(test_ctx->ev, test_ctx->ev, test_ctx->resolv, hostname, AF_INET); DEBUG(7, ("Sent resolv_gethostbyname\n")); if (req == NULL) { ret = ENOMEM; } if (ret == EOK) { tevent_req_set_callback(req, test_negative, test_ctx); ret = test_loop(test_ctx); } fail_unless(ret != EOK); fail_unless(test_ctx->error == ARES_ENOTFOUND); talloc_zfree(test_ctx); } END_TEST static void test_internet(struct tevent_req *req) { int recv_status; int status; const struct hostent *hostent; struct resolv_test_ctx *test_ctx = tevent_req_callback_data(req, struct resolv_test_ctx); test_ctx->done = true; recv_status = resolv_gethostbyname_recv(req, &status, NULL, &hostent); if (recv_status != EOK) { DEBUG(2, ("resolv_gethostbyname_recv failed: %d\n", recv_status)); test_ctx->error = recv_status; return; } DEBUG(7, ("resolv_gethostbyname_recv status: %d\n", status)); test_ctx->error = (hostent->h_length == 0) ? ENOENT : EOK; } START_TEST(test_resolv_internet) { int ret = EOK; struct tevent_req *req; const char *hostname = "redhat.com"; struct resolv_test_ctx *test_ctx; ret = setup_resolv_test(&test_ctx); if (ret != EOK) { fail("Could not set up test"); return; } req = resolv_gethostbyname_send(test_ctx->ev, test_ctx->ev, test_ctx->resolv, hostname, AF_INET); DEBUG(7, ("Sent resolv_gethostbyname\n")); if (req == NULL) { ret = ENOMEM; } if (ret == EOK) { tevent_req_set_callback(req, test_internet, test_ctx); ret = test_loop(test_ctx); } fail_unless(ret == EOK); talloc_zfree(test_ctx); } END_TEST static void resolv_free_context(struct tevent_context *ev, struct tevent_timer *te, struct timeval t, void *ptr) { struct resolv_ctx *rctx = talloc_get_type(ptr, struct resolv_ctx); DEBUG(7, ("freeing the context\n")); talloc_free(rctx); } static void resolv_free_done(struct tevent_context *ev, struct tevent_timer *te, struct timeval t, void *ptr) { struct resolv_test_ctx *tctx = talloc_get_type(ptr, struct resolv_test_ctx); DEBUG(7, ("marking test as done\n")); tctx->error = EOK; tctx->done = true; } START_TEST(test_resolv_free_context) { int ret = EOK; struct tevent_req *req; const char *hostname = "redhat.com"; struct resolv_test_ctx *test_ctx; struct tevent_timer *free_timer, *terminate_timer; struct timeval free_tv, terminate_tv; ret = setup_resolv_test(&test_ctx); if (ret != EOK) { fail("Could not set up test"); return; } req = resolv_gethostbyname_send(test_ctx, test_ctx->ev, test_ctx->resolv, hostname, AF_INET); DEBUG(7, ("Sent resolv_gethostbyname\n")); if (req == NULL) { fail("Error calling resolv_gethostbyname_send"); return; } gettimeofday(&free_tv, NULL); free_tv.tv_sec += 1; free_tv.tv_usec = 0; terminate_tv.tv_sec = free_tv.tv_sec + 1; terminate_tv.tv_usec = 0; free_timer = tevent_add_timer(test_ctx->ev, test_ctx, free_tv, resolv_free_context, test_ctx->resolv); if (free_timer == NULL) { fail("Error calling tevent_add_timer"); return; } terminate_timer = tevent_add_timer(test_ctx->ev, test_ctx, terminate_tv, resolv_free_done, test_ctx); if (terminate_timer == NULL) { fail("Error calling tevent_add_timer"); return; } ret = test_loop(test_ctx); fail_unless(ret == EOK); talloc_zfree(test_ctx); } END_TEST static void resolv_free_req(struct tevent_context *ev, struct tevent_timer *te, struct timeval t, void *ptr) { struct tevent_req *req = talloc_get_type(ptr, struct tevent_req); DEBUG(7, ("freeing the request\n")); talloc_free(req); } START_TEST(test_resolv_free_req) { int ret = EOK; struct tevent_req *req; const char *hostname = "redhat.com"; struct resolv_test_ctx *test_ctx; struct tevent_timer *free_timer, *terminate_timer; struct timeval free_tv, terminate_tv; ret = setup_resolv_test(&test_ctx); if (ret != EOK) { fail("Could not set up test"); return; } req = resolv_gethostbyname_send(test_ctx, test_ctx->ev, test_ctx->resolv, hostname, AF_INET); DEBUG(7, ("Sent resolv_gethostbyname\n")); if (req == NULL) { fail("Error calling resolv_gethostbyname_send"); return; } gettimeofday(&free_tv, NULL); free_tv.tv_sec += 1; free_tv.tv_usec = 0; terminate_tv.tv_sec = free_tv.tv_sec + 1; terminate_tv.tv_usec = 0; free_timer = tevent_add_timer(test_ctx->ev, test_ctx, free_tv, resolv_free_req, req); if (free_timer == NULL) { fail("Error calling tevent_add_timer"); return; } terminate_timer = tevent_add_timer(test_ctx->ev, test_ctx, terminate_tv, resolv_free_done, test_ctx); if (terminate_timer == NULL) { fail("Error calling tevent_add_timer"); return; } ret = test_loop(test_ctx); fail_unless(ret == EOK); talloc_zfree(test_ctx); } END_TEST Suite *create_resolv_suite(void) { Suite *s = suite_create("resolv"); TCase *tc_resolv = tcase_create("RESOLV Tests"); /* Do some testing */ tcase_add_test(tc_resolv, test_resolv_localhost); tcase_add_test(tc_resolv, test_resolv_negative); if (use_net_test) { tcase_add_test(tc_resolv, test_resolv_internet); } tcase_add_test(tc_resolv, test_resolv_free_context); tcase_add_test(tc_resolv, test_resolv_free_req); /* Add all test cases to the test suite */ suite_add_tcase(s, tc_resolv); return s; } int main(int argc, const char *argv[]) { int opt; poptContext pc; int failure_count; Suite *resolv_suite; SRunner *sr; int debug; struct poptOption long_options[] = { POPT_AUTOHELP { "debug-level", 'd', POPT_ARG_INT, &debug, 0, "Set debug level", NULL }, { "use-net-test", 'n', POPT_ARG_NONE, 0, 'n', "Run tests that need an active internet connection", NULL }, POPT_TABLEEND }; pc = poptGetContext(argv[0], argc, argv, long_options, 0); while((opt = poptGetNextOpt(pc)) != -1) { switch(opt) { case 'n': use_net_test = 1; break; default: fprintf(stderr, "\nInvalid option %s: %s\n\n", poptBadOption(pc, 0), poptStrerror(opt)); poptPrintUsage(pc, stderr, 0); return 1; } } poptFreeContext(pc); debug_level = debug; resolv_suite = create_resolv_suite(); sr = srunner_create(resolv_suite); srunner_run_all(sr, CK_VERBOSE); failure_count = srunner_ntests_failed(sr); srunner_free(sr); return (failure_count==0 ? EXIT_SUCCESS : EXIT_FAILURE); }