9bc92cd16dfb9694e21c539885b857f17fe2e56d
[obnox/cwrap/resolv_wrapper.git] / tests / test_dns_fake.c
1 /*
2  * Copyright (C) Jakub Hrozek 2014 <jakub.hrozek@gmail.com>
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
53 static void test_res_fake_a_query(void **state)
54 {
55         int rv;
56         struct __res_state dnsstate;
57         unsigned char answer[ANSIZE];
58         char addr[INET_ADDRSTRLEN];
59         ns_msg handle;
60         ns_rr rr;   /* expanded resource record */
61
62         (void) state; /* unused */
63
64         memset(&dnsstate, 0, sizeof(struct __res_state));
65         rv = res_ninit(&dnsstate);
66         assert_int_equal(rv, 0);
67
68         rv = res_nquery(&dnsstate, "cwrap.org", ns_c_in, ns_t_a,
69                         answer, sizeof(answer));
70         assert_in_range(rv, 1, 100);
71
72         ns_initparse(answer, sizeof(answer), &handle);
73         /* The query must finish w/o an error, have one answer and the answer
74          * must be a parseable RR of type A and have the address that our
75          * fake hosts file contains
76          */
77         assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
78         assert_int_equal(ns_msg_count(handle, ns_s_an), 1);
79         assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
80         assert_int_equal(ns_rr_type(rr), ns_t_a);
81         assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr),
82                         addr, sizeof(addr)));
83         assert_string_equal(addr, "127.0.0.21");
84 }
85
86 static void test_res_fake_a_query_case_insensitive(void **state)
87 {
88         int rv;
89         struct __res_state dnsstate;
90         unsigned char answer[ANSIZE];
91         char addr[INET_ADDRSTRLEN];
92         ns_msg handle;
93         ns_rr rr;   /* expanded resource record */
94
95         (void) state; /* unused */
96
97         memset(&dnsstate, 0, sizeof(struct __res_state));
98         rv = res_ninit(&dnsstate);
99         assert_int_equal(rv, 0);
100
101         rv = res_nquery(&dnsstate, "CWRAP.ORG", ns_c_in, ns_t_a,
102                         answer, sizeof(answer));
103         assert_in_range(rv, 1, 100);
104
105         ns_initparse(answer, sizeof(answer), &handle);
106         /* The query must finish w/o an error, have one answer and the answer
107          * must be a parseable RR of type A and have the address that our
108          * fake hosts file contains. Case does not matter.
109          */
110         assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
111         assert_int_equal(ns_msg_count(handle, ns_s_an), 1);
112         assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
113         assert_int_equal(ns_rr_type(rr), ns_t_a);
114         assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr),
115                         addr, sizeof(addr)));
116         assert_string_equal(addr, "127.0.0.21");
117
118         res_nclose(&dnsstate);
119 }
120
121 static void test_res_fake_a_query_trailing_dot(void **state)
122 {
123         int rv;
124         struct __res_state dnsstate;
125         unsigned char answer[ANSIZE];
126         char addr[INET_ADDRSTRLEN];
127         ns_msg handle;
128         ns_rr rr;   /* expanded resource record */
129
130         (void) state; /* unused */
131
132         memset(&dnsstate, 0, sizeof(struct __res_state));
133         rv = res_ninit(&dnsstate);
134         assert_int_equal(rv, 0);
135
136         rv = res_nquery(&dnsstate, "cwrap.org.", ns_c_in, ns_t_a,
137                         answer, ANSIZE);
138         assert_in_range(rv, 1, 100);
139
140         ns_initparse(answer, 256, &handle);
141         /* The query must finish w/o an error, have one answer and the answer
142          * must be a parseable RR of type A and have the address that our
143          * fake hosts file contains
144          */
145         assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
146         assert_int_equal(ns_msg_count(handle, ns_s_an), 1);
147         assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
148         assert_int_equal(ns_rr_type(rr), ns_t_a);
149         assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr), addr, 256));
150         assert_string_equal(addr, "127.0.0.21");
151
152         res_nclose(&dnsstate);
153 }
154
155 static void test_res_fake_a_query_notfound(void **state)
156 {
157         int rv;
158         struct __res_state dnsstate;
159         unsigned char answer[ANSIZE];
160         ns_msg handle;
161
162         (void) state; /* unused */
163
164         memset(&dnsstate, 0, sizeof(struct __res_state));
165         rv = res_ninit(&dnsstate);
166         assert_int_equal(rv, 0);
167
168         rv = res_nquery(&dnsstate, "nosuchentry.org", ns_c_in, ns_t_a,
169                         answer, sizeof(answer));
170         assert_in_range(rv, 1, 100);
171
172         ns_initparse(answer, sizeof(answer), &handle);
173         /* The query must finish w/o an error and have no answer */
174         assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
175         assert_int_equal(ns_msg_count(handle, ns_s_an), 0);
176 }
177
178 static void test_res_fake_aaaa_query(void **state)
179 {
180         int rv;
181         struct __res_state dnsstate;
182         unsigned char answer[ANSIZE];
183         char addr[INET6_ADDRSTRLEN];
184         ns_msg handle;
185         ns_rr rr;   /* expanded resource record */
186
187         (void) state; /* unused */
188
189         memset(&dnsstate, 0, sizeof(struct __res_state));
190         rv = res_ninit(&dnsstate);
191         assert_int_equal(rv, 0);
192
193         rv = res_nquery(&dnsstate, "cwrap6.org", ns_c_in, ns_t_aaaa,
194                         answer, sizeof(answer));
195         assert_in_range(rv, 1, 100);
196
197         ns_initparse(answer, sizeof(answer), &handle);
198         /* The query must finish w/o an error, have one answer and the answer
199          * must be a parseable RR of type AAAA and have the address that our
200          * fake hosts file contains
201          */
202         assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
203         assert_int_equal(ns_msg_count(handle, ns_s_an), 1);
204         assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
205         assert_int_equal(ns_rr_type(rr), ns_t_aaaa);
206         assert_non_null(inet_ntop(AF_INET6, ns_rr_rdata(rr),
207                         addr, sizeof(addr)));
208         assert_string_equal(addr, "2a00:1450:4013:c01::63");
209 }
210
211 static void test_res_fake_aaaa_query_notfound(void **state)
212 {
213         int rv;
214         struct __res_state dnsstate;
215         unsigned char answer[ANSIZE];
216         ns_msg handle;
217
218         (void) state; /* unused */
219
220         memset(&dnsstate, 0, sizeof(struct __res_state));
221         rv = res_ninit(&dnsstate);
222         assert_int_equal(rv, 0);
223
224         rv = res_nquery(&dnsstate, "nosuchentry.org", ns_c_in, ns_t_aaaa,
225                         answer, sizeof(answer));
226         assert_in_range(rv, 1, 100);
227
228         ns_initparse(answer, sizeof(answer), &handle);
229         /* The query must finish w/o an error and have no answer */
230         assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
231         assert_int_equal(ns_msg_count(handle, ns_s_an), 0);
232 }
233
234 static void test_res_fake_srv_query(void **state)
235 {
236         int rv;
237         struct __res_state dnsstate;
238         unsigned char answer[ANSIZE];
239         ns_msg handle;
240         ns_rr rr;   /* expanded resource record */
241         const uint8_t *rrdata;
242         int prio;
243         int weight;
244         int port;
245         char hostname[MAXDNAME];
246
247         (void) state; /* unused */
248
249         memset(&dnsstate, 0, sizeof(struct __res_state));
250         rv = res_ninit(&dnsstate);
251         assert_int_equal(rv, 0);
252
253         rv = res_nquery(&dnsstate, "_ldap._tcp.cwrap.org", ns_c_in, ns_t_srv,
254                         answer, sizeof(answer));
255         assert_in_range(rv, 1, 100);
256
257         ns_initparse(answer, sizeof(answer), &handle);
258
259         /*
260          * The query must finish w/o an error, have one answer and the answer
261          * must be a parseable RR of type SRV and have the priority, weight,
262          * port and hostname as in the fake hosts file
263          */
264         assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
265         assert_int_equal(ns_msg_count(handle, ns_s_an), 1);
266         assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
267         assert_int_equal(ns_rr_type(rr), ns_t_srv);
268
269         rrdata = ns_rr_rdata(rr);
270         NS_GET16(prio, rrdata);
271         NS_GET16(weight, rrdata);
272         NS_GET16(port, rrdata);
273
274         rv = ns_name_uncompress(ns_msg_base(handle),
275                                 ns_msg_end(handle),
276                                 rrdata,
277                                 hostname, MAXDNAME);
278         assert_int_not_equal(rv, -1);
279
280         assert_int_equal(prio, 1);
281         assert_int_equal(weight, 5);
282         assert_int_equal(port, 389);
283         assert_string_equal(hostname, "ldap.cwrap.org");
284 }
285
286 /*
287  * Test the case of a SRV record query where the
288  * fake hosts file entry is minimal in the sense
289  * that it omits the priority and weight entries.
290  * The server then fills in some default values.
291  */
292 static void test_res_fake_srv_query_minimal(void **state)
293 {
294         int rv;
295         struct __res_state dnsstate;
296         unsigned char answer[ANSIZE];
297         ns_msg handle;
298         ns_rr rr;   /* expanded resource record */
299         const uint8_t *rrdata;
300         int prio;
301         int weight;
302         int port;
303         char hostname[MAXDNAME];
304         char addr[INET_ADDRSTRLEN];
305
306         (void) state; /* unused */
307
308         memset(&dnsstate, 0, sizeof(struct __res_state));
309         rv = res_ninit(&dnsstate);
310         assert_int_equal(rv, 0);
311
312         rv = res_nquery(&dnsstate, "_krb5._tcp.cwrap.org", ns_c_in, ns_t_srv,
313                         answer, sizeof(answer));
314         assert_in_range(rv, 1, 256);
315
316         ns_initparse(answer, sizeof(answer), &handle);
317
318         /*
319          * The query must finish w/o an error, have one answer and the answer
320          * must be a parseable RR of type SRV and have the priority, weight,
321          * port and hostname as in the fake hosts file
322          */
323         assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
324         assert_int_equal(ns_msg_count(handle, ns_s_an), 1);
325         assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
326         assert_int_equal(ns_rr_type(rr), ns_t_srv);
327
328         rrdata = ns_rr_rdata(rr);
329         NS_GET16(prio, rrdata);
330         NS_GET16(weight, rrdata);
331         NS_GET16(port, rrdata);
332
333         rv = ns_name_uncompress(ns_msg_base(handle),
334                                 ns_msg_end(handle),
335                                 rrdata,
336                                 hostname, MAXDNAME);
337         assert_int_not_equal(rv, -1);
338
339         assert_int_equal(prio, 1);
340         assert_int_equal(weight, 100);
341         assert_int_equal(port, 88);
342         assert_string_equal(hostname, "krb5.cwrap.org");
343
344         /* The additional section contains the A record of krb5.cwrap.org */
345         assert_int_equal(ns_msg_count(handle, ns_s_ar), 1);
346         assert_int_equal(ns_parserr(&handle, ns_s_ar, 0, &rr), 0);
347         assert_int_equal(ns_rr_type(rr), ns_t_a);
348         assert_string_equal(ns_rr_name(rr), "krb5.cwrap.org");
349         assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr),
350                         addr, sizeof(addr)));
351         assert_string_equal(addr, "127.0.0.23");
352 }
353
354 static void test_res_fake_soa_query(void **state)
355 {
356         int rv;
357         struct __res_state dnsstate;
358         unsigned char answer[ANSIZE];
359         ns_msg handle;
360         ns_rr rr;   /* expanded resource record */
361         const uint8_t *rrdata;
362         char nameser[MAXDNAME];
363         char admin[MAXDNAME];
364         int serial;
365         int refresh;
366         int retry;
367         int expire;
368         int minimum;
369
370         (void) state; /* unused */
371
372         memset(&dnsstate, 0, sizeof(struct __res_state));
373         rv = res_ninit(&dnsstate);
374         assert_int_equal(rv, 0);
375
376         rv = res_nquery(&dnsstate, "cwrap.org", ns_c_in, ns_t_soa,
377                         answer, sizeof(answer));
378         assert_in_range(rv, 1, 100);
379
380         ns_initparse(answer, sizeof(answer), &handle);
381
382         /*
383          * The query must finish w/o an error, have one answer and the answer
384          * must be a parseable RR of type SOA and have the data as in the fake
385          * hosts file
386          */
387         assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
388         assert_int_equal(ns_msg_count(handle, ns_s_an), 1);
389         assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
390         assert_int_equal(ns_rr_type(rr), ns_t_soa);
391
392         rrdata = ns_rr_rdata(rr);
393
394         rv = ns_name_uncompress(ns_msg_base(handle),
395                                 ns_msg_end(handle),
396                                 rrdata,
397                                 nameser, MAXDNAME);
398         assert_int_not_equal(rv, -1);
399         rrdata += rv;
400
401         rv = ns_name_uncompress(ns_msg_base(handle),
402                                 ns_msg_end(handle),
403                                 rrdata,
404                                 admin, MAXDNAME);
405         assert_int_not_equal(rv, -1);
406         rrdata += rv;
407
408         NS_GET32(serial, rrdata);
409         NS_GET32(refresh, rrdata);
410         NS_GET32(retry, rrdata);
411         NS_GET32(expire, rrdata);
412         NS_GET32(minimum, rrdata);
413
414         assert_string_equal(nameser, "ns1.cwrap.org");
415         assert_string_equal(admin, "admin.cwrap.org");
416         assert_int_equal(serial, 2014100457);
417         assert_int_equal(refresh, 3600);
418         assert_int_equal(retry, 300);
419         assert_int_equal(expire, 1814400);
420         assert_int_equal(minimum, 600);
421 }
422
423 static void test_res_fake_cname_query(void **state)
424 {
425         int rv;
426         struct __res_state dnsstate;
427         unsigned char answer[ANSIZE];
428         ns_msg handle;
429         ns_rr rr;   /* expanded resource record */
430         const uint8_t *rrdata;
431         char cname[MAXDNAME];
432         char addr[INET_ADDRSTRLEN];
433
434         (void) state; /* unused */
435
436         memset(&dnsstate, 0, sizeof(struct __res_state));
437         rv = res_ninit(&dnsstate);
438         assert_int_equal(rv, 0);
439
440         rv = res_nquery(&dnsstate, "rwrap.org", ns_c_in, ns_t_cname,
441                         answer, sizeof(answer));
442         assert_in_range(rv, 1, 256);
443
444         ns_initparse(answer, 256, &handle);
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 CNAME and have the cname as in the
450          * 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_cname);
456
457         rrdata = ns_rr_rdata(rr);
458
459         rv = ns_name_uncompress(ns_msg_base(handle),
460                                 ns_msg_end(handle),
461                                 rrdata,
462                                 cname, MAXDNAME);
463         assert_int_not_equal(rv, -1);
464
465         assert_string_equal(cname, "web.cwrap.org");
466
467         /* The CNAME points to an A record that's present in the additional
468          * section
469          */
470         assert_int_equal(ns_msg_count(handle, ns_s_ar), 2);
471
472         assert_int_equal(ns_parserr(&handle, ns_s_ar, 0, &rr), 0);
473         assert_int_equal(ns_rr_type(rr), ns_t_cname);
474         assert_string_equal(ns_rr_name(rr), "web.cwrap.org");
475         rrdata = ns_rr_rdata(rr);
476
477         rv = ns_name_uncompress(ns_msg_base(handle),
478                                 ns_msg_end(handle),
479                                 rrdata,
480                                 cname, MAXDNAME);
481         assert_int_not_equal(rv, -1);
482
483         assert_string_equal(cname, "www.cwrap.org");
484
485         assert_int_equal(ns_parserr(&handle, ns_s_ar, 1, &rr), 0);
486         assert_int_equal(ns_rr_type(rr), ns_t_a);
487         assert_string_equal(ns_rr_name(rr), "www.cwrap.org");
488         assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr),
489                         addr, sizeof(addr)));
490         assert_string_equal(addr, "127.0.0.22");
491 }
492
493 static void test_res_fake_a_via_cname(void **state)
494 {
495         int rv;
496         struct __res_state dnsstate;
497         unsigned char answer[ANSIZE];
498         ns_msg handle;
499         ns_rr rr;   /* expanded resource record */
500         const uint8_t *rrdata;
501         char cname[MAXDNAME];
502         char addr[INET_ADDRSTRLEN];
503
504         (void) state; /* unused */
505
506         memset(&dnsstate, 0, sizeof(struct __res_state));
507         rv = res_ninit(&dnsstate);
508         assert_int_equal(rv, 0);
509
510         /* Query for A record, but the key is a CNAME. The expected result is
511          * that the whole chain of CNAMEs will be included in the answer section
512          * along with the resulting A
513          */
514         rv = res_nquery(&dnsstate, "rwrap.org", ns_c_in, ns_t_a,
515                         answer, sizeof(answer));
516         assert_in_range(rv, 1, 256);
517
518         ns_initparse(answer, sizeof(answer), &handle);
519
520         /*
521          * The query must finish w/o an error, have three answers and the answers
522          * must be a parseable RR of type CNAME and have the cname as in the
523          * fake hosts file
524          */
525         assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
526         assert_int_equal(ns_msg_count(handle, ns_s_an), 3);
527
528         assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
529         assert_int_equal(ns_rr_type(rr), ns_t_cname);
530
531         rrdata = ns_rr_rdata(rr);
532
533         rv = ns_name_uncompress(ns_msg_base(handle),
534                                 ns_msg_end(handle),
535                                 rrdata,
536                                 cname, MAXDNAME);
537         assert_int_not_equal(rv, -1);
538
539         assert_string_equal(cname, "web.cwrap.org");
540
541         assert_int_equal(ns_parserr(&handle, ns_s_an, 1, &rr), 0);
542         assert_int_equal(ns_rr_type(rr), ns_t_cname);
543
544         rrdata = ns_rr_rdata(rr);
545
546         rv = ns_name_uncompress(ns_msg_base(handle),
547                                 ns_msg_end(handle),
548                                 rrdata,
549                                 cname, MAXDNAME);
550         assert_int_not_equal(rv, -1);
551
552         assert_string_equal(cname, "www.cwrap.org");
553
554         assert_int_equal(ns_parserr(&handle, ns_s_an, 2, &rr), 0);
555         assert_int_equal(ns_rr_type(rr), ns_t_a);
556         assert_string_equal(ns_rr_name(rr), "www.cwrap.org");
557         assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr),
558                         addr, sizeof(addr)));
559         assert_string_equal(addr, "127.0.0.22");
560 }
561
562 int main(void)
563 {
564         int rc;
565
566         const struct CMUnitTest fake_tests[] = {
567                 cmocka_unit_test(test_res_fake_a_query),
568                 cmocka_unit_test(test_res_fake_a_query_case_insensitive),
569                 cmocka_unit_test(test_res_fake_a_query_trailing_dot),
570                 cmocka_unit_test(test_res_fake_a_query_notfound),
571                 cmocka_unit_test(test_res_fake_aaaa_query),
572                 cmocka_unit_test(test_res_fake_aaaa_query_notfound),
573                 cmocka_unit_test(test_res_fake_srv_query),
574                 cmocka_unit_test(test_res_fake_srv_query_minimal),
575                 cmocka_unit_test(test_res_fake_soa_query),
576                 cmocka_unit_test(test_res_fake_cname_query),
577                 cmocka_unit_test(test_res_fake_a_via_cname),
578         };
579
580         rc = cmocka_run_group_tests(fake_tests, NULL, NULL);
581
582         return rc;
583 }