Review Board 1.7.16


DNS core unit tests

Review Request #4462 - Created March 6, 2015 and submitted

Mark Michelson
/team/group/dns/
Reviewers
asterisk-dev
Asterisk
This provides unit tests for the DNS core as described here: https://wiki.asterisk.org/wiki/display/~jcolp/Asterisk+DNS+API

By "core" this means the bare-bones functionality, such as being able to set and retrieve data on DNS queries. This also includes a mock resolver, whose intention is to ensure that resolver methods are called into when expected.

If you have ideas for tests that have not been included here, please mention them in your reviews. Some things that are not covered here:

* Recurring asynchronous queries, query sets, NAPTR, SRV, and TLSA are not covered by these tests. These are higher-level APIs on top of the DNS core and will be covered in separate test files.
* Nominal asynchronous DNS cancellation is tested here, but off-nominal is not. Off-nominal asynchronous cancellation falls into two basic categories: canceling when there is no query in flight and canceling after a query has completed. You can't test canceling when there is no query in flight because putting the query in flight is what gives you the query object that you would attempt to cancel in the first place. Testing canceling after the query has completed does not test the DNS core as much as it does a specific resolver implementation. Since the resolver implementation is in charge of threading, the core does not try to make any determination of whether it makes sense to be canceling a query or not.
All tests pass consistently, and they do not leak memory (as evidenced by MALLOC_DEBUG)

Diff revision 1 (Latest)

  1. /trunk/tests/test_dns.c: Loading...
/trunk/tests/test_dns.c
New File

    
   
1
/*

    
   
2
 * Asterisk -- An open source telephony toolkit.

    
   
3
 *

    
   
4
 * Copyright (C) 2015, Mark Michelson

    
   
5
 *

    
   
6
 * Mark Michelson <mmichelson@digium.com>

    
   
7
 *

    
   
8
 * See http://www.asterisk.org for more information about

    
   
9
 * the Asterisk project. Please do not directly contact

    
   
10
 * any of the maintainers of this project for assistance;

    
   
11
 * the project provides a web site, mailing lists and IRC

    
   
12
 * channels for your use.

    
   
13
 *

    
   
14
 * This program is free software, distributed under the terms of

    
   
15
 * the GNU General Public License Version 2. See the LICENSE file

    
   
16
 * at the top of the source tree.

    
   
17
 */

    
   
18

   

    
   
19
/*** MODULEINFO

    
   
20
	<depend>TEST_FRAMEWORK</depend>

    
   
21
	<support_level>core</support_level>

    
   
22
 ***/

    
   
23

   

    
   
24
#include "asterisk.h"

    
   
25

   

    
   
26
#include <arpa/nameser.h>

    
   
27
#include <arpa/inet.h>

    
   
28

   

    
   
29
#include "asterisk/test.h"

    
   
30
#include "asterisk/module.h"

    
   
31
#include "asterisk/dns_core.h"

    
   
32
#include "asterisk/dns_resolver.h"

    
   
33
#include "asterisk/dns_internal.h"

    
   
34

   

    
   
35
/* Used when a stub is needed for certain tests */

    
   
36
static int stub_resolve(struct ast_dns_query *query)

    
   
37
{

    
   
38
	return 0;

    
   
39
}

    
   
40

   

    
   
41
/* Used when a stub is needed for certain tests */

    
   
42
static int stub_cancel(struct ast_dns_query *query)

    
   
43
{

    
   
44
	return 0;

    
   
45
}

    
   
46

   

    
   
47
AST_TEST_DEFINE(resolver_register_unregister)

    
   
48
{

    
   
49
	struct ast_dns_resolver cool_guy_resolver = {

    
   
50
		.name = "A snake that swallowed a deer",

    
   
51
		.priority = 19890504,

    
   
52
		.resolve = stub_resolve,

    
   
53
		.cancel = stub_cancel,

    
   
54
	};

    
   
55

   

    
   
56
	switch (cmd) {

    
   
57
	case TEST_INIT:

    
   
58
		info->name = "resolver_register_unregister";

    
   
59
		info->category = "/main/dns/";

    
   
60
		info->summary = "Test nominal resolver registration and unregistration";

    
   
61
		info->description =

    
   
62
			"The test performs the following steps:\n"

    
   
63
			"\t* Register a valid resolver.\n"

    
   
64
			"\t* Unregister the resolver.\n"

    
   
65
			"If either step fails, the test fails\n";

    
   
66
		return AST_TEST_NOT_RUN;

    
   
67
	case TEST_EXECUTE:

    
   
68
		break;

    
   
69
	}

    
   
70

   

    
   
71
	if (ast_dns_resolver_register(&cool_guy_resolver)) {

    
   
72
		ast_test_status_update(test, "Unable to register a perfectly good resolver\n");

    
   
73
		return AST_TEST_FAIL;

    
   
74
	}

    
   
75

   

    
   
76
	ast_dns_resolver_unregister(&cool_guy_resolver);

    
   
77

   

    
   
78
	return AST_TEST_PASS;

    
   
79
}

    
   
80

   

    
   
81
AST_TEST_DEFINE(resolver_register_off_nominal)

    
   
82
{

    
   
83
	struct ast_dns_resolver valid = {

    
   
84
		.name = "valid",

    
   
85
		.resolve = stub_resolve,

    
   
86
		.cancel = stub_cancel,

    
   
87
	};

    
   
88

   

    
   
89
	struct ast_dns_resolver incomplete1 = {

    
   
90
		.name = NULL,

    
   
91
		.resolve = stub_resolve,

    
   
92
		.cancel = stub_cancel,

    
   
93
	};

    
   
94

   

    
   
95
	struct ast_dns_resolver incomplete2 = {

    
   
96
		.name = "incomplete2",

    
   
97
		.resolve = NULL,

    
   
98
		.cancel = stub_cancel,

    
   
99
	};

    
   
100

   

    
   
101
	struct ast_dns_resolver incomplete3 = {

    
   
102
		.name = "incomplete3",

    
   
103
		.resolve = stub_resolve,

    
   
104
		.cancel = NULL,

    
   
105
	};

    
   
106

   

    
   
107
	switch (cmd) {

    
   
108
	case TEST_INIT:

    
   
109
		info->name = "resolver_register_off_nominal";

    
   
110
		info->category = "/main/dns/";

    
   
111
		info->summary = "Test off-nominal resolver registration";

    
   
112
		info->description =

    
   
113
			"Test off-nominal resolver registration:\n"

    
   
114
			"\t* Register a duplicate resolver\n"

    
   
115
			"\t* Register a resolver without a name\n"

    
   
116
			"\t* Register a resolver without a resolve() method\n"

    
   
117
			"\t* Register a resolver without a cancel() method\n";

    
   
118
		return AST_TEST_NOT_RUN;

    
   
119
	case TEST_EXECUTE:

    
   
120
		break;

    
   
121
	}

    
   
122

   

    
   
123
	if (ast_dns_resolver_register(&valid)) {

    
   
124
		ast_test_status_update(test, "Failed to register valid resolver\n");

    
   
125
		return AST_TEST_FAIL;

    
   
126
	}

    
   
127

   

    
   
128
	if (!ast_dns_resolver_register(&valid)) {

    
   
129
		ast_test_status_update(test, "Successfully registered the same resolver multiple times\n");

    
   
130
		return AST_TEST_FAIL;

    
   
131
	}

    
   
132

   

    
   
133
	ast_dns_resolver_unregister(&valid);

    
   
134

   

    
   
135
	if (!ast_dns_resolver_register(NULL)) {

    
   
136
		ast_test_status_update(test, "Successfully registered a NULL resolver\n");

    
   
137
		return AST_TEST_FAIL;

    
   
138
	}

    
   
139

   

    
   
140
	if (!ast_dns_resolver_register(&incomplete1)) {

    
   
141
		ast_test_status_update(test, "Successfully registered a DNS resolver with no name\n");

    
   
142
		return AST_TEST_FAIL;

    
   
143
	}

    
   
144

   

    
   
145
	if (!ast_dns_resolver_register(&incomplete2)) {

    
   
146
		ast_test_status_update(test, "Successfully registered a DNS resolver with no resolve() method\n");

    
   
147
		return AST_TEST_FAIL;

    
   
148
	}

    
   
149

   

    
   
150
	if (!ast_dns_resolver_register(&incomplete3)) {

    
   
151
		ast_test_status_update(test, "Successfully registered a DNS resolver with no cancel() method\n");

    
   
152
		return AST_TEST_FAIL;

    
   
153
	}

    
   
154

   

    
   
155
	return AST_TEST_PASS;

    
   
156
}

    
   
157

   

    
   
158
AST_TEST_DEFINE(resolver_unregister_off_nominal)

    
   
159
{

    
   
160
	struct ast_dns_resolver non_existent = {

    
   
161
		.name = "I do not exist",

    
   
162
		.priority = 20141004,

    
   
163
		.resolve = stub_resolve,

    
   
164
		.cancel = stub_cancel,

    
   
165
	};

    
   
166

   

    
   
167
	switch (cmd) {

    
   
168
	case TEST_INIT:

    
   
169
		info->name = "resolver_unregister_off_nominal";

    
   
170
		info->category = "/main/dns/";

    
   
171
		info->summary = "Test off-nominal DNS resolver unregister";

    
   
172
		info->description =

    
   
173
			"The test attempts the following:\n"

    
   
174
			"\t* Unregister a resolver that is not registered.\n"

    
   
175
			"\t* Unregister a NULL pointer.\n"

    
   
176
			"Because unregistering a resolver does not return an indicator of success, the best\n"

    
   
177
			"this test can do is verify that nothing blows up when this is attempted.\n";

    
   
178
		return AST_TEST_NOT_RUN;

    
   
179
	case TEST_EXECUTE:

    
   
180
		break;

    
   
181
	}

    
   
182

   

    
   
183
	ast_dns_resolver_unregister(&non_existent);

    
   
184
	ast_dns_resolver_unregister(NULL);

    
   
185

   

    
   
186
	return AST_TEST_PASS;

    
   
187
}

    
   
188

   

    
   
189
AST_TEST_DEFINE(resolver_data)

    
   
190
{

    
   
191
	struct ast_dns_query some_query;

    
   
192

   

    
   
193
	struct digits {

    
   
194
		int fingers;

    
   
195
		int toes;

    
   
196
	};

    
   
197

   

    
   
198
	RAII_VAR(struct digits *, average, NULL, ao2_cleanup);

    
   
199
	RAII_VAR(struct digits *, polydactyl, NULL, ao2_cleanup);

    
   
200

   

    
   
201
	struct digits *data_ptr;

    
   
202

   

    
   
203
	switch (cmd) {

    
   
204
	case TEST_INIT:

    
   
205
		info->name = "resolver_data";

    
   
206
		info->category = "/main/dns/";

    
   
207
		info->summary = "Test getting and setting data on a DNS resolver";

    
   
208
		info->description = "This test does the following:\n"

    
   
209
			"\t* Ensure that requesting resolver data results in a NULL return if no data has been set.\n"

    
   
210
			"\t* Ensure that setting resolver data does not result in an error.\n"

    
   
211
			"\t* Ensure that retrieving the set resolver data returns the data we expect\n"

    
   
212
			"\t* Ensure that setting new resolver data on the query does not result in an error\n"

    
   
213
			"\t* Ensure that retrieving the resolver data returns the new data that we set\n";

    
   
214
		return AST_TEST_NOT_RUN;

    
   
215
	case TEST_EXECUTE:

    
   
216
		break;

    
   
217
	}

    
   
218

   

    
   
219
	memset(&some_query, 0, sizeof(some_query));

    
   
220

   

    
   
221
	average = ao2_alloc(sizeof(*average), NULL);

    
   
222
	polydactyl = ao2_alloc(sizeof(*average), NULL);

    
   
223

   

    
   
224
	if (!average || !polydactyl) {

    
   
225
		ast_test_status_update(test, "Allocation failure during unit test\n");

    
   
226
		return AST_TEST_FAIL;

    
   
227
	}

    
   
228

   

    
   
229
	/* Ensure that NULL is retrieved if we haven't set anything on the query */

    
   
230
	data_ptr = ast_dns_resolver_get_data(&some_query);

    
   
231
	if (data_ptr) {

    
   
232
		ast_test_status_update(test, "Retrieved non-NULL resolver data from query unexpectedly\n");

    
   
233
		return AST_TEST_FAIL;

    
   
234
	}

    
   
235

   

    
   
236
	if (ast_dns_resolver_set_data(&some_query, average)) {

    
   
237
		ast_test_status_update(test, "Failed to set resolver data on query\n");

    
   
238
		return AST_TEST_FAIL;

    
   
239
	}

    
   
240

   

    
   
241
	/* Go ahead now and remove the query's reference to the resolver data to prevent memory leaks */

    
   
242
	ao2_ref(average, -1);

    
   
243

   

    
   
244
	/* Ensure that data can be set and retrieved */

    
   
245
	data_ptr = ast_dns_resolver_get_data(&some_query);

    
   
246
	if (!data_ptr) {

    
   
247
		ast_test_status_update(test, "Unable to retrieve resolver data from DNS query\n");

    
   
248
		return AST_TEST_FAIL;

    
   
249
	}

    
   
250

   

    
   
251
	if (data_ptr != average) {

    
   
252
		ast_test_status_update(test, "Unexpected resolver data retrieved from DNS query\n");

    
   
253
		return AST_TEST_FAIL;

    
   
254
	}

    
   
255

   

    
   
256
	/* Ensure that attempting to set new resolver data on the query fails */

    
   
257
	if (!ast_dns_resolver_set_data(&some_query, polydactyl)) {

    
   
258
		ast_test_status_update(test, "Successfully overwrote resolver data on a query. We shouldn't be able to do that\n");

    
   
259
		return AST_TEST_FAIL;

    
   
260
	}

    
   
261

   

    
   
262
	return AST_TEST_PASS;

    
   
263
}

    
   
264

   

    
   
265
static int test_results(struct ast_test *test, const struct ast_dns_query *query,

    
   
266
		unsigned int expected_secure, unsigned int expected_bogus,

    
   
267
		unsigned int expected_rcode, const char *expected_canonical)

    
   
268
{

    
   
269
	struct ast_dns_result *result;

    
   
270

   

    
   
271
	result = ast_dns_query_get_result(query);

    
   
272
	if (!result) {

    
   
273
		ast_test_status_update(test, "Unable to retrieve result from query\n");

    
   
274
		return -1;

    
   
275
	}

    
   
276

   

    
   
277
	if (ast_dns_result_get_secure(result) != expected_secure ||

    
   
278
			ast_dns_result_get_bogus(result) != expected_bogus ||

    
   
279
			ast_dns_result_get_rcode(result) != expected_rcode ||

    
   
280
			strcmp(ast_dns_result_get_canonical(result), expected_canonical)) {

    
   
281
		ast_test_status_update(test, "Unexpected values in result from query\n");

    
   
282
		return -1;

    
   
283
	}

    
   
284

   

    
   
285
	return 0;

    
   
286
}

    
   
287

   

    
   
288
AST_TEST_DEFINE(resolver_set_result)

    
   
289
{

    
   
290
	struct ast_dns_query some_query;

    
   
291
	struct ast_dns_result *result;

    
   
292

   

    
   
293
	struct dns_result {

    
   
294
		unsigned int secure;

    
   
295
		unsigned int bogus;

    
   
296
		unsigned int rcode;

    
   
297
	} results[] = {

    
   
298
		{ 0, 0, ns_r_noerror },

    
   
299
		{ 0, 1, ns_r_noerror },

    
   
300
		{ 1, 0, ns_r_noerror },

    
   
301
		{ 0, 0, ns_r_nxdomain },

    
   
302
	};

    
   
303
	int i;

    
   
304
	enum ast_test_result_state res = AST_TEST_PASS;

    
   
305

   

    
   
306
	switch (cmd) {

    
   
307
	case TEST_INIT:

    
   
308
		info->name = "resolver_set_result";

    
   
309
		info->category = "/main/dns/";

    
   
310
		info->summary = "Test setting and getting results on DNS queries";

    
   
311
		info->description =

    
   
312
			"This test performs the following:\n"

    
   
313
			"\t* Sets a result that is not secure, bogus, and has rcode 0\n"

    
   
314
			"\t* Sets a result that is not secure, has rcode 0, but is secure\n"

    
   
315
			"\t* Sets a result that is not bogus, has rcode 0, but is secure\n"

    
   
316
			"\t* Sets a result that is not secure or bogus, but has rcode NXDOMAIN\n"

    
   
317
			"After each result is set, we ensure that parameters retrieved from\n"

    
   
318
			"the result have the expected values.";

    
   
319
		return AST_TEST_NOT_RUN;

    
   
320
	case TEST_EXECUTE:

    
   
321
		break;

    
   
322
	}

    
   
323

   

    
   
324
	memset(&some_query, 0, sizeof(some_query));

    
   
325

   

    
   
326
	for (i = 0; i < ARRAY_LEN(results); ++i) {

    
   
327
		if (ast_dns_resolver_set_result(&some_query, results[i].secure, results[i].bogus,

    
   
328
				results[i].rcode, "asterisk.org")) {

    
   
329
			ast_test_status_update(test, "Unable to add DNS result to query\n");

    
   
330
			res = AST_TEST_FAIL;

    
   
331
		}

    
   
332

   

    
   
333
		if (test_results(test, &some_query, results[i].secure, results[i].bogus,

    
   
334
				results[i].rcode, "asterisk.org")) {

    
   
335
			res = AST_TEST_FAIL;

    
   
336
		}

    
   
337
	}

    
   
338

   

    
   
339
	/* The final result we set needs to be freed */

    
   
340
	result = ast_dns_query_get_result(&some_query);

    
   
341
	ast_dns_result_free(result);

    
   
342

   

    
   
343
	return res;

    
   
344
}

    
   
345

   

    
   
346
AST_TEST_DEFINE(resolver_set_result_off_nominal)

    
   
347
{

    
   
348
	struct ast_dns_query some_query;

    
   
349
	struct ast_dns_result *result;

    
   
350

   

    
   
351
	switch (cmd) {

    
   
352
	case TEST_INIT:

    
   
353
		info->name = "resolver_set_result_off_nominal";

    
   
354
		info->category = "/main/dns/";

    
   
355
		info->summary = "Test setting off-nominal DNS results\n";

    
   
356
		info->description =

    
   
357
			"This test performs the following:\n"

    
   
358
			"\t* Attempt to add a DNS result that is both bogus and secure\n"

    
   
359
			"\t* Attempt to add a DNS result that has no canonical name\n";

    
   
360
		return AST_TEST_NOT_RUN;

    
   
361
	case TEST_EXECUTE:

    
   
362
		break;

    
   
363
	}

    
   
364

   

    
   
365
	memset(&some_query, 0, sizeof(some_query));

    
   
366

   

    
   
367
	if (!ast_dns_resolver_set_result(&some_query, 1, 1, ns_r_noerror, "asterisk.org")) {

    
   
368
		ast_test_status_update(test, "Successfully added a result that was both secure and bogus\n");

    
   
369
		result = ast_dns_query_get_result(&some_query);

    
   
370
		ao2_cleanup(result);

    
   
371
		return AST_TEST_FAIL;

    
   
372
	}

    
   
373

   

    
   
374
	if (!ast_dns_resolver_set_result(&some_query, 0, 0, ns_r_noerror, NULL)) {

    
   
375
		ast_test_status_update(test, "Successfully added result with no canonical name\n");

    
   
376
		result = ast_dns_query_get_result(&some_query);

    
   
377
		ao2_cleanup(result);

    
   
378
		return AST_TEST_FAIL;

    
   
379
	}

    
   
380

   

    
   
381
	return AST_TEST_PASS;

    
   
382
}

    
   
383

   

    
   
384
static int test_record(struct ast_test *test, const struct ast_dns_record *record,

    
   
385
		int rr_type, int rr_class, int ttl, const char *data, const size_t size)

    
   
386
{

    
   
387
	if (ast_dns_record_get_rr_type(record) != rr_type) {

    
   
388
		ast_test_status_update(test, "Unexpected rr_type from DNS record\n");

    
   
389
		return -1;

    
   
390
	}

    
   
391

   

    
   
392
	if (ast_dns_record_get_rr_class(record) != rr_class) {

    
   
393
		ast_test_status_update(test, "Unexpected rr_class from DNS record\n");

    
   
394
		return -1;

    
   
395
	}

    
   
396

   

    
   
397
	if (ast_dns_record_get_ttl(record) != ttl) {

    
   
398
		ast_test_status_update(test, "Unexpected ttl from DNS record\n");

    
   
399
		return -1;

    
   
400
	}

    
   
401

   

    
   
402
	if (memcmp(ast_dns_record_get_data(record), data, size)) {

    
   
403
		ast_test_status_update(test, "Unexpected data in DNS record\n");

    
   
404
		return -1;

    
   
405
	}

    
   
406

   

    
   
407
	return 0;

    
   
408
}

    
   
409

   

    
   
410
AST_TEST_DEFINE(resolver_add_record)

    
   
411
{

    
   
412
	RAII_VAR(struct ast_dns_result *, result, NULL, ast_dns_result_free);

    
   
413
	struct ast_dns_query some_query;

    
   
414
	const struct ast_dns_record *record;

    
   
415

   

    
   
416
	static const char *V4 = "127.0.0.1";

    
   
417
	static const size_t V4_BUFSIZE = sizeof(struct in_addr);

    
   
418
	char v4_buf[V4_BUFSIZE];

    
   
419

   

    
   
420
	static const char *V6 = "::1";

    
   
421
	static const size_t V6_BUFSIZE = sizeof(struct in6_addr);

    
   
422
	char v6_buf[V6_BUFSIZE];

    
   
423

   

    
   
424
	struct dns_record_details {

    
   
425
		int type;

    
   
426
		int class;

    
   
427
		int ttl;

    
   
428
		const char *data;

    
   
429
		const size_t size;

    
   
430
		int visited;

    
   
431
	} records[] = {

    
   
432
		{ ns_t_a, ns_c_in, 12345, v4_buf, V4_BUFSIZE, 0, },

    
   
433
		{ ns_t_aaaa, ns_c_in, 12345, v6_buf, V6_BUFSIZE, 0, },

    
   
434
	};

    
   
435

   

    
   
436
	int num_records_visited = 0;

    
   
437

   

    
   
438
	switch (cmd) {

    
   
439
	case TEST_INIT:

    
   
440
		info->name = "resolver_add_record";

    
   
441
		info->category = "/main/dns/";

    
   
442
		info->summary = "Test adding DNS records to a query";

    
   
443
		info->description =

    
   
444
			"This test performs the following:\n"

    
   
445
			"\t* Ensure a nominal A record can be added to a query result\n"

    
   
446
			"\t* Ensures that the record can be retrieved\n"

    
   
447
			"\t* Ensure that a second record can be added to the query result\n"

    
   
448
			"\t* Ensures that both records can be retrieved\n";

    
   
449
		return AST_TEST_NOT_RUN;

    
   
450
	case TEST_EXECUTE:

    
   
451
		break;

    
   
452
	}

    
   
453

   

    
   
454
	memset(&some_query, 0, sizeof(some_query));

    
   
455

   

    
   
456
	if (ast_dns_resolver_set_result(&some_query, 0, 0, ns_r_noerror, "asterisk.org")) {

    
   
457
		ast_test_status_update(test, "Unable to set result for DNS query\n");

    
   
458
		return AST_TEST_FAIL;

    
   
459
	}

    
   
460

   

    
   
461
	result = ast_dns_query_get_result(&some_query);

    
   
462
	if (!result) {

    
   
463
		ast_test_status_update(test, "Unable to retrieve result from query\n");

    
   
464
		return AST_TEST_FAIL;

    
   
465
	}

    
   
466

   

    
   
467
	inet_pton(AF_INET, V4, v4_buf);

    
   
468

   

    
   
469
	/* Nominal Record */

    
   
470
	if (ast_dns_resolver_add_record(&some_query, records[0].type, records[0].class,

    
   
471
				records[0].ttl, records[0].data, records[0].size)) {

    
   
472
		ast_test_status_update(test, "Unable to add nominal record to query result\n");

    
   
473
		return AST_TEST_FAIL;

    
   
474
	}

    
   
475

   

    
   
476
	/* I should only be able to retrieve one record */

    
   
477
	record = ast_dns_result_get_records(result);

    
   
478
	if (!record) {

    
   
479
		ast_test_status_update(test, "Unable to retrieve record from result\n");

    
   
480
		return AST_TEST_FAIL;

    
   
481
	}

    
   
482

   

    
   
483
	if (test_record(test, record, records[0].type, records[0].class, records[0].ttl,

    
   
484
				records[0].data, records[0].size)) {

    
   
485
		return AST_TEST_FAIL;

    
   
486
	}

    
   
487

   

    
   
488
	if (ast_dns_record_get_next(record)) {

    
   
489
		ast_test_status_update(test, "Multiple records returned when only one was expected\n");

    
   
490
		return AST_TEST_FAIL;

    
   
491
	}

    
   
492

   

    
   
493
	inet_pton(AF_INET6, V6, v6_buf);

    
   
494

   

    
   
495
	if (ast_dns_resolver_add_record(&some_query, records[1].type, records[1].class,

    
   
496
				records[1].ttl, records[1].data, records[1].size)) {

    
   
497
		ast_test_status_update(test, "Unable to add second record to query result\n");

    
   
498
		return AST_TEST_FAIL;

    
   
499
	}

    
   
500

   

    
   
501
	for (record = ast_dns_result_get_records(result); record; record = ast_dns_record_get_next(record)) {

    
   
502
		int res;

    
   
503

   

    
   
504
		/* The order of returned records is not specified by the API. We use the record type

    
   
505
		 * as the discriminator to determine which record data to expect.

    
   
506
		 */

    
   
507
		if (ast_dns_record_get_rr_type(record) == records[0].type) {

    
   
508
			res = test_record(test, record, records[0].type, records[0].class, records[0].ttl, records[0].data, records[0].size);

    
   
509
			records[0].visited = 1;

    
   
510
		} else if (ast_dns_record_get_rr_type(record) == records[1].type) {

    
   
511
			res = test_record(test, record, records[1].type, records[1].class, records[1].ttl, records[1].data, records[1].size);

    
   
512
			records[1].visited = 1;

    
   
513
		} else {

    
   
514
			ast_test_status_update(test, "Unknown record type found in DNS results\n");

    
   
515
			return AST_TEST_FAIL;

    
   
516
		}

    
   
517

   

    
   
518
		if (res) {

    
   
519
			return AST_TEST_FAIL;

    
   
520
		}

    
   
521

   

    
   
522
		++num_records_visited;

    
   
523
	}

    
   
524

   

    
   
525
	if (!records[0].visited || !records[1].visited) {

    
   
526
		ast_test_status_update(test, "Did not visit all added DNS records\n");

    
   
527
		return AST_TEST_FAIL;

    
   
528
	}

    
   
529

   

    
   
530
	if (num_records_visited != ARRAY_LEN(records)) {

    
   
531
		ast_test_status_update(test, "Did not visit the expected number of DNS records\n");

    
   
532
		return AST_TEST_FAIL;

    
   
533
	}

    
   
534

   

    
   
535
	return AST_TEST_PASS;

    
   
536
}

    
   
537

   

    
   
538
AST_TEST_DEFINE(resolver_add_record_off_nominal)

    
   
539
{

    
   
540
	RAII_VAR(struct ast_dns_result *, result, NULL, ast_dns_result_free);

    
   
541
	struct ast_dns_query some_query;

    
   
542
	static const char *V4 = "127.0.0.1";

    
   
543
	static const size_t V4_BUFSIZE = sizeof(struct in_addr);

    
   
544
	char v4_buf[V4_BUFSIZE];

    
   
545

   

    
   
546
	switch (cmd) {

    
   
547
	case TEST_INIT:

    
   
548
		info->name = "resolver_add_record_off_nominal";

    
   
549
		info->category = "/main/dns/";

    
   
550
		info->summary = "Test adding off-nominal DNS records to a query";

    
   
551
		info->description =

    
   
552
			"This test performs the following:\n"

    
   
553
			"\t* Ensure a nominal A record cannot be added if no result has been set.\n"

    
   
554
			"\t* Ensure that an A record with invalid RR types cannot be added to a query\n"

    
   
555
			"\t* Ensure that an A record with invalid RR classes cannot be added to a query\n"

    
   
556
			"\t* Ensure that an A record with invalid TTL cannot be added to a query\n"

    
   
557
			"\t* Ensure that an A record with NULL data cannot be added to a query\n"

    
   
558
			"\t* Ensure that an A record with invalid length cannot be added to a query\n";

    
   
559
		return AST_TEST_NOT_RUN;

    
   
560
	case TEST_EXECUTE:

    
   
561
		break;

    
   
562
	}

    
   
563

   

    
   
564
	memset(&some_query, 0, sizeof(some_query));

    
   
565

   

    
   
566
	inet_ntop(AF_INET, V4, v4_buf, V4_BUFSIZE);

    
   
567

   

    
   
568
	/* Add record before setting result */

    
   
569
	if (!ast_dns_resolver_add_record(&some_query, ns_t_a, ns_c_in, 12345, v4_buf, V4_BUFSIZE)) {

    
   
570
		ast_test_status_update(test, "Successfully added DNS record to query before setting a result\n");

    
   
571
		return AST_TEST_FAIL;

    
   
572
	}

    
   
573

   

    
   
574
	if (ast_dns_resolver_set_result(&some_query, 0, 0, ns_r_noerror, "asterisk.org")) {

    
   
575
		ast_test_status_update(test, "Unable to set result for DNS query\n");

    
   
576
		return AST_TEST_FAIL;

    
   
577
	}

    
   
578

   

    
   
579
	/* We get the result so it will be cleaned up when the function exits */

    
   
580
	result = ast_dns_query_get_result(&some_query);

    
   
581

   

    
   
582
	/* Invalid RR types */

    
   
583
	if (!ast_dns_resolver_add_record(&some_query, -1, ns_c_in, 12345, v4_buf, V4_BUFSIZE)) {

    
   
584
		ast_test_status_update(test, "Successfully added DNS record with negative RR type\n");

    
   
585
		return AST_TEST_FAIL;

    
   
586
	}

    
   
587

   

    
   
588
	if (!ast_dns_resolver_add_record(&some_query, ns_t_max + 1, ns_c_in, 12345, v4_buf, V4_BUFSIZE)) {

    
   
589
		ast_test_status_update(test, "Successfully added DNS record with too large RR type\n");

    
   
590
		return AST_TEST_FAIL;

    
   
591
	}

    
   
592

   

    
   
593
	/* Invalid RR classes */

    
   
594
	if (!ast_dns_resolver_add_record(&some_query, ns_t_a, -1, 12345, v4_buf, V4_BUFSIZE)) {

    
   
595
		ast_test_status_update(test, "Successfully added DNS record with negative RR class\n");

    
   
596
		return AST_TEST_FAIL;

    
   
597
	}

    
   
598

   

    
   
599
	if (!ast_dns_resolver_add_record(&some_query, ns_t_a, ns_c_max + 1, 12345, v4_buf, V4_BUFSIZE)) {

    
   
600
		ast_test_status_update(test, "Successfully added DNS record with too large RR class\n");

    
   
601
		return AST_TEST_FAIL;

    
   
602
	}

    
   
603

   

    
   
604
	/* Invalid TTL */

    
   
605
	if (!ast_dns_resolver_add_record(&some_query, ns_t_a, ns_c_in, -1, v4_buf, V4_BUFSIZE)) {

    
   
606
		ast_test_status_update(test, "Successfully added DNS record with negative TTL\n");

    
   
607
		return AST_TEST_FAIL;

    
   
608
	}

    
   
609

   

    
   
610
	/* No data */

    
   
611
	if (!ast_dns_resolver_add_record(&some_query, ns_t_a, ns_c_in, 12345, NULL, 0)) {

    
   
612
		ast_test_status_update(test, "Successfully added a DNS record with no data\n");

    
   
613
		return AST_TEST_FAIL;

    
   
614
	}

    
   
615

   

    
   
616
	/* Lie about the length */

    
   
617
	if (!ast_dns_resolver_add_record(&some_query, ns_t_a, ns_c_in, 12345, v4_buf, 0)) {

    
   
618
		ast_test_status_update(test, "Successfully added a DNS record with length zero\n");

    
   
619
		return AST_TEST_FAIL;

    
   
620
	}

    
   
621

   

    
   
622
	return AST_TEST_PASS;

    
   
623
}

    
   
624

   

    
   
625
/*!

    
   
626
 * \brief File-scoped data used during resolver tests

    
   
627
 *

    
   
628
 * This data has to live at file-scope since it needs to be

    
   
629
 * accessible by multiple threads.

    
   
630
 */

    
   
631
static struct resolver_data {

    
   
632
	/*! True if the resolver's resolve() method has been called */

    
   
633
	int resolve_called;

    
   
634
	/*! True if the resolver's cancel() method has been called */

    
   
635
	int canceled;

    
   
636
	/*! True if resolution successfully completed. This is mutually exclusive with \ref canceled */

    
   
637
	int resolution_complete;

    
   
638
	/*! Lock used for protecting \ref cancel_cond */

    
   
639
	ast_mutex_t lock;

    
   
640
	/*! Condition variable used to coordinate canceling a query */

    
   
641
	ast_cond_t cancel_cond;

    
   
642
} test_resolver_data;

    
   
643

   

    
   
644
/*!

    
   
645
 * \brief Thread spawned by the mock resolver

    
   
646
 *

    
   
647
 * All DNS resolvers are required to be asynchronous. The mock resolver

    
   
648
 * spawns this thread for every DNS query that is executed.

    
   
649
 *

    
   
650
 * This thread waits for 5 seconds and then returns the same A record

    
   
651
 * every time. The 5 second wait is to allow for the query to be

    
   
652
 * canceled if desired

    
   
653
 *

    
   
654
 * \param dns_query The ast_dns_query that is being resolved

    
   
655
 * \return NULL

    
   
656
 */

    
   
657
static void *resolution_thread(void *dns_query)

    
   
658
{

    
   
659
	struct ast_dns_query *query = dns_query;

    
   
660
	struct timespec timeout;

    
   
661

   

    
   
662
	static const char *V4 = "127.0.0.1";

    
   
663
	static const size_t V4_BUFSIZE = sizeof(struct in_addr);

    
   
664
	char v4_buf[V4_BUFSIZE];

    
   
665

   

    
   
666
	clock_gettime(CLOCK_REALTIME, &timeout);

    
   
667
	timeout.tv_sec += 5;

    
   
668

   

    
   
669
	ast_mutex_lock(&test_resolver_data.lock);

    
   
670
	while (!test_resolver_data.canceled) {

    
   
671
		if (ast_cond_timedwait(&test_resolver_data.cancel_cond, &test_resolver_data.lock, &timeout) == ETIMEDOUT) {

    
   
672
			break;

    
   
673
		}

    
   
674
	}

    
   
675
	ast_mutex_unlock(&test_resolver_data.lock);

    
   
676

   

    
   
677
	if (test_resolver_data.canceled) {

    
   
678
		ast_dns_resolver_completed(query);

    
   
679
		ao2_ref(query, -1);

    
   
680
		return NULL;

    
   
681
	}

    
   
682

   

    
   
683
	ast_dns_resolver_set_result(query, 0, 0, ns_r_noerror, "asterisk.org");

    
   
684

   

    
   
685
	inet_pton(AF_INET, V4, v4_buf);

    
   
686
	ast_dns_resolver_add_record(query, ns_t_a, ns_c_in, 12345, v4_buf, V4_BUFSIZE);

    
   
687

   

    
   
688
	test_resolver_data.resolution_complete = 1;

    
   
689
	ast_dns_resolver_completed(query);

    
   
690

   

    
   
691
	ao2_ref(query, -1);

    
   
692
	return NULL;

    
   
693
}

    
   
694

   

    
   
695
/*!

    
   
696
 * \brief Mock resolver's resolve method

    
   
697
 *

    
   
698
 * \param query The query to resolve

    
   
699
 * \retval 0 Successfully spawned resolution thread

    
   
700
 * \retval non-zero Failed to spawn the resolution thread

    
   
701
 */

    
   
702
static int test_resolve(struct ast_dns_query *query)

    
   
703
{

    
   
704
	pthread_t resolver_thread;

    
   
705

   

    
   
706
	test_resolver_data.resolve_called = 1;

    
   
707
	return ast_pthread_create_detached(&resolver_thread, NULL, resolution_thread, ao2_bump(query));

    
   
708
}

    
   
709

   

    
   
710
/*!

    
   
711
 * \brief Mock resolver's cancel method

    
   
712
 *

    
   
713
 * This signals the resolution thread not to return any DNS results.

    
   
714
 *

    
   
715
 * \param query DNS query to cancel

    
   
716
 * \return 0

    
   
717
 */

    
   
718
static int test_cancel(struct ast_dns_query *query)

    
   
719
{

    
   
720
	ast_mutex_lock(&test_resolver_data.lock);

    
   
721
	test_resolver_data.canceled = 1;

    
   
722
	ast_cond_signal(&test_resolver_data.cancel_cond);

    
   
723
	ast_mutex_unlock(&test_resolver_data.lock);

    
   
724

   

    
   
725
	return 0;

    
   
726
}

    
   
727

   

    
   
728
/*!

    
   
729
 * \brief Initialize global mock resolver data.

    
   
730
 *

    
   
731
 * This must be called at the beginning of tests that use the mock resolver

    
   
732
 */

    
   
733
static void resolver_data_init(void)

    
   
734
{

    
   
735
	test_resolver_data.resolve_called = 0;

    
   
736
	test_resolver_data.canceled = 0;

    
   
737
	test_resolver_data.resolution_complete = 0;

    
   
738

   

    
   
739
	ast_mutex_init(&test_resolver_data.lock);

    
   
740
	ast_cond_init(&test_resolver_data.cancel_cond, NULL);

    
   
741
}

    
   
742

   

    
   
743
/*!

    
   
744
 * \brief Cleanup global mock resolver data

    
   
745
 *

    
   
746
 * This must be called at the end of tests that use the mock resolver

    
   
747
 */

    
   
748
static void resolver_data_cleanup(void)

    
   
749
{

    
   
750
	ast_mutex_destroy(&test_resolver_data.lock);

    
   
751
	ast_cond_destroy(&test_resolver_data.cancel_cond);

    
   
752
}

    
   
753

   

    
   
754
/*!

    
   
755
 * \brief The mock resolver

    
   
756
 *

    
   
757
 * The mock resolver does not care about the DNS query that is

    
   
758
 * actually being made on it. It simply regurgitates the same

    
   
759
 * DNS record no matter what.

    
   
760
 */

    
   
761
static struct ast_dns_resolver test_resolver = {

    
   
762
	.name = "test",

    
   
763
	.priority = 0,

    
   
764
	.resolve = test_resolve,

    
   
765
	.cancel = test_cancel,

    
   
766
};

    
   
767

   

    
   
768
AST_TEST_DEFINE(resolver_resolve_sync)

    
   
769
{

    
   
770
	RAII_VAR(struct ast_dns_result *, result, NULL, ast_dns_result_free);

    
   
771
	enum ast_test_result_state res = AST_TEST_PASS;

    
   
772

   

    
   
773
	switch (cmd) {

    
   
774
	case TEST_INIT:

    
   
775
		info->name = "resolver_resolve_sync";

    
   
776
		info->category = "/main/dns/";

    
   
777
		info->summary = "Test a nominal synchronous DNS resolution";

    
   
778
		info->description =

    
   
779
			"This test performs a synchronous DNS resolution of a domain. The goal of this\n"

    
   
780
			"test is not to check the records for accuracy. Rather, the goal is to ensure that\n"

    
   
781
			"the resolver is called into as expected, that the query completes entirely before\n"

    
   
782
			"returning from the synchronous resolution, that nothing tried to cancel the resolution\n,"

    
   
783
			"and that some records were returned.";

    
   
784
		return AST_TEST_NOT_RUN;

    
   
785
	case TEST_EXECUTE:

    
   
786
		break;

    
   
787
	}

    
   
788

   

    
   
789
	if (ast_dns_resolver_register(&test_resolver)) {

    
   
790
		ast_test_status_update(test, "Unable to register test resolver\n");

    
   
791
		return AST_TEST_FAIL;

    
   
792
	}

    
   
793

   

    
   
794
	resolver_data_init();

    
   
795

   

    
   
796
	if (ast_dns_resolve("asterisk.org", ns_t_a, ns_c_in, &result)) {

    
   
797
		ast_test_status_update(test, "Resolution of address failed\n");

    
   
798
		res = AST_TEST_FAIL;

    
   
799
		goto cleanup;

    
   
800
	}

    
   
801

   

    
   
802
	if (!result) {

    
   
803
		ast_test_status_update(test, "DNS resolution returned a NULL result\n");

    
   
804
		res = AST_TEST_FAIL;

    
   
805
		goto cleanup;

    
   
806
	}

    
   
807

   

    
   
808
	if (!test_resolver_data.resolve_called) {

    
   
809
		ast_test_status_update(test, "DNS resolution did not call resolver's resolve() method\n");

    
   
810
		res = AST_TEST_FAIL;

    
   
811
		goto cleanup;

    
   
812
	}

    
   
813

   

    
   
814
	if (test_resolver_data.canceled) {

    
   
815
		ast_test_status_update(test, "Resolver's cancel() method called for no reason\n");

    
   
816
		res = AST_TEST_FAIL;

    
   
817
		goto cleanup;

    
   
818
	}

    
   
819

   

    
   
820
	if (!test_resolver_data.resolution_complete) {

    
   
821
		ast_test_status_update(test, "Synchronous resolution completed early?\n");

    
   
822
		res = AST_TEST_FAIL;

    
   
823
		goto cleanup;

    
   
824
	}

    
   
825

   

    
   
826
	if (!ast_dns_result_get_records(result)) {

    
   
827
		ast_test_status_update(test, "Synchronous resolution yielded no records.\n");

    
   
828
		res = AST_TEST_FAIL;

    
   
829
		goto cleanup;

    
   
830
	}

    
   
831

   

    
   
832
cleanup:

    
   
833
	ast_dns_resolver_unregister(&test_resolver);

    
   
834
	resolver_data_cleanup();

    
   
835
	return res;

    
   
836
}

    
   
837

   

    
   
838
/*!

    
   
839
 * \brief A resolve() method that simply fails

    
   
840
 *

    
   
841
 * \param query The DNS query to resolve. This is ignored.

    
   
842
 * \return -1

    
   
843
 */

    
   
844
static int fail_resolve(struct ast_dns_query *query)

    
   
845
{

    
   
846
	return -1;

    
   
847
}

    
   
848

   

    
   
849
AST_TEST_DEFINE(resolver_resolve_sync_off_nominal)

    
   
850
{

    
   
851
	struct ast_dns_resolver terrible_resolver = {

    
   
852
		.name = "Uwe Boll's Filmography",

    
   
853
		.priority = 0,

    
   
854
		.resolve = fail_resolve,

    
   
855
		.cancel = stub_cancel,

    
   
856
	};

    
   
857

   

    
   
858
	struct ast_dns_result *result = NULL;

    
   
859

   

    
   
860
	struct dns_resolve_data {

    
   
861
		const char *name;

    
   
862
		int rr_type;

    
   
863
		int rr_class;

    
   
864
		struct ast_dns_result **result;

    
   
865
	} resolves [] = {

    
   
866
		{ NULL,           ns_t_a,       ns_c_in,      &result },

    
   
867
		{ "asterisk.org", -1,           ns_c_in,      &result },

    
   
868
		{ "asterisk.org", ns_t_max + 1, ns_c_in,      &result },

    
   
869
		{ "asterisk.org", ns_t_a,       -1,           &result },

    
   
870
		{ "asterisk.org", ns_t_a,       ns_c_max + 1, &result },

    
   
871
		{ "asterisk.org", ns_t_a,       ns_c_in,      NULL },

    
   
872
	};

    
   
873

   

    
   
874
	int i;

    
   
875

   

    
   
876
	enum ast_test_result_state res = AST_TEST_PASS;

    
   
877

   

    
   
878
	switch (cmd) {

    
   
879
	case TEST_INIT:

    
   
880
		info->name = "resolver_resolve_sync_off_nominal";

    
   
881
		info->category = "/main/dns/";

    
   
882
		info->summary = "Test off-nominal synchronous DNS resolution";

    
   
883
		info->description =

    
   
884
			"This test performs several off-nominal synchronous DNS resolutions:\n"

    
   
885
			"\t* Attempt resolution with NULL name\n",

    
   
886
			"\t* Attempt resolution with invalid RR type\n",

    
   
887
			"\t* Attempt resolution with invalid RR class\n",

    
   
888
			"\t* Attempt resolution with NULL result pointer\n",

    
   
889
			"\t* Attempt resolution with resolver that returns an error\n";

    
   
890
		return AST_TEST_NOT_RUN;

    
   
891
	case TEST_EXECUTE:

    
   
892
		break;

    
   
893
	}

    
   
894

   

    
   
895
	if (ast_dns_resolver_register(&test_resolver)) {

    
   
896
		ast_test_status_update(test, "Failed to register test resolver\n");

    
   
897
		return AST_TEST_FAIL;

    
   
898
	}

    
   
899

   

    
   
900
	for (i = 0; i < ARRAY_LEN(resolves); ++i) {

    
   
901
		if (!ast_dns_resolve(resolves[i].name, resolves[i].rr_type, resolves[i].rr_class, resolves[i].result)) {

    
   
902
			ast_test_status_update(test, "Successfully resolved DNS query with invalid parameters\n");

    
   
903
			res = AST_TEST_FAIL;

    
   
904
		} else if (result) {

    
   
905
			ast_test_status_update(test, "Failed resolution set a non-NULL result\n");

    
   
906
			ast_dns_result_free(result);

    
   
907
			res = AST_TEST_FAIL;

    
   
908
		}

    
   
909
	}

    
   
910

   

    
   
911
	ast_dns_resolver_unregister(&test_resolver);

    
   
912

   

    
   
913
	/* As a final test, try a legitimate query with a bad resolver */

    
   
914
	if (ast_dns_resolver_register(&terrible_resolver)) {

    
   
915
		ast_test_status_update(test, "Failed to register the terrible resolver\n");

    
   
916
		return AST_TEST_FAIL;

    
   
917
	}

    
   
918

   

    
   
919
	if (!ast_dns_resolve("asterisk.org", ns_t_a, ns_c_in, &result)) {

    
   
920
		ast_test_status_update(test, "DNS resolution succeeded when we expected it not to\n");

    
   
921
		ast_dns_resolver_unregister(&terrible_resolver);

    
   
922
		return AST_TEST_FAIL;

    
   
923
	}

    
   
924

   

    
   
925
	ast_dns_resolver_unregister(&terrible_resolver);

    
   
926

   

    
   
927
	if (result) {

    
   
928
		ast_test_status_update(test, "Failed DNS resolution set the result to something non-NULL\n");

    
   
929
		ast_dns_result_free(result);

    
   
930
		return AST_TEST_FAIL;

    
   
931
	}

    
   
932

   

    
   
933
	return res;

    
   
934
}

    
   
935

   

    
   
936
/*!

    
   
937
 * \brief Data used by async result callback

    
   
938
 *

    
   
939
 * This is the typical combination of boolean, lock, and condition

    
   
940
 * used to synchronize the activities of two threads. In this case,

    
   
941
 * the testing thread waits on the condition, and the async callback

    
   
942
 * signals the condition when the asynchronous callback is complete.

    
   
943
 */

    
   
944
struct async_resolution_data {

    
   
945
	int complete;

    
   
946
	ast_mutex_t lock;

    
   
947
	ast_cond_t cond;

    
   
948
};

    
   
949

   

    
   
950
/*!

    
   
951
 * \brief Destructor for async_resolution_data

    
   
952
 */

    
   
953
static void async_data_destructor(void *obj)

    
   
954
{

    
   
955
	struct async_resolution_data *async_data = obj;

    
   
956

   

    
   
957
	ast_mutex_destroy(&async_data->lock);

    
   
958
	ast_cond_destroy(&async_data->cond);

    
   
959
}

    
   
960

   

    
   
961
/*!

    
   
962
 * \brief Allocation/initialization for async_resolution_data

    
   
963
 *

    
   
964
 * The DNS core mandates that a query's user data has to be ao2 allocated,

    
   
965
 * so this is a helper method for doing that.

    
   
966
 *

    
   
967
 * \retval NULL Failed allocation

    
   
968
 * \retval non-NULL Newly allocated async_resolution_data

    
   
969
 */

    
   
970
static struct async_resolution_data *async_data_alloc(void)

    
   
971
{

    
   
972
	struct async_resolution_data *async_data;

    
   
973

   

    
   
974
	async_data = ao2_alloc(sizeof(*async_data), async_data_destructor);

    
   
975
	if (!async_data) {

    
   
976
		return NULL;

    
   
977
	}

    
   
978

   

    
   
979
	async_data->complete = 0;

    
   
980
	ast_mutex_init(&async_data->lock);

    
   
981
	ast_cond_init(&async_data->cond, NULL);

    
   
982

   

    
   
983
	return async_data;

    
   
984
}

    
   
985

   

    
   
986
/*!

    
   
987
 * \brief Async DNS callback

    
   
988
 *

    
   
989
 * This is called when an async query completes, either because it resolved or

    
   
990
 * because it was canceled. In our case, this callback is used to signal to the

    
   
991
 * test that it can continue

    
   
992
 *

    
   
993
 * \param query The DNS query that has completed

    
   
994
 */

    
   
995
static void async_callback(const struct ast_dns_query *query)

    
   
996
{

    
   
997
	struct async_resolution_data *async_data = ast_dns_query_get_data(query);

    
   
998

   

    
   
999
	ast_mutex_lock(&async_data->lock);

    
   
1000
	async_data->complete = 1;

    
   
1001
	ast_cond_signal(&async_data->cond);

    
   
1002
	ast_mutex_unlock(&async_data->lock);

    
   
1003
}

    
   
1004

   

    
   
1005
AST_TEST_DEFINE(resolver_resolve_async)

    
   
1006
{

    
   
1007
	RAII_VAR(struct async_resolution_data *, async_data, NULL, ao2_cleanup);

    
   
1008
	RAII_VAR(struct ast_dns_query *, query, NULL, ao2_cleanup);

    
   
1009
	struct ast_dns_result *result;

    
   
1010
	enum ast_test_result_state res = AST_TEST_PASS;

    
   
1011
	struct timespec timeout;

    
   
1012

   

    
   
1013
	switch (cmd) {

    
   
1014
	case TEST_INIT:

    
   
1015
		info->name = "resolver_resolve_async";

    
   
1016
		info->category = "/main/dns/";

    
   
1017
		info->summary = "Test a nominal asynchronous DNS resolution";

    
   
1018
		info->description =

    
   
1019
			"This test performs an asynchronous DNS resolution of a domain. The goal of this\n"

    
   
1020
			"test is not to check the records for accuracy. Rather, the goal is to ensure that\n"

    
   
1021
			"the resolver is called into as expected, that we regain control before the query\n"

    
   
1022
			"is completed, and to ensure that nothing tried to cancel the resolution.";

    
   
1023
		return AST_TEST_NOT_RUN;

    
   
1024
	case TEST_EXECUTE:

    
   
1025
		break;

    
   
1026
	}

    
   
1027

   

    
   
1028
	if (ast_dns_resolver_register(&test_resolver)) {

    
   
1029
		ast_test_status_update(test, "Unable to register test resolver\n");

    
   
1030
		return AST_TEST_FAIL;

    
   
1031
	}

    
   
1032

   

    
   
1033
	resolver_data_init();

    
   
1034

   

    
   
1035
	async_data = async_data_alloc();

    
   
1036
	if (!async_data) {

    
   
1037
		ast_test_status_update(test, "Failed to allocate asynchronous data\n");

    
   
1038
		res = AST_TEST_FAIL;

    
   
1039
		goto cleanup;

    
   
1040
	}

    
   
1041

   

    
   
1042
	query = ast_dns_resolve_async("asterisk.org", ns_t_a, ns_c_in, async_callback, async_data);

    
   
1043
	if (!query) {

    
   
1044
		ast_test_status_update(test, "Asynchronous resolution of address failed\n");

    
   
1045
		res = AST_TEST_FAIL;

    
   
1046
		goto cleanup;

    
   
1047
	}

    
   
1048

   

    
   
1049
	if (!test_resolver_data.resolve_called) {

    
   
1050
		ast_test_status_update(test, "DNS resolution did not call resolver's resolve() method\n");

    
   
1051
		res = AST_TEST_FAIL;

    
   
1052
		goto cleanup;

    
   
1053
	}

    
   
1054

   

    
   
1055
	if (test_resolver_data.canceled) {

    
   
1056
		ast_test_status_update(test, "Resolver's cancel() method called for no reason\n");

    
   
1057
		res = AST_TEST_FAIL;

    
   
1058
		goto cleanup;

    
   
1059
	}

    
   
1060

   

    
   
1061
	clock_gettime(CLOCK_REALTIME, &timeout);

    
   
1062
	timeout.tv_sec += 10;

    
   
1063
	ast_mutex_lock(&async_data->lock);

    
   
1064
	while (!async_data->complete) {

    
   
1065
		if (ast_cond_timedwait(&async_data->cond, &async_data->lock, &timeout) == ETIMEDOUT) {

    
   
1066
			break;

    
   
1067
		}

    
   
1068
	}

    
   
1069
	ast_mutex_unlock(&async_data->lock);

    
   
1070

   

    
   
1071
	if (!async_data->complete) {

    
   
1072
		ast_test_status_update(test, "Asynchronous resolution timed out\n");

    
   
1073
		res = AST_TEST_FAIL;

    
   
1074
		goto cleanup;

    
   
1075
	}

    
   
1076

   

    
   
1077
	if (!test_resolver_data.resolution_complete) {

    
   
1078
		ast_test_status_update(test, "Asynchronous resolution completed early?\n");

    
   
1079
		res = AST_TEST_FAIL;

    
   
1080
		goto cleanup;

    
   
1081
	}

    
   
1082

   

    
   
1083
	result = ast_dns_query_get_result(query);

    
   
1084
	if (!result) {

    
   
1085
		ast_test_status_update(test, "Asynchronous resolution yielded no result\n");

    
   
1086
		res = AST_TEST_FAIL;

    
   
1087
		goto cleanup;

    
   
1088
	}

    
   
1089

   

    
   
1090
	if (!ast_dns_result_get_records(result)) {

    
   
1091
		ast_test_status_update(test, "Asynchronous result had no records\n");

    
   
1092
		res = AST_TEST_FAIL;

    
   
1093
		goto cleanup;

    
   
1094
	}

    
   
1095

   

    
   
1096
cleanup:

    
   
1097
	ast_dns_resolver_unregister(&test_resolver);

    
   
1098
	resolver_data_cleanup();

    
   
1099
	return res;

    
   
1100
}

    
   
1101

   

    
   
1102
/*! Stub async resolution callback */

    
   
1103
static void stub_callback(const struct ast_dns_query *query)

    
   
1104
{

    
   
1105
	return;

    
   
1106
}

    
   
1107

   

    
   
1108
AST_TEST_DEFINE(resolver_resolve_async_off_nominal)

    
   
1109
{

    
   
1110
	struct ast_dns_resolver terrible_resolver = {

    
   
1111
		.name = "Ed Wood's Filmography",

    
   
1112
		.priority = 0,

    
   
1113
		.resolve = fail_resolve,

    
   
1114
		.cancel = stub_cancel,

    
   
1115
	};

    
   
1116

   

    
   
1117
	struct dns_resolve_data {

    
   
1118
		const char *name;

    
   
1119
		int rr_type;

    
   
1120
		int rr_class;

    
   
1121
		ast_dns_resolve_callback callback;

    
   
1122
	} resolves [] = {

    
   
1123
		{ NULL,           ns_t_a,       ns_c_in,      stub_callback },

    
   
1124
		{ "asterisk.org", -1,           ns_c_in,      stub_callback },

    
   
1125
		{ "asterisk.org", ns_t_max + 1, ns_c_in,      stub_callback },

    
   
1126
		{ "asterisk.org", ns_t_a,       -1,           stub_callback },

    
   
1127
		{ "asterisk.org", ns_t_a,       ns_c_max + 1, stub_callback },

    
   
1128
		{ "asterisk.org", ns_t_a,       ns_c_in,      NULL },

    
   
1129
	};

    
   
1130

   

    
   
1131
	struct ast_dns_query *query;

    
   
1132
	enum ast_test_result_state res = AST_TEST_PASS;

    
   
1133
	int i;

    
   
1134

   

    
   
1135
	switch (cmd) {

    
   
1136
	case TEST_INIT:

    
   
1137
		info->name = "resolver_resolve_async_off_nominal";

    
   
1138
		info->category = "/main/dns/";

    
   
1139
		info->summary = "Test off-nominal asynchronous DNS resolution";

    
   
1140
		info->description =

    
   
1141
			"This test performs several off-nominal asynchronous DNS resolutions:\n"

    
   
1142
			"\t* Attempt resolution with NULL name\n",

    
   
1143
			"\t* Attempt resolution with invalid RR type\n",

    
   
1144
			"\t* Attempt resolution with invalid RR class\n",

    
   
1145
			"\t* Attempt resolution with NULL callback pointer\n",

    
   
1146
			"\t* Attempt resolution with resolver that returns an error\n";

    
   
1147
		return AST_TEST_NOT_RUN;

    
   
1148
	case TEST_EXECUTE:

    
   
1149
		break;

    
   
1150
	}

    
   
1151

   

    
   
1152
	if (ast_dns_resolver_register(&test_resolver)) {

    
   
1153
		ast_test_status_update(test, "Failed to register test resolver\n");

    
   
1154
		return AST_TEST_FAIL;

    
   
1155
	}

    
   
1156

   

    
   
1157
	for (i = 0; i < ARRAY_LEN(resolves); ++i) {

    
   
1158
		query = ast_dns_resolve_async(resolves[i].name, resolves[i].rr_type, resolves[i].rr_class,

    
   
1159
				resolves[i].callback, NULL);

    
   
1160
		if (query) {

    
   
1161
			ast_test_status_update(test, "Successfully performed asynchronous resolution with invalid data\n");

    
   
1162
			ao2_ref(query, -1);

    
   
1163
			res = AST_TEST_FAIL;

    
   
1164
		}

    
   
1165
	}

    
   
1166

   

    
   
1167
	ast_dns_resolver_unregister(&test_resolver);

    
   
1168

   

    
   
1169
	if (ast_dns_resolver_register(&terrible_resolver)) {

    
   
1170
		ast_test_status_update(test, "Failed to register the DNS resolver\n");

    
   
1171
		return AST_TEST_FAIL;

    
   
1172
	}

    
   
1173

   

    
   
1174
	query = ast_dns_resolve_async("asterisk.org", ns_t_a, ns_c_in, stub_callback, NULL);

    
   
1175

   

    
   
1176
	ast_dns_resolver_unregister(&terrible_resolver);

    
   
1177

   

    
   
1178
	if (query) {

    
   
1179
		ast_test_status_update(test, "Successfully performed asynchronous resolution with invalid data\n");

    
   
1180
		ao2_ref(query, -1);

    
   
1181
		return AST_TEST_FAIL;

    
   
1182
	}

    
   
1183

   

    
   
1184
	return res;

    
   
1185
}

    
   
1186

   

    
   
1187
AST_TEST_DEFINE(resolver_resolve_async_cancel)

    
   
1188
{

    
   
1189
	RAII_VAR(struct async_resolution_data *, async_data, NULL, ao2_cleanup);

    
   
1190
	RAII_VAR(struct ast_dns_query *, query, NULL, ao2_cleanup);

    
   
1191
	struct ast_dns_result *result;

    
   
1192
	enum ast_test_result_state res = AST_TEST_PASS;

    
   
1193
	struct timespec timeout;

    
   
1194

   

    
   
1195
	switch (cmd) {

    
   
1196
	case TEST_INIT:

    
   
1197
		info->name = "resolver_resolve_async_cancel";

    
   
1198
		info->category = "/main/dns/";

    
   
1199
		info->summary = "Test canceling an asynchronous DNS resolution";

    
   
1200
		info->description =

    
   
1201
			"This test performs an asynchronous DNS resolution of a domain and then cancels\n"

    
   
1202
			"the resolution. The goal of this test is to ensure that the cancel() callback of\n"

    
   
1203
			"the resolver is called and that it properly interrupts the resolution such that no\n"

    
   
1204
			"records are returned.\n";

    
   
1205
		return AST_TEST_NOT_RUN;

    
   
1206
	case TEST_EXECUTE:

    
   
1207
		break;

    
   
1208
	}

    
   
1209

   

    
   
1210
	if (ast_dns_resolver_register(&test_resolver)) {

    
   
1211
		ast_test_status_update(test, "Unable to register test resolver\n");

    
   
1212
		return AST_TEST_FAIL;

    
   
1213
	}

    
   
1214

   

    
   
1215
	resolver_data_init();

    
   
1216

   

    
   
1217
	async_data = async_data_alloc();

    
   
1218
	if (!async_data) {

    
   
1219
		ast_test_status_update(test, "Failed to allocate asynchronous data\n");

    
   
1220
		res = AST_TEST_FAIL;

    
   
1221
		goto cleanup;

    
   
1222
	}

    
   
1223

   

    
   
1224
	query = ast_dns_resolve_async("asterisk.org", ns_t_a, ns_c_in, async_callback, async_data);

    
   
1225
	if (!query) {

    
   
1226
		ast_test_status_update(test, "Asynchronous resolution of address failed\n");

    
   
1227
		res = AST_TEST_FAIL;

    
   
1228
		goto cleanup;

    
   
1229
	}

    
   
1230

   

    
   
1231
	if (!test_resolver_data.resolve_called) {

    
   
1232
		ast_test_status_update(test, "DNS resolution did not call resolver's resolve() method\n");

    
   
1233
		res = AST_TEST_FAIL;

    
   
1234
		goto cleanup;

    
   
1235
	}

    
   
1236

   

    
   
1237
	if (test_resolver_data.canceled) {

    
   
1238
		ast_test_status_update(test, "Resolver's cancel() method called for no reason\n");

    
   
1239
		res = AST_TEST_FAIL;

    
   
1240
		goto cleanup;

    
   
1241
	}

    
   
1242

   

    
   
1243
	ast_dns_resolve_cancel(query);

    
   
1244

   

    
   
1245
	if (!test_resolver_data.canceled) {

    
   
1246
		ast_test_status_update(test, "Resolver's cancel() method was not called\n");

    
   
1247
		res = AST_TEST_FAIL;

    
   
1248
		goto cleanup;

    
   
1249
	}

    
   
1250

   

    
   
1251
	clock_gettime(CLOCK_REALTIME, &timeout);

    
   
1252
	timeout.tv_sec += 10;

    
   
1253
	ast_mutex_lock(&async_data->lock);

    
   
1254
	while (!async_data->complete) {

    
   
1255
		if (ast_cond_timedwait(&async_data->cond, &async_data->lock, &timeout) == ETIMEDOUT) {

    
   
1256
			break;

    
   
1257
		}

    
   
1258
	}

    
   
1259
	ast_mutex_unlock(&async_data->lock);

    
   
1260

   

    
   
1261
	if (!async_data->complete) {

    
   
1262
		ast_test_status_update(test, "Asynchronous resolution timed out\n");

    
   
1263
		res = AST_TEST_FAIL;

    
   
1264
		goto cleanup;

    
   
1265
	}

    
   
1266

   

    
   
1267
	if (test_resolver_data.resolution_complete) {

    
   
1268
		ast_test_status_update(test, "Resolution completed without cancelation\n");

    
   
1269
		res = AST_TEST_FAIL;

    
   
1270
		goto cleanup;

    
   
1271
	}

    
   
1272

   

    
   
1273
	result = ast_dns_query_get_result(query);

    
   
1274
	if (result) {

    
   
1275
		ast_test_status_update(test, "Canceled resolution had a result\n");

    
   
1276
		res = AST_TEST_FAIL;

    
   
1277
		goto cleanup;

    
   
1278
	}

    
   
1279

   

    
   
1280
cleanup:

    
   
1281
	ast_dns_resolver_unregister(&test_resolver);

    
   
1282
	resolver_data_cleanup();

    
   
1283
	return res;

    
   
1284
}

    
   
1285

   

    
   
1286
static int unload_module(void)

    
   
1287
{

    
   
1288
	AST_TEST_UNREGISTER(resolver_register_unregister);

    
   
1289
	AST_TEST_UNREGISTER(resolver_register_off_nominal);

    
   
1290
	AST_TEST_UNREGISTER(resolver_unregister_off_nominal);

    
   
1291
	AST_TEST_UNREGISTER(resolver_data);

    
   
1292
	AST_TEST_UNREGISTER(resolver_set_result);

    
   
1293
	AST_TEST_UNREGISTER(resolver_set_result_off_nominal);

    
   
1294
	AST_TEST_UNREGISTER(resolver_add_record);

    
   
1295
	AST_TEST_UNREGISTER(resolver_add_record_off_nominal);

    
   
1296
	AST_TEST_UNREGISTER(resolver_resolve_sync);

    
   
1297
	AST_TEST_UNREGISTER(resolver_resolve_sync_off_nominal);

    
   
1298
	AST_TEST_UNREGISTER(resolver_resolve_async);

    
   
1299
	AST_TEST_UNREGISTER(resolver_resolve_async_off_nominal);

    
   
1300
	AST_TEST_UNREGISTER(resolver_resolve_async_cancel);

    
   
1301

   

    
   
1302
	return 0;

    
   
1303
}

    
   
1304

   

    
   
1305
static int load_module(void)

    
   
1306
{

    
   
1307
	AST_TEST_REGISTER(resolver_register_unregister);

    
   
1308
	AST_TEST_REGISTER(resolver_register_off_nominal);

    
   
1309
	AST_TEST_REGISTER(resolver_unregister_off_nominal);

    
   
1310
	AST_TEST_REGISTER(resolver_data);

    
   
1311
	AST_TEST_REGISTER(resolver_set_result);

    
   
1312
	AST_TEST_REGISTER(resolver_set_result_off_nominal);

    
   
1313
	AST_TEST_REGISTER(resolver_add_record);

    
   
1314
	AST_TEST_REGISTER(resolver_add_record_off_nominal);

    
   
1315
	AST_TEST_REGISTER(resolver_resolve_sync);

    
   
1316
	AST_TEST_REGISTER(resolver_resolve_sync_off_nominal);

    
   
1317
	AST_TEST_REGISTER(resolver_resolve_async);

    
   
1318
	AST_TEST_REGISTER(resolver_resolve_async_off_nominal);

    
   
1319
	AST_TEST_REGISTER(resolver_resolve_async_cancel);

    
   
1320

   

    
   
1321
	return AST_MODULE_LOAD_SUCCESS;

    
   
1322
}

    
   
1323

   

    
   
1324
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DNS API Tests");
  1. /trunk/tests/test_dns.c: Loading...

https://reviewboard.asterisk.org/ runs on a server provided by Digium, Inc. and uses bandwidth donated to the open source Asterisk community by API Digital Communications in Huntsville, AL USA.
Please report problems with this site to asteriskteam@digium.com.