260a8e65f67229a795a1a052552fd920ffa1706c
[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, ANSIZE);
70         assert_int_not_equal(rv, -1);
71
72         ns_initparse(answer, 256, &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), addr, 256));
82         assert_string_equal(addr, "127.0.0.21");
83 }
84
85 static void test_res_fake_a_query_notfound(void **state)
86 {
87         int rv;
88         struct __res_state dnsstate;
89         unsigned char answer[ANSIZE];
90         ns_msg handle;
91
92         (void) state; /* unused */
93
94         memset(&dnsstate, 0, sizeof(struct __res_state));
95         rv = res_ninit(&dnsstate);
96         assert_int_equal(rv, 0);
97
98         rv = res_nquery(&dnsstate, "nosuchentry.org", ns_c_in, ns_t_a,
99                         answer, ANSIZE);
100         assert_int_not_equal(rv, -1);
101
102         ns_initparse(answer, 256, &handle);
103         /* The query must finish w/o an error and have no answer */
104         assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
105         assert_int_equal(ns_msg_count(handle, ns_s_an), 0);
106 }
107
108 static void test_res_fake_aaaa_query(void **state)
109 {
110         int rv;
111         struct __res_state dnsstate;
112         unsigned char answer[ANSIZE];
113         char addr[INET6_ADDRSTRLEN];
114         ns_msg handle;
115         ns_rr rr;   /* expanded resource record */
116
117         (void) state; /* unused */
118
119         memset(&dnsstate, 0, sizeof(struct __res_state));
120         rv = res_ninit(&dnsstate);
121         assert_int_equal(rv, 0);
122
123         rv = res_nquery(&dnsstate, "cwrap6.org", ns_c_in, ns_t_aaaa,
124                         answer, ANSIZE);
125         assert_int_not_equal(rv, -1);
126
127         ns_initparse(answer, 256, &handle);
128         /* The query must finish w/o an error, have one answer and the answer
129          * must be a parseable RR of type AAAA and have the address that our
130          * fake hosts file contains
131          */
132         assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
133         assert_int_equal(ns_msg_count(handle, ns_s_an), 1);
134         assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
135         assert_int_equal(ns_rr_type(rr), ns_t_aaaa);
136         assert_non_null(inet_ntop(AF_INET6, ns_rr_rdata(rr), addr, 256));
137         assert_string_equal(addr, "2a00:1450:4013:c01::63");
138 }
139
140 static void test_res_fake_aaaa_query_notfound(void **state)
141 {
142         int rv;
143         struct __res_state dnsstate;
144         unsigned char answer[ANSIZE];
145         ns_msg handle;
146
147         (void) state; /* unused */
148
149         memset(&dnsstate, 0, sizeof(struct __res_state));
150         rv = res_ninit(&dnsstate);
151         assert_int_equal(rv, 0);
152
153         rv = res_nquery(&dnsstate, "nosuchentry.org", ns_c_in, ns_t_aaaa,
154                         answer, ANSIZE);
155         assert_int_not_equal(rv, -1);
156
157         ns_initparse(answer, 256, &handle);
158         /* The query must finish w/o an error and have no answer */
159         assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
160         assert_int_equal(ns_msg_count(handle, ns_s_an), 0);
161 }
162
163 static void test_res_fake_srv_query(void **state)
164 {
165         int rv;
166         struct __res_state dnsstate;
167         unsigned char answer[ANSIZE];
168         ns_msg handle;
169         ns_rr rr;   /* expanded resource record */
170         const uint8_t *rrdata;
171         int prio;
172         int weight;
173         int port;
174         char hostname[MAXDNAME];
175
176         (void) state; /* unused */
177
178         memset(&dnsstate, 0, sizeof(struct __res_state));
179         rv = res_ninit(&dnsstate);
180         assert_int_equal(rv, 0);
181
182         rv = res_nquery(&dnsstate, "_ldap._tcp.cwrap.org", ns_c_in, ns_t_srv,
183                         answer, ANSIZE);
184         assert_int_not_equal(rv, -1);
185
186         ns_initparse(answer, 256, &handle);
187
188         /*
189          * The query must finish w/o an error, have one answer and the answer
190          * must be a parseable RR of type SRV and have the priority, weight,
191          * port and hostname as in the fake hosts file
192          */
193         assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
194         assert_int_equal(ns_msg_count(handle, ns_s_an), 1);
195         assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
196         assert_int_equal(ns_rr_type(rr), ns_t_srv);
197
198         rrdata = ns_rr_rdata(rr);
199         NS_GET16(prio, rrdata);
200         NS_GET16(weight, rrdata);
201         NS_GET16(port, rrdata);
202
203         rv = ns_name_uncompress(ns_msg_base(handle),
204                                 ns_msg_end(handle),
205                                 rrdata,
206                                 hostname, MAXDNAME);
207         assert_int_not_equal(rv, -1);
208
209         assert_int_equal(prio, 1);
210         assert_int_equal(weight, 5);
211         assert_int_equal(port, 389);
212         assert_string_equal(hostname, "ldap.cwrap.org");
213 }
214
215 /*
216  * Test the case of a SRV record query where the
217  * fake hosts file entry is minimal in the sense
218  * that it omits the priority and weight entries.
219  * The server then fills in some default values.
220  */
221 static void test_res_fake_srv_query_minimal(void **state)
222 {
223         int rv;
224         struct __res_state dnsstate;
225         unsigned char answer[ANSIZE];
226         ns_msg handle;
227         ns_rr rr;   /* expanded resource record */
228         const uint8_t *rrdata;
229         int prio;
230         int weight;
231         int port;
232         char hostname[MAXDNAME];
233
234         (void) state; /* unused */
235
236         memset(&dnsstate, 0, sizeof(struct __res_state));
237         rv = res_ninit(&dnsstate);
238         assert_int_equal(rv, 0);
239
240         rv = res_nquery(&dnsstate, "_krb5._tcp.cwrap.org", ns_c_in, ns_t_srv,
241                         answer, ANSIZE);
242         assert_int_not_equal(rv, -1);
243
244         ns_initparse(answer, 256, &handle);
245
246         /*
247          * The query must finish w/o an error, have one answer and the answer
248          * must be a parseable RR of type SRV and have the priority, weight,
249          * port and hostname as in the fake hosts file
250          */
251         assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
252         assert_int_equal(ns_msg_count(handle, ns_s_an), 1);
253         assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
254         assert_int_equal(ns_rr_type(rr), ns_t_srv);
255
256         rrdata = ns_rr_rdata(rr);
257         NS_GET16(prio, rrdata);
258         NS_GET16(weight, rrdata);
259         NS_GET16(port, rrdata);
260
261         rv = ns_name_uncompress(ns_msg_base(handle),
262                                 ns_msg_end(handle),
263                                 rrdata,
264                                 hostname, MAXDNAME);
265         assert_int_not_equal(rv, -1);
266
267         assert_int_equal(prio, 1);
268         assert_int_equal(weight, 100);
269         assert_int_equal(port, 88);
270         assert_string_equal(hostname, "krb5.cwrap.org");
271 }
272
273 static void test_res_fake_soa_query(void **state)
274 {
275         int rv;
276         struct __res_state dnsstate;
277         unsigned char answer[ANSIZE];
278         ns_msg handle;
279         ns_rr rr;   /* expanded resource record */
280         const uint8_t *rrdata;
281         char nameser[MAXDNAME];
282         char admin[MAXDNAME];
283         int serial;
284         int refresh;
285         int retry;
286         int expire;
287         int minimum;
288
289         (void) state; /* unused */
290
291         memset(&dnsstate, 0, sizeof(struct __res_state));
292         rv = res_ninit(&dnsstate);
293         assert_int_equal(rv, 0);
294
295         rv = res_nquery(&dnsstate, "cwrap.org", ns_c_in, ns_t_soa,
296                         answer, ANSIZE);
297         assert_int_not_equal(rv, -1);
298
299         ns_initparse(answer, 256, &handle);
300
301         /*
302          * The query must finish w/o an error, have one answer and the answer
303          * must be a parseable RR of type SOA and have the data as in the fake
304          * hosts file
305          */
306         assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
307         assert_int_equal(ns_msg_count(handle, ns_s_an), 1);
308         assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
309         assert_int_equal(ns_rr_type(rr), ns_t_soa);
310
311         rrdata = ns_rr_rdata(rr);
312
313         rv = ns_name_uncompress(ns_msg_base(handle),
314                                 ns_msg_end(handle),
315                                 rrdata,
316                                 nameser, MAXDNAME);
317         assert_int_not_equal(rv, -1);
318         rrdata += rv;
319
320         rv = ns_name_uncompress(ns_msg_base(handle),
321                                 ns_msg_end(handle),
322                                 rrdata,
323                                 admin, MAXDNAME);
324         assert_int_not_equal(rv, -1);
325         rrdata += rv;
326
327         NS_GET32(serial, rrdata);
328         NS_GET32(refresh, rrdata);
329         NS_GET32(retry, rrdata);
330         NS_GET32(expire, rrdata);
331         NS_GET32(minimum, rrdata);
332
333         assert_string_equal(nameser, "ns1.cwrap.org");
334         assert_string_equal(admin, "admin.cwrap.org");
335         assert_int_equal(serial, 2014100457);
336         assert_int_equal(refresh, 3600);
337         assert_int_equal(retry, 300);
338         assert_int_equal(expire, 1814400);
339         assert_int_equal(minimum, 600);
340 }
341
342 static void test_res_fake_cname_query(void **state)
343 {
344         int rv;
345         struct __res_state dnsstate;
346         unsigned char answer[ANSIZE];
347         ns_msg handle;
348         ns_rr rr;   /* expanded resource record */
349         const uint8_t *rrdata;
350         char cname[MAXDNAME];
351
352         (void) state; /* unused */
353
354         memset(&dnsstate, 0, sizeof(struct __res_state));
355         rv = res_ninit(&dnsstate);
356         assert_int_equal(rv, 0);
357
358         rv = res_nquery(&dnsstate, "cwrap.org", ns_c_in, ns_t_cname,
359                         answer, ANSIZE);
360         assert_int_not_equal(rv, -1);
361
362         ns_initparse(answer, 256, &handle);
363
364         /*
365          * The query must finish w/o an error, have one answer and the answer
366          * must be a parseable RR of type CNAME and have the cname as in the
367          * fake hosts file
368          */
369         assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror);
370         assert_int_equal(ns_msg_count(handle, ns_s_an), 1);
371         assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0);
372         assert_int_equal(ns_rr_type(rr), ns_t_cname);
373
374         rrdata = ns_rr_rdata(rr);
375
376         rv = ns_name_uncompress(ns_msg_base(handle),
377                                 ns_msg_end(handle),
378                                 rrdata,
379                                 cname, MAXDNAME);
380         assert_int_not_equal(rv, -1);
381
382         assert_string_equal(cname, "therealcwrap.org");
383 }
384
385 int main(void)
386 {
387         int rc;
388
389         const UnitTest tests[] = {
390                 unit_test(test_res_fake_a_query),
391                 unit_test(test_res_fake_a_query_notfound),
392                 unit_test(test_res_fake_aaaa_query),
393                 unit_test(test_res_fake_aaaa_query_notfound),
394                 unit_test(test_res_fake_srv_query),
395                 unit_test(test_res_fake_srv_query_minimal),
396                 unit_test(test_res_fake_soa_query),
397                 unit_test(test_res_fake_cname_query),
398         };
399
400         rc = run_tests(tests);
401
402         return rc;
403 }