1 /*
2  * Copyright (C) Jakub Hrozek 2014 <jakub.hrozek@posteo.se>
3  *
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the author nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include <stdarg.h>
35 #include <stddef.h>
36 #include <setjmp.h>
37 #include <cmocka.h>
38 
39 #include "config.h"
40 
41 #include <stdlib.h>
42 #include <unistd.h>
43 #include <string.h>
44 #include <stdio.h>
45 
46 #include <netinet/in.h>
47 #include <arpa/nameser.h>
48 #include <arpa/inet.h>
49 #include <resolv.h>
50 
51 #define ANSIZE 256
52 #define ns_t_uri 256
53 
test_res_fake_a_query(void ** state)54 static void test_res_fake_a_query(void **state)
55 {
56 	int rv;
57 	struct __res_state dnsstate;
58 	unsigned char answer[ANSIZE];
59 	char addr[INET_ADDRSTRLEN];
60 	ns_msg handle;
61 	ns_rr rr;   /* expanded resource record */
62 
63 	(void) state; /* unused */
64 
65 	memset(&dnsstate, 0, sizeof(struct __res_state));
66 	rv = res_ninit(&dnsstate);
67 	assert_int_equal(rv, 0);
68 
69 	rv = res_nquery(&dnsstate, "cwrap.org", ns_c_in, ns_t_a,
70 			answer, sizeof(answer));
71 	assert_in_range(rv, 1, 100);
72 
73 	ns_initparse(answer, sizeof(answer), &handle);
74 	/* The query must finish w/o an error, have one answer and the answer
75 	 * must be a parseable RR of type A and have the address that our
76 	 * fake hosts file contains
77 	 */
78 	assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
79 	assert_int_equal(ns_msg_count(handle, ns_s_an), 1);
80 	assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
81 	assert_int_equal(ns_rr_type(rr), ns_t_a);
82 	assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr),
83 			addr, sizeof(addr)));
84 	assert_string_equal(addr, "127.0.0.21");
85 }
86 
test_res_fake_a_query_case_insensitive(void ** state)87 static void test_res_fake_a_query_case_insensitive(void **state)
88 {
89 	int rv;
90 	struct __res_state dnsstate;
91 	unsigned char answer[ANSIZE];
92 	char addr[INET_ADDRSTRLEN];
93 	ns_msg handle;
94 	ns_rr rr;   /* expanded resource record */
95 
96 	(void) state; /* unused */
97 
98 	memset(&dnsstate, 0, sizeof(struct __res_state));
99 	rv = res_ninit(&dnsstate);
100 	assert_int_equal(rv, 0);
101 
102 	rv = res_nquery(&dnsstate, "CWRAP.ORG", ns_c_in, ns_t_a,
103 			answer, sizeof(answer));
104 	assert_in_range(rv, 1, 100);
105 
106 	ns_initparse(answer, sizeof(answer), &handle);
107 	/* The query must finish w/o an error, have one answer and the answer
108 	 * must be a parseable RR of type A and have the address that our
109 	 * fake hosts file contains. Case does not matter.
110 	 */
111 	assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
112 	assert_int_equal(ns_msg_count(handle, ns_s_an), 1);
113 	assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
114 	assert_int_equal(ns_rr_type(rr), ns_t_a);
115 	assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr),
116 			addr, sizeof(addr)));
117 	assert_string_equal(addr, "127.0.0.21");
118 
119 	res_nclose(&dnsstate);
120 }
121 
test_res_fake_a_query_trailing_dot(void ** state)122 static void test_res_fake_a_query_trailing_dot(void **state)
123 {
124 	int rv;
125 	struct __res_state dnsstate;
126 	unsigned char answer[ANSIZE];
127 	char addr[INET_ADDRSTRLEN];
128 	ns_msg handle;
129 	ns_rr rr;   /* expanded resource record */
130 
131 	(void) state; /* unused */
132 
133 	memset(&dnsstate, 0, sizeof(struct __res_state));
134 	rv = res_ninit(&dnsstate);
135 	assert_int_equal(rv, 0);
136 
137 	rv = res_nquery(&dnsstate, "cwrap.org.", ns_c_in, ns_t_a,
138 			answer, ANSIZE);
139 	assert_in_range(rv, 1, 100);
140 
141 	ns_initparse(answer, 256, &handle);
142 	/* The query must finish w/o an error, have one answer and the answer
143 	 * must be a parseable RR of type A and have the address that our
144 	 * fake hosts file contains
145 	 */
146 	assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
147 	assert_int_equal(ns_msg_count(handle, ns_s_an), 1);
148 	assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
149 	assert_int_equal(ns_rr_type(rr), ns_t_a);
150 	assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr), addr, 256));
151 	assert_string_equal(addr, "127.0.0.21");
152 
153 	res_nclose(&dnsstate);
154 }
155 
test_res_fake_a_query_notfound(void ** state)156 static void test_res_fake_a_query_notfound(void **state)
157 {
158 	int rv;
159 	struct __res_state dnsstate;
160 	unsigned char answer[ANSIZE];
161 	ns_msg handle;
162 
163 	(void) state; /* unused */
164 
165 	memset(&dnsstate, 0, sizeof(struct __res_state));
166 	rv = res_ninit(&dnsstate);
167 	assert_int_equal(rv, 0);
168 
169 	rv = res_nquery(&dnsstate, "nosuchentry.org", ns_c_in, ns_t_a,
170 			answer, sizeof(answer));
171 	assert_in_range(rv, 1, 100);
172 
173 	ns_initparse(answer, sizeof(answer), &handle);
174 	/* The query must finish w/o an error and have no answer */
175 	assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
176 	assert_int_equal(ns_msg_count(handle, ns_s_an), 0);
177 }
178 
test_res_fake_aaaa_query(void ** state)179 static void test_res_fake_aaaa_query(void **state)
180 {
181 	int rv;
182 	struct __res_state dnsstate;
183 	unsigned char answer[ANSIZE];
184 	char addr[INET6_ADDRSTRLEN];
185 	ns_msg handle;
186 	ns_rr rr;   /* expanded resource record */
187 
188 	(void) state; /* unused */
189 
190 	memset(&dnsstate, 0, sizeof(struct __res_state));
191 	rv = res_ninit(&dnsstate);
192 	assert_int_equal(rv, 0);
193 
194 	rv = res_nquery(&dnsstate, "cwrap6.org", ns_c_in, ns_t_aaaa,
195 			answer, sizeof(answer));
196 	assert_in_range(rv, 1, 100);
197 
198 	ns_initparse(answer, sizeof(answer), &handle);
199 	/* The query must finish w/o an error, have one answer and the answer
200 	 * must be a parseable RR of type AAAA and have the address that our
201 	 * fake hosts file contains
202 	 */
203 	assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
204 	assert_int_equal(ns_msg_count(handle, ns_s_an), 1);
205 	assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
206 	assert_int_equal(ns_rr_type(rr), ns_t_aaaa);
207 	assert_non_null(inet_ntop(AF_INET6, ns_rr_rdata(rr),
208 			addr, sizeof(addr)));
209 	assert_string_equal(addr, "2a00:1450:4013:c01::63");
210 }
211 
test_res_fake_aaaa_query_notfound(void ** state)212 static void test_res_fake_aaaa_query_notfound(void **state)
213 {
214 	int rv;
215 	struct __res_state dnsstate;
216 	unsigned char answer[ANSIZE];
217 	ns_msg handle;
218 
219 	(void) state; /* unused */
220 
221 	memset(&dnsstate, 0, sizeof(struct __res_state));
222 	rv = res_ninit(&dnsstate);
223 	assert_int_equal(rv, 0);
224 
225 	rv = res_nquery(&dnsstate, "nosuchentry.org", ns_c_in, ns_t_aaaa,
226 			answer, sizeof(answer));
227 	assert_in_range(rv, 1, 100);
228 
229 	ns_initparse(answer, sizeof(answer), &handle);
230 	/* The query must finish w/o an error and have no answer */
231 	assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
232 	assert_int_equal(ns_msg_count(handle, ns_s_an), 0);
233 }
234 
test_res_fake_srv_query(void ** state)235 static void test_res_fake_srv_query(void **state)
236 {
237 	int rv;
238 	struct __res_state dnsstate;
239 	unsigned char answer[ANSIZE];
240 	ns_msg handle;
241 	ns_rr rr;   /* expanded resource record */
242 	const uint8_t *rrdata;
243 	int prio;
244 	int weight;
245 	int port;
246 	char hostname[MAXDNAME];
247 
248 	(void) state; /* unused */
249 
250 	memset(&dnsstate, 0, sizeof(struct __res_state));
251 	rv = res_ninit(&dnsstate);
252 	assert_int_equal(rv, 0);
253 
254 	rv = res_nquery(&dnsstate, "_ldap._tcp.cwrap.org", ns_c_in, ns_t_srv,
255 			answer, sizeof(answer));
256 	assert_in_range(rv, 1, 100);
257 
258 	ns_initparse(answer, sizeof(answer), &handle);
259 
260 	/*
261 	 * The query must finish w/o an error, have one answer and the answer
262 	 * must be a parseable RR of type SRV and have the priority, weight,
263 	 * port and hostname as in the fake hosts file
264 	 */
265 	assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
266 	assert_int_equal(ns_msg_count(handle, ns_s_an), 1);
267 	assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
268 	assert_int_equal(ns_rr_type(rr), ns_t_srv);
269 
270 	rrdata = ns_rr_rdata(rr);
271 	NS_GET16(prio, rrdata);
272 	NS_GET16(weight, rrdata);
273 	NS_GET16(port, rrdata);
274 
275 	rv = ns_name_uncompress(ns_msg_base(handle),
276 				ns_msg_end(handle),
277 				rrdata,
278 				hostname, MAXDNAME);
279 	assert_int_not_equal(rv, -1);
280 
281 	assert_int_equal(prio, 1);
282 	assert_int_equal(weight, 5);
283 	assert_int_equal(port, 389);
284 	assert_string_equal(hostname, "ldap.cwrap.org");
285 }
286 
287 /*
288  * Test the case of a SRV record query where the
289  * fake hosts file entry is minimal in the sense
290  * that it omits the priority and weight entries.
291  * The server then fills in some default values.
292  */
test_res_fake_srv_query_minimal(void ** state)293 static void test_res_fake_srv_query_minimal(void **state)
294 {
295 	int rv;
296 	struct __res_state dnsstate;
297 	unsigned char answer[ANSIZE];
298 	ns_msg handle;
299 	ns_rr rr;   /* expanded resource record */
300 	const uint8_t *rrdata;
301 	int prio;
302 	int weight;
303 	int port;
304 	char hostname[MAXDNAME];
305 	char addr[INET_ADDRSTRLEN];
306 
307 	(void) state; /* unused */
308 
309 	memset(&dnsstate, 0, sizeof(struct __res_state));
310 	rv = res_ninit(&dnsstate);
311 	assert_int_equal(rv, 0);
312 
313 	rv = res_nquery(&dnsstate, "_krb5._tcp.cwrap.org", ns_c_in, ns_t_srv,
314 			answer, sizeof(answer));
315 	assert_in_range(rv, 1, 256);
316 
317 	ns_initparse(answer, sizeof(answer), &handle);
318 
319 	/*
320 	 * The query must finish w/o an error, have one answer and the answer
321 	 * must be a parseable RR of type SRV and have the priority, weight,
322 	 * port and hostname as in the fake hosts file
323 	 */
324 	assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
325 	assert_int_equal(ns_msg_count(handle, ns_s_an), 1);
326 	assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
327 	assert_int_equal(ns_rr_type(rr), ns_t_srv);
328 
329 	rrdata = ns_rr_rdata(rr);
330 	NS_GET16(prio, rrdata);
331 	NS_GET16(weight, rrdata);
332 	NS_GET16(port, rrdata);
333 
334 	rv = ns_name_uncompress(ns_msg_base(handle),
335 				ns_msg_end(handle),
336 				rrdata,
337 				hostname, MAXDNAME);
338 	assert_int_not_equal(rv, -1);
339 
340 	assert_int_equal(prio, 1);
341 	assert_int_equal(weight, 100);
342 	assert_int_equal(port, 88);
343 	assert_string_equal(hostname, "krb5.cwrap.org");
344 
345 	/* The additional section contains the A record of krb5.cwrap.org */
346 	assert_int_equal(ns_msg_count(handle, ns_s_ar), 1);
347 	assert_int_equal(ns_parserr(&handle, ns_s_ar, 0, &rr), 0);
348 	assert_int_equal(ns_rr_type(rr), ns_t_a);
349 	assert_string_equal(ns_rr_name(rr), "krb5.cwrap.org");
350 	assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr),
351 			addr, sizeof(addr)));
352 	assert_string_equal(addr, "127.0.0.23");
353 }
354 
test_res_fake_uri_query(void ** state)355 static void test_res_fake_uri_query(void **state)
356 {
357 	int rv;
358 	struct __res_state dnsstate;
359 	unsigned char answer[ANSIZE];
360 	ns_msg handle;
361 	ns_rr rr;   /* expanded resource record */
362 	const uint8_t *rrdata;
363 	int prio;
364 	int weight;
365 
366 	(void) state; /* unused */
367 
368 	memset(&dnsstate, 0, sizeof(struct __res_state));
369 	rv = res_ninit(&dnsstate);
370 	assert_int_equal(rv, 0);
371 
372 	rv = res_nquery(&dnsstate, "_vpn.cwrap.org", ns_c_in, ns_t_uri,
373 			answer, sizeof(answer));
374 	assert_in_range(rv, 1, ANSIZE);
375 
376 	ns_initparse(answer, sizeof(answer), &handle);
377 
378 	/*
379 	 * The query must finish w/o an error, have three answers and they must be
380 	 * a parseable RR of type URI and have the priority, weight, and URI string
381 	 * as in the hosts file.
382 	 */
383 	assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
384 	assert_int_equal(ns_msg_count(handle, ns_s_an), 3);
385 
386 	assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
387 	assert_int_equal(ns_rr_type(rr), ns_t_uri);
388 	rrdata = ns_rr_rdata(rr);
389 	NS_GET16(prio, rrdata);
390 	NS_GET16(weight, rrdata);
391 
392 	assert_int_equal(prio, 2);
393 	assert_int_equal(weight, 5);
394 	assert_string_equal(rrdata, "https://vpn.cwrap.org/VPN");
395 
396 	assert_int_equal(ns_parserr(&handle, ns_s_an, 1, &rr), 0);
397 	assert_int_equal(ns_rr_type(rr), ns_t_uri);
398 	rrdata = ns_rr_rdata(rr);
399 	NS_GET16(prio, rrdata);
400 	NS_GET16(weight, rrdata);
401 
402 	assert_int_equal(prio, 2);
403 	assert_int_equal(weight, 10);
404 	assert_string_equal(rrdata, "https://vpn2.cwrap.org/VPN");
405 
406 	assert_int_equal(ns_parserr(&handle, ns_s_an, 2, &rr), 0);
407 	assert_int_equal(ns_rr_type(rr), ns_t_uri);
408 	rrdata = ns_rr_rdata(rr);
409 	NS_GET16(prio, rrdata);
410 	NS_GET16(weight, rrdata);
411 
412 	assert_int_equal(prio, 2);
413 	assert_int_equal(weight, 20);
414 	assert_string_equal(rrdata, "https://vpn3.cwrap.org/VPN");
415 
416 }
417 
418 /*
419  * Test the case of a URI record query where the
420  * fake hosts file entry is minimal in the sense
421  * that it omits the priority and weight entries.
422  * The server then fills in some default values.
423  */
test_res_fake_uri_query_minimal(void ** state)424 static void test_res_fake_uri_query_minimal(void **state)
425 {
426 	int rv;
427 	struct __res_state dnsstate;
428 	unsigned char answer[ANSIZE];
429 	ns_msg handle;
430 	ns_rr rr;   /* expanded resource record */
431 	const uint8_t *rrdata;
432 	int prio;
433 	int weight;
434 
435 	(void) state; /* unused */
436 
437 	memset(&dnsstate, 0, sizeof(struct __res_state));
438 	rv = res_ninit(&dnsstate);
439 	assert_int_equal(rv, 0);
440 
441 	rv = res_nquery(&dnsstate, "_ftp.cwrap.org", ns_c_in, ns_t_uri,
442 			answer, sizeof(answer));
443 	assert_in_range(rv, 1, 256);
444 
445 	ns_initparse(answer, sizeof(answer), &handle);
446 
447 	/*
448 	 * The query must finish w/o an error, have one answer and the answer
449 	 * must be a parseable RR of type URI and have the priority, weight, and
450 	 * URI string as in the fake hosts file
451 	 */
452 	assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
453 	assert_int_equal(ns_msg_count(handle, ns_s_an), 1);
454 	assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
455 	assert_int_equal(ns_rr_type(rr), ns_t_uri);
456 
457 	rrdata = ns_rr_rdata(rr);
458 	NS_GET16(prio, rrdata);
459 	NS_GET16(weight, rrdata);
460 
461 	assert_int_equal(prio, 1);
462 	assert_int_equal(weight, 100);
463 	assert_string_equal(rrdata, "ftp://ftp.cwrap.org/public");
464 }
465 
test_res_fake_soa_query(void ** state)466 static void test_res_fake_soa_query(void **state)
467 {
468 	int rv;
469 	struct __res_state dnsstate;
470 	unsigned char answer[ANSIZE];
471 	ns_msg handle;
472 	ns_rr rr;   /* expanded resource record */
473 	const uint8_t *rrdata;
474 	char nameser[MAXDNAME];
475 	char admin[MAXDNAME];
476 	int serial;
477 	int refresh;
478 	int retry;
479 	int expire;
480 	int minimum;
481 
482 	(void) state; /* unused */
483 
484 	memset(&dnsstate, 0, sizeof(struct __res_state));
485 	rv = res_ninit(&dnsstate);
486 	assert_int_equal(rv, 0);
487 
488 	rv = res_nquery(&dnsstate, "cwrap.org", ns_c_in, ns_t_soa,
489 			answer, sizeof(answer));
490 	assert_in_range(rv, 1, 100);
491 
492 	ns_initparse(answer, sizeof(answer), &handle);
493 
494 	/*
495 	 * The query must finish w/o an error, have one answer and the answer
496 	 * must be a parseable RR of type SOA and have the data as in the fake
497 	 * hosts file
498 	 */
499 	assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
500 	assert_int_equal(ns_msg_count(handle, ns_s_an), 1);
501 	assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
502 	assert_int_equal(ns_rr_type(rr), ns_t_soa);
503 
504 	rrdata = ns_rr_rdata(rr);
505 
506 	rv = ns_name_uncompress(ns_msg_base(handle),
507 				ns_msg_end(handle),
508 				rrdata,
509 				nameser, MAXDNAME);
510 	assert_int_not_equal(rv, -1);
511 	rrdata += rv;
512 
513 	rv = ns_name_uncompress(ns_msg_base(handle),
514 				ns_msg_end(handle),
515 				rrdata,
516 				admin, MAXDNAME);
517 	assert_int_not_equal(rv, -1);
518 	rrdata += rv;
519 
520 	NS_GET32(serial, rrdata);
521 	NS_GET32(refresh, rrdata);
522 	NS_GET32(retry, rrdata);
523 	NS_GET32(expire, rrdata);
524 	NS_GET32(minimum, rrdata);
525 
526 	assert_string_equal(nameser, "ns1.cwrap.org");
527 	assert_string_equal(admin, "admin.cwrap.org");
528 	assert_int_equal(serial, 2014100457);
529 	assert_int_equal(refresh, 3600);
530 	assert_int_equal(retry, 300);
531 	assert_int_equal(expire, 1814400);
532 	assert_int_equal(minimum, 600);
533 }
534 
test_res_fake_cname_query(void ** state)535 static void test_res_fake_cname_query(void **state)
536 {
537 	int rv;
538 	struct __res_state dnsstate;
539 	unsigned char answer[ANSIZE];
540 	ns_msg handle;
541 	ns_rr rr;   /* expanded resource record */
542 	const uint8_t *rrdata;
543 	char cname[MAXDNAME];
544 	char addr[INET_ADDRSTRLEN];
545 
546 	(void) state; /* unused */
547 
548 	memset(&dnsstate, 0, sizeof(struct __res_state));
549 	rv = res_ninit(&dnsstate);
550 	assert_int_equal(rv, 0);
551 
552 	rv = res_nquery(&dnsstate, "rwrap.org", ns_c_in, ns_t_cname,
553 			answer, sizeof(answer));
554 	assert_in_range(rv, 1, 256);
555 
556 	ns_initparse(answer, 256, &handle);
557 	ns_initparse(answer, sizeof(answer), &handle);
558 
559 	/*
560 	 * The query must finish w/o an error, have one answer and the answer
561 	 * must be a parseable RR of type CNAME and have the cname as in the
562 	 * fake hosts file
563 	 */
564 	assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
565 	assert_int_equal(ns_msg_count(handle, ns_s_an), 1);
566 	assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
567 	assert_int_equal(ns_rr_type(rr), ns_t_cname);
568 
569 	rrdata = ns_rr_rdata(rr);
570 
571 	rv = ns_name_uncompress(ns_msg_base(handle),
572 				ns_msg_end(handle),
573 				rrdata,
574 				cname, MAXDNAME);
575 	assert_int_not_equal(rv, -1);
576 
577 	assert_string_equal(cname, "web.cwrap.org");
578 
579 	/* The CNAME points to an A record that's present in the additional
580 	 * section
581 	 */
582 	assert_int_equal(ns_msg_count(handle, ns_s_ar), 2);
583 
584 	assert_int_equal(ns_parserr(&handle, ns_s_ar, 0, &rr), 0);
585 	assert_int_equal(ns_rr_type(rr), ns_t_cname);
586 	assert_string_equal(ns_rr_name(rr), "web.cwrap.org");
587 	rrdata = ns_rr_rdata(rr);
588 
589 	rv = ns_name_uncompress(ns_msg_base(handle),
590 				ns_msg_end(handle),
591 				rrdata,
592 				cname, MAXDNAME);
593 	assert_int_not_equal(rv, -1);
594 
595 	assert_string_equal(cname, "www.cwrap.org");
596 
597 	assert_int_equal(ns_parserr(&handle, ns_s_ar, 1, &rr), 0);
598 	assert_int_equal(ns_rr_type(rr), ns_t_a);
599 	assert_string_equal(ns_rr_name(rr), "www.cwrap.org");
600 	assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr),
601 			addr, sizeof(addr)));
602 	assert_string_equal(addr, "127.0.0.22");
603 }
604 
test_res_fake_a_via_cname(void ** state)605 static void test_res_fake_a_via_cname(void **state)
606 {
607 	int rv;
608 	struct __res_state dnsstate;
609 	unsigned char answer[ANSIZE];
610 	ns_msg handle;
611 	ns_rr rr;   /* expanded resource record */
612 	const uint8_t *rrdata;
613 	char cname[MAXDNAME];
614 	char addr[INET_ADDRSTRLEN];
615 
616 	(void) state; /* unused */
617 
618 	memset(&dnsstate, 0, sizeof(struct __res_state));
619 	rv = res_ninit(&dnsstate);
620 	assert_int_equal(rv, 0);
621 
622 	/* Query for A record, but the key is a CNAME. The expected result is
623 	 * that the whole chain of CNAMEs will be included in the answer section
624 	 * along with the resulting A
625 	 */
626 	rv = res_nquery(&dnsstate, "rwrap.org", ns_c_in, ns_t_a,
627 			answer, sizeof(answer));
628 	assert_in_range(rv, 1, 256);
629 
630 	ns_initparse(answer, sizeof(answer), &handle);
631 
632 	/*
633 	 * The query must finish w/o an error, have three answers and the answers
634 	 * must be a parseable RR of type CNAME and have the cname as in the
635 	 * fake hosts file
636 	 */
637 	assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
638 	assert_int_equal(ns_msg_count(handle, ns_s_an), 3);
639 
640 	assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
641 	assert_int_equal(ns_rr_type(rr), ns_t_cname);
642 
643 	rrdata = ns_rr_rdata(rr);
644 
645 	rv = ns_name_uncompress(ns_msg_base(handle),
646 				ns_msg_end(handle),
647 				rrdata,
648 				cname, MAXDNAME);
649 	assert_int_not_equal(rv, -1);
650 
651 	assert_string_equal(cname, "web.cwrap.org");
652 
653 	assert_int_equal(ns_parserr(&handle, ns_s_an, 1, &rr), 0);
654 	assert_int_equal(ns_rr_type(rr), ns_t_cname);
655 
656 	rrdata = ns_rr_rdata(rr);
657 
658 	rv = ns_name_uncompress(ns_msg_base(handle),
659 				ns_msg_end(handle),
660 				rrdata,
661 				cname, MAXDNAME);
662 	assert_int_not_equal(rv, -1);
663 
664 	assert_string_equal(cname, "www.cwrap.org");
665 
666 	assert_int_equal(ns_parserr(&handle, ns_s_an, 2, &rr), 0);
667 	assert_int_equal(ns_rr_type(rr), ns_t_a);
668 	assert_string_equal(ns_rr_name(rr), "www.cwrap.org");
669 	assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr),
670 			addr, sizeof(addr)));
671 	assert_string_equal(addr, "127.0.0.22");
672 }
673 
test_res_fake_ptr_query(void ** state)674 static void test_res_fake_ptr_query(void **state)
675 {
676 	int rv;
677 	struct __res_state dnsstate;
678 	unsigned char answer[ANSIZE];
679 	const uint8_t *rrdata;
680 	char ptrname[MAXDNAME];
681 	ns_msg handle;
682 	ns_rr rr;   /* expanded resource record */
683 
684 	(void) state; /* unused */
685 
686 	memset(&dnsstate, 0, sizeof(struct __res_state));
687 	rv = res_ninit(&dnsstate);
688 	assert_int_equal(rv, 0);
689 
690 	rv = res_nquery(&dnsstate, "22.0.0.127.in-addr.arpa", ns_c_in, ns_t_ptr,
691 			answer, sizeof(answer));
692 	assert_in_range(rv, 1, 100);
693 
694 	ns_initparse(answer, sizeof(answer), &handle);
695 
696 	/*
697 	 * The query must finish w/o an error, have one answer and the answer
698 	 * must be a parseable RR of type PTR and have the name that our
699 	 * fake hosts file contains
700 	 */
701 	assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
702 	assert_int_equal(ns_msg_count(handle, ns_s_an), 1);
703 	assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
704 	assert_int_equal(ns_rr_type(rr), ns_t_ptr);
705 
706 	rrdata = ns_rr_rdata(rr);
707 
708 	rv = ns_name_uncompress(ns_msg_base(handle),
709 				ns_msg_end(handle),
710 				rrdata,
711 				ptrname, MAXDNAME);
712 	assert_int_not_equal(rv, -1);
713 
714 	assert_string_equal(ptrname, "www.cwrap.org");
715 }
716 
main(void)717 int main(void)
718 {
719 	int rc;
720 
721 	const struct CMUnitTest fake_tests[] = {
722 		cmocka_unit_test(test_res_fake_a_query),
723 		cmocka_unit_test(test_res_fake_a_query_case_insensitive),
724 		cmocka_unit_test(test_res_fake_a_query_trailing_dot),
725 		cmocka_unit_test(test_res_fake_a_query_notfound),
726 		cmocka_unit_test(test_res_fake_aaaa_query),
727 		cmocka_unit_test(test_res_fake_aaaa_query_notfound),
728 		cmocka_unit_test(test_res_fake_srv_query),
729 		cmocka_unit_test(test_res_fake_srv_query_minimal),
730 		cmocka_unit_test(test_res_fake_uri_query),
731 		cmocka_unit_test(test_res_fake_uri_query_minimal),
732 		cmocka_unit_test(test_res_fake_soa_query),
733 		cmocka_unit_test(test_res_fake_cname_query),
734 		cmocka_unit_test(test_res_fake_a_via_cname),
735 		cmocka_unit_test(test_res_fake_ptr_query),
736 	};
737 
738 	rc = cmocka_run_group_tests(fake_tests, NULL, NULL);
739 
740 	return rc;
741 }
742