Skip to content
Snippets Groups Projects
test_dns.c 38.9 KiB
Newer Older
  • Learn to ignore specific revisions
  •  *
     * This is called when an async query completes, either because it resolved or
     * because it was canceled. In our case, this callback is used to signal to the
     * test that it can continue
     *
     * \param query The DNS query that has completed
     */
    static void async_callback(const struct ast_dns_query *query)
    {
    	struct async_resolution_data *async_data = ast_dns_query_get_data(query);
    
    	ast_mutex_lock(&async_data->lock);
    	async_data->complete = 1;
    	ast_cond_signal(&async_data->cond);
    	ast_mutex_unlock(&async_data->lock);
    }
    
    AST_TEST_DEFINE(resolver_resolve_async)
    {
    	RAII_VAR(struct async_resolution_data *, async_data, NULL, ao2_cleanup);
    	RAII_VAR(struct ast_dns_query_active *, active, NULL, ao2_cleanup);
    	struct ast_dns_result *result;
    	enum ast_test_result_state res = AST_TEST_PASS;
    	struct timespec timeout;
    
    	switch (cmd) {
    	case TEST_INIT:
    		info->name = "resolver_resolve_async";
    		info->category = "/main/dns/";
    		info->summary = "Test a nominal asynchronous DNS resolution";
    		info->description =
    			"This test performs an asynchronous DNS resolution of a domain. The goal of this\n"
    			"test is not to check the records for accuracy. Rather, the goal is to ensure that\n"
    			"the resolver is called into as expected, that we regain control before the query\n"
    			"is completed, and to ensure that nothing tried to cancel the resolution.";
    		return AST_TEST_NOT_RUN;
    	case TEST_EXECUTE:
    		break;
    	}
    
    	if (ast_dns_resolver_register(&test_resolver)) {
    		ast_test_status_update(test, "Unable to register test resolver\n");
    		return AST_TEST_FAIL;
    	}
    
    	resolver_data_init();
    
    	async_data = async_data_alloc();
    	if (!async_data) {
    		ast_test_status_update(test, "Failed to allocate asynchronous data\n");
    		res = AST_TEST_FAIL;
    		goto cleanup;
    	}
    
    
    	active = ast_dns_resolve_async("asterisk.org", T_A, C_IN, async_callback, async_data);
    
    	if (!active) {
    		ast_test_status_update(test, "Asynchronous resolution of address failed\n");
    		res = AST_TEST_FAIL;
    		goto cleanup;
    	}
    
    	if (!test_resolver_data.resolve_called) {
    		ast_test_status_update(test, "DNS resolution did not call resolver's resolve() method\n");
    		res = AST_TEST_FAIL;
    		goto cleanup;
    	}
    
    	if (test_resolver_data.canceled) {
    		ast_test_status_update(test, "Resolver's cancel() method called for no reason\n");
    		res = AST_TEST_FAIL;
    		goto cleanup;
    	}
    
    
    	timeout = ast_tsnow();
    
    	timeout.tv_sec += 10;
    	ast_mutex_lock(&async_data->lock);
    	while (!async_data->complete) {
    		if (ast_cond_timedwait(&async_data->cond, &async_data->lock, &timeout) == ETIMEDOUT) {
    			break;
    		}
    	}
    	ast_mutex_unlock(&async_data->lock);
    
    	if (!async_data->complete) {
    		ast_test_status_update(test, "Asynchronous resolution timed out\n");
    		res = AST_TEST_FAIL;
    		goto cleanup;
    	}
    
    	if (!test_resolver_data.resolution_complete) {
    		ast_test_status_update(test, "Asynchronous resolution completed early?\n");
    		res = AST_TEST_FAIL;
    		goto cleanup;
    	}
    
    	result = ast_dns_query_get_result(active->query);
    	if (!result) {
    		ast_test_status_update(test, "Asynchronous resolution yielded no result\n");
    		res = AST_TEST_FAIL;
    		goto cleanup;
    	}
    
    	if (!ast_dns_result_get_records(result)) {
    		ast_test_status_update(test, "Asynchronous result had no records\n");
    		res = AST_TEST_FAIL;
    		goto cleanup;
    	}
    
    cleanup:
    	ast_dns_resolver_unregister(&test_resolver);
    	resolver_data_cleanup();
    	return res;
    }
    
    /*! Stub async resolution callback */
    static void stub_callback(const struct ast_dns_query *query)
    {
    	return;
    }
    
    AST_TEST_DEFINE(resolver_resolve_async_off_nominal)
    {
    	struct ast_dns_resolver terrible_resolver = {
    		.name = "Ed Wood's Filmography",
    		.priority = 0,
    		.resolve = fail_resolve,
    		.cancel = stub_cancel,
    	};
    
    	struct dns_resolve_data {
    		const char *name;
    		int rr_type;
    		int rr_class;
    		ast_dns_resolve_callback callback;
    	} resolves [] = {
    
    		{ NULL,           T_A,       C_IN,      stub_callback },
    		{ "asterisk.org", -1,        C_IN,      stub_callback },
    		{ "asterisk.org", 65536 + 1, C_IN,      stub_callback },
    		{ "asterisk.org", T_A,       -1,        stub_callback },
    		{ "asterisk.org", T_A,       65536 + 1, stub_callback },
    		{ "asterisk.org", T_A,       C_IN,      NULL },
    
    	};
    
    	struct ast_dns_query_active *active;
    	enum ast_test_result_state res = AST_TEST_PASS;
    	int i;
    
    	switch (cmd) {
    	case TEST_INIT:
    		info->name = "resolver_resolve_async_off_nominal";
    		info->category = "/main/dns/";
    		info->summary = "Test off-nominal asynchronous DNS resolution";
    		info->description =
    			"This test performs several off-nominal asynchronous DNS resolutions:\n"
    
    			"\t* Attempt resolution with NULL name\n"
    			"\t* Attempt resolution with invalid RR type\n"
    			"\t* Attempt resolution with invalid RR class\n"
    			"\t* Attempt resolution with NULL callback pointer\n"
    
    			"\t* Attempt resolution with resolver that returns an error";
    
    		return AST_TEST_NOT_RUN;
    	case TEST_EXECUTE:
    		break;
    	}
    
    	if (ast_dns_resolver_register(&test_resolver)) {
    		ast_test_status_update(test, "Failed to register test resolver\n");
    		return AST_TEST_FAIL;
    	}
    
    	for (i = 0; i < ARRAY_LEN(resolves); ++i) {
    		active = ast_dns_resolve_async(resolves[i].name, resolves[i].rr_type, resolves[i].rr_class,
    				resolves[i].callback, NULL);
    		if (active) {
    			ast_test_status_update(test, "Successfully performed asynchronous resolution with invalid data\n");
    			ao2_ref(active, -1);
    			res = AST_TEST_FAIL;
    		}
    	}
    
    	ast_dns_resolver_unregister(&test_resolver);
    
    	if (ast_dns_resolver_register(&terrible_resolver)) {
    		ast_test_status_update(test, "Failed to register the DNS resolver\n");
    		return AST_TEST_FAIL;
    	}
    
    
    	active = ast_dns_resolve_async("asterisk.org", T_A, C_IN, stub_callback, NULL);
    
    
    	ast_dns_resolver_unregister(&terrible_resolver);
    
    	if (active) {
    		ast_test_status_update(test, "Successfully performed asynchronous resolution with invalid data\n");
    		ao2_ref(active, -1);
    		return AST_TEST_FAIL;
    	}
    
    	return res;
    }
    
    AST_TEST_DEFINE(resolver_resolve_async_cancel)
    {
    	RAII_VAR(struct async_resolution_data *, async_data, NULL, ao2_cleanup);
    	RAII_VAR(struct ast_dns_query_active *, active, NULL, ao2_cleanup);
    	struct ast_dns_result *result;
    	enum ast_test_result_state res = AST_TEST_PASS;
    	struct timespec timeout;
    
    	switch (cmd) {
    	case TEST_INIT:
    		info->name = "resolver_resolve_async_cancel";
    		info->category = "/main/dns/";
    		info->summary = "Test canceling an asynchronous DNS resolution";
    		info->description =
    			"This test performs an asynchronous DNS resolution of a domain and then cancels\n"
    			"the resolution. The goal of this test is to ensure that the cancel() callback of\n"
    			"the resolver is called and that it properly interrupts the resolution such that no\n"
    
    		return AST_TEST_NOT_RUN;
    	case TEST_EXECUTE:
    		break;
    	}
    
    	if (ast_dns_resolver_register(&test_resolver)) {
    		ast_test_status_update(test, "Unable to register test resolver\n");
    		return AST_TEST_FAIL;
    	}
    
    	resolver_data_init();
    
    	async_data = async_data_alloc();
    	if (!async_data) {
    		ast_test_status_update(test, "Failed to allocate asynchronous data\n");
    		res = AST_TEST_FAIL;
    		goto cleanup;
    	}
    
    
    	active = ast_dns_resolve_async("asterisk.org", T_A, C_IN, async_callback, async_data);
    
    	if (!active) {
    		ast_test_status_update(test, "Asynchronous resolution of address failed\n");
    		res = AST_TEST_FAIL;
    		goto cleanup;
    	}
    
    	if (!test_resolver_data.resolve_called) {
    		ast_test_status_update(test, "DNS resolution did not call resolver's resolve() method\n");
    		res = AST_TEST_FAIL;
    		goto cleanup;
    	}
    
    	if (test_resolver_data.canceled) {
    		ast_test_status_update(test, "Resolver's cancel() method called for no reason\n");
    		res = AST_TEST_FAIL;
    		goto cleanup;
    	}
    
    	ast_dns_resolve_cancel(active);
    
    	if (!test_resolver_data.canceled) {
    		ast_test_status_update(test, "Resolver's cancel() method was not called\n");
    		res = AST_TEST_FAIL;
    		goto cleanup;
    	}
    
    
    	timeout = ast_tsnow();
    
    	timeout.tv_sec += 10;
    	ast_mutex_lock(&async_data->lock);
    	while (!async_data->complete) {
    		if (ast_cond_timedwait(&async_data->cond, &async_data->lock, &timeout) == ETIMEDOUT) {
    			break;
    		}
    	}
    	ast_mutex_unlock(&async_data->lock);
    
    	if (!async_data->complete) {
    		ast_test_status_update(test, "Asynchronous resolution timed out\n");
    		res = AST_TEST_FAIL;
    		goto cleanup;
    	}
    
    	if (test_resolver_data.resolution_complete) {
    		ast_test_status_update(test, "Resolution completed without cancelation\n");
    		res = AST_TEST_FAIL;
    		goto cleanup;
    	}
    
    	result = ast_dns_query_get_result(active->query);
    	if (result) {
    		ast_test_status_update(test, "Canceled resolution had a result\n");
    		res = AST_TEST_FAIL;
    		goto cleanup;
    	}
    
    cleanup:
    	ast_dns_resolver_unregister(&test_resolver);
    	resolver_data_cleanup();
    	return res;
    }
    
    static int unload_module(void)
    {
    	AST_TEST_UNREGISTER(resolver_register_unregister);
    	AST_TEST_UNREGISTER(resolver_register_off_nominal);
    	AST_TEST_UNREGISTER(resolver_unregister_off_nominal);
    	AST_TEST_UNREGISTER(resolver_data);
    	AST_TEST_UNREGISTER(resolver_set_result);
    	AST_TEST_UNREGISTER(resolver_set_result_off_nominal);
    	AST_TEST_UNREGISTER(resolver_add_record);
    	AST_TEST_UNREGISTER(resolver_add_record_off_nominal);
    	AST_TEST_UNREGISTER(resolver_resolve_sync);
    	AST_TEST_UNREGISTER(resolver_resolve_sync_off_nominal);
    	AST_TEST_UNREGISTER(resolver_resolve_async);
    	AST_TEST_UNREGISTER(resolver_resolve_async_off_nominal);
    	AST_TEST_UNREGISTER(resolver_resolve_async_cancel);
    
    	return 0;
    }
    
    static int load_module(void)
    {
    	AST_TEST_REGISTER(resolver_register_unregister);
    	AST_TEST_REGISTER(resolver_register_off_nominal);
    	AST_TEST_REGISTER(resolver_unregister_off_nominal);
    	AST_TEST_REGISTER(resolver_data);
    	AST_TEST_REGISTER(resolver_set_result);
    	AST_TEST_REGISTER(resolver_set_result_off_nominal);
    	AST_TEST_REGISTER(resolver_add_record);
    	AST_TEST_REGISTER(resolver_add_record_off_nominal);
    	AST_TEST_REGISTER(resolver_resolve_sync);
    	AST_TEST_REGISTER(resolver_resolve_sync_off_nominal);
    	AST_TEST_REGISTER(resolver_resolve_async);
    	AST_TEST_REGISTER(resolver_resolve_async_off_nominal);
    	AST_TEST_REGISTER(resolver_resolve_async_cancel);
    
    	return AST_MODULE_LOAD_SUCCESS;
    }
    
    AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DNS API Tests");